[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/busybox/src/miscutils/Config.src b/ap/app/busybox/src/miscutils/Config.src
new file mode 100644
index 0000000..b9fc196
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/Kbuild.src b/ap/app/busybox/src/miscutils/Kbuild.src
new file mode 100644
index 0000000..8c49864
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/adjtimex.c b/ap/app/busybox/src/miscutils/adjtimex.c
new file mode 100644
index 0000000..c8816e9
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/bbconfig.c b/ap/app/busybox/src/miscutils/bbconfig.c
new file mode 100644
index 0000000..e5f4eb3
--- /dev/null
+++ b/ap/app/busybox/src/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 "bb_archive.h"
+# include "bbconfigopts_bz2.h"
+#endif
+
+int bbconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int bbconfig_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+#if ENABLE_FEATURE_COMPRESS_BBCONFIG
+	bunzip_data *bd;
+	int i = start_bunzip(&bd,
+			/* src_fd: */ -1,
+			/* inbuf:  */ bbconfig_config_bz2,
+			/* len:    */ sizeof(bbconfig_config_bz2));
+	/* read_bunzip can longjmp to start_bunzip, and ultimately
+	 * end up here with i != 0 on read data errors! Not trivial */
+	if (!i) {
+		/* Cannot use xmalloc: will leak bd in NOFORK case! */
+		char *outbuf = malloc_or_warn(sizeof(bbconfig_config));
+		if (outbuf) {
+			read_bunzip(bd, outbuf, sizeof(bbconfig_config));
+			full_write1_str(outbuf);
+		}
+	}
+#else
+	full_write1_str(bbconfig_config);
+#endif
+	return 0;
+}
diff --git a/ap/app/busybox/src/miscutils/beep.c b/ap/app/busybox/src/miscutils/beep.c
new file mode 100644
index 0000000..910e03e
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/chat.c b/ap/app/busybox/src/miscutils/chat.c
new file mode 100644
index 0000000..ce994f8
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/chrt.c b/ap/app/busybox/src/miscutils/chrt.c
new file mode 100644
index 0000000..91b5397
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/conspy.c b/ap/app/busybox/src/miscutils/conspy.c
new file mode 100644
index 0000000..1a46a43
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/conspy.c
@@ -0,0 +1,577 @@
+/* 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 y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  A text-mode VNC like program for Linux virtual terminals.
+//config:	  example:  conspy NUM      shared access to console num
+//config:	  or        conspy -nd NUM  screenshot of console num
+//config:	  or        conspy -cs NUM  poor man's GNU screen like
+
+//usage:#define conspy_trivial_usage
+//usage:	"[-vcsndfFQ] [-x COL] [-y LINE] [CONSOLE_NO]"
+//usage:#define conspy_full_usage "\n\n"
+//usage:     "A text-mode VNC like program for Linux virtual consoles."
+//usage:     "\nTo exit, quickly press ESC 3 times."
+//usage:     "\n"
+//usage:     "\n	-v	Don't send keystrokes to the console"
+//usage:     "\n	-c	Create missing /dev/{tty,vcsa}N"
+//usage:     "\n	-s	Open a SHELL session"
+//usage:     "\n	-n	Black & white"
+//usage:     "\n	-d	Dump console to stdout"
+//usage:     "\n	-f	Follow cursor"
+//usage:     "\n	-F	Assume console is on a framebuffer device"
+//usage:     "\n	-Q	Disable exit on ESC-ESC-ESC"
+//usage:     "\n	-x COL	Starting column"
+//usage:     "\n	-y LINE	Starting line"
+
+#include "libbb.h"
+#include <sys/kd.h>
+
+#define ESC "\033"
+#define CURSOR_ON	-1
+#define CURSOR_OFF	1
+
+#define DEV_TTY		"/dev/tty"
+#define DEV_VCSA	"/dev/vcsa"
+
+struct screen_info {
+	unsigned char lines, cols, cursor_x, cursor_y;
+};
+
+#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("0;1;5;30;40m")];
+	// remote console
+	struct screen_info remote;
+	// saved local tty terminfo
+	struct termios term_orig;
+	char vcsa_name[sizeof(DEV_VCSA "NN")];
+};
+
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	G.width = G.height = UINT_MAX; \
+	G.last_attr--; \
+} while (0)
+
+enum {
+	FLAG_v,  // view only
+	FLAG_c,  // create device if need
+	FLAG_Q,  // never exit
+	FLAG_s,  // session
+	FLAG_n,  // no colors
+	FLAG_d,  // dump screen
+	FLAG_f,  // follow cursor
+	FLAG_F,  // framebuffer
+};
+#define FLAG(x) (1 << FLAG_##x)
+#define BW (option_mask32 & FLAG(n))
+
+static void putcsi(const char *s)
+{
+	fputs(ESC"[", stdout);
+	fputs(s, stdout);
+}
+
+static void clrscr(void)
+{
+	// Home, clear till end of screen
+	putcsi("1;1H" ESC"[J");
+	G.col = G.line = 0;
+}
+
+static void set_cursor(int state)
+{
+	if (G.curoff != state) {
+		G.curoff = state;
+		putcsi("?25");
+		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) NORETURN;
+static void cleanup(int code)
+{
+	set_cursor(CURSOR_ON);
+	tcsetattr(G.kbd_fd, TCSANOW, &G.term_orig);
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(G.kbd_fd);
+	}
+	// Reset attributes
+	if (!BW)
+		putcsi("0m");
+	bb_putchar('\n');
+	if (code > EXIT_FAILURE)
+		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);
+	}
+	if (G.size != i) {
+		cleanup(EXIT_FAILURE);
+	}
+	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 (y >= G.height || x >= G.width)
+				DATA(data) = 0;
+			else {
+				uint8_t ch = CHAR(data);
+				if (ch < ' ')
+					CHAR(data) = ch | 0x40;
+				else if (ch > 0x7e)
+					CHAR(data) = '?';
+			}
+		}
+	}
+}
+
+static void screen_char(char *data)
+{
+	if (!BW) {
+		uint8_t attr_diff;
+		uint8_t attr = ATTR(data);
+
+		if (option_mask32 & FLAG(F)) {
+			attr >>= 1;
+		}
+		attr_diff = G.last_attr ^ attr;
+		if (attr_diff) {
+// Attribute layout for VGA compatible text videobuffer:
+// blinking text
+// |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;
+
+			// (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++; // last attribute
+			}
+			if (ptr != G.attrbuf) {
+				ptr[-1] = 'm';
+				*ptr = '\0';
+				putcsi(G.attrbuf);
+			}
+		}
+	}
+	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 = CURSOR_OFF;
+
+	if (cx < G.width && cy < G.height) {
+		gotoxy(cx, cy);
+		cursor = CURSOR_ON;
+	}
+	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_TTY "NN")];
+#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"
+		"neverquit\0"    No_argument "Q"
+		"session\0"      No_argument "s"
+		"nocolors\0"     No_argument "n"
+		"dump\0"         No_argument "d"
+		"follow\0"       No_argument "f"
+		"framebuffer\0"  No_argument "F"
+		;
+
+	applet_long_options = getopt_longopts;
+#endif
+	INIT_G();
+	strcpy(G.vcsa_name, DEV_VCSA);
+
+	opt_complementary = "x+:y+"; // numeric params
+	opts = getopt32(argv, "vcQsndfFx:y:", &G.x, &G.y);
+	argv += optind;
+	ttynum = 0;
+	if (argv[0]) {
+		ttynum = xatou_range(argv[0], 0, 63);
+		sprintf(G.vcsa_name + sizeof(DEV_VCSA)-1, "%u", ttynum);
+	}
+	sprintf(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)
+				goto abort;
+			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)
+				goto abort;
+
+			// Do exit processing
+			if (!(option_mask32 & FLAG(Q))) {
+				for (i = 0; i < bytes_read; i++) {
+					if (k[i] != '\033')
+						G.escape_count = -1;
+					if (++G.escape_count >= 3)
+						cleanup(EXIT_SUCCESS);
+				}
+			}
+		}
+		poll_timeout_ms = 250;
+		if (option_mask32 & FLAG(v)) continue;
+
+		// Insert all keys pressed into the virtual console's input
+		// buffer.  Don't do this if the virtual console is in scan
+		// code mode - giving ASCII characters to a program expecting
+		// scan codes will confuse it.
+		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;
+
+				G.ioerror_count = 0;
+				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;
+				}
+			}
+			// We sometimes get spurious IO errors on the TTY
+			// as programs close and re-open it
+			else if (errno != EIO || ++G.ioerror_count > 4) {
+				if (ENABLE_FEATURE_CLEAN_UP)
+					close(handle);
+				goto abort;
+			}
+			// Close & re-open tty in case they have
+			// swapped virtual consoles
+			close(handle);
+		}
+	} /* while (1) */
+  abort:
+	cleanup(EXIT_FAILURE);
+}
diff --git a/ap/app/busybox/src/miscutils/crond.c b/ap/app/busybox/src/miscutils/crond.c
new file mode 100644
index 0000000..582dc99
--- /dev/null
+++ b/ap/app/busybox/src/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(CONFIG_PID_FILE_PATH "/crond.pid");
+
+	/* Main loop */
+	t2 = time(NULL);
+	rescan = 60;
+	sleep_time = 60;
+	for (;;) {
+		struct stat sbuf;
+		time_t t1;
+		long dt;
+
+		t1 = t2;
+
+		/* Synchronize to 1 minute, minimum 1 second */
+		sleep(sleep_time - (time(NULL) % sleep_time) + 1);
+
+		t2 = time(NULL);
+		dt = (long)t2 - (long)t1;
+
+		/*
+		 * The file 'cron.update' is checked to determine new cron
+		 * jobs.  The directory is rescanned once an hour to deal
+		 * with any screwups.
+		 *
+		 * Check for time jump.  Disparities over an hour either way
+		 * result in resynchronization.  A negative disparity
+		 * less than an hour causes us to effectively sleep until we
+		 * match the original time (i.e. no re-execution of jobs that
+		 * have just been run).  A positive disparity less than
+		 * an hour causes intermediate jobs to be run, but only once
+		 * in the worst case.
+		 *
+		 * When running jobs, the inequality used is greater but not
+		 * equal to t1, and less then or equal to t2.
+		 */
+		if (stat(G.crontab_dir_name, &sbuf) != 0)
+			sbuf.st_mtime = 0; /* force update (once) if dir was deleted */
+		if (G.crontab_dir_mtime != sbuf.st_mtime) {
+			G.crontab_dir_mtime = sbuf.st_mtime;
+			rescan = 1;
+		}
+		if (--rescan == 0) {
+			rescan = 60;
+			rescan_crontab_dir();
+		}
+		process_cron_update_file();
+		if (DebugOpt)
+			crondlog(LVL5 "wakeup dt=%ld", dt);
+		if (dt < -60 * 60 || dt > 60 * 60) {
+			crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60);
+			/* and we do not run any jobs in this case */
+		} else if (dt > 0) {
+			/* Usual case: time advances forward, as expected */
+			flag_starting_jobs(t1, t2);
+			start_jobs();
+			if (check_completions() > 0) {
+				/* some jobs are still running */
+				sleep_time = 10;
+			} else {
+				sleep_time = 60;
+			}
+		}
+		/* else: time jumped back, do not run any jobs */
+	} /* for (;;) */
+
+	return 0; /* not reached */
+}
diff --git a/ap/app/busybox/src/miscutils/crontab.c b/ap/app/busybox/src/miscutils/crontab.c
new file mode 100644
index 0000000..4731d8d
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/dc.c b/ap/app/busybox/src/miscutils/dc.c
new file mode 100644
index 0000000..6bcfbe2
--- /dev/null
+++ b/ap/app/busybox/src/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, xor,\n"
+//usage:       "p - print top of the stack (without popping),\n"
+//usage:       "f - print entire stack,\n"
+//usage:       "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n"
+//usage:       "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
+//usage:
+//usage:#define dc_example_usage
+//usage:       "$ dc 2 2 + p\n"
+//usage:       "4\n"
+//usage:       "$ dc 8 8 \\* 2 2 + / p\n"
+//usage:       "16\n"
+//usage:       "$ dc 0 1 and p\n"
+//usage:       "0\n"
+//usage:       "$ dc 0 1 or p\n"
+//usage:       "1\n"
+//usage:       "$ echo 72 9 div 8 mul p | dc\n"
+//usage:       "64\n"
+
+#if 0
+typedef unsigned data_t;
+#define DATA_FMT ""
+#elif 0
+typedef unsigned long data_t;
+#define DATA_FMT "l"
+#else
+typedef unsigned long long data_t;
+#define DATA_FMT "ll"
+#endif
+
+
+struct globals {
+	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},
+};
+
+static void stack_machine(const char *argument)
+{
+	char *end;
+	double d;
+	const struct op *o;
+
+	d = strtod(argument, &end);
+	if (end != argument && *end == '\0') {
+		push(d);
+		return;
+	}
+
+	o = operators;
+	do {
+		if (strcmp(o->name, argument) == 0) {
+			o->function();
+			return;
+		}
+		o++;
+	} while (o != operators + ARRAY_SIZE(operators));
+
+	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/ap/app/busybox/src/miscutils/devfsd.c b/ap/app/busybox/src/miscutils/devfsd.c
new file mode 100644
index 0000000..24c953b
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/devmem.c b/ap/app/busybox/src/miscutils/devmem.c
new file mode 100644
index 0000000..786a21b
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/eject.c b/ap/app/busybox/src/miscutils/eject.c
new file mode 100644
index 0000000..a20e04b
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/fbsplash.c b/ap/app/busybox/src/miscutils/fbsplash.c
new file mode 100644
index 0000000..12a77b7
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/fbsplash.c
@@ -0,0 +1,537 @@
+/* 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;
+	// cached (8 - scr_var.COLOR.length):
+	unsigned red_shift;
+	unsigned green_shift;
+	unsigned blue_shift;
+};
+#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
+
+/**
+ * Configure palette for RGB:332
+ */
+static void fb_setpal(int fd)
+{
+	struct fb_cmap cmap;
+	/* fb colors are 16 bit */
+	unsigned short red[256], green[256], blue[256];
+	unsigned i;
+
+	/* RGB:332 */
+	for (i = 0; i < 256; i++) {
+		/* Color is encoded in pixel value as rrrgggbb.
+		 * 3-bit color is mapped to 16-bit one as:
+		 * 000 -> 00000000 00000000
+		 * 001 -> 00100100 10010010
+		 * ...
+		 * 011 -> 01101101 10110110
+		 * 100 -> 10010010 01001001
+		 * ...
+		 * 111 -> 11111111 11111111
+		 */
+		red[i]   = (( i >> 5       ) * 0x9249) >> 2; // rrr * 00 10010010 01001001 >> 2
+		green[i] = (((i >> 2) & 0x7) * 0x9249) >> 2; // ggg * 00 10010010 01001001 >> 2
+		/* 2-bit color is easier: */
+		blue[i]  =  ( i       & 0x3) * 0x5555; // bb * 01010101 01010101
+	}
+
+	cmap.start = 0;
+	cmap.len   = 256;
+	cmap.red   = red;
+	cmap.green = green;
+	cmap.blue  = blue;
+	cmap.transp = 0;
+
+	xioctl(fd, FBIOPUTCMAP, &cmap);
+}
+
+/**
+ * Open and initialize the framebuffer device
+ * \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);
+
+	switch (G.scr_var.bits_per_pixel) {
+	case 8:
+		fb_setpal(fbfd);
+		break;
+
+	case 16:
+	case 24:
+	case 32:
+		break;
+
+	default:
+		bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel);
+		break;
+	}
+
+	G.red_shift   = 8 - G.scr_var.red.length;
+	G.green_shift = 8 - G.scr_var.green.length;
+	G.blue_shift  = 8 - G.scr_var.blue.length;
+	G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) >> 3;
+
+	// map the device in memory
+	G.addr = mmap(NULL,
+			G.scr_var.yres * G.scr_fix.line_length,
+			PROT_WRITE, MAP_SHARED, fbfd, 0);
+	if (G.addr == MAP_FAILED)
+		bb_perror_msg_and_die("mmap");
+
+	// point to the start of the visible screen
+	G.addr += G.scr_var.yoffset * G.scr_fix.line_length + G.scr_var.xoffset * G.bytes_per_pixel;
+	close(fbfd);
+}
+
+
+/**
+ * Return pixel value of the passed RGB color.
+ * This is performance critical fn.
+ */
+static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
+{
+	/* We assume that the r,g,b values are <= 255 */
+
+	if (G.bytes_per_pixel == 1) {
+		r = r        & 0xe0; // 3-bit red
+		g = (g >> 3) & 0x1c; // 3-bit green
+		b =  b >> 6;         // 2-bit blue
+		return r + g + b;
+	}
+	if (G.bytes_per_pixel == 2) {
+		// ARM PL110 on Integrator/CP has RGBA5551 bit arrangement.
+		// We want to support bit locations like that.
+		//
+		// First shift out unused bits
+		r = r >> G.red_shift;
+		g = g >> G.green_shift;
+		b = b >> G.blue_shift;
+		// Then shift the remaining bits to their offset
+		return (r << G.scr_var.red.offset) +
+			(g << G.scr_var.green.offset) +
+			(b << G.scr_var.blue.offset);
+	}
+	// RGB 888
+	return b + (g << 8) + (r << 16);
+}
+
+/**
+ * Draw pixel on framebuffer
+ */
+static void fb_write_pixel(unsigned char *addr, unsigned pixel)
+{
+	switch (G.bytes_per_pixel) {
+	case 1:
+		*addr = pixel;
+		break;
+	case 2:
+		*(uint16_t *)addr = pixel;
+		break;
+	case 4:
+		*(uint32_t *)addr = pixel;
+		break;
+	default: // 24 bits per pixel
+		addr[0] = pixel;
+		addr[1] = pixel >> 8;
+		addr[2] = pixel >> 16;
+	}
+}
+
+
+/**
+ * Draw hollow rectangle on framebuffer
+ */
+static void fb_drawrectangle(void)
+{
+	int cnt;
+	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_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+	ptr2 = G.addr + (G.nbar_posy + G.nbar_height - 1) * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+	cnt = G.nbar_width - 1;
+	do {
+		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_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+	ptr2 = G.addr + G.nbar_posy * G.scr_fix.line_length + (G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
+	cnt = G.nbar_height - 1;
+	do {
+		fb_write_pixel(ptr1, thispix);
+		fb_write_pixel(ptr2, thispix);
+		ptr1 += G.scr_fix.line_length;
+		ptr2 += G.scr_fix.line_length;
+	} while (--cnt >= 0);
+}
+
+
+/**
+ * Draw filled rectangle on framebuffer
+ * \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_fix.line_length + 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 left_x, top_y, pos_x;
+	unsigned width, height;
+
+	// outer box
+	left_x = G.nbar_posx;
+	top_y = G.nbar_posy;
+	width = G.nbar_width - 1;
+	height = G.nbar_height - 1;
+	if ((int)(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 ((int)(height | width) < 0)
+		return;
+
+	pos_x = left_x;
+	if (percent > 0) {
+		int i, y;
+
+		// actual progress bar
+		pos_x += (unsigned)(width * percent) / 100;
+
+		y = top_y;
+		i = height;
+		if (height == 0)
+			height++; // divide by 0 is bad
+		while (i >= 0) {
+			// draw one-line thick "rectangle"
+			// top line will have gray lvl 200, bottom one 100
+			unsigned gray_level = 100 + (unsigned)i*100 / height;
+			fb_drawfullrectangle(
+					left_x, y, pos_x, y,
+					gray_level, gray_level, gray_level);
+			y++;
+			i--;
+		}
+	}
+
+	fb_drawfullrectangle(
+			pos_x, top_y,
+			left_x + width, top_y + height,
+			G.nbar_colr, G.nbar_colg, G.nbar_colb);
+}
+
+
+/**
+ * Draw image from PPM file
+ */
+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/ap/app/busybox/src/miscutils/fbsplash.cfg b/ap/app/busybox/src/miscutils/fbsplash.cfg
new file mode 100644
index 0000000..b6cf607
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/flash_eraseall.c b/ap/app/busybox/src/miscutils/flash_eraseall.c
new file mode 100644
index 0000000..0598371
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/flash_lock_unlock.c b/ap/app/busybox/src/miscutils/flash_lock_unlock.c
new file mode 100644
index 0000000..1fefd95
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/flashcp.c b/ap/app/busybox/src/miscutils/flashcp.c
new file mode 100644
index 0000000..81cde90
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/flashcp.c
@@ -0,0 +1,156 @@
+/* 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;
+	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/ap/app/busybox/src/miscutils/hdparm.c b/ap/app/busybox/src/miscutils/hdparm.c
new file mode 100644
index 0000000..69726ae
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/hdparm.c
@@ -0,0 +1,2138 @@
+/* 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               )
+#define INIT_G() do { } while (0)
+
+
+/* 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;
+
+	INIT_G();
+
+	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/ap/app/busybox/src/miscutils/inotifyd.c b/ap/app/busybox/src/miscutils/inotifyd.c
new file mode 100644
index 0000000..7a1a6a2
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/inotifyd.c
@@ -0,0 +1,212 @@
+/* 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:     "\nIf PROG is -, events are sent to stdout."
+//usage:     "\nEvents:"
+//usage:     "\n	a	File is accessed"
+//usage:     "\n	c	File is modified"
+//usage:     "\n	e	Metadata changed"
+//usage:     "\n	w	Writable file is closed"
+//usage:     "\n	0	Unwritable file is closed"
+//usage:     "\n	r	File is opened"
+//usage:     "\n	D	File is deleted"
+//usage:     "\n	M	File is moved"
+//usage:     "\n	u	Backing fs is unmounted"
+//usage:     "\n	o	Event queue overflowed"
+//usage:     "\n	x	File can't be watched anymore"
+//usage:     "\nIf watching a directory:"
+//usage:     "\n	m	Subfile is moved into dir"
+//usage:     "\n	y	Subfile is moved out of dir"
+//usage:     "\n	n	Subfile is created"
+//usage:     "\n	d	Subfile is deleted"
+//usage:     "\n"
+//usage:     "\ninotifyd waits for PROG to exit."
+//usage:     "\nWhen x event happens for all FILEs, inotifyd exits."
+
+#include "libbb.h"
+#include <sys/inotify.h>
+
+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';
+				if (LONE_CHAR(args[0], '-')) {
+					/* "inotifyd - FILE": built-in echo */
+					printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events,
+							watches[ie->wd],
+							ie->name);
+					fflush(stdout);
+				} else {
+//					bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
+//						ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
+					args[1] = events;
+					args[2] = watches[ie->wd];
+					args[3] = ie->len ? ie->name : NULL;
+					spawn_and_wait((char **)args);
+				}
+				// we are done if all files got final x event
+				if (ie->mask & 0x8000) {
+					if (--argc <= 0)
+						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/ap/app/busybox/src/miscutils/ionice.c b/ap/app/busybox/src/miscutils/ionice.c
new file mode 100644
index 0000000..bd30060
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/last.c b/ap/app/busybox/src/miscutils/last.c
new file mode 100644
index 0000000..24f6e1c
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/last_fancy.c b/ap/app/busybox/src/miscutils/last_fancy.c
new file mode 100644
index 0000000..f687d7e
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/less.c b/ap/app/busybox/src/miscutils/less.c
new file mode 100644
index 0000000..5ce0a12
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/less.c
@@ -0,0 +1,1916 @@
+/* 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 -m/-M"
+//config:	default y
+//config:	depends on LESS
+//config:	help
+//config:	  The -M/-m flag enables a more sophisticated status line.
+//config:
+//config:config FEATURE_LESS_MARKS
+//config:	bool "Enable marks"
+//config:	default y
+//config:	depends on LESS
+//config:	help
+//config:	  Marks enable positions in a file to be stored for easy reference.
+//config:
+//config:config FEATURE_LESS_REGEXP
+//config:	bool "Enable regular expressions"
+//config:	default y
+//config:	depends on LESS
+//config:	help
+//config:	  Enable regular expressions, allowing complex file searches.
+//config:
+//config:config FEATURE_LESS_WINCH
+//config:	bool "Enable automatic resizing on window size changes"
+//config:	default y
+//config:	depends on LESS
+//config:	help
+//config:	  Makes less track window size changes.
+//config:
+//config:config FEATURE_LESS_ASK_TERMINAL
+//config:	bool "Use 'tell me cursor position' ESC sequence to measure window"
+//config:	default y
+//config:	depends on FEATURE_LESS_WINCH
+//config:	help
+//config:	  Makes less track window size changes.
+//config:	  If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
+//config:	  this option makes less perform a last-ditch effort to find it:
+//config:	  position cursor to 999,999 and ask terminal to report real
+//config:	  cursor position using "ESC [ 6 n" escape sequence, then read stdin.
+//config:
+//config:	  This is not clean but helps a lot on serial lines and such.
+//config:
+//config:config FEATURE_LESS_DASHCMD
+//config:	bool "Enable flag changes ('-' command)"
+//config:	default y
+//config:	depends on LESS
+//config:	help
+//config:	  This enables the ability to change command-line flags within
+//config:	  less itself ('-' keyboard command).
+//config:
+//config:config FEATURE_LESS_LINENUMS
+//config:	bool "Enable dynamic switching of line numbers"
+//config:	default y
+//config:	depends on FEATURE_LESS_DASHCMD
+//config:	help
+//config:	  Enables "-N" command.
+
+//usage:#define less_trivial_usage
+//usage:       "[-E" IF_FEATURE_LESS_FLAGS("Mm") "Nh~I?] [FILE]..."
+//usage:#define less_full_usage "\n\n"
+//usage:       "View FILE (or stdin) one screenful at a time\n"
+//usage:     "\n	-E	Quit once the end of a file is reached"
+//usage:	IF_FEATURE_LESS_FLAGS(
+//usage:     "\n	-M,-m	Display status line with line numbers"
+//usage:     "\n		and percentage through the file"
+//usage:	)
+//usage:     "\n	-N	Prefix line number to each line"
+//usage:     "\n	-I	Ignore case in all searches"
+//usage:     "\n	-~	Suppress ~s displayed past EOF"
+
+#include <sched.h>  /* sched_yield() */
+
+#include "libbb.h"
+#if ENABLE_FEATURE_LESS_REGEXP
+#include "xregex.h"
+#endif
+
+
+#define ESC "\033"
+/* The escape codes for highlighted and normal text */
+#define HIGHLIGHT   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/ap/app/busybox/src/miscutils/makedevs.c b/ap/app/busybox/src/miscutils/makedevs.c
new file mode 100644
index 0000000..c945a13
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/man.c b/ap/app/busybox/src/miscutils/man.c
new file mode 100644
index 0000000..e380fda
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/man.c
@@ -0,0 +1,283 @@
+/* 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
+
+*/
+
+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", 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" */
+static int show_manpage(const char *pager, char *man_filename, int man, int level)
+{
+#if SEAMLESS_COMPRESSION
+	/* We leak this allocation... */
+	char *filename_with_zext = xasprintf("%s.lzma", man_filename);
+	char *ext = strrchr(filename_with_zext, '.') + 1;
+#endif
+
+#if ENABLE_FEATURE_SEAMLESS_LZMA
+	if (run_pipe(pager, filename_with_zext, man, level))
+		return 1;
+#endif
+#if ENABLE_FEATURE_SEAMLESS_XZ
+	strcpy(ext, "xz");
+	if (run_pipe(pager, filename_with_zext, man, level))
+		return 1;
+#endif
+#if ENABLE_FEATURE_SEAMLESS_BZ2
+	strcpy(ext, "bz2");
+	if (run_pipe(pager, filename_with_zext, man, level))
+		return 1;
+#endif
+#if ENABLE_FEATURE_SEAMLESS_GZ
+	strcpy(ext, "gz");
+	if (run_pipe(pager, filename_with_zext, man, level))
+		return 1;
+#endif
+
+	return run_pipe(pager, man_filename, man, level);
+}
+
+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",
+							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/ap/app/busybox/src/miscutils/microcom.c b/ap/app/busybox/src/miscutils/microcom.c
new file mode 100644
index 0000000..5e29a1a
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/mountpoint.c b/ap/app/busybox/src/miscutils/mountpoint.c
new file mode 100644
index 0000000..7041f7c
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/mt.c b/ap/app/busybox/src/miscutils/mt.c
new file mode 100644
index 0000000..20afd3a
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/nandwrite.c b/ap/app/busybox/src/miscutils/nandwrite.c
new file mode 100644
index 0000000..5908ac7
--- /dev/null
+++ b/ap/app/busybox/src/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
+ */
+
+//config:config NANDWRITE
+//config:	bool "nandwrite"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Write to the specified MTD device, with bad blocks awareness
+//config:
+//config:config NANDDUMP
+//config:	bool "nanddump"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Dump the content of raw NAND chip
+
+//applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_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
+
+//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	Dump oob data"
+//usage:     "\n	-b	Omit bad block from the dump"
+//usage:     "\n	-s ADDR	Start address"
+//usage:     "\n	-l LEN	Length"
+//usage:     "\n	-f FILE	Dump to file ('-' for stdout)"
+
+#include "libbb.h"
+#include <mtd/mtd-user.h>
+
+#define IS_NANDDUMP  (ENABLE_NANDDUMP && (!ENABLE_NANDWRITE || (applet_name[4] == 'd')))
+#define IS_NANDWRITE (ENABLE_NANDWRITE && (!ENABLE_NANDDUMP || (applet_name[4] != 'd')))
+
+#define OPT_p  (1 << 0) /* nandwrite only */
+#define OPT_o  (1 << 0) /* nanddump only */
+#define OPT_s  (1 << 1)
+#define OPT_b  (1 << 2)
+#define OPT_f  (1 << 3)
+#define OPT_l  (1 << 4)
+
+/* helper for writing out 0xff for bad blocks pad */
+static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob)
+{
+	unsigned char buf[meminfo->writesize];
+	unsigned count;
+
+	/* round len to the next page */
+	len = (len | ~(meminfo->writesize - 1)) + 1;
+
+	memset(buf, 0xff, sizeof(buf));
+	for (count = 0; count < len; count += meminfo->writesize) {
+		xwrite(STDOUT_FILENO, buf, meminfo->writesize);
+		if (oob)
+			xwrite(STDOUT_FILENO, buf, meminfo->oobsize);
+	}
+}
+
+static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo,
+		unsigned block_offset)
+{
+	while (1) {
+		loff_t offs;
+
+		if (block_offset >= meminfo->size) {
+			if (IS_NANDWRITE)
+				bb_error_msg_and_die("not enough space in MTD device");
+			return block_offset; /* let the caller exit */
+		}
+		offs = block_offset;
+		if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0)
+			return block_offset;
+		/* ioctl returned 1 => "bad block" */
+		if (IS_NANDWRITE)
+			printf("Skipping bad block at 0x%08x\n", block_offset);
+		block_offset += meminfo->erasesize;
+	}
+}
+
+int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nandwrite_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* Buffer for OOB data */
+	unsigned char *oobbuf;
+	unsigned opts;
+	int fd;
+	ssize_t cnt;
+	unsigned mtdoffset, meminfo_writesize, blockstart, limit;
+	unsigned end_addr = ~0;
+	struct mtd_info_user meminfo;
+	struct mtd_oob_buf oob;
+	unsigned char *filebuf;
+	const char *opt_s = "0", *opt_f = "-", *opt_l;
+
+	if (IS_NANDDUMP) {
+		opt_complementary = "=1";
+		opts = getopt32(argv, "os:bf:l:", &opt_s, &opt_f, &opt_l);
+	} else { /* nandwrite */
+		opt_complementary = "-1:?2";
+		opts = getopt32(argv, "ps:", &opt_s);
+	}
+	argv += optind;
+
+	if (IS_NANDWRITE && argv[1])
+		opt_f = argv[1];
+	if (!LONE_DASH(opt_f)) {
+		int tmp_fd = xopen(opt_f,
+			IS_NANDDUMP ? O_WRONLY | O_TRUNC | O_CREAT : O_RDONLY
+		);
+		xmove_fd(tmp_fd, IS_NANDDUMP ? STDOUT_FILENO : STDIN_FILENO);
+	}
+
+	fd = xopen(argv[0], IS_NANDWRITE ? O_RDWR : O_RDONLY);
+	xioctl(fd, MEMGETINFO, &meminfo);
+
+	mtdoffset = xstrtou(opt_s, 0);
+	if (IS_NANDDUMP && (opts & OPT_l)) {
+		unsigned length = xstrtou(opt_l, 0);
+		if (length < meminfo.size - mtdoffset)
+			end_addr = mtdoffset + length;
+	}
+
+	/* Pull it into a CPU register (hopefully) - smaller code that way */
+	meminfo_writesize = meminfo.writesize;
+
+	if (mtdoffset & (meminfo_writesize - 1))
+		bb_error_msg_and_die("start address is not page aligned");
+
+	filebuf = xmalloc(meminfo_writesize);
+	oobbuf = xmalloc(meminfo.oobsize);
+
+	oob.start  = 0;
+	oob.length = meminfo.oobsize;
+	oob.ptr    = oobbuf;
+
+	blockstart = mtdoffset & ~(meminfo.erasesize - 1);
+	if (blockstart != mtdoffset) {
+		unsigned tmp;
+		/* mtdoffset is in the middle of an erase block, verify that
+		 * this block is OK. Advance mtdoffset only if this block is
+		 * bad.
+		 */
+		tmp = next_good_eraseblock(fd, &meminfo, blockstart);
+		if (tmp != blockstart) {
+			/* bad block(s), advance mtdoffset */
+			if (IS_NANDDUMP && !(opts & OPT_b)) {
+				int bad_len = MIN(tmp, end_addr) - mtdoffset;
+				dump_bad(&meminfo, bad_len, opts & OPT_o);
+			}
+			mtdoffset = tmp;
+		}
+	}
+
+	cnt = -1;
+	limit = MIN(meminfo.size, end_addr);
+	while (mtdoffset < limit) {
+		int input_fd = IS_NANDWRITE ? STDIN_FILENO : fd;
+		int output_fd = IS_NANDWRITE ? fd : STDOUT_FILENO;
+
+		blockstart = mtdoffset & ~(meminfo.erasesize - 1);
+		if (blockstart == mtdoffset) {
+			/* starting a new eraseblock */
+			mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart);
+			if (IS_NANDWRITE)
+				printf("Writing at 0x%08x\n", mtdoffset);
+			else if (mtdoffset > blockstart && !(opts & OPT_b)) {
+				int bad_len = MIN(mtdoffset, limit) - blockstart;
+				dump_bad(&meminfo, bad_len, opts & OPT_o);
+			}
+			if (mtdoffset >= limit)
+				break;
+		}
+		xlseek(fd, mtdoffset, SEEK_SET);
+
+		/* get some more data from input */
+		cnt = full_read(input_fd, filebuf, meminfo_writesize);
+		if (cnt == 0) {
+			/* even with -p, we do not pad past the end of input
+			 * (-p only zero-pads last incomplete page)
+			 */
+			break;
+		}
+		if (cnt < meminfo_writesize) {
+			if (IS_NANDDUMP)
+				bb_error_msg_and_die("short read");
+			if (!(opts & OPT_p))
+				bb_error_msg_and_die("input size is not rounded up to page size, "
+						"use -p to zero pad");
+			/* zero pad to end of write block */
+			memset(filebuf + cnt, 0, meminfo_writesize - cnt);
+		}
+		xwrite(output_fd, filebuf, meminfo_writesize);
+
+		if (IS_NANDDUMP && (opts & OPT_o)) {
+			/* Dump OOB data */
+			oob.start = mtdoffset;
+			xioctl(fd, MEMREADOOB, &oob);
+			xwrite(output_fd, oobbuf, meminfo.oobsize);
+		}
+
+		mtdoffset += meminfo_writesize;
+		if (cnt < meminfo_writesize)
+			break;
+	}
+
+	if (IS_NANDWRITE && cnt != 0) {
+		/* We filled entire MTD, but did we reach EOF on input? */
+		if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) {
+			/* no */
+			bb_error_msg_and_die("not enough space in MTD device");
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(filebuf);
+		close(fd);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/miscutils/raidautorun.c b/ap/app/busybox/src/miscutils/raidautorun.c
new file mode 100644
index 0000000..b72d890
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/readahead.c b/ap/app/busybox/src/miscutils/readahead.c
new file mode 100644
index 0000000..e22aaa4
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/rfkill.c b/ap/app/busybox/src/miscutils/rfkill.c
new file mode 100644
index 0000000..4671973
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/runlevel.c b/ap/app/busybox/src/miscutils/runlevel.c
new file mode 100644
index 0000000..76231df
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/rx.c b/ap/app/busybox/src/miscutils/rx.c
new file mode 100644
index 0000000..1dffb59
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/rx.c
@@ -0,0 +1,271 @@
+/* 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 */
+		errno = 0;
+		if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
+			bb_perror_msg(bb_msg_write_error);
+			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;
+		}
+
+		cksum_or_crc = read_byte(TIMEOUT);
+		if (cksum_or_crc < 0)
+			goto timeout;
+		if (do_crc) {
+			cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT);
+			if (cksum_or_crc < 0)
+				goto timeout;
+		}
+
+		if (blockNo == ((wantBlockNo - 1) & 0xff)) {
+			/* a repeat of the last block is ok, just ignore it. */
+			/* this also ignores the initial block 0 which is */
+			/* meta data. */
+			blockLength = 0;
+			goto next;
+		}
+		if (blockNo != (wantBlockNo & 0xff)) {
+			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:
+		blockLength = 0;
+		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/ap/app/busybox/src/miscutils/setserial.c b/ap/app/busybox/src/miscutils/setserial.c
new file mode 100644
index 0000000..2a034e3
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/setsid.c b/ap/app/busybox/src/miscutils/setsid.c
new file mode 100644
index 0000000..ad2c8a4
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/strings.c b/ap/app/busybox/src/miscutils/strings.c
new file mode 100644
index 0000000..9f50182
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/taskset.c b/ap/app/busybox/src/miscutils/taskset.c
new file mode 100644
index 0000000..4a9e323
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/time.c b/ap/app/busybox/src/miscutils/time.c
new file mode 100644
index 0000000..58ab510
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/time.c
@@ -0,0 +1,433 @@
+/* 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"
+#include <sys/resource.h> /* getrusage */
+
+#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_MMU__)
+#define fork    vfork
+#endif
+
+/* 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/ap/app/busybox/src/miscutils/timeout.c b/ap/app/busybox/src/miscutils/timeout.c
new file mode 100644
index 0000000..9d56593
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/ttysize.c b/ap/app/busybox/src/miscutils/ttysize.c
new file mode 100644
index 0000000..d2d48d0
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/ubi_tools.c b/ap/app/busybox/src/miscutils/ubi_tools.c
new file mode 100644
index 0000000..dd99a44
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/ubi_tools.c
@@ -0,0 +1,276 @@
+/* Ported to busybox from mtd-utils.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config UBIATTACH
+//config:	bool "ubiattach"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Attach MTD device to an UBI device.
+//config:
+//config:config UBIDETACH
+//config:	bool "ubidetach"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Detach MTD device from an UBI device.
+//config:
+//config:config UBIMKVOL
+//config:	bool "ubimkvol"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Create a UBI volume.
+//config:
+//config:config UBIRMVOL
+//config:	bool "ubirmvol"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Delete a UBI volume.
+//config:
+//config:config UBIRSVOL
+//config:	bool "ubirsvol"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Resize a UBI volume.
+//config:
+//config:config UBIUPDATEVOL
+//config:	bool "ubiupdatevol"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Update a UBI volume.
+
+//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach))
+//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach))
+//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol))
+//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol))
+//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol))
+//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol))
+
+//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIMKVOL)  += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIRMVOL)  += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIRSVOL)  += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o
+
+#include "libbb.h"
+/* Some versions of kernel have broken headers, need this hack */
+#ifndef __packed
+# define __packed __attribute__((packed))
+#endif
+#include <mtd/ubi-user.h>
+
+#define 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/ap/app/busybox/src/miscutils/volname.c b/ap/app/busybox/src/miscutils/volname.c
new file mode 100644
index 0000000..b50e795
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/wall.c b/ap/app/busybox/src/miscutils/wall.c
new file mode 100644
index 0000000..762f53b
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/miscutils/watchdog.c b/ap/app/busybox/src/miscutils/watchdog.c
new file mode 100644
index 0000000..85a559f
--- /dev/null
+++ b/ap/app/busybox/src/miscutils/watchdog.c
@@ -0,0 +1,134 @@
+/* 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';
+
+	remove_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
+	write(3, &V, 1);  /* Magic, see watchdog-api.txt in kernel */
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(3);
+	_exit(EXIT_SUCCESS);
+}
+
+static void watchdog_safe_terminate(int unused UNUSED_PARAM) NORETURN;
+static void watchdog_safe_terminate(int unused UNUSED_PARAM)
+{
+	write(3, "T", 1);	/* Magic safe terminate (ie, a longer timeout) */
+	close(3);
+	exit(0);
+}
+
+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;
+	int fd;
+
+	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);
+
+	fd = open("/proc/self/oom_score_adj", O_WRONLY);
+	if (fd >= 0) {
+		write(fd, "-1000", 5);
+		close(fd);
+	} else {
+		fd = open("/proc/self/oom_adj", O_WRONLY);
+		if (fd >= 0) {
+			write(fd, "-17", 3);
+			close(fd);
+		}
+	}
+
+	signal(SIGHUP, watchdog_shutdown);
+	signal(SIGINT, watchdog_shutdown);
+	signal(SIGTERM, watchdog_safe_terminate);
+	//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
+
+	write_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
+
+	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! */
+}