Initialize Tizen 2.3 2.3a_release submit/tizen_2.3/20140531.113219
authorSehong Na <sehong.na@samsung.com>
Sat, 31 May 2014 04:01:57 +0000 (13:01 +0900)
committerSehong Na <sehong.na@samsung.com>
Sat, 31 May 2014 04:01:57 +0000 (13:01 +0900)
311 files changed:
Config.in [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
configure [new file with mode: 0644]
generated/README.txt [new file with mode: 0644]
generated/help.h [new file with mode: 0644]
generated/portability.h [new file with mode: 0644]
kconfig/Makefile [new file with mode: 0644]
kconfig/conf.c [new file with mode: 0644]
kconfig/confdata.c [new file with mode: 0644]
kconfig/expr.c [new file with mode: 0644]
kconfig/expr.h [new file with mode: 0644]
kconfig/lex.zconf.c_shipped [new file with mode: 0644]
kconfig/lkc.h [new file with mode: 0644]
kconfig/lkc_proto.h [new file with mode: 0644]
kconfig/lxdialog/BIG.FAT.WARNING [new file with mode: 0644]
kconfig/lxdialog/check-lxdialog.sh [new file with mode: 0644]
kconfig/lxdialog/checklist.c [new file with mode: 0644]
kconfig/lxdialog/dialog.h [new file with mode: 0644]
kconfig/lxdialog/inputbox.c [new file with mode: 0644]
kconfig/lxdialog/menubox.c [new file with mode: 0644]
kconfig/lxdialog/textbox.c [new file with mode: 0644]
kconfig/lxdialog/util.c [new file with mode: 0644]
kconfig/lxdialog/yesno.c [new file with mode: 0644]
kconfig/mconf.c [new file with mode: 0644]
kconfig/menu.c [new file with mode: 0644]
kconfig/symbol.c [new file with mode: 0644]
kconfig/util.c [new file with mode: 0644]
kconfig/zconf.hash.c_shipped [new file with mode: 0644]
kconfig/zconf.tab.c_shipped [new file with mode: 0644]
lib/args.c [new file with mode: 0644]
lib/ash.h [new file with mode: 0644]
lib/ash_arith.h [new file with mode: 0644]
lib/ash_eval.h [new file with mode: 0644]
lib/ash_jobs.h [new file with mode: 0644]
lib/ash_lexyyc [new file with mode: 0644]
lib/ash_options.h [new file with mode: 0644]
lib/ash_shell.h [new file with mode: 0644]
lib/ash_syntax.h [new file with mode: 0644]
lib/ash_token.def [new file with mode: 0644]
lib/ash_ytab.h [new file with mode: 0644]
lib/ash_ytabc [new file with mode: 0644]
lib/awk.h [new file with mode: 0644]
lib/awk_ytabc [new file with mode: 0644]
lib/bunzip.c [new file with mode: 0644]
lib/dirtree.c [new file with mode: 0644]
lib/e2fs.c [new file with mode: 0644]
lib/getmountlist.c [new file with mode: 0644]
lib/help.c [new file with mode: 0644]
lib/lib.c [new file with mode: 0644]
lib/lib.h [new file with mode: 0644]
lib/llist.c [new file with mode: 0644]
lib/loop.c [new file with mode: 0644]
lib/net.c [new file with mode: 0644]
lib/password.c [new file with mode: 0644]
lib/portability.c [new file with mode: 0644]
lib/portability.h [new file with mode: 0644]
lib/proto.h [new file with mode: 0644]
lib/xregcomp.c [new file with mode: 0644]
lib/xregcomp.h [new file with mode: 0644]
main.c [new file with mode: 0644]
packaging/bin.links [new file with mode: 0644]
packaging/klogd.service [new file with mode: 0644]
packaging/sbin.links [new file with mode: 0644]
packaging/syslogd.manifest [new file with mode: 0644]
packaging/syslogd.service [new file with mode: 0644]
packaging/toybox.manifest [new file with mode: 0644]
packaging/toybox.spec [new file with mode: 0644]
packaging/toybox_tizen.config [new file with mode: 0644]
packaging/usrbin.links [new file with mode: 0644]
packaging/usrsbin.links [new file with mode: 0644]
scripts/bloatcheck [new file with mode: 0644]
scripts/config2help.py [new file with mode: 0755]
scripts/config2help.sh [new file with mode: 0755]
scripts/findglobals.sh [new file with mode: 0755]
scripts/genconfig.sh [new file with mode: 0755]
scripts/install.c [new file with mode: 0644]
scripts/install.sh [new file with mode: 0755]
scripts/make.sh [new file with mode: 0755]
scripts/minicom.sh [new file with mode: 0755]
scripts/mkstatus.py [new file with mode: 0755]
scripts/showasm [new file with mode: 0644]
scripts/test.sh [new file with mode: 0755]
scripts/test/basename.test [new file with mode: 0644]
scripts/test/cat.test [new file with mode: 0644]
scripts/test/chgrp.test [new file with mode: 0644]
scripts/test/cksum.test [new file with mode: 0644]
scripts/test/cmp.test [new file with mode: 0644]
scripts/test/cp.test [new file with mode: 0644]
scripts/test/dirname.test [new file with mode: 0644]
scripts/test/echo.test [new file with mode: 0644]
scripts/test/expand.test [new file with mode: 0644]
scripts/test/expr.test [new file with mode: 0644]
scripts/test/find.test [new file with mode: 0644]
scripts/test/head.test [new file with mode: 0644]
scripts/test/losetup.test [new file with mode: 0644]
scripts/test/md5sum.test [new file with mode: 0644]
scripts/test/mkdir.test [new file with mode: 0644]
scripts/test/mkfifo.test [new file with mode: 0644]
scripts/test/modinfo.test [new file with mode: 0644]
scripts/test/pwd.test [new file with mode: 0644]
scripts/test/readlink.test [new file with mode: 0644]
scripts/test/rev.test [new file with mode: 0644]
scripts/test/rmdir.test [new file with mode: 0644]
scripts/test/seq.test [new file with mode: 0644]
scripts/test/sha1sum.test [new file with mode: 0644]
scripts/test/sort.test [new file with mode: 0644]
scripts/test/split.test [new file with mode: 0644]
scripts/test/tac.test [new file with mode: 0644]
scripts/test/tail.test [new file with mode: 0644]
scripts/test/testing.sh [new file with mode: 0644]
scripts/test/touch.test [new file with mode: 0644]
scripts/test/uudecode.test [new file with mode: 0644]
scripts/test/uuencode.test [new file with mode: 0644]
scripts/test/wc.test [new file with mode: 0644]
scripts/test/xargs.test [new file with mode: 0644]
toynet.h [new file with mode: 0644]
toys.h [new file with mode: 0644]
toys/e2fs.h [new file with mode: 0644]
toys/lsb/README [new file with mode: 0644]
toys/lsb/addgroup.c [new file with mode: 0644]
toys/lsb/adduser.c [new file with mode: 0644]
toys/lsb/dmesg.c [new file with mode: 0644]
toys/lsb/hostname.c [new file with mode: 0644]
toys/lsb/killall.c [new file with mode: 0644]
toys/lsb/md5sum.c [new file with mode: 0644]
toys/lsb/mknod.c [new file with mode: 0644]
toys/lsb/mktemp.c [new file with mode: 0644]
toys/lsb/mount.c [new file with mode: 0644]
toys/lsb/passwd.c [new file with mode: 0644]
toys/lsb/pidof.c [new file with mode: 0644]
toys/lsb/seq.c [new file with mode: 0644]
toys/lsb/sync.c [new file with mode: 0644]
toys/lsb/umount.c [new file with mode: 0644]
toys/other/README [new file with mode: 0644]
toys/other/arping.c [new file with mode: 0644]
toys/other/bzcat.c [new file with mode: 0644]
toys/other/catv.c [new file with mode: 0644]
toys/other/chattr.c [new file with mode: 0644]
toys/other/chroot.c [new file with mode: 0644]
toys/other/chvt.c [new file with mode: 0644]
toys/other/clear.c [new file with mode: 0644]
toys/other/count.c [new file with mode: 0644]
toys/other/cttyhack.c [new file with mode: 0644]
toys/other/depmod.c [new file with mode: 0644]
toys/other/dos2unix.c [new file with mode: 0644]
toys/other/dumpkmap.c [new file with mode: 0644]
toys/other/dumpleases.c [new file with mode: 0644]
toys/other/eject.c [new file with mode: 0644]
toys/other/envdir.c [new file with mode: 0644]
toys/other/expr.c [new file with mode: 0644]
toys/other/fdisk.c [new file with mode: 0644]
toys/other/find.c [new file with mode: 0644]
toys/other/free.c [new file with mode: 0644]
toys/other/fsck.c [new file with mode: 0644]
toys/other/fsync.c [new file with mode: 0644]
toys/other/ftpget.c [new file with mode: 0644]
toys/other/getty.c [new file with mode: 0644]
toys/other/halt.c [new file with mode: 0644]
toys/other/hello.c [new file with mode: 0644]
toys/other/help.c [new file with mode: 0644]
toys/other/hexdump.c [new file with mode: 0644]
toys/other/hostid.c [new file with mode: 0644]
toys/other/ifconfig.c [new file with mode: 0644]
toys/other/ifconfig.h [new file with mode: 0644]
toys/other/ifup.c [new file with mode: 0644]
toys/other/init.c [new file with mode: 0644]
toys/other/inotifyd.c [new file with mode: 0644]
toys/other/insmod.c [new file with mode: 0644]
toys/other/iostat.c [new file with mode: 0644]
toys/other/klogd.c [new file with mode: 0644]
toys/other/login.c [new file with mode: 0644]
toys/other/losetup.c [new file with mode: 0644]
toys/other/lsattr.c [new file with mode: 0644]
toys/other/lsmod.c [new file with mode: 0644]
toys/other/lsusb.c [new file with mode: 0644]
toys/other/mdev.c [new file with mode: 0644]
toys/other/mesg.c [new file with mode: 0644]
toys/other/mkpasswd.c [new file with mode: 0644]
toys/other/mkswap.c [new file with mode: 0644]
toys/other/modinfo.c [new file with mode: 0644]
toys/other/modprobe.c [new file with mode: 0644]
toys/other/mountpoint.c [new file with mode: 0644]
toys/other/netcat.c [new file with mode: 0644]
toys/other/netstat.c [new file with mode: 0644]
toys/other/nslookup.c [new file with mode: 0644]
toys/other/oneit.c [new file with mode: 0644]
toys/other/pgrep.c [new file with mode: 0644]
toys/other/ping.c [new file with mode: 0644]
toys/other/ping6.c [new file with mode: 0644]
toys/other/printenv.c [new file with mode: 0644]
toys/other/readahead.c [new file with mode: 0644]
toys/other/readlink.c [new file with mode: 0644]
toys/other/realpath.c [new file with mode: 0644]
toys/other/reboot.c [new file with mode: 0644]
toys/other/rev.c [new file with mode: 0644]
toys/other/rmmod.c [new file with mode: 0644]
toys/other/route.c [new file with mode: 0644]
toys/other/setsid.c [new file with mode: 0644]
toys/other/stat.c [new file with mode: 0644]
toys/other/swapoff.c [new file with mode: 0644]
toys/other/swapon.c [new file with mode: 0644]
toys/other/switch_root.c [new file with mode: 0644]
toys/other/syslogd.c [new file with mode: 0644]
toys/other/tac.c [new file with mode: 0644]
toys/other/taskset.c [new file with mode: 0644]
toys/other/telnet.c [new file with mode: 0644]
toys/other/tftp.c [new file with mode: 0644]
toys/other/traceroute.c [new file with mode: 0644]
toys/other/truncate.c [new file with mode: 0644]
toys/other/udhcpc.c [new file with mode: 0644]
toys/other/udhcpd.c [new file with mode: 0644]
toys/other/unshare.c [new file with mode: 0644]
toys/other/uptime.c [new file with mode: 0644]
toys/other/usleep.c [new file with mode: 0644]
toys/other/vconfig.c [new file with mode: 0644]
toys/other/vmstat.c [new file with mode: 0644]
toys/other/w.c [new file with mode: 0644]
toys/other/wget.c [new file with mode: 0644]
toys/other/which.c [new file with mode: 0644]
toys/other/whoami.c [new file with mode: 0644]
toys/other/yes.c [new file with mode: 0644]
toys/pending/README [new file with mode: 0644]
toys/pending/getkey.c_1 [new file with mode: 0644]
toys/pending/last.c_1 [new file with mode: 0644]
toys/pending/mdev.c [new file with mode: 0644]
toys/pending/mke2fs.c [new file with mode: 0644]
toys/pending/more.c_1 [new file with mode: 0644]
toys/pending/nbd_client.c [new file with mode: 0644]
toys/pending/reset.c [new file with mode: 0644]
toys/pending/sed.c [new file with mode: 0644]
toys/pending/sh.c [new file with mode: 0644]
toys/pending/top.c_1 [new file with mode: 0644]
toys/pending/xzcat.c [new file with mode: 0644]
toys/posix/README [new file with mode: 0644]
toys/posix/ash.c [new file with mode: 0644]
toys/posix/awk.c [new file with mode: 0644]
toys/posix/basename.c [new file with mode: 0644]
toys/posix/cal.c [new file with mode: 0644]
toys/posix/cat.c [new file with mode: 0644]
toys/posix/chgrp.c [new file with mode: 0644]
toys/posix/chmod.c [new file with mode: 0644]
toys/posix/cksum.c [new file with mode: 0644]
toys/posix/cmp.c [new file with mode: 0644]
toys/posix/comm.c [new file with mode: 0644]
toys/posix/cp.c [new file with mode: 0644]
toys/posix/cut.c [new file with mode: 0644]
toys/posix/date.c [new file with mode: 0644]
toys/posix/dd.c [new file with mode: 0644]
toys/posix/df.c [new file with mode: 0644]
toys/posix/dirname.c [new file with mode: 0644]
toys/posix/du.c [new file with mode: 0644]
toys/posix/echo.c [new file with mode: 0644]
toys/posix/egrep.c [new file with mode: 0644]
toys/posix/env.c [new file with mode: 0644]
toys/posix/expand.c [new file with mode: 0644]
toys/posix/false.c [new file with mode: 0644]
toys/posix/fgrep.c [new file with mode: 0644]
toys/posix/grep.c [new file with mode: 0644]
toys/posix/head.c [new file with mode: 0644]
toys/posix/id.c [new file with mode: 0644]
toys/posix/kill.c [new file with mode: 0644]
toys/posix/link.c [new file with mode: 0644]
toys/posix/ln.c [new file with mode: 0644]
toys/posix/logger.c [new file with mode: 0644]
toys/posix/logname.c [new file with mode: 0644]
toys/posix/ls.c [new file with mode: 0644]
toys/posix/mkdir.c [new file with mode: 0644]
toys/posix/mkfifo.c [new file with mode: 0644]
toys/posix/mv.c [new file with mode: 0644]
toys/posix/nice.c [new file with mode: 0644]
toys/posix/nohup.c [new file with mode: 0644]
toys/posix/od.c [new file with mode: 0644]
toys/posix/patch.c [new file with mode: 0644]
toys/posix/printf.c [new file with mode: 0644]
toys/posix/pwd.c [new file with mode: 0644]
toys/posix/rm.c [new file with mode: 0644]
toys/posix/rmdir.c [new file with mode: 0644]
toys/posix/sleep.c [new file with mode: 0644]
toys/posix/sort.c [new file with mode: 0644]
toys/posix/split.c [new file with mode: 0644]
toys/posix/stty.c [new file with mode: 0644]
toys/posix/tail.c [new file with mode: 0644]
toys/posix/tee.c [new file with mode: 0644]
toys/posix/test.c [new file with mode: 0644]
toys/posix/time.c [new file with mode: 0644]
toys/posix/touch.c [new file with mode: 0644]
toys/posix/true.c [new file with mode: 0644]
toys/posix/tty.c [new file with mode: 0644]
toys/posix/uname.c [new file with mode: 0644]
toys/posix/uniq.c [new file with mode: 0644]
toys/posix/unlink.c [new file with mode: 0644]
toys/posix/uudecode.c [new file with mode: 0644]
toys/posix/uuencode.c [new file with mode: 0644]
toys/posix/wc.c [new file with mode: 0644]
toys/posix/who.c [new file with mode: 0644]
toys/posix/xargs.c [new file with mode: 0644]
www/about.html [new file with mode: 0644]
www/code.html [new file with mode: 0644]
www/design.html [new file with mode: 0644]
www/ext2.html [new file with mode: 0644]
www/footer.html [new file with mode: 0644]
www/header.html [new file with mode: 0644]
www/index.html [new symlink]
www/license.html [new file with mode: 0644]
www/news.html [new file with mode: 0644]
www/oldnews.html [new file with mode: 0644]
www/roadmap.html [new file with mode: 0644]
www/status.html [new file with mode: 0644]
www/toycans.png [new file with mode: 0644]

diff --git a/Config.in b/Config.in
new file mode 100644 (file)
index 0000000..ae050b7
--- /dev/null
+++ b/Config.in
@@ -0,0 +1,66 @@
+mainmenu "ToyBox Configuration"
+
+
+source generated/Config.probed
+source generated/Config.in
+
+comment ""
+
+menu "Toybox global settings"
+
+config TOYBOX
+       bool
+       default n
+       help
+         usage: toybox [command] [arguments...]
+
+         With no arguments, shows available commands. First argument is
+         name of a command to run, followed by any arguments to that command.
+
+config TOYBOX_SUID
+       bool "SUID support"
+       default y
+       help
+         Support for the Set User ID bit, to install toybox suid root and drop
+         permissions for commands which do not require root access. To use
+         this change ownership of the file to the root user and set the suid
+         bit in the file permissions:
+
+         chown root:root toybox; chmod +s toybox
+
+config TOYBOX_FLOAT
+       bool "Floating point support"
+       default y
+       help
+         Include floating point support infrastructure and commands that
+         require it.
+
+config TOYBOX_HELP
+       bool "Help messages"
+       default y
+       help
+         Include help text for each command.
+
+config TOYBOX_I18N
+       bool "Internationalization support"
+       default y
+       help
+         Support for UTF-8 character sets, and some locale support.
+
+config TOYBOX_FREE
+       bool "Free memory unnecessarily"
+       default n
+       help
+         When a program exits, the operating system will clean up after it
+         (free memory, close files, etc). To save size, toybox usually relies
+         on this behavior. If you're running toybox under a debugger or
+         without a real OS (ala newlib+libgloss), enable this to make toybox
+         clean up after itself.
+
+config TOYBOX_DEBUG
+       bool "Debugging tests"
+       default n
+       help
+         Enable extra checks for debugging purposes.
+
+endmenu
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..e683871
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,16 @@
+Copyright (C) 2006, 2013 by Rob Landley <rob@landley.net>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+(Note: some build infrastructure in the scripts directory is still GPL,
+cleaning that out is a TODO item, but it doesn't affect the resulting
+binary.)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..bbab258
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,68 @@
+# Makefile for toybox.
+# Copyright 2006 Rob Landley <rob@landley.net>
+
+all: toybox
+
+toybox toybox_unstripped: .config *.[ch] lib/*.[ch] toys/*.h toys/*/*.c scripts/*.sh
+       scripts/make.sh
+
+.PHONY: clean distclean baseline bloatcheck install install_flat \
+       uinstall uninstall_flat test tests help scripts/test
+
+include kconfig/Makefile
+
+$(KCONFIG_TOP): generated/Config.in
+generated/Config.in: toys/*/*.c scripts/genconfig.sh
+       scripts/genconfig.sh
+
+HOSTCC?=cc
+
+# Development targets
+baseline: toybox_unstripped
+       @cp toybox_unstripped toybox_old
+
+bloatcheck: toybox_old toybox_unstripped
+       @scripts/bloatcheck toybox_old toybox_unstripped
+
+instlist: toybox
+       $(HOSTCC) -I . scripts/install.c -o instlist
+
+install_flat: instlist
+       scripts/install.sh --symlink --force
+
+install:
+       scripts/install.sh --long --symlink --force
+
+uninstall_flat: instlist
+       scripts/install.sh --uninstall
+
+uninstall:
+       scripts/install.sh --long --uninstall
+
+clean::
+       rm -rf toybox toybox_unstripped generated/config.h generated/Config.in \
+               generated/newtoys.h generated/globals.h instlist testdir \
+               generated/Config.probed
+
+distclean: clean
+       rm -f toybox_old .config* generated/help.h
+
+test: tests
+
+tests:
+       scripts/test.sh
+
+help::
+       @echo  '  toybox          - Build toybox.'
+       @echo  '  baseline        - Create busybox_old for use by bloatcheck.'
+       @echo  '  bloatcheck      - Report size differences between old and current versions'
+       @echo  '  test            - Run test suite against compiled commands.'
+       @echo  '  clean           - Delete temporary files.'
+       @echo  "  distclean       - Delete everything that isn't shipped."
+       @echo  '  install_flat    - Install toybox into $$PREFIX directory.'
+       @echo  '  install         - Install toybox into subdirectories of $$PREFIX.'
+       @echo  '  uninstall_flat  - Remove toybox from $$PREFIX directory.'
+       @echo  '  uninstall       - Remove toybox from subdirectories of $$PREFIX.'
+       @echo  ''
+       @echo  'example: CFLAGS="--static" CROSS_COMPILE=armv5l- make defconfig toybox install'
+       @echo  ''
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..4fc2472
--- /dev/null
+++ b/README
@@ -0,0 +1,91 @@
+Toybox: all-in-one Linux command line.
+
+--- Getting started
+
+You can download static binaries for various targets from:
+
+  http://landley.net/toybox/bin
+
+The special name "." indicates the current directory (just like ".." means
+the parent directory), and you can run a program that isn't in the $PATH by
+specifying a path to it, so this should work:
+
+  wget http://landley.net/bin/toybox-x86_64
+  chmod +x toybox-x86_64
+  ./toybox-x86_64 echo hello world
+
+--- Building toybox
+
+Type "make help" for build instructions.
+
+Usually you want something like:
+
+  make defconfig
+  CFLAGS="--static" CROSS_COMPILE=armv5l- make toybox
+  PREFIX=/path/to/root/filesystem make install
+
+The CROSS_COMPILE argument is optional, and without it builds a version of
+toybox to run on the current machine. Cross compiling requires an appropriately
+prefixed cross compiler toolchain, several example toolchains are available at:
+
+  http;//landley.net/aboriginal/bin
+
+For the "CROSS_COMPILE=armv5l-" example above, download
+cross-compiler-armv5l.tar.bz2, extract it, and add its "bin" subdirectory to
+your $PATH. (And yes, the trailing - is significant, because the prefix
+includes a dash.)
+
+For more about cross compiling, see:
+
+  http://landley.net/writing/docs/cross-compiling.html
+  http://landley.net/aboriginal/architectures.html
+
+--- Using toybox
+
+The toybox build produces a multicall binary, a "swiss-army-knife" program
+that acts differently depending on the name it was called by (cp, mv, cat...).
+Installing toybox adds symlinks for each command name to the $PATH.
+
+The special "toybox" command treats its first argument as the command to run.
+With no arguments, it lists available commands. This allows you to use toybox
+without installing it. This is the only command that can have an arbitrary
+suffix (hence "toybox-armv5l").
+
+The "help" command provides information about each command (ala "help cat").
+
+--- Configuring toybox
+
+It works like the Linux kernel: allnoconfig, defconfig, and menuconfig edit
+a ".config" file that selects which features to include in the resulting
+binary.
+
+The maximum sane configuration is "make defconfig": allyesconfig isn't
+recommended for toybox because it enables unfinished commands and debug code.
+
+--- Creating a Toybox-based Linux system
+
+Toybox is not a complete operating system, it's a program that runs under
+an operating system. Booting a simple system to a shell prompt requires
+three packages: an operating system kernel (Linux) to drive the hardware,
+a program for the system to run (toybox), and a C library to tie them
+together (toybox has been tested with musl, uClibc, and glibc, on Android
+systems musl is recommended).</p>
+
+<p>The C library is part of a "toolchain", which is an integrated suite
+of compiler, assembler, and linker, plus the standard headers and libraries
+necessary to build C programs.</p>
+
+
+ Static linking (with the --static option)
+copies the shared library contents into the program, resulting in
+larger but more portable programs. Dynamically linked programs (the default)
+Otherwise, the
+"dynamically" linked programs require the
+library to be present on the target system ("man ldd" and "man ld.so" for
+details) statically linked programs do not.</p>
+
+Toybox is not a kernel, it needs Linux to drive the hardware.
+
+An example toybox-based system is Aboriginal Linux:
+
+  http://landley.net/aboriginal
diff --git a/configure b/configure
new file mode 100644 (file)
index 0000000..bf2561b
--- /dev/null
+++ b/configure
@@ -0,0 +1,16 @@
+# Toybox configuration file.
+
+# This sets environment variables used by scripts/make.sh
+
+# A synonym.
+[ -z "$CROSS_COMPILE" ] && CROSS_COMPILE="$CROSS"
+[ -z "$CFLAGS" ] && CFLAGS="-Wall -Wundef -Wno-char-subscripts"
+# Required for our expected ABI. we're 8-bit clean thus "char" must be unsigned.
+CFLAGS="$CFLAGS -funsigned-char"
+
+[ -z "$OPTIMIZE" ] && OPTIMIZE="-Os -ffunction-sections -fdata-sections -Wl,--gc-sections"
+[ -z "$CC" ] && CC=cc
+[ -z "$STRIP" ] && STRIP=strip
+
+# If HOSTCC needs CFLAGS, add them to the variable ala HOSTCC="blah-cc --static"
+[ -z "$HOSTCC" ] && HOSTCC=gcc
diff --git a/generated/README.txt b/generated/README.txt
new file mode 100644 (file)
index 0000000..93a1039
--- /dev/null
@@ -0,0 +1,17 @@
+All the files in this directory except this README are generated by the
+build.  (See scripts/make.sh)
+
+config.h: CFG_COMMAND and USE_COMMAND() macros set by menuconfig via .config.
+
+Config.in: Kconfig entries for each command.  Included by top level Config.in.
+           The help text in here is used to generated help.h
+
+help.h: Help text strings for use by "help" command.  Building this file
+        requires python on the host system, so the prebuilt file is shipped
+        in the build tarball to avoid requiring python to build toybox.
+
+newtoys.h: List of NEWTOY() or OLDTOY() macros for all available commands.
+           Associates command_main() functions with command names, provides
+           option string for command line parsing (see lib/args.c), specifies
+           where to install each command and whether toysh should fork before
+           calling it.
diff --git a/generated/help.h b/generated/help.h
new file mode 100644 (file)
index 0000000..86a5704
--- /dev/null
@@ -0,0 +1,206 @@
+#define help_addgroup "usage: addgroup [-g GID] [USER] GROUP\n\nAdd a group or add a user to a group\n\n-g GID  Group id\n-S      Create a system group\n"
+#define help_adduser "usage: adduser [OPTIONS] USER [GROUP]\n\nCreate new user, or add USER to GROUP\n\n-h DIR    Home directory\n-g GECOS  GECOS field\n-s SHELL  Login shell\n-G GRP    Add user to existing group\n-S        Create a system user\n-D        Don't assign a password\n-H        Don't create home directory\n-u UID    User id\n"
+#define help_dmesg "usage: dmesg [-n level] [-s bufsize] | -c\n\nPrint or control the kernel ring buffer.\n\n-n  Set kernel logging level (1-9).\n-s  Size of buffer to read (in bytes), default 16384.\n-c  Clear the ring buffer after printing.\n"
+#define help_hostname "usage: hostname [newname]\n-s  Short\n-i  Addresses for the hostname\n-d  DNS domain name\n-f  Fully qualified domain name\n-F FILE Use FILE's content as hostname\nGet/Set the current hostname/DNS Domain name\n"
+#define help_killall "usage: killall [-l] [-iqv] [-SIG] PROCESS_NAME...\n\nSend a signal (default: TERM) to all processes with the given names.\n\n-i  ask for confirmation before killing\n-l  print list of all available signals\n-q  don't print any warnings or error messages\n-v  report if the signal was successfully sent\n"
+#define help_md5sum "usage: md5sum [FILE]...\n\nCalculate md5 hash for each input file, reading from stdin if none.\nOutput one hash (16 hex digits) for each input file, followed by\nfilename.\n"
+#define help_md5sum_sha1sum "usage: sha1sum [FILE]...\n\ncalculate sha1 hash for each input file, reading from stdin if one.\nOutput one hash (20 hex digits) for each input file, followed by\nfilename.\n"
+#define help_mknod "usage: mknod NAME TYPE [MAJOR MINOR]\n\nCreate a special file NAME with a given type, possible types are\nb   block device\nc or u      character device\np   named pipe (ignores MAJOR/MINOR)\n"
+#define help_mktemp "usage: mktemp [-dq] [-p DIR] [TEMPLATE]\n\nSafely create new file and print its name. Default TEMPLATE is\n/tmp/tmp.XXXXXX and each trailing X is replaced with random char.\n\n-d, --directory        Create directory instead of file\n-p DIR, --tmpdir=DIR   Put new file in DIR\n-q                     Quiet\n"
+#define help_mount "usage: mount [-arw] [-t type] [-o OPT] [-O OPT] [dev] [dir]\n\n-a        Mount all filesystems in fstab.\n-f        Dry run.\n-v        Verbose\n-t FSTYPE filesystem type\n-r        Read-only mount\n-w        Read-Write mount(default)\n-o OPT    options\n-O OPT    options, mount only filestsystems with these options (used with -a only)\n\nOPT:\nloop    loop device\n[a]sync   Writes are [a]synchronous\n[no]atime   Disable/enable updates to inode access times\n[no]diratime  Disable/enable atime updates to directories\n[no]relatime  Disable/enable atime updates relative to modification time\n[no]dev   (Dis)allow use of special device files\n[no]exec  (Dis)allow use of executable files\n[no]suid  (Dis)allow set-user-id-root programs\n[r]shared   Convert [recursively] to a shared subtree\n[r]slave  Convert [recursively] to a slave subtree\n[r]private  Convert [recursively] to a private subtree\n[un]bindable  Make mount point [un]able to be bind mounted\n[r]bind   Bind a file or directory [recursively] to another location\nmove    Relocate an existing mount point\nremount   Remount a mounted filesystem, changing flags\nro/rw     Same as -r/-w\n\n"
+#define help_passwd "usage: passwd [-a ALGO] [-d] [-l] [-u] <account name>\n\nupdate user’s authentication tokens. Default : current user\n\n-a ALGO     Encryption method (des, md5, sha256, sha512) default: des\n-d          Set password to ''\n-l          Lock (disable) account\n-u          Unlock (enable) account\n"
+#define help_pidof "usage: pidof [-s] [-o omitpid[,omitpid..]] [NAME]...\n\nPrint the PIDs of all processes with the given names.\n-s  single shot, only return one pid.\n-o  omits processes with specified PID\n"
+#define help_seq "usage: seq [-f fmt_str] [-s sep_str] [first] [increment] last\n\nCount from first to last, by increment. Omitted arguments default\nto 1. Two arguments are used as first and last. Arguments can be\nnegative or floating point.\n\n-f  Use fmt_str as a floating point format string\n-s  Use sep_str as separator, default is a newline character\n"
+#define help_sync "usage: sync\n\nWrite pending cached data to disk (synchronize), blocking until done.\n"
+#define help_umount "usage: umount [-varlfd] [-t fstypes] [dir...]\n\n-a  Unmount all file systems\n-v  Verbose mode\n-r  Try to remount devices as read-only if mount is busy\n-l  Lazy umount (detach filesystem)\n-f  Force umount (i.e., unreachable NFS server)\n-d  Free loop device if it has been used\n-t FSTYPE Filesystem types to be acted upon (comma separated)\n\nunmount the filesystem.\n"
+#define help_arping "Usage: arping [-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP\n\nSend ARP requests/replies\n\n-f         Quit on first ARP reply\n-q         Quiet\n-b         Keep broadcasting, don't go unicast\n-D         Duplicated address detection mode\n-U         Unsolicited ARP mode, update your neighbors\n-A         ARP answer mode, update your neighbors\n-c N       Stop after sending N ARP requests\n-w TIMEOUT Time to wait for ARP reply, seconds\n-I IFACE   Interface to use (default eth0)\n-s SRC_IP  Sender IP address\nDST_IP    Target IP address\n"
+#define help_bzcat "usage: bzcat [filename...]\n\nDecompress listed files to stdout. Use stdin if no files listed.\n"
+#define help_catv "usage: catv [-evt] [filename...]\n\nDisplay nonprinting characters as escape sequences. Use M-x for\nhigh ascii characters (>127), and ^x for other nonprinting chars.\n\n-e  Mark each newline with $\n-t  Show tabs as ^I\n-v  Don't use ^x or M-x escapes.\n"
+#define help_chattr "usage: chattr [-R] [-+=AacDdijsStTu] [-v version] [Files...]\n\nChange file attributes on a Linux second extended file system.\n\nOperators:\n'-' Remove attributes.\n'+' Add attributes.\n'=' Set attributes.\n\nAttributes:\nA  Don't track atime.\na  Append mode only.\nc  Enable compress.\nD  Write dir contents synchronously.\nd  Don't backup with dump.\ni  Cannot be modified (immutable).\nj  Write all data to journal first.\ns  Zero disk storage when deleted.\nS  Write file contents synchronously.\nt  Disable tail-merging of partial blocks with other files.\nu  Allow file to be undeleted.\n-R Recurse.\n-v Set the file's version/generation number.\n"
+#define help_chroot "usage: chroot NEWPATH [commandline...]\n\nRun command within a new root directory. If no command, run /bin/sh.\n"
+#define help_chvt "usage: chvt N\n\nChange to virtual terminal number N. (This only works in text mode.)\n\nVirtual terminals are the Linux VGA text mode displays, ordinarily\nswitched between via alt-F1, alt-F2, etc. Use ctrl-alt-F1 to switch\nfrom X to a virtual terminal, and alt-F6 (or F7, or F8) to get back.\n"
+#define help_clear "Clear the screen.\n"
+#define help_count "usage: count\n\nCopy stdin to stdout, displaying simple progress indicator to stderr.\n"
+#define help_cttyhack "usage: cttyhack PROG ARGS\n\ncttyhack will try to get a controlling terminal and execute the PROG.\n"
+#define help_depmod "usage: depmod [-a][-n] [-b basedir] version MODULE...\n-a          Probe all modules, Default\n-n          Write the dependency file on stdout only\n-b Basedir  Basedir needed  when modules are in staging state\n"
+#define help_dos2unix "usage: dos2unix/unix2dos [file...]\n\nConvert newline format between dos (\\r\\n) and unix (just \\n)\nIf no files listed copy from stdin, \"-\" is a synonym for stdin.\n"
+#define help_dumpkmap "usage: dumpkmap > keymap\n\nPrint a binary keyboard translation table to stdout.\n"
+#define help_dumpleases "Usage: dumpleases [-r|-a] [-f LEASEFILE]\n\nDisplay DHCP leases granted by udhcpd\n-f FILE,  Lease file\n-r        Show remaining time\n-a        Show expiration time\n"
+#define help_eject "usage: eject [-s] [-t] [-T] [...]\n\nEject DEVICE or default /dev/cdrom\n\n-s  SCSI device\n-t  Close tray\n-T  Open/close tray (toggle).\n"
+#define help_envdir "usage: envdir [d] [child]\n\nd is a single argument. child consists of one or more arguments.\n\nenvdir sets various environment variables as specified by files\nin the directory named d. It then runs child.\n"
+#define help_expr "usage: expr EXPRESSION\n\nPrint the value of EXPRESSION to stdout\nEXPRESSION may be:\nARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\nARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\nARG1 < ARG2 1 if ARG1 is less than ARG2, else 0. Similarly:\nARG1 <= ARG2\nARG1 = ARG2\nARG1 != ARG2\nARG1 >= ARG2\nARG1 > ARG2\nARG1 + ARG2 Sum of ARG1 and ARG2. Similarly:\nARG1 - ARG2\nARG1 * ARG2\nARG1 / ARG2\nARG1 % ARG2\nSTRING : REGEXP   Anchored pattern match of REGEXP in STRING\nmatch STRING REGEXP Same as STRING : REGEXP\nsubstr STRING POS LENGTH Substring of STRING, POS counted from 1\nindex STRING CHARS  Index in STRING where any CHARS is found, or 0\nlength STRING   Length of STRING\nquote TOKEN   Interpret TOKEN as a string, even if\nit is a keyword like 'match' or an\noperator like '/'\n(EXPRESSION)  Value of EXPRESSION\n\nBeware that many operators need to be escaped or quoted for shells.\nComparisons are arithmetic if both ARGs are numbers, else\nlexicographical. Pattern matches return the string matched between\n\\( and \\) or null; if \\( and \\) are not used, they return the number\nof characters matched or 0.\n"
+#define help_fdisk "usage: fdisk [-lu] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SECTSZ] DISK\n\nChange partition table\n\n-u            Start and End are in sectors (instead of cylinders)\n-l            Show partition table for each DISK, then exit\n-b size       sector size (512, 1024, 2048 or 4096)\n-C CYLINDERS  Set number of cylinders/heads/sectors\n-H HEADS\n-S SECTORS\n"
+#define help_find "Usage: find [PATH]... [OPTIONS] [ACTIONS]\n\nSearch for files and perform actions on them.\nFirst failed action stops processing of current file.\nDefaults: PATH is current directory, action is '-print'\n\n-follow             Follow symlinks\n\n-xdev               Don't descend directories on other filesystems\n\n-maxdepth N         Descend at most N levels. -maxdepth 0 applies\nactions to command line arguments only\n\n-mindepth N         Don't act on first N levels\n\n-depth               Act on directory after traversing it\n\nActions:\n( ACTIONS )         Group actions for -o / -a\n\n! ACT               Invert ACT's success/failure\n\nACT1 [-a] ACT2      If ACT1 fails, stop, else do ACT2\n\nACT1 -o ACT2        If ACT1 succeeds, stop, else do ACT2\n\n-name PATTERN       Match file name (w/o directory name) to PATTERN\n\n-iname PATTERN      Case insensitive -name\n\n-path PATTERN       Match path to PATTERN\n\n-ipath PATTERN      Case insensitive -path\n\n-regex PATTERN      Match path to regex PATTERN\n\n-type X             File type is X (one of: f,d,l,b,c,...)\n\n-perm MASK          At least one mask bit (+MASK), all bits (-MASK),\nor exactly MASK bits are set in file's mode\n\n-mtime DAYS         mtime is greater than (+N), less than (-N),\nor exactly N days in the past\n\n-mmin MINS          mtime is greater than (+N), less than (-N),\nor exactly N minutes in the past\n\n-newer FILE         mtime is more recent than FILE's\n\n-inum N             File has inode number N\n\n-user NAME/ID       File is owned by given user\n\n-group NAME/ID      File is owned by given group\n\n-size N[bck]        File size is N (c:bytes,k:kbytes,b:512 bytes(def.))\n+/-N: file size is bigger/smaller than N\n\n-links N            Number of links is greater than (+N), less than (-N),\nor exactly N\n\n-prune              If current file is directory, don't descend into it\nIf none of the following actions is specified, -print is assumed\n\n-print              Print file name\n\n-print0             Print file name, NUL terminated\n\n-exec CMD ARG ;     Run CMD with all instances of {} replaced by\nfile name. Fails if CMD exits with nonzero\n\n-delete             Delete current file/directory. Turns on -depth option\n"
+#define help_free "usage: free [-bkmg]\n\nDisplay the total, free and used amount of physical memory and\nswap space.\n\n-bkmg       Output in bytes (default), KB, MB or GB\n"
+#define help_fsck "usage: fsck [-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]...\n\n-A  Walk /etc/fstab and check all filesystems\n-N  Don't execute, just show what would be done\n-P  With -A, check filesystems in parallel\n-R  With -A, skip the root filesystem\n-T  Don't show title on startup\n-V  Verbose\n-C n  Write status information to specified filedescriptor\n-t TYPE  List of filesystem types to check\n"
+#define help_fsync "usage: fsync [d] [FILE...]\n\nSynchronize a file's in-core state with storage device.\n\n-d Avoid syncing metadata.\n"
+#define help_ftpget "usage: ftpget [-c -v -u username -p password P PortNumber] HOST_NAME [LOCAL_FILENAME] REMOTE_FILENAME\nusage: ftpput [-v -u username -p password P PortNumber] HOST_NAME [REMOTE_FILENAME] LOCAL_FILENAME\n\nftpget - Get a remote file from FTP.\nftpput - Upload a local file on remote machine through FTP.\n\n-c Continue previous transfer.\n-v Verbose.\n-u User name.\n-p Password.\n-P Port Number.\n"
+#define help_getty "Usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]\n-h    Enable hardware RTS/CTS flow control\n-L    Set CLOCAL (ignore Carrier Detect state)\n-m    Get baud rate from modem's CONNECT status message\n-n    Don't prompt for login name\n-w    Wait for CR or LF before sending /etc/issue\n-i    Don't display /etc/issue\n-f ISSUE_FILE  Display ISSUE_FILE instead of /etc/issue\n-l LOGIN  Invoke LOGIN instead of /bin/login\n-t SEC    Terminate after SEC if no login name is read\n-I INITSTR  Send INITSTR before anything else\n-H HOST    Log HOST into the utmp file as the hostname\n"
+#define help_halt "usage: halt [-d delay] [-n] [-f][-w]\n\nhalt utility to halt the sytem.\n-d DELAY  waits DELAY seconds before halt.\n-n    overrides sync() call before halt.\n-f    Forces system HALT and bypasses init process.\n-w    Logs message in system logs.\n"
+#define help_hello "usage: hello [-a] [-b string] [-c number] [-d list] [-e count] [...]\n\nA hello world program.  You don't need this.\n\nMostly used as an example/skeleton file for adding new commands,\noccasionally nice to test kernel booting via \"init=/bin/hello\".\n"
+#define help_help "usage: help [command]\n\nShow usage information for toybox commands.\nRun \"toybox\" with no arguments for a list of available commands.\n"
+#define help_hexdump "\nusage: hexdump [-bCcdovxR] [-e format_string] [-f format_file] [-n length] [-s skip] [file ...]\n\nA program to dump any file into hexadecimal, octal, decimal or character format.\n\noptions:\n-b                One-byte octal display\n-C                Canonical hex+ASCII, 16 bytes per line\n-c                One-byte character display\n-d                Two-byte decimal display\n-e format_string  Specify a format string to be used for displaying data\n-f format_file    Specify a file that contains one or more newline separated format strings\n-n length         Interpret only length bytes of input\n-o                Two-byte octal display\n-s OFFSET         Skip OFFSET bytes from the beginning of the input\n-v                Display all input data\n-x                Two-byte hexadecimal display\n-R                Reverse of 'hexdump -Cv'\n\n"
+#define help_hostid "usage: hostid\n\nPrint the numeric identifier for the current host.\n"
+#define help_ifconfig "usage: ifconfig [-a] interface [address]\n\nConfigure network interface.\n\n[add ADDRESS[/PREFIXLEN]]\n[del ADDRESS[/PREFIXLEN]]\n[[-]broadcast [ADDRESS]] [[-]pointopoint [ADDRESS]]\n[netmask ADDRESS] [dstaddr ADDRESS]\n[outfill NN] [keepalive NN]\n[hw ether|infiniband ADDRESS] [metric NN] [mtu NN]\n[[-]trailers] [[-]arp] [[-]allmulti]\n[multicast] [[-]promisc] [txqueuelen NN] [[-]dynamic]\n[mem_start NN] [io_addr NN] [irq NN]\n[up|down] ...\n"
+#define help_ifup "usage: ifup [[anmvf] [i FILE]] [IFACE...]\nusage: ifdown [[anmvf] [i FILE]] [IFACE...]\n\nifup - Bring a network interface up.\nifdown - Take a network interface down.\n\n-a          De/configure all interfaces marked \"auto\".\n-i FILE     Use file for interface definitions.\n-n          Print out what would happen, but don't do it. (Note: It doesn't disable mapping).\n-m          Don't run any mapping.\n-v          Print out what would happen before doing it.\n-f          Force De/configuration.\n"
+#define help_init "usage: init\n\ninit the system.\n"
+#define help_inotifyd "usage: inotifyd PROG FILE[:mask] ...\n\nRun PROG on filesystem changes.\nWhen a filesystem event matching MASK occurs on FILEn,\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run.\nIf PROG is -, events are sent to stdout.\n\nEvents:\na   File is accessed\nc   File is modified\ne   Metadata changed\nw   Writable file is closed\n0   Unwritable file is closed\nr   File is opened\nD   File is deleted\nM   File is moved\nu   Backing fs is unmounted\no   Event queue overflowed\nx   File can't be watched anymore\nIf watching a directory:\nm   Subfile is moved into dir\ny   Subfile is moved out of dir\nn   Subfile is created\nd   Subfile is deleted\n\ninotifyd waits for PROG to exit.\nWhen x event happens for all FILEs, inotifyd exits.\n"
+#define help_insmod "usage: insmod MODULE [MODULE_OPTIONS]\n\nLoad the module named MODULE passing options if given.\n"
+#define help_iostat "Usage: iostat [-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]\n\nReport CPU and I/O statistics\n-c  Show CPU utilization\n-d  Show device utilization\n-t  Print current time\n-z  Omit devices with no activity\n-k  Use kb/s\n-m  Use Mb/s\n"
+#define help_klogd "usage: klogd [-n] [-c N]\n\n-c  N   Print to console messages more urgent than prio N (1-8)\"\n-n    Run in foreground.\n"
+#define help_login "usage: login [-p] [-h host] [[-f] username]\n\nEstablish a new session with the system.\n-p  Preserve environment\n-h  The name of the remote host for this login\n-f  Do not perform authentication\n"
+#define help_losetup "usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}}\n\nAssociate a loopback device with a file, or show current file (if any)\nassociated with a loop device.\n\nInstead of a device:\n-a  Iterate through all loopback devices\n-f  Find first unused loop device (may create one)\n-j  Iterate through all loopback devices associated with FILE\n\nexisting:\n-c  Check capacity (file size changed)\n-d  Detach loopback device\n\nnew:\n-s  Show device name (alias --show)\n-o  Start assocation at OFFSET into FILE\n-r  Read only\n-S  Limit SIZE of loopback association (alias --sizelimit)\n"
+#define help_lsattr "usage: lsattr [-Radlv] [Files...]\n\nList file attributes on a Linux second extended file system.\n\n-R Recursively list attributes of directories and their contents.\n-a List all files in directories, including files that start with `.'.\n-d List directories like other files, rather than listing their contents.\n-l List long flag names.\n-v List the file's version/generation number.\n"
+#define help_lsmod "usage: lsmod\n\nDisplay the currently loaded modules, their sizes and their dependencies.\n"
+#define help_lsusb "usage: lsusb\n\nList USB hosts/devices.\n"
+#define help_mdev "usage: mdev [-s]\n\nCreate devices in /dev using information from /sys.\n\n-s  Scan all entries in /sys to populate /dev.\n"
+#define help_mdev_conf "The mdev config file (/etc/mdev.conf) contains lines that look like:\nhd[a-z][0-9]* 0:3 660\n\nEach line must contain three whitespace separated fields. The first\nfield is a regular expression matching one or more device names, and\nthe second and third fields are uid:gid and file permissions for\nmatching devies.\n"
+#define help_mesg "Usage: mesg [y|n]\n\nControl write access to your terminal\ny Allow write access to your terminal\nn Disallow write access to your terminal\n"
+#define help_mkpasswd "usage: mkpasswd [OPTIONS] [PASSWORD] [SALT]\n\nCrypt PASSWORD using crypt(3)\n\n-P N    Read password from fd N\n-m TYPE Encryption method, when TYPE='help', then show the methods available\n-S SALT\n"
+#define help_mkswap "usage: mkswap DEVICE\n\nSets up a Linux swap area on a device or file.\n"
+#define help_modinfo "usage: modinfo [-0] [-F field] [modulename...]\n"
+#define help_modprobe "usage: modprobe [-alrqvsDb] MODULE [symbol=value][...]\n\nmodprobe utility - inserts modules and dependencies.\n\n-a    Load multiple MODULEs\n-l    List (MODULE is a pattern)\n-r    Remove MODULE (stacks) or do autoclean\n-q    Quiet\n-v    Verbose\n-s    Log to syslog\n-D    Show dependencies\n-b    Apply blacklist to module names too\n"
+#define help_mountpoint "usage: mountpoint [-q] [-d] directory\nmountpoint [-q] [-x] device\n\n-q  Be quiet, return zero if directory is a mountpoint\n-d  Print major/minor device number of the directory\n-x  Print major/minor device number of the block device\n"
+#define help_netcat "usage: netcat [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|-let} [-e COMMAND]\n\n-w  SECONDS timeout for connection\n-p  local port number\n-s  local ipv4 address\n-q  SECONDS quit this many seconds after EOF on stdin.\n-f  use FILENAME (ala /dev/ttyS0) instead of network\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n"
+#define help_netcat_listen "-t  allocate tty (must come before -l or -L)\n-l  listen for one incoming connection.\n-L  listen for multiple incoming connections (server mode).\n\nAny additional command line arguments after -l or -L are executed\nto handle each incoming connection. If none, the connection is\nforwarded to stdin/stdout.\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l\n"
+#define help_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information.\n\n-r  Display routing table.\n-a  Display all sockets (Default: Connected).\n-l  Display listening server sockets.\n-t  Display TCP sockets.\n-u  Display UDP sockets.\n-w  Display Raw sockets.\n-x  Display Unix sockets.\n-e  Display other/more information.\n-n  Don't resolve names.\n-W  Wide Display.\n-p  Display PID/Program name for sockets.\n"
+#define help_nslookup "usage: nslookup [HOST] [SERVER]\n\nQuery the nameserver for the IP address of the given HOST\noptionally using a specified DNS server.\n\nNote:- Only non-interactive mode is supported.\n"
+#define help_oneit "usage: oneit [-p] [-c /dev/tty0] command [...]\n\nA simple init program that runs a single supplied command line with a\ncontrolling tty (so CTRL-C can kill it).\n\n-p  Power off instead of rebooting when command exits.\n-c  Which console device to use.\n\nThe oneit command runs the supplied command line as a child process\n(because PID 1 has signals blocked), attached to /dev/tty0, in its\nown session. Then oneit reaps zombies until the child exits, at\nwhich point it reboots (or with -p, powers off) the system.\n"
+#define help_pgrep "usage: pgrep [-flnovx] [-s SID|-P PPID|PATTERN]\npkill [-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]\n\n-l  Show command name too / List all signals\n-f  Match against entire command line\n-n  Show/Signal the newest process only\n-o  Show/Signal the oldest process only\n-v  Negate the match\n-x  Match whole name (not substring)\n-s  Match session ID (0 for current)\n-P  Match parent process ID\n"
+#define help_ping "usage: ping [OPTIONS] HOST\n\nSend ICMP ECHO_REQUEST packets to network hosts\n\nOptions:\n-4, -6      Force IP or IPv6 name resolution\n-c CNT      Send only CNT pings\n-s SIZE     Send SIZE data bytes in packets (default:56)\n-t TTL      Set TTL\n-I IFACE/IP Use interface or IP address as source\n-W SEC      Seconds to wait for the first response (default:10)\n(after all -c CNT packets are sent)\n-w SEC      Seconds until ping exits (default:infinite)\n(can exit earlier with -c CNT)\n-q          Quiet, only displays output at start\nand when finished\n"
+#define help_ping6 "usage: ping6 [OPTIONS] HOST\n\nSend ICMP ECHO_REQUEST packets to network hosts\n\nOptions:\n-c CNT      Send only CNT pings\n-s SIZE     Send SIZE data bytes in packets (default:56)\n-I IFACE/IP Use interface or IP address as source\n-q          Quiet, only displays output at start\nand when finished\n"
+#define help_printenv "usage: printenv [-0] [env_var...]\n\nPrint environment variables.\n\n-0  Use \\0 as delimiter instead of \\n\n"
+#define help_readahead "usage: readahead FILE...\n\nPreload files into disk cache.\n"
+#define help_readlink "usage: readlink FILE\n\nWith no options, show what symlink points to, return error if not symlink.\n\nOptions for producing cannonical paths (all symlinks/./.. resolved):\n\n-e  cannonical path to existing entry (fail if missing)\n-f  full path (fail if directory missing)\n-n  no trailing newline\n-q  quiet (no output, just error code)\n"
+#define help_realpath "usage: realpath FILE...\n\nDisplay the canonical absolute pathname\n"
+#define help_reboot "usage: reboot [-d delay] [-n] [-f]\n\nReboot utility to reboot the sytem.\n-d DELAY  waits DELAY seconds before rebooting.\n-n    overrides sync() call before reboot.\n-f    Forces system REBOOT and bypasses init process.\n"
+#define help_rev "usage: rev [FILE...]\n\nOutput each line reversed, when no files are given stdin is used.\n"
+#define help_rmmod "usage: rmmod [-wf] [MODULE]\n\nUnload the module named MODULE from the Linux kernel.\n-f  Force unload of a module\n-w  Wait until the module is no longer used.\n\n"
+#define help_route "usage: route -neA inet{6} / [{add|del}]\n\nDisplay/Edit kernel routing tables.\n\n-n  Don't resolve names\n-e  Display other/more information\n-A  inet{6} Select Address Family\n"
+#define help_setsid "usage: setsid [-t] command [args...]\n\nRun process in a new session.\n\n-t  Grab tty (become foreground process, receiving keyboard signals)\n"
+#define help_stat "usage: stat [-f] [-c FORMAT] FILE...\n\nDisplay status of files or filesystems.\n\n-f display filesystem status instead of file status\n-c Output specified FORMAT string instead of default\n\nThe valid format escape sequences for files:\n%a  Access bits (octal) |%A  Access bits (flags)|%b  Blocks allocated\n%B  Bytes per block     |%d  Device ID (dec)    |%D  Device ID (hex)\n%f  All mode bits (hex) |%F  File type          |%g  Group ID\n%G  Group name          |%h  Hard links         |%i  Inode\n%n  Filename            |%N  Long filename      |%o  I/O block size\n%s  Size (bytes)        |%u  User ID            |%U  User name\n%x  Access time         |%X  Access unix time   |%y  File write time\n%Y  File write unix time|%z  Dir change time    |%Z  Dir change unix time\n\nThe valid format escape sequences for filesystems:\n%a  Available blocks    |%b  Total blocks       |%c  Total inodes\n%d  Free inodes         |%f  Free blocks        |%i  File system ID\n%l  Max filename length |%n  File name          |%s  Fragment size\n%S  Best transfer size  |%t  File system type\n"
+#define help_swapoff "usage: swapoff swapregion\n\nDisable swapping on a given swapregion.\n"
+#define help_swapon "usage: swapon [-p priority] filename\n\nEnable swapping on a given device/file.\n"
+#define help_switch_root "usage: switch_root [-c /dev/console] NEW_ROOT NEW_INIT...\n\nUse from PID 1 under initramfs to free initramfs, chroot to NEW_ROOT,\nand exec NEW_INIT.\n\n-c  Redirect console to device in NEW_ROOT\n-h  Hang instead of exiting on failure (avoids kernel panic)\n"
+#define help_syslogd "Usage: syslogd  [-a socket][-p socket][-O logfile][-f config file][-m interval]\n[-n][-p socket][-S][-s SIZE][-b N][-R HOST][-L][-K][-l N][-D]\n\nSystem logging utility\n\n-a        Extra unix socket for listen\n-O FILE   Default log file <DEFAULT: /var/log/messages>\n-f FILE   Config file <DEFAULT: /etc/syslog.conf>\n-p        Alternative unix domain socket <DEFAULT : /dev/log>\n-n        Avoid auto-backgrounding.\n-S        Smaller output\n-m MARK   interval <DEFAULT: 20 minutes> (RANGE: 0 to 71582787)\n-R HOST   Log to IP or hostname on PORT (default PORT=514/UDP)\"\n-L        Log locally and via network (default is network only if -R)\"\n-s SIZE   Max size (KB) before rotation (default:2000KB, 0=off)\n-b N      rotated logs to keep (default:1, max=99, 0=purge)\n-K        Log to kernel printk buffer (use dmesg to read it)\n-l N      Log only messages more urgent than prio(default:8 max:8 min:1)\n-D        Drop duplicates\n"
+#define help_tac "usage: tac [FILE...]\n\nOutput lines in reverse order.\n"
+#define help_taskset "usage: taskset [-ap] [mask] [PID | cmd [args...]]\n\nLaunch a new task which may only run on certain processors, or change\nthe processor affinity of an exisitng PID.\n\nMask is a hex string where each bit represents a processor the process\nis allowed to run on. PID without a mask displays existing affinity.\n\n-p  Set/get the affinity of given PID instead of a new command.\n-a  Set/get the affinity of all threads of the PID.\n"
+#define help_telnet "usage: telnet HOST [PORT]\nConnect to telnet server\n"
+#define help_tftp "usage: tftp [OPTIONS] HOST [PORT]\n\nTransfer file from/to tftp server.\n\n-l FILE Local FILE\n-r FILE Remote FILE\n-g    Get file\n-p    Put file\n-b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)\n"
+#define help_traceroute "usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n[-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]\n\nTrace the route to HOST\n\n-4,-6   Force IP or IPv6 name resolution\n-F    Set the don't fragment bit\n-U    Use UDP datagrams instead of ICMP ECHO\n-I    Use ICMP ECHO instead of UDP datagrams\n-l    Display the TTL value of the returned packet\n-f    Start from the 1ST_TTL hop (instead from 1)(RANGE 0 to 255)\n-d    Set SO_DEBUG options to socket\n-n    Print numeric addresses\n-v    verbose\n-r    Bypass routing tables, send directly to HOST\n-m    Max time-to-live (max number of hops)(RANGE 1 to 255)\n-p    Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)\n-q    Number of probes per TTL (default 3)(RANGE 1 to 255)\n-s    IP address to use as the source address\n-t    Type-of-service in probe packets (default 0)(RANGE 0 to 255)\n-w    Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)\n-g    Loose source route gateway (8 max)\n-z    Pause Time in sec (default 0)(RANGE 0 to 86400)\n"
+#define help_truncate "usage: truncate [-c] -s file...\n\nSet length of file(s), extending sparsely if necessary.\n\n-c  Don't create file if it doesn't exist.\n-s  New size\n"
+#define help_udhcpc "usage: udhcpc [-fbnqvoCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n[-H HOSTNAME] [-V VENDOR] [-x OPT:VAL] [-O OPT]\n\nConfigure network dynamicaly using DHCP.\n\n-i Interface to use (default eth0)\n-p Create pidfile\n-s Run PROG at DHCP events (default /usr/share/udhcpc/default.script)\n-B Request broadcast replies\n-t Send up to N discover packets\n-T Pause between packets (default 3 seconds)\n-A Wait N seconds after failure (default 20)\n-f Run in foreground\n-b Background if lease is not obtained\n-n Exit if lease is not obtained\n-q Exit after obtaining lease\n-R Release IP on exit\n-S Log to syslog too\n-a Use arping to validate offered address\n-O Request option OPT from server (cumulative)\n-o Don't request any options (unless -O is given)\n-r Request this IP address\n-x OPT:VAL  Include option OPT in sent packets (cumulative)\n-F Ask server to update DNS mapping for NAME\n-H Send NAME as client hostname (default none)\n-V VENDOR Vendor identifier (default 'udhcp VERSION')\n-C Don't send MAC as client identifier\n-v Verbose\n\nSignals:\nUSR1  Renew current lease\nUSR2  Release current lease\n\n"
+#define help_udhcpd "Usage: udhcpd [-fS] [-P N] [CONFFILE]\n\n-f    Run in foreground\n-S    Log to syslog too\n-P N  Use port N (default 67)\n"
+#define help_unshare "usage: unshare [-muin] COMMAND...\n\nCreate new namespace(s) for this process and its children, so some\nattribute is not shared with the parent process.  This is part of\nLinux Containers.  Each process can have its own:\n\n-m  Mount/unmount tree\n-u  Host and domain names\n-i  SysV IPC (message queues, semaphores, shared memory)\n-n  Network address, sockets, routing, iptables\n"
+#define help_uptime "usage: uptime\n\nTell how long the system has been running and the system load\naverages for the past 1, 5 and 15 minutes.\n"
+#define help_usleep "usage: usleep MICROSECONDS\n\nPause for MICROSECONDS microseconds.\n"
+#define help_vconfig "usage: vconfig COMMAND [OPTIONS]\n\nadd             [interface-name] [vlan_id]\nrem             [vlan-name]\nset_flag        [interface-name] [flag-num]       [0 | 1]\nset_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]\nset_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]\nset_name_type   [name-type]\n\nCreate and remove virtual ethernet devices\n"
+#define help_vmstat "usage: vmstat [-n] [delay [count]]\n-n  Display the header only once\ndelay       The delay between updates in seconds, when not specified\nthe average since boot is displayed.\ncount       Number of updates to display, the default is inifinite.\n"
+#define help_w "usage: w\n\nShow who is logged on and since how long they logged in.\n"
+#define help_wget "usage: wget [-c -s -q -O outputfile -P dirprefix -Y Proxy -U useragent -T timeout] URL.\n\nwget - Get files from HTTP or FTP.\n\n-c  resume getting a partially-downloaded file.\n-s  spider mode. Don’t download anything, only check file existence.\n-q  quiet (no output).\n-O FILE  write documents to FILE. ('-' for stdout).\n-P DIR  save files to DIR (default .)\n-Y Use   proxy ('on' or 'off').\n-U AGENT Use AGENT for User-Agent header.\n-T SEC  set all timeout values to SECONDS.\n"
+#define help_which "usage: which [-a] filename ...\n\nSearch $PATH for executable files matching filename(s).\n\n-a  Show all matches\n"
+#define help_whoami "usage: whoami\n\nPrint effective user name.\n"
+#define help_yes "usage: yes [args...]\n\nRepeatedly output line until killed. If no args, output 'y'.\n"
+#define help_mdev "usage: mdev [-s]\n\nCreate devices in /dev using information from /sys.\n\n-s  Scan all entries in /sys to populate /dev.\n"
+#define help_mdev_conf "The mdev config file (/etc/mdev.conf) contains lines that look like:\nhd[a-z][0-9]* 0:3 660\n\nEach line must contain three whitespace separated fields. The first\nfield is a regular expression matching one or more device names, and\nthe second and third fields are uid:gid and file permissions for\nmatching devies.\n"
+#define help_mke2fs "usage: mke2fs [-Fnq] [-b ###] [-N|i ###] [-m ###] device\n\nCreate an ext2 filesystem on a block device or filesystem image.\n\n-F         Force to run on a mounted device\n-n         Don't write to device\n-q         Quiet (no output)\n-b size    Block size (1024, 2048, or 4096)\n-N inodes  Allocate this many inodes\n-i bytes   Allocate one inode for every XXX bytes of device\n-m percent Reserve this percent of filesystem space for root user\n"
+#define help_mke2fs_journal "usage: [-j] [-J size=###,device=XXX]\n\n-j         Create journal (ext3)\n-J         Journal options\nsize: Number of blocks (1024-102400)\ndevice: Specify an external journal\n"
+#define help_mke2fs_gen "usage: gene2fs [options] device filename\n\nThe [options] are the same as mke2fs.\n"
+#define help_mke2fs_label "usage: mke2fs [-L label] [-M path] [-o string]\n\n-L         Volume label\n-M         Path to mount point\n-o         Created by\n"
+#define help_mke2fs_extended "usage: mke2fs [-E stride=###] [-O option[,option]]\n\n-E stride= Set RAID stripe size (in blocks)\n-O [opts]  Specify fewer ext2 option flags (for old kernels)\nAll of these are on by default (as appropriate)\nnone         Clear default options (all but journaling)\ndir_index    Use htree indexes for large directories\nfiletype     Store file type info in directory entry\nhas_journal  Set by -j\njournal_dev  Set by -J device=XXX\nsparse_super Don't allocate huge numbers of redundant superblocks\n"
+#define help_nbd_client "usage: nbd-client [-ns] HOST PORT DEVICE\n\n-n  Do not fork into background\n-s  nbd swap support (lock server into memory)\n"
+#define help_reset "usage: reset\n\nA program to reset the terminal.\n"
+#define help_sed "usage: sed [-irn] {command | [-e command]...} [FILE...]\n\nStream EDitor, transforms text by appling script of command to each line\nof input.\n\n-e  Add expression to the command script (if no -e, use first argument)\n-i  Modify file in place\n-n  No default output (p commands only)\n-r  Use extended regular expression syntex\n"
+#define help_sh "usage: sh [-c command] [script]\n\nCommand shell.  Runs a shell script, or reads input interactively\nand responds to it.\n\n-c  command line to execute\n"
+#define help_sh_tty "Add terminal control to toysh.  This is necessary for interactive use,\nso the shell isn't killed by CTRL-C.\n"
+#define help_sh_profile "Read /etc/profile and ~/.profile when running interactively.\n\nAlso enables the built-in command \"source\".\n"
+#define help_sh_jobctl "Add job control to toysh.  This lets toysh handle CTRL-Z, and enables\nthe built-in commands \"fg\", \"bg\", and \"jobs\".\n\nWith pipe support, enable use of \"&\" to run background processes.\n"
+#define help_sh_flowctl "Add flow control to toysh.  This enables the if/then/else/fi,\nwhile/do/done, and for/do/done constructs.\n\nWith pipe support, this enables the ability to define functions\nusing the \"function name\" or \"name()\" syntax, plus curly brackets\n\"{ }\" to group commands.\n"
+#define help_sh_quotes "Add support for parsing \"\" and '' style quotes to the toysh command\nparser, with lets arguments have spaces in them.\n"
+#define help_sh_wildcards "Expand wildcards in argument names, ala \"ls -l *.t?z\" and\n\"rm subdir/{one,two,three}.txt\".\n"
+#define help_sh_procargs "Add support for executing arguments contianing $() and ``, using\nthe output of the command as the new argument value(s).\n\n(Bash calls this \"command substitution\".)\n"
+#define help_sh_envvars "Substitute environment variable values for $VARNAME or ${VARNAME},\nand enable the built-in command \"export\".\n"
+#define help_sh_locals "Support for local variables, fancy prompts ($PS1), the \"set\" command,\nand $?.\n"
+#define help_sh_arrays "Support for ${blah[blah]} style array variables.\n"
+#define help_sh_pipes "Support multiple commands on the same command line.  This includes\n| pipes, > >> < redirects, << here documents, || && conditional\nexecution, () subshells, ; sequential execution, and (with job\ncontrol) & background processes.\n"
+#define help_sh_builtins "Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set,\nunset, read, alias.\n"
+#define help_exit "usage: exit [status]\n\nExit shell.  If no return value supplied on command line, use value\nof most recent command, or 0 if none.\n"
+#define help_cd "usage: cd [path]\n\nChange current directory.  With no arguments, go to $HOME.\n"
+#define help_cd_p "usage: cd [-PL]\n\n-P    Physical path: resolve symlinks in path.\n-L    Cancel previous -P and restore default behavior.\n"
+#define help_xzcat "usage: xzcat < file.xz\n\nRead xz-compressed file from stdin and write decompressed file to stdout.\n\n"
+#define help_ash "usage: ash [-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]\n\nASH shell Interpreter.\n"
+#define help_awk "usage: awk [OPTIONS] [AWK_PROGRAM] [FILE]...\n\n-v VAR=VAL    Set variable\n-F SEP      Use SEP as field separator\n-f FILE     Read program from FILE\n"
+#define help_basename "usage: basename string [suffix]\n\nReturn non-directory portion of a pathname removing suffix\n"
+#define help_cal "usage: cal [[month] year]\nPrint a calendar.\n\nWith one argument, prints all months of the specified year.\nWith two arguments, prints calendar for month and year.\n"
+#define help_cat "usage: cat [-u] [file...]\nCopy (concatenate) files to stdout.  If no files listed, copy from stdin.\nFilename \"-\" is a synonym for stdin.\n\n-u  Copy one byte at a time (slow).\n"
+#define help_chgrp "usage: chown [-RHLP] [-fvh] [owner][:group] file...\nusage: chgrp [-RHLP] [-fvh] group file...\n\nChange ownership of one or more files.\n\n-f  suppress most error messages.\n-h  change symlinks instead of what they point to\n-R  recurse into subdirectories (implies -h).\n-H  with -R change target of symlink, follow command line symlinks\n-L  with -R change target of symlink, follow all symlinks\n-P  with -R change symlink, do not follow symlinks (default)\n-v  verbose output.\n"
+#define help_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777    ug uuugggooo    top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1    sstrwxrwxrwx    bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only\n"
+#define help_cksum "usage: cksum [-IPLN] [file...]\n\nFor each file, output crc32 checksum value, length and name of file.\nIf no files listed, copy from stdin.  Filename \"-\" is a synonym for stdin.\n\n-L  Little endian (defaults to big endian)\n-P  Pre-inversion\n-I  Skip post-inversion\n-N  Do not include length in CRC calculation\n"
+#define help_cmp "usage: cmp [-l] [-s] FILE1 FILE2\n\nCompare the contents of two files.\n\n-l  show all differing bytes\n-s  silent\n"
+#define help_comm "usage: comm [-123] FILE1 FILE2\n\nReads FILE1 and FILE2, which should be ordered, and produces three text\ncolumns as output: lines only in FILE1; lines only in FILE2; and lines\nin both files. Filename \"-\" is a synonym for stdin.\n\n-1 suppress the output column of lines unique to FILE1\n-2 suppress the output column of lines unique to FILE2\n-3 suppress the output column of lines duplicated in FILE1 and FILE2\n"
+#define help_cp "usage: cp [-fipRHLP] SOURCE... DEST\n\nCopy files from SOURCE to DEST.  If more than one SOURCE, DEST must\nbe a directory.\n\n-f  force copy by deleting destination file\n-i  interactive, prompt before overwriting existing DEST\n-p  preserve timestamps, ownership, and permissions\n-R  recurse into subdirectories (DEST must be a directory)\n-H  Follow symlinks listed on command line\n-L  Follow all symlinks\n-P  Do not follow symlinks [default]\n"
+#define help_cp_more "usage: cp [-rdavsl]\n\n-a  same as -dpr\n-d  don't dereference symlinks\n-l  hard link instead of copy\n-n  no clobber (don't overwrite DEST)\n-r  synonym for -R\n-s  symlink instead of copy\n-v  verbose\n"
+#define help_cut "usage: cut OPTION... [FILE]...\nPrint selected parts of lines from each FILE to standard output.\n-b LIST    select only these bytes from LIST.\n-c LIST    select only these characters from LIST.\n-f LIST    select only these fields.\n-d DELIM  use DELIM instead of TAB for field delimiter.\n-s    do not print lines not containing delimiters.\n-n    don't split multibyte characters (Ignored).\n"
+#define help_date "usage: date [-u] [-r file] [+format] | mmddhhmm[[cc]yy]\n\nSet/get the current date/time\n"
+#define help_dd "usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]\n[seek=N] [conv=notrunc|noerror|sync|fsync]\n\nOptions:\nif=FILE   Read from FILE instead of stdin\nof=FILE   Write to FILE instead of stdout\nbs=N    Read and write N bytes at a time\nibs=N     Read N bytes at a time\nobs=N     Write N bytes at a time\ncount=N   Copy only N input blocks\nskip=N    Skip N input blocks\nseek=N    Skip N output blocks\nconv=notrunc  Don't truncate output file\nconv=noerror  Continue after read errors\nconv=sync   Pad blocks with zeros\nconv=fsync  Physically write data out before finishing\n\nNumbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\nMD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)\nCopy a file, converting and formatting according to the operands.\n"
+#define help_df "usage: df [-t type] [FILESYSTEM ...]\n\nThe \"disk free\" command, df shows total/used/available disk space for\neach filesystem listed on the command line, or all currently mounted\nfilesystems.\n\n-t type     Display only filesystems of this type.\n"
+#define help_df_pedantic "usage: df [-Pk]\n\n-P  The SUSv3 \"Pedantic\" option\n\nProvides a slightly less useful output format dictated by\nthe Single Unix Specification version 3, and sets the\nunits to 512 bytes instead of the default 1024 bytes.\n\n-k  Sets units back to 1024 bytes (the default without -P)\n"
+#define help_dirname "usage: dirname PATH\n\nShow directory portion of path.\n"
+#define help_du "usage: du [-d N] [-askxHLlmc] [file...]\n\nEstimate file space usage (default in unit of 512 blocks).\n-a    Show all file sizes\n-H    Follow symlinks on cmdline\n-L    Follow all symlinks\n-k    Show size in units of 1024.\n-s    Show only the total Size for each file specified\n-x    Estimate size only on the same device\n-c    Print total size of all arguments\n-d N  Limit output to directories (and files with -a) of depth < N\n-l    Count sizes many times if hard linked\n-h    Sizes in human readable format (e.g., 1K 243M 2G )\n-m    Sizes in megabytes\n"
+#define help_echo "usage: echo [-ne] [args...]\n\nWrite each argument to stdout, with one space between each, followed\nby a newline.\n\n-n  No trailing newline.\n-e  Process the following escape sequences:\n\\\\      backslash\n\\0NNN   octal values (1 to 3 digits)\n\\a      alert (beep/flash)\n\\b      backspace\n\\c      stop output here (avoids trailing newline)\n\\f      form feed\n\\n      newline\n\\r      carriage return\n\\t      horizontal tab\n\\v      vertical tab\n\\xHH    hexadecimal values (1 to 2 digits)\n"
+#define help_egrep "usage: egrep [-H] [-h] [-n] [-l] [-L] [-c] [-o] [-q] [-v] [-s] [-r] [-i] [-w] [-m NUMBER] [-A NUMBER] [-B NUMBER] [-C NUMBER] [-e PTRN] [-f FILE]\n\nA grep program to find extended PATTERN - egrep.\n\nMostly used for finding in files.\n"
+#define help_env "usage: env [-i] [NAME=VALUE...] [command [option...]]\n\nSet the environment for command invocation.\n\n-i  Clear existing environment.\n"
+#define help_expand "usage: expand [-t tablist] [file...]\n\nExpand tabs to spaces according to tabstops.\n\n-t  tablist\n\nSpecify tab stops, either a single number instead of the default 8,\nor a comma separated list of increasing numbers representing tabstop\npositions (absolute, not increments) with each additional tab beyound\nthat becoming one space.\n"
+#define help_false "Return nonzero.\n"
+#define help_fgrep "usage: fgrep [-H] [-h] [-n] [-l] [-L] [-c] [-o] [-q] [-v] [-s] [-r] [-i] [-w] [-m NUMBER] [-A NUMBER] [-B NUMBER] [-C NUMBER] [-e PTRN] [-f FILE]\n\nA grep program to find fix PATTERN - fgrep.\n\nMostly used for finding in files.\n"
+#define help_grep "usage: grep [-aHhnlLcoqvsxriwFE] [-m NUMBER] [-A NUMBER] [-B NUMBER] [-C NUMBER] PATTERN/ [-e PTRN] [-f FILE] [FILE]...\n\nSearch for PATTERN in FILEs (or stdin)\n\n-a  Process a binary file as text file.\n-H  Add 'filename:' prefix\n-h  Do not add 'filename:' prefix\n-n  Add 'line_no:' prefix\n-l  Show only names of files that match\n-L  Show only names of files that don't match\n-c  Show only count of matching lines\n-o  Show only the matching part of line\n-q  Quiet. Return 0 if PATTERN is found, 1 otherwise\n-v  Select non-matching lines\n-s  Suppress open and read errors\n-r  Recurse\n-i  Ignore case\n-w  Match whole words only\n-x  Match whole lines only\n-F  PATTERN is a literal (not regexp)\n-E  PATTERN is an extended regexp\n-m N  Match up to N times per file\n-A N  Print N lines of trailing context\n-B N  Print N lines of leading context\n-C N  Same as '-A N -B N'\n-e PTRN Pattern to match\n-f FILE Read pattern from file\n"
+#define help_head "usage: head [-n number] [file...]\n\nCopy first lines from files to stdout. If no files listed, copy from\nstdin. Filename \"-\" is a synonym for stdin.\n\n-n  Number of lines to copy.\n"
+#define help_id "usage: id [-nGgru]\n\nPrint user and group ID.\n\n-n  print names instead of numeric IDs (to be used with -Ggu)\n-G  Show only the group IDs\n-g  Show only the effective group ID\n-r  Show real ID instead of effective ID\n-u  Show only the effective user ID\n"
+#define help_id_groups "usage: groups [user]\n\nPrint the groups a user is in.\n\n"
+#define help_kill "usage: kill [-l [SIGNAL] | -s SIGNAL | -SIGNAL] pid...\n\nSend a signal to a process\n"
+#define help_link "usage: link FILE NEWLINK\n\nCreate hardlink to a file.\n"
+#define help_ln "usage: ln [-sfn] [FROM...] TO\n\nCreate a link between FROM and TO.\nWith only one argument, create link in current directory.\n\n-s  Create a symbolic link\n-f  Force the creation of the link, even if TO already exists\n-n  Symlink at destination treated as file\n"
+#define help_logger "usage: logger [-s] [-t TAG] [-p PRIORITY] [message]\n\n-i    Log the process id of the logger process with each line.\n-s    Log to stderr as well as the system log\n-t TAG  Log using the specified tag (defaults to user name)\n-p PRIO Priority (numeric or facility.level pair)\n"
+#define help_logname "usage: logname\n\nPrints the calling user's name or an error when this cannot be\ndetermined.\n"
+#define help_ls "usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...]\nlist files\n\nwhat to show:\n-a  all files including .hidden\n-c  use ctime for timestamps\n-d  directory, not contents\n-i  inode number\n-k  block sizes in kilobytes\n-p  put a '/' after directory names\n-q  unprintable chars as '?'\n-s  size (in blocks)\n-u  use access time for timestamps\n-A  list all files except . and ..\n-H  follow command line symlinks\n-L  follow symlinks\n-R  recursively list files in subdirectories\n-F  append file type indicator (/=dir, *=exe, @=symlink, |=FIFO)\n\noutput formats:\n-1  list one file per line\n-C  columns (sorted vertically)\n-g  like -l but no owner\n-l  long (show full details for each file)\n-m  comma separated\n-n  like -l but numeric uid/gid\n-o  like -l but no group\n-x  columns (sorted horizontally)\n\nsorting (default is alphabetical):\n-f  unsorted\n-r  reverse\n-t  timestamp\n-S  size\n"
+#define help_mkdir "usage: mkdir [-p] [-m mode] [dirname...]\nCreate one or more directories.\n\n-p  make parent directories as needed.\n-m  set permissions of directory to mode.\n"
+#define help_mkfifo "usage: mkfifo [fifo_name...]\n\nCreate FIFOs (named pipes).\n"
+#define help_mv "usage: mv [-fin] SOURCE DEST\nmv [-fin] SOURCE... DIRECTORY\n\nRename SOURCE to DEST or Move SOURCES(s) to DIRECTORY.\n\n-f  Don't Prompt before overwrite\n-i  interactive, prompt before overwrite\n-n  Don't overwrite an existing file\n"
+#define help_nice "usage: nice [-n PRIORITY] command [args...]\n\nRun a command line at an increased or decreased scheduling priority.\n\nHigher numbers make a program yield more CPU time, from -20 (highest\npriority) to 19 (lowest).  By default processes inherit their parent's\nniceness (usually 0).  By default this command adds 10 to the parent's\npriority.  Only root can set a negative niceness level.\n"
+#define help_nohup "usage: nohup COMMAND [ARGS...]\n\nRun a command that survives the end of its terminal.\nIf stdin is a tty, redirect from /dev/null\nIf stdout is a tty, redirect to file \"nohup.out\"\n"
+#define help_od "usage: od [-bdosxv] [-j #] [-N #] [-A doxn] [-t arg]\n\n-A  Address base (decimal, octal, hexdecimal, none)\n-t  output type(s) a (ascii) c (char) d (decimal) foux\n"
+#define help_patch "usage: patch [-i file] [-p depth] [-Ru]\n\nApply a unified diff to one or more files.\n\n-i  Input file (defaults=stdin)\n-l  Loose match (ignore whitespace)\n-p  Number of '/' to strip from start of file paths (default=all)\n-R  Reverse patch.\n-u  Ignored (only handles \"unified\" diffs)\n\nThis version of patch only handles unified diffs, and only modifies\na file when all all hunks to that file apply.  Patch prints failed\nhunks to stderr, and exits with nonzero status if any hunks fail.\n\nA file compared against /dev/null (or with a date <= the epoch) is\ncreated/deleted as appropriate.\n"
+#define help_printf "usage: printf Format [Arguments..]\n\nFormat and print ARGUMENT(s) according to FORMAT.\nFormat is 'C' control output as 'C' printf.\n"
+#define help_pwd "usage: pwd [-L|-P]\n\nPrint working (current) directory.\n\n-L  Use shell's path from $PWD (when applicable)\n-P  Print cannonical absolute path\n"
+#define help_rm "usage: rm [-fiRr] FILE...\n\nRemove each argument from the filesystem.\n\n-f  force: remove without confirmation, no error if it doesn't exist\n-i  interactive: prompt for confirmation\n-rR recursive: remove directory contents\n"
+#define help_rmdir "usage: rmdir [-p] [dirname...]\nRemove one or more directories.\n\n-p  Remove path.\n"
+#define help_sleep "usage: sleep SECONDS\n\nWait before exiting.\n"
+#define help_sleep_float "The delay can be a decimal fraction. An optional suffix can be \"m\"\n(minutes), \"h\" (hours), \"d\" (days), or \"s\" (seconds, the default).\n"
+#define help_sort "usage: sort [-run] [FILE...]\n\nSort all lines of text from input files (or stdin) to stdout.\n\n-r    reverse\n-u    unique lines only\n-n    numeric order (instead of alphabetical)\n"
+#define help_sort_big "usage: sort [-bcdfiMsz] [-k#[,#[x]] [-t X]] [-o FILE]\n\n-b    ignore leading blanks (or trailing blanks in second part of key)\n-c    check whether input is sorted\n-d    dictionary order (use alphanumeric and whitespace chars only)\n-f    force uppercase (case insensitive sort)\n-i    ignore nonprinting characters\n-M    month sort (jan, feb, etc).\n-x    Hexadecimal numerical sort\n-s    skip fallback sort (only sort with keys)\n-z    zero (null) terminated input\n-k    sort by \"key\" (see below)\n-t    use a key separator other than whitespace\n-o    output to FILE instead of stdout\n\nSorting by key looks at a subset of the words on each line.  -k2\nuses the second word to the end of the line, -k2,2 looks at only\nthe second word, -k2,4 looks from the start of the second to the end\nof the fourth word.  Specifying multiple keys uses the later keys as\ntie breakers, in order.  A type specifier appended to a sort key\n(such as -2,2n) applies only to sorting that key.\n"
+#define help_sort_float "usage: sort [-g]\n\nThis version of sort requires floating point.\n\n-g  general numeric sort (double precision with nan and inf)\n\n"
+#define help_split "usage: split [-a SUFFIX_LEN] [-b BYTES] [-l LINES] [INPUT [OUTPUT]]\n\nCopy INPUT (or stdin) data to a series of OUTPUT (or \"x\") files with\nalphabetically increasing suffix (aa, ab, ac... az, ba, bb...).\n\n-a  Suffix length (default 2)\n-b  BYTES/file (10, 10k, 10m, 10g...)\n-l  LINES/file (default 1000)\n"
+#define help_stty "usage: stty [-F device] [-a] [-g] [...]\n\nWithout arguments, prints baud rate, line discipline,\nand deviations from stty sane\n\n-F DEVICE   Open device instead of stdin\n-a    Print all current settings in human-readable form\n-g    Print in stty-readable form\n"
+#define help_tail "usage: tail [-n|c number] [-f] [file...]\n\nCopy last lines from files to stdout. If no files listed, copy from\nstdin. Filename \"-\" is a synonym for stdin.\n\n-n  output the last X lines (default 10), +X counts from start.\n-c  output the last X bytes, +X counts from start\n-f  follow file, waiting for more data to be appended\n"
+#define help_tail_seek "This version uses lseek, which is faster on large files.\n"
+#define help_tee "usage: tee [-ai] [file...]\n\nCopy stdin to each listed file, and also to stdout.\nFilename \"-\" is a synonym for stdout.\n\n-a  append to files.\n-i  ignore SIGINT.\n"
+#define help_test "usage: test [EXPRESSION] [EXPRESSION] ...\n\ncheck file types and compare values (Evaluate Expression).\n"
+#define help_time "usage: time [-p] COMMAND [ARGS...]\n\nRun command line and report real, user, and system time elapsed in seconds.\n(real = clock on the wall, user = cpu used by command's code,\nsystem = cpu used by OS on behalf of command.)\n\n-p  posix mode (ignored)\n"
+#define help_touch "Usage: Usage: touch [OPTION]... FILE...\n\nUpdate the access and modification times of each FILE to the current time.\n\n-a  change access time\n-m  change modification time\n-c  don't create file\n-d DATE     use YYYY-MM-DDThh:mm:SS[.frac][tz] as time\n-t TIME     use [[CC]YY]MMDDhhmm[.ss][frac] as time\n-r FILE     use reference file's time\n"
+#define help_true "Return zero.\n"
+#define help_tty "Show filename of terminal connected to stdin.\n\nPrints \"not a tty\" and exits with nonzero status if no terminal\nis connected to stdin.\n\n-s  silent mode\n"
+#define help_uname "usage: uname [-asnrvmpio]\n\nPrint system information.\n\n-s  System name\n-n  Network (domain) name\n-r  Release number\n-v  Version (build date)\n-m  Machine (hardware) name\n-a  All of the above\n"
+#define help_uniq "usage: uniq [-cduiz] [-w maxchars] [-f fields] [-s char] [input_file [output_file]]\n\nReport or filter out repeated lines in a file\n\n-c  show counts before each line\n-d  show only lines that are repeated\n-u  show only lines that are unique\n-i  ignore case when comparing lines\n-z  lines end with \\0 not \\n\n-w  compare maximum X chars per line\n-f  ignore first X fields\n-s  ignore first X chars\n"
+#define help_unlink "usage: unlink FILE\n\nDeletes one file.\n"
+#define help_uudecode "usage: uudecode [-o OUTFILE] [INFILE]\n\nDecode file from stdin (or INFILE).\n\n-o  write to OUTFILE instead of filename in header\n"
+#define help_uuencode "usage: uuencode [-m] [file] encode-filename\n\nUuencode stdin (or file) to stdout, with encode-filename in the output.\n\n-m  base64-encode\n"
+#define help_wc "usage: wc -lwcm [FILE...]\n\nCount lines, words, and characters in input.\n\n-l  show lines\n-w  show words\n-c  show bytes\n-m  show characters\n\nBy default outputs lines, words, bytes, and filename for each\nargument (or from stdin if none). Displays only either bytes\nor characters.\n"
+#define help_who "usage: who\n\nPrint logged user information on system\n"
+#define help_xargs "usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND...\n\nRun command line one or more times, appending arguments from stdin.\n\nIf command exits with 255, don't launch another even if arguments remain.\n\n-s  Size in bytes per command line\n-n  Max number of arguments per command\n-0  Each argument is NULL terminated, no whitespace or quote processing\n#-p Prompt for y/n from tty before running each command\n#-t Trace, print command line to stderr\n#-x Exit if can't fit everything in one command\n#-r Don't run command with empty input\n#-L Max number of lines of input per command\n-E  stop at line matching string\n"
+#define help_toybox "usage: toybox [command] [arguments...]\n\nWith no arguments, shows available commands. First argument is\nname of a command to run, followed by any arguments to that command.\n"
+#define help_toybox_suid "Support for the Set User ID bit, to install toybox suid root and drop\npermissions for commands which do not require root access. To use\nthis change ownership of the file to the root user and set the suid\nbit in the file permissions:\n\nchown root:root toybox; chmod +s toybox\n"
+#define help_toybox_float "Include floating point support infrastructure and commands that\nrequire it.\n"
+#define help_toybox_help "Include help text for each command.\n"
+#define help_toybox_i18n "Support for UTF-8 character sets, and some locale support.\n"
+#define help_toybox_free "When a program exits, the operating system will clean up after it\n(free memory, close files, etc). To save size, toybox usually relies\non this behavior. If you're running toybox under a debugger or\nwithout a real OS (ala newlib+libgloss), enable this to make toybox\nclean up after itself.\n"
+#define help_toybox_debug "Enable extra checks for debugging purposes.\n"
diff --git a/generated/portability.h b/generated/portability.h
new file mode 100644 (file)
index 0000000..6b5884e
--- /dev/null
@@ -0,0 +1 @@
+#define O_NOFOLLOW 0x20000
diff --git a/kconfig/Makefile b/kconfig/Makefile
new file mode 100644 (file)
index 0000000..7eaee95
--- /dev/null
@@ -0,0 +1,65 @@
+# ===========================================================================
+# Kernel configuration targets
+# These targets are used from top-level makefile
+
+KCONFIG_TOP = Config.in
+KCONFIG_PROJECT = ToyBox
+obj = ./kconfig
+PHONY += clean help oldconfig menuconfig config silentoldconfig \
+       randconfig allyesconfig allnoconfig allmodconfig defconfig
+
+menuconfig: $(obj)/mconf $(KCONFIG_TOP)
+       $< $(KCONFIG_TOP)
+
+config: $(obj)/conf $(KCONFIG_TOP)
+       $< $(KCONFIG_TOP)
+
+oldconfig: $(obj)/conf $(KCONFIG_TOP)
+       $< -o $(KCONFIG_TOP)
+
+silentoldconfig: $(obj)/conf $(KCONFIG_TOP)
+       $< -s $(KCONFIG_TOP)
+
+randconfig: $(obj)/conf $(KCONFIG_TOP)
+       $< -r $(KCONFIG_TOP)
+
+allyesconfig: $(obj)/conf $(KCONFIG_TOP)
+       $< -y $(KCONFIG_TOP)
+
+allnoconfig: $(obj)/conf $(KCONFIG_TOP)
+       $< -n $(KCONFIG_TOP)
+
+defconfig: $(obj)/conf $(KCONFIG_TOP)
+       $< -D /dev/null $(KCONFIG_TOP)
+
+# Help text used by make help
+help::
+       @echo  '  config          - Update current config utilising a line-oriented program'
+       @echo  '  menuconfig      - Update current config utilising a menu based program'
+       @echo  '  oldconfig       - Update current config utilising a provided .config as base'
+       @echo  '  silentoldconfig - Same as oldconfig, but quietly'
+       @echo  '  randconfig      - New config with random answer to all options'
+       @echo  '  defconfig       - New config with default answer to all options'
+       @echo  '  allyesconfig    - New config where all options are accepted with yes'
+       @echo  '  allnoconfig     - New config where all options are answered with no'
+
+# Cheesy build
+
+SHIPPED = kconfig/zconf.tab.c kconfig/lex.zconf.c kconfig/zconf.hash.c
+
+%.c: %.c_shipped
+       @ln -s $(notdir $<) $@
+
+gen_config.h: .config
+
+kconfig/mconf: $(SHIPPED)
+       $(HOSTCC) -o $@ kconfig/mconf.c kconfig/zconf.tab.c \
+               kconfig/lxdialog/*.c -lcurses -DCURSES_LOC="<ncurses.h>" \
+               -DKBUILD_NO_NLS=1 -DPROJECT_NAME=\"$(KCONFIG_PROJECT)\"
+
+kconfig/conf: $(SHIPPED)
+       $(HOSTCC) -o $@ kconfig/conf.c kconfig/zconf.tab.c -DKBUILD_NO_NLS=1 \
+               -DPROJECT_NAME=\"$(KCONFIG_PROJECT)\"
+
+clean::
+       rm -f $(wildcard kconfig/*zconf*.c) kconfig/conf kconfig/mconf
diff --git a/kconfig/conf.c b/kconfig/conf.c
new file mode 100644 (file)
index 0000000..72940e1
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf(struct menu *menu);
+static void check_conf(struct menu *menu);
+
+enum {
+       ask_all,
+       ask_new,
+       ask_silent,
+       set_default,
+       set_yes,
+       set_mod,
+       set_no,
+       set_random
+} input_mode = ask_all;
+char *defconfig_file;
+
+static int indent = 1;
+static int valid_stdin = 1;
+static int conf_cnt;
+static char line[128];
+static struct menu *rootEntry;
+
+static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n");
+
+static void strip(char *str)
+{
+       char *p = str;
+       int l;
+
+       while ((isspace(*p)))
+               p++;
+       l = strlen(p);
+       if (p != str)
+               memmove(str, p, l + 1);
+       if (!l)
+               return;
+       p = str + l - 1;
+       while ((isspace(*p)))
+               *p-- = 0;
+}
+
+static void check_stdin(void)
+{
+       if (!valid_stdin && input_mode == ask_silent) {
+               printf(_("aborted!\n\n"));
+               printf(_("Console input/output is redirected. "));
+               printf(_("Run 'make oldconfig' to update configuration.\n\n"));
+               exit(1);
+       }
+}
+
+static void conf_askvalue(struct symbol *sym, const char *def)
+{
+       enum symbol_type type = sym_get_type(sym);
+       tristate val;
+
+       if (!sym_has_value(sym))
+               printf("(NEW) ");
+
+       line[0] = '\n';
+       line[1] = 0;
+
+       if (!sym_is_changable(sym)) {
+               printf("%s\n", def);
+               line[0] = '\n';
+               line[1] = 0;
+               return;
+       }
+
+       switch (input_mode) {
+       case set_no:
+       case set_mod:
+       case set_yes:
+       case set_random:
+               if (sym_has_value(sym)) {
+                       printf("%s\n", def);
+                       return;
+               }
+               break;
+       case ask_new:
+       case ask_silent:
+               if (sym_has_value(sym)) {
+                       printf("%s\n", def);
+                       return;
+               }
+               check_stdin();
+       case ask_all:
+               fflush(stdout);
+               fgets(line, 128, stdin);
+               return;
+       case set_default:
+               printf("%s\n", def);
+               return;
+       default:
+               break;
+       }
+
+       switch (type) {
+       case S_INT:
+       case S_HEX:
+       case S_STRING:
+               printf("%s\n", def);
+               return;
+       default:
+               ;
+       }
+       switch (input_mode) {
+       case set_yes:
+               if (sym_tristate_within_range(sym, yes)) {
+                       line[0] = 'y';
+                       line[1] = '\n';
+                       line[2] = 0;
+                       break;
+               }
+       case set_mod:
+               if (type == S_TRISTATE) {
+                       if (sym_tristate_within_range(sym, mod)) {
+                               line[0] = 'm';
+                               line[1] = '\n';
+                               line[2] = 0;
+                               break;
+                       }
+               } else {
+                       if (sym_tristate_within_range(sym, yes)) {
+                               line[0] = 'y';
+                               line[1] = '\n';
+                               line[2] = 0;
+                               break;
+                       }
+               }
+       case set_no:
+               if (sym_tristate_within_range(sym, no)) {
+                       line[0] = 'n';
+                       line[1] = '\n';
+                       line[2] = 0;
+                       break;
+               }
+       case set_random:
+               do {
+                       val = (tristate)(random() % 3);
+               } while (!sym_tristate_within_range(sym, val));
+               switch (val) {
+               case no: line[0] = 'n'; break;
+               case mod: line[0] = 'm'; break;
+               case yes: line[0] = 'y'; break;
+               }
+               line[1] = '\n';
+               line[2] = 0;
+               break;
+       default:
+               break;
+       }
+       printf("%s", line);
+}
+
+int conf_string(struct menu *menu)
+{
+       struct symbol *sym = menu->sym;
+       const char *def, *help;
+
+       while (1) {
+               printf("%*s%s ", indent - 1, "", menu->prompt->text);
+               printf("(%s) ", sym->name);
+               def = sym_get_string_value(sym);
+               if (sym_get_string_value(sym))
+                       printf("[%s] ", def);
+               conf_askvalue(sym, def);
+               switch (line[0]) {
+               case '\n':
+                       break;
+               case '?':
+                       /* print help */
+                       if (line[1] == '\n') {
+                               help = nohelp_text;
+                               if (menu->sym->help)
+                                       help = menu->sym->help;
+                               printf("\n%s\n", menu->sym->help);
+                               def = NULL;
+                               break;
+                       }
+               default:
+                       line[strlen(line)-1] = 0;
+                       def = line;
+               }
+               if (def && sym_set_string_value(sym, def))
+                       return 0;
+       }
+}
+
+static int conf_sym(struct menu *menu)
+{
+       struct symbol *sym = menu->sym;
+       int type;
+       tristate oldval, newval;
+       const char *help;
+
+       while (1) {
+               printf("%*s%s ", indent - 1, "", menu->prompt->text);
+               if (sym->name)
+                       printf("(%s) ", sym->name);
+               type = sym_get_type(sym);
+               putchar('[');
+               oldval = sym_get_tristate_value(sym);
+               switch (oldval) {
+               case no:
+                       putchar('N');
+                       break;
+               case mod:
+                       putchar('M');
+                       break;
+               case yes:
+                       putchar('Y');
+                       break;
+               }
+               if (oldval != no && sym_tristate_within_range(sym, no))
+                       printf("/n");
+               if (oldval != mod && sym_tristate_within_range(sym, mod))
+                       printf("/m");
+               if (oldval != yes && sym_tristate_within_range(sym, yes))
+                       printf("/y");
+               if (sym->help)
+                       printf("/?");
+               printf("] ");
+               conf_askvalue(sym, sym_get_string_value(sym));
+               strip(line);
+
+               switch (line[0]) {
+               case 'n':
+               case 'N':
+                       newval = no;
+                       if (!line[1] || !strcmp(&line[1], "o"))
+                               break;
+                       continue;
+               case 'm':
+               case 'M':
+                       newval = mod;
+                       if (!line[1])
+                               break;
+                       continue;
+               case 'y':
+               case 'Y':
+                       newval = yes;
+                       if (!line[1] || !strcmp(&line[1], "es"))
+                               break;
+                       continue;
+               case 0:
+                       newval = oldval;
+                       break;
+               case '?':
+                       goto help;
+               default:
+                       continue;
+               }
+               if (sym_set_tristate_value(sym, newval))
+                       return 0;
+help:
+               help = nohelp_text;
+               if (sym->help)
+                       help = sym->help;
+               printf("\n%s\n", help);
+       }
+}
+
+static int conf_choice(struct menu *menu)
+{
+       struct symbol *sym, *def_sym;
+       struct menu *child;
+       int type;
+       bool is_new;
+
+       sym = menu->sym;
+       type = sym_get_type(sym);
+       is_new = !sym_has_value(sym);
+       if (sym_is_changable(sym)) {
+               conf_sym(menu);
+               sym_calc_value(sym);
+               switch (sym_get_tristate_value(sym)) {
+               case no:
+                       return 1;
+               case mod:
+                       return 0;
+               case yes:
+                       break;
+               }
+       } else {
+               switch (sym_get_tristate_value(sym)) {
+               case no:
+                       return 1;
+               case mod:
+                       printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+                       return 0;
+               case yes:
+                       break;
+               }
+       }
+
+       while (1) {
+               int cnt, def;
+
+               printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+               def_sym = sym_get_choice_value(sym);
+               cnt = def = 0;
+               line[0] = 0;
+               for (child = menu->list; child; child = child->next) {
+                       if (!menu_is_visible(child))
+                               continue;
+                       if (!child->sym) {
+                               printf("%*c %s\n", indent, '*', menu_get_prompt(child));
+                               continue;
+                       }
+                       cnt++;
+                       if (child->sym == def_sym) {
+                               def = cnt;
+                               printf("%*c", indent, '>');
+                       } else
+                               printf("%*c", indent, ' ');
+                       printf(" %d. %s", cnt, menu_get_prompt(child));
+                       if (child->sym->name)
+                               printf(" (%s)", child->sym->name);
+                       if (!sym_has_value(child->sym))
+                               printf(" (NEW)");
+                       printf("\n");
+               }
+               printf("%*schoice", indent - 1, "");
+               if (cnt == 1) {
+                       printf("[1]: 1\n");
+                       goto conf_childs;
+               }
+               printf("[1-%d", cnt);
+               if (sym->help)
+                       printf("?");
+               printf("]: ");
+               switch (input_mode) {
+               case ask_new:
+               case ask_silent:
+                       if (!is_new) {
+                               cnt = def;
+                               printf("%d\n", cnt);
+                               break;
+                       }
+                       check_stdin();
+               case ask_all:
+                       fflush(stdout);
+                       fgets(line, 128, stdin);
+                       strip(line);
+                       if (line[0] == '?') {
+                               printf("\n%s\n", menu->sym->help ?
+                                       menu->sym->help : nohelp_text);
+                               continue;
+                       }
+                       if (!line[0])
+                               cnt = def;
+                       else if (isdigit(line[0]))
+                               cnt = atoi(line);
+                       else
+                               continue;
+                       break;
+               case set_random:
+                       def = (random() % cnt) + 1;
+               case set_default:
+               case set_yes:
+               case set_mod:
+               case set_no:
+                       cnt = def;
+                       printf("%d\n", cnt);
+                       break;
+               }
+
+       conf_childs:
+               for (child = menu->list; child; child = child->next) {
+                       if (!child->sym || !menu_is_visible(child))
+                               continue;
+                       if (!--cnt)
+                               break;
+               }
+               if (!child)
+                       continue;
+               if (line[strlen(line) - 1] == '?') {
+                       printf("\n%s\n", child->sym->help ?
+                               child->sym->help : nohelp_text);
+                       continue;
+               }
+               sym_set_choice_value(sym, child->sym);
+               if (child->list) {
+                       indent += 2;
+                       conf(child->list);
+                       indent -= 2;
+               }
+               return 1;
+       }
+}
+
+static void conf(struct menu *menu)
+{
+       struct symbol *sym;
+       struct property *prop;
+       struct menu *child;
+
+       if (!menu_is_visible(menu))
+               return;
+
+       sym = menu->sym;
+       prop = menu->prompt;
+       if (prop) {
+               const char *prompt;
+
+               switch (prop->type) {
+               case P_MENU:
+                       if (input_mode == ask_silent && rootEntry != menu) {
+                               check_conf(menu);
+                               return;
+                       }
+               case P_COMMENT:
+                       prompt = menu_get_prompt(menu);
+                       if (prompt)
+                               printf("%*c\n%*c %s\n%*c\n",
+                                       indent, '*',
+                                       indent, '*', prompt,
+                                       indent, '*');
+               default:
+                       ;
+               }
+       }
+
+       if (!sym)
+               goto conf_childs;
+
+       if (sym_is_choice(sym)) {
+               conf_choice(menu);
+               if (sym->curr.tri != mod)
+                       return;
+               goto conf_childs;
+       }
+
+       switch (sym->type) {
+       case S_INT:
+       case S_HEX:
+       case S_STRING:
+               conf_string(menu);
+               break;
+       default:
+               conf_sym(menu);
+               break;
+       }
+
+conf_childs:
+       if (sym)
+               indent += 2;
+       for (child = menu->list; child; child = child->next)
+               conf(child);
+       if (sym)
+               indent -= 2;
+}
+
+static void check_conf(struct menu *menu)
+{
+       struct symbol *sym;
+       struct menu *child;
+
+       if (!menu_is_visible(menu))
+               return;
+
+       sym = menu->sym;
+       if (sym && !sym_has_value(sym)) {
+               if (sym_is_changable(sym) ||
+                   (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
+                       if (!conf_cnt++)
+                               printf(_("*\n* Restart config...\n*\n"));
+                       rootEntry = menu_get_parent_menu(menu);
+                       conf(rootEntry);
+               }
+       }
+
+       for (child = menu->list; child; child = child->next)
+               check_conf(child);
+}
+
+int main(int ac, char **av)
+{
+       int i = 1;
+       const char *name;
+       struct stat tmpstat;
+
+       if (ac > i && av[i][0] == '-') {
+               switch (av[i++][1]) {
+               case 'o':
+                       input_mode = ask_new;
+                       break;
+               case 's':
+                       input_mode = ask_silent;
+                       valid_stdin = isatty(0) && isatty(1) && isatty(2);
+                       break;
+               case 'd':
+                       input_mode = set_default;
+                       break;
+               case 'D':
+                       input_mode = set_default;
+                       defconfig_file = av[i++];
+                       if (!defconfig_file) {
+                               printf(_("%s: No default config file specified\n"),
+                                       av[0]);
+                               exit(1);
+                       }
+                       break;
+               case 'n':
+                       input_mode = set_no;
+                       break;
+               case 'm':
+                       input_mode = set_mod;
+                       break;
+               case 'y':
+                       input_mode = set_yes;
+                       break;
+               case 'r':
+                       input_mode = set_random;
+                       srandom(time(NULL));
+                       break;
+               case 'h':
+               case '?':
+                       fprintf(stderr, "See README for usage info\n");
+                       exit(0);
+               }
+       }
+       name = av[i];
+       if (!name) {
+               printf(_("%s: Kconfig file missing\n"), av[0]);
+               exit(1);
+       }
+       conf_parse(name);
+       //zconfdump(stdout);
+       switch (input_mode) {
+       case set_default:
+               if (!defconfig_file)
+                       defconfig_file = conf_get_default_confname();
+               if (conf_read(defconfig_file)) {
+                       printf("***\n"
+                               "*** Can't find default configuration \"%s\"!\n"
+                               "***\n", defconfig_file);
+                       exit(1);
+               }
+               break;
+       case ask_silent:
+               if (stat(".config", &tmpstat)) {
+                       printf(_("***\n"
+                               "*** You have not yet configured your "PROJECT_NAME"!\n"
+                               "***\n"
+                               "*** Please run some configurator (e.g. \"make oldconfig\" or\n"
+                               "*** \"make menuconfig\" or \"make xconfig\").\n"
+                               "***\n"));
+                       exit(1);
+               }
+       case ask_all:
+       case ask_new:
+               conf_read(NULL);
+               break;
+       case set_no:
+       case set_mod:
+       case set_yes:
+       case set_random:
+               name = getenv("KCONFIG_ALLCONFIG");
+               if (name && !stat(name, &tmpstat)) {
+                       conf_read_simple(name, S_DEF_USER);
+                       break;
+               }
+               switch (input_mode) {
+               case set_no:     name = "allno.config"; break;
+               case set_mod:    name = "allmod.config"; break;
+               case set_yes:    name = "allyes.config"; break;
+               case set_random: name = "allrandom.config"; break;
+               default: break;
+               }
+               if (!stat(name, &tmpstat))
+                       conf_read_simple(name, S_DEF_USER);
+               else if (!stat("all.config", &tmpstat))
+                       conf_read_simple("all.config", S_DEF_USER);
+               break;
+       default:
+               break;
+       }
+
+       if (input_mode != ask_silent) {
+               rootEntry = &rootmenu;
+               conf(&rootmenu);
+               if (input_mode == ask_all) {
+                       input_mode = ask_silent;
+                       valid_stdin = 1;
+               }
+       } else if (sym_change_count) {
+               name = getenv("KCONFIG_NOSILENTUPDATE");
+               if (name && *name) {
+                       fprintf(stderr, _("\n*** "PROJECT_NAME" configuration requires explicit update.\n\n"));
+                       return 1;
+               }
+       } else
+               goto skip_check;
+
+       do {
+               conf_cnt = 0;
+               check_conf(&rootmenu);
+       } while (conf_cnt);
+
+       if (!conf_write(NULL)) {
+skip_check:
+               if (!(input_mode == ask_silent && conf_write_autoconf()))
+                       return 0;
+       }
+       fprintf(stderr, _("\n*** Error writing "PROJECT_NAME" configuration.\n\n"));
+       return 1;
+}
diff --git a/kconfig/confdata.c b/kconfig/confdata.c
new file mode 100644 (file)
index 0000000..91c1ed5
--- /dev/null
@@ -0,0 +1,806 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf_warning(const char *fmt, ...)
+       __attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings, conf_unsaved;
+
+#ifndef conf_defname
+const char conf_defname[] = "arch/$ARCH/defconfig";
+#endif
+
+#ifndef CONFIG_PREFIX
+#define CONFIG_PREFIX "CONFIG_"
+#endif
+
+static void conf_warning(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+       conf_warnings++;
+}
+
+const char *conf_get_configname(void)
+{
+       char *name = getenv("KCONFIG_CONFIG");
+
+       return name ? name : ".config";
+}
+
+static char *conf_expand_value(const char *in)
+{
+       struct symbol *sym;
+       const char *src;
+       static char res_value[SYMBOL_MAXLENGTH];
+       char *dst, name[SYMBOL_MAXLENGTH];
+
+       res_value[0] = 0;
+       dst = name;
+       while ((src = strchr(in, '$'))) {
+               strncat(res_value, in, src - in);
+               src++;
+               dst = name;
+               while (isalnum(*src) || *src == '_')
+                       *dst++ = *src++;
+               *dst = 0;
+               sym = sym_lookup(name, 0);
+               sym_calc_value(sym);
+               strcat(res_value, sym_get_string_value(sym));
+               in = src;
+       }
+       strcat(res_value, in);
+
+       return res_value;
+}
+
+char *conf_get_default_confname(void)
+{
+       struct stat buf;
+       static char fullname[PATH_MAX+1];
+       char *env, *name;
+
+       name = conf_expand_value(conf_defname);
+       env = getenv(SRCTREE);
+       if (env) {
+               sprintf(fullname, "%s/%s", env, name);
+               if (!stat(fullname, &buf))
+                       return fullname;
+       }
+       return name;
+}
+
+int conf_read_simple(const char *name, int def)
+{
+       FILE *in = NULL;
+       char line[1024];
+       char *p, *p2;
+       struct symbol *sym;
+       int i, def_flags;
+
+       if (name) {
+               in = zconf_fopen(name);
+       } else {
+               struct property *prop;
+
+               name = conf_get_configname();
+               in = zconf_fopen(name);
+               if (in)
+                       goto load;
+               sym_change_count++;
+               if (!sym_defconfig_list)
+                       return 1;
+
+               for_all_defaults(sym_defconfig_list, prop) {
+                       if (expr_calc_value(prop->visible.expr) == no ||
+                           prop->expr->type != E_SYMBOL)
+                               continue;
+                       name = conf_expand_value(prop->expr->left.sym->name);
+                       in = zconf_fopen(name);
+                       if (in) {
+                               printf(_("#\n"
+                                        "# using defaults found in %s\n"
+                                        "#\n"), name);
+                               goto load;
+                       }
+               }
+       }
+       if (!in)
+               return 1;
+
+load:
+       conf_filename = name;
+       conf_lineno = 0;
+       conf_warnings = 0;
+       conf_unsaved = 0;
+
+       def_flags = SYMBOL_DEF << def;
+       for_all_symbols(i, sym) {
+               sym->flags |= SYMBOL_CHANGED;
+               sym->flags &= ~(def_flags|SYMBOL_VALID);
+               if (sym_is_choice(sym))
+                       sym->flags |= def_flags;
+               switch (sym->type) {
+               case S_INT:
+               case S_HEX:
+               case S_STRING:
+                       if (sym->def[def].val)
+                               free(sym->def[def].val);
+               default:
+                       sym->def[def].val = NULL;
+                       sym->def[def].tri = no;
+               }
+       }
+
+       while (fgets(line, sizeof(line), in)) {
+               conf_lineno++;
+               sym = NULL;
+               switch (line[0]) {
+               case '#':
+                       if (line[1]!=' ' || memcmp(line + 2, CONFIG_PREFIX,
+                                               strlen(CONFIG_PREFIX))) {
+                               continue;
+                       }
+                       p = strchr(line + 2 + strlen(CONFIG_PREFIX), ' ');
+                       if (!p)
+                               continue;
+                       *p++ = 0;
+                       if (strncmp(p, "is not set", 10))
+                               continue;
+                       if (def == S_DEF_USER) {
+                               sym = sym_find(line + 2 + strlen(CONFIG_PREFIX));
+                               if (!sym) {
+                                       conf_warning("trying to assign nonexistent symbol %s", line + 2 + strlen(CONFIG_PREFIX));
+                                       break;
+                               }
+                       } else {
+                               sym = sym_lookup(line + 9, 0);
+                               if (sym->type == S_UNKNOWN)
+                                       sym->type = S_BOOLEAN;
+                       }
+                       if (sym->flags & def_flags) {
+                               conf_warning("trying to reassign symbol %s", sym->name);
+                               break;
+                       }
+                       switch (sym->type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               sym->def[def].tri = no;
+                               sym->flags |= def_flags;
+                               break;
+                       default:
+                               ;
+                       }
+                       break;
+               case 'A' ... 'Z':
+                       if (memcmp(line, CONFIG_PREFIX, strlen(CONFIG_PREFIX))) {
+                               conf_warning("unexpected data");
+                               continue;
+                       }
+                       p = strchr(line + strlen(CONFIG_PREFIX), '=');
+                       if (!p)
+                               continue;
+                       *p++ = 0;
+                       p2 = strchr(p, '\n');
+                       if (p2) {
+                               *p2-- = 0;
+                               if (*p2 == '\r')
+                                       *p2 = 0;
+                       }
+                       if (def == S_DEF_USER) {
+                               sym = sym_find(line + strlen(CONFIG_PREFIX));
+                               if (!sym) {
+                                       conf_warning("trying to assign nonexistent symbol %s", line + 7);
+                                       break;
+                               }
+                       } else {
+                               sym = sym_lookup(line + strlen(CONFIG_PREFIX), 0);
+                               if (sym->type == S_UNKNOWN)
+                                       sym->type = S_OTHER;
+                       }
+                       if (sym->flags & def_flags) {
+                               conf_warning("trying to reassign symbol %s", sym->name);
+                               break;
+                       }
+                       switch (sym->type) {
+                       case S_TRISTATE:
+                               if (p[0] == 'm') {
+                                       sym->def[def].tri = mod;
+                                       sym->flags |= def_flags;
+                                       break;
+                               }
+                       case S_BOOLEAN:
+                               if (p[0] == 'y') {
+                                       sym->def[def].tri = yes;
+                                       sym->flags |= def_flags;
+                                       break;
+                               }
+                               if (p[0] == 'n') {
+                                       sym->def[def].tri = no;
+                                       sym->flags |= def_flags;
+                                       break;
+                               }
+                               conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+                               break;
+                       case S_OTHER:
+                               if (*p != '"') {
+                                       for (p2 = p; *p2 && !isspace(*p2); p2++)
+                                               ;
+                                       sym->type = S_STRING;
+                                       goto done;
+                               }
+                       case S_STRING:
+                               if (*p++ != '"')
+                                       break;
+                               for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+                                       if (*p2 == '"') {
+                                               *p2 = 0;
+                                               break;
+                                       }
+                                       memmove(p2, p2 + 1, strlen(p2));
+                               }
+                               if (!p2) {
+                                       conf_warning("invalid string found");
+                                       continue;
+                               }
+                       case S_INT:
+                       case S_HEX:
+                       done:
+                               if (sym_string_valid(sym, p)) {
+                                       sym->def[def].val = strdup(p);
+                                       sym->flags |= def_flags;
+                               } else {
+                                       conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+                                       continue;
+                               }
+                               break;
+                       default:
+                               ;
+                       }
+                       break;
+               case '\r':
+               case '\n':
+                       break;
+               default:
+                       conf_warning("unexpected data");
+                       continue;
+               }
+               if (sym && sym_is_choice_value(sym)) {
+                       struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+                       switch (sym->def[def].tri) {
+                       case no:
+                               break;
+                       case mod:
+                               if (cs->def[def].tri == yes) {
+                                       conf_warning("%s creates inconsistent choice state", sym->name);
+                                       cs->flags &= ~def_flags;
+                               }
+                               break;
+                       case yes:
+                               if (cs->def[def].tri != no) {
+                                       conf_warning("%s creates inconsistent choice state", sym->name);
+                                       cs->flags &= ~def_flags;
+                               } else
+                                       cs->def[def].val = sym;
+                               break;
+                       }
+                       cs->def[def].tri = E_OR(cs->def[def].tri, sym->def[def].tri);
+               }
+       }
+       fclose(in);
+
+       if (modules_sym)
+               sym_calc_value(modules_sym);
+       return 0;
+}
+
+int conf_read(const char *name)
+{
+       struct symbol *sym;
+       struct property *prop;
+       struct expr *e;
+       int i, flags;
+
+       sym_change_count = 0;
+
+       if (conf_read_simple(name, S_DEF_USER))
+               return 1;
+
+       for_all_symbols(i, sym) {
+               sym_calc_value(sym);
+               if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
+                       goto sym_ok;
+               if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+                       /* check that calculated value agrees with saved value */
+                       switch (sym->type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym))
+                                       break;
+                               if (!sym_is_choice(sym))
+                                       goto sym_ok;
+                       default:
+                               if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
+                                       goto sym_ok;
+                               break;
+                       }
+               } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
+                       /* no previous value and not saved */
+                       goto sym_ok;
+               conf_unsaved++;
+               /* maybe print value in verbose mode... */
+       sym_ok:
+               if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
+                       if (sym->visible == no)
+                               sym->flags &= ~SYMBOL_DEF_USER;
+                       switch (sym->type) {
+                       case S_STRING:
+                       case S_INT:
+                       case S_HEX:
+                               if (!sym_string_within_range(sym, sym->def[S_DEF_USER].val))
+                                       sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
+                       default:
+                               break;
+                       }
+               }
+               if (!sym_is_choice(sym))
+                       continue;
+               prop = sym_get_choice_prop(sym);
+               flags = sym->flags;
+               for (e = prop->expr; e; e = e->left.expr)
+                       if (e->right.sym->visible != no)
+                               flags &= e->right.sym->flags;
+               sym->flags &= flags | ~SYMBOL_DEF_USER;
+       }
+
+       sym_change_count += conf_warnings || conf_unsaved;
+
+       return 0;
+}
+
+struct menu *next_menu(struct menu *menu)
+{
+       if (menu->list) return menu->list;
+       do {
+               if (menu->next) {
+                       menu = menu->next;
+                       break;
+               }
+       } while ((menu = menu->parent));
+
+       return menu;
+}
+
+#define SYMBOL_FORCEWRITE (1<<31)
+
+int conf_write(const char *name)
+{
+       FILE *out;
+       struct symbol *sym;
+       struct menu *menu;
+       const char *basename;
+       char dirname[128], tmpname[128], newname[128];
+       int type, l, writetype;
+       const char *str;
+       time_t now;
+       int use_timestamp = 1;
+       char *env;
+
+       dirname[0] = 0;
+       if (name && name[0]) {
+               struct stat st;
+               char *slash;
+
+               if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
+                       strcpy(dirname, name);
+                       strcat(dirname, "/");
+                       basename = conf_get_configname();
+               } else if ((slash = strrchr(name, '/'))) {
+                       int size = slash - name + 1;
+                       memcpy(dirname, name, size);
+                       dirname[size] = 0;
+                       if (slash[1])
+                               basename = slash + 1;
+                       else
+                               basename = conf_get_configname();
+               } else
+                       basename = name;
+       } else
+               basename = conf_get_configname();
+
+       sprintf(newname, "%s%s", dirname, basename);
+       env = getenv("KCONFIG_OVERWRITECONFIG");
+       if (!env || !*env) {
+               sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid());
+               out = fopen(tmpname, "w");
+       } else {
+               *tmpname = 0;
+               out = fopen(newname, "w");
+       }
+       if (!out)
+               return 1;
+
+       sym = sym_lookup("KCONFIG_VERSION", 0);
+       sym_calc_value(sym);
+       time(&now);
+       env = getenv("KCONFIG_NOTIMESTAMP");
+       if (env && *env)
+               use_timestamp = 0;
+
+       fprintf(out, _("#\n"
+                      "# Automatically generated make config: don't edit\n"
+                      "# "PROJECT_NAME" version: %s\n"
+                      "%s%s"
+                      "#\n"),
+                    sym_get_string_value(sym),
+                    use_timestamp ? "# " : "",
+                    use_timestamp ? ctime(&now) : "");
+
+       if (!sym_change_count)
+               sym_clear_all_valid();
+
+       // Write out all symbols (even in closed sub-menus).
+       if (1) {
+               for (menu = rootmenu.list; menu; menu = next_menu(menu))
+                       if (menu->sym) menu->sym->flags |= SYMBOL_FORCEWRITE;
+               writetype = SYMBOL_FORCEWRITE;
+
+       // Don't write  out symbols in closed menus.
+
+       } else writetype = SYMBOL_WRITE;
+
+
+       menu = rootmenu.list;
+       while (menu) {
+               sym = menu->sym;
+               if (!sym) {
+                       if (!menu_is_visible(menu))
+                               goto next;
+                       str = menu_get_prompt(menu);
+                       fprintf(out, "\n"
+                                    "#\n"
+                                    "# %s\n"
+                                    "#\n", str);
+               } else if (!(sym->flags & SYMBOL_CHOICE)) {
+                       sym_calc_value(sym);
+                       if (!(sym->flags & writetype))
+                               goto next;
+                       sym->flags &= ~writetype;
+                       type = sym->type;
+                       if (type == S_TRISTATE) {
+                               sym_calc_value(modules_sym);
+                               if (modules_sym->curr.tri == no)
+                                       type = S_BOOLEAN;
+                       }
+                       switch (type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               switch (sym_get_tristate_value(sym)) {
+                               case no:
+                                       fprintf(out, "# "CONFIG_PREFIX"%s is not set\n", sym->name);
+                                       break;
+                               case mod:
+                                       fprintf(out, CONFIG_PREFIX"%s=m\n", sym->name);
+                                       break;
+                               case yes:
+                                       fprintf(out, CONFIG_PREFIX"%s=y\n", sym->name);
+                                       break;
+                               }
+                               break;
+                       case S_STRING:
+                               str = sym_get_string_value(sym);
+                               fprintf(out, CONFIG_PREFIX"%s=\"", sym->name);
+                               while (1) {
+                                       l = strcspn(str, "\"\\");
+                                       if (l) {
+                                               fwrite(str, l, 1, out);
+                                               str += l;
+                                       }
+                                       if (!*str)
+                                               break;
+                                       fprintf(out, "\\%c", *str++);
+                               }
+                               fputs("\"\n", out);
+                               break;
+                       case S_HEX:
+                               str = sym_get_string_value(sym);
+                               if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+                                       fprintf(out, CONFIG_PREFIX"%s=%s\n", sym->name, *str ? str : "0");
+                                       break;
+                               }
+                       case S_INT:
+                               str = sym_get_string_value(sym);
+                               fprintf(out, CONFIG_PREFIX"%s=%s\n", sym->name, *str ? str : "0");
+                               break;
+                       }
+               }
+
+       next:
+               if (writetype == SYMBOL_WRITE) {
+                       if (menu->list) {
+                               menu = menu->list;
+                               continue;
+                       }
+                       if (menu->next)
+                               menu = menu->next;
+                       else while ((menu = menu->parent)) {
+                               if (menu->next) {
+                                       menu = menu->next;
+                                       break;
+                               }
+                       }
+               } else
+                       menu = next_menu(menu);
+       }
+       fclose(out);
+
+       if (*tmpname) {
+               strcat(dirname, basename);
+               strcat(dirname, ".old");
+               rename(newname, dirname);
+               if (rename(tmpname, newname))
+                       return 1;
+       }
+
+       printf(_("#\n"
+                "# configuration written to %s\n"
+                "#\n"), newname);
+
+       sym_change_count = 0;
+
+       return 0;
+}
+
+int conf_split_config(void)
+{
+       char *name, path[128];
+       char *s, *d, c;
+       struct symbol *sym;
+       struct stat sb;
+       int res, i, fd;
+
+       name = getenv("KCONFIG_AUTOCONFIG");
+       if (!name)
+               name = "include/config/auto.conf";
+       conf_read_simple(name, S_DEF_AUTO);
+
+       if (chdir("include/config"))
+               return 1;
+
+       res = 0;
+       for_all_symbols(i, sym) {
+               sym_calc_value(sym);
+               if ((sym->flags & SYMBOL_AUTO) || !sym->name)
+                       continue;
+               if (sym->flags & SYMBOL_WRITE) {
+                       if (sym->flags & SYMBOL_DEF_AUTO) {
+                               /*
+                                * symbol has old and new value,
+                                * so compare them...
+                                */
+                               switch (sym->type) {
+                               case S_BOOLEAN:
+                               case S_TRISTATE:
+                                       if (sym_get_tristate_value(sym) ==
+                                           sym->def[S_DEF_AUTO].tri)
+                                               continue;
+                                       break;
+                               case S_STRING:
+                               case S_HEX:
+                               case S_INT:
+                                       if (!strcmp(sym_get_string_value(sym),
+                                                   sym->def[S_DEF_AUTO].val))
+                                               continue;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       } else {
+                               /*
+                                * If there is no old value, only 'no' (unset)
+                                * is allowed as new value.
+                                */
+                               switch (sym->type) {
+                               case S_BOOLEAN:
+                               case S_TRISTATE:
+                                       if (sym_get_tristate_value(sym) == no)
+                                               continue;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+               } else if (!(sym->flags & SYMBOL_DEF_AUTO))
+                       /* There is neither an old nor a new value. */
+                       continue;
+               /* else
+                *      There is an old value, but no new value ('no' (unset)
+                *      isn't saved in auto.conf, so the old value is always
+                *      different from 'no').
+                */
+
+               /* Replace all '_' and append ".h" */
+               s = sym->name;
+               d = path;
+               while ((c = *s++)) {
+                       c = tolower(c);
+                       *d++ = (c == '_') ? '/' : c;
+               }
+               strcpy(d, ".h");
+
+               /* Assume directory path already exists. */
+               fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+               if (fd == -1) {
+                       if (errno != ENOENT) {
+                               res = 1;
+                               break;
+                       }
+                       /*
+                        * Create directory components,
+                        * unless they exist already.
+                        */
+                       d = path;
+                       while ((d = strchr(d, '/'))) {
+                               *d = 0;
+                               if (stat(path, &sb) && mkdir(path, 0755)) {
+                                       res = 1;
+                                       goto out;
+                               }
+                               *d++ = '/';
+                       }
+                       /* Try it again. */
+                       fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                       if (fd == -1) {
+                               res = 1;
+                               break;
+                       }
+               }
+               close(fd);
+       }
+out:
+       if (chdir("../.."))
+               return 1;
+
+       return res;
+}
+
+int conf_write_autoconf(void)
+{
+       struct symbol *sym;
+       const char *str;
+       char *name;
+       FILE *out, *out_h;
+       time_t now;
+       int i, l;
+
+       sym_clear_all_valid();
+
+       file_write_dep("include/config/auto.conf.cmd");
+
+       if (conf_split_config())
+               return 1;
+
+       out = fopen(".tmpconfig", "w");
+       if (!out)
+               return 1;
+
+       out_h = fopen(".tmpconfig.h", "w");
+       if (!out_h) {
+               fclose(out);
+               return 1;
+       }
+
+       sym = sym_lookup("KCONFIG_VERSION", 0);
+       sym_calc_value(sym);
+       time(&now);
+       fprintf(out, "#\n"
+                    "# Automatically generated make config: don't edit\n"
+                    "# "PROJECT_NAME" version: %s\n"
+                    "# %s"
+                    "#\n",
+                    sym_get_string_value(sym), ctime(&now));
+       fprintf(out_h, "/*\n"
+                      " * Automatically generated C config: don't edit\n"
+                      " * "PROJECT_NAME" version: %s\n"
+                      " * %s"
+                      " */\n"
+                      // "#define AUTOCONF_INCLUDED\n"
+                      , sym_get_string_value(sym), ctime(&now));
+
+       for_all_symbols(i, sym) {
+               sym_calc_value(sym);
+               if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
+                       continue;
+               switch (sym->type) {
+               case S_BOOLEAN:
+               case S_TRISTATE:
+                       switch (sym_get_tristate_value(sym)) {
+                       case no:
+                               break;
+                       case mod:
+                               fprintf(out, CONFIG_PREFIX"%s=m\n", sym->name);
+                               fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
+                               break;
+                       case yes:
+                               fprintf(out, CONFIG_PREFIX"%s=y\n", sym->name);
+                               fprintf(out_h, "#define "CONFIG_PREFIX"%s 1\n", sym->name);
+                               break;
+                       }
+                       break;
+               case S_STRING:
+                       str = sym_get_string_value(sym);
+                       fprintf(out, CONFIG_PREFIX"%s=\"", sym->name);
+                       fprintf(out_h, "#define "CONFIG_PREFIX"%s \"", sym->name);
+                       while (1) {
+                               l = strcspn(str, "\"\\");
+                               if (l) {
+                                       fwrite(str, l, 1, out);
+                                       fwrite(str, l, 1, out_h);
+                                       str += l;
+                               }
+                               if (!*str)
+                                       break;
+                               fprintf(out, "\\%c", *str);
+                               fprintf(out_h, "\\%c", *str);
+                               str++;
+                       }
+                       fputs("\"\n", out);
+                       fputs("\"\n", out_h);
+                       break;
+               case S_HEX:
+                       str = sym_get_string_value(sym);
+                       if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+                               fprintf(out, CONFIG_PREFIX"%s=%s\n", sym->name, str);
+                               fprintf(out_h, "#define "CONFIG_PREFIX"%s 0x%s\n", sym->name, str);
+                               break;
+                       }
+               case S_INT:
+                       str = sym_get_string_value(sym);
+                       fprintf(out, CONFIG_PREFIX"%s=%s\n", sym->name, str);
+                       fprintf(out_h, "#define "CONFIG_PREFIX"%s %s\n", sym->name, str);
+                       break;
+               default:
+                       break;
+               }
+       }
+       fclose(out);
+       fclose(out_h);
+
+       name = getenv("KCONFIG_AUTOHEADER");
+       if (!name)
+               name = "include/linux/autoconf.h";
+       if (rename(".tmpconfig.h", name))
+               return 1;
+       name = getenv("KCONFIG_AUTOCONFIG");
+       if (!name)
+               name = "include/config/auto.conf";
+       /*
+        * This must be the last step, kbuild has a dependency on auto.conf
+        * and this marks the successful completion of the previous steps.
+        */
+       if (rename(".tmpconfig", name))
+               return 1;
+
+       return 0;
+}
diff --git a/kconfig/expr.c b/kconfig/expr.c
new file mode 100644 (file)
index 0000000..6f98dbf
--- /dev/null
@@ -0,0 +1,1100 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define DEBUG_EXPR     0
+
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = E_SYMBOL;
+       e->left.sym = sym;
+       return e;
+}
+
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = type;
+       e->left.expr = ce;
+       return e;
+}
+
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = type;
+       e->left.expr = e1;
+       e->right.expr = e2;
+       return e;
+}
+
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = type;
+       e->left.sym = s1;
+       e->right.sym = s2;
+       return e;
+}
+
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
+{
+       if (!e1)
+               return e2;
+       return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
+}
+
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
+{
+       if (!e1)
+               return e2;
+       return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
+}
+
+struct expr *expr_copy(struct expr *org)
+{
+       struct expr *e;
+
+       if (!org)
+               return NULL;
+
+       e = malloc(sizeof(*org));
+       memcpy(e, org, sizeof(*org));
+       switch (org->type) {
+       case E_SYMBOL:
+               e->left = org->left;
+               break;
+       case E_NOT:
+               e->left.expr = expr_copy(org->left.expr);
+               break;
+       case E_EQUAL:
+       case E_UNEQUAL:
+               e->left.sym = org->left.sym;
+               e->right.sym = org->right.sym;
+               break;
+       case E_AND:
+       case E_OR:
+       case E_CHOICE:
+               e->left.expr = expr_copy(org->left.expr);
+               e->right.expr = expr_copy(org->right.expr);
+               break;
+       default:
+               printf("can't copy type %d\n", e->type);
+               free(e);
+               e = NULL;
+               break;
+       }
+
+       return e;
+}
+
+void expr_free(struct expr *e)
+{
+       if (!e)
+               return;
+
+       switch (e->type) {
+       case E_SYMBOL:
+               break;
+       case E_NOT:
+               expr_free(e->left.expr);
+               return;
+       case E_EQUAL:
+       case E_UNEQUAL:
+               break;
+       case E_OR:
+       case E_AND:
+               expr_free(e->left.expr);
+               expr_free(e->right.expr);
+               break;
+       default:
+               printf("how to free type %d?\n", e->type);
+               break;
+       }
+       free(e);
+}
+
+static int trans_count;
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+       if (e1->type == type) {
+               __expr_eliminate_eq(type, &e1->left.expr, &e2);
+               __expr_eliminate_eq(type, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               __expr_eliminate_eq(type, &e1, &e2->left.expr);
+               __expr_eliminate_eq(type, &e1, &e2->right.expr);
+               return;
+       }
+       if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+           e1->left.sym == e2->left.sym &&
+           (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no))
+               return;
+       if (!expr_eq(e1, e2))
+               return;
+       trans_count++;
+       expr_free(e1); expr_free(e2);
+       switch (type) {
+       case E_OR:
+               e1 = expr_alloc_symbol(&symbol_no);
+               e2 = expr_alloc_symbol(&symbol_no);
+               break;
+       case E_AND:
+               e1 = expr_alloc_symbol(&symbol_yes);
+               e2 = expr_alloc_symbol(&symbol_yes);
+               break;
+       default:
+               ;
+       }
+}
+
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+{
+       if (!e1 || !e2)
+               return;
+       switch (e1->type) {
+       case E_OR:
+       case E_AND:
+               __expr_eliminate_eq(e1->type, ep1, ep2);
+       default:
+               ;
+       }
+       if (e1->type != e2->type) switch (e2->type) {
+       case E_OR:
+       case E_AND:
+               __expr_eliminate_eq(e2->type, ep1, ep2);
+       default:
+               ;
+       }
+       e1 = expr_eliminate_yn(e1);
+       e2 = expr_eliminate_yn(e2);
+}
+
+#undef e1
+#undef e2
+
+int expr_eq(struct expr *e1, struct expr *e2)
+{
+       int res, old_count;
+
+       if (e1->type != e2->type)
+               return 0;
+       switch (e1->type) {
+       case E_EQUAL:
+       case E_UNEQUAL:
+               return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
+       case E_SYMBOL:
+               return e1->left.sym == e2->left.sym;
+       case E_NOT:
+               return expr_eq(e1->left.expr, e2->left.expr);
+       case E_AND:
+       case E_OR:
+               e1 = expr_copy(e1);
+               e2 = expr_copy(e2);
+               old_count = trans_count;
+               expr_eliminate_eq(&e1, &e2);
+               res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+                      e1->left.sym == e2->left.sym);
+               expr_free(e1);
+               expr_free(e2);
+               trans_count = old_count;
+               return res;
+       case E_CHOICE:
+       case E_RANGE:
+       case E_NONE:
+               /* panic */;
+       }
+
+       if (DEBUG_EXPR) {
+               expr_fprint(e1, stdout);
+               printf(" = ");
+               expr_fprint(e2, stdout);
+               printf(" ?\n");
+       }
+
+       return 0;
+}
+
+struct expr *expr_eliminate_yn(struct expr *e)
+{
+       struct expr *tmp;
+
+       if (e) switch (e->type) {
+       case E_AND:
+               e->left.expr = expr_eliminate_yn(e->left.expr);
+               e->right.expr = expr_eliminate_yn(e->right.expr);
+               if (e->left.expr->type == E_SYMBOL) {
+                       if (e->left.expr->left.sym == &symbol_no) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_no;
+                               e->right.expr = NULL;
+                               return e;
+                       } else if (e->left.expr->left.sym == &symbol_yes) {
+                               free(e->left.expr);
+                               tmp = e->right.expr;
+                               *e = *(e->right.expr);
+                               free(tmp);
+                               return e;
+                       }
+               }
+               if (e->right.expr->type == E_SYMBOL) {
+                       if (e->right.expr->left.sym == &symbol_no) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_no;
+                               e->right.expr = NULL;
+                               return e;
+                       } else if (e->right.expr->left.sym == &symbol_yes) {
+                               free(e->right.expr);
+                               tmp = e->left.expr;
+                               *e = *(e->left.expr);
+                               free(tmp);
+                               return e;
+                       }
+               }
+               break;
+       case E_OR:
+               e->left.expr = expr_eliminate_yn(e->left.expr);
+               e->right.expr = expr_eliminate_yn(e->right.expr);
+               if (e->left.expr->type == E_SYMBOL) {
+                       if (e->left.expr->left.sym == &symbol_no) {
+                               free(e->left.expr);
+                               tmp = e->right.expr;
+                               *e = *(e->right.expr);
+                               free(tmp);
+                               return e;
+                       } else if (e->left.expr->left.sym == &symbol_yes) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_yes;
+                               e->right.expr = NULL;
+                               return e;
+                       }
+               }
+               if (e->right.expr->type == E_SYMBOL) {
+                       if (e->right.expr->left.sym == &symbol_no) {
+                               free(e->right.expr);
+                               tmp = e->left.expr;
+                               *e = *(e->left.expr);
+                               free(tmp);
+                               return e;
+                       } else if (e->right.expr->left.sym == &symbol_yes) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_yes;
+                               e->right.expr = NULL;
+                               return e;
+                       }
+               }
+               break;
+       default:
+               ;
+       }
+       return e;
+}
+
+/*
+ * bool FOO!=n => FOO
+ */
+struct expr *expr_trans_bool(struct expr *e)
+{
+       if (!e)
+               return NULL;
+       switch (e->type) {
+       case E_AND:
+       case E_OR:
+       case E_NOT:
+               e->left.expr = expr_trans_bool(e->left.expr);
+               e->right.expr = expr_trans_bool(e->right.expr);
+               break;
+       case E_UNEQUAL:
+               // FOO!=n -> FOO
+               if (e->left.sym->type == S_TRISTATE) {
+                       if (e->right.sym == &symbol_no) {
+                               e->type = E_SYMBOL;
+                               e->right.sym = NULL;
+                       }
+               }
+               break;
+       default:
+               ;
+       }
+       return e;
+}
+
+/*
+ * e1 || e2 -> ?
+ */
+struct expr *expr_join_or(struct expr *e1, struct expr *e2)
+{
+       struct expr *tmp;
+       struct symbol *sym1, *sym2;
+
+       if (expr_eq(e1, e2))
+               return expr_copy(e1);
+       if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+               return NULL;
+       if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+               return NULL;
+       if (e1->type == E_NOT) {
+               tmp = e1->left.expr;
+               if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+                       return NULL;
+               sym1 = tmp->left.sym;
+       } else
+               sym1 = e1->left.sym;
+       if (e2->type == E_NOT) {
+               if (e2->left.expr->type != E_SYMBOL)
+                       return NULL;
+               sym2 = e2->left.expr->left.sym;
+       } else
+               sym2 = e2->left.sym;
+       if (sym1 != sym2)
+               return NULL;
+       if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+               return NULL;
+       if (sym1->type == S_TRISTATE) {
+               if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+                   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+                    (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
+                       // (a='y') || (a='m') -> (a!='n')
+                       return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
+               }
+               if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+                   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+                    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
+                       // (a='y') || (a='n') -> (a!='m')
+                       return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
+               }
+               if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+                   ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+                    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
+                       // (a='m') || (a='n') -> (a!='y')
+                       return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
+               }
+       }
+       if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+               if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
+                   (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
+                       return expr_alloc_symbol(&symbol_yes);
+       }
+
+       if (DEBUG_EXPR) {
+               printf("optimize (");
+               expr_fprint(e1, stdout);
+               printf(") || (");
+               expr_fprint(e2, stdout);
+               printf(")?\n");
+       }
+       return NULL;
+}
+
+struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+{
+       struct expr *tmp;
+       struct symbol *sym1, *sym2;
+
+       if (expr_eq(e1, e2))
+               return expr_copy(e1);
+       if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+               return NULL;
+       if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+               return NULL;
+       if (e1->type == E_NOT) {
+               tmp = e1->left.expr;
+               if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+                       return NULL;
+               sym1 = tmp->left.sym;
+       } else
+               sym1 = e1->left.sym;
+       if (e2->type == E_NOT) {
+               if (e2->left.expr->type != E_SYMBOL)
+                       return NULL;
+               sym2 = e2->left.expr->left.sym;
+       } else
+               sym2 = e2->left.sym;
+       if (sym1 != sym2)
+               return NULL;
+       if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+               return NULL;
+
+       if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
+           (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
+               // (a) && (a='y') -> (a='y')
+               return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+       if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
+           (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
+               // (a) && (a!='n') -> (a)
+               return expr_alloc_symbol(sym1);
+
+       if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
+           (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
+               // (a) && (a!='m') -> (a='y')
+               return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+       if (sym1->type == S_TRISTATE) {
+               if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
+                       // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+                       sym2 = e1->right.sym;
+                       if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+                               return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+                                                            : expr_alloc_symbol(&symbol_no);
+               }
+               if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
+                       // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+                       sym2 = e2->right.sym;
+                       if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+                               return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+                                                            : expr_alloc_symbol(&symbol_no);
+               }
+               if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+                          ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+                           (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
+                       // (a!='y') && (a!='n') -> (a='m')
+                       return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
+
+               if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+                          ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+                           (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
+                       // (a!='y') && (a!='m') -> (a='n')
+                       return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
+
+               if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+                          ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+                           (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
+                       // (a!='m') && (a!='n') -> (a='m')
+                       return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+               if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
+                   (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
+                   (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
+                   (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
+                       return NULL;
+       }
+
+       if (DEBUG_EXPR) {
+               printf("optimize (");
+               expr_fprint(e1, stdout);
+               printf(") && (");
+               expr_fprint(e2, stdout);
+               printf(")?\n");
+       }
+       return NULL;
+}
+
+static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+       struct expr *tmp;
+
+       if (e1->type == type) {
+               expr_eliminate_dups1(type, &e1->left.expr, &e2);
+               expr_eliminate_dups1(type, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               expr_eliminate_dups1(type, &e1, &e2->left.expr);
+               expr_eliminate_dups1(type, &e1, &e2->right.expr);
+               return;
+       }
+       if (e1 == e2)
+               return;
+
+       switch (e1->type) {
+       case E_OR: case E_AND:
+               expr_eliminate_dups1(e1->type, &e1, &e1);
+       default:
+               ;
+       }
+
+       switch (type) {
+       case E_OR:
+               tmp = expr_join_or(e1, e2);
+               if (tmp) {
+                       expr_free(e1); expr_free(e2);
+                       e1 = expr_alloc_symbol(&symbol_no);
+                       e2 = tmp;
+                       trans_count++;
+               }
+               break;
+       case E_AND:
+               tmp = expr_join_and(e1, e2);
+               if (tmp) {
+                       expr_free(e1); expr_free(e2);
+                       e1 = expr_alloc_symbol(&symbol_yes);
+                       e2 = tmp;
+                       trans_count++;
+               }
+               break;
+       default:
+               ;
+       }
+#undef e1
+#undef e2
+}
+
+static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+       struct expr *tmp, *tmp1, *tmp2;
+
+       if (e1->type == type) {
+               expr_eliminate_dups2(type, &e1->left.expr, &e2);
+               expr_eliminate_dups2(type, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               expr_eliminate_dups2(type, &e1, &e2->left.expr);
+               expr_eliminate_dups2(type, &e1, &e2->right.expr);
+       }
+       if (e1 == e2)
+               return;
+
+       switch (e1->type) {
+       case E_OR:
+               expr_eliminate_dups2(e1->type, &e1, &e1);
+               // (FOO || BAR) && (!FOO && !BAR) -> n
+               tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+               tmp2 = expr_copy(e2);
+               tmp = expr_extract_eq_and(&tmp1, &tmp2);
+               if (expr_is_yes(tmp1)) {
+                       expr_free(e1);
+                       e1 = expr_alloc_symbol(&symbol_no);
+                       trans_count++;
+               }
+               expr_free(tmp2);
+               expr_free(tmp1);
+               expr_free(tmp);
+               break;
+       case E_AND:
+               expr_eliminate_dups2(e1->type, &e1, &e1);
+               // (FOO && BAR) || (!FOO || !BAR) -> y
+               tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+               tmp2 = expr_copy(e2);
+               tmp = expr_extract_eq_or(&tmp1, &tmp2);
+               if (expr_is_no(tmp1)) {
+                       expr_free(e1);
+                       e1 = expr_alloc_symbol(&symbol_yes);
+                       trans_count++;
+               }
+               expr_free(tmp2);
+               expr_free(tmp1);
+               expr_free(tmp);
+               break;
+       default:
+               ;
+       }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_eliminate_dups(struct expr *e)
+{
+       int oldcount;
+       if (!e)
+               return e;
+
+       oldcount = trans_count;
+       while (1) {
+               trans_count = 0;
+               switch (e->type) {
+               case E_OR: case E_AND:
+                       expr_eliminate_dups1(e->type, &e, &e);
+                       expr_eliminate_dups2(e->type, &e, &e);
+               default:
+                       ;
+               }
+               if (!trans_count)
+                       break;
+               e = expr_eliminate_yn(e);
+       }
+       trans_count = oldcount;
+       return e;
+}
+
+struct expr *expr_transform(struct expr *e)
+{
+       struct expr *tmp;
+
+       if (!e)
+               return NULL;
+       switch (e->type) {
+       case E_EQUAL:
+       case E_UNEQUAL:
+       case E_SYMBOL:
+       case E_CHOICE:
+               break;
+       default:
+               e->left.expr = expr_transform(e->left.expr);
+               e->right.expr = expr_transform(e->right.expr);
+       }
+
+       switch (e->type) {
+       case E_EQUAL:
+               if (e->left.sym->type != S_BOOLEAN)
+                       break;
+               if (e->right.sym == &symbol_no) {
+                       e->type = E_NOT;
+                       e->left.expr = expr_alloc_symbol(e->left.sym);
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_mod) {
+                       printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
+                       e->type = E_SYMBOL;
+                       e->left.sym = &symbol_no;
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_yes) {
+                       e->type = E_SYMBOL;
+                       e->right.sym = NULL;
+                       break;
+               }
+               break;
+       case E_UNEQUAL:
+               if (e->left.sym->type != S_BOOLEAN)
+                       break;
+               if (e->right.sym == &symbol_no) {
+                       e->type = E_SYMBOL;
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_mod) {
+                       printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
+                       e->type = E_SYMBOL;
+                       e->left.sym = &symbol_yes;
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_yes) {
+                       e->type = E_NOT;
+                       e->left.expr = expr_alloc_symbol(e->left.sym);
+                       e->right.sym = NULL;
+                       break;
+               }
+               break;
+       case E_NOT:
+               switch (e->left.expr->type) {
+               case E_NOT:
+                       // !!a -> a
+                       tmp = e->left.expr->left.expr;
+                       free(e->left.expr);
+                       free(e);
+                       e = tmp;
+                       e = expr_transform(e);
+                       break;
+               case E_EQUAL:
+               case E_UNEQUAL:
+                       // !a='x' -> a!='x'
+                       tmp = e->left.expr;
+                       free(e);
+                       e = tmp;
+                       e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+                       break;
+               case E_OR:
+                       // !(a || b) -> !a && !b
+                       tmp = e->left.expr;
+                       e->type = E_AND;
+                       e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+                       tmp->type = E_NOT;
+                       tmp->right.expr = NULL;
+                       e = expr_transform(e);
+                       break;
+               case E_AND:
+                       // !(a && b) -> !a || !b
+                       tmp = e->left.expr;
+                       e->type = E_OR;
+                       e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+                       tmp->type = E_NOT;
+                       tmp->right.expr = NULL;
+                       e = expr_transform(e);
+                       break;
+               case E_SYMBOL:
+                       if (e->left.expr->left.sym == &symbol_yes) {
+                               // !'y' -> 'n'
+                               tmp = e->left.expr;
+                               free(e);
+                               e = tmp;
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_no;
+                               break;
+                       }
+                       if (e->left.expr->left.sym == &symbol_mod) {
+                               // !'m' -> 'm'
+                               tmp = e->left.expr;
+                               free(e);
+                               e = tmp;
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_mod;
+                               break;
+                       }
+                       if (e->left.expr->left.sym == &symbol_no) {
+                               // !'n' -> 'y'
+                               tmp = e->left.expr;
+                               free(e);
+                               e = tmp;
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_yes;
+                               break;
+                       }
+                       break;
+               default:
+                       ;
+               }
+               break;
+       default:
+               ;
+       }
+       return e;
+}
+
+int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+{
+       if (!dep)
+               return 0;
+
+       switch (dep->type) {
+       case E_AND:
+       case E_OR:
+               return expr_contains_symbol(dep->left.expr, sym) ||
+                      expr_contains_symbol(dep->right.expr, sym);
+       case E_SYMBOL:
+               return dep->left.sym == sym;
+       case E_EQUAL:
+       case E_UNEQUAL:
+               return dep->left.sym == sym ||
+                      dep->right.sym == sym;
+       case E_NOT:
+               return expr_contains_symbol(dep->left.expr, sym);
+       default:
+               ;
+       }
+       return 0;
+}
+
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+{
+       if (!dep)
+               return false;
+
+       switch (dep->type) {
+       case E_AND:
+               return expr_depends_symbol(dep->left.expr, sym) ||
+                      expr_depends_symbol(dep->right.expr, sym);
+       case E_SYMBOL:
+               return dep->left.sym == sym;
+       case E_EQUAL:
+               if (dep->left.sym == sym) {
+                       if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
+                               return true;
+               }
+               break;
+       case E_UNEQUAL:
+               if (dep->left.sym == sym) {
+                       if (dep->right.sym == &symbol_no)
+                               return true;
+               }
+               break;
+       default:
+               ;
+       }
+       return false;
+}
+
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2)
+{
+       struct expr *tmp = NULL;
+       expr_extract_eq(E_AND, &tmp, ep1, ep2);
+       if (tmp) {
+               *ep1 = expr_eliminate_yn(*ep1);
+               *ep2 = expr_eliminate_yn(*ep2);
+       }
+       return tmp;
+}
+
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2)
+{
+       struct expr *tmp = NULL;
+       expr_extract_eq(E_OR, &tmp, ep1, ep2);
+       if (tmp) {
+               *ep1 = expr_eliminate_yn(*ep1);
+               *ep2 = expr_eliminate_yn(*ep2);
+       }
+       return tmp;
+}
+
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+       if (e1->type == type) {
+               expr_extract_eq(type, ep, &e1->left.expr, &e2);
+               expr_extract_eq(type, ep, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               expr_extract_eq(type, ep, ep1, &e2->left.expr);
+               expr_extract_eq(type, ep, ep1, &e2->right.expr);
+               return;
+       }
+       if (expr_eq(e1, e2)) {
+               *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1;
+               expr_free(e2);
+               if (type == E_AND) {
+                       e1 = expr_alloc_symbol(&symbol_yes);
+                       e2 = expr_alloc_symbol(&symbol_yes);
+               } else if (type == E_OR) {
+                       e1 = expr_alloc_symbol(&symbol_no);
+                       e2 = expr_alloc_symbol(&symbol_no);
+               }
+       }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+{
+       struct expr *e1, *e2;
+
+       if (!e) {
+               e = expr_alloc_symbol(sym);
+               if (type == E_UNEQUAL)
+                       e = expr_alloc_one(E_NOT, e);
+               return e;
+       }
+       switch (e->type) {
+       case E_AND:
+               e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+               e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+               if (sym == &symbol_yes)
+                       e = expr_alloc_two(E_AND, e1, e2);
+               if (sym == &symbol_no)
+                       e = expr_alloc_two(E_OR, e1, e2);
+               if (type == E_UNEQUAL)
+                       e = expr_alloc_one(E_NOT, e);
+               return e;
+       case E_OR:
+               e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+               e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+               if (sym == &symbol_yes)
+                       e = expr_alloc_two(E_OR, e1, e2);
+               if (sym == &symbol_no)
+                       e = expr_alloc_two(E_AND, e1, e2);
+               if (type == E_UNEQUAL)
+                       e = expr_alloc_one(E_NOT, e);
+               return e;
+       case E_NOT:
+               return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
+       case E_UNEQUAL:
+       case E_EQUAL:
+               if (type == E_EQUAL) {
+                       if (sym == &symbol_yes)
+                               return expr_copy(e);
+                       if (sym == &symbol_mod)
+                               return expr_alloc_symbol(&symbol_no);
+                       if (sym == &symbol_no)
+                               return expr_alloc_one(E_NOT, expr_copy(e));
+               } else {
+                       if (sym == &symbol_yes)
+                               return expr_alloc_one(E_NOT, expr_copy(e));
+                       if (sym == &symbol_mod)
+                               return expr_alloc_symbol(&symbol_yes);
+                       if (sym == &symbol_no)
+                               return expr_copy(e);
+               }
+               break;
+       case E_SYMBOL:
+               return expr_alloc_comp(type, e->left.sym, sym);
+       case E_CHOICE:
+       case E_RANGE:
+       case E_NONE:
+               /* panic */;
+       }
+       return NULL;
+}
+
+tristate expr_calc_value(struct expr *e)
+{
+       tristate val1, val2;
+       const char *str1, *str2;
+
+       if (!e)
+               return yes;
+
+       switch (e->type) {
+       case E_SYMBOL:
+               sym_calc_value(e->left.sym);
+               return e->left.sym->curr.tri;
+       case E_AND:
+               val1 = expr_calc_value(e->left.expr);
+               val2 = expr_calc_value(e->right.expr);
+               return E_AND(val1, val2);
+       case E_OR:
+               val1 = expr_calc_value(e->left.expr);
+               val2 = expr_calc_value(e->right.expr);
+               return E_OR(val1, val2);
+       case E_NOT:
+               val1 = expr_calc_value(e->left.expr);
+               return E_NOT(val1);
+       case E_EQUAL:
+               sym_calc_value(e->left.sym);
+               sym_calc_value(e->right.sym);
+               str1 = sym_get_string_value(e->left.sym);
+               str2 = sym_get_string_value(e->right.sym);
+               return !strcmp(str1, str2) ? yes : no;
+       case E_UNEQUAL:
+               sym_calc_value(e->left.sym);
+               sym_calc_value(e->right.sym);
+               str1 = sym_get_string_value(e->left.sym);
+               str2 = sym_get_string_value(e->right.sym);
+               return !strcmp(str1, str2) ? no : yes;
+       default:
+               printf("expr_calc_value: %d?\n", e->type);
+               return no;
+       }
+}
+
+int expr_compare_type(enum expr_type t1, enum expr_type t2)
+{
+#if 0
+       return 1;
+#else
+       if (t1 == t2)
+               return 0;
+       switch (t1) {
+       case E_EQUAL:
+       case E_UNEQUAL:
+               if (t2 == E_NOT)
+                       return 1;
+       case E_NOT:
+               if (t2 == E_AND)
+                       return 1;
+       case E_AND:
+               if (t2 == E_OR)
+                       return 1;
+       case E_OR:
+               if (t2 == E_CHOICE)
+                       return 1;
+       case E_CHOICE:
+               if (t2 == 0)
+                       return 1;
+       default:
+               return -1;
+       }
+       printf("[%dgt%d?]", t1, t2);
+       return 0;
+#endif
+}
+
+void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)
+{
+       if (!e) {
+               fn(data, NULL, "y");
+               return;
+       }
+
+       if (expr_compare_type(prevtoken, e->type) > 0)
+               fn(data, NULL, "(");
+       switch (e->type) {
+       case E_SYMBOL:
+               if (e->left.sym->name)
+                       fn(data, e->left.sym, e->left.sym->name);
+               else
+                       fn(data, NULL, "<choice>");
+               break;
+       case E_NOT:
+               fn(data, NULL, "!");
+               expr_print(e->left.expr, fn, data, E_NOT);
+               break;
+       case E_EQUAL:
+               fn(data, e->left.sym, e->left.sym->name);
+               fn(data, NULL, "=");
+               fn(data, e->right.sym, e->right.sym->name);
+               break;
+       case E_UNEQUAL:
+               fn(data, e->left.sym, e->left.sym->name);
+               fn(data, NULL, "!=");
+               fn(data, e->right.sym, e->right.sym->name);
+               break;
+       case E_OR:
+               expr_print(e->left.expr, fn, data, E_OR);
+               fn(data, NULL, " || ");
+               expr_print(e->right.expr, fn, data, E_OR);
+               break;
+       case E_AND:
+               expr_print(e->left.expr, fn, data, E_AND);
+               fn(data, NULL, " && ");
+               expr_print(e->right.expr, fn, data, E_AND);
+               break;
+       case E_CHOICE:
+               fn(data, e->right.sym, e->right.sym->name);
+               if (e->left.expr) {
+                       fn(data, NULL, " ^ ");
+                       expr_print(e->left.expr, fn, data, E_CHOICE);
+               }
+               break;
+       case E_RANGE:
+               fn(data, NULL, "[");
+               fn(data, e->left.sym, e->left.sym->name);
+               fn(data, NULL, " ");
+               fn(data, e->right.sym, e->right.sym->name);
+               fn(data, NULL, "]");
+               break;
+       default:
+         {
+               char buf[32];
+               sprintf(buf, "<unknown type %d>", e->type);
+               fn(data, NULL, buf);
+               break;
+         }
+       }
+       if (expr_compare_type(prevtoken, e->type) > 0)
+               fn(data, NULL, ")");
+}
+
+static void expr_print_file_helper(void *data, struct symbol *sym, const char *str)
+{
+       fwrite(str, strlen(str), 1, data);
+}
+
+void expr_fprint(struct expr *e, FILE *out)
+{
+       expr_print(e, expr_print_file_helper, out, E_NONE);
+}
+
+static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str)
+{
+       str_append((struct gstr*)data, str);
+}
+
+void expr_gstr_print(struct expr *e, struct gstr *gs)
+{
+       expr_print(e, expr_print_gstr_helper, gs, E_NONE);
+}
diff --git a/kconfig/expr.h b/kconfig/expr.h
new file mode 100644 (file)
index 0000000..6084525
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef EXPR_H
+#define EXPR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+struct file {
+       struct file *next;
+       struct file *parent;
+       char *name;
+       int lineno;
+       int flags;
+};
+
+#define FILE_BUSY              0x0001
+#define FILE_SCANNED           0x0002
+#define FILE_PRINTED           0x0004
+
+typedef enum tristate {
+       no, mod, yes
+} tristate;
+
+enum expr_type {
+       E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE
+};
+
+union expr_data {
+       struct expr *expr;
+       struct symbol *sym;
+};
+
+struct expr {
+       enum expr_type type;
+       union expr_data left, right;
+};
+
+#define E_OR(dep1, dep2)       (((dep1)>(dep2))?(dep1):(dep2))
+#define E_AND(dep1, dep2)      (((dep1)<(dep2))?(dep1):(dep2))
+#define E_NOT(dep)             (2-(dep))
+
+struct expr_value {
+       struct expr *expr;
+       tristate tri;
+};
+
+struct symbol_value {
+       void *val;
+       tristate tri;
+};
+
+enum symbol_type {
+       S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
+};
+
+enum {
+       S_DEF_USER,             /* main user value */
+       S_DEF_AUTO,
+};
+
+struct symbol {
+       struct symbol *next;
+       char *name;
+       char *help;
+       enum symbol_type type;
+       struct symbol_value curr;
+       struct symbol_value def[4];
+       tristate visible;
+       int flags;
+       struct property *prop;
+       struct expr *dep, *dep2;
+       struct expr_value rev_dep;
+};
+
+#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
+
+#define SYMBOL_CONST           0x0001
+#define SYMBOL_CHECK           0x0008
+#define SYMBOL_CHOICE          0x0010
+#define SYMBOL_CHOICEVAL       0x0020
+#define SYMBOL_PRINTED         0x0040
+#define SYMBOL_VALID           0x0080
+#define SYMBOL_OPTIONAL                0x0100
+#define SYMBOL_WRITE           0x0200
+#define SYMBOL_CHANGED         0x0400
+#define SYMBOL_AUTO            0x1000
+#define SYMBOL_CHECKED         0x2000
+#define SYMBOL_WARNED          0x8000
+#define SYMBOL_DEF             0x10000
+#define SYMBOL_DEF_USER                0x10000
+#define SYMBOL_DEF_AUTO                0x20000
+#define SYMBOL_DEF3            0x40000
+#define SYMBOL_DEF4            0x80000
+
+#define SYMBOL_MAXLENGTH       256
+#define SYMBOL_HASHSIZE                257
+#define SYMBOL_HASHMASK                0xff
+
+enum prop_type {
+       P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE
+};
+
+struct property {
+       struct property *next;
+       struct symbol *sym;
+       enum prop_type type;
+       const char *text;
+       struct expr_value visible;
+       struct expr *expr;
+       struct menu *menu;
+       struct file *file;
+       int lineno;
+};
+
+#define for_all_properties(sym, st, tok) \
+       for (st = sym->prop; st; st = st->next) \
+               if (st->type == (tok))
+#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
+#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
+#define for_all_prompts(sym, st) \
+       for (st = sym->prop; st; st = st->next) \
+               if (st->text)
+
+struct menu {
+       struct menu *next;
+       struct menu *parent;
+       struct menu *list;
+       struct symbol *sym;
+       struct property *prompt;
+       struct expr *dep;
+       unsigned int flags;
+       //char *help;
+       struct file *file;
+       int lineno;
+       void *data;
+};
+
+#define MENU_CHANGED           0x0001
+#define MENU_ROOT              0x0002
+
+#ifndef SWIG
+
+extern struct file *file_list;
+extern struct file *current_file;
+struct file *lookup_file(const char *name);
+
+extern struct symbol symbol_yes, symbol_no, symbol_mod;
+extern struct symbol *modules_sym;
+extern struct symbol *sym_defconfig_list;
+extern int cdebug;
+struct expr *expr_alloc_symbol(struct symbol *sym);
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
+struct expr *expr_copy(struct expr *org);
+void expr_free(struct expr *e);
+int expr_eq(struct expr *e1, struct expr *e2);
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
+tristate expr_calc_value(struct expr *e);
+struct expr *expr_eliminate_yn(struct expr *e);
+struct expr *expr_trans_bool(struct expr *e);
+struct expr *expr_eliminate_dups(struct expr *e);
+struct expr *expr_transform(struct expr *e);
+int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2);
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2);
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2);
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+
+void expr_fprint(struct expr *e, FILE *out);
+struct gstr; /* forward */
+void expr_gstr_print(struct expr *e, struct gstr *gs);
+
+static inline int expr_is_yes(struct expr *e)
+{
+       return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
+}
+
+static inline int expr_is_no(struct expr *e)
+{
+       return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXPR_H */
diff --git a/kconfig/lex.zconf.c_shipped b/kconfig/lex.zconf.c_shipped
new file mode 100644 (file)
index 0000000..800f8c7
--- /dev/null
@@ -0,0 +1,2350 @@
+
+#line 3 "scripts/kconfig/lex.zconf.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE zconfrestart(zconfin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int zconfleng;
+
+extern FILE *zconfin, *zconfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up zconftext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               *yy_cp = (yy_hold_char); \
+               YY_RESTORE_YY_MORE_OFFSET \
+               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via zconfrestart()), so that the user can continue scanning by
+        * just pointing zconfin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when zconftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+int zconfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow zconfwrap()'s to do buffer switches
+ * instead of setting up a fresh zconfin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void zconfrestart (FILE *input_file  );
+void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size  );
+void zconf_delete_buffer (YY_BUFFER_STATE b  );
+void zconf_flush_buffer (YY_BUFFER_STATE b  );
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void zconfpop_buffer_state (void );
+
+static void zconfensure_buffer_stack (void );
+static void zconf_load_buffer_state (void );
+static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len  );
+
+void *zconfalloc (yy_size_t  );
+void *zconfrealloc (void *,yy_size_t  );
+void zconffree (void *  );
+
+#define yy_new_buffer zconf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){ \
+        zconfensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){\
+        zconfensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define zconfwrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int zconflineno;
+
+int zconflineno = 1;
+
+extern char *zconftext;
+#define yytext_ptr zconftext
+static yyconst flex_int16_t yy_nxt[][17] =
+    {
+    {
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0
+    },
+
+    {
+       11,   12,   13,   14,   12,   12,   15,   12,   12,   12,
+       12,   12,   12,   12,   12,   12,   12
+    },
+
+    {
+       11,   12,   13,   14,   12,   12,   15,   12,   12,   12,
+       12,   12,   12,   12,   12,   12,   12
+    },
+
+    {
+       11,   16,   16,   17,   16,   16,   16,   16,   16,   16,
+       16,   16,   16,   18,   16,   16,   16
+    },
+
+    {
+       11,   16,   16,   17,   16,   16,   16,   16,   16,   16,
+       16,   16,   16,   18,   16,   16,   16
+
+    },
+
+    {
+       11,   19,   20,   21,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19
+    },
+
+    {
+       11,   19,   20,   21,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19
+    },
+
+    {
+       11,   22,   22,   23,   22,   24,   22,   22,   24,   22,
+       22,   22,   22,   22,   22,   25,   22
+    },
+
+    {
+       11,   22,   22,   23,   22,   24,   22,   22,   24,   22,
+       22,   22,   22,   22,   22,   25,   22
+    },
+
+    {
+       11,   26,   26,   27,   28,   29,   30,   31,   29,   32,
+       33,   34,   35,   35,   36,   37,   38
+
+    },
+
+    {
+       11,   26,   26,   27,   28,   29,   30,   31,   29,   32,
+       33,   34,   35,   35,   36,   37,   38
+    },
+
+    {
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11,  -11,  -11,  -11,  -11
+    },
+
+    {
+       11,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12,  -12,  -12,  -12,  -12
+    },
+
+    {
+       11,  -13,   39,   40,  -13,  -13,   41,  -13,  -13,  -13,
+      -13,  -13,  -13,  -13,  -13,  -13,  -13
+    },
+
+    {
+       11,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14
+
+    },
+
+    {
+       11,   42,   42,   43,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42
+    },
+
+    {
+       11,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,
+      -16,  -16,  -16,  -16,  -16,  -16,  -16
+    },
+
+    {
+       11,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,
+      -17,  -17,  -17,  -17,  -17,  -17,  -17
+    },
+
+    {
+       11,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,
+      -18,  -18,  -18,   44,  -18,  -18,  -18
+    },
+
+    {
+       11,   45,   45,  -19,   45,   45,   45,   45,   45,   45,
+       45,   45,   45,   45,   45,   45,   45
+
+    },
+
+    {
+       11,  -20,   46,   47,  -20,  -20,  -20,  -20,  -20,  -20,
+      -20,  -20,  -20,  -20,  -20,  -20,  -20
+    },
+
+    {
+       11,   48,  -21,  -21,   48,   48,   48,   48,   48,   48,
+       48,   48,   48,   48,   48,   48,   48
+    },
+
+    {
+       11,   49,   49,   50,   49,  -22,   49,   49,  -22,   49,
+       49,   49,   49,   49,   49,  -22,   49
+    },
+
+    {
+       11,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,
+      -23,  -23,  -23,  -23,  -23,  -23,  -23
+    },
+
+    {
+       11,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24,  -24,  -24,  -24,  -24
+
+    },
+
+    {
+       11,   51,   51,   52,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51
+    },
+
+    {
+       11,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26,  -26,  -26,  -26,  -26
+    },
+
+    {
+       11,  -27,  -27,  -27,  -27,  -27,  -27,  -27,  -27,  -27,
+      -27,  -27,  -27,  -27,  -27,  -27,  -27
+    },
+
+    {
+       11,  -28,  -28,  -28,  -28,  -28,  -28,  -28,  -28,  -28,
+      -28,  -28,  -28,  -28,   53,  -28,  -28
+    },
+
+    {
+       11,  -29,  -29,  -29,  -29,  -29,  -29,  -29,  -29,  -29,
+      -29,  -29,  -29,  -29,  -29,  -29,  -29
+
+    },
+
+    {
+       11,   54,   54,  -30,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54
+    },
+
+    {
+       11,  -31,  -31,  -31,  -31,  -31,  -31,   55,  -31,  -31,
+      -31,  -31,  -31,  -31,  -31,  -31,  -31
+    },
+
+    {
+       11,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,
+      -32,  -32,  -32,  -32,  -32,  -32,  -32
+    },
+
+    {
+       11,  -33,  -33,  -33,  -33,  -33,  -33,  -33,  -33,  -33,
+      -33,  -33,  -33,  -33,  -33,  -33,  -33
+    },
+
+    {
+       11,  -34,  -34,  -34,  -34,  -34,  -34,  -34,  -34,  -34,
+      -34,   56,   57,   57,  -34,  -34,  -34
+
+    },
+
+    {
+       11,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,
+      -35,   57,   57,   57,  -35,  -35,  -35
+    },
+
+    {
+       11,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,
+      -36,  -36,  -36,  -36,  -36,  -36,  -36
+    },
+
+    {
+       11,  -37,  -37,   58,  -37,  -37,  -37,  -37,  -37,  -37,
+      -37,  -37,  -37,  -37,  -37,  -37,  -37
+    },
+
+    {
+       11,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,
+      -38,  -38,  -38,  -38,  -38,  -38,   59
+    },
+
+    {
+       11,  -39,   39,   40,  -39,  -39,   41,  -39,  -39,  -39,
+      -39,  -39,  -39,  -39,  -39,  -39,  -39
+
+    },
+
+    {
+       11,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40
+    },
+
+    {
+       11,   42,   42,   43,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42
+    },
+
+    {
+       11,   42,   42,   43,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42
+    },
+
+    {
+       11,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -43,  -43,  -43,  -43,  -43
+    },
+
+    {
+       11,  -44,  -44,  -44,  -44,  -44,  -44,  -44,  -44,  -44,
+      -44,  -44,  -44,   44,  -44,  -44,  -44
+
+    },
+
+    {
+       11,   45,   45,  -45,   45,   45,   45,   45,   45,   45,
+       45,   45,   45,   45,   45,   45,   45
+    },
+
+    {
+       11,  -46,   46,   47,  -46,  -46,  -46,  -46,  -46,  -46,
+      -46,  -46,  -46,  -46,  -46,  -46,  -46
+    },
+
+    {
+       11,   48,  -47,  -47,   48,   48,   48,   48,   48,   48,
+       48,   48,   48,   48,   48,   48,   48
+    },
+
+    {
+       11,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48
+    },
+
+    {
+       11,   49,   49,   50,   49,  -49,   49,   49,  -49,   49,
+       49,   49,   49,   49,   49,  -49,   49
+
+    },
+
+    {
+       11,  -50,  -50,  -50,  -50,  -50,  -50,  -50,  -50,  -50,
+      -50,  -50,  -50,  -50,  -50,  -50,  -50
+    },
+
+    {
+       11,  -51,  -51,   52,  -51,  -51,  -51,  -51,  -51,  -51,
+      -51,  -51,  -51,  -51,  -51,  -51,  -51
+    },
+
+    {
+       11,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52
+    },
+
+    {
+       11,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -53,  -53,  -53,  -53,  -53
+    },
+
+    {
+       11,   54,   54,  -54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54
+
+    },
+
+    {
+       11,  -55,  -55,  -55,  -55,  -55,  -55,  -55,  -55,  -55,
+      -55,  -55,  -55,  -55,  -55,  -55,  -55
+    },
+
+    {
+       11,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,
+      -56,   60,   57,   57,  -56,  -56,  -56
+    },
+
+    {
+       11,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,   57,   57,   57,  -57,  -57,  -57
+    },
+
+    {
+       11,  -58,  -58,  -58,  -58,  -58,  -58,  -58,  -58,  -58,
+      -58,  -58,  -58,  -58,  -58,  -58,  -58
+    },
+
+    {
+       11,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,
+      -59,  -59,  -59,  -59,  -59,  -59,  -59
+
+    },
+
+    {
+       11,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,   57,   57,   57,  -60,  -60,  -60
+    },
+
+    } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up zconftext.
+ */
+#define YY_DO_BEFORE_ACTION \
+       (yytext_ptr) = yy_bp; \
+       zconfleng = (size_t) (yy_cp - yy_bp); \
+       (yy_hold_char) = *yy_cp; \
+       *yy_cp = '\0'; \
+       (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 33
+#define YY_END_OF_BUFFER 34
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+       {
+       flex_int32_t yy_verify;
+       flex_int32_t yy_nxt;
+       };
+static yyconst flex_int16_t yy_accept[61] =
+    {   0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+       34,    5,    4,    2,    3,    7,    8,    6,   32,   29,
+       31,   24,   28,   27,   26,   22,   17,   13,   16,   20,
+       22,   11,   12,   19,   19,   14,   22,   22,    4,    2,
+        3,    3,    1,    6,   32,   29,   31,   30,   24,   23,
+       26,   25,   15,   20,    9,   19,   19,   21,   10,   18
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    6,    1,    1,    7,    8,    9,
+       10,    1,    1,    1,   11,   12,   12,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,    1,    1,    1,
+       14,    1,    1,    1,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+        1,   15,    1,    1,   13,    1,   13,   13,   13,   13,
+
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,    1,   16,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+extern int zconf_flex_debug;
+int zconf_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *zconftext;
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE  16
+
+static struct {
+       struct file *file;
+       int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+        struct buffer *parent;
+        YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+void new_string(void)
+{
+       text = malloc(START_STRSIZE);
+       text_asize = START_STRSIZE;
+       text_size = 0;
+       *text = 0;
+}
+
+void append_string(const char *str, int size)
+{
+       int new_size = text_size + size + 1;
+       if (new_size > text_asize) {
+               new_size += START_STRSIZE - 1;
+               new_size &= -START_STRSIZE;
+               text = realloc(text, new_size);
+               text_asize = new_size;
+       }
+       memcpy(text + text_size, str, size);
+       text_size += size;
+       text[text_size] = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+       text = malloc(size + 1);
+       memcpy(text, str, size);
+       text[size] = 0;
+}
+
+#define INITIAL 0
+#define COMMAND 1
+#define HELP 2
+#define STRING 3
+#define PARAM 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int zconfwrap (void );
+#else
+extern int zconfwrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+    
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       errno=0; \
+       while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
+       { \
+               if( errno != EINTR) \
+               { \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+                       break; \
+               } \
+               errno=0; \
+               clearerr(zconfin); \
+       }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int zconflex (void);
+
+#define YY_DECL int zconflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after zconftext and zconfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+    
+       int str = 0;
+       int ts, i;
+
+       if ( !(yy_init) )
+               {
+               (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! (yy_start) )
+                       (yy_start) = 1; /* first start state */
+
+               if ( ! zconfin )
+                       zconfin = stdin;
+
+               if ( ! zconfout )
+                       zconfout = stdout;
+
+               if ( ! YY_CURRENT_BUFFER ) {
+                       zconfensure_buffer_stack ();
+                       YY_CURRENT_BUFFER_LVALUE =
+                               zconf_create_buffer(zconfin,YY_BUF_SIZE );
+               }
+
+               zconf_load_buffer_state( );
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = (yy_c_buf_p);
+
+               /* Support of zconftext. */
+               *yy_cp = (yy_hold_char);
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = (yy_start);
+yy_match:
+               while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)]  ]) > 0 )
+                       ++yy_cp;
+
+               yy_current_state = -yy_current_state;
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+
+               YY_DO_BEFORE_ACTION;
+
+do_action:     /* This label is used only to access EOF actions. */
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+case 1:
+/* rule 1 can match eol */
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+       current_file->lineno++;
+       return T_EOL;
+}
+       YY_BREAK
+case 3:
+YY_RULE_SETUP
+
+       YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+       BEGIN(COMMAND);
+}
+       YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+       unput(zconftext[0]);
+       BEGIN(COMMAND);
+}
+       YY_BREAK
+
+case 6:
+YY_RULE_SETUP
+{
+               struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+               BEGIN(PARAM);
+               current_pos.file = current_file;
+               current_pos.lineno = current_file->lineno;
+               if (id && id->flags & TF_COMMAND) {
+                       zconflval.id = id;
+                       return id->token;
+               }
+               alloc_string(zconftext, zconfleng);
+               zconflval.string = text;
+               return T_WORD;
+       }
+       YY_BREAK
+case 7:
+YY_RULE_SETUP
+
+       YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+{
+               BEGIN(INITIAL);
+               current_file->lineno++;
+               return T_EOL;
+       }
+       YY_BREAK
+
+case 9:
+YY_RULE_SETUP
+return T_AND;
+       YY_BREAK
+case 10:
+YY_RULE_SETUP
+return T_OR;
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+       YY_BREAK
+case 12:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+       YY_BREAK
+case 13:
+YY_RULE_SETUP
+return T_NOT;
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+return T_EQUAL;
+       YY_BREAK
+case 15:
+YY_RULE_SETUP
+return T_UNEQUAL;
+       YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+               str = zconftext[0];
+               new_string();
+               BEGIN(STRING);
+       }
+       YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+       YY_BREAK
+case 18:
+YY_RULE_SETUP
+/* ignore */
+       YY_BREAK
+case 19:
+YY_RULE_SETUP
+{
+               struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+               if (id && id->flags & TF_PARAM) {
+                       zconflval.id = id;
+                       return id->token;
+               }
+               alloc_string(zconftext, zconfleng);
+               zconflval.string = text;
+               return T_WORD;
+       }
+       YY_BREAK
+case 20:
+YY_RULE_SETUP
+/* comment */
+       YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+       YY_BREAK
+case 22:
+YY_RULE_SETUP
+
+       YY_BREAK
+case YY_STATE_EOF(PARAM):
+{
+               BEGIN(INITIAL);
+       }
+       YY_BREAK
+
+case 23:
+/* rule 23 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+               append_string(zconftext, zconfleng);
+               zconflval.string = text;
+               return T_WORD_QUOTE;
+       }
+       YY_BREAK
+case 24:
+YY_RULE_SETUP
+{
+               append_string(zconftext, zconfleng);
+       }
+       YY_BREAK
+case 25:
+/* rule 25 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+               append_string(zconftext + 1, zconfleng - 1);
+               zconflval.string = text;
+               return T_WORD_QUOTE;
+       }
+       YY_BREAK
+case 26:
+YY_RULE_SETUP
+{
+               append_string(zconftext + 1, zconfleng - 1);
+       }
+       YY_BREAK
+case 27:
+YY_RULE_SETUP
+{
+               if (str == zconftext[0]) {
+                       BEGIN(PARAM);
+                       zconflval.string = text;
+                       return T_WORD_QUOTE;
+               } else
+                       append_string(zconftext, 1);
+       }
+       YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+{
+               printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+               current_file->lineno++;
+               BEGIN(INITIAL);
+               return T_EOL;
+       }
+       YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+               BEGIN(INITIAL);
+       }
+       YY_BREAK
+
+case 29:
+YY_RULE_SETUP
+{
+               ts = 0;
+               for (i = 0; i < zconfleng; i++) {
+                       if (zconftext[i] == '\t')
+                               ts = (ts & ~7) + 8;
+                       else
+                               ts++;
+               }
+               last_ts = ts;
+               if (first_ts) {
+                       if (ts < first_ts) {
+                               zconf_endhelp();
+                               return T_HELPTEXT;
+                       }
+                       ts -= first_ts;
+                       while (ts > 8) {
+                               append_string("        ", 8);
+                               ts -= 8;
+                       }
+                       append_string("        ", ts);
+               }
+       }
+       YY_BREAK
+case 30:
+/* rule 30 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+               current_file->lineno++;
+               zconf_endhelp();
+               return T_HELPTEXT;
+       }
+       YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+{
+               current_file->lineno++;
+               append_string("\n", 1);
+       }
+       YY_BREAK
+case 32:
+YY_RULE_SETUP
+{
+               append_string(zconftext, zconfleng);
+               if (!first_ts)
+                       first_ts = last_ts;
+       }
+       YY_BREAK
+case YY_STATE_EOF(HELP):
+{
+               zconf_endhelp();
+               return T_HELPTEXT;
+       }
+       YY_BREAK
+
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMAND):
+{
+       if (current_file) {
+               zconf_endfile();
+               return T_EOL;
+       }
+       fclose(zconfin);
+       yyterminate();
+}
+       YY_BREAK
+case 33:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+       YY_BREAK
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = (yy_hold_char);
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed zconfin at a new source and called
+                        * zconflex().  If so, then we have to assure
+                        * consistency between YY_CURRENT_BUFFER and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state(  );
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++(yy_c_buf_p);
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = (yy_c_buf_p);
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer(  ) )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               (yy_did_buffer_switch_on_eof) = 0;
+
+                               if ( zconfwrap( ) )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * zconftext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               (yy_c_buf_p) =
+                                       (yytext_ptr) + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               (yy_c_buf_p) =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+} /* end of zconflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = (yytext_ptr);
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+       else
+               {
+                       int num_to_read =
+                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+                       int yy_c_buf_p_offset =
+                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+                                               number_to_move - 1;
+
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+                       (yy_n_chars), num_to_read );
+
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       if ( (yy_n_chars) == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       zconfrestart(zconfin  );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       (yy_n_chars) += number_to_move;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+       return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+    
+       yy_current_state = (yy_start);
+
+       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+               {
+               yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+               }
+
+       return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+       register int yy_is_jam;
+    
+       yy_current_state = yy_nxt[yy_current_state][1];
+       yy_is_jam = (yy_current_state <= 0);
+
+       return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+       register char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+       /* undo effects of setting up zconftext */
+       *yy_cp = (yy_hold_char);
+
+       if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+               { /* need to shift things up to make room */
+               /* +2 for EOB chars. */
+               register int number_to_move = (yy_n_chars) + 2;
+               register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+                                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+               register char *source =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+               while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       *--dest = *--source;
+
+               yy_cp += (int) (dest - source);
+               yy_bp += (int) (dest - source);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+               if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+                       YY_FATAL_ERROR( "flex scanner push-back overflow" );
+               }
+
+       *--yy_cp = (char) c;
+
+       (yytext_ptr) = yy_bp;
+       (yy_hold_char) = *yy_cp;
+       (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+       int c;
+    
+       *(yy_c_buf_p) = (yy_hold_char);
+
+       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       /* This was really a NUL. */
+                       *(yy_c_buf_p) = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = (yy_c_buf_p) - (yytext_ptr);
+                       ++(yy_c_buf_p);
+
+                       switch ( yy_get_next_buffer(  ) )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       zconfrestart(zconfin );
+
+                                       /*FALLTHROUGH*/
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( zconfwrap( ) )
+                                               return EOF;
+
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       (yy_c_buf_p) = (yytext_ptr) + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
+       *(yy_c_buf_p) = '\0';   /* preserve zconftext */
+       (yy_hold_char) = *++(yy_c_buf_p);
+
+       return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void zconfrestart  (FILE * input_file )
+{
+    
+       if ( ! YY_CURRENT_BUFFER ){
+        zconfensure_buffer_stack ();
+               YY_CURRENT_BUFFER_LVALUE =
+            zconf_create_buffer(zconfin,YY_BUF_SIZE );
+       }
+
+       zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
+       zconf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void zconf_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+       /* TODO. We should be able to replace this entire function body
+        * with
+        *              zconfpop_buffer_state();
+        *              zconfpush_buffer_state(new_buffer);
+     */
+       zconfensure_buffer_stack ();
+       if ( YY_CURRENT_BUFFER == new_buffer )
+               return;
+
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+       zconf_load_buffer_state( );
+
+       /* We don't actually know whether we did this switch during
+        * EOF (zconfwrap()) processing, but the only time this flag
+        * is looked at is after zconfwrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void zconf_load_buffer_state  (void)
+{
+       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+       zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+       (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE zconf_create_buffer  (FILE * file, int  size )
+{
+       YY_BUFFER_STATE b;
+    
+       b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2  );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       zconf_init_buffer(b,file );
+
+       return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with zconf_create_buffer()
+ * 
+ */
+    void zconf_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+       if ( ! b )
+               return;
+
+       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               zconffree((void *) b->yy_ch_buf  );
+
+       zconffree((void *) b  );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a zconfrestart() or at EOF.
+ */
+    static void zconf_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+       int oerrno = errno;
+    
+       zconf_flush_buffer(b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then zconf_init_buffer was _probably_
+     * called from zconfrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = 0;
+    
+       errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void zconf_flush_buffer (YY_BUFFER_STATE  b )
+{
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == YY_CURRENT_BUFFER )
+               zconf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+       if (new_buffer == NULL)
+               return;
+
+       zconfensure_buffer_stack();
+
+       /* This block is copied from zconf_switch_to_buffer. */
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       /* Only push if top exists. Otherwise, replace top. */
+       if (YY_CURRENT_BUFFER)
+               (yy_buffer_stack_top)++;
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+       /* copied from zconf_switch_to_buffer. */
+       zconf_load_buffer_state( );
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void zconfpop_buffer_state (void)
+{
+       if (!YY_CURRENT_BUFFER)
+               return;
+
+       zconf_delete_buffer(YY_CURRENT_BUFFER );
+       YY_CURRENT_BUFFER_LVALUE = NULL;
+       if ((yy_buffer_stack_top) > 0)
+               --(yy_buffer_stack_top);
+
+       if (YY_CURRENT_BUFFER) {
+               zconf_load_buffer_state( );
+               (yy_did_buffer_switch_on_eof) = 1;
+       }
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void zconfensure_buffer_stack (void)
+{
+       int num_to_alloc;
+    
+       if (!(yy_buffer_stack)) {
+
+               /* First allocation is just for 2 elements, since we don't know if this
+                * scanner will even need a stack. We use 2 instead of 1 to avoid an
+                * immediate realloc on the next call.
+         */
+               num_to_alloc = 1;
+               (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
+                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               
+               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+                               
+               (yy_buffer_stack_max) = num_to_alloc;
+               (yy_buffer_stack_top) = 0;
+               return;
+       }
+
+       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+               /* Increase the buffer to prepare for a possible push. */
+               int grow_size = 8 /* arbitrary grow size */;
+
+               num_to_alloc = (yy_buffer_stack_max) + grow_size;
+               (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
+                                                               ((yy_buffer_stack),
+                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+
+               /* zero only the new slots.*/
+               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+               (yy_buffer_stack_max) = num_to_alloc;
+       }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE zconf_scan_buffer  (char * base, yy_size_t  size )
+{
+       YY_BUFFER_STATE b;
+    
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       zconf_switch_to_buffer(b  );
+
+       return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to zconflex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       zconf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr )
+{
+    
+       return zconf_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+    
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = _yybytes_len + 2;
+       buf = (char *) zconfalloc(n  );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
+
+       for ( i = 0; i < _yybytes_len; ++i )
+               buf[i] = yybytes[i];
+
+       buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = zconf_scan_buffer(buf,n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up zconftext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               zconftext[zconfleng] = (yy_hold_char); \
+               (yy_c_buf_p) = zconftext + yyless_macro_arg; \
+               (yy_hold_char) = *(yy_c_buf_p); \
+               *(yy_c_buf_p) = '\0'; \
+               zconfleng = yyless_macro_arg; \
+               } \
+       while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int zconfget_lineno  (void)
+{
+        
+    return zconflineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *zconfget_in  (void)
+{
+        return zconfin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *zconfget_out  (void)
+{
+        return zconfout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int zconfget_leng  (void)
+{
+        return zconfleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *zconfget_text  (void)
+{
+        return zconftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void zconfset_lineno (int  line_number )
+{
+    
+    zconflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see zconf_switch_to_buffer
+ */
+void zconfset_in (FILE *  in_str )
+{
+        zconfin = in_str ;
+}
+
+void zconfset_out (FILE *  out_str )
+{
+        zconfout = out_str ;
+}
+
+int zconfget_debug  (void)
+{
+        return zconf_flex_debug;
+}
+
+void zconfset_debug (int  bdebug )
+{
+        zconf_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from zconflex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    zconfin = stdin;
+    zconfout = stdout;
+#else
+    zconfin = (FILE *) 0;
+    zconfout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * zconflex_init()
+     */
+    return 0;
+}
+
+/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
+int zconflex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+       while(YY_CURRENT_BUFFER){
+               zconf_delete_buffer(YY_CURRENT_BUFFER  );
+               YY_CURRENT_BUFFER_LVALUE = NULL;
+               zconfpop_buffer_state();
+       }
+
+       /* Destroy the stack itself. */
+       zconffree((yy_buffer_stack) );
+       (yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * zconflex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+}
+#endif
+
+void *zconfalloc (yy_size_t  size )
+{
+       return (void *) malloc( size );
+}
+
+void *zconfrealloc  (void * ptr, yy_size_t  size )
+{
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+}
+
+void zconffree (void * ptr )
+{
+       free( (char *) ptr );   /* see zconfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+void zconf_starthelp(void)
+{
+       new_string();
+       last_ts = first_ts = 0;
+       BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+       zconflval.string = text;
+       BEGIN(INITIAL);
+}
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+       char *env, fullname[PATH_MAX+1];
+       FILE *f;
+
+       f = fopen(name, "r");
+       if (!f && name[0] != '/') {
+               env = getenv(SRCTREE);
+               if (env) {
+                       sprintf(fullname, "%s/%s", env, name);
+                       f = fopen(fullname, "r");
+               }
+       }
+       return f;
+}
+
+void zconf_initscan(const char *name)
+{
+       zconfin = zconf_fopen(name);
+       if (!zconfin) {
+               printf("can't find file %s\n", name);
+               exit(1);
+       }
+
+       current_buf = malloc(sizeof(*current_buf));
+       memset(current_buf, 0, sizeof(*current_buf));
+
+       current_file = file_lookup(name);
+       current_file->lineno = 1;
+       current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+       struct file *file = file_lookup(name);
+       struct buffer *buf = malloc(sizeof(*buf));
+       memset(buf, 0, sizeof(*buf));
+
+       current_buf->state = YY_CURRENT_BUFFER;
+       zconfin = zconf_fopen(name);
+       if (!zconfin) {
+               printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+               exit(1);
+       }
+       zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE));
+       buf->parent = current_buf;
+       current_buf = buf;
+
+       if (file->flags & FILE_BUSY) {
+               printf("recursive scan (%s)?\n", name);
+               exit(1);
+       }
+       if (file->flags & FILE_SCANNED) {
+               printf("file %s already scanned?\n", name);
+               exit(1);
+       }
+       file->flags |= FILE_BUSY;
+       file->lineno = 1;
+       file->parent = current_file;
+       current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+       struct buffer *parent;
+
+       current_file->flags |= FILE_SCANNED;
+       current_file->flags &= ~FILE_BUSY;
+       current_file = current_file->parent;
+
+       parent = current_buf->parent;
+       if (parent) {
+               fclose(zconfin);
+               zconf_delete_buffer(YY_CURRENT_BUFFER);
+               zconf_switch_to_buffer(parent->state);
+       }
+       free(current_buf);
+       current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+       return current_pos.lineno;
+}
+
+char *zconf_curname(void)
+{
+       return current_pos.file ? current_pos.file->name : "<none>";
+}
+
diff --git a/kconfig/lkc.h b/kconfig/lkc.h
new file mode 100644 (file)
index 0000000..9b629ff
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef LKC_H
+#define LKC_H
+
+// Make some warnings go away
+#define YYENABLE_NLS 0
+#define YYLTYPE_IS_TRIVIAL 0
+
+#include "expr.h"
+
+#ifndef KBUILD_NO_NLS
+# include <libintl.h>
+#else
+# define gettext(Msgid) ((const char *) (Msgid))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LKC_DIRECT_LINK
+#define P(name,type,arg)       extern type name arg
+#else
+#include "lkc_defs.h"
+#define P(name,type,arg)       extern type (*name ## _p) arg
+#endif
+#include "lkc_proto.h"
+#undef P
+
+#define SRCTREE "srctree"
+
+#define PACKAGE "linux"
+#define LOCALEDIR "/usr/share/locale"
+
+#define _(text) gettext(text)
+#define N_(text) (text)
+
+
+#define TF_COMMAND     0x0001
+#define TF_PARAM       0x0002
+#define TF_OPTION      0x0004
+
+#define T_OPT_MODULES          1
+#define T_OPT_DEFCONFIG_LIST   2
+
+struct kconf_id {
+       int name;
+       int token;
+       unsigned int flags;
+       enum symbol_type stype;
+};
+
+int zconfparse(void);
+void zconfdump(FILE *out);
+
+extern int zconfdebug;
+void zconf_starthelp(void);
+FILE *zconf_fopen(const char *name);
+void zconf_initscan(const char *name);
+void zconf_nextfile(const char *name);
+int zconf_lineno(void);
+char *zconf_curname(void);
+
+/* confdata.c */
+char *conf_get_default_confname(void);
+
+/* kconfig_load.c */
+void kconfig_load(void);
+
+/* menu.c */
+void menu_init(void);
+struct menu *menu_add_menu(void);
+void menu_end_menu(void);
+void menu_add_entry(struct symbol *sym);
+void menu_end_entry(void);
+void menu_add_dep(struct expr *dep);
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep);
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+void menu_add_option(int token, char *arg);
+void menu_finalize(struct menu *parent);
+void menu_set_type(int type);
+
+/* util.c */
+struct file *file_lookup(const char *name);
+int file_write_dep(const char *name);
+
+struct gstr {
+       size_t len;
+       char  *s;
+};
+struct gstr str_new(void);
+struct gstr str_assign(const char *s);
+void str_free(struct gstr *gs);
+void str_append(struct gstr *gs, const char *s);
+void str_printf(struct gstr *gs, const char *fmt, ...);
+const char *str_get(struct gstr *gs);
+
+/* symbol.c */
+void sym_init(void);
+void sym_clear_all_valid(void);
+void sym_set_all_changed(void);
+void sym_set_changed(struct symbol *sym);
+struct symbol *sym_check_deps(struct symbol *sym);
+struct property *prop_alloc(enum prop_type type, struct symbol *sym);
+struct symbol *prop_get_symbol(struct property *prop);
+
+static inline tristate sym_get_tristate_value(struct symbol *sym)
+{
+       return sym->curr.tri;
+}
+
+
+static inline struct symbol *sym_get_choice_value(struct symbol *sym)
+{
+       return (struct symbol *)sym->curr.val;
+}
+
+static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
+{
+       return sym_set_tristate_value(chval, yes);
+}
+
+static inline bool sym_is_choice(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_CHOICE ? true : false;
+}
+
+static inline bool sym_is_choice_value(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_CHOICEVAL ? true : false;
+}
+
+static inline bool sym_is_optional(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_OPTIONAL ? true : false;
+}
+
+static inline bool sym_has_value(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_DEF_USER ? true : false;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LKC_H */
diff --git a/kconfig/lkc_proto.h b/kconfig/lkc_proto.h
new file mode 100644 (file)
index 0000000..a263746
--- /dev/null
@@ -0,0 +1,42 @@
+
+/* confdata.c */
+P(conf_parse,void,(const char *name));
+P(conf_read,int,(const char *name));
+P(conf_read_simple,int,(const char *name, int));
+P(conf_write,int,(const char *name));
+P(conf_write_autoconf,int,(void));
+
+/* menu.c */
+P(rootmenu,struct menu,);
+
+P(menu_is_visible,bool,(struct menu *menu));
+P(menu_get_prompt,const char *,(struct menu *menu));
+P(menu_get_root_menu,struct menu *,(struct menu *menu));
+P(menu_get_parent_menu,struct menu *,(struct menu *menu));
+
+/* symbol.c */
+P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]);
+P(sym_change_count,int,);
+
+P(sym_lookup,struct symbol *,(const char *name, int isconst));
+P(sym_find,struct symbol *,(const char *name));
+P(sym_re_search,struct symbol **,(const char *pattern));
+P(sym_type_name,const char *,(enum symbol_type type));
+P(sym_calc_value,void,(struct symbol *sym));
+P(sym_get_type,enum symbol_type,(struct symbol *sym));
+P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri));
+P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri));
+P(sym_toggle_tristate_value,tristate,(struct symbol *sym));
+P(sym_string_valid,bool,(struct symbol *sym, const char *newval));
+P(sym_string_within_range,bool,(struct symbol *sym, const char *str));
+P(sym_set_string_value,bool,(struct symbol *sym, const char *newval));
+P(sym_is_changable,bool,(struct symbol *sym));
+P(sym_get_choice_prop,struct property *,(struct symbol *sym));
+P(sym_get_default_prop,struct property *,(struct symbol *sym));
+P(sym_get_string_value,const char *,(struct symbol *sym));
+
+P(prop_get_type_name,const char *,(enum prop_type type));
+
+/* expr.c */
+P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2));
+P(expr_print,void,(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken));
diff --git a/kconfig/lxdialog/BIG.FAT.WARNING b/kconfig/lxdialog/BIG.FAT.WARNING
new file mode 100644 (file)
index 0000000..a8999d8
--- /dev/null
@@ -0,0 +1,4 @@
+This is NOT the official version of dialog.  This version has been
+significantly modified from the original.  It is for use by the Linux
+kernel configuration script.  Please do not bother Savio Lam with 
+questions about this program.
diff --git a/kconfig/lxdialog/check-lxdialog.sh b/kconfig/lxdialog/check-lxdialog.sh
new file mode 100644 (file)
index 0000000..120d624
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+# Check ncurses compatibility
+
+# What library to link
+ldflags()
+{
+       $cc -print-file-name=libncursesw.so | grep -q /
+       if [ $? -eq 0 ]; then
+               echo '-lncursesw'
+               exit
+       fi
+       $cc -print-file-name=libncurses.so | grep -q /
+       if [ $? -eq 0 ]; then
+               echo '-lncurses'
+               exit
+       fi
+       $cc -print-file-name=libcurses.so | grep -q /
+       if [ $? -eq 0 ]; then
+               echo '-lcurses'
+               exit
+       fi
+       exit 1
+}
+
+# Where is ncurses.h?
+ccflags()
+{
+       if [ -f /usr/include/ncurses/ncurses.h ]; then
+               echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
+       elif [ -f /usr/include/ncurses/curses.h ]; then
+               echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
+       elif [ -f /usr/include/ncurses.h ]; then
+               echo '-DCURSES_LOC="<ncurses.h>"'
+       else
+               echo '-DCURSES_LOC="<curses.h>"'
+       fi
+}
+
+# Temp file, try to clean up after us
+tmp=.lxdialog.tmp
+trap "rm -f $tmp" 0 1 2 3 15
+
+# Check if we can link to ncurses
+check() {
+       echo "main() {}" | $cc -xc - -o $tmp 2> /dev/null
+       if [ $? != 0 ]; then
+               echo " *** Unable to find the ncurses libraries."          1>&2
+               echo " *** make menuconfig require the ncurses libraries"  1>&2
+               echo " *** "                                               1>&2
+               echo " *** Install ncurses (ncurses-devel) and try again"  1>&2
+               echo " *** "                                               1>&2
+               exit 1
+       fi
+}
+
+usage() {
+       printf "Usage: $0 [-check compiler options|-header|-library]\n"
+}
+
+if [ $# == 0 ]; then
+       usage
+       exit 1
+fi
+
+cc=""
+case "$1" in
+       "-check")
+               shift
+               cc="$@"
+               check
+               ;;
+       "-ccflags")
+               ccflags
+               ;;
+       "-ldflags")
+               shift
+               cc="$@"
+               ldflags
+               ;;
+       "*")
+               usage
+               exit 1
+               ;;
+esac
diff --git a/kconfig/lxdialog/checklist.c b/kconfig/lxdialog/checklist.c
new file mode 100644 (file)
index 0000000..cf69708
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ *  checklist.c -- implements the checklist box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *     Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
+ *     Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static int list_width, check_x, item_x;
+
+/*
+ * Print list item
+ */
+static void print_item(WINDOW * win, int choice, int selected)
+{
+       int i;
+
+       /* Clear 'residue' of last item */
+       wattrset(win, dlg.menubox.atr);
+       wmove(win, choice, 0);
+       for (i = 0; i < list_width; i++)
+               waddch(win, ' ');
+
+       wmove(win, choice, check_x);
+       wattrset(win, selected ? dlg.check_selected.atr
+                : dlg.check.atr);
+       wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' ');
+
+       wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr);
+       mvwaddch(win, choice, item_x, item_str()[0]);
+       wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
+       waddstr(win, (char *)item_str() + 1);
+       if (selected) {
+               wmove(win, choice, check_x + 1);
+               wrefresh(win);
+       }
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
+            int y, int x, int height)
+{
+       wmove(win, y, x);
+
+       if (scroll > 0) {
+               wattrset(win, dlg.uarrow.atr);
+               waddch(win, ACS_UARROW);
+               waddstr(win, "(-)");
+       } else {
+               wattrset(win, dlg.menubox.atr);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+       }
+
+       y = y + height + 1;
+       wmove(win, y, x);
+
+       if ((height < item_no) && (scroll + choice < item_no - 1)) {
+               wattrset(win, dlg.darrow.atr);
+               waddch(win, ACS_DARROW);
+               waddstr(win, "(+)");
+       } else {
+               wattrset(win, dlg.menubox_border.atr);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+       }
+}
+
+/*
+ *  Display the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+       int x = width / 2 - 11;
+       int y = height - 2;
+
+       print_button(dialog, "Select", y, x, selected == 0);
+       print_button(dialog, " Help ", y, x + 14, selected == 1);
+
+       wmove(dialog, y, x + 1 + 14 * selected);
+       wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with a list of options that can be turned on or off
+ * in the style of radiolist (only one option turned on at a time).
+ */
+int dialog_checklist(const char *title, const char *prompt, int height,
+                    int width, int list_height)
+{
+       int i, x, y, box_x, box_y;
+       int key = 0, button = 0, choice = 0, scroll = 0, max_choice;
+       WINDOW *dialog, *list;
+
+       /* which item to highlight */
+       item_foreach() {
+               if (item_is_tag('X'))
+                       choice = item_n();
+               if (item_is_selected()) {
+                       choice = item_n();
+                       break;
+               }
+       }
+
+do_resize:
+       if (getmaxy(stdscr) < (height + 6))
+               return -ERRDISPLAYTOOSMALL;
+       if (getmaxx(stdscr) < (width + 6))
+               return -ERRDISPLAYTOOSMALL;
+
+       max_choice = MIN(list_height, item_count());
+
+       /* center dialog box on screen */
+       x = (COLS - width) / 2;
+       y = (LINES - height) / 2;
+
+       draw_shadow(stdscr, y, x, height, width);
+
+       dialog = newwin(height, width, y, x);
+       keypad(dialog, TRUE);
+
+       draw_box(dialog, 0, 0, height, width,
+                dlg.dialog.atr, dlg.border.atr);
+       wattrset(dialog, dlg.border.atr);
+       mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+       for (i = 0; i < width - 2; i++)
+               waddch(dialog, ACS_HLINE);
+       wattrset(dialog, dlg.dialog.atr);
+       waddch(dialog, ACS_RTEE);
+
+       print_title(dialog, title, width);
+
+       wattrset(dialog, dlg.dialog.atr);
+       print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+       list_width = width - 6;
+       box_y = height - list_height - 5;
+       box_x = (width - list_width) / 2 - 1;
+
+       /* create new window for the list */
+       list = subwin(dialog, list_height, list_width, y + box_y + 1,
+                     x + box_x + 1);
+
+       keypad(list, TRUE);
+
+       /* draw a box around the list items */
+       draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
+                dlg.menubox_border.atr, dlg.menubox.atr);
+
+       /* Find length of longest item in order to center checklist */
+       check_x = 0;
+       item_foreach()
+               check_x = MAX(check_x, strlen(item_str()) + 4);
+
+       check_x = (list_width - check_x) / 2;
+       item_x = check_x + 4;
+
+       if (choice >= list_height) {
+               scroll = choice - list_height + 1;
+               choice -= scroll;
+       }
+
+       /* Print the list */
+       for (i = 0; i < max_choice; i++) {
+               item_set(scroll + i);
+               print_item(list, i, i == choice);
+       }
+
+       print_arrows(dialog, choice, item_count(), scroll,
+                    box_y, box_x + check_x + 5, list_height);
+
+       print_buttons(dialog, height, width, 0);
+
+       wnoutrefresh(dialog);
+       wnoutrefresh(list);
+       doupdate();
+
+       while (key != KEY_ESC) {
+               key = wgetch(dialog);
+
+               for (i = 0; i < max_choice; i++) {
+                       item_set(i + scroll);
+                       if (toupper(key) == toupper(item_str()[0]))
+                               break;
+               }
+
+               if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
+                   key == '+' || key == '-') {
+                       if (key == KEY_UP || key == '-') {
+                               if (!choice) {
+                                       if (!scroll)
+                                               continue;
+                                       /* Scroll list down */
+                                       if (list_height > 1) {
+                                               /* De-highlight current first item */
+                                               item_set(scroll);
+                                               print_item(list, 0, FALSE);
+                                               scrollok(list, TRUE);
+                                               wscrl(list, -1);
+                                               scrollok(list, FALSE);
+                                       }
+                                       scroll--;
+                                       item_set(scroll);
+                                       print_item(list, 0, TRUE);
+                                       print_arrows(dialog, choice, item_count(),
+                                                    scroll, box_y, box_x + check_x + 5, list_height);
+
+                                       wnoutrefresh(dialog);
+                                       wrefresh(list);
+
+                                       continue;       /* wait for another key press */
+                               } else
+                                       i = choice - 1;
+                       } else if (key == KEY_DOWN || key == '+') {
+                               if (choice == max_choice - 1) {
+                                       if (scroll + choice >= item_count() - 1)
+                                               continue;
+                                       /* Scroll list up */
+                                       if (list_height > 1) {
+                                               /* De-highlight current last item before scrolling up */
+                                               item_set(scroll + max_choice - 1);
+                                               print_item(list,
+                                                           max_choice - 1,
+                                                           FALSE);
+                                               scrollok(list, TRUE);
+                                               wscrl(list, 1);
+                                               scrollok(list, FALSE);
+                                       }
+                                       scroll++;
+                                       item_set(scroll + max_choice - 1);
+                                       print_item(list, max_choice - 1, TRUE);
+
+                                       print_arrows(dialog, choice, item_count(),
+                                                    scroll, box_y, box_x + check_x + 5, list_height);
+
+                                       wnoutrefresh(dialog);
+                                       wrefresh(list);
+
+                                       continue;       /* wait for another key press */
+                               } else
+                                       i = choice + 1;
+                       }
+                       if (i != choice) {
+                               /* De-highlight current item */
+                               item_set(scroll + choice);
+                               print_item(list, choice, FALSE);
+                               /* Highlight new item */
+                               choice = i;
+                               item_set(scroll + choice);
+                               print_item(list, choice, TRUE);
+                               wnoutrefresh(dialog);
+                               wrefresh(list);
+                       }
+                       continue;       /* wait for another key press */
+               }
+               switch (key) {
+               case 'H':
+               case 'h':
+               case '?':
+                       button = 1;
+                       /* fall-through */
+               case 'S':
+               case 's':
+               case ' ':
+               case '\n':
+                       item_foreach()
+                               item_set_selected(0);
+                       item_set(scroll + choice);
+                       item_set_selected(1);
+                       delwin(list);
+                       delwin(dialog);
+                       return button;
+               case TAB:
+               case KEY_LEFT:
+               case KEY_RIGHT:
+                       button = ((key == KEY_LEFT ? --button : ++button) < 0)
+                           ? 1 : (button > 1 ? 0 : button);
+
+                       print_buttons(dialog, height, width, button);
+                       wrefresh(dialog);
+                       break;
+               case 'X':
+               case 'x':
+                       key = KEY_ESC;
+                       break;
+               case KEY_ESC:
+                       key = on_key_esc(dialog);
+                       break;
+               case KEY_RESIZE:
+                       delwin(list);
+                       delwin(dialog);
+                       on_key_resize();
+                       goto do_resize;
+               }
+
+               /* Now, update everything... */
+               doupdate();
+       }
+       delwin(list);
+       delwin(dialog);
+       return key;             /* ESC pressed */
+}
diff --git a/kconfig/lxdialog/dialog.h b/kconfig/lxdialog/dialog.h
new file mode 100644 (file)
index 0000000..fd695e1
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ *  dialog.h -- common declarations for all dialog modules
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef __sun__
+#define CURS_MACROS
+#endif
+#include CURSES_LOC
+
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked.  This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses.  The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef  wbkgdset
+#define wbkgdset(w,p)          /*nothing */
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
+#define KEY_ESC 27
+#define TAB 9
+#define MAX_LEN 2048
+#define BUF_SIZE (10*1024)
+#define MIN(x,y) (x < y ? x : y)
+#define MAX(x,y) (x > y ? x : y)
+
+#ifndef ACS_ULCORNER
+#define ACS_ULCORNER '+'
+#endif
+#ifndef ACS_LLCORNER
+#define ACS_LLCORNER '+'
+#endif
+#ifndef ACS_URCORNER
+#define ACS_URCORNER '+'
+#endif
+#ifndef ACS_LRCORNER
+#define ACS_LRCORNER '+'
+#endif
+#ifndef ACS_HLINE
+#define ACS_HLINE '-'
+#endif
+#ifndef ACS_VLINE
+#define ACS_VLINE '|'
+#endif
+#ifndef ACS_LTEE
+#define ACS_LTEE '+'
+#endif
+#ifndef ACS_RTEE
+#define ACS_RTEE '+'
+#endif
+#ifndef ACS_UARROW
+#define ACS_UARROW '^'
+#endif
+#ifndef ACS_DARROW
+#define ACS_DARROW 'v'
+#endif
+
+/* error return codes */
+#define ERRDISPLAYTOOSMALL (KEY_MAX + 1)
+
+/*
+ *   Color definitions
+ */
+struct dialog_color {
+       chtype atr;     /* Color attribute */
+       int fg;         /* foreground */
+       int bg;         /* background */
+       int hl;         /* highlight this item */
+};
+
+struct dialog_info {
+       const char *backtitle;
+       struct dialog_color screen;
+       struct dialog_color shadow;
+       struct dialog_color dialog;
+       struct dialog_color title;
+       struct dialog_color border;
+       struct dialog_color button_active;
+       struct dialog_color button_inactive;
+       struct dialog_color button_key_active;
+       struct dialog_color button_key_inactive;
+       struct dialog_color button_label_active;
+       struct dialog_color button_label_inactive;
+       struct dialog_color inputbox;
+       struct dialog_color inputbox_border;
+       struct dialog_color searchbox;
+       struct dialog_color searchbox_title;
+       struct dialog_color searchbox_border;
+       struct dialog_color position_indicator;
+       struct dialog_color menubox;
+       struct dialog_color menubox_border;
+       struct dialog_color item;
+       struct dialog_color item_selected;
+       struct dialog_color tag;
+       struct dialog_color tag_selected;
+       struct dialog_color tag_key;
+       struct dialog_color tag_key_selected;
+       struct dialog_color check;
+       struct dialog_color check_selected;
+       struct dialog_color uarrow;
+       struct dialog_color darrow;
+};
+
+/*
+ * Global variables
+ */
+extern struct dialog_info dlg;
+extern char dialog_input_result[];
+
+/*
+ * Function prototypes
+ */
+
+/* item list as used by checklist and menubox */
+void item_reset(void);
+void item_make(const char *fmt, ...);
+void item_add_str(const char *fmt, ...);
+void item_set_tag(char tag);
+void item_set_data(void *p);
+void item_set_selected(int val);
+int item_activate_selected(void);
+void *item_data(void);
+char item_tag(void);
+
+/* item list manipulation for lxdialog use */
+#define MAXITEMSTR 200
+struct dialog_item {
+       char str[MAXITEMSTR];   /* promtp displayed */
+       char tag;
+       void *data;     /* pointer to menu item - used by menubox+checklist */
+       int selected;   /* Set to 1 by dialog_*() function if selected. */
+};
+
+/* list of lialog_items */
+struct dialog_list {
+       struct dialog_item node;
+       struct dialog_list *next;
+};
+
+extern struct dialog_list *item_cur;
+extern struct dialog_list item_nil;
+extern struct dialog_list *item_head;
+
+int item_count(void);
+void item_set(int n);
+int item_n(void);
+const char *item_str(void);
+int item_is_selected(void);
+int item_is_tag(char tag);
+#define item_foreach() \
+       for (item_cur = item_head ? item_head: item_cur; \
+            item_cur && (item_cur != &item_nil); item_cur = item_cur->next)
+
+/* generic key handlers */
+int on_key_esc(WINDOW *win);
+int on_key_resize(void);
+
+void init_dialog(const char *backtitle);
+void reset_dialog(void);
+void end_dialog(void);
+void attr_clear(WINDOW * win, int height, int width, chtype attr);
+void dialog_clear(void);
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
+void print_button(WINDOW * win, const char *label, int y, int x, int selected);
+void print_title(WINDOW *dialog, const char *title, int width);
+void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
+             chtype border);
+void draw_shadow(WINDOW * win, int y, int x, int height, int width);
+
+int first_alpha(const char *string, const char *exempt);
+int dialog_yesno(const char *title, const char *prompt, int height, int width);
+int dialog_msgbox(const char *title, const char *prompt, int height,
+                 int width, int pause);
+int dialog_textbox(const char *title, const char *file, int height, int width);
+int dialog_menu(const char *title, const char *prompt,
+               const void *selected, int *s_scroll);
+int dialog_checklist(const char *title, const char *prompt, int height,
+                    int width, int list_height);
+extern char dialog_input_result[];
+int dialog_inputbox(const char *title, const char *prompt, int height,
+                   int width, const char *init);
+
+/*
+ * This is the base for fictitious keys, which activate
+ * the buttons.
+ *
+ * Mouse-generated keys are the following:
+ *   -- the first 32 are used as numbers, in addition to '0'-'9'
+ *   -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
+ *   -- uppercase chars are used to invoke the button (M_EVENT + 'O')
+ */
+#define M_EVENT (KEY_MAX+1)
diff --git a/kconfig/lxdialog/inputbox.c b/kconfig/lxdialog/inputbox.c
new file mode 100644 (file)
index 0000000..05e7206
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ *  inputbox.c -- implements the input box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+char dialog_input_result[MAX_LEN + 1];
+
+/*
+ *  Print the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+       int x = width / 2 - 11;
+       int y = height - 2;
+
+       print_button(dialog, "  Ok  ", y, x, selected == 0);
+       print_button(dialog, " Help ", y, x + 14, selected == 1);
+
+       wmove(dialog, y, x + 1 + 14 * selected);
+       wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box for inputing a string
+ */
+int dialog_inputbox(const char *title, const char *prompt, int height, int width,
+                    const char *init)
+{
+       int i, x, y, box_y, box_x, box_width;
+       int input_x = 0, scroll = 0, key = 0, button = -1;
+       char *instr = dialog_input_result;
+       WINDOW *dialog;
+
+       if (!init)
+               instr[0] = '\0';
+       else
+               strcpy(instr, init);
+
+do_resize:
+       if (getmaxy(stdscr) <= (height - 2))
+               return -ERRDISPLAYTOOSMALL;
+       if (getmaxx(stdscr) <= (width - 2))
+               return -ERRDISPLAYTOOSMALL;
+
+       /* center dialog box on screen */
+       x = (COLS - width) / 2;
+       y = (LINES - height) / 2;
+
+       draw_shadow(stdscr, y, x, height, width);
+
+       dialog = newwin(height, width, y, x);
+       keypad(dialog, TRUE);
+
+       draw_box(dialog, 0, 0, height, width,
+                dlg.dialog.atr, dlg.border.atr);
+       wattrset(dialog, dlg.border.atr);
+       mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+       for (i = 0; i < width - 2; i++)
+               waddch(dialog, ACS_HLINE);
+       wattrset(dialog, dlg.dialog.atr);
+       waddch(dialog, ACS_RTEE);
+
+       print_title(dialog, title, width);
+
+       wattrset(dialog, dlg.dialog.atr);
+       print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+       /* Draw the input field box */
+       box_width = width - 6;
+       getyx(dialog, y, x);
+       box_y = y + 2;
+       box_x = (width - box_width) / 2;
+       draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2,
+                dlg.border.atr, dlg.dialog.atr);
+
+       print_buttons(dialog, height, width, 0);
+
+       /* Set up the initial value */
+       wmove(dialog, box_y, box_x);
+       wattrset(dialog, dlg.inputbox.atr);
+
+       input_x = strlen(instr);
+
+       if (input_x >= box_width) {
+               scroll = input_x - box_width + 1;
+               input_x = box_width - 1;
+               for (i = 0; i < box_width - 1; i++)
+                       waddch(dialog, instr[scroll + i]);
+       } else {
+               waddstr(dialog, instr);
+       }
+
+       wmove(dialog, box_y, box_x + input_x);
+
+       wrefresh(dialog);
+
+       while (key != KEY_ESC) {
+               key = wgetch(dialog);
+
+               if (button == -1) {     /* Input box selected */
+                       switch (key) {
+                       case TAB:
+                       case KEY_UP:
+                       case KEY_DOWN:
+                               break;
+                       case KEY_LEFT:
+                               continue;
+                       case KEY_RIGHT:
+                               continue;
+                       case KEY_BACKSPACE:
+                       case 127:
+                               if (input_x || scroll) {
+                                       wattrset(dialog, dlg.inputbox.atr);
+                                       if (!input_x) {
+                                               scroll = scroll < box_width - 1 ? 0 : scroll - (box_width - 1);
+                                               wmove(dialog, box_y, box_x);
+                                               for (i = 0; i < box_width; i++)
+                                                       waddch(dialog,
+                                                              instr[scroll + input_x + i] ?
+                                                              instr[scroll + input_x + i] : ' ');
+                                               input_x = strlen(instr) - scroll;
+                                       } else
+                                               input_x--;
+                                       instr[scroll + input_x] = '\0';
+                                       mvwaddch(dialog, box_y, input_x + box_x, ' ');
+                                       wmove(dialog, box_y, input_x + box_x);
+                                       wrefresh(dialog);
+                               }
+                               continue;
+                       default:
+                               if (key < 0x100 && isprint(key)) {
+                                       if (scroll + input_x < MAX_LEN) {
+                                               wattrset(dialog, dlg.inputbox.atr);
+                                               instr[scroll + input_x] = key;
+                                               instr[scroll + input_x + 1] = '\0';
+                                               if (input_x == box_width - 1) {
+                                                       scroll++;
+                                                       wmove(dialog, box_y, box_x);
+                                                       for (i = 0; i < box_width - 1; i++)
+                                                               waddch(dialog, instr [scroll + i]);
+                                               } else {
+                                                       wmove(dialog, box_y, input_x++ + box_x);
+                                                       waddch(dialog, key);
+                                               }
+                                               wrefresh(dialog);
+                                       } else
+                                               flash();        /* Alarm user about overflow */
+                                       continue;
+                               }
+                       }
+               }
+               switch (key) {
+               case 'O':
+               case 'o':
+                       delwin(dialog);
+                       return 0;
+               case 'H':
+               case 'h':
+                       delwin(dialog);
+                       return 1;
+               case KEY_UP:
+               case KEY_LEFT:
+                       switch (button) {
+                       case -1:
+                               button = 1;     /* Indicates "Cancel" button is selected */
+                               print_buttons(dialog, height, width, 1);
+                               break;
+                       case 0:
+                               button = -1;    /* Indicates input box is selected */
+                               print_buttons(dialog, height, width, 0);
+                               wmove(dialog, box_y, box_x + input_x);
+                               wrefresh(dialog);
+                               break;
+                       case 1:
+                               button = 0;     /* Indicates "OK" button is selected */
+                               print_buttons(dialog, height, width, 0);
+                               break;
+                       }
+                       break;
+               case TAB:
+               case KEY_DOWN:
+               case KEY_RIGHT:
+                       switch (button) {
+                       case -1:
+                               button = 0;     /* Indicates "OK" button is selected */
+                               print_buttons(dialog, height, width, 0);
+                               break;
+                       case 0:
+                               button = 1;     /* Indicates "Cancel" button is selected */
+                               print_buttons(dialog, height, width, 1);
+                               break;
+                       case 1:
+                               button = -1;    /* Indicates input box is selected */
+                               print_buttons(dialog, height, width, 0);
+                               wmove(dialog, box_y, box_x + input_x);
+                               wrefresh(dialog);
+                               break;
+                       }
+                       break;
+               case ' ':
+               case '\n':
+                       delwin(dialog);
+                       return (button == -1 ? 0 : button);
+               case 'X':
+               case 'x':
+                       key = KEY_ESC;
+                       break;
+               case KEY_ESC:
+                       key = on_key_esc(dialog);
+                       break;
+               case KEY_RESIZE:
+                       delwin(dialog);
+                       on_key_resize();
+                       goto do_resize;
+               }
+       }
+
+       delwin(dialog);
+       return KEY_ESC;         /* ESC pressed */
+}
diff --git a/kconfig/lxdialog/menubox.c b/kconfig/lxdialog/menubox.c
new file mode 100644 (file)
index 0000000..0d83159
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ *  menubox.c -- implements the menu box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *  Changes by Clifford Wolf (god@clifford.at)
+ *
+ *  [ 1998-06-13 ]
+ *
+ *    *)  A bugfix for the Page-Down problem
+ *
+ *    *)  Formerly when I used Page Down and Page Up, the cursor would be set 
+ *        to the first position in the menu box.  Now lxdialog is a bit
+ *        smarter and works more like other menu systems (just have a look at
+ *        it).
+ *
+ *    *)  Formerly if I selected something my scrolling would be broken because
+ *        lxdialog is re-invoked by the Menuconfig shell script, can't
+ *        remember the last scrolling position, and just sets it so that the
+ *        cursor is at the bottom of the box.  Now it writes the temporary file
+ *        lxdialog.scrltmp which contains this information. The file is
+ *        deleted by lxdialog if the user leaves a submenu or enters a new
+ *        one, but it would be nice if Menuconfig could make another "rm -f"
+ *        just to be sure.  Just try it out - you will recognise a difference!
+ *
+ *  [ 1998-06-14 ]
+ *
+ *    *)  Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
+ *        and menus change their size on the fly.
+ *
+ *    *)  If for some reason the last scrolling position is not saved by
+ *        lxdialog, it sets the scrolling so that the selected item is in the
+ *        middle of the menu box, not at the bottom.
+ *
+ * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+ * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
+ * This fixes a bug in Menuconfig where using ' ' to descend into menus
+ * would leave mis-synchronized lxdialog.scrltmp files lying around,
+ * fscanf would read in 'scroll', and eventually that value would get used.
+ */
+
+#include "dialog.h"
+
+static int menu_width, item_x;
+
+/*
+ * Print menu item
+ */
+static void do_print_item(WINDOW * win, const char *item, int line_y,
+                          int selected, int hotkey)
+{
+       int j;
+       char *menu_item = malloc(menu_width + 1);
+
+       strncpy(menu_item, item, menu_width - item_x);
+       menu_item[menu_width - item_x] = '\0';
+       j = first_alpha(menu_item, "YyNnMmHh");
+
+       /* Clear 'residue' of last item */
+       wattrset(win, dlg.menubox.atr);
+       wmove(win, line_y, 0);
+#if OLD_NCURSES
+       {
+               int i;
+               for (i = 0; i < menu_width; i++)
+                       waddch(win, ' ');
+       }
+#else
+       wclrtoeol(win);
+#endif
+       wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
+       mvwaddstr(win, line_y, item_x, menu_item);
+       if (hotkey) {
+               wattrset(win, selected ? dlg.tag_key_selected.atr
+                        : dlg.tag_key.atr);
+               mvwaddch(win, line_y, item_x + j, menu_item[j]);
+       }
+       if (selected) {
+               wmove(win, line_y, item_x + 1);
+       }
+       free(menu_item);
+       wrefresh(win);
+}
+
+#define print_item(index, choice, selected)                            \
+do {                                                                   \
+       item_set(index);                                                \
+       do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
+} while (0)
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
+                        int height)
+{
+       int cur_y, cur_x;
+
+       getyx(win, cur_y, cur_x);
+
+       wmove(win, y, x);
+
+       if (scroll > 0) {
+               wattrset(win, dlg.uarrow.atr);
+               waddch(win, ACS_UARROW);
+               waddstr(win, "(-)");
+       } else {
+               wattrset(win, dlg.menubox.atr);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+       }
+
+       y = y + height + 1;
+       wmove(win, y, x);
+       wrefresh(win);
+
+       if ((height < item_no) && (scroll + height < item_no)) {
+               wattrset(win, dlg.darrow.atr);
+               waddch(win, ACS_DARROW);
+               waddstr(win, "(+)");
+       } else {
+               wattrset(win, dlg.menubox_border.atr);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+               waddch(win, ACS_HLINE);
+       }
+
+       wmove(win, cur_y, cur_x);
+       wrefresh(win);
+}
+
+/*
+ * Display the termination buttons.
+ */
+static void print_buttons(WINDOW * win, int height, int width, int selected)
+{
+       int x = width / 2 - 16;
+       int y = height - 2;
+
+       print_button(win, "Select", y, x, selected == 0);
+       print_button(win, " Exit ", y, x + 12, selected == 1);
+       print_button(win, " Help ", y, x + 24, selected == 2);
+
+       wmove(win, y, x + 1 + 12 * selected);
+       wrefresh(win);
+}
+
+/* scroll up n lines (n may be negative) */
+static void do_scroll(WINDOW *win, int *scroll, int n)
+{
+       /* Scroll menu up */
+       scrollok(win, TRUE);
+       wscrl(win, n);
+       scrollok(win, FALSE);
+       *scroll = *scroll + n;
+       wrefresh(win);
+}
+
+/*
+ * Display a menu for choosing among a number of options
+ */
+int dialog_menu(const char *title, const char *prompt,
+                const void *selected, int *s_scroll)
+{
+       int i, j, x, y, box_x, box_y;
+       int height, width, menu_height;
+       int key = 0, button = 0, scroll = 0, choice = 0;
+       int first_item =  0, max_choice;
+       WINDOW *dialog, *menu;
+
+do_resize:
+       height = getmaxy(stdscr);
+       width = getmaxx(stdscr);
+       if (height < 15 || width < 65)
+               return -ERRDISPLAYTOOSMALL;
+
+       height -= 4;
+       width  -= 5;
+       menu_height = height - 10;
+
+       max_choice = MIN(menu_height, item_count());
+
+       /* center dialog box on screen */
+       x = (COLS - width) / 2;
+       y = (LINES - height) / 2;
+
+       draw_shadow(stdscr, y, x, height, width);
+
+       dialog = newwin(height, width, y, x);
+       keypad(dialog, TRUE);
+
+       draw_box(dialog, 0, 0, height, width,
+                dlg.dialog.atr, dlg.border.atr);
+       wattrset(dialog, dlg.border.atr);
+       mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+       for (i = 0; i < width - 2; i++)
+               waddch(dialog, ACS_HLINE);
+       wattrset(dialog, dlg.dialog.atr);
+       wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
+       waddch(dialog, ACS_RTEE);
+
+       print_title(dialog, title, width);
+
+       wattrset(dialog, dlg.dialog.atr);
+       print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+       menu_width = width - 6;
+       box_y = height - menu_height - 5;
+       box_x = (width - menu_width) / 2 - 1;
+
+       /* create new window for the menu */
+       menu = subwin(dialog, menu_height, menu_width,
+                     y + box_y + 1, x + box_x + 1);
+       keypad(menu, TRUE);
+
+       /* draw a box around the menu items */
+       draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+                dlg.menubox_border.atr, dlg.menubox.atr);
+
+       if (menu_width >= 80)
+               item_x = (menu_width - 70) / 2;
+       else
+               item_x = 4;
+
+       /* Set choice to default item */
+       item_foreach()
+               if (selected && (selected == item_data()))
+                       choice = item_n();
+       /* get the saved scroll info */
+       scroll = *s_scroll;
+       if ((scroll <= choice) && (scroll + max_choice > choice) &&
+          (scroll >= 0) && (scroll + max_choice <= item_count())) {
+               first_item = scroll;
+               choice = choice - scroll;
+       } else {
+               scroll = 0;
+       }
+       if ((choice >= max_choice)) {
+               if (choice >= item_count() - max_choice / 2)
+                       scroll = first_item = item_count() - max_choice;
+               else
+                       scroll = first_item = choice - max_choice / 2;
+               choice = choice - scroll;
+       }
+
+       /* Print the menu */
+       for (i = 0; i < max_choice; i++) {
+               print_item(first_item + i, i, i == choice);
+       }
+
+       wnoutrefresh(menu);
+
+       print_arrows(dialog, item_count(), scroll,
+                    box_y, box_x + item_x + 1, menu_height);
+
+       print_buttons(dialog, height, width, 0);
+       wmove(menu, choice, item_x + 1);
+       wrefresh(menu);
+
+       while (key != KEY_ESC) {
+               key = wgetch(menu);
+
+               if (key < 256 && isalpha(key))
+                       key = tolower(key);
+
+               if (strchr("ynmh", key))
+                       i = max_choice;
+               else {
+                       for (i = choice + 1; i < max_choice; i++) {
+                               item_set(scroll + i);
+                               j = first_alpha(item_str(), "YyNnMmHh");
+                               if (key == tolower(item_str()[j]))
+                                       break;
+                       }
+                       if (i == max_choice)
+                               for (i = 0; i < max_choice; i++) {
+                                       item_set(scroll + i);
+                                       j = first_alpha(item_str(), "YyNnMmHh");
+                                       if (key == tolower(item_str()[j]))
+                                               break;
+                               }
+               }
+
+               if (i < max_choice ||
+                   key == KEY_UP || key == KEY_DOWN ||
+                   key == '-' || key == '+' ||
+                   key == KEY_PPAGE || key == KEY_NPAGE) {
+                       /* Remove highligt of current item */
+                       print_item(scroll + choice, choice, FALSE);
+
+                       if (key == KEY_UP || key == '-') {
+                               if (choice < 2 && scroll) {
+                                       /* Scroll menu down */
+                                       do_scroll(menu, &scroll, -1);
+
+                                       print_item(scroll, 0, FALSE);
+                               } else
+                                       choice = MAX(choice - 1, 0);
+
+                       } else if (key == KEY_DOWN || key == '+') {
+                               print_item(scroll+choice, choice, FALSE);
+
+                               if ((choice > max_choice - 3) &&
+                                   (scroll + max_choice < item_count())) {
+                                       /* Scroll menu up */
+                                       do_scroll(menu, &scroll, 1);
+
+                                       print_item(scroll+max_choice - 1,
+                                                  max_choice - 1, FALSE);
+                               } else
+                                       choice = MIN(choice + 1, max_choice - 1);
+
+                       } else if (key == KEY_PPAGE) {
+                               scrollok(menu, TRUE);
+                               for (i = 0; (i < max_choice); i++) {
+                                       if (scroll > 0) {
+                                               do_scroll(menu, &scroll, -1);
+                                               print_item(scroll, 0, FALSE);
+                                       } else {
+                                               if (choice > 0)
+                                                       choice--;
+                                       }
+                               }
+
+                       } else if (key == KEY_NPAGE) {
+                               for (i = 0; (i < max_choice); i++) {
+                                       if (scroll + max_choice < item_count()) {
+                                               do_scroll(menu, &scroll, 1);
+                                               print_item(scroll+max_choice-1,
+                                                          max_choice - 1, FALSE);
+                                       } else {
+                                               if (choice + 1 < max_choice)
+                                                       choice++;
+                                       }
+                               }
+                       } else
+                               choice = i;
+
+                       print_item(scroll + choice, choice, TRUE);
+
+                       print_arrows(dialog, item_count(), scroll,
+                                    box_y, box_x + item_x + 1, menu_height);
+
+                       wnoutrefresh(dialog);
+                       wrefresh(menu);
+
+                       continue;       /* wait for another key press */
+               }
+
+               switch (key) {
+               case KEY_LEFT:
+               case TAB:
+               case KEY_RIGHT:
+                       button = ((key == KEY_LEFT ? --button : ++button) < 0)
+                           ? 2 : (button > 2 ? 0 : button);
+
+                       print_buttons(dialog, height, width, button);
+                       wrefresh(menu);
+                       break;
+               case ' ':
+               case 's':
+               case 'y':
+               case 'n':
+               case 'm':
+               case '/':
+                       /* save scroll info */
+                       *s_scroll = scroll;
+                       delwin(menu);
+                       delwin(dialog);
+                       item_set(scroll + choice);
+                       item_set_selected(1);
+                       switch (key) {
+                       case 's':
+                               return 3;
+                       case 'y':
+                               return 3;
+                       case 'n':
+                               return 4;
+                       case 'm':
+                               return 5;
+                       case ' ':
+                               return 6;
+                       case '/':
+                               return 7;
+                       }
+                       return 0;
+               case 'h':
+               case '?':
+                       button = 2;
+               case '\n':
+                       *s_scroll = scroll;
+                       delwin(menu);
+                       delwin(dialog);
+                       item_set(scroll + choice);
+                       item_set_selected(1);
+                       return button;
+               case 'e':
+               case 'x':
+                       key = KEY_ESC;
+                       break;
+               case KEY_ESC:
+                       key = on_key_esc(menu);
+                       break;
+               case KEY_RESIZE:
+                       on_key_resize();
+                       delwin(menu);
+                       delwin(dialog);
+                       goto do_resize;
+               }
+       }
+       delwin(menu);
+       delwin(dialog);
+       return key;             /* ESC pressed */
+}
diff --git a/kconfig/lxdialog/textbox.c b/kconfig/lxdialog/textbox.c
new file mode 100644 (file)
index 0000000..fabfc1a
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ *  textbox.c -- implements the text box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void back_lines(int n);
+static void print_page(WINDOW * win, int height, int width);
+static void print_line(WINDOW * win, int row, int width);
+static char *get_line(void);
+static void print_position(WINDOW * win);
+
+static int hscroll;
+static int begin_reached, end_reached, page_length;
+static const char *buf;
+static const char *page;
+
+/*
+ * refresh window content
+ */
+static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
+                                                         int cur_y, int cur_x)
+{
+       print_page(box, boxh, boxw);
+       print_position(dialog);
+       wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
+       wrefresh(dialog);
+}
+
+
+/*
+ * Display text from a file in a dialog box.
+ */
+int dialog_textbox(const char *title, const char *tbuf,
+                  int initial_height, int initial_width)
+{
+       int i, x, y, cur_x, cur_y, key = 0;
+       int height, width, boxh, boxw;
+       int passed_end;
+       WINDOW *dialog, *box;
+
+       begin_reached = 1;
+       end_reached = 0;
+       page_length = 0;
+       hscroll = 0;
+       buf = tbuf;
+       page = buf;     /* page is pointer to start of page to be displayed */
+
+do_resize:
+       getmaxyx(stdscr, height, width);
+       if (height < 8 || width < 8)
+               return -ERRDISPLAYTOOSMALL;
+       if (initial_height != 0)
+               height = initial_height;
+       else
+               if (height > 4)
+                       height -= 4;
+               else
+                       height = 0;
+       if (initial_width != 0)
+               width = initial_width;
+       else
+               if (width > 5)
+                       width -= 5;
+               else
+                       width = 0;
+
+       /* center dialog box on screen */
+       x = (COLS - width) / 2;
+       y = (LINES - height) / 2;
+
+       draw_shadow(stdscr, y, x, height, width);
+
+       dialog = newwin(height, width, y, x);
+       keypad(dialog, TRUE);
+
+       /* Create window for box region, used for scrolling text */
+       boxh = height - 4;
+       boxw = width - 2;
+       box = subwin(dialog, boxh, boxw, y + 1, x + 1);
+       wattrset(box, dlg.dialog.atr);
+       wbkgdset(box, dlg.dialog.atr & A_COLOR);
+
+       keypad(box, TRUE);
+
+       /* register the new window, along with its borders */
+       draw_box(dialog, 0, 0, height, width,
+                dlg.dialog.atr, dlg.border.atr);
+
+       wattrset(dialog, dlg.border.atr);
+       mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+       for (i = 0; i < width - 2; i++)
+               waddch(dialog, ACS_HLINE);
+       wattrset(dialog, dlg.dialog.atr);
+       wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
+       waddch(dialog, ACS_RTEE);
+
+       print_title(dialog, title, width);
+
+       print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
+       wnoutrefresh(dialog);
+       getyx(dialog, cur_y, cur_x);    /* Save cursor position */
+
+       /* Print first page of text */
+       attr_clear(box, boxh, boxw, dlg.dialog.atr);
+       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
+
+       while ((key != KEY_ESC) && (key != '\n')) {
+               key = wgetch(dialog);
+               switch (key) {
+               case 'E':       /* Exit */
+               case 'e':
+               case 'X':
+               case 'x':
+                       delwin(box);
+                       delwin(dialog);
+                       return 0;
+               case 'g':       /* First page */
+               case KEY_HOME:
+                       if (!begin_reached) {
+                               begin_reached = 1;
+                               page = buf;
+                               refresh_text_box(dialog, box, boxh, boxw,
+                                                cur_y, cur_x);
+                       }
+                       break;
+               case 'G':       /* Last page */
+               case KEY_END:
+
+                       end_reached = 1;
+                       /* point to last char in buf */
+                       page = buf + strlen(buf);
+                       back_lines(boxh);
+                       refresh_text_box(dialog, box, boxh, boxw,
+                                        cur_y, cur_x);
+                       break;
+               case 'K':       /* Previous line */
+               case 'k':
+               case KEY_UP:
+                       if (!begin_reached) {
+                               back_lines(page_length + 1);
+
+                               /* We don't call print_page() here but use
+                                * scrolling to ensure faster screen update.
+                                * However, 'end_reached' and 'page_length'
+                                * should still be updated, and 'page' should
+                                * point to start of next page. This is done
+                                * by calling get_line() in the following
+                                * 'for' loop. */
+                               scrollok(box, TRUE);
+                               wscrl(box, -1); /* Scroll box region down one line */
+                               scrollok(box, FALSE);
+                               page_length = 0;
+                               passed_end = 0;
+                               for (i = 0; i < boxh; i++) {
+                                       if (!i) {
+                                               /* print first line of page */
+                                               print_line(box, 0, boxw);
+                                               wnoutrefresh(box);
+                                       } else
+                                               /* Called to update 'end_reached' and 'page' */
+                                               get_line();
+                                       if (!passed_end)
+                                               page_length++;
+                                       if (end_reached && !passed_end)
+                                               passed_end = 1;
+                               }
+
+                               print_position(dialog);
+                               wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
+                               wrefresh(dialog);
+                       }
+                       break;
+               case 'B':       /* Previous page */
+               case 'b':
+               case KEY_PPAGE:
+                       if (begin_reached)
+                               break;
+                       back_lines(page_length + boxh);
+                       refresh_text_box(dialog, box, boxh, boxw,
+                                        cur_y, cur_x);
+                       break;
+               case 'J':       /* Next line */
+               case 'j':
+               case KEY_DOWN:
+                       if (!end_reached) {
+                               begin_reached = 0;
+                               scrollok(box, TRUE);
+                               scroll(box);    /* Scroll box region up one line */
+                               scrollok(box, FALSE);
+                               print_line(box, boxh - 1, boxw);
+                               wnoutrefresh(box);
+                               print_position(dialog);
+                               wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
+                               wrefresh(dialog);
+                       }
+                       break;
+               case KEY_NPAGE: /* Next page */
+               case ' ':
+                       if (end_reached)
+                               break;
+
+                       begin_reached = 0;
+                       refresh_text_box(dialog, box, boxh, boxw,
+                                        cur_y, cur_x);
+                       break;
+               case '0':       /* Beginning of line */
+               case 'H':       /* Scroll left */
+               case 'h':
+               case KEY_LEFT:
+                       if (hscroll <= 0)
+                               break;
+
+                       if (key == '0')
+                               hscroll = 0;
+                       else
+                               hscroll--;
+                       /* Reprint current page to scroll horizontally */
+                       back_lines(page_length);
+                       refresh_text_box(dialog, box, boxh, boxw,
+                                        cur_y, cur_x);
+                       break;
+               case 'L':       /* Scroll right */
+               case 'l':
+               case KEY_RIGHT:
+                       if (hscroll >= MAX_LEN)
+                               break;
+                       hscroll++;
+                       /* Reprint current page to scroll horizontally */
+                       back_lines(page_length);
+                       refresh_text_box(dialog, box, boxh, boxw,
+                                        cur_y, cur_x);
+                       break;
+               case KEY_ESC:
+                       key = on_key_esc(dialog);
+                       break;
+               case KEY_RESIZE:
+                       back_lines(height);
+                       delwin(box);
+                       delwin(dialog);
+                       on_key_resize();
+                       goto do_resize;
+               }
+       }
+       delwin(box);
+       delwin(dialog);
+       return key;             /* ESC pressed */
+}
+
+/*
+ * Go back 'n' lines in text. Called by dialog_textbox().
+ * 'page' will be updated to point to the desired line in 'buf'.
+ */
+static void back_lines(int n)
+{
+       int i;
+
+       begin_reached = 0;
+       /* Go back 'n' lines */
+       for (i = 0; i < n; i++) {
+               if (*page == '\0') {
+                       if (end_reached) {
+                               end_reached = 0;
+                               continue;
+                       }
+               }
+               if (page == buf) {
+                       begin_reached = 1;
+                       return;
+               }
+               page--;
+               do {
+                       if (page == buf) {
+                               begin_reached = 1;
+                               return;
+                       }
+                       page--;
+               } while (*page != '\n');
+               page++;
+       }
+}
+
+/*
+ * Print a new page of text. Called by dialog_textbox().
+ */
+static void print_page(WINDOW * win, int height, int width)
+{
+       int i, passed_end = 0;
+
+       page_length = 0;
+       for (i = 0; i < height; i++) {
+               print_line(win, i, width);
+               if (!passed_end)
+                       page_length++;
+               if (end_reached && !passed_end)
+                       passed_end = 1;
+       }
+       wnoutrefresh(win);
+}
+
+/*
+ * Print a new line of text. Called by dialog_textbox() and print_page().
+ */
+static void print_line(WINDOW * win, int row, int width)
+{
+       int y, x;
+       char *line;
+
+       line = get_line();
+       line += MIN(strlen(line), hscroll);     /* Scroll horizontally */
+       wmove(win, row, 0);     /* move cursor to correct line */
+       waddch(win, ' ');
+       waddnstr(win, line, MIN(strlen(line), width - 2));
+
+       getyx(win, y, x);
+       /* Clear 'residue' of previous line */
+#if OLD_NCURSES
+       {
+               int i;
+               for (i = 0; i < width - x; i++)
+                       waddch(win, ' ');
+       }
+#else
+       wclrtoeol(win);
+#endif
+}
+
+/*
+ * Return current line of text. Called by dialog_textbox() and print_line().
+ * 'page' should point to start of current line before calling, and will be
+ * updated to point to start of next line.
+ */
+static char *get_line(void)
+{
+       int i = 0;
+       static char line[MAX_LEN + 1];
+
+       end_reached = 0;
+       while (*page != '\n') {
+               if (*page == '\0') {
+                       if (!end_reached) {
+                               end_reached = 1;
+                               break;
+                       }
+               } else if (i < MAX_LEN)
+                       line[i++] = *(page++);
+               else {
+                       /* Truncate lines longer than MAX_LEN characters */
+                       if (i == MAX_LEN)
+                               line[i++] = '\0';
+                       page++;
+               }
+       }
+       if (i <= MAX_LEN)
+               line[i] = '\0';
+       if (!end_reached)
+               page++;         /* move pass '\n' */
+
+       return line;
+}
+
+/*
+ * Print current position
+ */
+static void print_position(WINDOW * win)
+{
+       int percent;
+
+       wattrset(win, dlg.position_indicator.atr);
+       wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
+       percent = (page - buf) * 100 / strlen(buf);
+       wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
+       wprintw(win, "(%3d%%)", percent);
+}
diff --git a/kconfig/lxdialog/util.c b/kconfig/lxdialog/util.c
new file mode 100644 (file)
index 0000000..ebc781b
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ *  util.c
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+struct dialog_info dlg;
+
+static void set_mono_theme(void)
+{
+       dlg.screen.atr = A_NORMAL;
+       dlg.shadow.atr = A_NORMAL;
+       dlg.dialog.atr = A_NORMAL;
+       dlg.title.atr = A_BOLD;
+       dlg.border.atr = A_NORMAL;
+       dlg.button_active.atr = A_REVERSE;
+       dlg.button_inactive.atr = A_DIM;
+       dlg.button_key_active.atr = A_REVERSE;
+       dlg.button_key_inactive.atr = A_BOLD;
+       dlg.button_label_active.atr = A_REVERSE;
+       dlg.button_label_inactive.atr = A_NORMAL;
+       dlg.inputbox.atr = A_NORMAL;
+       dlg.inputbox_border.atr = A_NORMAL;
+       dlg.searchbox.atr = A_NORMAL;
+       dlg.searchbox_title.atr = A_BOLD;
+       dlg.searchbox_border.atr = A_NORMAL;
+       dlg.position_indicator.atr = A_BOLD;
+       dlg.menubox.atr = A_NORMAL;
+       dlg.menubox_border.atr = A_NORMAL;
+       dlg.item.atr = A_NORMAL;
+       dlg.item_selected.atr = A_REVERSE;
+       dlg.tag.atr = A_BOLD;
+       dlg.tag_selected.atr = A_REVERSE;
+       dlg.tag_key.atr = A_BOLD;
+       dlg.tag_key_selected.atr = A_REVERSE;
+       dlg.check.atr = A_BOLD;
+       dlg.check_selected.atr = A_REVERSE;
+       dlg.uarrow.atr = A_BOLD;
+       dlg.darrow.atr = A_BOLD;
+}
+
+#define DLG_COLOR(dialog, f, b, h) \
+do {                               \
+       dlg.dialog.fg = (f);       \
+       dlg.dialog.bg = (b);       \
+       dlg.dialog.hl = (h);       \
+} while (0)
+
+static void set_classic_theme(void)
+{
+       DLG_COLOR(screen,                COLOR_CYAN,   COLOR_BLUE,   true);
+       DLG_COLOR(shadow,                COLOR_BLACK,  COLOR_BLACK,  true);
+       DLG_COLOR(dialog,                COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(title,                 COLOR_YELLOW, COLOR_WHITE,  true);
+       DLG_COLOR(border,                COLOR_WHITE,  COLOR_WHITE,  true);
+       DLG_COLOR(button_active,         COLOR_WHITE,  COLOR_BLUE,   true);
+       DLG_COLOR(button_inactive,       COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(button_key_active,     COLOR_WHITE,  COLOR_BLUE,   true);
+       DLG_COLOR(button_key_inactive,   COLOR_RED,    COLOR_WHITE,  false);
+       DLG_COLOR(button_label_active,   COLOR_YELLOW, COLOR_BLUE,   true);
+       DLG_COLOR(button_label_inactive, COLOR_BLACK,  COLOR_WHITE,  true);
+       DLG_COLOR(inputbox,              COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(inputbox_border,       COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(searchbox,             COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(searchbox_title,       COLOR_YELLOW, COLOR_WHITE,  true);
+       DLG_COLOR(searchbox_border,      COLOR_WHITE,  COLOR_WHITE,  true);
+       DLG_COLOR(position_indicator,    COLOR_YELLOW, COLOR_WHITE,  true);
+       DLG_COLOR(menubox,               COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(menubox_border,        COLOR_WHITE,  COLOR_WHITE,  true);
+       DLG_COLOR(item,                  COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(item_selected,         COLOR_WHITE,  COLOR_BLUE,   true);
+       DLG_COLOR(tag,                   COLOR_YELLOW, COLOR_WHITE,  true);
+       DLG_COLOR(tag_selected,          COLOR_YELLOW, COLOR_BLUE,   true);
+       DLG_COLOR(tag_key,               COLOR_YELLOW, COLOR_WHITE,  true);
+       DLG_COLOR(tag_key_selected,      COLOR_YELLOW, COLOR_BLUE,   true);
+       DLG_COLOR(check,                 COLOR_BLACK,  COLOR_WHITE,  false);
+       DLG_COLOR(check_selected,        COLOR_WHITE,  COLOR_BLUE,   true);
+       DLG_COLOR(uarrow,                COLOR_GREEN,  COLOR_WHITE,  true);
+       DLG_COLOR(darrow,                COLOR_GREEN,  COLOR_WHITE,  true);
+}
+
+static void set_blackbg_theme(void)
+{
+       DLG_COLOR(screen, COLOR_RED,   COLOR_BLACK, true);
+       DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false);
+       DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false);
+       DLG_COLOR(title,  COLOR_RED,   COLOR_BLACK, false);
+       DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true);
+
+       DLG_COLOR(button_active,         COLOR_YELLOW, COLOR_RED,   false);
+       DLG_COLOR(button_inactive,       COLOR_YELLOW, COLOR_BLACK, false);
+       DLG_COLOR(button_key_active,     COLOR_YELLOW, COLOR_RED,   true);
+       DLG_COLOR(button_key_inactive,   COLOR_RED,    COLOR_BLACK, false);
+       DLG_COLOR(button_label_active,   COLOR_WHITE,  COLOR_RED,   false);
+       DLG_COLOR(button_label_inactive, COLOR_BLACK,  COLOR_BLACK, true);
+
+       DLG_COLOR(inputbox,         COLOR_YELLOW, COLOR_BLACK, false);
+       DLG_COLOR(inputbox_border,  COLOR_YELLOW, COLOR_BLACK, false);
+
+       DLG_COLOR(searchbox,        COLOR_YELLOW, COLOR_BLACK, false);
+       DLG_COLOR(searchbox_title,  COLOR_YELLOW, COLOR_BLACK, true);
+       DLG_COLOR(searchbox_border, COLOR_BLACK,  COLOR_BLACK, true);
+
+       DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK,  false);
+
+       DLG_COLOR(menubox,          COLOR_YELLOW, COLOR_BLACK, false);
+       DLG_COLOR(menubox_border,   COLOR_BLACK,  COLOR_BLACK, true);
+
+       DLG_COLOR(item,             COLOR_WHITE, COLOR_BLACK, false);
+       DLG_COLOR(item_selected,    COLOR_WHITE, COLOR_RED,   false);
+
+       DLG_COLOR(tag,              COLOR_RED,    COLOR_BLACK, false);
+       DLG_COLOR(tag_selected,     COLOR_YELLOW, COLOR_RED,   true);
+       DLG_COLOR(tag_key,          COLOR_RED,    COLOR_BLACK, false);
+       DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED,   true);
+
+       DLG_COLOR(check,            COLOR_YELLOW, COLOR_BLACK, false);
+       DLG_COLOR(check_selected,   COLOR_YELLOW, COLOR_RED,   true);
+
+       DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false);
+       DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false);
+}
+
+static void set_bluetitle_theme(void)
+{
+       set_classic_theme();
+       DLG_COLOR(title,               COLOR_BLUE,   COLOR_WHITE, true);
+       DLG_COLOR(button_key_active,   COLOR_YELLOW, COLOR_BLUE,  true);
+       DLG_COLOR(button_label_active, COLOR_WHITE,  COLOR_BLUE,  true);
+       DLG_COLOR(searchbox_title,     COLOR_BLUE,   COLOR_WHITE, true);
+       DLG_COLOR(position_indicator,  COLOR_BLUE,   COLOR_WHITE, true);
+       DLG_COLOR(tag,                 COLOR_BLUE,   COLOR_WHITE, true);
+       DLG_COLOR(tag_key,             COLOR_BLUE,   COLOR_WHITE, true);
+
+}
+
+/*
+ * Select color theme
+ */
+static int set_theme(const char *theme)
+{
+       int use_color = 1;
+       if (!theme)
+               set_bluetitle_theme();
+       else if (strcmp(theme, "classic") == 0)
+               set_classic_theme();
+       else if (strcmp(theme, "bluetitle") == 0)
+               set_bluetitle_theme();
+       else if (strcmp(theme, "blackbg") == 0)
+               set_blackbg_theme();
+       else if (strcmp(theme, "mono") == 0)
+               use_color = 0;
+
+       return use_color;
+}
+
+static void init_one_color(struct dialog_color *color)
+{
+       static int pair = 0;
+
+       pair++;
+       init_pair(pair, color->fg, color->bg);
+       if (color->hl)
+               color->atr = A_BOLD | COLOR_PAIR(pair);
+       else
+               color->atr = COLOR_PAIR(pair);
+}
+
+static void init_dialog_colors(void)
+{
+       init_one_color(&dlg.screen);
+       init_one_color(&dlg.shadow);
+       init_one_color(&dlg.dialog);
+       init_one_color(&dlg.title);
+       init_one_color(&dlg.border);
+       init_one_color(&dlg.button_active);
+       init_one_color(&dlg.button_inactive);
+       init_one_color(&dlg.button_key_active);
+       init_one_color(&dlg.button_key_inactive);
+       init_one_color(&dlg.button_label_active);
+       init_one_color(&dlg.button_label_inactive);
+       init_one_color(&dlg.inputbox);
+       init_one_color(&dlg.inputbox_border);
+       init_one_color(&dlg.searchbox);
+       init_one_color(&dlg.searchbox_title);
+       init_one_color(&dlg.searchbox_border);
+       init_one_color(&dlg.position_indicator);
+       init_one_color(&dlg.menubox);
+       init_one_color(&dlg.menubox_border);
+       init_one_color(&dlg.item);
+       init_one_color(&dlg.item_selected);
+       init_one_color(&dlg.tag);
+       init_one_color(&dlg.tag_selected);
+       init_one_color(&dlg.tag_key);
+       init_one_color(&dlg.tag_key_selected);
+       init_one_color(&dlg.check);
+       init_one_color(&dlg.check_selected);
+       init_one_color(&dlg.uarrow);
+       init_one_color(&dlg.darrow);
+}
+
+/*
+ * Setup for color display
+ */
+static void color_setup(const char *theme)
+{
+       if (set_theme(theme)) {
+               if (has_colors()) {     /* Terminal supports color? */
+                       start_color();
+                       init_dialog_colors();
+               }
+       }
+       else
+       {
+               set_mono_theme();
+       }
+}
+
+/*
+ * Set window to attribute 'attr'
+ */
+void attr_clear(WINDOW * win, int height, int width, chtype attr)
+{
+       int i, j;
+
+       wattrset(win, attr);
+       for (i = 0; i < height; i++) {
+               wmove(win, i, 0);
+               for (j = 0; j < width; j++)
+                       waddch(win, ' ');
+       }
+       touchwin(win);
+}
+
+void dialog_clear(void)
+{
+       attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
+       /* Display background title if it exists ... - SLH */
+       if (dlg.backtitle != NULL) {
+               int i;
+
+               wattrset(stdscr, dlg.screen.atr);
+               mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
+               wmove(stdscr, 1, 1);
+               for (i = 1; i < COLS - 1; i++)
+                       waddch(stdscr, ACS_HLINE);
+       }
+       wnoutrefresh(stdscr);
+}
+
+/*
+ * Do some initialization for dialog
+ */
+void init_dialog(const char *backtitle)
+{
+       dlg.backtitle = backtitle;
+       color_setup(getenv("MENUCONFIG_COLOR"));
+}
+
+void reset_dialog(void)
+{
+       initscr();              /* Init curses */
+       keypad(stdscr, TRUE);
+       cbreak();
+       noecho();
+       dialog_clear();
+}
+
+/*
+ * End using dialog functions.
+ */
+void end_dialog(void)
+{
+       endwin();
+}
+
+/* Print the title of the dialog. Center the title and truncate
+ * tile if wider than dialog (- 2 chars).
+ **/
+void print_title(WINDOW *dialog, const char *title, int width)
+{
+       if (title) {
+               int tlen = MIN(width - 2, strlen(title));
+               wattrset(dialog, dlg.title.atr);
+               mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
+               mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
+               waddch(dialog, ' ');
+       }
+}
+
+/*
+ * Print a string of text in a window, automatically wrap around to the
+ * next line if the string is too long to fit on one line. Newline
+ * characters '\n' are replaced by spaces.  We start on a new line
+ * if there is no room for at least 4 nonblanks following a double-space.
+ */
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
+{
+       int newl, cur_x, cur_y;
+       int i, prompt_len, room, wlen;
+       char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
+
+       strcpy(tempstr, prompt);
+
+       prompt_len = strlen(tempstr);
+
+       /*
+        * Remove newlines
+        */
+       for (i = 0; i < prompt_len; i++) {
+               if (tempstr[i] == '\n')
+                       tempstr[i] = ' ';
+       }
+
+       if (prompt_len <= width - x * 2) {      /* If prompt is short */
+               wmove(win, y, (width - prompt_len) / 2);
+               waddstr(win, tempstr);
+       } else {
+               cur_x = x;
+               cur_y = y;
+               newl = 1;
+               word = tempstr;
+               while (word && *word) {
+                       sp = index(word, ' ');
+                       if (sp)
+                               *sp++ = 0;
+
+                       /* Wrap to next line if either the word does not fit,
+                          or it is the first word of a new sentence, and it is
+                          short, and the next word does not fit. */
+                       room = width - cur_x;
+                       wlen = strlen(word);
+                       if (wlen > room ||
+                           (newl && wlen < 4 && sp
+                            && wlen + 1 + strlen(sp) > room
+                            && (!(sp2 = index(sp, ' '))
+                                || wlen + 1 + (sp2 - sp) > room))) {
+                               cur_y++;
+                               cur_x = x;
+                       }
+                       wmove(win, cur_y, cur_x);
+                       waddstr(win, word);
+                       getyx(win, cur_y, cur_x);
+                       cur_x++;
+                       if (sp && *sp == ' ') {
+                               cur_x++;        /* double space */
+                               while (*++sp == ' ') ;
+                               newl = 1;
+                       } else
+                               newl = 0;
+                       word = sp;
+               }
+       }
+}
+
+/*
+ * Print a button
+ */
+void print_button(WINDOW * win, const char *label, int y, int x, int selected)
+{
+       int i, temp;
+
+       wmove(win, y, x);
+       wattrset(win, selected ? dlg.button_active.atr
+                : dlg.button_inactive.atr);
+       waddstr(win, "<");
+       temp = strspn(label, " ");
+       label += temp;
+       wattrset(win, selected ? dlg.button_label_active.atr
+                : dlg.button_label_inactive.atr);
+       for (i = 0; i < temp; i++)
+               waddch(win, ' ');
+       wattrset(win, selected ? dlg.button_key_active.atr
+                : dlg.button_key_inactive.atr);
+       waddch(win, label[0]);
+       wattrset(win, selected ? dlg.button_label_active.atr
+                : dlg.button_label_inactive.atr);
+       waddstr(win, (char *)label + 1);
+       wattrset(win, selected ? dlg.button_active.atr
+                : dlg.button_inactive.atr);
+       waddstr(win, ">");
+       wmove(win, y, x + temp + 1);
+}
+
+/*
+ * Draw a rectangular box with line drawing characters
+ */
+void
+draw_box(WINDOW * win, int y, int x, int height, int width,
+        chtype box, chtype border)
+{
+       int i, j;
+
+       wattrset(win, 0);
+       for (i = 0; i < height; i++) {
+               wmove(win, y + i, x);
+               for (j = 0; j < width; j++)
+                       if (!i && !j)
+                               waddch(win, border | ACS_ULCORNER);
+                       else if (i == height - 1 && !j)
+                               waddch(win, border | ACS_LLCORNER);
+                       else if (!i && j == width - 1)
+                               waddch(win, box | ACS_URCORNER);
+                       else if (i == height - 1 && j == width - 1)
+                               waddch(win, box | ACS_LRCORNER);
+                       else if (!i)
+                               waddch(win, border | ACS_HLINE);
+                       else if (i == height - 1)
+                               waddch(win, box | ACS_HLINE);
+                       else if (!j)
+                               waddch(win, border | ACS_VLINE);
+                       else if (j == width - 1)
+                               waddch(win, box | ACS_VLINE);
+                       else
+                               waddch(win, box | ' ');
+       }
+}
+
+/*
+ * Draw shadows along the right and bottom edge to give a more 3D look
+ * to the boxes
+ */
+void draw_shadow(WINDOW * win, int y, int x, int height, int width)
+{
+       int i;
+
+       if (has_colors()) {     /* Whether terminal supports color? */
+               wattrset(win, dlg.shadow.atr);
+               wmove(win, y + height, x + 2);
+               for (i = 0; i < width; i++)
+                       waddch(win, winch(win) & A_CHARTEXT);
+               for (i = y + 1; i < y + height + 1; i++) {
+                       wmove(win, i, x + width);
+                       waddch(win, winch(win) & A_CHARTEXT);
+                       waddch(win, winch(win) & A_CHARTEXT);
+               }
+               wnoutrefresh(win);
+       }
+}
+
+/*
+ *  Return the position of the first alphabetic character in a string.
+ */
+int first_alpha(const char *string, const char *exempt)
+{
+       int i, in_paren = 0, c;
+
+       for (i = 0; i < strlen(string); i++) {
+               c = tolower(string[i]);
+
+               if (strchr("<[(", c))
+                       ++in_paren;
+               if (strchr(">])", c) && in_paren > 0)
+                       --in_paren;
+
+               if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
+                       return i;
+       }
+
+       return 0;
+}
+
+/*
+ * ncurses uses ESC to detect escaped char sequences. This resutl in
+ * a small timeout before ESC is actually delivered to the application.
+ * lxdialog suggest <ESC> <ESC> which is correctly translated to two
+ * times esc. But then we need to ignore the second esc to avoid stepping
+ * out one menu too much. Filter away all escaped key sequences since
+ * keypad(FALSE) turn off ncurses support for escape sequences - and thats
+ * needed to make notimeout() do as expected.
+ */
+int on_key_esc(WINDOW *win)
+{
+       int key;
+       int key2;
+       int key3;
+
+       nodelay(win, TRUE);
+       keypad(win, FALSE);
+       key = wgetch(win);
+       key2 = wgetch(win);
+       do {
+               key3 = wgetch(win);
+       } while (key3 != ERR);
+       nodelay(win, FALSE);
+       keypad(win, TRUE);
+       if (key == KEY_ESC && key2 == ERR)
+               return KEY_ESC;
+       else if (key != ERR && key != KEY_ESC && key2 == ERR)
+               ungetch(key);
+
+       return -1;
+}
+
+/* redraw screen in new size */
+int on_key_resize(void)
+{
+       dialog_clear();
+       return KEY_RESIZE;
+}
+
+struct dialog_list *item_cur;
+struct dialog_list item_nil;
+struct dialog_list *item_head;
+
+void item_reset(void)
+{
+       struct dialog_list *p, *next;
+
+       for (p = item_head; p; p = next) {
+               next = p->next;
+               free(p);
+       }
+       item_head = NULL;
+       item_cur = &item_nil;
+}
+
+void item_make(const char *fmt, ...)
+{
+       va_list ap;
+       struct dialog_list *p = malloc(sizeof(*p));
+
+       if (item_head)
+               item_cur->next = p;
+       else
+               item_head = p;
+       item_cur = p;
+       memset(p, 0, sizeof(*p));
+
+       va_start(ap, fmt);
+       vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
+       va_end(ap);
+}
+
+void item_add_str(const char *fmt, ...)
+{
+       va_list ap;
+        size_t avail;
+
+       avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
+
+       va_start(ap, fmt);
+       vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
+                 avail, fmt, ap);
+       item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
+       va_end(ap);
+}
+
+void item_set_tag(char tag)
+{
+       item_cur->node.tag = tag;
+}
+void item_set_data(void *ptr)
+{
+       item_cur->node.data = ptr;
+}
+
+void item_set_selected(int val)
+{
+       item_cur->node.selected = val;
+}
+
+int item_activate_selected(void)
+{
+       item_foreach()
+               if (item_is_selected())
+                       return 1;
+       return 0;
+}
+
+void *item_data(void)
+{
+       return item_cur->node.data;
+}
+
+char item_tag(void)
+{
+       return item_cur->node.tag;
+}
+
+int item_count(void)
+{
+       int n = 0;
+       struct dialog_list *p;
+
+       for (p = item_head; p; p = p->next)
+               n++;
+       return n;
+}
+
+void item_set(int n)
+{
+       int i = 0;
+       item_foreach()
+               if (i++ == n)
+                       return;
+}
+
+int item_n(void)
+{
+       int n = 0;
+       struct dialog_list *p;
+
+       for (p = item_head; p; p = p->next) {
+               if (p == item_cur)
+                       return n;
+               n++;
+       }
+       return 0;
+}
+
+const char *item_str(void)
+{
+       return item_cur->node.str;
+}
+
+int item_is_selected(void)
+{
+       return (item_cur->node.selected != 0);
+}
+
+int item_is_tag(char tag)
+{
+       return (item_cur->node.tag == tag);
+}
diff --git a/kconfig/lxdialog/yesno.c b/kconfig/lxdialog/yesno.c
new file mode 100644 (file)
index 0000000..ee0a04e
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *  yesno.c -- implements the yes/no box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+       int x = width / 2 - 10;
+       int y = height - 2;
+
+       print_button(dialog, " Yes ", y, x, selected == 0);
+       print_button(dialog, "  No  ", y, x + 13, selected == 1);
+
+       wmove(dialog, y, x + 1 + 13 * selected);
+       wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with two buttons - Yes and No
+ */
+int dialog_yesno(const char *title, const char *prompt, int height, int width)
+{
+       int i, x, y, key = 0, button = 0;
+       WINDOW *dialog;
+
+do_resize:
+       if (getmaxy(stdscr) < (height + 4))
+               return -ERRDISPLAYTOOSMALL;
+       if (getmaxx(stdscr) < (width + 4))
+               return -ERRDISPLAYTOOSMALL;
+
+       /* center dialog box on screen */
+       x = (COLS - width) / 2;
+       y = (LINES - height) / 2;
+
+       draw_shadow(stdscr, y, x, height, width);
+
+       dialog = newwin(height, width, y, x);
+       keypad(dialog, TRUE);
+
+       draw_box(dialog, 0, 0, height, width,
+                dlg.dialog.atr, dlg.border.atr);
+       wattrset(dialog, dlg.border.atr);
+       mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+       for (i = 0; i < width - 2; i++)
+               waddch(dialog, ACS_HLINE);
+       wattrset(dialog, dlg.dialog.atr);
+       waddch(dialog, ACS_RTEE);
+
+       print_title(dialog, title, width);
+
+       wattrset(dialog, dlg.dialog.atr);
+       print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+       print_buttons(dialog, height, width, 0);
+
+       while (key != KEY_ESC) {
+               key = wgetch(dialog);
+               switch (key) {
+               case 'Y':
+               case 'y':
+                       delwin(dialog);
+                       return 0;
+               case 'N':
+               case 'n':
+                       delwin(dialog);
+                       return 1;
+
+               case TAB:
+               case KEY_LEFT:
+               case KEY_RIGHT:
+                       button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button);
+
+                       print_buttons(dialog, height, width, button);
+                       wrefresh(dialog);
+                       break;
+               case ' ':
+               case '\n':
+                       delwin(dialog);
+                       return button;
+               case KEY_ESC:
+                       key = on_key_esc(dialog);
+                       break;
+               case KEY_RESIZE:
+                       delwin(dialog);
+                       on_key_resize();
+                       goto do_resize;
+               }
+       }
+
+       delwin(dialog);
+       return key;             /* ESC pressed */
+}
diff --git a/kconfig/mconf.c b/kconfig/mconf.c
new file mode 100644 (file)
index 0000000..c2acdc0
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ * Introduced single menu mode (show all sub-menus in one large tree).
+ * 2002-11-06 Petr Baudis <pasky@ucw.cz>
+ *
+ * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <locale.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+#include "lxdialog/dialog.h"
+
+static char menu_backtitle[128];
+static const char mconf_readme[] = N_(
+"Overview\n"
+"--------\n"
+"Some features may be built directly into the project.\n"
+"Some may be made into loadable runtime modules.  Some features\n"
+"may be completely removed altogether.  There are also certain\n"
+"parameters which are not really features, but must be\n"
+"entered in as decimal or hexadecimal numbers or possibly text.\n"
+"\n"
+"Menu items beginning with [*], <M> or [ ] represent features\n"
+"configured to be built in, modularized or removed respectively.\n"
+"Pointed brackets <> represent module capable features.\n"
+"\n"
+"To change any of these features, highlight it with the cursor\n"
+"keys and press <Y> to build it in, <M> to make it a module or\n"
+"<N> to removed it.  You may also press the <Space Bar> to cycle\n"
+"through the available options (ie. Y->N->M->Y).\n"
+"\n"
+"Some additional keyboard hints:\n"
+"\n"
+"Menus\n"
+"----------\n"
+"o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
+"   you wish to change or submenu wish to select and press <Enter>.\n"
+"   Submenus are designated by \"--->\".\n"
+"\n"
+"   Shortcut: Press the option's highlighted letter (hotkey).\n"
+"             Pressing a hotkey more than once will sequence\n"
+"             through all visible items which use that hotkey.\n"
+"\n"
+"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
+"   unseen options into view.\n"
+"\n"
+"o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
+"   and press <ENTER>.\n"
+"\n"
+"   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
+"             using those letters.  You may press a single <ESC>, but\n"
+"             there is a delayed response which you may find annoying.\n"
+"\n"
+"   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
+"   <Exit> and <Help>\n"
+"\n"
+"o  To get help with an item, use the cursor keys to highlight <Help>\n"
+"   and Press <ENTER>.\n"
+"\n"
+"   Shortcut: Press <H> or <?>.\n"
+"\n"
+"\n"
+"Radiolists  (Choice lists)\n"
+"-----------\n"
+"o  Use the cursor keys to select the option you wish to set and press\n"
+"   <S> or the <SPACE BAR>.\n"
+"\n"
+"   Shortcut: Press the first letter of the option you wish to set then\n"
+"             press <S> or <SPACE BAR>.\n"
+"\n"
+"o  To see available help for the item, use the cursor keys to highlight\n"
+"   <Help> and Press <ENTER>.\n"
+"\n"
+"   Shortcut: Press <H> or <?>.\n"
+"\n"
+"   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
+"   <Help>\n"
+"\n"
+"\n"
+"Data Entry\n"
+"-----------\n"
+"o  Enter the requested information and press <ENTER>\n"
+"   If you are entering hexadecimal values, it is not necessary to\n"
+"   add the '0x' prefix to the entry.\n"
+"\n"
+"o  For help, use the <TAB> or cursor keys to highlight the help option\n"
+"   and press <ENTER>.  You can try <TAB><H> as well.\n"
+"\n"
+"\n"
+"Text Box    (Help Window)\n"
+"--------\n"
+"o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
+"   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
+"   who are familiar with less and lynx.\n"
+"\n"
+"o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
+"\n"
+"\n"
+"Alternate Configuration Files\n"
+"-----------------------------\n"
+"Menuconfig supports the use of alternate configuration files for\n"
+"those who, for various reasons, find it necessary to switch\n"
+"between different configurations.\n"
+"\n"
+"At the end of the main menu you will find two options.  One is\n"
+"for saving the current configuration to a file of your choosing.\n"
+"The other option is for loading a previously saved alternate\n"
+"configuration.\n"
+"\n"
+"Even if you don't use alternate configuration files, but you\n"
+"find during a Menuconfig session that you have completely messed\n"
+"up your settings, you may use the \"Load Alternate...\" option to\n"
+"restore your previously saved settings from \".config\" without\n"
+"restarting Menuconfig.\n"
+"\n"
+"Other information\n"
+"-----------------\n"
+"If you use Menuconfig in an XTERM window make sure you have your\n"
+"$TERM variable set to point to a xterm definition which supports color.\n"
+"Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
+"display correctly in a RXVT window because rxvt displays only one\n"
+"intensity of color, bright.\n"
+"\n"
+"Menuconfig will display larger menus on screens or xterms which are\n"
+"set to display more than the standard 25 row by 80 column geometry.\n"
+"In order for this to work, the \"stty size\" command must be able to\n"
+"display the screen's current row and column geometry.  I STRONGLY\n"
+"RECOMMEND that you make sure you do NOT have the shell variables\n"
+"LINES and COLUMNS exported into your environment.  Some distributions\n"
+"export those variables via /etc/profile.  Some ncurses programs can\n"
+"become confused when those variables (LINES & COLUMNS) don't reflect\n"
+"the true screen size.\n"
+"\n"
+"Optional personality available\n"
+"------------------------------\n"
+"If you prefer to have all of the options listed in a single\n"
+"menu, rather than the default multimenu hierarchy, run the menuconfig\n"
+"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
+"\n"
+"make MENUCONFIG_MODE=single_menu menuconfig\n"
+"\n"
+"<Enter> will then unroll the appropriate category, or enfold it if it\n"
+"is already unrolled.\n"
+"\n"
+"Note that this mode can eventually be a little more CPU expensive\n"
+"(especially with a larger number of unrolled categories) than the\n"
+"default mode.\n"
+"\n"
+"Different color themes available\n"
+"--------------------------------\n"
+"It is possible to select different color themes using the variable\n"
+"MENUCONFIG_COLOR. To select a theme use:\n"
+"\n"
+"make MENUCONFIG_COLOR=<theme> menuconfig\n"
+"\n"
+"Available themes are\n"
+" mono       => selects colors suitable for monochrome displays\n"
+" blackbg    => selects a color scheme with black background\n"
+" classic    => theme with blue background. The classic look\n"
+" bluetitle  => a LCD friendly version of classic. (default)\n"
+"\n"),
+menu_instructions[] = N_(
+       "Arrow keys navigate the menu.  "
+       "<Enter> selects submenus --->.  "
+       "Highlighted letters are hotkeys.  "
+       "Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
+       "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
+       "Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
+radiolist_instructions[] = N_(
+       "Use the arrow keys to navigate this window or "
+       "press the hotkey of the item you wish to select "
+       "followed by the <SPACE BAR>. "
+       "Press <?> for additional information about this option."),
+inputbox_instructions_int[] = N_(
+       "Please enter a decimal value. "
+       "Fractions will not be accepted.  "
+       "Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_hex[] = N_(
+       "Please enter a hexadecimal value. "
+       "Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_string[] = N_(
+       "Please enter a string value. "
+       "Use the <TAB> key to move from the input field to the buttons below it."),
+setmod_text[] = N_(
+       "This feature depends on another which has been configured as a module.\n"
+       "As a result, this feature will be built as a module."),
+nohelp_text[] = N_(
+       "There is no help available for this option.\n"),
+load_config_text[] = N_(
+       "Enter the name of the configuration file you wish to load.  "
+       "Accept the name shown to restore the configuration you "
+       "last retrieved.  Leave blank to abort."),
+load_config_help[] = N_(
+       "\n"
+       "For various reasons, one may wish to keep several different\n"
+       "configurations available on a single machine.\n"
+       "\n"
+       "If you have saved a previous configuration in a file other than the\n"
+       "default, entering the name of the file here will allow you\n"
+       "to modify that configuration.\n"
+       "\n"
+       "If you are uncertain, then you have probably never used alternate\n"
+       "configuration files.  You should therefor leave this blank to abort.\n"),
+save_config_text[] = N_(
+       "Enter a filename to which this configuration should be saved "
+       "as an alternate.  Leave blank to abort."),
+save_config_help[] = N_(
+       "\n"
+       "For various reasons, one may wish to keep different\n"
+       "configurations available on a single machine.\n"
+       "\n"
+       "Entering a file name here will allow you to later retrieve, modify\n"
+       "and use the current configuration as an alternate to whatever\n"
+       "configuration options you have selected at that time.\n"
+       "\n"
+       "If you are uncertain what all this means then you should probably\n"
+       "leave this blank.\n"),
+search_help[] = N_(
+       "\n"
+       "Search for CONFIG_ symbols and display their relations.\n"
+       "Regular expressions are allowed.\n"
+       "Example: search for \"^FOO\"\n"
+       "Result:\n"
+       "-----------------------------------------------------------------\n"
+       "Symbol: FOO [=m]\n"
+       "Prompt: Foo bus is used to drive the bar HW\n"
+       "Defined at drivers/pci/Kconfig:47\n"
+       "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
+       "Location:\n"
+       "  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
+       "    -> PCI support (PCI [=y])\n"
+       "      -> PCI access mode (<choice> [=y])\n"
+       "Selects: LIBCRC32\n"
+       "Selected by: BAR\n"
+       "-----------------------------------------------------------------\n"
+       "o The line 'Prompt:' shows the text used in the menu structure for\n"
+       "  this CONFIG_ symbol\n"
+       "o The 'Defined at' line tell at what file / line number the symbol\n"
+       "  is defined\n"
+       "o The 'Depends on:' line tell what symbols needs to be defined for\n"
+       "  this symbol to be visible in the menu (selectable)\n"
+       "o The 'Location:' lines tell where in the menu structure this symbol\n"
+       "  is located\n"
+       "    A location followed by a [=y] indicate that this is a selectable\n"
+       "    menu item - and current value is displayed inside brackets.\n"
+       "o The 'Selects:' line tell what symbol will be automatically\n"
+       "  selected if this symbol is selected (y or m)\n"
+       "o The 'Selected by' line tell what symbol has selected this symbol\n"
+       "\n"
+       "Only relevant lines are shown.\n"
+       "\n\n"
+       "Search examples:\n"
+       "Examples: USB  => find all CONFIG_ symbols containing USB\n"
+       "          ^USB => find all CONFIG_ symbols starting with USB\n"
+       "          USB$ => find all CONFIG_ symbols ending with USB\n"
+       "\n");
+
+static char filename[PATH_MAX+1] = ".config";
+static int indent;
+static struct termios ios_org;
+static int rows = 0, cols = 0;
+static struct menu *current_menu;
+static int child_count;
+static int single_menu_mode;
+
+static void conf(struct menu *menu);
+static void conf_choice(struct menu *menu);
+static void conf_string(struct menu *menu);
+static void conf_load(void);
+static void conf_save(void);
+static void show_textbox(const char *title, const char *text, int r, int c);
+static void show_helptext(const char *title, const char *text);
+static void show_help(struct menu *menu);
+
+static void init_wsize(void)
+{
+       struct winsize ws;
+       char *env;
+
+       if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
+               rows = ws.ws_row;
+               cols = ws.ws_col;
+       }
+
+       if (!rows) {
+               env = getenv("LINES");
+               if (env)
+                       rows = atoi(env);
+               if (!rows)
+                       rows = 24;
+       }
+       if (!cols) {
+               env = getenv("COLUMNS");
+               if (env)
+                       cols = atoi(env);
+               if (!cols)
+                       cols = 80;
+       }
+
+       if (rows < 19 || cols < 80) {
+               fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
+               fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
+               exit(1);
+       }
+
+       rows -= 4;
+       cols -= 5;
+}
+
+static void get_prompt_str(struct gstr *r, struct property *prop)
+{
+       int i, j;
+       struct menu *submenu[8], *menu;
+
+       str_printf(r, "Prompt: %s\n", prop->text);
+       str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
+               prop->menu->lineno);
+       if (!expr_is_yes(prop->visible.expr)) {
+               str_append(r, "  Depends on: ");
+               expr_gstr_print(prop->visible.expr, r);
+               str_append(r, "\n");
+       }
+       menu = prop->menu->parent;
+       for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
+               submenu[i++] = menu;
+       if (i > 0) {
+               str_printf(r, "  Location:\n");
+               for (j = 4; --i >= 0; j += 2) {
+                       menu = submenu[i];
+                       str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
+                       if (menu->sym) {
+                               str_printf(r, " (%s [=%s])", menu->sym->name ?
+                                       menu->sym->name : "<choice>",
+                                       sym_get_string_value(menu->sym));
+                       }
+                       str_append(r, "\n");
+               }
+       }
+}
+
+static void get_symbol_str(struct gstr *r, struct symbol *sym)
+{
+       bool hit;
+       struct property *prop;
+
+       str_printf(r, "Symbol: %s [=%s]\n", sym->name,
+                                      sym_get_string_value(sym));
+       for_all_prompts(sym, prop)
+               get_prompt_str(r, prop);
+       hit = false;
+       for_all_properties(sym, prop, P_SELECT) {
+               if (!hit) {
+                       str_append(r, "  Selects: ");
+                       hit = true;
+               } else
+                       str_printf(r, " && ");
+               expr_gstr_print(prop->expr, r);
+       }
+       if (hit)
+               str_append(r, "\n");
+       if (sym->rev_dep.expr) {
+               str_append(r, "  Selected by: ");
+               expr_gstr_print(sym->rev_dep.expr, r);
+               str_append(r, "\n");
+       }
+       str_append(r, "\n\n");
+}
+
+static struct gstr get_relations_str(struct symbol **sym_arr)
+{
+       struct symbol *sym;
+       struct gstr res = str_new();
+       int i;
+
+       for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
+               get_symbol_str(&res, sym);
+       if (!i)
+               str_append(&res, "No matches found.\n");
+       return res;
+}
+
+static void search_conf(void)
+{
+       struct symbol **sym_arr;
+       struct gstr res;
+       int dres;
+again:
+       dialog_clear();
+       dres = dialog_inputbox(_("Search Configuration Parameter"),
+                             _("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"),
+                             10, 75, "");
+       switch (dres) {
+       case 0:
+               break;
+       case 1:
+               show_helptext(_("Search Configuration"), search_help);
+               goto again;
+       default:
+               return;
+       }
+
+       sym_arr = sym_re_search(dialog_input_result);
+       res = get_relations_str(sym_arr);
+       free(sym_arr);
+       show_textbox(_("Search Results"), str_get(&res), 0, 0);
+       str_free(&res);
+}
+
+static void build_conf(struct menu *menu)
+{
+       struct symbol *sym;
+       struct property *prop;
+       struct menu *child;
+       int type, tmp, doint = 2;
+       tristate val;
+       char ch;
+
+       if (!menu_is_visible(menu))
+               return;
+
+       sym = menu->sym;
+       prop = menu->prompt;
+       if (!sym) {
+               if (prop && menu != current_menu) {
+                       const char *prompt = menu_get_prompt(menu);
+                       switch (prop->type) {
+                       case P_MENU:
+                               child_count++;
+                               if (single_menu_mode) {
+                                       item_make("%s%*c%s",
+                                                 menu->data ? "-->" : "++>",
+                                                 indent + 1, ' ', prompt);
+                               } else
+                                       item_make("   %*c%s  --->", indent + 1, ' ', prompt);
+
+                               item_set_tag('m');
+                               item_set_data(menu);
+                               if (single_menu_mode && menu->data)
+                                       goto conf_childs;
+                               return;
+                       default:
+                               if (prompt) {
+                                       child_count++;
+                                       item_make("---%*c%s", indent + 1, ' ', prompt);
+                                       item_set_tag(':');
+                                       item_set_data(menu);
+                               }
+                       }
+               } else
+                       doint = 0;
+               goto conf_childs;
+       }
+
+       type = sym_get_type(sym);
+       if (sym_is_choice(sym)) {
+               struct symbol *def_sym = sym_get_choice_value(sym);
+               struct menu *def_menu = NULL;
+
+               child_count++;
+               for (child = menu->list; child; child = child->next) {
+                       if (menu_is_visible(child) && child->sym == def_sym)
+                               def_menu = child;
+               }
+
+               val = sym_get_tristate_value(sym);
+               if (sym_is_changable(sym)) {
+                       switch (type) {
+                       case S_BOOLEAN:
+                               item_make("[%c]", val == no ? ' ' : '*');
+                               break;
+                       case S_TRISTATE:
+                               switch (val) {
+                               case yes: ch = '*'; break;
+                               case mod: ch = 'M'; break;
+                               default:  ch = ' '; break;
+                               }
+                               item_make("<%c>", ch);
+                               break;
+                       }
+                       item_set_tag('t');
+                       item_set_data(menu);
+               } else {
+                       item_make("   ");
+                       item_set_tag(def_menu ? 't' : ':');
+                       item_set_data(menu);
+               }
+
+               item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+               if (val == yes) {
+                       if (def_menu) {
+                               item_add_str(" (%s)", menu_get_prompt(def_menu));
+                               item_add_str("  --->");
+                               if (def_menu->list) {
+                                       indent += 2;
+                                       build_conf(def_menu);
+                                       indent -= 2;
+                               }
+                       }
+                       return;
+               }
+       } else {
+               if (menu == current_menu) {
+                       item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+                       item_set_tag(':');
+                       item_set_data(menu);
+                       goto conf_childs;
+               }
+               child_count++;
+               val = sym_get_tristate_value(sym);
+               if (sym_is_choice_value(sym) && val == yes) {
+                       item_make("   ");
+                       item_set_tag(':');
+                       item_set_data(menu);
+               } else {
+                       switch (type) {
+                       case S_BOOLEAN:
+                               if (sym_is_changable(sym))
+                                       item_make("[%c]", val == no ? ' ' : '*');
+                               else
+                                       item_make("---");
+                               item_set_tag('t');
+                               item_set_data(menu);
+                               break;
+                       case S_TRISTATE:
+                               switch (val) {
+                               case yes: ch = '*'; break;
+                               case mod: ch = 'M'; break;
+                               default:  ch = ' '; break;
+                               }
+                               if (sym_is_changable(sym))
+                                       item_make("<%c>", ch);
+                               else
+                                       item_make("---");
+                               item_set_tag('t');
+                               item_set_data(menu);
+                               break;
+                       default:
+                               tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
+                               item_make("(%s)", sym_get_string_value(sym));
+                               tmp = indent - tmp + 4;
+                               if (tmp < 0)
+                                       tmp = 0;
+                               item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
+                                            (sym_has_value(sym) || !sym_is_changable(sym)) ?
+                                            "" : " (NEW)");
+                               item_set_tag('s');
+                               item_set_data(menu);
+                               goto conf_childs;
+                       }
+               }
+               item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
+                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
+                         "" : " (NEW)");
+               if (menu->prompt->type == P_MENU) {
+                       item_add_str("  --->");
+                       return;
+               }
+       }
+
+conf_childs:
+       indent += doint;
+       for (child = menu->list; child; child = child->next)
+               build_conf(child);
+       indent -= doint;
+}
+
+static void conf(struct menu *menu)
+{
+       struct menu *submenu;
+       const char *prompt = menu_get_prompt(menu);
+       struct symbol *sym;
+       struct menu *active_menu = NULL;
+       int res;
+       int s_scroll = 0;
+
+       while (1) {
+               item_reset();
+               current_menu = menu;
+               build_conf(menu);
+               if (!child_count)
+                       break;
+               if (menu == &rootmenu) {
+                       item_make("--- ");
+                       item_set_tag(':');
+                       item_make(_("    Load an Alternate Configuration File"));
+                       item_set_tag('L');
+                       item_make(_("    Save an Alternate Configuration File"));
+                       item_set_tag('S');
+               }
+               dialog_clear();
+               res = dialog_menu(prompt ? prompt : _("Main Menu"),
+                                 _(menu_instructions),
+                                 active_menu, &s_scroll);
+               if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
+                       break;
+               if (!item_activate_selected())
+                       continue;
+               if (!item_tag())
+                       continue;
+
+               submenu = item_data();
+               active_menu = item_data();
+               if (submenu)
+                       sym = submenu->sym;
+               else
+                       sym = NULL;
+
+               switch (res) {
+               case 0:
+                       switch (item_tag()) {
+                       case 'm':
+                               if (single_menu_mode)
+                                       submenu->data = (void *) (long) !submenu->data;
+                               else
+                                       conf(submenu);
+                               break;
+                       case 't':
+                               if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
+                                       conf_choice(submenu);
+                               else if (submenu->prompt->type == P_MENU)
+                                       conf(submenu);
+                               break;
+                       case 's':
+                               conf_string(submenu);
+                               break;
+                       case 'L':
+                               conf_load();
+                               break;
+                       case 'S':
+                               conf_save();
+                               break;
+                       }
+                       break;
+               case 2:
+                       if (sym)
+                               show_help(submenu);
+                       else
+                               show_helptext("README", _(mconf_readme));
+                       break;
+               case 3:
+                       if (item_is_tag('t')) {
+                               if (sym_set_tristate_value(sym, yes))
+                                       break;
+                               if (sym_set_tristate_value(sym, mod))
+                                       show_textbox(NULL, setmod_text, 6, 74);
+                       }
+                       break;
+               case 4:
+                       if (item_is_tag('t'))
+                               sym_set_tristate_value(sym, no);
+                       break;
+               case 5:
+                       if (item_is_tag('t'))
+                               sym_set_tristate_value(sym, mod);
+                       break;
+               case 6:
+                       if (item_is_tag('t'))
+                               sym_toggle_tristate_value(sym);
+                       else if (item_is_tag('m'))
+                               conf(submenu);
+                       break;
+               case 7:
+                       search_conf();
+                       break;
+               }
+       }
+}
+
+static void show_textbox(const char *title, const char *text, int r, int c)
+{
+       dialog_clear();
+       dialog_textbox(title, text, r, c);
+}
+
+static void show_helptext(const char *title, const char *text)
+{
+       show_textbox(title, text, 0, 0);
+}
+
+static void show_help(struct menu *menu)
+{
+       struct gstr help = str_new();
+       struct symbol *sym = menu->sym;
+
+       if (sym->help)
+       {
+               if (sym->name) {
+                       str_printf(&help, "CONFIG_%s:\n\n", sym->name);
+                       str_append(&help, _(sym->help));
+                       str_append(&help, "\n");
+               }
+       } else {
+               str_append(&help, nohelp_text);
+       }
+       get_symbol_str(&help, sym);
+       show_helptext(menu_get_prompt(menu), str_get(&help));
+       str_free(&help);
+}
+
+static void conf_choice(struct menu *menu)
+{
+       const char *prompt = menu_get_prompt(menu);
+       struct menu *child;
+       struct symbol *active;
+
+       active = sym_get_choice_value(menu->sym);
+       while (1) {
+               int res;
+               int selected;
+               item_reset();
+
+               current_menu = menu;
+               for (child = menu->list; child; child = child->next) {
+                       if (!menu_is_visible(child))
+                               continue;
+                       item_make("%s", menu_get_prompt(child));
+                       item_set_data(child);
+                       if (child->sym == active)
+                               item_set_selected(1);
+                       if (child->sym == sym_get_choice_value(menu->sym))
+                               item_set_tag('X');
+               }
+               dialog_clear();
+               res = dialog_checklist(prompt ? prompt : _("Main Menu"),
+                                       _(radiolist_instructions),
+                                        15, 70, 6);
+               selected = item_activate_selected();
+               switch (res) {
+               case 0:
+                       if (selected) {
+                               child = item_data();
+                               sym_set_tristate_value(child->sym, yes);
+                       }
+                       return;
+               case 1:
+                       if (selected) {
+                               child = item_data();
+                               show_help(child);
+                               active = child->sym;
+                       } else
+                               show_help(menu);
+                       break;
+               case KEY_ESC:
+                       return;
+               case -ERRDISPLAYTOOSMALL:
+                       return;
+               }
+       }
+}
+
+static void conf_string(struct menu *menu)
+{
+       const char *prompt = menu_get_prompt(menu);
+
+       while (1) {
+               int res;
+               char *heading;
+
+               switch (sym_get_type(menu->sym)) {
+               case S_INT:
+                       heading = (char *)_(inputbox_instructions_int);
+                       break;
+               case S_HEX:
+                       heading = (char *)_(inputbox_instructions_hex);
+                       break;
+               case S_STRING:
+                       heading = (char *)_(inputbox_instructions_string);
+                       break;
+               default:
+                       heading = "Internal mconf error!";
+               }
+               dialog_clear();
+               res = dialog_inputbox(prompt ? prompt : _("Main Menu"),
+                                     heading, 10, 75,
+                                     sym_get_string_value(menu->sym));
+               switch (res) {
+               case 0:
+                       if (sym_set_string_value(menu->sym, dialog_input_result))
+                               return;
+                       show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
+                       break;
+               case 1:
+                       show_help(menu);
+                       break;
+               case KEY_ESC:
+                       return;
+               }
+       }
+}
+
+static void conf_load(void)
+{
+
+       while (1) {
+               int res;
+               dialog_clear();
+               res = dialog_inputbox(NULL, load_config_text,
+                                     11, 55, filename);
+               switch(res) {
+               case 0:
+                       if (!dialog_input_result[0])
+                               return;
+                       if (!conf_read(dialog_input_result))
+                               return;
+                       show_textbox(NULL, _("File does not exist!"), 5, 38);
+                       break;
+               case 1:
+                       show_helptext(_("Load Alternate Configuration"), load_config_help);
+                       break;
+               case KEY_ESC:
+                       return;
+               }
+       }
+}
+
+static void conf_save(void)
+{
+       while (1) {
+               int res;
+               dialog_clear();
+               res = dialog_inputbox(NULL, save_config_text,
+                                     11, 55, filename);
+               switch(res) {
+               case 0:
+                       if (!dialog_input_result[0])
+                               return;
+                       if (!conf_write(dialog_input_result))
+                               return;
+                       show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
+                       break;
+               case 1:
+                       show_helptext(_("Save Alternate Configuration"), save_config_help);
+                       break;
+               case KEY_ESC:
+                       return;
+               }
+       }
+}
+
+static void conf_cleanup(void)
+{
+       tcsetattr(1, TCSAFLUSH, &ios_org);
+}
+
+int main(int ac, char **av)
+{
+       struct symbol *sym;
+       char *mode;
+       int res;
+
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+
+       conf_parse(av[1] ? av[1] : "");
+       conf_read(NULL);
+
+       sym = sym_lookup("KERNELVERSION", 0);
+       sym_calc_value(sym);
+       sprintf(menu_backtitle, _(PROJECT_NAME" v%s Configuration"),
+               sym_get_string_value(sym));
+
+       mode = getenv("MENUCONFIG_MODE");
+       if (mode) {
+               if (!strcasecmp(mode, "single_menu"))
+                       single_menu_mode = 1;
+       }
+
+       tcgetattr(1, &ios_org);
+       atexit(conf_cleanup);
+       init_wsize();
+       reset_dialog();
+       init_dialog(menu_backtitle);
+       do {
+               conf(&rootmenu);
+               dialog_clear();
+               res = dialog_yesno(NULL,
+                                  _("Do you wish to save your "
+                                    "new "PROJECT_NAME" configuration?\n"
+                                    "<ESC><ESC> to continue."),
+                                  6, 60);
+       } while (res == KEY_ESC);
+       end_dialog();
+       if (res == 0) {
+               if (conf_write(NULL)) {
+                       fprintf(stderr, _("\n\n"
+                               "Error writing "PROJECT_NAME" configuration.\n"
+                               "Your configuration changes were NOT saved."
+                               "\n\n"));
+                       return 1;
+               }
+               printf(_("\n\n"
+                       "*** End of "PROJECT_NAME" configuration.\n"
+                       "*** Execute 'make' to build, or try 'make help'."
+                       "\n\n"));
+       } else {
+               fprintf(stderr, _("\n\n"
+                       "Your configuration changes were NOT saved."
+                       "\n\n"));
+       }
+
+       return 0;
+}
diff --git a/kconfig/menu.c b/kconfig/menu.c
new file mode 100644 (file)
index 0000000..c86c27f
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct menu rootmenu;
+static struct menu **last_entry_ptr;
+
+struct file *file_list;
+struct file *current_file;
+
+static void menu_warn(struct menu *menu, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+}
+
+static void prop_warn(struct property *prop, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+}
+
+void menu_init(void)
+{
+       current_entry = current_menu = &rootmenu;
+       last_entry_ptr = &rootmenu.list;
+}
+
+void menu_add_entry(struct symbol *sym)
+{
+       struct menu *menu;
+
+       menu = malloc(sizeof(*menu));
+       memset(menu, 0, sizeof(*menu));
+       menu->sym = sym;
+       menu->parent = current_menu;
+       menu->file = current_file;
+       menu->lineno = zconf_lineno();
+
+       *last_entry_ptr = menu;
+       last_entry_ptr = &menu->next;
+       current_entry = menu;
+}
+
+void menu_end_entry(void)
+{
+}
+
+struct menu *menu_add_menu(void)
+{
+       menu_end_entry();
+       last_entry_ptr = &current_entry->list;
+       return current_menu = current_entry;
+}
+
+void menu_end_menu(void)
+{
+       last_entry_ptr = &current_menu->next;
+       current_menu = current_menu->parent;
+}
+
+struct expr *menu_check_dep(struct expr *e)
+{
+       if (!e)
+               return e;
+
+       switch (e->type) {
+       case E_NOT:
+               e->left.expr = menu_check_dep(e->left.expr);
+               break;
+       case E_OR:
+       case E_AND:
+               e->left.expr = menu_check_dep(e->left.expr);
+               e->right.expr = menu_check_dep(e->right.expr);
+               break;
+       case E_SYMBOL:
+               /* change 'm' into 'm' && MODULES */
+               if (e->left.sym == &symbol_mod)
+                       return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
+               break;
+       default:
+               break;
+       }
+       return e;
+}
+
+void menu_add_dep(struct expr *dep)
+{
+       current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
+}
+
+void menu_set_type(int type)
+{
+       struct symbol *sym = current_entry->sym;
+
+       if (sym->type == type)
+               return;
+       if (sym->type == S_UNKNOWN) {
+               sym->type = type;
+               return;
+       }
+       menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'",
+           sym->name ? sym->name : "<choice>",
+           sym_type_name(sym->type), sym_type_name(type));
+}
+
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
+{
+       struct property *prop = prop_alloc(type, current_entry->sym);
+
+       prop->menu = current_entry;
+       prop->expr = expr;
+       prop->visible.expr = menu_check_dep(dep);
+
+       if (prompt) {
+               if (isspace(*prompt)) {
+                       prop_warn(prop, "leading whitespace ignored");
+                       while (isspace(*prompt))
+                               prompt++;
+               }
+               if (current_entry->prompt)
+                       prop_warn(prop, "prompt redefined");
+               current_entry->prompt = prop;
+       }
+       prop->text = prompt;
+
+       return prop;
+}
+
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
+{
+       return menu_add_prop(type, prompt, NULL, dep);
+}
+
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
+{
+       menu_add_prop(type, NULL, expr, dep);
+}
+
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
+{
+       menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
+}
+
+void menu_add_option(int token, char *arg)
+{
+       struct property *prop;
+
+       switch (token) {
+       case T_OPT_MODULES:
+               prop = prop_alloc(P_DEFAULT, modules_sym);
+               prop->expr = expr_alloc_symbol(current_entry->sym);
+               break;
+       case T_OPT_DEFCONFIG_LIST:
+               if (!sym_defconfig_list)
+                       sym_defconfig_list = current_entry->sym;
+               else if (sym_defconfig_list != current_entry->sym)
+                       zconf_error("trying to redefine defconfig symbol");
+               break;
+       }
+}
+
+static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2)
+{
+       return sym2->type == S_INT || sym2->type == S_HEX ||
+              (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+}
+
+void sym_check_prop(struct symbol *sym)
+{
+       struct property *prop;
+       struct symbol *sym2;
+       for (prop = sym->prop; prop; prop = prop->next) {
+               switch (prop->type) {
+               case P_DEFAULT:
+                       if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
+                           prop->expr->type != E_SYMBOL)
+                               prop_warn(prop,
+                                   "default for config symbol '%'"
+                                   " must be a single symbol", sym->name);
+                       break;
+               case P_SELECT:
+                       sym2 = prop_get_symbol(prop);
+                       if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
+                               prop_warn(prop,
+                                   "config symbol '%s' uses select, but is "
+                                   "not boolean or tristate", sym->name);
+                       else if (sym2->type == S_UNKNOWN)
+                               prop_warn(prop,
+                                   "'select' used by config symbol '%s' "
+                                   "refer to undefined symbol '%s'",
+                                   sym->name, sym2->name);
+                       else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
+                               prop_warn(prop,
+                                   "'%s' has wrong type. 'select' only "
+                                   "accept arguments of boolean and "
+                                   "tristate type", sym2->name);
+                       break;
+               case P_RANGE:
+                       if (sym->type != S_INT && sym->type != S_HEX)
+                               prop_warn(prop, "range is only allowed "
+                                               "for int or hex symbols");
+                       if (!menu_range_valid_sym(sym, prop->expr->left.sym) ||
+                           !menu_range_valid_sym(sym, prop->expr->right.sym))
+                               prop_warn(prop, "range is invalid");
+                       break;
+               default:
+                       ;
+               }
+       }
+}
+
+void menu_finalize(struct menu *parent)
+{
+       struct menu *menu, *last_menu;
+       struct symbol *sym;
+       struct property *prop;
+       struct expr *parentdep, *basedep, *dep, *dep2, **ep;
+
+       sym = parent->sym;
+       if (parent->list) {
+               if (sym && sym_is_choice(sym)) {
+                       /* find the first choice value and find out choice type */
+                       for (menu = parent->list; menu; menu = menu->next) {
+                               if (menu->sym) {
+                                       current_entry = parent;
+                                       menu_set_type(menu->sym->type);
+                                       current_entry = menu;
+                                       menu_set_type(sym->type);
+                                       break;
+                               }
+                       }
+                       parentdep = expr_alloc_symbol(sym);
+               } else if (parent->prompt)
+                       parentdep = parent->prompt->visible.expr;
+               else
+                       parentdep = parent->dep;
+
+               for (menu = parent->list; menu; menu = menu->next) {
+                       basedep = expr_transform(menu->dep);
+                       basedep = expr_alloc_and(expr_copy(parentdep), basedep);
+                       basedep = expr_eliminate_dups(basedep);
+                       menu->dep = basedep;
+                       if (menu->sym)
+                               prop = menu->sym->prop;
+                       else
+                               prop = menu->prompt;
+                       for (; prop; prop = prop->next) {
+                               if (prop->menu != menu)
+                                       continue;
+                               dep = expr_transform(prop->visible.expr);
+                               dep = expr_alloc_and(expr_copy(basedep), dep);
+                               dep = expr_eliminate_dups(dep);
+                               if (menu->sym && menu->sym->type != S_TRISTATE)
+                                       dep = expr_trans_bool(dep);
+                               prop->visible.expr = dep;
+                               if (prop->type == P_SELECT) {
+                                       struct symbol *es = prop_get_symbol(prop);
+                                       es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+                                                       expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+                               }
+                       }
+               }
+               for (menu = parent->list; menu; menu = menu->next)
+                       menu_finalize(menu);
+       } else if (sym) {
+               basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
+               basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
+               basedep = expr_eliminate_dups(expr_transform(basedep));
+               last_menu = NULL;
+               for (menu = parent->next; menu; menu = menu->next) {
+                       dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
+                       if (!expr_contains_symbol(dep, sym))
+                               break;
+                       if (expr_depends_symbol(dep, sym))
+                               goto next;
+                       dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
+                       dep = expr_eliminate_dups(expr_transform(dep));
+                       dep2 = expr_copy(basedep);
+                       expr_eliminate_eq(&dep, &dep2);
+                       expr_free(dep);
+                       if (!expr_is_yes(dep2)) {
+                               expr_free(dep2);
+                               break;
+                       }
+                       expr_free(dep2);
+               next:
+                       menu_finalize(menu);
+                       menu->parent = parent;
+                       last_menu = menu;
+               }
+               if (last_menu) {
+                       parent->list = parent->next;
+                       parent->next = last_menu->next;
+                       last_menu->next = NULL;
+               }
+       }
+       for (menu = parent->list; menu; menu = menu->next) {
+               if (sym && sym_is_choice(sym) && menu->sym) {
+                       menu->sym->flags |= SYMBOL_CHOICEVAL;
+                       if (!menu->prompt)
+                               menu_warn(menu, "choice value must have a prompt");
+                       for (prop = menu->sym->prop; prop; prop = prop->next) {
+                               if (prop->type == P_PROMPT && prop->menu != menu) {
+                                       prop_warn(prop, "choice values "
+                                           "currently only support a "
+                                           "single prompt");
+                               }
+                               if (prop->type == P_DEFAULT)
+                                       prop_warn(prop, "defaults for choice "
+                                           "values not supported");
+                       }
+                       current_entry = menu;
+                       menu_set_type(sym->type);
+                       menu_add_symbol(P_CHOICE, sym, NULL);
+                       prop = sym_get_choice_prop(sym);
+                       for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
+                               ;
+                       *ep = expr_alloc_one(E_CHOICE, NULL);
+                       (*ep)->right.sym = menu->sym;
+               }
+               if (menu->list && (!menu->prompt || !menu->prompt->text)) {
+                       for (last_menu = menu->list; ; last_menu = last_menu->next) {
+                               last_menu->parent = parent;
+                               if (!last_menu->next)
+                                       break;
+                       }
+                       last_menu->next = menu->next;
+                       menu->next = menu->list;
+                       menu->list = NULL;
+               }
+       }
+
+       if (sym && !(sym->flags & SYMBOL_WARNED)) {
+               if (sym->type == S_UNKNOWN)
+                       menu_warn(parent, "config symbol defined without type");
+
+               if (sym_is_choice(sym) && !parent->prompt)
+                       menu_warn(parent, "choice must have a prompt");
+
+               /* Check properties connected to this symbol */
+               sym_check_prop(sym);
+               sym->flags |= SYMBOL_WARNED;
+       }
+
+       if (sym && !sym_is_optional(sym) && parent->prompt) {
+               sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+                               expr_alloc_and(parent->prompt->visible.expr,
+                                       expr_alloc_symbol(&symbol_mod)));
+       }
+}
+
+bool menu_is_visible(struct menu *menu)
+{
+       struct menu *child;
+       struct symbol *sym;
+       tristate visible;
+
+       if (!menu->prompt)
+               return false;
+       sym = menu->sym;
+       if (sym) {
+               sym_calc_value(sym);
+               visible = menu->prompt->visible.tri;
+       } else
+               visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
+
+       if (visible != no)
+               return true;
+       if (!sym || sym_get_tristate_value(menu->sym) == no)
+               return false;
+
+       for (child = menu->list; child; child = child->next)
+               if (menu_is_visible(child))
+                       return true;
+       return false;
+}
+
+const char *menu_get_prompt(struct menu *menu)
+{
+       if (menu->prompt)
+               return _(menu->prompt->text);
+       else if (menu->sym)
+               return _(menu->sym->name);
+       return NULL;
+}
+
+struct menu *menu_get_root_menu(struct menu *menu)
+{
+       return &rootmenu;
+}
+
+struct menu *menu_get_parent_menu(struct menu *menu)
+{
+       enum prop_type type;
+
+       for (; menu != &rootmenu; menu = menu->parent) {
+               type = menu->prompt ? menu->prompt->type : 0;
+               if (type == P_MENU)
+                       break;
+       }
+       return menu;
+}
+
diff --git a/kconfig/symbol.c b/kconfig/symbol.c
new file mode 100644 (file)
index 0000000..ee225ce
--- /dev/null
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <sys/utsname.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+       .name = "y",
+       .curr = { "y", yes },
+       .flags = SYMBOL_CONST|SYMBOL_VALID,
+}, symbol_mod = {
+       .name = "m",
+       .curr = { "m", mod },
+       .flags = SYMBOL_CONST|SYMBOL_VALID,
+}, symbol_no = {
+       .name = "n",
+       .curr = { "n", no },
+       .flags = SYMBOL_CONST|SYMBOL_VALID,
+}, symbol_empty = {
+       .name = "",
+       .curr = { "", no },
+       .flags = SYMBOL_VALID,
+};
+
+int sym_change_count;
+struct symbol *sym_defconfig_list;
+struct symbol *modules_sym;
+tristate modules_val;
+
+void sym_add_default(struct symbol *sym, const char *def)
+{
+       struct property *prop = prop_alloc(P_DEFAULT, sym);
+
+       prop->expr = expr_alloc_symbol(sym_lookup(def, 1));
+}
+
+void sym_init(void)
+{
+       struct symbol *sym;
+       struct utsname uts;
+       char *p;
+       static bool inited = false;
+
+       if (inited)
+               return;
+       inited = true;
+
+       uname(&uts);
+
+       sym = sym_lookup("ARCH", 0);
+       sym->type = S_STRING;
+       sym->flags |= SYMBOL_AUTO;
+       p = getenv("ARCH");
+       if (p)
+               sym_add_default(sym, p);
+
+       sym = sym_lookup("KERNELVERSION", 0);
+       sym->type = S_STRING;
+       sym->flags |= SYMBOL_AUTO;
+       p = getenv("KERNELVERSION");
+       if (p)
+               sym_add_default(sym, p);
+
+       sym = sym_lookup("UNAME_RELEASE", 0);
+       sym->type = S_STRING;
+       sym->flags |= SYMBOL_AUTO;
+       sym_add_default(sym, uts.release);
+}
+
+enum symbol_type sym_get_type(struct symbol *sym)
+{
+       enum symbol_type type = sym->type;
+
+       if (type == S_TRISTATE) {
+               if (sym_is_choice_value(sym) && sym->visible == yes)
+                       type = S_BOOLEAN;
+               else if (modules_val == no)
+                       type = S_BOOLEAN;
+       }
+       return type;
+}
+
+const char *sym_type_name(enum symbol_type type)
+{
+       switch (type) {
+       case S_BOOLEAN:
+               return "boolean";
+       case S_TRISTATE:
+               return "tristate";
+       case S_INT:
+               return "integer";
+       case S_HEX:
+               return "hex";
+       case S_STRING:
+               return "string";
+       case S_UNKNOWN:
+               return "unknown";
+       case S_OTHER:
+               break;
+       }
+       return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+       struct property *prop;
+
+       for_all_choices(sym, prop)
+               return prop;
+       return NULL;
+}
+
+struct property *sym_get_default_prop(struct symbol *sym)
+{
+       struct property *prop;
+
+       for_all_defaults(sym, prop) {
+               prop->visible.tri = expr_calc_value(prop->visible.expr);
+               if (prop->visible.tri != no)
+                       return prop;
+       }
+       return NULL;
+}
+
+struct property *sym_get_range_prop(struct symbol *sym)
+{
+       struct property *prop;
+
+       for_all_properties(sym, prop, P_RANGE) {
+               prop->visible.tri = expr_calc_value(prop->visible.expr);
+               if (prop->visible.tri != no)
+                       return prop;
+       }
+       return NULL;
+}
+
+static int sym_get_range_val(struct symbol *sym, int base)
+{
+       sym_calc_value(sym);
+       switch (sym->type) {
+       case S_INT:
+               base = 10;
+               break;
+       case S_HEX:
+               base = 16;
+               break;
+       default:
+               break;
+       }
+       return strtol(sym->curr.val, NULL, base);
+}
+
+static void sym_validate_range(struct symbol *sym)
+{
+       struct property *prop;
+       int base, val, val2;
+       char str[64];
+
+       switch (sym->type) {
+       case S_INT:
+               base = 10;
+               break;
+       case S_HEX:
+               base = 16;
+               break;
+       default:
+               return;
+       }
+       prop = sym_get_range_prop(sym);
+       if (!prop)
+               return;
+       val = strtol(sym->curr.val, NULL, base);
+       val2 = sym_get_range_val(prop->expr->left.sym, base);
+       if (val >= val2) {
+               val2 = sym_get_range_val(prop->expr->right.sym, base);
+               if (val <= val2)
+                       return;
+       }
+       if (sym->type == S_INT)
+               sprintf(str, "%d", val2);
+       else
+               sprintf(str, "0x%x", val2);
+       sym->curr.val = strdup(str);
+}
+
+static void sym_calc_visibility(struct symbol *sym)
+{
+       struct property *prop;
+       tristate tri;
+
+       /* any prompt visible? */
+       tri = no;
+       for_all_prompts(sym, prop) {
+               prop->visible.tri = expr_calc_value(prop->visible.expr);
+               tri = E_OR(tri, prop->visible.tri);
+       }
+       if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
+               tri = yes;
+       if (sym->visible != tri) {
+               sym->visible = tri;
+               sym_set_changed(sym);
+       }
+       if (sym_is_choice_value(sym))
+               return;
+       tri = no;
+       if (sym->rev_dep.expr)
+               tri = expr_calc_value(sym->rev_dep.expr);
+       if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+               tri = yes;
+       if (sym->rev_dep.tri != tri) {
+               sym->rev_dep.tri = tri;
+               sym_set_changed(sym);
+       }
+}
+
+static struct symbol *sym_calc_choice(struct symbol *sym)
+{
+       struct symbol *def_sym;
+       struct property *prop;
+       struct expr *e;
+
+       /* is the user choice visible? */
+       def_sym = sym->def[S_DEF_USER].val;
+       if (def_sym) {
+               sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       return def_sym;
+       }
+
+       /* any of the defaults visible? */
+       for_all_defaults(sym, prop) {
+               prop->visible.tri = expr_calc_value(prop->visible.expr);
+               if (prop->visible.tri == no)
+                       continue;
+               def_sym = prop_get_symbol(prop);
+               sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       return def_sym;
+       }
+
+       /* just get the first visible value */
+       prop = sym_get_choice_prop(sym);
+       for (e = prop->expr; e; e = e->left.expr) {
+               def_sym = e->right.sym;
+               sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       return def_sym;
+       }
+
+       /* no choice? reset tristate value */
+       sym->curr.tri = no;
+       return NULL;
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+       struct symbol_value newval, oldval;
+       struct property *prop;
+       struct expr *e;
+
+       if (!sym)
+               return;
+
+       if (sym->flags & SYMBOL_VALID)
+               return;
+       sym->flags |= SYMBOL_VALID;
+
+       oldval = sym->curr;
+
+       switch (sym->type) {
+       case S_INT:
+       case S_HEX:
+       case S_STRING:
+               newval = symbol_empty.curr;
+               break;
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               newval = symbol_no.curr;
+               break;
+       default:
+               sym->curr.val = sym->name;
+               sym->curr.tri = no;
+               return;
+       }
+       if (!sym_is_choice_value(sym))
+               sym->flags &= ~SYMBOL_WRITE;
+
+       sym_calc_visibility(sym);
+
+       /* set default if recursively called */
+       sym->curr = newval;
+
+       switch (sym_get_type(sym)) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               if (sym_is_choice_value(sym) && sym->visible == yes) {
+                       prop = sym_get_choice_prop(sym);
+                       newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
+               } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) {
+                       sym->flags |= SYMBOL_WRITE;
+                       if (sym_has_value(sym))
+                               newval.tri = sym->def[S_DEF_USER].tri;
+                       else if (!sym_is_choice(sym)) {
+                               prop = sym_get_default_prop(sym);
+                               if (prop)
+                                       newval.tri = expr_calc_value(prop->expr);
+                       }
+                       newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri);
+               } else if (!sym_is_choice(sym)) {
+                       prop = sym_get_default_prop(sym);
+                       if (prop) {
+                               sym->flags |= SYMBOL_WRITE;
+                               newval.tri = expr_calc_value(prop->expr);
+                       }
+               }
+               if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
+                       newval.tri = yes;
+               break;
+       case S_STRING:
+       case S_HEX:
+       case S_INT:
+               if (sym->visible != no) {
+                       sym->flags |= SYMBOL_WRITE;
+                       if (sym_has_value(sym)) {
+                               newval.val = sym->def[S_DEF_USER].val;
+                               break;
+                       }
+               }
+               prop = sym_get_default_prop(sym);
+               if (prop) {
+                       struct symbol *ds = prop_get_symbol(prop);
+                       if (ds) {
+                               sym->flags |= SYMBOL_WRITE;
+                               sym_calc_value(ds);
+                               newval.val = ds->curr.val;
+                       }
+               }
+               break;
+       default:
+               ;
+       }
+
+       sym->curr = newval;
+       if (sym_is_choice(sym) && newval.tri == yes)
+               sym->curr.val = sym_calc_choice(sym);
+       sym_validate_range(sym);
+
+       if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {
+               sym_set_changed(sym);
+               if (modules_sym == sym) {
+                       sym_set_all_changed();
+                       modules_val = modules_sym->curr.tri;
+               }
+       }
+
+       if (sym_is_choice(sym)) {
+               int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
+               prop = sym_get_choice_prop(sym);
+               for (e = prop->expr; e; e = e->left.expr) {
+                       e->right.sym->flags |= flags;
+                       if (flags & SYMBOL_CHANGED)
+                               sym_set_changed(e->right.sym);
+               }
+       }
+}
+
+void sym_clear_all_valid(void)
+{
+       struct symbol *sym;
+       int i;
+
+       for_all_symbols(i, sym)
+               sym->flags &= ~SYMBOL_VALID;
+       sym_change_count++;
+       if (modules_sym)
+               sym_calc_value(modules_sym);
+}
+
+void sym_set_changed(struct symbol *sym)
+{
+       struct property *prop;
+
+       sym->flags |= SYMBOL_CHANGED;
+       for (prop = sym->prop; prop; prop = prop->next) {
+               if (prop->menu)
+                       prop->menu->flags |= MENU_CHANGED;
+       }
+}
+
+void sym_set_all_changed(void)
+{
+       struct symbol *sym;
+       int i;
+
+       for_all_symbols(i, sym)
+               sym_set_changed(sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+       int type = sym_get_type(sym);
+
+       if (sym->visible == no)
+               return false;
+
+       if (type != S_BOOLEAN && type != S_TRISTATE)
+               return false;
+
+       if (type == S_BOOLEAN && val == mod)
+               return false;
+       if (sym->visible <= sym->rev_dep.tri)
+               return false;
+       if (sym_is_choice_value(sym) && sym->visible == yes)
+               return val == yes;
+       return val >= sym->rev_dep.tri && val <= sym->visible;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+       tristate oldval = sym_get_tristate_value(sym);
+
+       if (oldval != val && !sym_tristate_within_range(sym, val))
+               return false;
+
+       if (!(sym->flags & SYMBOL_DEF_USER)) {
+               sym->flags |= SYMBOL_DEF_USER;
+               sym_set_changed(sym);
+       }
+       /*
+        * setting a choice value also resets the new flag of the choice
+        * symbol and all other choice values.
+        */
+       if (sym_is_choice_value(sym) && val == yes) {
+               struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+               struct property *prop;
+               struct expr *e;
+
+               cs->def[S_DEF_USER].val = sym;
+               cs->flags |= SYMBOL_DEF_USER;
+               prop = sym_get_choice_prop(cs);
+               for (e = prop->expr; e; e = e->left.expr) {
+                       if (e->right.sym->visible != no)
+                               e->right.sym->flags |= SYMBOL_DEF_USER;
+               }
+       }
+
+       sym->def[S_DEF_USER].tri = val;
+       if (oldval != val)
+               sym_clear_all_valid();
+
+       return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+       tristate oldval, newval;
+
+       oldval = newval = sym_get_tristate_value(sym);
+       do {
+               switch (newval) {
+               case no:
+                       newval = mod;
+                       break;
+               case mod:
+                       newval = yes;
+                       break;
+               case yes:
+                       newval = no;
+                       break;
+               }
+               if (sym_set_tristate_value(sym, newval))
+                       break;
+       } while (oldval != newval);
+       return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+       signed char ch;
+
+       switch (sym->type) {
+       case S_STRING:
+               return true;
+       case S_INT:
+               ch = *str++;
+               if (ch == '-')
+                       ch = *str++;
+               if (!isdigit(ch))
+                       return false;
+               if (ch == '0' && *str != 0)
+                       return false;
+               while ((ch = *str++)) {
+                       if (!isdigit(ch))
+                               return false;
+               }
+               return true;
+       case S_HEX:
+               if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+                       str += 2;
+               ch = *str++;
+               do {
+                       if (!isxdigit(ch))
+                               return false;
+               } while ((ch = *str++));
+               return true;
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               switch (str[0]) {
+               case 'y': case 'Y':
+               case 'm': case 'M':
+               case 'n': case 'N':
+                       return true;
+               }
+               return false;
+       default:
+               return false;
+       }
+}
+
+bool sym_string_within_range(struct symbol *sym, const char *str)
+{
+       struct property *prop;
+       int val;
+
+       switch (sym->type) {
+       case S_STRING:
+               return sym_string_valid(sym, str);
+       case S_INT:
+               if (!sym_string_valid(sym, str))
+                       return false;
+               prop = sym_get_range_prop(sym);
+               if (!prop)
+                       return true;
+               val = strtol(str, NULL, 10);
+               return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
+                      val <= sym_get_range_val(prop->expr->right.sym, 10);
+       case S_HEX:
+               if (!sym_string_valid(sym, str))
+                       return false;
+               prop = sym_get_range_prop(sym);
+               if (!prop)
+                       return true;
+               val = strtol(str, NULL, 16);
+               return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
+                      val <= sym_get_range_val(prop->expr->right.sym, 16);
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               switch (str[0]) {
+               case 'y': case 'Y':
+                       return sym_tristate_within_range(sym, yes);
+               case 'm': case 'M':
+                       return sym_tristate_within_range(sym, mod);
+               case 'n': case 'N':
+                       return sym_tristate_within_range(sym, no);
+               }
+               return false;
+       default:
+               return false;
+       }
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+       const char *oldval;
+       char *val;
+       int size;
+
+       switch (sym->type) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               switch (newval[0]) {
+               case 'y': case 'Y':
+                       return sym_set_tristate_value(sym, yes);
+               case 'm': case 'M':
+                       return sym_set_tristate_value(sym, mod);
+               case 'n': case 'N':
+                       return sym_set_tristate_value(sym, no);
+               }
+               return false;
+       default:
+               ;
+       }
+
+       if (!sym_string_within_range(sym, newval))
+               return false;
+
+       if (!(sym->flags & SYMBOL_DEF_USER)) {
+               sym->flags |= SYMBOL_DEF_USER;
+               sym_set_changed(sym);
+       }
+
+       oldval = sym->def[S_DEF_USER].val;
+       size = strlen(newval) + 1;
+       if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+               size += 2;
+               sym->def[S_DEF_USER].val = val = malloc(size);
+               *val++ = '0';
+               *val++ = 'x';
+       } else if (!oldval || strcmp(oldval, newval))
+               sym->def[S_DEF_USER].val = val = malloc(size);
+       else
+               return true;
+
+       strcpy(val, newval);
+       free((void *)oldval);
+       sym_clear_all_valid();
+
+       return true;
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+       tristate val;
+
+       switch (sym->type) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               val = sym_get_tristate_value(sym);
+               switch (val) {
+               case no:
+                       return "n";
+               case mod:
+                       return "m";
+               case yes:
+                       return "y";
+               }
+               break;
+       default:
+               ;
+       }
+       return (const char *)sym->curr.val;
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+       return sym->visible > sym->rev_dep.tri;
+}
+
+struct symbol *sym_lookup(const char *name, int isconst)
+{
+       struct symbol *symbol;
+       const char *ptr;
+       char *new_name;
+       int hash = 0;
+
+       if (name) {
+               if (name[0] && !name[1]) {
+                       switch (name[0]) {
+                       case 'y': return &symbol_yes;
+                       case 'm': return &symbol_mod;
+                       case 'n': return &symbol_no;
+                       }
+               }
+               for (ptr = name; *ptr; ptr++)
+                       hash += *ptr;
+               hash &= 0xff;
+
+               for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+                       if (!strcmp(symbol->name, name)) {
+                               if ((isconst && symbol->flags & SYMBOL_CONST) ||
+                                   (!isconst && !(symbol->flags & SYMBOL_CONST)))
+                                       return symbol;
+                       }
+               }
+               new_name = strdup(name);
+       } else {
+               new_name = NULL;
+               hash = 256;
+       }
+
+       symbol = malloc(sizeof(*symbol));
+       memset(symbol, 0, sizeof(*symbol));
+       symbol->name = new_name;
+       symbol->type = S_UNKNOWN;
+       if (isconst)
+               symbol->flags |= SYMBOL_CONST;
+
+       symbol->next = symbol_hash[hash];
+       symbol_hash[hash] = symbol;
+
+       return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+       struct symbol *symbol = NULL;
+       const char *ptr;
+       int hash = 0;
+
+       if (!name)
+               return NULL;
+
+       if (name[0] && !name[1]) {
+               switch (name[0]) {
+               case 'y': return &symbol_yes;
+               case 'm': return &symbol_mod;
+               case 'n': return &symbol_no;
+               }
+       }
+       for (ptr = name; *ptr; ptr++)
+               hash += *ptr;
+       hash &= 0xff;
+
+       for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+               if (!strcmp(symbol->name, name) &&
+                   !(symbol->flags & SYMBOL_CONST))
+                               break;
+       }
+
+       return symbol;
+}
+
+struct symbol **sym_re_search(const char *pattern)
+{
+       struct symbol *sym, **sym_arr = NULL;
+       int i, cnt, size;
+       regex_t re;
+
+       cnt = size = 0;
+       /* Skip if empty */
+       if (strlen(pattern) == 0)
+               return NULL;
+       if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE))
+               return NULL;
+
+       for_all_symbols(i, sym) {
+               if (sym->flags & SYMBOL_CONST || !sym->name)
+                       continue;
+               if (regexec(&re, sym->name, 0, NULL, 0))
+                       continue;
+               if (cnt + 1 >= size) {
+                       void *tmp = sym_arr;
+                       size += 16;
+                       sym_arr = realloc(sym_arr, size * sizeof(struct symbol *));
+                       if (!sym_arr) {
+                               free(tmp);
+                               return NULL;
+                       }
+               }
+               sym_arr[cnt++] = sym;
+       }
+       if (sym_arr)
+               sym_arr[cnt] = NULL;
+       regfree(&re);
+
+       return sym_arr;
+}
+
+
+struct symbol *sym_check_deps(struct symbol *sym);
+
+static struct symbol *sym_check_expr_deps(struct expr *e)
+{
+       struct symbol *sym;
+
+       if (!e)
+               return NULL;
+       switch (e->type) {
+       case E_OR:
+       case E_AND:
+               sym = sym_check_expr_deps(e->left.expr);
+               if (sym)
+                       return sym;
+               return sym_check_expr_deps(e->right.expr);
+       case E_NOT:
+               return sym_check_expr_deps(e->left.expr);
+       case E_EQUAL:
+       case E_UNEQUAL:
+               sym = sym_check_deps(e->left.sym);
+               if (sym)
+                       return sym;
+               return sym_check_deps(e->right.sym);
+       case E_SYMBOL:
+               return sym_check_deps(e->left.sym);
+       default:
+               break;
+       }
+       printf("Oops! How to check %d?\n", e->type);
+       return NULL;
+}
+
+struct symbol *sym_check_deps(struct symbol *sym)
+{
+       struct symbol *sym2;
+       struct property *prop;
+
+       if (sym->flags & SYMBOL_CHECK) {
+               printf("Warning! Found recursive dependency: %s", sym->name);
+               return sym;
+       }
+       if (sym->flags & SYMBOL_CHECKED)
+               return NULL;
+
+       sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+       sym2 = sym_check_expr_deps(sym->rev_dep.expr);
+       if (sym2)
+               goto out;
+
+       for (prop = sym->prop; prop; prop = prop->next) {
+               if (prop->type == P_CHOICE || prop->type == P_SELECT)
+                       continue;
+               sym2 = sym_check_expr_deps(prop->visible.expr);
+               if (sym2)
+                       goto out;
+               if (prop->type != P_DEFAULT || sym_is_choice(sym))
+                       continue;
+               sym2 = sym_check_expr_deps(prop->expr);
+               if (sym2)
+                       goto out;
+       }
+out:
+       if (sym2) {
+               printf(" %s", sym->name);
+               if (sym2 == sym) {
+                       printf("\n");
+                       sym2 = NULL;
+               }
+       }
+       sym->flags &= ~SYMBOL_CHECK;
+       return sym2;
+}
+
+struct property *prop_alloc(enum prop_type type, struct symbol *sym)
+{
+       struct property *prop;
+       struct property **propp;
+
+       prop = malloc(sizeof(*prop));
+       memset(prop, 0, sizeof(*prop));
+       prop->type = type;
+       prop->sym = sym;
+       prop->file = current_file;
+       prop->lineno = zconf_lineno();
+
+       /* append property to the prop list of symbol */
+       if (sym) {
+               for (propp = &sym->prop; *propp; propp = &(*propp)->next)
+                       ;
+               *propp = prop;
+       }
+
+       return prop;
+}
+
+struct symbol *prop_get_symbol(struct property *prop)
+{
+       if (prop->expr && (prop->expr->type == E_SYMBOL ||
+                          prop->expr->type == E_CHOICE))
+               return prop->expr->left.sym;
+       return NULL;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+       switch (type) {
+       case P_PROMPT:
+               return "prompt";
+       case P_COMMENT:
+               return "comment";
+       case P_MENU:
+               return "menu";
+       case P_DEFAULT:
+               return "default";
+       case P_CHOICE:
+               return "choice";
+       case P_SELECT:
+               return "select";
+       case P_RANGE:
+               return "range";
+       case P_UNKNOWN:
+               break;
+       }
+       return "unknown";
+}
diff --git a/kconfig/util.c b/kconfig/util.c
new file mode 100644 (file)
index 0000000..e3f28b9
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <string.h>
+#include "lkc.h"
+
+/* file already present in list? If not add it */
+struct file *file_lookup(const char *name)
+{
+       struct file *file;
+
+       for (file = file_list; file; file = file->next) {
+               if (!strcmp(name, file->name))
+                       return file;
+       }
+
+       file = malloc(sizeof(*file));
+       memset(file, 0, sizeof(*file));
+       file->name = strdup(name);
+       file->next = file_list;
+       file_list = file;
+       return file;
+}
+
+/* write a dependency file as used by kbuild to track dependencies */
+int file_write_dep(const char *name)
+{
+       struct file *file;
+       FILE *out;
+
+       if (!name)
+               name = ".kconfig.d";
+       out = fopen("..config.tmp", "w");
+       if (!out)
+               return 1;
+       fprintf(out, "deps_config := \\\n");
+       for (file = file_list; file; file = file->next) {
+               if (file->next)
+                       fprintf(out, "\t%s \\\n", file->name);
+               else
+                       fprintf(out, "\t%s\n", file->name);
+       }
+       fprintf(out, "\ninclude/config/auto.conf: \\\n"
+                    "\t$(deps_config)\n\n"
+                    "$(deps_config): ;\n");
+       fclose(out);
+       rename("..config.tmp", name);
+       return 0;
+}
+
+
+/* Allocate initial growable sting */
+struct gstr str_new(void)
+{
+       struct gstr gs;
+       gs.s = malloc(sizeof(char) * 64);
+       gs.len = 16;
+       strcpy(gs.s, "\0");
+       return gs;
+}
+
+/* Allocate and assign growable string */
+struct gstr str_assign(const char *s)
+{
+       struct gstr gs;
+       gs.s = strdup(s);
+       gs.len = strlen(s) + 1;
+       return gs;
+}
+
+/* Free storage for growable string */
+void str_free(struct gstr *gs)
+{
+       if (gs->s)
+               free(gs->s);
+       gs->s = NULL;
+       gs->len = 0;
+}
+
+/* Append to growable string */
+void str_append(struct gstr *gs, const char *s)
+{
+       size_t l = strlen(gs->s) + strlen(s) + 1;
+       if (l > gs->len) {
+               gs->s   = realloc(gs->s, l);
+               gs->len = l;
+       }
+       strcat(gs->s, s);
+}
+
+/* Append printf formatted string to growable string */
+void str_printf(struct gstr *gs, const char *fmt, ...)
+{
+       va_list ap;
+       char s[10000]; /* big enough... */
+       va_start(ap, fmt);
+       vsnprintf(s, sizeof(s), fmt, ap);
+       str_append(gs, s);
+       va_end(ap);
+}
+
+/* Retrieve value of growable string */
+const char *str_get(struct gstr *gs)
+{
+       return gs->s;
+}
+
diff --git a/kconfig/zconf.hash.c_shipped b/kconfig/zconf.hash.c_shipped
new file mode 100644 (file)
index 0000000..47c8b5b
--- /dev/null
@@ -0,0 +1,242 @@
+/* ANSI-C code produced by gperf version 3.0.1 */
+/* Command-line: gperf  */
+/* Computed positions: -k'1,3' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+struct kconf_id;
+/* maximum key range = 45, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+kconf_id_hash (register const char *str, register unsigned int len)
+{
+  static unsigned char asso_values[] =
+    {
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 25, 30, 15,
+       0, 15,  0, 47,  5, 15, 47, 47, 30, 20,
+       5,  0, 25, 15,  0,  0, 10, 35, 47, 47,
+       5, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47
+    };
+  register int hval = len;
+
+  switch (hval)
+    {
+      default:
+        hval += asso_values[(unsigned char)str[2]];
+      /*FALLTHROUGH*/
+      case 2:
+      case 1:
+        hval += asso_values[(unsigned char)str[0]];
+        break;
+    }
+  return hval;
+}
+
+struct kconf_id_strings_t
+  {
+    char kconf_id_strings_str2[sizeof("on")];
+    char kconf_id_strings_str6[sizeof("string")];
+    char kconf_id_strings_str7[sizeof("default")];
+    char kconf_id_strings_str8[sizeof("def_bool")];
+    char kconf_id_strings_str10[sizeof("range")];
+    char kconf_id_strings_str11[sizeof("def_boolean")];
+    char kconf_id_strings_str12[sizeof("def_tristate")];
+    char kconf_id_strings_str13[sizeof("hex")];
+    char kconf_id_strings_str14[sizeof("defconfig_list")];
+    char kconf_id_strings_str16[sizeof("option")];
+    char kconf_id_strings_str17[sizeof("if")];
+    char kconf_id_strings_str18[sizeof("optional")];
+    char kconf_id_strings_str20[sizeof("endif")];
+    char kconf_id_strings_str21[sizeof("choice")];
+    char kconf_id_strings_str22[sizeof("endmenu")];
+    char kconf_id_strings_str23[sizeof("requires")];
+    char kconf_id_strings_str24[sizeof("endchoice")];
+    char kconf_id_strings_str26[sizeof("config")];
+    char kconf_id_strings_str27[sizeof("modules")];
+    char kconf_id_strings_str28[sizeof("int")];
+    char kconf_id_strings_str29[sizeof("menu")];
+    char kconf_id_strings_str31[sizeof("prompt")];
+    char kconf_id_strings_str32[sizeof("depends")];
+    char kconf_id_strings_str33[sizeof("tristate")];
+    char kconf_id_strings_str34[sizeof("bool")];
+    char kconf_id_strings_str35[sizeof("menuconfig")];
+    char kconf_id_strings_str36[sizeof("select")];
+    char kconf_id_strings_str37[sizeof("boolean")];
+    char kconf_id_strings_str39[sizeof("help")];
+    char kconf_id_strings_str41[sizeof("source")];
+    char kconf_id_strings_str42[sizeof("comment")];
+    char kconf_id_strings_str43[sizeof("mainmenu")];
+    char kconf_id_strings_str46[sizeof("enable")];
+  };
+static struct kconf_id_strings_t kconf_id_strings_contents =
+  {
+    "on",
+    "string",
+    "default",
+    "def_bool",
+    "range",
+    "def_boolean",
+    "def_tristate",
+    "hex",
+    "defconfig_list",
+    "option",
+    "if",
+    "optional",
+    "endif",
+    "choice",
+    "endmenu",
+    "requires",
+    "endchoice",
+    "config",
+    "modules",
+    "int",
+    "menu",
+    "prompt",
+    "depends",
+    "tristate",
+    "bool",
+    "menuconfig",
+    "select",
+    "boolean",
+    "help",
+    "source",
+    "comment",
+    "mainmenu",
+    "enable"
+  };
+#define kconf_id_strings ((const char *) &kconf_id_strings_contents)
+#ifdef __GNUC__
+__inline
+#endif
+struct kconf_id *
+kconf_id_lookup (register const char *str, register unsigned int len)
+{
+  enum
+    {
+      TOTAL_KEYWORDS = 33,
+      MIN_WORD_LENGTH = 2,
+      MAX_WORD_LENGTH = 14,
+      MIN_HASH_VALUE = 2,
+      MAX_HASH_VALUE = 46
+    };
+
+  static struct kconf_id wordlist[] =
+    {
+      {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2,            T_ON,           TF_PARAM},
+      {-1}, {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6,            T_TYPE,         TF_COMMAND, S_STRING},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7,    T_DEFAULT,      TF_COMMAND, S_UNKNOWN},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8,    T_DEFAULT,      TF_COMMAND, S_BOOLEAN},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10,           T_RANGE,        TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11,   T_DEFAULT,      TF_COMMAND, S_BOOLEAN},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12,   T_DEFAULT,      TF_COMMAND, S_TRISTATE},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13,           T_TYPE,         TF_COMMAND, S_HEX},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14,   T_OPT_DEFCONFIG_LIST,TF_OPTION},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16,           T_OPTION,       TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17,           T_IF,           TF_COMMAND|TF_PARAM},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18,   T_OPTIONAL,     TF_COMMAND},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20,           T_ENDIF,        TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21,           T_CHOICE,       TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22,   T_ENDMENU,      TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23,   T_REQUIRES,     TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str24,   T_ENDCHOICE,    TF_COMMAND},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26,           T_CONFIG,       TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27,   T_OPT_MODULES,  TF_OPTION},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28,           T_TYPE,         TF_COMMAND, S_INT},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29,           T_MENU,         TF_COMMAND},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31,           T_PROMPT,       TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32,   T_DEPENDS,      TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33,   T_TYPE,         TF_COMMAND, S_TRISTATE},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34,           T_TYPE,         TF_COMMAND, S_BOOLEAN},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str35,   T_MENUCONFIG,   TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str36,           T_SELECT,       TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37,   T_TYPE,         TF_COMMAND, S_BOOLEAN},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str39,           T_HELP,         TF_COMMAND},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41,           T_SOURCE,       TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str42,   T_COMMENT,      TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str43,   T_MAINMENU,     TF_COMMAND},
+      {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46,           T_SELECT,       TF_COMMAND}
+    };
+
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = kconf_id_hash (str, len);
+
+      if (key <= MAX_HASH_VALUE && key >= 0)
+        {
+          register int o = wordlist[key].name;
+          if (o >= 0)
+            {
+              register const char *s = o + kconf_id_strings;
+
+              if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+                return &wordlist[key];
+            }
+        }
+    }
+  return 0;
+}
+
diff --git a/kconfig/zconf.tab.c_shipped b/kconfig/zconf.tab.c_shipped
new file mode 100644 (file)
index 0000000..34ccabd
--- /dev/null
@@ -0,0 +1,2345 @@
+/* A Bison parser, made by GNU Bison 2.1.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.1"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names.  */
+#define yyparse zconfparse
+#define yylex   zconflex
+#define yyerror zconferror
+#define yylval  zconflval
+#define yychar  zconfchar
+#define yydebug zconfdebug
+#define yynerrs zconfnerrs
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     T_MAINMENU = 258,
+     T_MENU = 259,
+     T_ENDMENU = 260,
+     T_SOURCE = 261,
+     T_CHOICE = 262,
+     T_ENDCHOICE = 263,
+     T_COMMENT = 264,
+     T_CONFIG = 265,
+     T_MENUCONFIG = 266,
+     T_HELP = 267,
+     T_HELPTEXT = 268,
+     T_IF = 269,
+     T_ENDIF = 270,
+     T_DEPENDS = 271,
+     T_REQUIRES = 272,
+     T_OPTIONAL = 273,
+     T_PROMPT = 274,
+     T_TYPE = 275,
+     T_DEFAULT = 276,
+     T_SELECT = 277,
+     T_RANGE = 278,
+     T_OPTION = 279,
+     T_ON = 280,
+     T_WORD = 281,
+     T_WORD_QUOTE = 282,
+     T_UNEQUAL = 283,
+     T_CLOSE_PAREN = 284,
+     T_OPEN_PAREN = 285,
+     T_EOL = 286,
+     T_OR = 287,
+     T_AND = 288,
+     T_EQUAL = 289,
+     T_NOT = 290
+   };
+#endif
+/* Tokens.  */
+#define T_MAINMENU 258
+#define T_MENU 259
+#define T_ENDMENU 260
+#define T_SOURCE 261
+#define T_CHOICE 262
+#define T_ENDCHOICE 263
+#define T_COMMENT 264
+#define T_CONFIG 265
+#define T_MENUCONFIG 266
+#define T_HELP 267
+#define T_HELPTEXT 268
+#define T_IF 269
+#define T_ENDIF 270
+#define T_DEPENDS 271
+#define T_REQUIRES 272
+#define T_OPTIONAL 273
+#define T_PROMPT 274
+#define T_TYPE 275
+#define T_DEFAULT 276
+#define T_SELECT 277
+#define T_RANGE 278
+#define T_OPTION 279
+#define T_ON 280
+#define T_WORD 281
+#define T_WORD_QUOTE 282
+#define T_UNEQUAL 283
+#define T_CLOSE_PAREN 284
+#define T_OPEN_PAREN 285
+#define T_EOL 286
+#define T_OR 287
+#define T_AND 288
+#define T_EQUAL 289
+#define T_NOT 290
+
+
+
+
+/* Copy the first part of user declarations.  */
+
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#include "zconf.hash.c"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD         0x0001
+#define DEBUG_PARSE    0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+static struct menu *current_menu, *current_entry;
+
+#define YYDEBUG 0
+#if YYDEBUG
+#define YYERROR_VERBOSE
+#endif
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+
+typedef union YYSTYPE {
+       char *string;
+       struct file *file;
+       struct symbol *symbol;
+       struct expr *expr;
+       struct menu *menu;
+       struct kconf_id *id;
+} YYSTYPE;
+/* Line 196 of yacc.c.  */
+
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 219 of yacc.c.  */
+
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T) && (defined (__STDC__) || defined (__cplusplus))
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#ifndef YY_
+# if YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if defined (__STDC__) || defined (__cplusplus)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     define YYINCLUDED_STDLIB_H
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2005 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM ((YYSIZE_T) -1)
+#  endif
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if (! defined (malloc) && ! defined (YYINCLUDED_STDLIB_H) \
+       && (defined (__STDC__) || defined (__cplusplus)))
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if (! defined (free) && ! defined (YYINCLUDED_STDLIB_H) \
+       && (defined (__STDC__) || defined (__cplusplus)))
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifdef __cplusplus
+}
+#  endif
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+        || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short int yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short int) + sizeof (YYSTYPE))                    \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined (__GNUC__) && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         YYSIZE_T yyi;                         \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)                                       \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack, Stack, yysize);                          \
+       Stack = &yyptr->Stack;                                          \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  3
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   275
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  36
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  45
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  110
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  183
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   290
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned short int yyprhs[] =
+{
+       0,     0,     3,     5,     6,     9,    12,    15,    20,    23,
+      28,    33,    37,    39,    41,    43,    45,    47,    49,    51,
+      53,    55,    57,    59,    61,    63,    67,    70,    74,    77,
+      81,    84,    85,    88,    91,    94,    97,   100,   103,   107,
+     112,   117,   122,   128,   132,   133,   137,   138,   141,   144,
+     147,   149,   153,   154,   157,   160,   163,   166,   169,   174,
+     178,   181,   186,   187,   190,   194,   196,   200,   201,   204,
+     207,   210,   214,   217,   219,   223,   224,   227,   230,   233,
+     237,   241,   244,   247,   250,   251,   254,   257,   260,   265,
+     269,   273,   274,   277,   279,   281,   284,   287,   290,   292,
+     295,   296,   299,   301,   305,   309,   313,   316,   320,   324,
+     326
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      37,     0,    -1,    38,    -1,    -1,    38,    40,    -1,    38,
+      54,    -1,    38,    65,    -1,    38,     3,    75,    77,    -1,
+      38,    76,    -1,    38,    26,     1,    31,    -1,    38,    39,
+       1,    31,    -1,    38,     1,    31,    -1,    16,    -1,    19,
+      -1,    20,    -1,    22,    -1,    18,    -1,    23,    -1,    21,
+      -1,    31,    -1,    60,    -1,    69,    -1,    43,    -1,    45,
+      -1,    67,    -1,    26,     1,    31,    -1,     1,    31,    -1,
+      10,    26,    31,    -1,    42,    46,    -1,    11,    26,    31,
+      -1,    44,    46,    -1,    -1,    46,    47,    -1,    46,    48,
+      -1,    46,    73,    -1,    46,    71,    -1,    46,    41,    -1,
+      46,    31,    -1,    20,    74,    31,    -1,    19,    75,    78,
+      31,    -1,    21,    79,    78,    31,    -1,    22,    26,    78,
+      31,    -1,    23,    80,    80,    78,    31,    -1,    24,    49,
+      31,    -1,    -1,    49,    26,    50,    -1,    -1,    34,    75,
+      -1,     7,    31,    -1,    51,    55,    -1,    76,    -1,    52,
+      57,    53,    -1,    -1,    55,    56,    -1,    55,    73,    -1,
+      55,    71,    -1,    55,    31,    -1,    55,    41,    -1,    19,
+      75,    78,    31,    -1,    20,    74,    31,    -1,    18,    31,
+      -1,    21,    26,    78,    31,    -1,    -1,    57,    40,    -1,
+      14,    79,    77,    -1,    76,    -1,    58,    61,    59,    -1,
+      -1,    61,    40,    -1,    61,    65,    -1,    61,    54,    -1,
+       4,    75,    31,    -1,    62,    72,    -1,    76,    -1,    63,
+      66,    64,    -1,    -1,    66,    40,    -1,    66,    65,    -1,
+      66,    54,    -1,     6,    75,    31,    -1,     9,    75,    31,
+      -1,    68,    72,    -1,    12,    31,    -1,    70,    13,    -1,
+      -1,    72,    73,    -1,    72,    31,    -1,    72,    41,    -1,
+      16,    25,    79,    31,    -1,    16,    79,    31,    -1,    17,
+      79,    31,    -1,    -1,    75,    78,    -1,    26,    -1,    27,
+      -1,     5,    31,    -1,     8,    31,    -1,    15,    31,    -1,
+      31,    -1,    77,    31,    -1,    -1,    14,    79,    -1,    80,
+      -1,    80,    34,    80,    -1,    80,    28,    80,    -1,    30,
+      79,    29,    -1,    35,    79,    -1,    79,    32,    79,    -1,
+      79,    33,    79,    -1,    26,    -1,    27,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned short int yyrline[] =
+{
+       0,   105,   105,   107,   109,   110,   111,   112,   113,   114,
+     115,   119,   123,   123,   123,   123,   123,   123,   123,   127,
+     128,   129,   130,   131,   132,   136,   137,   143,   151,   157,
+     165,   175,   177,   178,   179,   180,   181,   182,   185,   193,
+     199,   209,   215,   221,   224,   226,   237,   238,   243,   252,
+     257,   265,   268,   270,   271,   272,   273,   274,   277,   283,
+     294,   300,   310,   312,   317,   325,   333,   336,   338,   339,
+     340,   345,   352,   357,   365,   368,   370,   371,   372,   375,
+     383,   390,   397,   403,   410,   412,   413,   414,   417,   422,
+     427,   435,   437,   442,   443,   446,   447,   448,   452,   453,
+     456,   457,   460,   461,   462,   463,   464,   465,   466,   469,
+     470
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
+  "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
+  "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
+  "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT",
+  "T_SELECT", "T_RANGE", "T_OPTION", "T_ON", "T_WORD", "T_WORD_QUOTE",
+  "T_UNEQUAL", "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND",
+  "T_EQUAL", "T_NOT", "$accept", "input", "stmt_list", "option_name",
+  "common_stmt", "option_error", "config_entry_start", "config_stmt",
+  "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+  "config_option", "symbol_option", "symbol_option_list",
+  "symbol_option_arg", "choice", "choice_entry", "choice_end",
+  "choice_stmt", "choice_option_list", "choice_option", "choice_block",
+  "if_entry", "if_end", "if_stmt", "if_block", "menu", "menu_entry",
+  "menu_end", "menu_stmt", "menu_block", "source_stmt", "comment",
+  "comment_stmt", "help_start", "help", "depends_list", "depends",
+  "prompt_stmt_opt", "prompt", "end", "nl", "if_expr", "expr", "symbol", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short int yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    36,    37,    38,    38,    38,    38,    38,    38,    38,
+      38,    38,    39,    39,    39,    39,    39,    39,    39,    40,
+      40,    40,    40,    40,    40,    41,    41,    42,    43,    44,
+      45,    46,    46,    46,    46,    46,    46,    46,    47,    47,
+      47,    47,    47,    48,    49,    49,    50,    50,    51,    52,
+      53,    54,    55,    55,    55,    55,    55,    55,    56,    56,
+      56,    56,    57,    57,    58,    59,    60,    61,    61,    61,
+      61,    62,    63,    64,    65,    66,    66,    66,    66,    67,
+      68,    69,    70,    71,    72,    72,    72,    72,    73,    73,
+      73,    74,    74,    75,    75,    76,    76,    76,    77,    77,
+      78,    78,    79,    79,    79,    79,    79,    79,    79,    80,
+      80
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     1,     0,     2,     2,     2,     4,     2,     4,
+       4,     3,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     3,     2,     3,     2,     3,
+       2,     0,     2,     2,     2,     2,     2,     2,     3,     4,
+       4,     4,     5,     3,     0,     3,     0,     2,     2,     2,
+       1,     3,     0,     2,     2,     2,     2,     2,     4,     3,
+       2,     4,     0,     2,     3,     1,     3,     0,     2,     2,
+       2,     3,     2,     1,     3,     0,     2,     2,     2,     3,
+       3,     2,     2,     2,     0,     2,     2,     2,     4,     3,
+       3,     0,     2,     1,     1,     2,     2,     2,     1,     2,
+       0,     2,     1,     3,     3,     3,     2,     3,     3,     1,
+       1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       3,     0,     0,     1,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,    12,    16,    13,    14,
+      18,    15,    17,     0,    19,     0,     4,    31,    22,    31,
+      23,    52,    62,     5,    67,    20,    84,    75,     6,    24,
+      84,    21,     8,    11,    93,    94,     0,     0,    95,     0,
+      48,    96,     0,     0,     0,   109,   110,     0,     0,     0,
+     102,    97,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,    98,     7,    71,    79,    80,    27,    29,     0,
+     106,     0,     0,    64,     0,     0,     9,    10,     0,     0,
+       0,     0,     0,    91,     0,     0,     0,    44,     0,    37,
+      36,    32,    33,     0,    35,    34,     0,     0,    91,     0,
+      56,    57,    53,    55,    54,    63,    51,    50,    68,    70,
+      66,    69,    65,    86,    87,    85,    76,    78,    74,    77,
+      73,    99,   105,   107,   108,   104,   103,    26,    82,     0,
+       0,     0,   100,     0,   100,   100,   100,     0,     0,     0,
+      83,    60,   100,     0,   100,     0,    89,    90,     0,     0,
+      38,    92,     0,     0,   100,    46,    43,    25,     0,    59,
+       0,    88,   101,    39,    40,    41,     0,     0,    45,    58,
+      61,    42,    47
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short int yydefgoto[] =
+{
+      -1,     1,     2,    25,    26,   100,    27,    28,    29,    30,
+      64,   101,   102,   148,   178,    31,    32,   116,    33,    66,
+     112,    67,    34,   120,    35,    68,    36,    37,   128,    38,
+      70,    39,    40,    41,   103,   104,    69,   105,   143,   144,
+      42,    73,   159,    59,    60
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -135
+static const short int yypact[] =
+{
+    -135,     2,   170,  -135,   -14,    56,    56,    -8,    56,    24,
+      67,    56,     7,    14,    62,    97,  -135,  -135,  -135,  -135,
+    -135,  -135,  -135,   156,  -135,   166,  -135,  -135,  -135,  -135,
+    -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,
+    -135,  -135,  -135,  -135,  -135,  -135,   138,   151,  -135,   152,
+    -135,  -135,   163,   167,   176,  -135,  -135,    62,    62,   185,
+     -19,  -135,   188,   190,    42,   103,   194,    85,    70,   222,
+      70,   132,  -135,   191,  -135,  -135,  -135,  -135,  -135,   127,
+    -135,    62,    62,   191,   104,   104,  -135,  -135,   193,   203,
+       9,    62,    56,    56,    62,   161,   104,  -135,   196,  -135,
+    -135,  -135,  -135,   233,  -135,  -135,   204,    56,    56,   221,
+    -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,
+    -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,
+    -135,  -135,  -135,   219,  -135,  -135,  -135,  -135,  -135,    62,
+     209,   212,   240,   224,   240,    -1,   240,   104,    41,   225,
+    -135,  -135,   240,   226,   240,   218,  -135,  -135,    62,   227,
+    -135,  -135,   228,   229,   240,   230,  -135,  -135,   231,  -135,
+     232,  -135,   112,  -135,  -135,  -135,   234,    56,  -135,  -135,
+    -135,  -135,  -135
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const short int yypgoto[] =
+{
+    -135,  -135,  -135,  -135,    94,   -45,  -135,  -135,  -135,  -135,
+     237,  -135,  -135,  -135,  -135,  -135,  -135,  -135,   -54,  -135,
+    -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,  -135,     1,
+    -135,  -135,  -135,  -135,  -135,   195,   235,   -44,   159,    -5,
+      98,   210,  -134,   -53,   -77
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -82
+static const short int yytable[] =
+{
+      46,    47,     3,    49,    79,    80,    52,   135,   136,    84,
+     161,   162,   163,   158,   119,    85,   127,    43,   168,   147,
+     170,   111,   114,    48,   124,   125,   124,   125,   133,   134,
+     176,    81,    82,    53,   139,    55,    56,   140,   141,    57,
+      54,   145,   -28,    88,    58,   -28,   -28,   -28,   -28,   -28,
+     -28,   -28,   -28,   -28,    89,    50,   -28,   -28,    90,    91,
+     -28,    92,    93,    94,    95,    96,    97,   165,    98,   121,
+     164,   129,   166,    99,     6,     7,     8,     9,    10,    11,
+      12,    13,    44,    45,    14,    15,   155,   142,    55,    56,
+       7,     8,    57,    10,    11,    12,    13,    58,    51,    14,
+      15,    24,   152,   -30,    88,   172,   -30,   -30,   -30,   -30,
+     -30,   -30,   -30,   -30,   -30,    89,    24,   -30,   -30,    90,
+      91,   -30,    92,    93,    94,    95,    96,    97,    61,    98,
+      55,    56,   -81,    88,    99,   -81,   -81,   -81,   -81,   -81,
+     -81,   -81,   -81,   -81,    81,    82,   -81,   -81,    90,    91,
+     -81,   -81,   -81,   -81,   -81,   -81,   132,    62,    98,    81,
+      82,   115,   118,   123,   126,   117,   122,    63,   130,    72,
+      -2,     4,   182,     5,     6,     7,     8,     9,    10,    11,
+      12,    13,    74,    75,    14,    15,    16,   146,    17,    18,
+      19,    20,    21,    22,    76,    88,    23,   149,    77,   -49,
+     -49,    24,   -49,   -49,   -49,   -49,    89,    78,   -49,   -49,
+      90,    91,   106,   107,   108,   109,    72,    81,    82,    86,
+      98,    87,   131,    88,   137,   110,   -72,   -72,   -72,   -72,
+     -72,   -72,   -72,   -72,   138,   151,   -72,   -72,    90,    91,
+     156,    81,    82,   157,    81,    82,   150,   154,    98,   171,
+      81,    82,    82,   123,   158,   160,   167,   169,   173,   174,
+     175,   113,   179,   180,   177,   181,    65,   153,     0,    83,
+       0,     0,     0,     0,     0,    71
+};
+
+static const short int yycheck[] =
+{
+       5,     6,     0,     8,    57,    58,    11,    84,    85,    28,
+     144,   145,   146,    14,    68,    34,    70,    31,   152,    96,
+     154,    66,    66,    31,    69,    69,    71,    71,    81,    82,
+     164,    32,    33,    26,    25,    26,    27,    90,    91,    30,
+      26,    94,     0,     1,    35,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    12,    31,    14,    15,    16,    17,
+      18,    19,    20,    21,    22,    23,    24,    26,    26,    68,
+     147,    70,    31,    31,     4,     5,     6,     7,     8,     9,
+      10,    11,    26,    27,    14,    15,   139,    92,    26,    27,
+       5,     6,    30,     8,     9,    10,    11,    35,    31,    14,
+      15,    31,   107,     0,     1,   158,     3,     4,     5,     6,
+       7,     8,     9,    10,    11,    12,    31,    14,    15,    16,
+      17,    18,    19,    20,    21,    22,    23,    24,    31,    26,
+      26,    27,     0,     1,    31,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    32,    33,    14,    15,    16,    17,
+      18,    19,    20,    21,    22,    23,    29,     1,    26,    32,
+      33,    67,    68,    31,    70,    67,    68,     1,    70,    31,
+       0,     1,   177,     3,     4,     5,     6,     7,     8,     9,
+      10,    11,    31,    31,    14,    15,    16,    26,    18,    19,
+      20,    21,    22,    23,    31,     1,    26,     1,    31,     5,
+       6,    31,     8,     9,    10,    11,    12,    31,    14,    15,
+      16,    17,    18,    19,    20,    21,    31,    32,    33,    31,
+      26,    31,    31,     1,    31,    31,     4,     5,     6,     7,
+       8,     9,    10,    11,    31,    31,    14,    15,    16,    17,
+      31,    32,    33,    31,    32,    33,    13,    26,    26,    31,
+      32,    33,    33,    31,    14,    31,    31,    31,    31,    31,
+      31,    66,    31,    31,    34,    31,    29,   108,    -1,    59,
+      -1,    -1,    -1,    -1,    -1,    40
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,    37,    38,     0,     1,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    14,    15,    16,    18,    19,    20,
+      21,    22,    23,    26,    31,    39,    40,    42,    43,    44,
+      45,    51,    52,    54,    58,    60,    62,    63,    65,    67,
+      68,    69,    76,    31,    26,    27,    75,    75,    31,    75,
+      31,    31,    75,    26,    26,    26,    27,    30,    35,    79,
+      80,    31,     1,     1,    46,    46,    55,    57,    61,    72,
+      66,    72,    31,    77,    31,    31,    31,    31,    31,    79,
+      79,    32,    33,    77,    28,    34,    31,    31,     1,    12,
+      16,    17,    19,    20,    21,    22,    23,    24,    26,    31,
+      41,    47,    48,    70,    71,    73,    18,    19,    20,    21,
+      31,    41,    56,    71,    73,    40,    53,    76,    40,    54,
+      59,    65,    76,    31,    41,    73,    40,    54,    64,    65,
+      76,    31,    29,    79,    79,    80,    80,    31,    31,    25,
+      79,    79,    75,    74,    75,    79,    26,    80,    49,     1,
+      13,    31,    75,    74,    26,    79,    31,    31,    14,    78,
+      31,    78,    78,    78,    80,    26,    31,    31,    78,    31,
+      78,    31,    79,    31,    31,    31,    78,    34,    50,    31,
+      31,    31,    75
+};
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL         goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      yytoken = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK;                                              \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                 \
+    }                                                          \
+while (0)
+
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)                               \
+    do                                                                 \
+      if (N)                                                           \
+       {                                                               \
+         (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;        \
+         (Current).first_column = YYRHSLOC (Rhs, 1).first_column;      \
+         (Current).last_line    = YYRHSLOC (Rhs, N).last_line;         \
+         (Current).last_column  = YYRHSLOC (Rhs, N).last_column;       \
+       }                                                               \
+      else                                                             \
+       {                                                               \
+         (Current).first_line   = (Current).last_line   =              \
+           YYRHSLOC (Rhs, 0).last_line;                                \
+         (Current).first_column = (Current).last_column =              \
+           YYRHSLOC (Rhs, 0).last_column;                              \
+       }                                                               \
+    while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)                 \
+     fprintf (File, "%d.%d-%d.%d",                     \
+              (Loc).first_line, (Loc).first_column,    \
+              (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)         \
+do {                                                           \
+  if (yydebug)                                                 \
+    {                                                          \
+      YYFPRINTF (stderr, "%s ", Title);                                \
+      yysymprint (stderr,                                      \
+                  Type, Value);        \
+      YYFPRINTF (stderr, "\n");                                        \
+    }                                                          \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short int *bottom;
+    short int *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu), ",
+             yyrule - 1, yylno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname[yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (Rule);            \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+\f
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      size_t yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+       switch (*++yyp)
+         {
+         case '\'':
+         case ',':
+           goto do_not_strip_quotes;
+
+         case '\\':
+           if (*++yyp != '\\')
+             goto do_not_strip_quotes;
+           /* Fall through.  */
+         default:
+           if (yyres)
+             yyres[yyn] = *yyp;
+           yyn++;
+           break;
+
+         case '"':
+           if (yyres)
+             yyres[yyn] = '\0';
+           return yyn;
+         }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+#endif /* YYERROR_VERBOSE */
+
+\f
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+      case 52: /* "choice_entry" */
+
+        {
+       fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+               (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+       if (current_menu == (yyvaluep->menu))
+               menu_end_menu();
+};
+
+        break;
+      case 58: /* "if_entry" */
+
+        {
+       fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+               (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+       if (current_menu == (yyvaluep->menu))
+               menu_end_menu();
+};
+
+        break;
+      case 63: /* "menu_entry" */
+
+        {
+       fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+               (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+       if (current_menu == (yyvaluep->menu))
+               menu_end_menu();
+};
+
+        break;
+
+      default:
+        break;
+    }
+}
+\f
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+    ;
+#endif
+#endif
+{
+  
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short int yyssa[YYINITDEPTH];
+  short int *yyss = yyssa;
+  short int *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack. Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       short int *yyss1 = yyss;
+
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow (YY_("memory exhausted"),
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       short int *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyexhaustedlab;
+       YYSTACK_RELOCATE (yyss);
+       YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a look-ahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 8:
+
+    { zconf_error("unexpected end statement"); ;}
+    break;
+
+  case 9:
+
+    { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); ;}
+    break;
+
+  case 10:
+
+    {
+       zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[-2].id)->name);
+;}
+    break;
+
+  case 11:
+
+    { zconf_error("invalid statement"); ;}
+    break;
+
+  case 25:
+
+    { zconf_error("unknown option \"%s\"", (yyvsp[-2].string)); ;}
+    break;
+
+  case 26:
+
+    { zconf_error("invalid option"); ;}
+    break;
+
+  case 27:
+
+    {
+       struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+       sym->flags |= SYMBOL_OPTIONAL;
+       menu_add_entry(sym);
+       printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+    break;
+
+  case 28:
+
+    {
+       menu_end_entry();
+       printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 29:
+
+    {
+       struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+       sym->flags |= SYMBOL_OPTIONAL;
+       menu_add_entry(sym);
+       printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+    break;
+
+  case 30:
+
+    {
+       if (current_entry->prompt)
+               current_entry->prompt->type = P_MENU;
+       else
+               zconfprint("warning: menuconfig statement without prompt");
+       menu_end_entry();
+       printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 38:
+
+    {
+       menu_set_type((yyvsp[-2].id)->stype);
+       printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+               zconf_curname(), zconf_lineno(),
+               (yyvsp[-2].id)->stype);
+;}
+    break;
+
+  case 39:
+
+    {
+       menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 40:
+
+    {
+       menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr));
+       if ((yyvsp[-3].id)->stype != S_UNKNOWN)
+               menu_set_type((yyvsp[-3].id)->stype);
+       printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+               zconf_curname(), zconf_lineno(),
+               (yyvsp[-3].id)->stype);
+;}
+    break;
+
+  case 41:
+
+    {
+       menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 42:
+
+    {
+       menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 45:
+
+    {
+       struct kconf_id *id = kconf_id_lookup((yyvsp[-1].string), strlen((yyvsp[-1].string)));
+       if (id && id->flags & TF_OPTION)
+               menu_add_option(id->token, (yyvsp[0].string));
+       else
+               zconfprint("warning: ignoring unknown option %s", (yyvsp[-1].string));
+       free((yyvsp[-1].string));
+;}
+    break;
+
+  case 46:
+
+    { (yyval.string) = NULL; ;}
+    break;
+
+  case 47:
+
+    { (yyval.string) = (yyvsp[0].string); ;}
+    break;
+
+  case 48:
+
+    {
+       struct symbol *sym = sym_lookup(NULL, 0);
+       sym->flags |= SYMBOL_CHOICE;
+       menu_add_entry(sym);
+       menu_add_expr(P_CHOICE, NULL, NULL);
+       printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 49:
+
+    {
+       (yyval.menu) = menu_add_menu();
+;}
+    break;
+
+  case 50:
+
+    {
+       if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+       }
+;}
+    break;
+
+  case 58:
+
+    {
+       menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 59:
+
+    {
+       if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) {
+               menu_set_type((yyvsp[-2].id)->stype);
+               printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+                       zconf_curname(), zconf_lineno(),
+                       (yyvsp[-2].id)->stype);
+       } else
+               YYERROR;
+;}
+    break;
+
+  case 60:
+
+    {
+       current_entry->sym->flags |= SYMBOL_OPTIONAL;
+       printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 61:
+
+    {
+       if ((yyvsp[-3].id)->stype == S_UNKNOWN) {
+               menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+               printd(DEBUG_PARSE, "%s:%d:default\n",
+                       zconf_curname(), zconf_lineno());
+       } else
+               YYERROR;
+;}
+    break;
+
+  case 64:
+
+    {
+       printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+       menu_add_entry(NULL);
+       menu_add_dep((yyvsp[-1].expr));
+       (yyval.menu) = menu_add_menu();
+;}
+    break;
+
+  case 65:
+
+    {
+       if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+       }
+;}
+    break;
+
+  case 71:
+
+    {
+       menu_add_entry(NULL);
+       menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL);
+       printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 72:
+
+    {
+       (yyval.menu) = menu_add_menu();
+;}
+    break;
+
+  case 73:
+
+    {
+       if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+       }
+;}
+    break;
+
+  case 79:
+
+    {
+       printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+       zconf_nextfile((yyvsp[-1].string));
+;}
+    break;
+
+  case 80:
+
+    {
+       menu_add_entry(NULL);
+       menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL);
+       printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 81:
+
+    {
+       menu_end_entry();
+;}
+    break;
+
+  case 82:
+
+    {
+       printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+       zconf_starthelp();
+;}
+    break;
+
+  case 83:
+
+    {
+       current_entry->sym->help = (yyvsp[0].string);
+;}
+    break;
+
+  case 88:
+
+    {
+       menu_add_dep((yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 89:
+
+    {
+       menu_add_dep((yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 90:
+
+    {
+       menu_add_dep((yyvsp[-1].expr));
+       printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 92:
+
+    {
+       menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr));
+;}
+    break;
+
+  case 95:
+
+    { (yyval.id) = (yyvsp[-1].id); ;}
+    break;
+
+  case 96:
+
+    { (yyval.id) = (yyvsp[-1].id); ;}
+    break;
+
+  case 97:
+
+    { (yyval.id) = (yyvsp[-1].id); ;}
+    break;
+
+  case 100:
+
+    { (yyval.expr) = NULL; ;}
+    break;
+
+  case 101:
+
+    { (yyval.expr) = (yyvsp[0].expr); ;}
+    break;
+
+  case 102:
+
+    { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); ;}
+    break;
+
+  case 103:
+
+    { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+    break;
+
+  case 104:
+
+    { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+    break;
+
+  case 105:
+
+    { (yyval.expr) = (yyvsp[-1].expr); ;}
+    break;
+
+  case 106:
+
+    { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); ;}
+    break;
+
+  case 107:
+
+    { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+    break;
+
+  case 108:
+
+    { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+    break;
+
+  case 109:
+
+    { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); ;}
+    break;
+
+  case 110:
+
+    { (yyval.symbol) = sym_lookup((yyvsp[0].string), 1); free((yyvsp[0].string)); ;}
+    break;
+
+
+      default: break;
+    }
+
+/* Line 1126 of yacc.c.  */
+
+\f
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+       {
+         int yytype = YYTRANSLATE (yychar);
+         YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+         YYSIZE_T yysize = yysize0;
+         YYSIZE_T yysize1;
+         int yysize_overflow = 0;
+         char *yymsg = 0;
+#        define YYERROR_VERBOSE_ARGS_MAXIMUM 5
+         char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+         int yyx;
+
+#if 0
+         /* This is so xgettext sees the translatable formats that are
+            constructed on the fly.  */
+         YY_("syntax error, unexpected %s");
+         YY_("syntax error, unexpected %s, expecting %s");
+         YY_("syntax error, unexpected %s, expecting %s or %s");
+         YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+         YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+#endif
+         char *yyfmt;
+         char const *yyf;
+         static char const yyunexpected[] = "syntax error, unexpected %s";
+         static char const yyexpecting[] = ", expecting %s";
+         static char const yyor[] = " or %s";
+         char yyformat[sizeof yyunexpected
+                       + sizeof yyexpecting - 1
+                       + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+                          * (sizeof yyor - 1))];
+         char const *yyprefix = yyexpecting;
+
+         /* Start YYX at -YYN if negative to avoid negative indexes in
+            YYCHECK.  */
+         int yyxbegin = yyn < 0 ? -yyn : 0;
+
+         /* Stay within bounds of both yycheck and yytname.  */
+         int yychecklim = YYLAST - yyn;
+         int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+         int yycount = 1;
+
+         yyarg[0] = yytname[yytype];
+         yyfmt = yystpcpy (yyformat, yyunexpected);
+
+         for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+           if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+             {
+               if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                 {
+                   yycount = 1;
+                   yysize = yysize0;
+                   yyformat[sizeof yyunexpected - 1] = '\0';
+                   break;
+                 }
+               yyarg[yycount++] = yytname[yyx];
+               yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+               yysize_overflow |= yysize1 < yysize;
+               yysize = yysize1;
+               yyfmt = yystpcpy (yyfmt, yyprefix);
+               yyprefix = yyor;
+             }
+
+         yyf = YY_(yyformat);
+         yysize1 = yysize + yystrlen (yyf);
+         yysize_overflow |= yysize1 < yysize;
+         yysize = yysize1;
+
+         if (!yysize_overflow && yysize <= YYSTACK_ALLOC_MAXIMUM)
+           yymsg = (char *) YYSTACK_ALLOC (yysize);
+         if (yymsg)
+           {
+             /* Avoid sprintf, as that infringes on the user's name space.
+                Don't have undefined behavior even if the translation
+                produced a string with the wrong number of "%s"s.  */
+             char *yyp = yymsg;
+             int yyi = 0;
+             while ((*yyp = *yyf))
+               {
+                 if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+                   {
+                     yyp += yytnamerr (yyp, yyarg[yyi++]);
+                     yyf += 2;
+                   }
+                 else
+                   {
+                     yyp++;
+                     yyf++;
+                   }
+               }
+             yyerror (yymsg);
+             YYSTACK_FREE (yymsg);
+           }
+         else
+           {
+             yyerror (YY_("syntax error"));
+             goto yyexhaustedlab;
+           }
+       }
+      else
+#endif /* YYERROR_VERBOSE */
+       yyerror (YY_("syntax error"));
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+         /* Return failure if at end of input.  */
+         if (yychar == YYEOF)
+           YYABORT;
+        }
+      else
+       {
+         yydestruct ("Error: discarding", yytoken, &yylval);
+         yychar = YYEMPTY;
+       }
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (0)
+     goto yyerrorlab;
+
+yyvsp -= yylen;
+  yyssp -= yylen;
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+
+      yydestruct ("Error: popping", yystos[yystate], yyvsp);
+      YYPOPSTACK;
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token. */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+                yytoken, &yylval);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                 yystos[*yyssp], yyvsp);
+      YYPOPSTACK;
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+
+
+
+void conf_parse(const char *name)
+{
+       struct symbol *sym;
+       int i;
+
+       zconf_initscan(name);
+
+       sym_init();
+       menu_init();
+       modules_sym = sym_lookup(NULL, 0);
+       modules_sym->type = S_BOOLEAN;
+       modules_sym->flags |= SYMBOL_AUTO;
+       rootmenu.prompt = menu_add_prompt(P_MENU, PROJECT_NAME" Configuration", NULL);
+
+#if YYDEBUG
+       if (getenv("ZCONF_DEBUG"))
+               zconfdebug = 1;
+#endif
+       zconfparse();
+       if (zconfnerrs)
+               exit(1);
+       if (!modules_sym->prop) {
+               struct property *prop;
+
+               prop = prop_alloc(P_DEFAULT, modules_sym);
+               prop->expr = expr_alloc_symbol(sym_lookup("MODULES", 0));
+       }
+       menu_finalize(&rootmenu);
+       for_all_symbols(i, sym) {
+               sym_check_deps(sym);
+        }
+
+       sym_change_count = 1;
+}
+
+const char *zconf_tokenname(int token)
+{
+       switch (token) {
+       case T_MENU:            return "menu";
+       case T_ENDMENU:         return "endmenu";
+       case T_CHOICE:          return "choice";
+       case T_ENDCHOICE:       return "endchoice";
+       case T_IF:              return "if";
+       case T_ENDIF:           return "endif";
+       case T_DEPENDS:         return "depends";
+       }
+       return "<token>";
+}
+
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
+{
+       if (id->token != endtoken) {
+               zconf_error("unexpected '%s' within %s block",
+                       kconf_id_strings + id->name, zconf_tokenname(starttoken));
+               zconfnerrs++;
+               return false;
+       }
+       if (current_menu->file != current_file) {
+               zconf_error("'%s' in different file than '%s'",
+                       kconf_id_strings + id->name, zconf_tokenname(starttoken));
+               fprintf(stderr, "%s:%d: location of the '%s'\n",
+                       current_menu->file->name, current_menu->lineno,
+                       zconf_tokenname(starttoken));
+               zconfnerrs++;
+               return false;
+       }
+       return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+       va_list ap;
+
+       fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+       va_start(ap, err);
+       vfprintf(stderr, err, ap);
+       va_end(ap);
+       fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+       va_list ap;
+
+       zconfnerrs++;
+       fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+       va_start(ap, err);
+       vfprintf(stderr, err, ap);
+       va_end(ap);
+       fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+#if YYDEBUG
+       fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+#endif
+}
+
+void print_quoted_string(FILE *out, const char *str)
+{
+       const char *p;
+       int len;
+
+       putc('"', out);
+       while ((p = strchr(str, '"'))) {
+               len = p - str;
+               if (len)
+                       fprintf(out, "%.*s", len, str);
+               fputs("\\\"", out);
+               str = p + 1;
+       }
+       fputs(str, out);
+       putc('"', out);
+}
+
+void print_symbol(FILE *out, struct menu *menu)
+{
+       struct symbol *sym = menu->sym;
+       struct property *prop;
+
+       if (sym_is_choice(sym))
+               fprintf(out, "choice\n");
+       else
+               fprintf(out, "config %s\n", sym->name);
+       switch (sym->type) {
+       case S_BOOLEAN:
+               fputs("  boolean\n", out);
+               break;
+       case S_TRISTATE:
+               fputs("  tristate\n", out);
+               break;
+       case S_STRING:
+               fputs("  string\n", out);
+               break;
+       case S_INT:
+               fputs("  integer\n", out);
+               break;
+       case S_HEX:
+               fputs("  hex\n", out);
+               break;
+       default:
+               fputs("  ???\n", out);
+               break;
+       }
+       for (prop = sym->prop; prop; prop = prop->next) {
+               if (prop->menu != menu)
+                       continue;
+               switch (prop->type) {
+               case P_PROMPT:
+                       fputs("  prompt ", out);
+                       print_quoted_string(out, prop->text);
+                       if (!expr_is_yes(prop->visible.expr)) {
+                               fputs(" if ", out);
+                               expr_fprint(prop->visible.expr, out);
+                       }
+                       fputc('\n', out);
+                       break;
+               case P_DEFAULT:
+                       fputs( "  default ", out);
+                       expr_fprint(prop->expr, out);
+                       if (!expr_is_yes(prop->visible.expr)) {
+                               fputs(" if ", out);
+                               expr_fprint(prop->visible.expr, out);
+                       }
+                       fputc('\n', out);
+                       break;
+               case P_CHOICE:
+                       fputs("  #choice value\n", out);
+                       break;
+               default:
+                       fprintf(out, "  unknown prop %d!\n", prop->type);
+                       break;
+               }
+       }
+       if (sym->help) {
+               int len = strlen(sym->help);
+               while (sym->help[--len] == '\n')
+                       sym->help[len] = 0;
+               fprintf(out, "  help\n%s\n", sym->help);
+       }
+       fputc('\n', out);
+}
+
+void zconfdump(FILE *out)
+{
+       struct property *prop;
+       struct symbol *sym;
+       struct menu *menu;
+
+       menu = rootmenu.list;
+       while (menu) {
+               if ((sym = menu->sym))
+                       print_symbol(out, menu);
+               else if ((prop = menu->prompt)) {
+                       switch (prop->type) {
+                       case P_COMMENT:
+                               fputs("\ncomment ", out);
+                               print_quoted_string(out, prop->text);
+                               fputs("\n", out);
+                               break;
+                       case P_MENU:
+                               fputs("\nmenu ", out);
+                               print_quoted_string(out, prop->text);
+                               fputs("\n", out);
+                               break;
+                       default:
+                               ;
+                       }
+                       if (!expr_is_yes(prop->visible.expr)) {
+                               fputs("  depends ", out);
+                               expr_fprint(prop->visible.expr, out);
+                               fputc('\n', out);
+                       }
+                       fputs("\n", out);
+               }
+
+               if (menu->list)
+                       menu = menu->list;
+               else if (menu->next)
+                       menu = menu->next;
+               else while ((menu = menu->parent)) {
+                       if (menu->prompt && menu->prompt->type == P_MENU)
+                               fputs("\nendmenu\n", out);
+                       if (menu->next) {
+                               menu = menu->next;
+                               break;
+                       }
+               }
+       }
+}
+
+#include "lex.zconf.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
+
+
diff --git a/lib/args.c b/lib/args.c
new file mode 100644 (file)
index 0000000..e4dcbcb
--- /dev/null
@@ -0,0 +1,449 @@
+/* args.c - Command line argument parsing.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+// Design goals:
+//   Don't use getopt()
+//   Don't permute original arguments.
+//   handle --long gracefully "(noshort)a(along)b(blong1)(blong2)"
+//   After each argument:
+//       Note that pointer and long are always the same size, even on 64 bit.
+//     : plus a string argument, keep most recent if more than one
+//     * plus a string argument, appended to a list
+//     # plus a signed long argument
+//       <LOW     - die if less than LOW
+//       >HIGH    - die if greater than HIGH
+//       =DEFAULT - value if not specified
+//     - plus a signed long argument defaulting to negative
+//     . plus a double precision floating point argument (with CFG_TOYBOX_FLOAT)
+//       Chop this out with USE_TOYBOX_FLOAT() around option string
+//       Same <LOW>HIGH=DEFAULT as #
+//     @ plus an occurrence counter (which is a long)
+//     (longopt)
+//     | this is required.  If more than one marked, only one required. TODO
+//     ^ Stop parsing after encountering this argument
+//    " " (space char) the "plus an  argument" must be separate
+//        I.E. "-j 3" not "-j3". So "kill -stop" != "kill -s top"
+//
+//   at the beginning:
+//     ^ stop at first nonoption argument
+//     <0 die if less than # leftover arguments (default 0)
+//     >9 die if > # leftover arguments (default MAX_INT)
+//     ? Allow unknown arguments (pass them through to command).
+//     & first argument has imaginary dash (ala tar/ps)
+//       If given twice, all arguments have imaginary dash
+//
+//   At the end: [groups] of previously seen options
+//     - Only one in group (switch off)    [-abc] means -ab=-b, -ba=-a, -abc=-c
+//     | Synonyms (switch on all)          [|abc] means -ab=-abc, -c=-abc
+//     ! More than one in group is error   [!abc] means -ab calls error_exit()
+//     + First in group switches rest on   [+abc] means -a=-abc, -b=-b, -c=-c
+//       primarily useful if you can switch things back off again.
+//     
+
+// Notes from getopt man page
+//   - and -- cannot be arguments.
+//     -- force end of arguments
+//     - is a synonym for stdin in file arguments
+//   -abc means -a -b -c
+
+/* This uses a getopt-like option string, but not getopt() itself. We call
+ * it the get_opt string.
+ *
+ * Each option in the get_opt string corresponds to a bit position in the
+ * return value. The rightmost argument is (1<<0), the next to last is (1<<1)
+ * and so on. If the option isn't seen in argv[], its bit remains 0.
+ *
+ * Options which have an argument fill in the corresponding slot in the global
+ * union "this" (see generated/globals.h), which it treats as an array of longs
+ * (note that sizeof(long)==sizeof(pointer) is guaranteed by LP64).
+ *
+ * You don't have to free the option strings, which point into the environment
+ * space. List objects should be freed by main() when command_main() returns.
+ *
+ * Example:
+ *   Calling get_optflags() when toys.which->options="ab:c:d" and
+ *   argv = ["command", "-b", "fruit", "-d", "walrus"] results in:
+ *
+ *     Changes to struct toys:
+ *       toys.optflags = 5  (-b=4 | -d=1)
+ *       toys.optargs[0]="walrus" (leftover argument)
+ *       toys.optargs[1]=NULL (end of list)
+ *       toys.optc=1 (there was 1 leftover argument)
+ *
+ *     Changes to union this:
+ *       this[0]=NULL (because -c didn't get an argument this time)
+ *       this[1]="fruit" (argument to -b)
+ */
+
+// Linked list of all known options (option string parsed into this).
+struct opts {
+  struct opts *next;
+  long *arg;         // Pointer into union "this" to store arguments at.
+  int c;             // Argument character to match
+  int flags;         // |=1, ^=2
+  unsigned dex[3];   // which bits to disable/enable/exclude in toys.optflags
+  char type;         // Type of arguments to store union "this"
+  union {
+    long l;
+    FLOAT f;
+  } val[3];          // low, high, default - range of allowed values
+};
+
+struct longopts {
+  struct longopts *next;
+  struct opts *opt;
+  char *str;
+  int len;
+};
+
+// State during argument parsing.
+struct getoptflagstate
+{
+  int argc, minargs, maxargs, nodash;
+  char *arg;
+  struct opts *opts;
+  struct longopts *longopts;
+  int noerror, nodash_now, stopearly;
+  unsigned excludes;
+};
+
+// Use getoptflagstate to parse parse one command line option from argv
+static int gotflag(struct getoptflagstate *gof, struct opts *opt)
+{
+  int type;
+
+  // Did we recognize this option?
+  if (!opt) {
+    if (gof->noerror) return 1;
+    error_exit("Unknown option %s", gof->arg);
+  }
+
+  // Set flags
+  toys.optflags &= ~opt->dex[0];
+  toys.optflags |= opt->dex[1];
+  gof->excludes |= opt->dex[2];
+  if (opt->flags&2) gof->stopearly=2;
+
+  if (toys.optflags & gof->excludes) {
+    struct opts *bad;
+    unsigned i = 1;
+
+    for (bad=gof->opts, i=1; ;bad = bad->next, i<<=1) {
+      if (opt == bad || !(i & toys.optflags)) continue;
+      if (toys.optflags & bad->dex[2]) break;
+    }
+    error_exit("No '%c' with '%c'", opt->c, bad->c);
+  }
+
+  // Does this option take an argument?
+  gof->arg++;
+  type = opt->type;
+  if (type) {
+    char *arg = gof->arg;
+
+    // Handle "-xblah" and "-x blah", but also a third case: "abxc blah"
+    // to make "tar xCjfv blah1 blah2 thingy" work like
+    // "tar -x -C blah1 -j -f blah2 -v thingy"
+
+    if (gof->nodash_now || !arg[0]) arg = toys.argv[++gof->argc];
+    // TODO: The following line doesn't display --longopt correctly
+    if (!arg) error_exit("Missing argument to -%c", opt->c);
+
+    if (type == ':') *(opt->arg) = (long)arg;
+    else if (type == '*') {
+      struct arg_list **list;
+
+      list = (struct arg_list **)opt->arg;
+      while (*list) list=&((*list)->next);
+      *list = xzalloc(sizeof(struct arg_list));
+      (*list)->arg = arg;
+    } else if (type == '#' || type == '-') {
+      long l = atolx(arg);
+      if (type == '-' && !ispunct(*arg)) l*=-1;
+      if (l < opt->val[0].l) error_exit("-%c < %ld", opt->c, opt->val[0].l);
+      if (l > opt->val[1].l) error_exit("-%c > %ld", opt->c, opt->val[1].l);
+
+      *(opt->arg) = l;
+    } else if (CFG_TOYBOX_FLOAT && type == '.') {
+      FLOAT *f = (FLOAT *)(opt->arg);
+
+      *f = strtod(arg, &arg);
+      if (opt->val[0].l != LONG_MIN && *f < opt->val[0].f)
+        error_exit("-%c < %lf", opt->c, (double)opt->val[0].f);
+      if (opt->val[1].l != LONG_MAX && *f > opt->val[1].f)
+        error_exit("-%c > %lf", opt->c, (double)opt->val[1].f);
+    } else if (type == '@') ++*(opt->arg);
+
+    if (!gof->nodash_now) gof->arg = "";
+  }
+
+  return 0;
+}
+
+// Parse this command's options string into struct getoptflagstate, which
+// includes a struct opts linked list in reverse order (I.E. right-to-left)
+void parse_optflaglist(struct getoptflagstate *gof)
+{
+  char *options = toys.which->options;
+  long *nextarg = (long *)&this;
+  struct opts *new = 0;
+  int idx;
+
+  // Parse option format string
+  memset(gof, 0, sizeof(struct getoptflagstate));
+  gof->maxargs = INT_MAX;
+  if (!options) return;
+
+  // Parse leading special behavior indicators
+  for (;;) {
+    if (*options == '^') gof->stopearly++;
+    else if (*options == '<') gof->minargs=*(++options)-'0';
+    else if (*options == '>') gof->maxargs=*(++options)-'0';
+    else if (*options == '?') gof->noerror++;
+    else if (*options == '&') gof->nodash++;
+    else break;
+    options++;
+  }
+
+  // Parse option string into a linked list of options with attributes.
+
+  if (!*options) gof->stopearly++;
+  while (*options) {
+    char *temp;
+
+    // Option groups come after all options are defined
+    if (*options == '[') break;
+
+    // Allocate a new list entry when necessary
+    if (!new) {
+      new = xzalloc(sizeof(struct opts));
+      new->next = gof->opts;
+      gof->opts = new;
+      new->val[0].l = LONG_MIN;
+      new->val[1].l = LONG_MAX;
+    }
+    // Each option must start with "(" or an option character.  (Bare
+    // longopts only come at the start of the string.)
+    if (*options == '(') {
+      char *end;
+      struct longopts *lo = xmalloc(sizeof(struct longopts));
+
+      // Find the end of the longopt
+      for (end = ++options; *end && *end != ')'; end++);
+      if (CFG_TOYBOX_DEBUG && !*end) error_exit("(longopt) didn't end");
+
+      // init a new struct longopts
+      lo->next = gof->longopts;
+      lo->opt = new;
+      lo->str = options;
+      lo->len = end-options;
+      gof->longopts = lo;
+      options = end;
+
+      // Mark this struct opt as used, even when no short opt.
+      if (!new->c) {
+        new->c = -1;
+        new = 0;
+      }
+
+    // If this is the start of a new option that wasn't a longopt,
+
+    } else if (strchr(":*#@.-", *options)) {
+      if (CFG_TOYBOX_DEBUG && new->type)
+        error_exit("multiple types %c:%c%c", new->c, new->type, *options);
+      new->type = *options;
+    } else if (-1 != (idx = stridx("|^ ", *options))) new->flags |= 1<<idx;
+    // bounds checking
+    else if (-1 != (idx = stridx("<>=", *options))) {
+      if (new->type == '#') {
+        long l = strtol(++options, &temp, 10);
+        if (temp != options) new->val[idx].l = l;
+      } else if (CFG_TOYBOX_FLOAT && new->type == '.') {
+        FLOAT f = strtod(++options, &temp);
+        if (temp != options) new->val[idx].f = f;
+      } else if (CFG_TOYBOX_DEBUG) error_exit("<>= only after .#");
+      options = --temp;
+    }
+
+    // At this point, we've hit the end of the previous option.  The
+    // current character is the start of a new option.  If we've already
+    // assigned an option to this struct, loop to allocate a new one.
+    // (It'll get back here afterwards and fall through to next else.)
+    else if (new->c) {
+      new = NULL;
+      continue;
+
+    // Claim this option, loop to see what's after it.
+    } else new->c = *options;
+
+    options++;
+  }
+
+  // Initialize enable/disable/exclude masks and pointers to store arguments.
+  // (This goes right to left so we need the whole list before we can start.)
+  idx = 0;
+  for (new = gof->opts; new; new = new->next) {
+    new->dex[1] = 1<<idx++;
+    if (new->type) {
+      new->arg = (void *)nextarg;
+      *(nextarg++) = new->val[2].l;
+    }
+  }
+
+  // Parse trailing group indicators
+  while (*options) {
+    unsigned bits = 0;
+
+    if (CFG_TOYBOX_DEBUG && *options != '[') error_exit("trailing %s", options);
+
+    idx = stridx("-|!+", *++options);
+    if (CFG_TOYBOX_DEBUG && idx == -1) error_exit("[ needs +-!");
+
+    // Don't advance past ] but do process it once in loop.
+    while (*(options++) != ']') {
+      struct opts *opt, *opt2 = 0;
+      int i;
+
+      if (CFG_TOYBOX_DEBUG && !*options) error_exit("[ without ]");
+      // Find this option flag (in previously parsed struct opt)
+      for (i=0, opt = gof->opts; ; i++, opt = opt->next) {
+        if (*options == ']') {
+          if (!opt) break;
+          if (idx == 3) {
+            opt2->dex[1] |= bits;
+            break;
+          }
+          if (bits&(1<<i)) opt->dex[idx] |= bits&~(1<<i);
+        } else {
+          if (CFG_TOYBOX_DEBUG && !opt)
+            error_exit("[] unknown target %c", *options);
+          if (opt->c == *options) {
+            bits |= 1<<i;
+            if (!opt2) opt2=opt;
+            break;
+          }
+        }
+      }
+    }
+  }
+}
+
+// Fill out toys.optflags, toys.optargs, and this[] from toys.argv
+
+void get_optflags(void)
+{
+  struct getoptflagstate gof;
+  struct opts *catch;
+  long saveflags;
+  char *letters[]={"s",""};
+
+  // Option parsing is a two stage process: parse the option string into
+  // a struct opts list, then use that list to process argv[];
+
+  toys.exithelp++;
+  // Allocate memory for optargs
+  saveflags = 0;
+  while (toys.argv[saveflags++]);
+  toys.optargs = xzalloc(sizeof(char *)*saveflags);
+
+  parse_optflaglist(&gof);
+
+  // Iterate through command line arguments, skipping argv[0]
+  for (gof.argc=1; toys.argv[gof.argc]; gof.argc++) {
+    gof.arg = toys.argv[gof.argc];
+    catch = NULL;
+
+    // Parse this argument
+    if (gof.stopearly>1) goto notflag;
+
+    gof.nodash_now = 0;
+
+    // Various things with dashes
+    if (*gof.arg == '-') {
+
+      // Handle -
+      if (!gof.arg[1]) goto notflag;
+      gof.arg++;
+      if (*gof.arg=='-') {
+        struct longopts *lo;
+
+        gof.arg++;
+        // Handle --
+        if (!*gof.arg) {
+          gof.stopearly += 2;
+          continue;
+        }
+        // Handle --longopt
+
+        for (lo = gof.longopts; lo; lo = lo->next) {
+          if (!strncmp(gof.arg, lo->str, lo->len)) {
+            if (gof.arg[lo->len]) {
+              if (gof.arg[lo->len]=='=' && lo->opt->type) gof.arg += lo->len;
+              else continue;
+            }
+            // It's a match.
+//            gof.arg = "";
+            catch = lo->opt;
+            break;
+          }
+        }
+
+        // Should we handle this --longopt as a non-option argument?
+        if (!lo && gof.noerror) {
+          gof.arg-=2;
+          goto notflag;
+        }
+
+        // Long option parsed, handle option.
+        gotflag(&gof, catch);
+        continue;
+      }
+
+    // Handle things that don't start with a dash.
+    } else {
+      if (gof.nodash && (gof.nodash>1 || gof.argc == 1)) gof.nodash_now = 1;
+      else goto notflag;
+    }
+
+    // At this point, we have the args part of -args.  Loop through
+    // each entry (could be -abc meaning -a -b -c)
+    saveflags = toys.optflags;
+    while (*gof.arg) {
+
+      // Identify next option char.
+      for (catch = gof.opts; catch; catch = catch->next)
+        if (*gof.arg == catch->c)
+          if (!((catch->flags&4) && gof.arg[1])) break;
+
+      // Handle option char (advancing past what was used)
+      if (gotflag(&gof, catch) ) {
+        toys.optflags = saveflags;
+        gof.arg = toys.argv[gof.argc];
+        goto notflag;
+      }
+    }
+    continue;
+
+    // Not a flag, save value in toys.optargs[]
+notflag:
+    if (gof.stopearly) gof.stopearly++;
+    toys.optargs[toys.optc++] = toys.argv[gof.argc];
+  }
+
+  // Sanity check
+  if (toys.optc<gof.minargs)
+    error_exit("Need%s %d argument%s", letters[!!(gof.minargs-1)],
+      gof.minargs, letters[!(gof.minargs-1)]);
+  if (toys.optc>gof.maxargs)
+    error_exit("Max %d argument%s", gof.maxargs, letters[!(gof.maxargs-1)]);
+  toys.exithelp = 0;
+
+  if (CFG_TOYBOX_FREE) {
+    llist_traverse(gof.opts, free);
+    llist_traverse(gof.longopts, free);
+  }
+}
diff --git a/lib/ash.h b/lib/ash.h
new file mode 100644 (file)
index 0000000..6c31948
--- /dev/null
+++ b/lib/ash.h
@@ -0,0 +1,1058 @@
+/* vi: set sw=4 ts=4:
+ *
+ * ash.h - ash header file.
+ *
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+*/
+
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *    This product includes software developed by the University of
+ *    California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+//==================================================================================
+//Header files.
+#include <stdio.h>  /* defines BUFSIZ */
+#include <string.h>
+#include "ash_syntax.h"
+
+#ifdef __STDC__
+# include "stdarg.h"
+#else
+# include <varargs.h>
+#endif
+
+#include <stdlib.h>
+
+#include "ash_shell.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include "ash_eval.h"
+#include "ash_jobs.h"
+
+#ifdef  BSD
+#undef BSD    /* temporary, already defined in <sys/param.h> */
+#include <sys/param.h>
+#include <unistd.h>
+#endif
+
+#ifndef BSD
+#define BSD
+#endif
+
+#if JOBS
+# include "sgtty.h"
+# undef CEOF      /* syntax.h redefines this */
+#endif
+
+#ifdef BSD
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+#include "ash_syntax.h"
+
+#define DEFINE_OPTIONS
+#include "ash_options.h"
+#undef DEFINE_OPTIONS
+
+
+#if ! DIRENT
+# include <dirent.h>
+#endif
+
+/* values returned by readtoken */
+#include "ash_token.def"
+#include <setjmp.h>
+#include <termios.h>
+
+//==================================================================================
+//defines.
+#define PROFILE 0
+
+#ifndef S_ISDIR        /* macro to test for directory file */
+#define  S_ISDIR(mode)    (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01    /* exit after evaluating tree */
+#define EV_TESTED 02    /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04    /* command executing within back quotes */
+
+/* reasons for skipping commands (see comment on breakcmd routine) */
+#define SKIPBREAK 1
+#define SKIPCONT 2
+#define SKIPFUNC 3
+
+#define CMDTABLESIZE 31    /* should be prime */
+#define ARB 1      /* actual size determined at run time */
+
+#define EOF_NLEFT -99    /* value of parsenleft when EOF pushed back */
+#define MAXMBOXES 10
+#define MINSIZE 504    /* minimum size of a block */
+#define EOFMARKLEN 79
+#ifdef EMPTY
+  #undef EMPTY
+  #define EMPTY -2    /* marks an unused slot in redirtab */
+#endif
+#define PIPESIZE 4096    /* amount of buffering in a pipe */
+#define S_DFL 1      /* default signal handling (SIG_DFL) */
+#define S_CATCH 2    /* signal is caught */
+#define S_IGN 3      /* signal is ignored (SIG_IGN) */
+#define S_HARD_IGN 4    /* signal is ignored permenantly */
+#define OUTBUFSIZ BUFSIZ
+#define BLOCK_OUT -2    /* output to a fixed block of memory */
+#define MEM_OUT -3    /* output to dynamically allocated memory */
+#define OUTPUT_ERR 01    /* error occurred on output */
+#define VTABSIZE 39
+
+//builtin commands.
+#define BLTINCMD 0
+#define BGCMD 1
+#define BREAKCMD 2
+#define CDCMD 3
+#define DOTCMD 4
+#define ECHOCMD 5
+#define EVALCMD 6
+#define EXECCMD 7
+#define EXITCMD 8
+#define EXPORTCMD 9
+#define FALSECMD 10
+#define FGCMD 11
+#define GETOPTSCMD 12
+#define HASHCMD 13
+#define JOBIDCMD 14
+#define JOBSCMD 15
+#define LOCALCMD 16
+#define PWDCMD 17
+#define READCMD 18
+#define RETURNCMD 19
+#define SETCMD 20
+#define SETVARCMD 21
+#define SHIFTCMD 22
+#define TRAPCMD 23
+#define TRUECMD 24
+#define UMASKCMD 25
+#define UNSETCMD 26
+#define WAITCMD 27
+#define TIMESCMD 28
+#define HELPCMD 29
+#define KILLCMD 30
+#define TESTCMD 31
+#define PRINTFCMD 32
+#define ALIASCMD 33
+#define UNALIASCMD 34
+#define TYPECMD 35
+#define ULIMITCMD 36
+#define HISTORYCMD 37
+#define EXPCMD 38
+
+/* values of cmdtype */
+#define CMDUNKNOWN -1    /* no entry in table for command */
+#define CMDNORMAL 0      /* command is an executable program */
+#define CMDBUILTIN 1    /* command is a shell builtin */
+#define CMDFUNCTION 2    /* command is a shell function */
+#define CMDSPLBLTIN 3     /* command is a special shell builtin */
+
+/* flags passed to redirect */
+#define REDIR_PUSH 01    /* save previous values of file descriptors */
+#define REDIR_BACKQ 02    /* save the command output in memory */
+
+/* flags */
+#define VEXPORT    01  /* variable is exported */
+#define VREADONLY  02  /* variable cannot be modified */
+#define VSTRFIXED  04  /* variable struct is staticly allocated */
+#define VTEXTFIXED  010  /* text is staticly allocated */
+#define VSTACK    020  /* text is allocated on the stack */
+#define VUNSET    040  /* the variable is not set */
+
+/*
+ * The following macros access the values of the above variables.
+ * They have to skip over the name.  They return the null string
+ * for unset variables.
+ */
+#define ifsval()  (vifs.text + 4)
+#define mailval()  (vmail.text + 5)
+#define mpathval()  (vmpath.text + 9)
+#define pathval()  (vpath.text + 5)
+#define ps1val()  (vps1.text + 4)
+#define ps2val()  (vps2.text + 4)
+#if ATTY
+#define termval()  (vterm.text + 5)
+#endif
+
+#if ATTY
+#define attyset()  ((vatty.flags & VUNSET) == 0)
+#endif
+#define mpathset()  ((vmpath.flags & VUNSET) == 0)
+
+/* control characters in argument strings */
+#define CTLESC '\201'
+#define CTLVAR '\202'
+#define CTLENDVAR '\203'
+#define CTLBACKQ '\204'
+#define CTLQUOTE 01    /* ored with CTLBACKQ code if in quotes */
+
+/* variable substitution byte (follows CTLVAR) */
+#define VSTYPE 0x0f     /* type of variable substitution */
+#define VSNUL 0x10     /* colon--treat the empty string as unset */
+#define VSQUOTE 0x80     /* inside double quotes--suppress splitting */
+
+/* values of VSTYPE field */
+#define VSNORMAL 1    /* normal variable:  $var or ${var} */
+#define VSMINUS 2    /* ${var-text} */
+#define VSPLUS 3    /* ${var+text} */
+#define VSQUESTION 4    /* ${var?message} */
+#define VSASSIGN 5    /* ${var=text} */
+#define VSTRIMRIGHT     0x6     /* ${var%pattern} */
+#define VSTRIMRIGHTMAX  0x7     /* ${var%%pattern} */
+#define VSTRIMLEFT      0x8     /* ${var#pattern} */
+#define VSTRIMLEFTMAX   0x9     /* ${var##pattern} */
+#define VSLENGTH        0xa     /* ${#var} */
+
+/*
+ * NEOF is returned by parsecmd when it encounters an end of file.  It
+ * must be distinct from NULL, so we use the address of a variable that
+ * happens to be handy.
+ */
+#define NEOF ((union node *)&tokpushback)
+
+#define NSEMI 0
+#define NCMD 1
+#define NPIPE 2
+#define NREDIR 3
+#define NBACKGND 4
+#define NSUBSHELL 5
+#define NAND 6
+#define NOR 7
+#define NIF 8
+#define NWHILE 9
+#define NUNTIL 10
+#define NFOR 11
+#define NCASE 12
+#define NCLIST 13
+#define NDEFUN 14
+#define NARG 15
+#define NTO 16
+#define NFROM 17
+#define NAPPEND 18
+#define NTOFD 19
+#define NFROMFD 20
+#define NHERE 21
+#define NXHERE 22
+#define NNOT 23
+
+#define pgetc_macro()  (--parsenleft >= 0? *parsenextc++ : preadbuffer())
+
+#define stackblock() stacknxt
+#define stackblocksize() stacknleft
+#define STARTSTACKSTR(p)  p = stackblock(), sstrnleft = stackblocksize()
+#define STPUTC(c, p)  (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
+#define CHECKSTRSPACE(n, p)  if (sstrnleft < n) p = makestrspace(); else
+#define USTPUTC(c, p)  (--sstrnleft, *p++ = (c))
+#define STACKSTRNUL(p)  (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STUNPUTC(p)  (++sstrnleft, --p)
+#define STTOPC(p)  p[-1]
+#define STADJUST(amount, p)  (p += (amount), sstrnleft -= (amount))
+#define grabstackstr(p)  stalloc(stackblocksize() - sstrnleft)
+
+#define ckfree(p)  free((pointer)(p))
+
+/*
+ * Types of operations (passed to the errmsg routine).
+ */
+#define E_OPEN 01  /* opening a file */
+#define E_CREAT 02  /* creating a file */
+#define E_EXEC 04  /* executing a program */
+
+/* exceptions */
+#define EXINT 0    /* SIGINT received */
+#define EXERROR 1  /* a generic error */
+#define EXSHELLPROC 2  /* execute a shell procedure */
+
+#define INTOFF suppressint++
+#define INTON if (--suppressint == 0 && intpending) onint(); else
+#define FORCEINTON {suppressint = 0; if (intpending) onint();}
+#define CLEAR_PENDING_INT intpending = 0
+#define int_pending() intpending
+
+#ifndef SYSV
+#define strchr mystrchr
+#endif
+
+#define equal(s1, s2)  (strcmp(s1, s2) == 0)
+#define scopy(s1, s2)  ((void)strcpy(s2, s1))
+#define bcopy(src, dst, n)  mybcopy((pointer)(src), (pointer)(dst), n)
+
+#ifndef ALIGN
+union align {
+  int i;
+  char *cp;
+};
+
+#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & (~(sizeof(union align) - 1)))
+#endif
+
+#define MAXSIG 31
+
+/*
+ * This file is included by programs which are optionally built into the
+ * shell.  If SHELL is defined, we try to map the standard UNIX library
+ * routines to ash routines using defines.
+ */
+
+#ifdef SHELL
+#define stdout out1
+#define stderr out2
+#define printf out1fmt
+#define putc(c, file)  outc(c, file)
+#define putchar(c)  out1c(c)
+#define fprintf outfmt
+#define fputs outstr
+#define fflush flushout
+#define INITARGS(argv)
+#else
+#define INITARGS(argv)  if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
+#endif
+#define ISDOTORDOTDOT(s) ((s)[0] == '.' && (!(s)[1] || ((s)[1] == '.' && !(s)[2])))
+
+#define EXP_FULL    0x1   /* perform word splitting & file globbing */
+#define EXP_TILDE     0x2   /* do normal tilde expansion */
+#define EXP_VARTILDE  0x4   /* expand tildes in an assignment */
+#define EXP_REDIR     0x8   /* file glob for a redirection (1 match only) */
+#define EXP_CASE    0x10  /* keeps quotes around for CASE pattern */
+#define EXP_IFS_SPLIT   0x20  /* need to record arguments for ifs breakup */
+
+//==================================================================================
+//struct.
+struct cmdentry {
+  int cmdtype;
+  union param {
+    int index;
+    union node *func;
+  } u;
+};
+
+struct tblentry {
+  struct tblentry *next;  /* next entry in hash chain */
+  union param param;  /* definition of builtin function */
+  short cmdtype;    /* index identifying command */
+  char rehash;    /* if set, cd done since entry created */
+  char cmdname[ARB];  /* name of command */
+};
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+struct ifsregion {
+  struct ifsregion *next;  /* next region in list */
+  int begoff;    /* offset of start of region */
+  int endoff;    /* offset of end of region */
+  int nulonly;    /* search for nul bytes only */
+};
+
+struct strpush {
+    struct strpush *prev;   /* preceding string on stack */
+    char *prevstring;
+    int prevnleft;
+    int prevlleft;
+    struct alias *ap;     /* if push was associated with an alias */
+};
+
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+struct parsefile {
+    int linno;        /* current line */
+    int fd;         /* file descriptor (or -1 if string) */
+    int nleft;        /* number of chars left in buffer */
+    char *nextc;      /* next char in buffer */
+    int lleft;        /* number of chars left in this line */
+    struct parsefile *prev; /* preceding file on stack */
+    char *buf;        /* input buffer */
+    struct strpush *strpush; /* for pushing strings at this level */
+    struct strpush basestrpush; /* so pushing one is fast */
+
+};
+
+/*
+ * Parse trees for commands are allocated in lifo order, so we use a stack
+ * to make this more efficient, and also to avoid all sorts of exception
+ * handling code to handle interrupts in the middle of a parse.
+ *
+ * The size 504 was chosen because the Ultrix malloc handles that size
+ * well.
+ */
+struct stack_block {
+  struct stack_block *prev;
+  char space[MINSIZE];
+};
+
+struct heredoc {
+  struct heredoc *next;  /* next here document in list */
+  union node *here;    /* redirection node */
+  char *eofmark;    /* string indicating end of input */
+  int striptabs;    /* if set, strip leading tabs */
+};
+
+struct redirtab {
+  struct redirtab *next;
+  short renamed[10];
+};
+
+struct varinit {
+  struct var *var;
+  int flags;
+  char *text;
+};
+
+struct strlist {
+  struct strlist *next;
+  char *text;
+};
+
+
+struct arglist {
+  struct strlist *list;
+  struct strlist **lastp;
+};
+
+struct builtincmd {
+    char *name;
+    int code;
+};
+
+struct var {
+  struct var *next;    /* next entry in hash list */
+  int flags;    /* flags are defined above */
+  char *text;    /* name=value */
+};
+
+
+struct localvar {
+  struct localvar *next;  /* next local variable in list */
+  struct var *vp;    /* the variable that was made local */
+  int flags;    /* saved flags */
+  char *text;    /* saved text */
+};
+
+
+struct nbinary {
+    int type;
+    union node *ch1;
+    union node *ch2;
+};
+
+
+struct ncmd {
+    int type;
+    int backgnd;
+    union node *args;
+    union node *redirect;
+};
+
+
+struct npipe {
+    int type;
+    int backgnd;
+    struct nodelist *cmdlist;
+};
+
+
+struct nredir {
+    int type;
+    union node *n;
+    union node *redirect;
+};
+
+
+struct nif {
+    int type;
+    union node *test;
+    union node *ifpart;
+    union node *elsepart;
+};
+
+struct nfor {
+    int type;
+    union node *args;
+    union node *body;
+    char *var;
+};
+
+struct ncase {
+    int type;
+    union node *expr;
+    union node *cases;
+};
+
+struct nclist {
+    int type;
+    union node *next;
+    union node *pattern;
+    union node *body;
+};
+
+struct narg {
+    int type;
+    union node *next;
+    char *text;
+    struct nodelist *backquote;
+};
+
+struct nfile {
+    int type;
+    union node *next;
+    int fd;
+    union node *fname;
+    char *expfname;
+};
+
+struct ndup {
+    int type;
+    union node *next;
+    int fd;
+    int dupfd;
+};
+
+struct nhere {
+    int type;
+    union node *next;
+    int fd;
+    union node *doc;
+};
+
+struct nnot {
+    int type;
+    union node *com;
+};
+
+union node {
+    int type;
+    struct nbinary nbinary;
+    struct ncmd ncmd;
+    struct npipe npipe;
+    struct nredir nredir;
+    struct nif nif;
+    struct nfor nfor;
+    struct ncase ncase;
+    struct nclist nclist;
+    struct narg narg;
+    struct nfile nfile;
+    struct ndup ndup;
+    struct nhere nhere;
+    struct nnot nnot;
+};
+
+struct nodelist {
+  struct nodelist *next;
+  union node *n;
+};
+
+struct stackmark {
+  struct stack_block *stackp;
+  char *stacknxt;
+  int stacknleft;
+};
+
+/*
+ * We enclose jmp_buf in a structure so that we can declare pointers to
+ * jump locations.  The global variable handler contains the location to
+ * jump to when an exception occurs, and the global variable exception
+ * contains a code identifying the exeception.  To implement nested
+ * exception handlers, the user should save the value of handler on entry
+ * to an inner scope, set handler to point to a jmploc structure for the
+ * inner scope, and restore handler on exit from the scope.
+ */
+struct jmploc {
+  jmp_buf loc;
+};
+//==================================================================================
+//Function declarations.
+#ifdef __STDC__
+void readcmdfile(char *);
+void cmdloop(int);
+STATIC void read_profile(char *);
+STATIC int docd(char *, int);
+STATIC void updatepwd(char *);
+STATIC void getpwd(void);
+STATIC char *getcomponent(void);
+STATIC void evalloop(union node *);
+STATIC void evalfor(union node *);
+STATIC void evalcase(union node *, int);
+STATIC void evalsubshell(union node *, int);
+STATIC void expredir(union node *);
+STATIC void evalpipe(union node *);
+STATIC void evalcommand(union node *, int, struct backcmd *);
+STATIC void prehash(union node *);
+STATIC void tryexec(char *, char **, char **);
+STATIC void execinterp(char **, char **);
+STATIC void printentry(struct tblentry *);
+STATIC void clearcmdentry(int);
+STATIC struct tblentry *cmdlookup(char *, int);
+STATIC void delete_cmd_entry(void);
+STATIC void argstr(char *, int);
+STATIC void expbackq(union node *, int, int);
+STATIC char *evalvar(char *, int);
+STATIC int varisset(int);
+STATIC void varvalue(int, int, int);
+STATIC void recordregion(int, int, int);
+STATIC void ifsbreakup(char *, struct arglist *);
+STATIC void expandmeta(struct strlist *);
+STATIC void expmeta(char *, char *);
+STATIC void addfname(char *);
+STATIC struct strlist *expsort(struct strlist *);
+STATIC struct strlist *msort(struct strlist *, int);
+STATIC int pmatch(char *, char *);
+STATIC void pushfile(void);
+STATIC void restartjob(struct job *);
+STATIC struct job *getjob(char *);
+STATIC void freejob(struct job *);
+STATIC int dowait(int, struct job *);
+STATIC int waitproc(int, int *);
+STATIC char *commandtext(union node *);
+STATIC void sh_options(int);
+STATIC void setoption(char, int);
+STATIC void openredirect(union node *, char *);
+STATIC int openhere(union node *);
+STATIC void calcsize(union node *);
+STATIC void sizenodelist(struct nodelist *);
+STATIC union node *copynode(union node *);
+STATIC struct nodelist *copynodelist(struct nodelist *);
+STATIC char *nodesavestr(char *);
+union node;
+void expandarg(union node *, struct arglist *, int);
+void expandhere(union node *, int);
+int patmatch(char *, char *);
+void rmescapes(char *);
+int casematch(union node *, char *);
+void shellexec(char **, char **, char *, int);
+char *padvance(char **, char *);
+void find_command(char *, struct cmdentry *, int);
+int find_builtin(char *);
+void hashcd(void);
+void changepath(char *);
+void defun(char *, union node *);
+void unsetfunc(char *);
+union node;
+STATIC void redirect(union node *, int);
+void popredir(void);
+void clearredir(void);
+int copyfd(int, int);
+int fd0_redirected_p(void);
+void initvar();
+void setvar(char *, char *, int);
+void setvareq(char *, int);
+struct strlist;
+void listsetvar(struct strlist *);
+char *lookupvar(char *);
+char *bltinlookup(char *, int);
+char **environment();
+int showvarscmd(int, char **);
+void mklocal(char *);
+void poplocalvars(void);
+void chkmail(int);
+union node *parsecmd(int);
+int goodname(char *);
+union node *copyfunc(union node *);
+void freefunc(union node *);
+char *pfgets(char *, int);
+static int pgetc(void);
+int preadbuffer(void);
+void pungetc(void);
+void ppushback(char *, int);
+void pushstring(char *, int , void *);
+void popstring(void);
+void setinputfile(char *, int);
+void setinputfd(int, int);
+void setinputstring(char *, int);
+void popfile(void);
+void popallfiles(void);
+void closescript(void);
+void clear_traps(void);
+int setsignal(int);
+void ignoresig(int);
+void dotrap(void);
+void setinteractive(int);
+void exitshell(int);
+pointer ckmalloc(int);
+pointer ckrealloc(pointer, int);
+void free(pointer);    /* defined in C library */
+char *savestr(char *);
+pointer stalloc(int);
+void stunalloc(pointer);
+void setstackmark(struct stackmark *);
+void popstackmark(struct stackmark *);
+void growstackblock(void);
+void grabstackblock(int);
+char *growstackstr(void);
+char *makestrspace(void);
+void ungrabstackstr(char *, char *);
+void exraise(int);
+static void onint(void);
+void error2(char *, char *);
+void sh_error(char *, ...);
+char *errmsg(int, int);
+void init(void);
+void reset(void);
+void initshellproc(void);
+void scopyn(const char *, char *, int);
+char *strchr(const char *, char);
+void mybcopy(const pointer, pointer, int);
+int prefix(const char *, const char *);
+int number(const char *);
+static int is_number(const char *);
+int strcmp(const char *, const char *);  /* from C library */
+char *strcpy(char *, const char *);  /* from C library */
+char *strcat(char *, const char *);  /* from C library */
+pointer stalloc(int);
+void error(char *, ...);
+int nextopt(char *optstring);
+int trputs(char *s);
+STATIC int peektoken();
+
+#else
+void readcmdfile();
+void cmdloop();
+STATIC void read_profile();
+char *getenv();
+STATIC int docd();
+STATIC void updatepwd();
+STATIC void getpwd();
+STATIC char *getcomponent();
+STATIC void evalloop();
+STATIC void evalfor();
+STATIC void evalcase();
+STATIC void evalsubshell();
+STATIC void expredir();
+STATIC void evalpipe();
+STATIC void evalcommand();
+STATIC void prehash();
+STATIC void tryexec();
+STATIC void execinterp();
+STATIC void printentry();
+STATIC void clearcmdentry();
+STATIC struct tblentry *cmdlookup();
+STATIC void delete_cmd_entry();
+STATIC void argstr();
+STATIC void expbackq();
+STATIC char *evalvar();
+STATIC int varisset();
+STATIC void varvalue();
+STATIC void recordregion();
+STATIC void ifsbreakup();
+STATIC void expandmeta();
+STATIC void expmeta();
+STATIC void addfname();
+STATIC struct strlist *expsort();
+STATIC struct strlist *msort();
+STATIC int pmatch();
+STATIC void pushfile();
+STATIC void restartjob();
+STATIC struct job *getjob();
+STATIC void freejob();
+STATIC int dowait();
+STATIC int waitproc();
+STATIC char *commandtext();
+STATIC void sh_options();
+STATIC void setoption();
+STATIC void openredirect();
+STATIC int openhere();
+STATIC void calcsize();
+STATIC void sizenodelist();
+STATIC union node *copynode();
+STATIC struct nodelist *copynodelist();
+STATIC char *nodesavestr();
+void expandarg();
+void expandhere();
+int patmatch();
+void rmescapes();
+int casematch();
+void shellexec();
+char *padvance();
+void find_command();
+int find_builtin();
+void hashcd();
+void changepath();
+void defun();
+void unsetfunc();
+STATIC void redirect();
+void popredir();
+void clearredir();
+int copyfd();
+int fd0_redirected_p();
+void initvar();
+void setvar();
+void setvareq();
+void listsetvar();
+char *lookupvar();
+char *bltinlookup();
+char **environment();
+int showvarscmd();
+void mklocal();
+void poplocalvars();
+void chkmail();
+union node *parsecmd();
+int goodname();
+union node *copyfunc();
+void freefunc();
+char *pfgets();
+static int pgetc();
+int preadbuffer();
+void pungetc();
+void ppushback();
+void pushstring();
+void popstring();
+void setinputfile();
+void setinputfd();
+void setinputstring();
+void popfile();
+void popallfiles();
+void closescript();
+void clear_traps();
+int setsignal();
+void ignoresig();
+void dotrap();
+void setinteractive();
+void exitshell();
+pointer ckmalloc();
+pointer ckrealloc();
+void free();    /* defined in C library */
+char *savestr();
+pointer stalloc();
+void stunalloc();
+void setstackmark();
+void popstackmark();
+void growstackblock();
+void grabstackblock();
+char *growstackstr();
+char *makestrspace();
+void ungrabstackstr();
+void exraise();
+static void onint();
+void error2();
+void sh_error();
+char *errmsg();
+void init();
+void reset();
+void initshellproc();
+void scopyn();
+char *strchr();
+void mybcopy();
+int prefix();
+int number();
+static int is_number();
+int strcmp();
+char *strcpy();
+int strlen();
+char *strcat();
+pointer stalloc();
+void error();
+int nextopt();
+int trputs();
+int peektoken();
+#endif
+
+#if UDIR
+#ifdef __STDC__
+STATIC char *expudir(char *);
+#else
+STATIC char *expudir();
+#endif
+#endif /* UDIR */
+
+STATIC union node *list __P((int));
+STATIC union node *andor __P((void));
+STATIC union node *pipeline __P((void));
+STATIC union node *command __P((void));
+STATIC union node *simplecmd __P((union node **, union node *));
+STATIC void parsefname __P((void));
+STATIC void parseheredoc __P((void));
+STATIC int readtoken __P((void));
+STATIC int readtoken1 __P((int, char const *, char *, int));
+STATIC int noexpand __P((char *));
+STATIC void synexpect __P((int));
+STATIC void synerror __P((char *));
+
+#if ATTY
+STATIC void putprompt __P((char *));
+#else /* not ATTY */
+#define putprompt(s)  out2str(s)
+#endif
+
+void shprocvar();
+int jobctl;
+void deletefuncs();
+
+//==================================================================================
+//output
+#ifndef OUTPUT_INCL
+struct output {
+  char *nextc;
+  int nleft;
+  char *buf;
+  int bufsize;
+  short fd;
+  short flags;
+};
+
+extern struct output output;
+extern struct output errout;
+extern struct output memout;
+extern struct output *out1;
+extern struct output *out2;
+
+#ifdef __STDC__
+void outstr(char *, struct output *);
+void out1str(char *);
+void out2str(char *);
+void outfmt(struct output *, char *, ...);
+void out1fmt(char *, ...);
+void fmtstr(char *, int, char *, ...);
+void doformat();
+void emptyoutbuf(struct output *);
+void flushall(void);
+void flushout(struct output *);
+void freestdout(void);
+int xxwrite(int, char *, int);
+#else
+void outstr();
+void out1str();
+void out2str();
+void outfmt();
+void out1fmt();
+void fmtstr();
+void doformat();
+void emptyoutbuf();
+void flushall();
+void flushout();
+void freestdout();
+int xxwrite();
+#endif
+
+#define outc(c, file)  (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
+#define out1c(c)  outc(c, out1);
+#define out2c(c)  outc(c, out2);
+
+#define OUTPUT_INCL
+#endif
+//==================================================================================
+//for alias
+#define ALIASINUSE 1
+struct alias {
+  struct alias *next;
+  char *name;
+  char *val;
+  int flag;
+};
+
+#define ATABSIZE 39
+struct alias *atab[ATABSIZE];
+
+struct alias *lookupalias(char *, int);
+char *get_alias_text(char *);
+void rmaliases(void);
+static void setalias(char *, char *);
+static int unalias(char *);
+static struct alias **hashalias(char *);
+//==================================================================================
+//ulimit command
+struct limits {
+  const char *name;
+  int  cmd;
+  int factor; /* multiply by to get rlim_{cur,max} values */
+  char option;
+};
+
+static const struct limits limits[] = {
+#ifdef RLIMIT_CPU
+    { "time(seconds)",      RLIMIT_CPU,    1, 't' },
+#endif
+#ifdef RLIMIT_FSIZE
+    { "file(blocks)",      RLIMIT_FSIZE,  512, 'f' },
+#endif
+#ifdef RLIMIT_DATA
+    { "data(kbytes)",       RLIMIT_DATA,  1024, 'd' },
+#endif
+#ifdef RLIMIT_STACK
+    { "stack(kbytes)",      RLIMIT_STACK,   1024, 's' },
+#endif
+#ifdef  RLIMIT_CORE
+    { "coredump(blocks)",     RLIMIT_CORE,   512, 'c' },
+#endif
+#ifdef RLIMIT_RSS
+    { "memory(kbytes)",     RLIMIT_RSS,   1024, 'm' },
+#endif
+#ifdef RLIMIT_MEMLOCK
+    { "locked memory(kbytes)",  RLIMIT_MEMLOCK, 1024, 'l' },
+#endif
+#ifdef RLIMIT_NPROC
+    { "process(processes)",   RLIMIT_NPROC,    1, 'p' },
+#endif
+#ifdef RLIMIT_NOFILE
+    { "nofiles(descriptors)",   RLIMIT_NOFILE,   1, 'n' },
+#endif
+#ifdef RLIMIT_VMEM
+    { "vmemory(kbytes)",    RLIMIT_VMEM,  1024, 'v' },
+#endif
+#ifdef RLIMIT_SWAP
+    { "swap(kbytes)",       RLIMIT_SWAP,  1024, 'w' },
+#endif
+    { (char *) 0,         0,         0,  '\0' }
+};
+//==================================================================================
+//For History
+#define NUM_OF_HISTORY_ITEMS 256
+void get_history(); //get cmds from file and fill in the history buff.
+void save_history(); //write history buff to file.
+void add_to_history(const char *p);
+char *search_in_history(int key);
+
+struct double_list *hist_list = NULL;
+struct double_list *key_list_ptr = NULL;
+struct double_list *p_list = NULL;
+struct double_list *path_list = NULL;
+int up_key_pressed = 0;
+int enter_flag = 1;
+//==================================================================================
diff --git a/lib/ash_arith.h b/lib/ash_arith.h
new file mode 100644 (file)
index 0000000..4e510e2
--- /dev/null
@@ -0,0 +1,40 @@
+/*  $NetBSD: arith.h,v 1.2 1997/07/04 21:01:49 christos Exp $  */
+
+/*-
+ * Copyright (c) 1995
+ *    The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  @(#)arith.h  1.1 (Berkeley) 5/4/95
+ */
+int expcmd __P((int , char **));
+void arith_lex_reset __P((void));
+static int yylex __P((void));
+
diff --git a/lib/ash_eval.h b/lib/ash_eval.h
new file mode 100644 (file)
index 0000000..cd85422
--- /dev/null
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  @(#)eval.h  5.2 (Berkeley) 4/12/91
+ *
+ *  $Header: /cvsroot-fuse/beastiebox/beastiebox/beastiebin/sh/eval.h,v 1.1.1.1 2008/11/27 21:05:32 imilh Exp $
+ */
+
+extern char *commandname;  /* currently executing command */
+extern int exitstatus;    /* exit status of last command */
+extern struct strlist *cmdenviron;  /* environment for builtin command */
+
+
+struct backcmd {    /* result of evalbackcmd */
+  int fd;      /* file descriptor to read from */
+  char *buf;    /* buffer */
+  int nleft;    /* number of chars in buffer */
+  struct job *jp;    /* job structure for command */
+};
+
+
+#ifdef __STDC__
+void evalstring(char *);
+union node;  /* BLETCH for ansi C */
+void evaltree(union node *, int);
+void evalbackcmd(union node *, struct backcmd *);
+#else
+void evalstring();
+void evaltree();
+void evalbackcmd();
+#endif
+
+/* in_function returns nonzero if we are currently evaluating a function */
+#define in_function()  funcnest
+extern int funcnest;
diff --git a/lib/ash_jobs.h b/lib/ash_jobs.h
new file mode 100644 (file)
index 0000000..addd5ff
--- /dev/null
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  @(#)jobs.h  5.1 (Berkeley) 3/7/91
+ *
+ *  $Header: /cvsroot-fuse/beastiebox/beastiebox/beastiebin/sh/jobs.h,v 1.1.1.1 2008/11/27 21:05:32 imilh Exp $
+ */
+
+/* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
+#define FORK_FG 0
+#define FORK_BG 1
+#define FORK_NOJOB 2
+
+
+/*
+ * A job structure contains information about a job.  A job is either a
+ * single process or a set of processes contained in a pipeline.  In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+
+struct procstat {
+  short pid;    /* process id */
+  short status;    /* status flags (defined above) */
+  char *cmd;    /* text of command being run */
+};
+
+
+/* states */
+#define JOBSTOPPED 1    /* all procs are stopped */
+#define JOBDONE 2    /* all procs are completed */
+
+
+struct job {
+  struct procstat ps0;  /* status of process */
+  struct procstat *ps;  /* status or processes when more than one */
+  short nprocs;    /* number of processes */
+  short pgrp;    /* process group of this job */
+  char state;    /* true if job is finished */
+  char used;    /* true if this entry is in used */
+  char changed;    /* true if status has changed */
+#if JOBS
+  char jobctl;    /* job running under job control */
+#endif
+};
+
+extern short backgndpid;  /* pid of last background process */
+
+
+#ifdef __STDC__
+void setjobctl(int);
+void showjobs(int);
+struct job *makejob(union node *, int);
+int forkshell(struct job *, union node *, int);
+int waitforjob(struct job *);
+#else
+void setjobctl();
+void showjobs();
+struct job *makejob();
+int forkshell();
+int waitforjob();
+#endif
+
+#if ! JOBS
+#define setjobctl(on)  /* do nothing */
+#endif
diff --git a/lib/ash_lexyyc b/lib/ash_lexyyc
new file mode 100644 (file)
index 0000000..514753c
--- /dev/null
@@ -0,0 +1,1969 @@
+
+#line 3 "lex.yy.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+//extern FILE *yyin, *yyout;
+static FILE *yyin;
+extern FILE *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               *yy_cp = (yy_hold_char); \
+               YY_RESTORE_YY_MORE_OFFSET \
+               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via yyrestart()), so that the user can continue scanning by
+        * just pointing yyin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file  );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
+void yy_delete_buffer (YY_BUFFER_STATE b  );
+void yy_flush_buffer (YY_BUFFER_STATE b  );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len  );
+
+void *yyalloc (yy_size_t  );
+void *yyrealloc (void *,yy_size_t  );
+void yyfree (void *  );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+static FILE *yyin = (FILE *) 0;
+FILE *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+       (yytext_ptr) = yy_bp; \
+       yyleng = (size_t) (yy_cp - yy_bp); \
+       (yy_hold_char) = *yy_cp; \
+       *yy_cp = '\0'; \
+       (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 38
+#define YY_END_OF_BUFFER 39
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+       {
+       flex_int32_t yy_verify;
+       flex_int32_t yy_nxt;
+       };
+static yyconst flex_int16_t yy_accept[47] =
+    {   0,
+        0,    0,   39,   37,    1,    1,   27,   23,   12,    6,
+        7,   21,   24,   25,   22,    3,    4,   17,   28,   15,
+        5,   11,   10,   26,   14,   35,    9,   36,   33,   30,
+       29,   32,   31,   34,    3,    0,    4,   19,   18,   13,
+       16,   20,    5,    8,    2,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    1,    1,    1,    5,    6,    1,    7,
+        8,    9,   10,    1,   11,    1,   12,   13,   14,   14,
+       14,   14,   14,   14,   14,   15,   15,    1,    1,   16,
+       17,   18,    1,    1,   19,   19,   19,   19,   19,   19,
+       20,   20,   20,   20,   20,   20,   20,   20,   20,   20,
+       20,   20,   20,   20,   20,   20,   20,   20,   20,   20,
+        1,    1,    1,   21,   20,    1,   19,   19,   19,   19,
+
+       19,   19,   20,   20,   20,   20,   20,   20,   20,   20,
+       20,   20,   20,   20,   20,   20,   20,   20,   20,   22,
+       20,   20,    1,   23,    1,   24,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[25] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    2,    2,    2,    1,    1,    1,    2,    3,
+        1,    3,    1,    1
+    } ;
+
+static yyconst flex_int16_t yy_base[49] =
+    {   0,
+        0,    0,   55,   56,   56,   56,   37,   36,   46,   56,
+       56,   16,   17,   15,   34,   15,   25,   14,   33,   18,
+        0,   56,   26,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   28,    0,   30,   56,   56,   56,
+       56,   56,    0,   56,    0,   56,   44,   46
+    } ;
+
+static yyconst flex_int16_t yy_def[49] =
+    {   0,
+       46,    1,   46,   46,   46,   46,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   46,   46,   46,   46,   46,
+       47,   46,   46,   46,   46,   46,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   48,   46,   46,   46,   46,
+       46,   46,   47,   46,   48,    0,   46,   46
+    } ;
+
+static yyconst flex_int16_t yy_nxt[81] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       14,   15,   16,   17,   17,   18,   19,   20,   21,   21,
+       22,   21,   23,   24,   28,   32,   30,   35,   35,   38,
+       39,   33,   29,   31,   41,   42,   36,   37,   37,   37,
+       35,   35,   37,   37,   37,   43,   43,   45,   44,   40,
+       34,   27,   26,   25,   46,    3,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   46,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   46,   46,   46,   46,   46
+    } ;
+
+static yyconst flex_int16_t yy_chk[81] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,   12,   14,   13,   16,   16,   18,
+       18,   14,   12,   13,   20,   20,   16,   17,   17,   17,
+       35,   35,   37,   37,   37,   47,   47,   48,   23,   19,
+       15,    9,    8,    7,    3,   46,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   46,   46,   46,   46,   46,
+       46,   46,   46,   46,   46,   46,   46,   46,   46,   46
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "arith_lex.l"
+#line 2 "arith_lex.l"
+/*     $NetBSD: arith_lex.l,v 1.16 2012/03/20 18:42:29 matt Exp $      */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+#include "ash_arith.h"
+#include "ash_ytab.h" 
+
+
+extern long yylval;
+extern const char *arith_buf, *arith_startbuf;
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max) \
+       result = (*buf = *arith_buf++) ? 1 : YY_NULL;
+#define YY_NO_INPUT 1
+#line 524 "lex.yy.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in (void );
+
+void yyset_in  (FILE * in_str  );
+
+FILE *yyget_out (void );
+
+void yyset_out  (FILE * out_str  );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ash_yywrap (void );
+#else
+extern int ash_yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+int ash_yywrap(void) { return(1); }
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+               { \
+               int c = '*'; \
+               unsigned n; \
+               for ( n = 0; n < max_size && \
+                            (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+                       buf[n] = (char) c; \
+               if ( c == '\n' ) \
+                       buf[n++] = (char) c; \
+               if ( c == EOF && ferror( yyin ) ) \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+               result = n; \
+               } \
+       else \
+               { \
+               errno=0; \
+               while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+                       { \
+                       if( errno != EINTR) \
+                               { \
+                               YY_FATAL_ERROR( "input in flex scanner failed" ); \
+                               break; \
+                               } \
+                       errno=0; \
+                       clearerr(yyin); \
+                       } \
+               }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+//extern int yylex (void);
+static int yylex(void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+    
+#line 51 "arith_lex.l"
+
+#line 706 "lex.yy.c"
+
+       if ( !(yy_init) )
+               {
+               (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! (yy_start) )
+                       (yy_start) = 1; /* first start state */
+
+               if ( ! yyin )
+                       yyin = stdin;
+
+               if ( ! yyout )
+                       yyout = stdout;
+
+               if ( ! YY_CURRENT_BUFFER ) {
+                       yyensure_buffer_stack ();
+                       YY_CURRENT_BUFFER_LVALUE =
+                               yy_create_buffer(yyin,YY_BUF_SIZE );
+               }
+
+               yy_load_buffer_state( );
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = (yy_c_buf_p);
+
+               /* Support of yytext. */
+               *yy_cp = (yy_hold_char);
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = (yy_start);
+yy_match:
+               do
+                       {
+                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+                       if ( yy_accept[yy_current_state] )
+                               {
+                               (yy_last_accepting_state) = yy_current_state;
+                               (yy_last_accepting_cpos) = yy_cp;
+                               }
+                       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                               {
+                               yy_current_state = (int) yy_def[yy_current_state];
+                               if ( yy_current_state >= 47 )
+                                       yy_c = yy_meta[(unsigned int) yy_c];
+                               }
+                       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+                       ++yy_cp;
+                       }
+               while ( yy_base[yy_current_state] != 56 );
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+               if ( yy_act == 0 )
+                       { /* have to back up */
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       yy_act = yy_accept[yy_current_state];
+                       }
+
+               YY_DO_BEFORE_ACTION;
+
+do_action:     /* This label is used only to access EOF actions. */
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+                       case 0: /* must back up */
+                       /* undo the effects of YY_DO_BEFORE_ACTION */
+                       *yy_cp = (yy_hold_char);
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 52 "arith_lex.l"
+{ ; }
+       YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 53 "arith_lex.l"
+{ yylval = strtoimax(yytext, 0, 0); return(ARITH_NUM); }
+       YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 54 "arith_lex.l"
+{ yylval = strtoimax(yytext, 0, 0); return(ARITH_NUM); }
+       YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 55 "arith_lex.l"
+{ yylval = strtoimax(yytext, 0, 0); return(ARITH_NUM); }
+       YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 56 "arith_lex.l"
+{ 
+                    char *v = lookupvar(yytext);
+                    if(!v) {
+                        char *val = "0";
+                        setvar(yytext, val, 0);
+                        v = lookupvar(yytext);
+                    }
+                    if (v) {
+                        yylval = (long)strdup(yytext);
+                        return ARITH_VAR;
+                    }
+                    sh_error("arith: syntax error: \"%s\"", arith_startbuf);
+                } 
+       YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 69 "arith_lex.l"
+{ return(ARITH_LPAREN); }
+       YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 70 "arith_lex.l"
+{ return(ARITH_RPAREN); }
+       YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 71 "arith_lex.l"
+{ return(ARITH_OR); }
+       YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 72 "arith_lex.l"
+{ return(ARITH_AND); }
+       YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 73 "arith_lex.l"
+{ return(ARITH_BOR); }
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 74 "arith_lex.l"
+{ return(ARITH_BXOR); }
+       YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 75 "arith_lex.l"
+{ return(ARITH_BAND); }
+       YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 76 "arith_lex.l"
+{ return(ARITH_EQ); }
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 77 "arith_lex.l"
+{ return(ARITH_NE); }
+       YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 78 "arith_lex.l"
+{ return(ARITH_GT); }
+       YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 79 "arith_lex.l"
+{ return(ARITH_GE); }
+       YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 80 "arith_lex.l"
+{ return(ARITH_LT); }
+       YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 81 "arith_lex.l"
+{ return(ARITH_LE); }
+       YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 82 "arith_lex.l"
+{ return(ARITH_LSHIFT); }
+       YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 83 "arith_lex.l"
+{ return(ARITH_RSHIFT); }
+       YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 84 "arith_lex.l"
+{ return(ARITH_MUL); }
+       YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 85 "arith_lex.l"
+{ return(ARITH_DIV); }
+       YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 86 "arith_lex.l"
+{ return(ARITH_REM); }
+       YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 87 "arith_lex.l"
+{ return(ARITH_ADD); }
+       YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 88 "arith_lex.l"
+{ return(ARITH_SUB); }
+       YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 89 "arith_lex.l"
+{ return(ARITH_BNOT); }
+       YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 90 "arith_lex.l"
+{ return(ARITH_NOT); }
+       YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 91 "arith_lex.l"
+{ return(ARITH_ASGN);}
+       YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 92 "arith_lex.l"
+{ return(ARITH_PLUS_ASGN);}
+       YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 93 "arith_lex.l"
+{ return(ARITH_PLUS_PLUS);}
+       YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 94 "arith_lex.l"
+{ return(ARITH_MINUS_ASGN);}
+       YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 95 "arith_lex.l"
+{ return(ARITH_MINUS_MINUS);}
+       YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 96 "arith_lex.l"
+{ return(ARITH_MUL_ASGN);}
+       YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 97 "arith_lex.l"
+{ return(ARITH_DIV_ASGN);}
+       YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 98 "arith_lex.l"
+{ return(ARITH_REM_ASGN);}
+       YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 99 "arith_lex.l"
+{ return(ARITH_POWER);}
+       YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 100 "arith_lex.l"
+{ sh_error("arith: syntax error: \"%s\"", arith_startbuf);}
+       YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 102 "arith_lex.l"
+ECHO;
+       YY_BREAK
+#line 992 "lex.yy.c"
+case YY_STATE_EOF(INITIAL):
+       yyterminate();
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = (yy_hold_char);
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed yyin at a new source and called
+                        * yylex().  If so, then we have to assure
+                        * consistency between YY_CURRENT_BUFFER and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state(  );
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++(yy_c_buf_p);
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = (yy_c_buf_p);
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer(  ) )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               (yy_did_buffer_switch_on_eof) = 0;
+
+                               if ( ash_yywrap( ) )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * yytext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               (yy_c_buf_p) =
+                                       (yytext_ptr) + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               (yy_c_buf_p) =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = (yytext_ptr);
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+       else
+               {
+                       int num_to_read =
+                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+                       int yy_c_buf_p_offset =
+                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+                                               number_to_move - 1;
+
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+                       (yy_n_chars), (size_t) num_to_read );
+
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       if ( (yy_n_chars) == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       yyrestart(yyin  );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+               /* Extend the array by 50%, plus the number we really need. */
+               yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+               YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+               if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+       }
+
+       (yy_n_chars) += number_to_move;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+       return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+    
+       yy_current_state = (yy_start);
+
+       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+               {
+               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+               if ( yy_accept[yy_current_state] )
+                       {
+                       (yy_last_accepting_state) = yy_current_state;
+                       (yy_last_accepting_cpos) = yy_cp;
+                       }
+               while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                       {
+                       yy_current_state = (int) yy_def[yy_current_state];
+                       if ( yy_current_state >= 47 )
+                               yy_c = yy_meta[(unsigned int) yy_c];
+                       }
+               yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+               }
+
+       return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+       register int yy_is_jam;
+       register char *yy_cp = (yy_c_buf_p);
+
+       register YY_CHAR yy_c = 1;
+       if ( yy_accept[yy_current_state] )
+               {
+               (yy_last_accepting_state) = yy_current_state;
+               (yy_last_accepting_cpos) = yy_cp;
+               }
+       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+               {
+               yy_current_state = (int) yy_def[yy_current_state];
+               if ( yy_current_state >= 47 )
+                       yy_c = yy_meta[(unsigned int) yy_c];
+               }
+       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+       yy_is_jam = (yy_current_state == 46);
+
+       return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+       int c;
+    
+       *(yy_c_buf_p) = (yy_hold_char);
+
+       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       /* This was really a NUL. */
+                       *(yy_c_buf_p) = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = (yy_c_buf_p) - (yytext_ptr);
+                       ++(yy_c_buf_p);
+
+                       switch ( yy_get_next_buffer(  ) )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       yyrestart(yyin );
+
+                                       /*FALLTHROUGH*/
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( ash_yywrap( ) )
+                                               return EOF;
+
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       (yy_c_buf_p) = (yytext_ptr) + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
+       *(yy_c_buf_p) = '\0';   /* preserve yytext */
+       (yy_hold_char) = *++(yy_c_buf_p);
+
+       return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+       if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+               YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE );
+       }
+
+       yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+       yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+       /* TODO. We should be able to replace this entire function body
+        * with
+        *              yypop_buffer_state();
+        *              yypush_buffer_state(new_buffer);
+     */
+       yyensure_buffer_stack ();
+       if ( YY_CURRENT_BUFFER == new_buffer )
+               return;
+
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+       yy_load_buffer_state( );
+
+       /* We don't actually know whether we did this switch during
+        * EOF (yywrap()) processing, but the only time this flag
+        * is looked at is after yywrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+       yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+       (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+       YY_BUFFER_STATE b;
+    
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       yy_init_buffer(b,file );
+
+       return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+       if ( ! b )
+               return;
+
+       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               yyfree((void *) b->yy_ch_buf  );
+
+       yyfree((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+       int oerrno = errno;
+    
+       yy_flush_buffer(b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+       errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == YY_CURRENT_BUFFER )
+               yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+       if (new_buffer == NULL)
+               return;
+
+       yyensure_buffer_stack();
+
+       /* This block is copied from yy_switch_to_buffer. */
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       /* Only push if top exists. Otherwise, replace top. */
+       if (YY_CURRENT_BUFFER)
+               (yy_buffer_stack_top)++;
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+       /* copied from yy_switch_to_buffer. */
+       yy_load_buffer_state( );
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+       if (!YY_CURRENT_BUFFER)
+               return;
+
+       yy_delete_buffer(YY_CURRENT_BUFFER );
+       YY_CURRENT_BUFFER_LVALUE = NULL;
+       if ((yy_buffer_stack_top) > 0)
+               --(yy_buffer_stack_top);
+
+       if (YY_CURRENT_BUFFER) {
+               yy_load_buffer_state( );
+               (yy_did_buffer_switch_on_eof) = 1;
+       }
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+       int num_to_alloc;
+    
+       if (!(yy_buffer_stack)) {
+
+               /* First allocation is just for 2 elements, since we don't know if this
+                * scanner will even need a stack. We use 2 instead of 1 to avoid an
+                * immediate realloc on the next call.
+         */
+               num_to_alloc = 1;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+                                                                 
+               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+                               
+               (yy_buffer_stack_max) = num_to_alloc;
+               (yy_buffer_stack_top) = 0;
+               return;
+       }
+
+       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+               /* Increase the buffer to prepare for a possible push. */
+               int grow_size = 8 /* arbitrary grow size */;
+
+               num_to_alloc = (yy_buffer_stack_max) + grow_size;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+                                                               ((yy_buffer_stack),
+                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+               /* zero only the new slots.*/
+               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+               (yy_buffer_stack_max) = num_to_alloc;
+       }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+       YY_BUFFER_STATE b;
+    
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       yy_switch_to_buffer(b  );
+
+       return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+    
+       return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+    
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = _yybytes_len + 2;
+       buf = (char *) yyalloc(n  );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+       for ( i = 0; i < _yybytes_len; ++i )
+               buf[i] = yybytes[i];
+
+       buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = yy_scan_buffer(buf,n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               yytext[yyleng] = (yy_hold_char); \
+               (yy_c_buf_p) = yytext + yyless_macro_arg; \
+               (yy_hold_char) = *(yy_c_buf_p); \
+               *(yy_c_buf_p) = '\0'; \
+               yyleng = yyless_macro_arg; \
+               } \
+       while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+        
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void yyset_lineno (int  line_number )
+{
+    
+    yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str )
+{
+        yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str )
+{
+        yyout = out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug )
+{
+        yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+       while(YY_CURRENT_BUFFER){
+               yy_delete_buffer(YY_CURRENT_BUFFER  );
+               YY_CURRENT_BUFFER_LVALUE = NULL;
+               yypop_buffer_state();
+       }
+
+       /* Destroy the stack itself. */
+       yyfree((yy_buffer_stack) );
+       (yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+       return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+       free( (char *) ptr );   /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 102 "arith_lex.l"
+
+
+
+void arith_lex_reset(void) 
+{
+#ifdef YY_NEW_FILE
+       YY_NEW_FILE;
+#endif
+}
+
+
diff --git a/lib/ash_options.h b/lib/ash_options.h
new file mode 100644 (file)
index 0000000..d17b8d2
--- /dev/null
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  @(#)options.h  5.1 (Berkeley) 3/7/91
+ *
+ *  $Header: /cvsroot-fuse/beastiebox/beastiebox/beastiebin/sh/options.h,v 1.1.1.1 2008/11/27 21:05:31 imilh Exp $
+ */
+
+struct shparam {
+  int nparam;  /* number of positional parameters (without $0) */
+  char malloc;  /* true if parameter list dynamicly allocated */
+  char **p;    /* parameter list */
+  char **optnext;  /* next parameter to be processed by getopts */
+  char *optptr;  /* used by getopts */
+};
+#define eflag optval[0]
+#define fflag optval[1]
+#define Iflag optval[2]
+#define iflag optval[3]
+#define jflag optval[4]
+#define nflag optval[5]
+#define sflag optval[6]
+#define xflag optval[7]
+#define zflag optval[8]
+#define vflag optval[9]
+#define aflag optval[10]
+#define Cflag optval[11]
+#define uflag optval[12]
+#define bflag optval[13]
+#define mflag optval[14]
+#define lflag optval[15] //For workaround to read /etc/profile
+
+#define NOPTS 20
+
+#ifdef DEFINE_OPTIONS
+const char optchar[NOPTS+1] = "efIijnsxzvaCubml";     /* shell flags */
+char optval[NOPTS+1];       /* values of option flags */
+#else
+extern const char optchar[NOPTS+1];
+extern char optval[NOPTS+1];
+#endif
+
+struct optent {
+    const char *name;         /* for set -o <name> */
+    const char letter;        /* set [+/-]<letter> and $- */
+    const char opt_set;       /* mutually exclusive option set */
+    unsigned char val;        /* value of <letter>flag */
+};
+
+struct optent optlist[] = {
+    {"errexit",   'e', 0, 0}, //optval[0]},
+    {"noglob",    'f', 0, 0}, //optval[1]},
+    {"ignoreeof",   'I', 0, 0}, //optval[2]},
+    {"interactive", 'i', 0, 0}, //optval[3]},
+    {"jobctl",    'j', 0, 0}, //optval[4]},
+    {"noexec",    'n', 0, 0}, //optval[5]},
+    {"stdin",     's', 0, 0}, //optval[6]},
+    {"xtrace",    'x', 0, 0}, //optval[7]},
+    {"zflag",     'z', 0, 0}, //optval[8]},
+    {"verbose",   'v', 0, 0}, //optval[9]},
+    {"allexport",   'a', 0, 0}, //optval[10]},
+    {"noclobber",   'C', 0, 0}, //optval[11]},
+    {"nounset",   'u', 0, 0}, //optval[12]},
+    {"notify",    'b', 0, 0}, //optval[13]},
+    {"monitor",   'm', 0, 0}, //optval[14]},
+    {"login",     'l', 0, 0}, //optval[15]},
+    {"vi",     '\0', 0, 0},
+    {"pipefail",   '\0', 0, 0},
+    {"nolog",    '\0', 0, 0},
+    {"debug",    '\0', 0, 0},
+    {0, 0, 0, 0}
+};
+
+extern char *minusc;    /* argument to -c option */
+extern char *arg0;    /* $0 */
+extern struct shparam shellparam;  /* $@ */
+extern char **argptr;    /* argument list for builtin commands */
+extern char *optarg;    /* set by nextopt */
+extern char *optptr;    /* used by nextopt */
+
diff --git a/lib/ash_shell.h b/lib/ash_shell.h
new file mode 100644 (file)
index 0000000..043e23a
--- /dev/null
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  @(#)shell.h  5.4 (Berkeley) 4/12/91
+ *
+ *  $Header: /cvsroot-fuse/beastiebox/beastiebox/beastiebin/sh/shell.h,v 1.1.1.1 2008/11/27 21:05:30 imilh Exp $
+ */
+
+/*
+ * The follow should be set to reflect the type of system you have:
+ *  JOBS -> 1 if you have Berkeley job control, 0 otherwise.
+ *  SYMLINKS -> 1 if your system includes symbolic links, 0 otherwise.
+ *  DIRENT -> 1 if your system has the SVR3 directory(3X) routines.
+ *  UDIR -> 1 if you want the shell to simulate the /u directory.
+ *  ATTY -> 1 to include code for atty(1).
+ *  SHORTNAMES -> 1 if your linker cannot handle long names.
+ *  define BSD if you are running 4.2 BSD or later.
+ *  define SYSV if you are running under System V.
+ *  define DEBUG=1 to compile in debugging (set global "debug" to turn on)
+ *  define DEBUG=2 to compile in and turn on debugging.
+ *
+ * When debugging is on, debugging info will be written to $HOME/trace and
+ * a quit signal will generate a core dump.
+ */
+
+
+#define JOBS 1
+#define SYMLINKS 1
+#define DIRENT 1
+#define UDIR 0
+#define ATTY 0
+#define BSD
+#define DEBUG 1
+
+#ifdef __STDC__
+typedef void *pointer;
+#ifndef NULL
+#define NULL (void *)0
+#endif
+#else /* not __STDC__ */
+typedef char *pointer;
+#ifndef NULL
+#define NULL 0
+#endif
+#endif /*  not __STDC__ */
+//#define STATIC  /* empty */
+#define STATIC static
+#define MKINIT  /* empty */
+
+#include <sys/cdefs.h>
+
+extern char nullstr[1];    /* null string */
+
+
+#ifdef DEBUG
+#define TRACE(param)  trace param
+#else
+#define TRACE(param)
+#endif
diff --git a/lib/ash_syntax.h b/lib/ash_syntax.h
new file mode 100644 (file)
index 0000000..8197891
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file was generated by the mksyntax program.
+ */
+
+#include <sys/cdefs.h>
+/* Syntax classes */
+#define CWORD 0      /* character is nothing special */
+#define CNL 1      /* newline character */
+#define CBACK 2      /* a backslash character */
+#define CSQUOTE 3    /* single quote */
+#define CDQUOTE 4    /* double quote */
+#define CENDQUOTE 5    /* a terminating quote */
+#define CBQUOTE 6    /* backwards single quote */
+#define CVAR 7      /* a dollar sign */
+#define CENDVAR 8    /* a '}' character */
+#ifndef CEOF
+  #define CEOF 9      /* end of file */
+#endif
+#define CCTL 10      /* like CWORD, except it must be escaped */
+#define CSPCL 11    /* these terminate a word */
+
+/* Syntax classes for is_ functions */
+#define ISDIGIT 01    /* a digit */
+#define ISUPPER 02    /* an upper case letter */
+#define ISLOWER 04    /* a lower case letter */
+#define ISUNDER 010    /* an underscore */
+#define ISSPECL 020    /* the name of a special parameter */
+
+#define SYNBASE 129
+#define PEOF -129
+
+
+#define BASESYNTAX (basesyntax + SYNBASE)
+#define DQSYNTAX (dqsyntax + SYNBASE)
+#define SQSYNTAX (sqsyntax + SYNBASE)
+
+#define is_digit(c)  ((unsigned)((c) - '0') <= 9)
+#define is_alpha(c)  ((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER))
+#define is_name(c)  ((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER))
+#define is_in_name(c)  ((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))
+#define is_special(c)  ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
+#define digit_val(c)  ((c) - '0')
+
+extern const char basesyntax[];
+extern const char dqsyntax[];
+extern const char sqsyntax[];
+extern const char is_type[];
diff --git a/lib/ash_token.def b/lib/ash_token.def
new file mode 100644 (file)
index 0000000..93541e2
--- /dev/null
@@ -0,0 +1,112 @@
+#define TEOF 0
+#define TNL 1
+#define TSEMI 2
+#define TBACKGND 3
+#define TAND 4
+#define TOR 5
+#define TPIPE 6
+#define TLP 7
+#define TRP 8
+#define TENDCASE 9
+#define TENDBQUOTE 10
+#define TREDIR 11
+#define TWORD 12
+#define TIF 13
+#define TTHEN 14
+#define TELSE 15
+#define TELIF 16
+#define TFI 17
+#define TWHILE 18
+#define TUNTIL 19
+#define TFOR 20
+#define TDO 21
+#define TDONE 22
+#define TBEGIN 23
+#define TEND 24
+#define TCASE 25
+#define TESAC 26
+#define TNOT 27
+
+/* Array indicating which tokens mark the end of a list */
+static const char tokendlist[] = {
+       1,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       1,
+       1,
+       1,
+       0,
+       0,
+       0,
+       1,
+       1,
+       1,
+       1,
+       0,
+       0,
+       0,
+       1,
+       1,
+       0,
+       1,
+       0,
+       1,
+       0,
+};
+
+static char *const tokname[] = {
+       "end of file",
+       "newline",
+       "\";\"",
+       "\"&\"",
+       "\"&&\"",
+       "\"||\"",
+       "\"|\"",
+       "\"(\"",
+       "\")\"",
+       "\";;\"",
+       "\"`\"",
+       "redirection",
+       "word",
+       "\"if\"",
+       "\"then\"",
+       "\"else\"",
+       "\"elif\"",
+       "\"fi\"",
+       "\"while\"",
+       "\"until\"",
+       "\"for\"",
+       "\"do\"",
+       "\"done\"",
+       "\"{\"",
+       "\"}\"",
+       "\"case\"",
+       "\"esac\"",
+       "\"!\"",
+};
+
+#define KWDOFFSET 13
+
+static char *const parsekwd[] = {
+       "if",
+       "then",
+       "else",
+       "elif",
+       "fi",
+       "while",
+       "until",
+       "for",
+       "do",
+       "done",
+       "{",
+       "}",
+       "case",
+       "esac",
+       "!",
+       0
+};
diff --git a/lib/ash_ytab.h b/lib/ash_ytab.h
new file mode 100644 (file)
index 0000000..da16044
--- /dev/null
@@ -0,0 +1,35 @@
+#define ARITH_NUM 257
+#define ARITH_LPAREN 258
+#define ARITH_RPAREN 259
+#define ARITH_VAR 260
+#define ARITH_OR 261
+#define ARITH_AND 262
+#define ARITH_BOR 263
+#define ARITH_BXOR 264
+#define ARITH_BAND 265
+#define ARITH_EQ 266
+#define ARITH_NE 267
+#define ARITH_LT 268
+#define ARITH_GT 269
+#define ARITH_GE 270
+#define ARITH_LE 271
+#define ARITH_LSHIFT 272
+#define ARITH_RSHIFT 273
+#define ARITH_ADD 274
+#define ARITH_SUB 275
+#define ARITH_MUL 276
+#define ARITH_DIV 277
+#define ARITH_REM 278
+#define ARITH_POWER 279
+#define ARITH_UNARYMINUS 280
+#define ARITH_UNARYPLUS 281
+#define ARITH_NOT 282
+#define ARITH_BNOT 283
+#define ARITH_ASGN 284
+#define ARITH_PLUS_ASGN 285
+#define ARITH_PLUS_PLUS 286
+#define ARITH_MINUS_ASGN 287
+#define ARITH_MINUS_MINUS 288
+#define ARITH_MUL_ASGN 289
+#define ARITH_DIV_ASGN 290
+#define ARITH_REM_ASGN 291
diff --git a/lib/ash_ytabc b/lib/ash_ytabc
new file mode 100644 (file)
index 0000000..7e53bb2
--- /dev/null
@@ -0,0 +1,970 @@
+#ifndef lint
+static const char yysccsid[] = "@(#)yaccpar    1.9 (Berkeley) 02/21/93";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYPATCH 20100216
+
+#define YYEMPTY        (-1)
+#define yyclearin      (yychar = YYEMPTY)
+#define yyerrok        (yyerrflag = 0)
+#define YYRECOVERING() (yyerrflag != 0)
+
+#define YYPREFIX "yy"
+
+/* compatibility with bison */
+#ifdef YYPARSE_PARAM
+/* compatibility with FreeBSD */
+#ifdef YYPARSE_PARAM_TYPE
+#define YYPARSE_DECL() yyparse(YYPARSE_PARAM_TYPE YYPARSE_PARAM)
+#else
+#define YYPARSE_DECL() yyparse(void *YYPARSE_PARAM)
+#endif
+#else
+#define YYPARSE_DECL() yyparse(void)
+#endif /* YYPARSE_PARAM */
+
+//extern static int YYPARSE_DECL();
+static int YYPARSE_DECL();
+
+#line 2 "arith.y"
+/*     $NetBSD: arith.y,v 1.22 2012/03/20 18:42:29 matt Exp $  */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define intmax_t long
+
+typedef intmax_t YYSTYPE;
+#define YYSTYPE YYSTYPE
+
+#define LONG_CHARS  20
+intmax_t arith_result;
+const char *arith_buf, *arith_startbuf;
+
+static void yyerror(const char *);
+#ifdef TESTARITH
+int main(int , char *[]);
+int error(char *);
+#endif
+
+#line 89 "y.tab.c"
+#define ARITH_NUM 257
+#define ARITH_LPAREN 258
+#define ARITH_RPAREN 259
+#define ARITH_VAR 260
+#define ARITH_OR 261
+#define ARITH_AND 262
+#define ARITH_BOR 263
+#define ARITH_BXOR 264
+#define ARITH_BAND 265
+#define ARITH_EQ 266
+#define ARITH_NE 267
+#define ARITH_LT 268
+#define ARITH_GT 269
+#define ARITH_GE 270
+#define ARITH_LE 271
+#define ARITH_LSHIFT 272
+#define ARITH_RSHIFT 273
+#define ARITH_ADD 274
+#define ARITH_SUB 275
+#define ARITH_MUL 276
+#define ARITH_DIV 277
+#define ARITH_REM 278
+#define ARITH_POWER 279
+#define ARITH_UNARYMINUS 280
+#define ARITH_UNARYPLUS 281
+#define ARITH_NOT 282
+#define ARITH_BNOT 283
+#define ARITH_ASGN 284
+#define ARITH_PLUS_ASGN 285
+#define ARITH_PLUS_PLUS 286
+#define ARITH_MINUS_ASGN 287
+#define ARITH_MINUS_MINUS 288
+#define ARITH_MUL_ASGN 289
+#define ARITH_DIV_ASGN 290
+#define ARITH_REM_ASGN 291
+#define YYERRCODE 256
+static const short yylhs[] = {                           -1,
+    0,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+    1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+    1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+    1,    1,    1,    1,    1,    1,    1,
+};
+static const short yylen[] = {                            2,
+    1,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+    3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+    2,    2,    2,    2,    1,    3,    1,    3,    3,    2,
+    2,    3,    2,    2,    3,    3,    3,
+};
+static const short yydefred[] = {                         0,
+   25,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,   30,    0,   33,    0,    0,    0,
+   24,   23,   21,   22,   31,   34,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    2,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,   18,   19,   20,
+   26,
+};
+static const short yydgoto[] = {                         10,
+   11,
+};
+static const short yysindex[] = {                      -254,
+    0, -254, -226, -254, -254, -254, -254, -260, -255,    0,
+  -28,  -47, -254, -254,    0, -254,    0, -254, -254, -254,
+    0,    0,    0,    0,    0,    0, -254, -254, -254, -254,
+ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254,
+ -254, -254, -254, -254, -254,    0,  -28,  -28,  -28,  -28,
+  -28,  -28,  179,  196,  212, -224,  226, -261, -261, -199,
+ -199, -199, -199, -186, -186, -252, -252,    0,    0,    0,
+    0,
+};
+static const short yyrindex[] = {                         0,
+    0,    0,    1,    0,    0,    0,    0,    0,    0,    0,
+   19,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    2,   23,   40,   57,
+   72,   87,  178,  174,  169,  163,  156,  138,  147,   86,
+   99,  112,  125,   56,   71,   22,   39,    0,    0,    0,
+    0,
+};
+static const short yygindex[] = {                         0,
+  166,
+};
+#define YYTABLESIZE 505
+static const short yytable[] = {                         25,
+   27,   28,    1,    2,   26,    3,   34,   35,   36,   37,
+   38,   39,   40,   41,   42,   43,   44,   45,    1,    4,
+    5,   16,   29,   42,   43,   44,   45,    6,    7,    0,
+    0,    8,    0,    9,    0,    0,    0,    0,   17,   32,
+   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
+   41,   42,   43,   44,   45,   14,   35,   13,   14,   15,
+   16,   17,   18,   19,   20,    0,    0,    0,    0,    0,
+   15,   36,   38,   39,   40,   41,   42,   43,   44,   45,
+    0,    0,    0,    0,    0,   11,   37,   40,   41,   42,
+   43,   44,   45,    0,    0,    0,    0,    0,    9,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,   10,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,   12,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    8,    0,    0,
+    0,    0,    0,    0,    0,    0,   13,    0,    0,    0,
+    0,    0,    0,    0,    0,    7,    0,    0,    0,    0,
+    0,    0,    6,    0,    0,    0,    0,   12,    5,   21,
+   22,   23,   24,    4,    0,    0,    0,    3,   47,   48,
+    0,   49,    0,   50,   51,   52,    0,    0,    0,    0,
+    0,    0,   53,   54,   55,   56,   57,   58,   59,   60,
+   61,   62,   63,   64,   65,   66,   67,   68,   69,   70,
+   71,   46,    0,   27,   28,   29,   30,   31,   32,   33,
+   34,   35,   36,   37,   38,   39,   40,   41,   42,   43,
+   44,   45,   27,   28,   29,   30,   31,   32,   33,   34,
+   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,
+   45,    0,    0,    0,    0,    0,    0,    0,    0,   27,
+   28,   27,   27,   27,   27,   27,   27,   27,   27,   27,
+   27,   27,   27,   27,   27,   27,   27,   27,   27,   27,
+   16,   29,   16,   16,   16,   16,   16,   16,   16,   16,
+   16,   16,   16,   16,   16,   16,   16,   17,   32,   17,
+   17,   17,   17,   17,   17,   17,   17,   17,   17,   17,
+   17,   17,   17,   17,   14,   35,   14,   14,   14,   14,
+   14,   14,   14,   14,   14,   14,   14,   14,   14,   15,
+   36,   15,   15,   15,   15,   15,   15,   15,   15,   15,
+   15,   15,   15,   15,   11,   37,   11,   11,   11,   11,
+   11,   11,   11,   11,   11,   11,   11,    9,    0,    9,
+    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+   10,    0,   10,   10,   10,   10,   10,   10,   10,   10,
+   10,   10,   10,   12,    0,   12,   12,   12,   12,   12,
+   12,   12,   12,   12,   12,   12,    8,    0,    8,    8,
+    8,    8,    8,    8,    8,   13,    0,   13,   13,   13,
+   13,   13,   13,   13,    7,    0,    7,    7,    7,    7,
+    7,    6,    0,    6,    6,    6,    6,    5,    0,    5,
+    5,    5,    4,    0,    4,    4,    3,    0,    3,    0,
+   28,   29,   30,   31,   32,   33,   34,   35,   36,   37,
+   38,   39,   40,   41,   42,   43,   44,   45,   29,   30,
+   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
+   41,   42,   43,   44,   45,   30,   31,   32,   33,   34,
+   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,
+   45,   32,   33,   34,   35,   36,   37,   38,   39,   40,
+   41,   42,   43,   44,   45,
+};
+static const short yycheck[] = {                        260,
+    0,    0,  257,  258,  260,  260,  268,  269,  270,  271,
+  272,  273,  274,  275,  276,  277,  278,  279,    0,  274,
+  275,    0,    0,  276,  277,  278,  279,  282,  283,   -1,
+   -1,  286,   -1,  288,   -1,   -1,   -1,   -1,    0,    0,
+  265,  266,  267,  268,  269,  270,  271,  272,  273,  274,
+  275,  276,  277,  278,  279,    0,    0,  284,  285,  286,
+  287,  288,  289,  290,  291,   -1,   -1,   -1,   -1,   -1,
+    0,    0,  272,  273,  274,  275,  276,  277,  278,  279,
+   -1,   -1,   -1,   -1,   -1,    0,    0,  274,  275,  276,
+  277,  278,  279,   -1,   -1,   -1,   -1,   -1,    0,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,    0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,   -1,
+   -1,   -1,    0,   -1,   -1,   -1,   -1,    2,    0,    4,
+    5,    6,    7,    0,   -1,   -1,   -1,    0,   13,   14,
+   -1,   16,   -1,   18,   19,   20,   -1,   -1,   -1,   -1,
+   -1,   -1,   27,   28,   29,   30,   31,   32,   33,   34,
+   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,
+   45,  259,   -1,  261,  262,  263,  264,  265,  266,  267,
+  268,  269,  270,  271,  272,  273,  274,  275,  276,  277,
+  278,  279,  261,  262,  263,  264,  265,  266,  267,  268,
+  269,  270,  271,  272,  273,  274,  275,  276,  277,  278,
+  279,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  259,
+  259,  261,  262,  263,  264,  265,  266,  267,  268,  269,
+  270,  271,  272,  273,  274,  275,  276,  277,  278,  279,
+  259,  259,  261,  262,  263,  264,  265,  266,  267,  268,
+  269,  270,  271,  272,  273,  274,  275,  259,  259,  261,
+  262,  263,  264,  265,  266,  267,  268,  269,  270,  271,
+  272,  273,  274,  275,  259,  259,  261,  262,  263,  264,
+  265,  266,  267,  268,  269,  270,  271,  272,  273,  259,
+  259,  261,  262,  263,  264,  265,  266,  267,  268,  269,
+  270,  271,  272,  273,  259,  259,  261,  262,  263,  264,
+  265,  266,  267,  268,  269,  270,  271,  259,   -1,  261,
+  262,  263,  264,  265,  266,  267,  268,  269,  270,  271,
+  259,   -1,  261,  262,  263,  264,  265,  266,  267,  268,
+  269,  270,  271,  259,   -1,  261,  262,  263,  264,  265,
+  266,  267,  268,  269,  270,  271,  259,   -1,  261,  262,
+  263,  264,  265,  266,  267,  259,   -1,  261,  262,  263,
+  264,  265,  266,  267,  259,   -1,  261,  262,  263,  264,
+  265,  259,   -1,  261,  262,  263,  264,  259,   -1,  261,
+  262,  263,  259,   -1,  261,  262,  259,   -1,  261,   -1,
+  262,  263,  264,  265,  266,  267,  268,  269,  270,  271,
+  272,  273,  274,  275,  276,  277,  278,  279,  263,  264,
+  265,  266,  267,  268,  269,  270,  271,  272,  273,  274,
+  275,  276,  277,  278,  279,  264,  265,  266,  267,  268,
+  269,  270,  271,  272,  273,  274,  275,  276,  277,  278,
+  279,  266,  267,  268,  269,  270,  271,  272,  273,  274,
+  275,  276,  277,  278,  279,
+};
+#define YYFINAL 10
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 291
+#if YYDEBUG
+static const char *yyname[] = {
+
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,"'*'","'+'",0,"'-'",0,"'/'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"ARITH_NUM",
+"ARITH_LPAREN","ARITH_RPAREN","ARITH_VAR","ARITH_OR","ARITH_AND","ARITH_BOR",
+"ARITH_BXOR","ARITH_BAND","ARITH_EQ","ARITH_NE","ARITH_LT","ARITH_GT",
+"ARITH_GE","ARITH_LE","ARITH_LSHIFT","ARITH_RSHIFT","ARITH_ADD","ARITH_SUB",
+"ARITH_MUL","ARITH_DIV","ARITH_REM","ARITH_POWER","ARITH_UNARYMINUS",
+"ARITH_UNARYPLUS","ARITH_NOT","ARITH_BNOT","ARITH_ASGN","ARITH_PLUS_ASGN",
+"ARITH_PLUS_PLUS","ARITH_MINUS_ASGN","ARITH_MINUS_MINUS","ARITH_MUL_ASGN",
+"ARITH_DIV_ASGN","ARITH_REM_ASGN",
+};
+static const char *yyrule[] = {
+"$accept : exp",
+"exp : expr",
+"expr : ARITH_LPAREN expr ARITH_RPAREN",
+"expr : expr ARITH_OR expr",
+"expr : expr ARITH_AND expr",
+"expr : expr ARITH_BOR expr",
+"expr : expr ARITH_BXOR expr",
+"expr : expr ARITH_BAND expr",
+"expr : expr ARITH_EQ expr",
+"expr : expr ARITH_GT expr",
+"expr : expr ARITH_GE expr",
+"expr : expr ARITH_LT expr",
+"expr : expr ARITH_LE expr",
+"expr : expr ARITH_NE expr",
+"expr : expr ARITH_LSHIFT expr",
+"expr : expr ARITH_RSHIFT expr",
+"expr : expr ARITH_ADD expr",
+"expr : expr ARITH_SUB expr",
+"expr : expr ARITH_MUL expr",
+"expr : expr ARITH_DIV expr",
+"expr : expr ARITH_REM expr",
+"expr : ARITH_NOT expr",
+"expr : ARITH_BNOT expr",
+"expr : ARITH_SUB expr",
+"expr : ARITH_ADD expr",
+"expr : ARITH_NUM",
+"expr : expr ARITH_POWER expr",
+"expr : ARITH_VAR",
+"expr : ARITH_VAR ARITH_ASGN expr",
+"expr : ARITH_VAR ARITH_PLUS_ASGN expr",
+"expr : ARITH_VAR ARITH_PLUS_PLUS",
+"expr : ARITH_PLUS_PLUS ARITH_VAR",
+"expr : ARITH_VAR ARITH_MINUS_ASGN expr",
+"expr : ARITH_VAR ARITH_MINUS_MINUS",
+"expr : ARITH_MINUS_MINUS ARITH_VAR",
+"expr : ARITH_VAR ARITH_MUL_ASGN expr",
+"expr : ARITH_VAR ARITH_DIV_ASGN expr",
+"expr : ARITH_VAR ARITH_REM_ASGN expr",
+
+};
+#endif
+#ifndef YYSTYPE
+typedef int YYSTYPE;
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+
+/* define the initial stack-sizes */
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH  YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH  500
+#endif
+#endif
+
+#define YYINITSTACKSIZE 500
+
+int      yydebug;
+int      yynerrs;
+
+typedef struct {
+    unsigned stacksize;
+    short    *s_base;
+    short    *s_mark;
+    short    *s_last;
+    YYSTYPE  *l_base;
+    YYSTYPE  *l_mark;
+} YYSTACKDATA;
+
+#define YYPURE 0
+
+int      yyerrflag;
+int      yychar;
+YYSTYPE  yyval;
+YYSTYPE  yylval;
+
+/* variables for the parser stack */
+static YYSTACKDATA yystack;
+#line 231 "arith.y"
+static intmax_t arith(const char *s)
+{
+       intmax_t result;
+       yyclearin;
+    arith_lex_reset();
+
+       arith_buf = arith_startbuf = s;
+       INTOFF;
+       (void) yyparse();
+       result = arith_result;
+       arith_lex_reset();      /* reprime lex */
+       INTON;
+       fflush(stdout);
+       return (result);
+}
+
+
+/*
+ *  The exp(1) builtin.
+ */
+int expcmd(int argc, char **argv)
+{
+       intmax_t i;
+    int is_exp = 0;
+    is_exp = strcmp("let", argv[0]);
+
+    argv++;
+    if (!*argv)
+        sh_error("expression expected");
+    do {                                                                                                                                                                                                                                       
+        (void)arith(*argv);
+        i = arith_result;
+    } while (*++argv);
+
+    if(is_exp)
+       out1fmt("%ld\n", i);
+       return (! i);
+}
+
+/*************************/
+#ifdef TEST_ARITH
+#include <stdio.h>
+main(argc, argv)
+       char *argv[];
+{
+       printf("%"PRIdMAX"\n", exp(argv[1]));
+}
+error(s)
+       char *s;
+{
+       fprintf(stderr, "exp: %s\n", s);
+       exit(1);
+}
+#endif
+
+static void yyerror(const char *s)
+{
+
+       yyerrok;
+       yyclearin;
+       arith_lex_reset();      /* reprime lex */
+       sh_error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+       /* NOTREACHED */
+}
+
+#line 455 "y.tab.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack(YYSTACKDATA *data)
+{
+    int i;
+    unsigned newsize;
+    short *newss;
+    YYSTYPE *newvs;
+
+    if ((newsize = data->stacksize) == 0)
+        newsize = YYINITSTACKSIZE;
+    else if (newsize >= YYMAXDEPTH)
+        return -1;
+    else if ((newsize *= 2) > YYMAXDEPTH)
+        newsize = YYMAXDEPTH;
+
+    i = data->s_mark - data->s_base;
+    newss = (data->s_base != 0)
+          ? (short *)realloc(data->s_base, newsize * sizeof(*newss))
+          : (short *)malloc(newsize * sizeof(*newss));
+    if (newss == 0)
+        return -1;
+
+    data->s_base  = newss;
+    data->s_mark = newss + i;
+
+    newvs = (data->l_base != 0)
+          ? (YYSTYPE *)realloc(data->l_base, newsize * sizeof(*newvs))
+          : (YYSTYPE *)malloc(newsize * sizeof(*newvs));
+    if (newvs == 0)
+        return -1;
+
+    data->l_base = newvs;
+    data->l_mark = newvs + i;
+
+    data->stacksize = newsize;
+    data->s_last = data->s_base + newsize - 1;
+    return 0;
+}
+
+#if YYPURE || defined(YY_NO_LEAKS)
+static void yyfreestack(YYSTACKDATA *data)
+{
+    free(data->s_base);
+    free(data->l_base);
+    memset(data, 0, sizeof(*data));
+}
+#else
+#define yyfreestack(data) /* nothing */
+#endif
+
+#define YYABORT  goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR  goto yyerrlab
+
+static int YYPARSE_DECL()
+{
+    int yym, yyn, yystate;
+#if YYDEBUG
+    const char *yys;
+
+    if ((yys = getenv("YYDEBUG")) != 0)
+    {
+        yyn = *yys;
+        if (yyn >= '0' && yyn <= '9')
+            yydebug = yyn - '0';
+    }
+#endif
+
+    yynerrs = 0;
+    yyerrflag = 0;
+    yychar = YYEMPTY;
+    yystate = 0;
+
+#if YYPURE
+    memset(&yystack, 0, sizeof(yystack));
+#endif
+
+    if (yystack.s_base == NULL && yygrowstack(&yystack)) goto yyoverflow;
+    yystack.s_mark = yystack.s_base;
+    yystack.l_mark = yystack.l_base;
+    yystate = 0;
+    *yystack.s_mark = 0;
+
+yyloop:
+    if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+    if (yychar < 0)
+    {
+        if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, reading %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+    }
+    if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: state %d, shifting to state %d\n",
+                    YYPREFIX, yystate, yytable[yyn]);
+#endif
+        if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+        {
+            goto yyoverflow;
+        }
+        yystate = yytable[yyn];
+        *++yystack.s_mark = yytable[yyn];
+        *++yystack.l_mark = yylval;
+        yychar = YYEMPTY;
+        if (yyerrflag > 0)  --yyerrflag;
+        goto yyloop;
+    }
+    if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+        yyn = yytable[yyn];
+        goto yyreduce;
+    }
+    if (yyerrflag) goto yyinrecovery;
+
+    yyerror("syntax error");
+
+    goto yyerrlab;
+
+yyerrlab:
+    ++yynerrs;
+
+yyinrecovery:
+    if (yyerrflag < 3)
+    {
+        yyerrflag = 3;
+        for (;;)
+        {
+            if ((yyn = yysindex[*yystack.s_mark]) && (yyn += YYERRCODE) >= 0 &&
+                    yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yystack.s_mark, yytable[yyn]);
+#endif
+                if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+                {
+                    goto yyoverflow;
+                }
+                yystate = yytable[yyn];
+                *++yystack.s_mark = yytable[yyn];
+                *++yystack.l_mark = yylval;
+                goto yyloop;
+            }
+            else
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: error recovery discarding state %d\n",
+                            YYPREFIX, *yystack.s_mark);
+#endif
+                if (yystack.s_mark <= yystack.s_base) goto yyabort;
+                --yystack.s_mark;
+                --yystack.l_mark;
+            }
+        }
+    }
+    else
+    {
+        if (yychar == 0) goto yyabort;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+        yychar = YYEMPTY;
+        goto yyloop;
+    }
+
+yyreduce:
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+                YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+    yym = yylen[yyn];
+    if (yym)
+        yyval = yystack.l_mark[1-yym];
+    else
+        memset(&yyval, 0, sizeof yyval);
+    switch (yyn)
+    {
+case 1:
+#line 80 "arith.y"
+       {
+                       /*
+                        * yyparse() returns int, so we have to save
+                        * the desired result elsewhere.
+                        */
+                       arith_result = yystack.l_mark[0];
+               }
+break;
+case 2:
+#line 90 "arith.y"
+       { yyval = yystack.l_mark[-1]; }
+break;
+case 3:
+#line 91 "arith.y"
+       { yyval = yystack.l_mark[-2] ? yystack.l_mark[-2] : yystack.l_mark[0] ? yystack.l_mark[0] : 0; }
+break;
+case 4:
+#line 92 "arith.y"
+       { yyval = yystack.l_mark[-2] ? ( yystack.l_mark[0] ? yystack.l_mark[0] : 0 ) : 0; }
+break;
+case 5:
+#line 93 "arith.y"
+       { yyval = yystack.l_mark[-2] | yystack.l_mark[0]; }
+break;
+case 6:
+#line 94 "arith.y"
+       { yyval = yystack.l_mark[-2] ^ yystack.l_mark[0]; }
+break;
+case 7:
+#line 95 "arith.y"
+       { yyval = yystack.l_mark[-2] & yystack.l_mark[0]; }
+break;
+case 8:
+#line 96 "arith.y"
+       { yyval = yystack.l_mark[-2] == yystack.l_mark[0]; }
+break;
+case 9:
+#line 97 "arith.y"
+       { yyval = yystack.l_mark[-2] > yystack.l_mark[0]; }
+break;
+case 10:
+#line 98 "arith.y"
+       { yyval = yystack.l_mark[-2] >= yystack.l_mark[0]; }
+break;
+case 11:
+#line 99 "arith.y"
+       { yyval = yystack.l_mark[-2] < yystack.l_mark[0]; }
+break;
+case 12:
+#line 100 "arith.y"
+       { yyval = yystack.l_mark[-2] <= yystack.l_mark[0]; }
+break;
+case 13:
+#line 101 "arith.y"
+       { yyval = yystack.l_mark[-2] != yystack.l_mark[0]; }
+break;
+case 14:
+#line 102 "arith.y"
+       { yyval = yystack.l_mark[-2] << yystack.l_mark[0]; }
+break;
+case 15:
+#line 103 "arith.y"
+       { yyval = yystack.l_mark[-2] >> yystack.l_mark[0]; }
+break;
+case 16:
+#line 104 "arith.y"
+       { yyval = yystack.l_mark[-2] + yystack.l_mark[0]; }
+break;
+case 17:
+#line 105 "arith.y"
+       { yyval = yystack.l_mark[-2] - yystack.l_mark[0]; }
+break;
+case 18:
+#line 106 "arith.y"
+       { yyval = yystack.l_mark[-2] * yystack.l_mark[0]; }
+break;
+case 19:
+#line 107 "arith.y"
+       {
+                       if (yystack.l_mark[0] == 0)
+                               yyerror("division by zero");
+                       yyval = yystack.l_mark[-2] / yystack.l_mark[0];
+               }
+break;
+case 20:
+#line 112 "arith.y"
+       {
+                       if (yystack.l_mark[0] == 0)
+                               yyerror("division by zero");
+                       yyval = yystack.l_mark[-2] % yystack.l_mark[0];
+               }
+break;
+case 21:
+#line 117 "arith.y"
+       { yyval = !(yystack.l_mark[0]); }
+break;
+case 22:
+#line 118 "arith.y"
+       { yyval = ~(yystack.l_mark[0]); }
+break;
+case 23:
+#line 119 "arith.y"
+       { yyval = -(yystack.l_mark[0]); }
+break;
+case 24:
+#line 120 "arith.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+case 26:
+#line 122 "arith.y"
+       { yyval = pow(yystack.l_mark[-2], yystack.l_mark[0]); if(yyval == HUGE_VAL) yyerror("Range Overflow"); }
+break;
+case 27:
+#line 123 "arith.y"
+       { yyval = strtoimax(lookupvar((char *)yystack.l_mark[0]), 0, 0); free((void*)yystack.l_mark[0]); }
+break;
+case 28:
+#line 124 "arith.y"
+       { 
+        yyval = yystack.l_mark[0];
+               char ptr[LONG_CHARS];
+               sprintf(ptr, "%ld", yystack.l_mark[0]);
+               setvar((char *)yystack.l_mark[-2], ptr, 0);
+        free((void*)yystack.l_mark[-2]);
+       }
+break;
+case 29:
+#line 131 "arith.y"
+       { /*$$ = $1 + $3; */
+               long lval; 
+        char ptr[LONG_CHARS];
+               char *val = lookupvar((char *)yystack.l_mark[-2]);
+        lval = strtoimax(val, 0, 0);
+        lval = lval + yystack.l_mark[0];
+        sprintf(ptr, "%ld", lval);
+        yyval = lval;
+        setvar((char *)yystack.l_mark[-2], ptr, 0);
+        free((void*)yystack.l_mark[-2]);
+       }
+break;
+case 30:
+#line 142 "arith.y"
+       {
+               char ptr[LONG_CHARS];
+               char *val = lookupvar((char *)yystack.l_mark[-1]);
+               long lval = strtoimax(val, 0, 0);
+               yyval = lval;
+               sprintf(ptr, "%ld", (lval +1));
+               setvar((char *)yystack.l_mark[-1], ptr, 0);
+        free((void*)yystack.l_mark[-1]);
+       }
+break;
+case 31:
+#line 151 "arith.y"
+       {
+                char ptr[LONG_CHARS];
+                char *val = lookupvar((char *)yystack.l_mark[0]);
+                long lval = strtoimax(val, 0, 0);
+                ++lval;
+                sprintf(ptr, "%ld", lval);
+                yyval = lval;
+                setvar((char *)yystack.l_mark[0], ptr, 0);
+                free((void*)yystack.l_mark[0]);
+        }
+break;
+case 32:
+#line 161 "arith.y"
+       { /*$$ = $1 + $3; */
+                char ptr[LONG_CHARS];
+                long lval;
+                char *val = lookupvar((char *)yystack.l_mark[-2]);
+                lval = strtoimax(val, 0, 0);
+                lval = lval - yystack.l_mark[0];
+                sprintf(ptr, "%ld", lval);
+                yyval = lval;
+                setvar((char *)yystack.l_mark[-2], ptr, 0);
+                free((void*)yystack.l_mark[-2]);
+        }
+break;
+case 33:
+#line 172 "arith.y"
+       {
+                char ptr[LONG_CHARS];
+                char *val = lookupvar((char *)yystack.l_mark[-1]);
+                long lval = strtoimax(val, 0, 0);
+                yyval = lval;
+                sprintf(ptr, "%ld", (lval -1));
+                setvar((char *)yystack.l_mark[-1], ptr, 0);
+                free((void*)yystack.l_mark[-1]);
+        }
+break;
+case 34:
+#line 181 "arith.y"
+       {
+                char ptr[LONG_CHARS];
+                char *val = lookupvar((char *)yystack.l_mark[0]);
+                long lval = strtoimax(val, 0, 0);
+                --lval;
+                sprintf(ptr, "%ld", lval);
+                yyval = lval;
+                setvar((char *)yystack.l_mark[0], ptr, 0);
+                free((void*)yystack.l_mark[0]);
+        }
+break;
+case 35:
+#line 192 "arith.y"
+       { /*$$ = $1 + $3; */
+                char ptr[LONG_CHARS];
+                long lval;
+                char *val = lookupvar((char *)yystack.l_mark[-2]);
+                lval = strtoimax(val, 0, 0);
+                lval = lval * yystack.l_mark[0];
+                sprintf(ptr, "%ld", lval);
+                yyval = lval;
+                setvar((char *)yystack.l_mark[-2], ptr, 0);
+                free((void*)yystack.l_mark[-2]);
+        }
+break;
+case 36:
+#line 203 "arith.y"
+       { /*$$ = $1 + $3; */
+                char ptr[LONG_CHARS];
+                long lval;
+                char *val = lookupvar((char *)yystack.l_mark[-2]);
+                lval = strtoimax(val, 0, 0);
+                if(yystack.l_mark[0] == 0)
+                    yyerror("division by zero");
+                lval = lval / yystack.l_mark[0];
+                sprintf(ptr, "%ld", lval);
+                yyval = lval;
+                setvar((char *)yystack.l_mark[-2], ptr, 0);
+                free((void*)yystack.l_mark[-2]);
+        }
+break;
+case 37:
+#line 216 "arith.y"
+       { /*$$ = $1 + $3; */
+                char ptr[LONG_CHARS];
+                long lval;
+                char *val = lookupvar((char *)yystack.l_mark[-2]);
+                lval = strtoimax(val, 0, 0);
+                if(yystack.l_mark[0] == 0)
+                    yyerror("division by zero");
+                lval = lval % yystack.l_mark[0];
+                sprintf(ptr, "%ld", lval);
+                yyval = lval;
+                setvar((char *)yystack.l_mark[-2], ptr, 0);
+                free((void*)yystack.l_mark[-2]);
+        }
+break;
+#line 909 "y.tab.c"
+    }
+    yystack.s_mark -= yym;
+    yystate = *yystack.s_mark;
+    yystack.l_mark -= yym;
+    yym = yylhs[yyn];
+    if (yystate == 0 && yym == 0)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+        yystate = YYFINAL;
+        *++yystack.s_mark = YYFINAL;
+        *++yystack.l_mark = yyval;
+        if (yychar < 0)
+        {
+            if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+            if (yydebug)
+            {
+                yys = 0;
+                if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+                if (!yys) yys = "illegal-symbol";
+                printf("%sdebug: state %d, reading %d (%s)\n",
+                        YYPREFIX, YYFINAL, yychar, yys);
+            }
+#endif
+        }
+        if (yychar == 0) goto yyaccept;
+        goto yyloop;
+    }
+    if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+        yystate = yytable[yyn];
+    else
+        yystate = yydgoto[yym];
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yystack.s_mark, yystate);
+#endif
+    if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+    {
+        goto yyoverflow;
+    }
+    *++yystack.s_mark = (short) yystate;
+    *++yystack.l_mark = yyval;
+    goto yyloop;
+
+yyoverflow:
+    yyerror("yacc stack overflow");
+
+yyabort:
+    yyfreestack(&yystack);
+    return (1);
+
+yyaccept:
+    yyfreestack(&yystack);
+    return (0);
+}
diff --git a/lib/awk.h b/lib/awk.h
new file mode 100644 (file)
index 0000000..c7cad66
--- /dev/null
+++ b/lib/awk.h
@@ -0,0 +1,235 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#include <assert.h>
+#include <errno.h>
+
+typedef double  Awkfloat;
+
+/* unsigned char is more trouble than it's worth */
+
+typedef  unsigned char uschar;
+
+#define  xfree(a)  { if ((a) != NULL) { free((void *) (a)); (a) = NULL; } }
+
+#define  NN(p)  ((p) ? (p) : "(null)")  /* guaranteed non-null for dprintf 
+*/
+#define  DEBUG
+#ifdef  DEBUG
+      /* uses have to be doubly parenthesized */
+#  define  dprintf(x)  if (dbg) printf x
+#else
+#  define  dprintf(x)
+#endif
+
+extern int  compile_time;  /* 1 if compiling, 0 if running */
+extern int  safe;    /* 0 => unsafe, 1 => safe */
+
+#define  RECSIZE  (8 * 1024)  /* sets limit on records, fields, etc., etc. */
+extern int  recsize;  /* size of current record, orig RECSIZE */
+
+extern char  **FS;
+extern char  **RS;
+extern char  **ORS;
+extern char  **OFS;
+extern char  **OFMT;
+extern Awkfloat *NR;
+extern Awkfloat *FNR;
+extern Awkfloat *NF;
+extern char  **FILENAME;
+extern char  **SUBSEP;
+extern Awkfloat *RSTART;
+extern Awkfloat *RLENGTH;
+
+extern char  *record;  /* points to $0 */
+extern int  lineno;    /* line number in awk program */
+extern int  errorflag;  /* 1 if error has occurred */
+extern int  donefld;  /* 1 if record broken into fields */
+extern int  donerec;  /* 1 if record is valid (no fld has changed */
+extern char  inputFS[];  /* FS at time of input, for field splitting */
+
+extern int  dbg;
+
+extern  char  *patbeg;  /* beginning of pattern matched */
+extern  int  patlen;    /* length of pattern matched.  set in b.c */
+
+/* Cell:  all information about a variable or constant */
+
+typedef struct Cell {
+  uschar  ctype;    /* OCELL, OBOOL, OJUMP, etc. */
+  uschar  csub;    /* CCON, CTEMP, CFLD, etc. */
+  char  *nval;    /* name, for variables only */
+  char  *sval;    /* string value */
+  Awkfloat fval;    /* value as number */
+  int   tval;    /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */
+  struct Cell *cnext;  /* ptr to next if chained */
+} Cell;
+
+typedef struct Array {    /* symbol table array */
+  int  nelem;    /* elements in table right now */
+  int  size;    /* size of tab */
+  Cell  **tab;    /* hash table pointers */
+} Array;
+
+#define  NSYMTAB  50  /* initial size of a symbol table */
+extern Array  *symtab;
+
+extern Cell  *nrloc;    /* NR */
+extern Cell  *fnrloc;  /* FNR */
+extern Cell  *nfloc;    /* NF */
+extern Cell  *errloc;  /* ERRNO */
+extern Cell  *rstartloc;  /* RSTART */
+extern Cell  *rlengthloc;  /* RLENGTH */
+
+/* Cell.tval values: */
+#define  NUM  01  /* number value is valid */
+#define  STR  02  /* string value is valid */
+#define DONTFREE 04  /* string space is not freeable */
+#define  CON  010  /* this is a constant */
+#define  ARR  020  /* this is an array */
+#define  FCN  040  /* this is a function name */
+#define FLD  0100  /* this is a field $1, $2, ... */
+#define  REC  0200  /* this is $0 */
+
+/* function types */
+#define  FLENGTH  1
+#define  FSQRT  2
+#define  FEXP  3
+#define  FLOG  4
+#define  FINT  5
+#define  FSYSTEM  6
+#define  FRAND  7
+#define  FSRAND  8
+#define  FSIN  9
+#define  FCOS  10
+#define  FATAN  11
+#define  FTOUPPER 12
+#define  FTOLOWER 13
+#define  FFLUSH  14
+#define  FMKTIME  15
+#define  FSYSTIME 16
+#define  FSTRFTIME 17
+
+/* Node:  parse tree is made of nodes, with Cell's at bottom */
+
+typedef struct Node {
+  int  ntype;
+  struct  Node *nnext;
+  int  lineno;
+  int  nobj;
+  struct  Node *narg[1];  /* variable: actual size set by calling malloc */
+} Node;
+
+#define  NIL  ((Node *) 0)
+
+extern Node  *winner;
+extern Node  *nullstat;
+extern Node  *nullnode;
+
+/* ctypes */
+#define OCELL  1
+#define OBOOL  2
+#define OJUMP  3
+
+/* Cell subtypes: csub */
+#define  CFREE  7
+#define CCOPY  6
+#define CCON  5
+#define CTEMP  4
+#define CNAME  3 
+#define CVAR  2
+#define CFLD  1
+#define  CUNK  0
+
+/* bool subtypes */
+#define BTRUE  11
+#define BFALSE  12
+
+/* jump subtypes */
+#define JEXIT  21
+#define JNEXT  22
+#define  JBREAK  23
+#define  JCONT  24
+#define  JRET  25
+#define  JNEXTFILE  26
+
+/* node types */
+#define NVALUE  1
+#define NSTAT  2
+#define NEXPR  3
+
+extern  int  pairstack[], paircnt;
+
+#define notlegal(n)  (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
+#define isvalue(n)  ((n)->ntype == NVALUE)
+#define isexpr(n)  ((n)->ntype == NEXPR)
+#define isjump(n)  ((n)->ctype == OJUMP)
+#define isexit(n)  ((n)->csub == JEXIT)
+#define  isbreak(n)  ((n)->csub == JBREAK)
+#define  iscont(n)  ((n)->csub == JCONT)
+#define  isnext(n)  ((n)->csub == JNEXT || (n)->csub == JNEXTFILE)
+#define  isret(n)  ((n)->csub == JRET)
+#define isrec(n)  ((n)->tval & REC)
+#define isfld(n)  ((n)->tval & FLD)
+#define isstr(n)  ((n)->tval & STR)
+#define isnum(n)  ((n)->tval & NUM)
+#define isarr(n)  ((n)->tval & ARR)
+#define isfcn(n)  ((n)->tval & FCN)
+#define istrue(n)  ((n)->csub == BTRUE)
+#define istemp(n)  ((n)->csub == CTEMP)
+#define  isargument(n)  ((n)->nobj == ARG)
+/* #define freeable(p)  (!((p)->tval & DONTFREE)) */
+#define freeable(p)  ( ((p)->tval & (STR|DONTFREE)) == STR )
+
+/* structures used by regular expression matching machinery, mostly b.c: */
+
+#define NCHARS  (256+3)    /* 256 handles 8-bit chars; 128 does 7-bit */
+        /* watch out in match(), etc. */
+#define NSTATES  32
+
+typedef struct rrow {
+  long  ltype;  /* long avoids pointer warnings on 64-bit */
+  union {
+    int i;
+    Node *np;
+    uschar *up;
+  } lval;    /* because Al stores a pointer in it! */
+  int  *lfollow;
+} rrow;
+
+typedef struct fa {
+  uschar  gototab[NSTATES][NCHARS];
+  uschar  out[NSTATES];
+  uschar  *restr;
+  int  *posns[NSTATES];
+  int  anchor;
+  int  use;
+  int  initstat;
+  int  curstat;
+  int  accept;
+  int  reset;
+  struct  rrow re[1];  /* variable: actual size set by calling malloc */
+} fa;
+
+#include "proto.h"
diff --git a/lib/awk_ytabc b/lib/awk_ytabc
new file mode 100644 (file)
index 0000000..7f07f5d
--- /dev/null
@@ -0,0 +1,3931 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+
+#ifndef lint
+static const char yysccsid[] = "@(#)yaccpar  1.9 (Berkeley) 02/21/93";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYPATCH 20100216
+
+#define YYEMPTY    (-1)
+#define yyclearin    (yychar = YYEMPTY)
+#define yyerrok    (yyerrflag = 0)
+#define YYRECOVERING() (yyerrflag != 0)
+
+#define YYPREFIX "yy"
+
+/* compatibility with bison */
+#ifdef YYPARSE_PARAM
+/* compatibility with FreeBSD */
+#ifdef YYPARSE_PARAM_TYPE
+#define YYPARSE_DECL() yyparse(YYPARSE_PARAM_TYPE YYPARSE_PARAM)
+#else
+#define YYPARSE_DECL() yyparse(void *YYPARSE_PARAM)
+#endif
+#else
+#define YYPARSE_DECL() yyparse(void)
+#endif /* YYPARSE_PARAM */
+
+extern int YYPARSE_DECL();
+
+#line 26 "awkgram.y"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+void checkdup(Node *list, Cell *item);
+int yywrap(void) { return(1); }
+
+Node  *beginloc = 0;
+Node  *endloc = 0;
+int  infunc  = 0;  /* = 1 if in arglist or body of func */
+int  inloop  = 0;  /* = 1 if in while, for, do */
+char  *curfname = 0;  /* current function name */
+Node  *arglist = 0;  /* list of args for current function */
+#line 42 "awkgram.y"
+typedef union {
+  Node  *p;
+  Cell  *cp;
+  int  i;
+  char  *s;
+} YYSTYPE;
+#line 56 "y.tab.c"
+#define FIRSTTOKEN 257
+#define PROGRAM 258
+#define PASTAT 259
+#define PASTAT2 260
+#define XBEGIN 261
+#define XEND 262
+#define NL 263
+#define ARRAY 264
+#define MATCH 265
+#define NOTMATCH 266
+#define MATCHOP 267
+#define FINAL 268
+#define DOT 269
+#define ALL 270
+#define CCL 271
+#define NCCL 272
+#define CHAR 273
+#define OR 274
+#define STAR 275
+#define QUEST 276
+#define PLUS 277
+#define EMPTYRE 278
+#define AND 279
+#define BOR 280
+#define BITOR 281
+#define BITAND 282
+#define BITCOMPL 283
+#define BITLSHIFT 284
+#define BITRSHIFT 285
+#define BITXOR 286
+#define APPEND 287
+#define EQ 288
+#define GE 289
+#define GT 290
+#define LE 291
+#define LT 292
+#define NE 293
+#define IN 294
+#define ARG 295
+#define BLTIN 296
+#define BREAK 297
+#define CLOSE 298
+#define CONTINUE 299
+#define DELETE 300
+#define DO 301
+#define EXIT 302
+#define FOR 303
+#define FUNC 304
+#define SUB 305
+#define GSUB 306
+#define IF 307
+#define INDEX 308
+#define LSUBSTR 309
+#define MATCHFCN 310
+#define NEXT 311
+#define NEXTFILE 312
+#define ADD 313
+#define MINUS 314
+#define MULT 315
+#define DIVIDE 316
+#define MOD 317
+#define ASSIGN 318
+#define ASGNOP 319
+#define ADDEQ 320
+#define SUBEQ 321
+#define MULTEQ 322
+#define DIVEQ 323
+#define MODEQ 324
+#define POWEQ 325
+#define PRINT 326
+#define PRINTF 327
+#define SPRINTF 328
+#define ELSE 329
+#define INTEST 330
+#define CONDEXPR 331
+#define POSTINCR 332
+#define PREINCR 333
+#define POSTDECR 334
+#define PREDECR 335
+#define VAR 336
+#define IVAR 337
+#define VARNF 338
+#define CALL 339
+#define NUMBER 340
+#define STRING 341
+#define VARERR 342
+#define REGEXPR 343
+#define GETLINE 344
+#define RETURN 345
+#define SPLIT 346
+#define SUBSTR 347
+#define WHILE 348
+#define CAT 349
+#define NOT 350
+#define UMINUS 351
+#define POWER 352
+#define DECR 353
+#define INCR 354
+#define INDIRECT 355
+#define LASTTOKEN 356
+#define YYERRCODE 256
+
+static char *printname[100] = {
+  (char *) "FIRSTTOKEN",  /* 257 */
+  (char *) "PROGRAM",  /* 258 */
+  (char *) "PASTAT",  /* 259 */
+  (char *) "PASTAT2",  /* 260 */
+  (char *) "XBEGIN",  /* 261 */
+  (char *) "XEND",  /* 262 */
+  (char *) "NL",  /* 263 */
+  (char *) "ARRAY",  /* 264 */
+  (char *) "MATCH",  /* 265 */
+  (char *) "NOTMATCH",  /* 266 */
+  (char *) "MATCHOP",  /* 267 */
+  (char *) "FINAL",  /* 268 */
+  (char *) "DOT",  /* 269 */
+  (char *) "ALL",  /* 270 */
+  (char *) "CCL",  /* 271 */
+  (char *) "NCCL",  /* 272 */
+  (char *) "CHAR",  /* 273 */
+  (char *) "OR",  /* 274 */
+  (char *) "STAR",  /* 275 */
+  (char *) "QUEST",  /* 276 */
+  (char *) "PLUS",  /* 277 */
+  (char *) "EMPTYRE",  /* 278 */
+  (char *) "AND",  /* 279 */
+  (char *) "BOR",  /* 280 */
+  (char *) "BITOR",  /* 281 */
+  (char *) "BITAND",  /* 282 */
+  (char *) "BITCOMPL",  /* 283 */
+  (char *) "BITLSHIFT",  /* 284 */
+  (char *) "BITRSHIFT",  /* 285 */
+  (char *) "BITXOR",  /* 286 */
+  (char *) "APPEND",  /* 287 */
+  (char *) "EQ",  /* 288 */
+  (char *) "GE",  /* 289 */
+  (char *) "GT",  /* 290 */
+  (char *) "LE",  /* 291 */
+  (char *) "LT",  /* 292 */
+  (char *) "NE",  /* 293 */
+  (char *) "IN",  /* 294 */
+  (char *) "ARG",  /* 295 */
+  (char *) "BLTIN",  /* 296 */
+  (char *) "BREAK",  /* 297 */
+  (char *) "CLOSE",  /* 298 */
+  (char *) "CONTINUE",  /* 299 */
+  (char *) "DELETE",  /* 300 */
+  (char *) "DO",  /* 301 */
+  (char *) "EXIT",  /* 302 */
+  (char *) "FOR",  /* 303 */
+  (char *) "FUNC",  /* 304 */
+  (char *) "SUB",  /* 305 */
+  (char *) "GSUB",  /* 306 */
+  (char *) "IF",  /* 307 */
+  (char *) "INDEX",  /* 308 */
+  (char *) "LSUBSTR",  /* 309 */
+  (char *) "MATCHFCN",  /* 310 */
+  (char *) "NEXT",  /* 311 */
+  (char *) "NEXTFILE",  /* 312 */
+  (char *) "ADD",  /* 313 */
+  (char *) "MINUS",  /* 314 */
+  (char *) "MULT",  /* 315 */
+  (char *) "DIVIDE",  /* 316 */
+  (char *) "MOD",  /* 317 */
+  (char *) "ASSIGN",  /* 318 */
+  (char *) "ASGNOP",  /* 319 */
+  (char *) "ADDEQ",  /* 320 */
+  (char *) "SUBEQ",  /* 321 */
+  (char *) "MULTEQ",  /* 322 */
+  (char *) "DIVEQ",  /* 323 */
+  (char *) "MODEQ",  /* 324 */
+  (char *) "POWEQ",  /* 325 */
+  (char *) "PRINT",  /* 326 */
+  (char *) "PRINTF",  /* 327 */
+  (char *) "SPRINTF",  /* 328 */
+  (char *) "ELSE",  /* 329 */
+  (char *) "INTEST",  /* 330 */
+  (char *) "CONDEXPR",  /* 331 */
+  (char *) "POSTINCR",  /* 332 */
+  (char *) "PREINCR",  /* 333 */
+  (char *) "POSTDECR",  /* 334 */
+  (char *) "PREDECR",  /* 335 */
+  (char *) "VAR",  /* 336 */
+  (char *) "IVAR",  /* 337 */
+  (char *) "VARNF",  /* 338 */
+  (char *) "CALL",  /* 339 */
+  (char *) "NUMBER",  /* 340 */
+  (char *) "STRING",  /* 341 */
+  (char *) "VARERR",  /* 342 */
+  (char *) "REGEXPR",  /* 343 */
+  (char *) "GETLINE",  /* 344 */
+  (char *) "RETURN",  /* 345 */
+  (char *) "SPLIT",  /* 346 */
+  (char *) "SUBSTR",  /* 347 */
+  (char *) "WHILE",  /* 348 */
+  (char *) "CAT",  /* 349 */
+  (char *) "NOT",  /* 350 */
+  (char *) "UMINUS",  /* 351 */
+  (char *) "POWER",  /* 352 */
+  (char *) "DECR",  /* 353 */
+  (char *) "INCR",  /* 354 */
+  (char *) "INDIRECT",  /* 355 */
+  (char *) "LASTTOKEN",  /* 356 */
+};
+
+Cell *(*proctab[100])(Node **, int) = {
+  nullproc,  /* FIRSTTOKEN */
+  program,  /* PROGRAM */
+  pastat,  /* PASTAT */
+  dopa2,  /* PASTAT2 */
+  nullproc,  /* XBEGIN */
+  nullproc,  /* XEND */
+  nullproc,  /* NL */
+  array,  /* ARRAY */
+  matchop,  /* MATCH */
+  matchop,  /* NOTMATCH */
+  nullproc,  /* MATCHOP */
+  nullproc,  /* FINAL */
+  nullproc,  /* DOT */
+  nullproc,  /* ALL */
+  nullproc,  /* CCL */
+  nullproc,  /* NCCL */
+  nullproc,  /* CHAR */
+  nullproc,  /* OR */
+  nullproc,  /* STAR */
+  nullproc,  /* QUEST */
+  nullproc,  /* PLUS */
+  nullproc,  /* EMPTYRE */
+  boolop,  /* AND */
+  boolop,  /* BOR */
+  bitops,  /* BITOR */
+  bitops,  /* BITAND */
+  bitops,  /* BITCOMPL */
+  bitops,  /* BITLSHIFT */
+  bitops,  /* BITRSHIFT */
+  bitops,  /* BITXOR */
+  nullproc,  /* APPEND */
+  relop,  /* EQ */
+  relop,  /* GE */
+  relop,  /* GT */
+  relop,  /* LE */
+  relop,  /* LT */
+  relop,  /* NE */
+  instat,  /* IN */
+  arg,  /* ARG */
+  bltin,  /* BLTIN */
+  jump,  /* BREAK */
+  closefile,  /* CLOSE */
+  jump,  /* CONTINUE */
+  awkdelete,  /* DELETE */
+  dostat,  /* DO */
+  jump,  /* EXIT */
+  forstat,  /* FOR */
+  nullproc,  /* FUNC */
+  sub,  /* SUB */
+  gsub,  /* GSUB */
+  ifstat,  /* IF */
+  sindex,  /* INDEX */
+  nullproc,  /* LSUBSTR */
+  matchop,  /* MATCHFCN */
+  jump,  /* NEXT */
+  jump,  /* NEXTFILE */
+  arith,  /* ADD */
+  arith,  /* MINUS */
+  arith,  /* MULT */
+  arith,  /* DIVIDE */
+  arith,  /* MOD */
+  assign,  /* ASSIGN */
+  nullproc,  /* ASGNOP */
+  assign,  /* ADDEQ */
+  assign,  /* SUBEQ */
+  assign,  /* MULTEQ */
+  assign,  /* DIVEQ */
+  assign,  /* MODEQ */
+  assign,  /* POWEQ */
+  printstat,  /* PRINT */
+  awkprintf,  /* PRINTF */
+  awksprintf,  /* SPRINTF */
+  nullproc,  /* ELSE */
+  intest,  /* INTEST */
+  condexpr,  /* CONDEXPR */
+  incrdecr,  /* POSTINCR */
+  incrdecr,  /* PREINCR */
+  incrdecr,  /* POSTDECR */
+  incrdecr,  /* PREDECR */
+  nullproc,  /* VAR */
+  nullproc,  /* IVAR */
+  getnf,  /* VARNF */
+  call,  /* CALL */
+  nullproc,  /* NUMBER */
+  nullproc,  /* STRING */
+  geterrno,  /* VARERR */
+  nullproc,  /* REGEXPR */
+  awkgetline,  /* GETLINE */
+  jump,  /* RETURN */
+  split,  /* SPLIT */
+  substr,  /* SUBSTR */
+  whilestat,  /* WHILE */
+  cat,  /* CAT */
+  boolop,  /* NOT */
+  arith,  /* UMINUS */
+  arith,  /* POWER */
+  nullproc,  /* DECR */
+  nullproc,  /* INCR */
+  indirect,  /* INDIRECT */
+  nullproc,  /* LASTTOKEN */
+};
+
+char *tokname(int n)
+{
+  static char buf[100];
+
+  if (n < FIRSTTOKEN || n > LASTTOKEN) {
+    sprintf(buf, "token %d", n);
+    return buf;
+  }
+  return printname[n-FIRSTTOKEN];
+}
+
+static const short yylhs[] = {               -1,
+  0,  0,   36,   36,   37,   37,   38,   38,   38,   38,
+   38,   38,   33,   33,   26,   26,   24,   24,   41,   22,
+   42,   22,   43,   22,   20,   20,   23,   30,   30,   34,
+   34,   35,   35,   29,   29,   15,   15,  1,  1,   10,
+   11,   11,   11,   11,   11,   11,   11,   44,   11,   12,
+   12,  6,  6,  3,  3,  3,  3,  3,  3,  3,
+  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,
+  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
+  2,  2,  2,  4,  4,  5,  5,  7,  7,  7,
+   40,   40,   28,   28,   28,   28,   31,   31,  9,  9,
+   45,   13,   32,   32,   14,   14,   14,   14,   14,   14,
+   14,   14,   27,   27,   16,   16,   46,   47,   16,   16,
+   16,   16,   16,   16,   16,   16,   16,   16,   16,   16,
+   48,   16,   16,   16,   17,   17,   39,   39,  8,  8,
+  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+  8,  8,   18,   18,   18,   18,   21,   21,   21,   19,
+   19,   19,   19,   25,
+};
+static const short yylen[] = {              2,
+  1,  1,  1,  2,  1,  2,  1,  1,  1,  1,
+  1,  1,  1,  2,  1,  2,  1,  2,  0,   12,
+  0,   10,  0,  8,  1,  1,  4,  1,  2,  1,
+  2,  0,  1,  0,  1,  0,  1,  1,  3,  1,
+  1,  4,  4,  7,  3,  4,  4,  0,  9,  1,
+  3,  1,  3,  3,  5,  3,  3,  3,  3,  3,
+  5,  2,  1,  1,  3,  5,  3,  3,  3,  3,
+  3,  3,  3,  3,  3,  3,  3,  5,  4,  3,
+  2,  1,  1,  3,  3,  1,  3,  0,  1,  3,
+  1,  1,  1,  1,  2,  2,  1,  2,  1,  2,
+  0,  4,  1,  2,  4,  4,  4,  2,  5,  2,
+  1,  1,  1,  2,  2,  2,  0,  0,  9,  3,
+  2,  1,  4,  2,  3,  2,  2,  3,  2,  2,
+  0,  3,  2,  2,  1,  2,  1,  1,  4,  3,
+  3,  3,  3,  3,  3,  2,  2,  2,  3,  4,
+  1,  3,  4,  2,  2,  2,  2,  2,  4,  3,
+  2,  1,  6,  4,  6,  6,  3,  6,  6,  1,
+  8,  8,  6,  4,  1,  6,  6,  8,  8,  8,
+  6,  1,  1,  4,  1,  2,  0,  1,  3,  1,
+  1,  1,  1,  4,
+};
+static const short yydefred[] = {             0,
+  2,   93,   94,  0,  1,  0,  0,   95,   96,  0,
+  0,   28,  0,  101,  7,  8,  0,   11,   10,   12,
+  191,  0,  0,  0,  137,  138,  0,  0,  0,  190,
+  185,  192,  0,  170,  175,  193,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   82,  0,   50,
+  0,   99,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   25,   26,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  100,  155,  156,  186,  0,  0,  3,  5,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  157,  158,  0,  112,   29,  0,  0,  0,  0,
+   15,  0,  0,  0,  0,  0,   91,   92,  0,  0,
+  0,  0,  0,  135,  0,  122,  0,  131,  0,  0,
+  0,  0,  0,  0,  0,   13,  167,  0,  0,  0,
+  0,  0,  149,  0,  0,  0,  0,  0,  0,  0,
+  152,  0,  0,  0,  0,  0,  0,  0,   75,  0,
+  0,  0,  0,  0,  0,   77,  0,  4,  0,  6,
+  0,  0,  0,  0,  0,  0,  0,  0,   30,  0,
+  0,  0,   51,  0,  0,  133,  0,  115,  0,  116,
+  0,  0,  121,  0,  0,  126,  127,  134,  0,  129,
+  0,  130,   45,  136,  0,  0,   16,  0,  0,  0,
+  0,  0,  0,  0,   63,  0,  0,  0,  0,   46,
+   47,   14,  0,  0,  0,  102,  164,  150,  0,  188,
+  0,  0,  0,  174,  153,  0,  0,  0,   79,  0,
+  0,   31,  0,   42,  184,  114,  0,  120,   37,  0,
+  0,  0,  128,  0,   17,  0,  132,  118,   97,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   78,  0,  103,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   18,  123,  0,   98,  0,  0,  0,
+   58,   60,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  104,  0,  189,  165,  166,  169,  168,
+  173,  0,  181,  0,  0,  109,  0,  0,  0,  0,
+  0,  0,  0,  163,  177,  0,  176,  0,  0,  0,
+  0,  0,   44,  0,  0,  0,  0,   61,  0,  0,
+  0,  0,  0,  171,  172,  180,  0,  0,  0,  0,
+  179,  178,   49,  0,  0,   24,  0,  0,  0,  119,
+   22,  0,  0,   20,
+};
+static const short yydgoto[] = {              4,
+  5,  132,  221,   61,  222,  155,  223,   47,   48,   49,
+   50,   51,   52,  133,  260,  134,  135,   53,   54,   71,
+  241,  136,  137,  266,  138,  139,  198,  6,  7,  140,
+  270,  289,  239,  199,  191,  100,  101,   56,   57,  141,
+  383,  378,  369,  325,   62,  218,  306,  216,
+};
+static const short yysindex[] = {             -55,
+  0,  0,  0,  0,  0,  -57, 8934,  0,  0,  -90,
+  -90,  0,10035,  0,  0,  0,   -3,  0,  0,  0,
+  0,   39, 6751, -273,  0,  0,   65,   71,   87,  0,
+  0,  0,   91,  0,  0,  0,  292,   92,  106, 6751,
+ 6751,10115, -208, -208, 6751, 8299,  -37,  0,  -10,  0,
+  -40,  0, -167,  -56, 4994,  144,  148, 4994, 4994, 6674,
+   32, -271,10035, 9374,10035, 6751,  -37, -232,  0,  0,
+  150,10035,10035,10035, 9459, 6751, -176,10035,10035, -216,
+ -216, -216,  0,  0,  0,  0, -173,10035,  0,  0,
+10035,10035,10035,10035,10035,10035, -138,10035,  -37, 9541,
+ 9621,10200, 6751, 6751, 6751, 6751, 6751, -103, 4994, 8934,
+10035,  0,  0,10035,  0,  0, -103,  -35,  -35, -138,
+  0, 9188,  152,  163,  -35,  -35,  0,  0,  -35, 9188,
+  174, 8299,  -35,  0, 5101,  0, 6046,  0,  -33, 4994,
+10282,10035,10035, 5227, 5334,  0,  0, 9706,  -72, 9706,
+  177, 7217,  0, 8299,   47, 7308, -110, 7404, 7404,   57,
+  0,   76,  -37, 6751, 7404, 7404, -208, 1335,  0, 1335,
+ 1335, 1335, 1335, 1335, 1335,  0, 7486,  0, 8757,  0,
+ 8661, 6751, -216,  -26,  -26, -216, -216, -216,  0,  -23,
+10035, 5421,  0, 8299,  -13,  0, -103,  0,  -23,  0,
+  154, 6864,  0, 9021,10035,  0,  0,  0, 6864,  0,
+10035,  0,  0,  0,  -70, 6046,  0, 6046, 5500,10035,
+ 9103,  223,  -94,  -37,  0, -143, 7404, 7404,  223,  0,
+  0,  0, 8299, -138, 8299,  0,  0,  0, 9706,  0,
+  120, 9706, 9706,  0,  0,  -37,   69, 9706,  0,10035,
+  -37,  0,  -90,  0,  0,  0,10035,  0,  0,  214,
+  -76, 7579,  0, 7579,  0, 5607,  0,  0,  0,   15,
+  125,10362, -138,10362,  -37, 9788, 9868, 9953, 6751,10362,
+10362,10362, 9706, 9706, 9706,  0, 8299,  0,   16, -213,
+ 7675,  261, 7757,  262,  131, 6946, 8299, 4994,   14,  -32,
+ -138,   16,   16,  0,  0,  -38,  0,   18,10035, 1335,
+  0,  0, 2902, 2978, 9269, 9103,  -37, 9103, 9103, 9103,
+ 7850, 7037, 7135,  0,  -90,  0,  0,  0,  0,  0,
+  0, 9706,  0, 9706, 5694,  0, -103,10035,  273,  276,
+ -138,  137,10362,  0,  0, -121,  0, -121, 4994, 7946,
+  282, 8028,  0, 9021, 8121,   16,10035,  0,   18, 9103,
+  287,  293, 5773,  0,  0,  0,  273, -103, 6046, 8217,
+  0,  0,  0,   16, 9021,  0,  -35, 6046,  273,  0,
+  0,   16, 6046,  0,
+};
+static const short yyrindex[] = {            2721,
+  0,  0,  0,  0,  0, 2808,  350,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   96,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0, 3080,  0,  0,  0,
+  0,  0,  0,  0,  0,  566, 2255,  0, 2528,  0,
+ 2721,  0, 1473,  1,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0, 1786, 1263,  0,  0,
+  0,  0,  0,  0,  0,  0,  207,  0,  0,  417,
+  527,  623,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0, 2403,  0,
+  0,  0,  0,  0,  0,  0,  0,10447,  0,  352,
+  0,  0,  0,  0,  0,  0, 4661,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0, 4288,  0,
+  0,  -27,  0,  0,  0,  0,  0,  0, 6121,  0,
+  -34,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   33,  0,  0,  280,  0,  0,  0,
+  0,  0, 1938,  0,  0,  0, 3174, 2729,  0, 3351,
+ 3646, 3937, 4119, 4186, 4260,  0,  0,  0,   56,  0,
+  595,  0,  737, 1576, 1679,  943, 1057, 1153,  0, 4574,
+  0,  0,  0,  104,  0,  0, 4661,  0, 4784,  0,
+   -5,  0,  0,  302,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0, 5927,  0,  0,  0,  0,  0,
+  479,  842,   -2, 8488,  0, 4473,  0,  0, 8390,  0,
+  0,  0,  281,  0,  305,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0, 2045,  0,  0,  0,  0,
+ 2144,  0, 2631,  0,  0,  0,  0,  0,  0,  0,
+ 4377,  0,  0,  0,  0,  0,  0,  0,  0, 4894,
+  0,  0,  0,  0, 8570,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   51,  0,  239,  0,
+  0, 8390,  0, 8390,  0,  0,  109,  0,  0,10447,
+  0, 6200, 6275,  0,  0,  0,  0,  881,  0, 3384,
+  0,  0,  0,  563,  136,  879,  6,  8,   12,   11,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0, 8844,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+ 8390,  0,  0,  327,  0, 6394,  0,  0,  0,  475,
+  0,  0,  0,  0,  0,  0,  0, 8844,  0,  0,
+  0,  0,  0, 6473,  327,  0,  0,  0,  0,  0,
+  0, 6548,  0,  0,
+};
+static const short yygindex[] = {             0,
+  0, 3468,  301, -191,  0,  -52,  0, 3974,  -39,  178,
+  263,  0,  -60, -184, -314, 2437,  -41, 3689,  835,  0,
+  0,  0,  0,  0,  0,  0,  -44,  0,  323,  2,
+  0, -154, 5265,  -24,  -91,  278,  841,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,
+};
+#define YYTABLESIZE 10802
+static const short yytable[] = {            106,
+  183,  9,   83,  3,  105,  103,   88,  104,   55,  102,
+  106,   58,   59,  111,  114,  105,  144,  145,  3,  259,
+  102,  160,  162,  197,   88,  196,  337,  169,  271,  279,
+  146,  111,   12,  108,  114,  110,   63,  183,  108,  367,
+  183,  183,  183,  183,  183,  183,  105,  183,  106,  232,
+  109,   54,  107,  110,   54,   68,  108,  146,  183,  183,
+  379,  195,   69,  183,  105,   70,  106,  192,   54,   54,
+  107,  151,  149,   52,  200,  146,   52,  203,   64,  255,
+  206,  207,  229,  190,  208,  210,   21,  238,  212,   88,
+  146,   53,  190,  183,   53,  151,   68,  244,  219,   68,
+  146,  225,   68,   65,   72,  256,  336,  302,   66,  303,
+   73,   55,   12,   68,   68,  164,  245,  342,   68,  146,
+  112,  113,  326,  183,  183,   52,   74,   30,   31,   32,
+   75,   78,  151,   36,   54,  107,  151,  151,  151,  151,
+  151,  232,  151,   53,   65,   79,   45,   65,   68,   66,
+   65,  111,   66,  151,  151,   66,   21,  258,  151,  189,
+  288,   65,   65,  146,  263,  308,   66,   66,  146,  259,
+  167,  331,  190,   21,  146,  282,   56,  359,   68,   56,
+  146,  292,  294,  142,  356,  112,  113,  143,  151,  157,
+  259,  204,  280,   56,   56,  281,   65,   30,   56,   32,
+  1,   66,  205,   36,  299,  8,  161,  2,  338,  112,
+  113,  311,  374,  211,   30,   31,   32,  301,  151,  151,
+   36,  234,  2,  236,  382,  240,   65,  189,   88,  217,
+  189,   66,  225,   45,  225,  111,  225,  225,  225,  252,
+  225,  225,  225,  161,  257,  354,  161,  161,  161,  161,
+  161,  161,   88,  161,  298,   88,  335,  110,  265,   56,
+  108,  183,  183,  183,  161,  161,  146,  183,  105,  161,
+  106,  351,  300,   54,  107,  190,  375,  307,  324,  183,
+  183,  183,  183,  183,  183,  183,  183,  183,  183,  183,
+  183,  183,  183,  183,  183,  183,  183,   54,  183,  161,
+   54,  328,  330,  225,  183,  183,  183,  363,  183,  340,
+  183,  341,  190,  288,  107,  357,   68,   68,   68,  183,
+  187,   84,  365,  187,   84,  107,  349,  371,  183,  161,
+  161,  232,  380,  372,   68,   68,  183,  183,  183,  183,
+  183,  183,  183,  190,  183,   85,  183,  183,   85,   38,
+  183,   39,  183,  183,  183,  183,  151,  151,  151,   68,
+   36,   48,  151,   21,   65,   65,   65,   36,  253,   66,
+   66,   66,  193,  110,  151,  151,  151,  151,  151,  151,
+  151,  151,  151,  151,  151,  151,  151,  151,  151,  151,
+  151,  151,  0,  151,  0,  0,  0,  0,   56,  151,
+  151,  151,  0,  151,   30,  151,   32,   65,  0,  0,
+   36,  0,   66,  0,  151,   56,  147,  0,  0,  0,
+  0,  0,   56,  151,  0,   56,  0,  0,  0,  0,
+  0,  151,  151,  151,  151,  151,  151,  151,  0,  151,
+  0,  151,  151,  0,  0,  151,  0,  151,  151,  151,
+  151,  0,  0,  147,  0,  0,  147,  147,  147,  147,
+  147,  147,  0,  147,  0,  0,  0,  161,  161,  161,
+  0,  0,  0,  161,  147,  147,  0,  0,  0,  147,
+  0,  0,  0,  0,  0,  161,  161,  161,  161,  161,
+  161,  161,  161,  161,  161,  161,  161,  161,  276,  161,
+  161,  161,  161,  0,  161,  0,  0,  0,  0,  147,
+  161,  161,  161,  0,  161,   55,  161,  0,   55,   86,
+  0,  0,   86,  0,  0,  161,  146,  0,  0,  0,
+  0,  0,   55,   55,  161,  0,  0,   86,  0,  147,
+  147,  0,  161,  161,  161,  161,  161,  161,  161,  0,
+  161,  0,  161,  161,  0,  0,  161,  0,  161,  161,
+  161,  161,  0,  146,  0,   40,  146,  146,  146,  146,
+  146,  146,  310,  146,  313,  0,  314,  315,  316,  0,
+  318,  319,  320,   76,  146,  146,   21,  276,  0,  146,
+  276,  276,  276,  276,   67,  276,  276,  276,   55,  0,
+  0,  0,   86,   57,  0,  0,   57,  0,  0,   40,
+  0,  0,   40,  0,  0,  0,  0,  0,  0,  146,
+   57,   57,  148,  0,   40,   57,  0,   30,   31,   32,
+  0,  0,  0,   36,  0,   67,  0,  276,   67,  0,
+  0,   67,  0,  360,  0,  0,   45,  0,  0,  146,
+  146,  0,   67,   67,  0,  0,  0,   67,  0,  148,
+  0,  0,  148,  148,  148,  148,  148,  148,  0,  148,
+  0,  0,  0,  0,  0,  0,  0,  147,  147,  147,
+  148,  148,  0,  147,  0,  148,   57,   67,   40,  0,
+  0,  0,  0,  0,  0,  147,  147,  147,  147,  147,
+  147,  147,  147,  147,  147,  147,  147,  147,  147,  147,
+  147,  147,  147,  0,  147,  148,  0,   67,  0,  0,
+  147,  147,  147,  0,  147,  0,  147,  0,  0,  0,
+  0,  0,  0,  0,  0,  147,  143,   55,  0,  0,
+  0,   86,  0,  0,  147,  148,  148,  0,  0,  0,
+  0,  0,  147,  147,  147,  147,  147,  147,  147,  0,
+  147,   55,  147,  147,   55,   86,  147,  0,   86,  147,
+  147,  147,  0,  143,  0,  0,  143,  143,  143,  143,
+  143,  143,  0,  143,  0,  0,  0,  146,  146,  146,
+  0,  0,  0,  146,  143,  143,  0,  0,  0,  143,
+  0,  0,  0,  0,  0,  146,  146,  146,  146,  146,
+  146,  146,  146,  146,  146,  146,  146,  146,  146,  146,
+  146,  146,  146,  0,  146,   57,   40,   40,   40,  143,
+  146,  146,  146,  0,  146,  0,  146,  0,  0,  0,
+  0,   57,   57,  0,  0,  146,  0,  0,  0,   57,
+  0,  0,   57,  0,  146,   67,   67,   67,  0,  143,
+  143,  0,  146,  146,  146,  146,  146,  146,  146,   40,
+  146,  0,  146,  146,   67,  0,  146,  0,  0,  146,
+  146,  146,   89,  148,  148,  148,  0,  0,  0,  148,
+  0,  0,  0,  0,  0,  0,  0,  0,   67,  0,
+   89,  148,  148,  148,  148,  148,  148,  148,  148,  148,
+  148,  148,  148,  148,  148,  148,  148,  148,  148,   87,
+  148,   90,   87,  0,  0,  0,  148,  148,  148,  0,
+  148,  176,  148,  0,  0,  0,  0,   87,  0,   90,
+  0,  148,  142,  0,  0,  0,  0,  0,  0,  0,
+  148,  0,  0,  0,  201,  0,  0,  0,  148,  148,
+  148,  148,  148,  148,  148,   89,  148,  0,  148,  148,
+  0,  0,  148,  0,  0,  148,  148,  148,  0,  142,
+  0,  0,  142,  142,  142,  142,  142,  142,  0,  142,
+  0,  0,  0,  0,  0,  0,  0,  143,  143,  143,
+  142,  142,   87,  143,   90,  142,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  143,  143,  143,  143,  143,
+  143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+  143,  143,  143,  0,  143,  142,  0,  0,  261,  0,
+  143,  143,  143,  0,  143,  0,  143,  0,  0,  0,
+  0,  0,  0,  0,  0,  143,  144,  0,  0,  0,
+  0,  277,  0,  0,  143,  142,  142,  0,  286,  0,
+  0,  0,  143,  143,  143,  143,  143,  143,  143,  0,
+  143,  295,  143,  143,  0,  0,  143,  0,  0,  143,
+  143,  143,  0,  144,  0,  0,  144,  144,  144,  144,
+  144,  144,  0,  144,   89,  0,  0,  312,  0,  0,
+  0,  0,  0,  0,  144,  144,  0,  0,  0,  144,
+  0,  0,  0,  0,  0,  0,  0,  0,   89,  0,
+  0,   89,  0,  0,  0,  339,  0,  0,  0,  0,
+  0,   87,  0,   90,  0,  0,  0,  0,  0,  144,
+  277,  0,  145,  277,  277,  277,  277,  0,  277,  277,
+  277,  0,  0,  0,  0,   87,  0,   90,   87,  0,
+   90,  0,  0,  0,  0,  358,  0,  0,  0,  144,
+  144,  0,  0,  0,  0,  0,  0,  0,  0,  145,
+  0,  0,  145,  145,  145,  145,  145,  145,  0,  145,
+  277,  0,  0,  142,  142,  142,  0,  0,  0,  142,
+  145,  145,  0,  0,  0,  145,  0,  0,  0,  0,
+  0,  142,  142,  142,  142,  142,  142,  142,  142,  142,
+  142,  142,  142,  142,  142,  142,  142,  142,  142,  0,
+  142,  0,  0,  0,  0,  145,  142,  142,  142,  0,
+  142,  0,  142,  0,  0,  0,  0,  0,  0,  0,
+  0,  142,  182,  0,  0,  0,  0,  0,  0,  0,
+  142,  0,  0,  0,  0,  145,  145,  0,  142,  142,
+  142,  142,  142,  142,  142,  0,  142,  0,  142,  142,
+  0,  0,  142,  0,  0,  142,  142,  142,  0,  182,
+  0,  0,  182,  182,  182,  182,  182,  182,  0,  182,
+  0,  0,  0,  0,  0,  0,  0,  144,  144,  144,
+  182,  182,  0,  144,  0,  182,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  144,  144,  144,  144,  144,
+  144,  144,  144,  144,  144,  144,  144,  144,  144,  144,
+  144,  144,  144,  0,  144,  182,  0,  0,  0,  0,
+  144,  144,  144,  0,  144,  0,  144,  0,  0,  0,
+  0,  0,  0,  0,   65,  144,  0,   40,  0,   41,
+  0,  0,  0,  0,  144,  182,  182,  0,  0,  0,
+  0,  0,  144,  144,  144,  144,  144,  144,  144,  0,
+  144,  0,  144,  144,  0,  0,  144,  0,  0,  144,
+  144,  144,  0,  145,  145,  145,  0,  0,  0,  145,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  145,  145,  145,  145,  145,  145,  145,  145,  145,
+  145,  145,  145,  145,  145,  145,  145,  145,  145,  0,
+  145,  0,  0,  0,  0,  0,  145,  145,  145,  0,
+  145,  0,  145,  0,  0,  0,  0,  0,  0,  0,
+  0,  145,  182,  0,  0,  0,  0,  0,  0,  0,
+  145,  0,  0,  0,  0,  0,  0,  0,  145,  145,
+  145,  145,  145,  145,  145,  0,  145,  0,  145,  145,
+  0,  0,  145,  0,  0,  145,  145,  145,  0,  182,
+  0,  0,  182,  182,  182,  182,  182,  182,  0,  182,
+  0,  0,  0,  182,  182,  182,  0,  0,  0,  182,
+  182,  182,  0,  0,  0,  182,  0,  0,  0,  0,
+  0,  182,  182,  182,  182,  182,  182,  182,  182,  182,
+  182,  182,  182,  182,  182,  182,  182,  182,  182,  0,
+  182,  0,  0,  0,  0,  182,  182,  182,  182,  0,
+  182,  0,  182,  0,  0,  140,  0,  0,  0,  0,
+  0,  182,  0,  0,  0,  0,  0,  0,  0,  0,
+  182,  0,  0,  0,  0,  182,  182,  0,  182,  182,
+  182,  182,  182,  182,  182,  0,  182,  0,  182,  182,
+  0,  0,  182,  0,  182,  140,  140,  182,  140,  140,
+  140,  0,  0,  0,  0,  0,  0,  0,  0,   21,
+   22,  0,   23,  140,  140,  0,  0,  0,  140,   25,
+   26,  0,   27,  0,   28,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   29,  0,  0,  0,  0,  0,  140,  0,
+   30,   31,   32,   33,   34,   35,   36,  0,  141,  0,
+   38,   39,  0,  0,   66,  0,  0,   43,   44,   45,
+  0,  0,  0,  0,  0,  0,  0,  0,  140,  140,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  141,  141,
+  0,  141,  141,  141,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  182,  182,  182,  141,  141,  0,  182,
+  0,  141,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  182,  182,  182,  182,  182,  182,  182,  182,  0,
+  182,  182,  182,  182,  182,  182,  182,  182,  182,  0,
+  182,  141,  0,  0,  0,  0,  182,  182,  182,  0,
+  182,  0,  182,  0,  0,  154,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  182,  141,  141,  0,  0,  0,  0,  0,  182,  182,
+  182,  182,  182,  182,  182,  0,  182,  0,  182,  182,
+  0,  0,  182,  0,  182,  154,  154,  182,  0,  154,
+  0,  0,  0,  0,  0,  0,  140,  140,  140,  0,
+  0,  0,  140,  154,  154,  0,  0,  0,  154,  0,
+  0,  0,  0,  0,  140,  140,  140,  140,  140,  140,
+  140,  140,  140,  140,  140,  140,  140,  140,  140,  140,
+  140,  140,  0,  140,  0,  0,  0,  0,  154,  140,
+  140,  140,  0,  140,  0,  140,  0,  0,  0,  0,
+  0,  0,  0,  0,  140,  0,  0,  0,  0,  0,
+  0,  0,  0,  140,  0,  0,  0,  0,  154,  154,
+  0,  140,  140,  140,  140,  140,  140,  140,  0,  140,
+  0,  140,  140,  0,  0,  140,  0,  0,  140,  140,
+  140,  0,  0,  0,  0,  0,  0,  160,  0,  141,
+  141,  141,  0,  0,  0,  141,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  141,  141,  141,
+  141,  141,  141,  141,  141,  141,  141,  141,  141,  141,
+  141,  141,  141,  141,  141,  0,  141,  160,  160,  0,
+  0,  160,  141,  141,  141,  0,  141,  0,  141,  0,
+  0,  0,  0,  0,  0,  160,  160,  141,  0,  0,
+  160,  0,  0,  0,  0,  0,  141,  0,  0,  0,
+  0,  0,  0,  0,  141,  141,  141,  141,  141,  141,
+  141,  0,  141,  0,  141,  141,  0,  0,  141,  0,
+  160,  141,  141,  141,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  159,  0,  154,  154,  154,  0,
+  0,  0,  154,  0,  0,  0,  0,  0,  0,  0,
+  160,  160,  0,  0,  154,  154,  154,  154,  154,  154,
+  154,  154,  154,  154,  154,  154,  154,  154,  154,  154,
+  154,  154,  0,  154,  159,  159,  0,  0,  159,  154,
+  154,  154,  0,  154,  0,  154,  0,  0,  0,  0,
+  0,  0,  159,  159,  154,  0,  0,  159,  0,  0,
+  0,  0,  0,  154,  0,  0,  0,  0,  0,  0,
+  0,  154,  154,  154,  154,  154,  154,  154,  0,  154,
+  0,  154,  154,  0,  0,  154,  0,  159,  154,  154,
+  154,  0,  0,  139,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  159,  159,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  139,  139,  0,  0,  139,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  160,  160,
+  160,  139,  139,  0,  160,  0,  139,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  160,  160,  160,  160,
+  160,  160,  160,  160,  160,  160,  160,  160,  160,  160,
+  160,  160,  160,  160,  0,  160,  139,  0,  0,  0,
+  0,  160,  160,  160,  0,  160,  0,  160,  0,  0,
+  0,  0,  0,  0,   83,  0,  160,  0,  0,  0,
+  0,  0,  0,  0,  0,  160,  139,  139,  0,  0,
+  0,  0,  0,  160,  160,  160,  160,  160,  160,  160,
+  0,  160,  0,  160,  160,  0,  0,  160,  0,  0,
+  160,  160,  160,  0,   83,   83,  0,  0,   83,  0,
+  0,  0,  0,  0,  0,  159,  159,  159,  0,  0,
+  0,  159,   83,   83,  0,  0,  0,   83,  0,  0,
+  0,  0,  0,  159,  159,  159,  159,  159,  159,  159,
+  159,  159,  159,  159,  159,  159,  159,  159,  159,  159,
+  159,  0,  159,  0,  0,  0,  0,   83,  159,  159,
+  159,  0,  159,  0,  159,  0,  0,  0,  0,  0,
+  0,  0,  0,  159,  0,  0,  0,  0,  0,  0,
+  0,  0,  159,  0,  0,  0,  0,   83,   83,  0,
+  159,  159,  159,  159,  159,  159,  159,  0,  159,  0,
+  159,  159,  0,  0,  159,  0,  0,  159,  159,  159,
+  0,  0,   81,  0,  139,  139,  139,  0,  0,  0,
+  139,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  139,  139,  139,  139,  139,  139,  139,  139,
+  139,  139,  139,  139,  139,  139,  139,  139,  139,  139,
+  0,  139,   81,   81,  0,  0,   81,  139,  139,  139,
+  0,  139,  0,  139,  0,  0,  0,  0,  0,  0,
+   81,   81,  139,  0,  0,   81,  0,  0,  0,  0,
+  0,  139,  0,  0,  0,  0,  0,  0,  0,  139,
+  139,  139,  139,  139,  139,  139,  0,  139,  0,  139,
+  139,  0,  0,  139,  0,   81,  139,  139,  139,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   83,   83,   83,  0,  0,
+  0,   83,  0,  0,  0,   81,   81,   41,  0,  0,
+  0,  0,  0,   83,   83,   83,   83,   83,   83,   83,
+   83,  0,   83,   83,   83,   83,   83,   83,   83,   83,
+   83,  0,   83,  0,  0,  0,  0,  0,   83,   83,
+   83,  0,   83,  0,   83,  0,  0,   41,  0,  0,
+   41,  214,   41,  215,   41,  0,  0,  0,  0,  0,
+  214,  214,   83,  0,  0,  0,   41,  0,  0,  0,
+   83,   83,   83,   83,   83,   83,   83,  0,   83,  0,
+   83,   83,  0,  0,   83,  0,  0,   83,   83,   83,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  214,  0,
+   43,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  267,  0,  268,  214,  0,  0,  0,  0,
+  0,  0,  0,   81,   81,   81,  0,  0,  0,   81,
+   43,  0,  0,   43,  0,   43,  0,   43,  0,  0,
+  0,   81,   81,   81,   81,   81,   81,   81,   81,   43,
+   81,   81,   81,   81,   81,   81,   81,   81,   81,  0,
+   81,  0,  305,  0,  0,  0,   81,   81,   81,  0,
+   81,  0,   81,  0,  0,  0,  0,  0,  0,  0,
+   34,  0,  0,  0,  0,  0,  0,  0,   76,  0,
+   81,  0,  0,  0,  0,  0,  0,  0,   81,   81,
+   81,   81,   81,   81,   81,  0,   81,  0,   81,   81,
+  0,  0,   81,  0,  0,   81,   81,   81,  0,  0,
+   34,  0,  0,   34,  0,   34,  0,   34,  0,   76,
+  0,  214,   76,  0,  0,   76,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   76,   76,   41,   41,
+   41,   76,  0,  0,  0,  0,  0,  0,  0,  214,
+  0,  0,  0,  0,  0,  376,  0,   35,   41,   41,
+   41,   41,   41,   41,  381,  0,  0,  0,  0,  384,
+  0,   76,   41,   41,  0,   41,  0,  0,  0,  0,
+  0,   41,   41,   41,  0,   41,  0,   41,  0,  0,
+  0,  0,  0,   34,  0,  0,  0,   35,  0,  0,
+   35,   76,   35,  0,   35,   41,  0,  0,  0,  0,
+  0,  0,  0,   41,   41,   41,   41,   41,   41,   41,
+  0,   41,  0,   41,   41,  0,  0,   41,  0,  0,
+   41,   41,   41,  0,  0,  0,  0,  0,  0,  0,
+  0,   43,   43,   43,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   43,   43,   43,   43,   43,   43,  0,  0,  0,
+  0,  0,  0,  0,  0,   43,   43,  0,   43,  0,
+   35,  0,  0,  0,   43,   43,   43,  0,   43,  0,
+   43,   65,  0,  0,   40,  0,   41,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   43,  343,
+  0,  0,  0,  0,  274,  0,   43,   43,   43,   43,
+   43,   43,   43,  0,   43,  0,   43,   43,  0,  0,
+   43,   34,   34,   43,   43,   43,  0,  0,  0,   76,
+   76,   76,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   34,   34,   34,   34,   34,   34,   76,   76,   76,
+   76,   76,   76,   76,   76,   34,   34,   65,   34,  0,
+   40,  0,   41,  0,   34,   34,   34,  0,   34,  0,
+   34,  0,   76,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   34,  0,
+  0,  0,  0,  0,  0,  0,   34,   34,   34,   34,
+   34,   34,   34,  0,   34,  0,   34,   34,   35,   35,
+   34,  0,   76,   34,   34,   34,  0,  0,  0,  162,
+  0,  0,  0,  0,  0,  0,  0,  0,   35,   35,
+   35,   35,   35,   35,  0,  0,  0,  0,  0,  0,
+  0,  0,   35,   35,  0,   35,  0,  0,  0,  0,
+  0,   35,   35,   35,  0,   35,  162,   35,  0,  162,
+  162,  162,  162,  162,  162,  0,  162,  0,  0,  0,
+  0,  0,  0,  0,  0,   35,  0,  162,  162,  0,
+  0,  0,  162,   35,   35,   35,   35,   35,   35,   35,
+  0,   35,  0,   35,   35,  0,  0,   35,  0,  0,
+   35,   35,   35,  0,  0,  0,  0,  0,  272,  0,
+  0,  0,  162,   80,  0,  0,  0,  0,  0,  0,
+   89,   90,   15,   16,   17,   18,   19,   20,  0,  0,
+  0,  0,  0,  0,  0,  273,   21,   22,  0,   23,
+  0,  0,  162,  162,  0,  0,   25,   26,  0,   27,
+  0,   28,  0,   80,   80,  0,   80,   80,   80,  0,
+   80,  0,  0,  0,  0,  0,  0,  0,  0,   29,
+  0,   80,   80,  0,  0,  0,   80,   30,   31,   32,
+   33,   34,   35,   36,  272,   37,  0,   38,   39,  0,
+  0,   66,  0,  0,   43,   44,   45,  0,   15,   16,
+   17,   18,   19,   20,  0,  0,   80,  0,  0,  0,
+  0,  273,   21,   22,  0,   23,  0,  0,  0,  0,
+  0,  0,   25,   26,  0,   27,  0,   28,  0,  0,
+  0,  0,  0,  0,  0,  0,   80,   80,  0,  0,
+  0,  0,  0,  0,  0,   29,  0,  0,  0,  0,
+  0,  0,  0,   30,   31,   32,   33,   34,   35,   36,
+  0,   37,  0,   38,   39,  0,  0,   66,  0,  0,
+   43,   44,   45,  0,  0,  0,  0,  0,  0,  0,
+  162,  162,  162,  0,  0,  0,  162,  0,  0,  0,
+   69,  0,  0,  0,  0,  0,  0,  0,  162,  162,
+  162,  162,  162,  162,  162,  162,  162,  162,  162,  162,
+  162,  0,  162,  162,  0,  162,  0,  162,  0,  0,
+  0,  0,  0,  162,  162,  162,  0,  162,  0,  162,
+  0,   69,  0,  0,   69,  0,  0,   69,  162,  0,
+  0,  0,  0,  0,  0,  0,  0,  162,   69,   69,
+  0,  0,  0,   69,  0,  0,  0,  0,  162,  162,
+  162,  0,  0,  162,   59,  162,  162,   59,  0,  162,
+  0,  162,  162,  162,   80,   80,   80,  0,  0,  0,
+   80,   59,   59,   69,  0,  0,   59,  0,  0,  0,
+  0,  0,   80,   80,   80,   80,   80,   80,   80,   80,
+  0,   80,   80,   80,   80,   80,   80,   80,  0,   80,
+  0,   80,  0,   69,   46,  0,  0,   80,   80,   80,
+   60,   80,  0,   80,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   80,  0,  0,  0,  0,  0,   59,  0,  0,
+  0,  0,   80,   80,   80,  0,  0,   80,  0,   80,
+   80,  0,  0,   80,  0,  0,   80,   80,  0,  0,
+  152,  154,  156,  0,  0,  0,  0,  0,  0,  158,
+  159,  154,  154,  0,  0,  165,  166,  0,  0,  0,
+  0,  0,  0,  0,  0,  168,  0,  0,  170,  171,
+  172,  173,  174,  175,  0,  177,  0,  179,  181,  0,
+  0,  0,  0,  0,  0,  0,  0,   46,  194,  0,
+  0,  154,  0,  0,  0,  0,  0,  0,  0,  202,
+  0,  0,  0,  0,  0,  0,  0,  209,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  227,
+  228,   69,   69,   69,  0,  233,  0,  235,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   69,
+   69,   69,   69,   69,   69,   69,   69,  0,  0,  0,
+  0,  0,  0,  0,  0,   70,   59,  0,  0,  0,
+  0,  0,  0,  0,   69,  0,  0,  0,   46,  0,
+  0,  0,   59,   59,   59,   59,   59,   59,   59,   59,
+   59,  0,  262,   59,  0,  0,  0,  0,  264,  0,
+  0,  0,  0,  0,  0,  0,   70,   60,  0,   70,
+  0,  0,   70,  0,   69,  0,  0,  0,  0,  0,
+  0,  0,  0,   70,   70,  0,  287,  0,   70,  291,
+  293,   68,  0,  0,  0,  296,  0,  297,  0,  0,
+  0,  0,  0,  0,  154,   77,  0,   59,   68,   68,
+   68,   84,   85,   68,   68,  0,  0,  0,   70,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   68,  0,
+  321,  322,  323,  0,   68,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   68,  0,  0,  0,   70,  0,
+  0,  0,  0,  0,  0,  0,   60,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   68,   68,   68,   68,   68,   68,  0,  0,  0,  350,
+  0,  352,  0,  0,  0,  355,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   68,  0,  0,  0,  370,  0,  0,  0,  0,  226,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   68,  0,   68,  0,   68,  0,   68,   68,  0,  0,
+  0,  0,   68,   68,   68,  249,   68,  0,   68,   68,
+   68,   68,   68,   68,  0,   68,  0,   68,  0,   68,
+   68,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   68,  0,  0,  0,  0,  0,  0,  0,
+   68,  0,  0,  0,  0,  0,  0,   68,  0,  0,
+  0,  0,  0,  0,  0,  0,   70,   70,   70,   68,
+  0,  0,  0,  0,  0,   68,   68,  0,  0,  0,
+  0,   68,  0,   68,   70,   70,   70,   70,   70,   70,
+   70,   70,  0,  0,  0,  0,   71,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   70,
+   68,  0,   68,  0,  0,  0,  0,  0,  0,  0,
+  226,  0,  226,  0,  226,  226,  226,   68,  226,  226,
+  226,  0,  0,  0,  0,   68,  0,   71,  0,   68,
+   71,   68,  0,   71,   68,   68,  0,  0,  0,   70,
+  0,  0,  0,  0,   71,   71,   67,  0,   68,   71,
+  0,   68,   68,   68,   68,  0,   68,   68,   68,   68,
+   68,   68,  0,   80,   81,   82,  0,  0,   86,   99,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   71,
+  0,  226,  0,   99,  361,  0,  362,  0,   68,   82,
+   68,  0,  0,   68,  0,  0,  0,  0,   68,  163,
+  0,  0,  0,  0,  0,  0,  0,  0,   68,   71,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  183,  184,  185,  186,  187,
+  188,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   99,  0,  0,  0,  0,
+  0,  0,  0,  0,  224,  0,  0,  0,   72,  0,
+  0,  0,  0,  0,  0,   99,  0,   99,  0,   99,
+  0,   99,   99,  0,  0,  0,  0,  246,   99,   99,
+  0,   99,  0,   99,   99,   99,   99,   99,   99,  0,
+   99,  0,   99,  0,   99,  251,  0,  0,  0,   72,
+  0,  0,   72,  0,  0,   72,  0,   99,  0,  0,
+  0,  0,  0,  0,  0,   99,   72,   72,  0,  0,
+  0,   72,   99,  0,  0,   73,  0,  0,  0,  0,
+  0,  0,  0,  0,  275,  0,  0,   71,   71,   71,
+   99,   99,  0,  0,  0,  0,   99,  0,   99,  0,
+  0,   72,  0,  0,  0,   71,   71,   71,   71,   71,
+   71,   71,   71,  0,  0,  0,   73,  0,  0,   73,
+  0,  0,   73,  0,  0,   99,  0,   99,  0,  0,
+   71,   72,  0,   73,   73,  224,  0,  224,   73,  224,
+  224,  224,  317,  224,  224,  224,  0,  0,  0,   74,
+   99,  0,  0,  0,   99,  0,   99,  0,  0,   99,
+   99,  0,  0,  0,  0,  0,  0,  0,   73,  0,
+   71,  0,  0,  275,  0,  0,  275,  275,  275,  275,
+  0,  275,  275,  275,   99,   99,   99,  0,  0,  0,
+   74,  0,  0,   74,  0,  0,   74,  0,   73,  0,
+  0,  0,  0,  0,  0,  0,  224,   74,   74,  0,
+  0,  0,   74,   99,  190,   99,  0,  190,   99,  190,
+  190,  0,  190,  275,  190,  0,  0,  0,  0,  0,
+  0,  0,  0,   99,  0,  0,  0,  0,  0,  0,
+  190,  0,   74,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  190,   72,
+   72,   72,   74,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   72,   72,   72,
+   72,   72,   72,   72,   72,  0,  0,  0,  0,  0,
+  0,  190,  0,  183,  0,  0,  183,  0,  183,  183,
+  0,  183,   72,  183,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  183,  0,  0,  0,  183,
+  0,  0,  0,  0,  0,  0,   73,   73,   73,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   72,  0,   73,   73,   73,   73,   73,   73,
+   73,   73,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   73,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  183,  0,  0,  0,  0,  0,  0,  0,  0,  182,
+  0,  0,  182,  182,  182,  182,  182,  182,  0,  182,
+   74,   74,   74,  0,  0,  0,  0,  0,  0,   73,
+  182,  182,  0,  0,  0,  182,  0,  0,   74,   74,
+   74,   74,   74,   74,   74,   74,  0,  0,  0,  0,
+  0,  0,  0,  0,  190,  0,  0,  0,  0,  0,
+  0,  0,  0,   74,  0,  0,  190,  190,  190,  190,
+  190,  190,  190,  190,  0,  190,  190,  190,  190,  190,
+  190,  190,  190,  190,  0,  190,  0,  0,  0,  0,
+  0,  0,  190,  190,  0,  190,  182,  190,  0,  0,
+  0,  0,  0,   74,  0,  0,  190,  0,  0,  0,
+  0,  0,  0,   33,   33,  190,   33,  0,   33,  0,
+   33,  0,  0,  190,  190,  190,  190,  190,  190,  190,
+  0,  190,   33,  190,  190,  0,  0,  190,  0,  190,
+  190,  190,  190,  183,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  183,  183,  183,  183,  183,
+  183,  183,  183,  0,  183,  183,  183,  183,  183,  183,
+  0,  183,  183,  0,  183,  0,  0,  0,  0,  0,
+  0,  183,  183,  0,  183,  0,  183,  0,  0,  0,
+  0,  0,  0,  0,  0,  183,   33,  0,   33,  0,
+   32,  0,  0,   32,  183,   32,  0,   32,  0,  0,
+  0,  0,  183,  183,  183,  183,  183,  183,  183,   32,
+  183,  0,  183,  183,  0,  0,  183,  0,  183,  183,
+  183,  183,  0,  0,  0,  182,  0,  0,  0,  182,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  182,  182,  182,  182,  182,  182,  182,  182,  182,
+  0,  0,  182,  0,  0,  0,  182,  182,  182,  0,
+  182,  0,  0,  0,  0,  0,  0,  182,  182,  0,
+  182,  0,  182,   32,  0,   32,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  182,  0,  0,  0,  0,  0,  0,  0,  182,  182,
+  182,  182,  182,  182,  182,  0,  182,  0,  182,  182,
+  0,  0,  182,  113,  182,  0,  113,  182,  113,   33,
+  113,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  113,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   33,   33,   33,   33,   33,   33,
+  0,  0,  0,  0,  0,  0,  0,  0,   33,   33,
+   33,   33,   33,   33,   33,   33,   33,  0,   33,   33,
+   33,   33,  0,   33,   33,   33,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   33,
+   33,   33,   33,  0,  0,  0,  113,  0,  113,   33,
+   33,   33,   33,   33,   33,   33,   32,   33,   33,   33,
+   33,   33,  0,   33,  0,  0,   33,   33,   33,  0,
+  0,  0,  0,  125,  0,  0,  125,  0,  125,  0,
+  125,   32,   32,   32,   32,   32,   32,  0,  0,  0,
+  0,  0,  125,  0,  0,   32,   32,   32,   32,   32,
+   32,   32,   32,   32,  0,   32,   32,   32,   32,  0,
+   32,   32,   32,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   32,   32,   32,   32,
+  0,  0,  0,  0,  0,  0,   32,   32,   32,   32,
+   32,   32,   32,  0,   32,   32,   32,   32,   32,  0,
+   32,  0,  0,   32,   32,   32,  125,  0,  125,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   13,  0,  0,   40,  0,   41,  113,
+   14,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  117,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  113,  113,  113,  113,  113,  113,
+  0,  0,  0,  0,  0,  0,  0,  0,  113,  113,
+  113,  113,  113,  113,  113,  113,  113,  0,  113,  113,
+  113,  113,  0,  113,  113,  113,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  113,
+  113,  113,  113,  0,  0,  0,   12,  0,  0,  113,
+  113,  113,  113,  113,  113,  113,  0,  113,  113,  113,
+  113,  113,  0,  113,  0,  0,  113,  113,  113,  0,
+   13,  0,  0,   40,  0,   41,  0,   14,  0,  125,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  117,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  125,  125,  125,  125,  125,  125,
+  0,  0,  0,  0,  0,  0,  0,  0,  125,  125,
+  125,  125,  125,  125,  125,  125,  125,  0,  125,  125,
+  125,  125,  0,  125,  125,  125,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  125,
+  125,  125,  125,   12,  0,  213,  0,  0,  0,  125,
+  125,  125,  125,  125,  125,  125,  0,  125,  125,  125,
+  125,  125,  0,  125,  0,  0,  125,  125,  125,  115,
+  0,  0,  0,  0,  0,  0,  116,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   13,  0,  0,   40,
+  0,   41,  0,   14,   15,   16,   17,   18,   19,   20,
+  0,  0,  0,  0,  0,  117,  0,  0,   21,   22,
+  118,   23,  119,  120,  121,  122,  123,  0,   25,   26,
+  124,   27,  0,   28,  125,  126,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  127,
+  128,   29,  0,  0,  148,  150,  0,  0,  0,  129,
+   31,   32,   33,   34,   35,   36,  0,   37,  130,   38,
+   39,  131,  0,   42,  0,  0,   43,   44,   45,   12,
+  0,  230,  0,  0,  0,  0,  115,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   13,  0,  0,   40,  0,   41,  0,
+   14,   15,   16,   17,   18,   19,   20,  0,  0,  0,
+  0,  0,  117,  0,  0,   21,   22,  118,   23,  119,
+  120,  121,  122,  123,  0,   25,   26,  124,   27,  0,
+   28,  125,  126,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  242,  243,  0,  0,  127,  128,   29,  247,
+  248,  0,  0,  0,  0,  0,  129,   31,   32,   33,
+   34,   35,   36,  0,   37,  130,   38,   39,  131,  0,
+   42,  0,  0,   43,   44,   45,   12,  0,  231,  0,
+   13,  0,  0,   40,  0,   41,  0,   14,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  117,
+  0,  0,  115,  0,  0,  0,  278,  0,  0,  0,
+  0,  283,  284,  285,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  290,  0,   15,   16,   17,
+   18,   19,   20,  0,  0,  0,  0,  0,  0,  0,
+  0,   21,   22,  118,   23,  119,  120,  121,  122,  123,
+  0,   25,   26,  124,   27,  150,   28,  125,  126,   13,
+  0,  0,   40,   12,   41,  254,   14,  0,  0,  0,
+  0,  0,  127,  128,   29,  0,  0,  0,  117,  332,
+  334,  0,  129,   31,   32,   33,   34,   35,   36,  0,
+   37,  130,   38,   39,  131,  0,   42,  0,  0,   43,
+   44,   45,  0,  0,  0,  0,  346,  348,  0,  115,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  150,  0,  0,  0,
+  0,  0,  0,  0,   15,   16,   17,   18,   19,   20,
+  0,  0,   12,  0,  269,  0,  0,  0,   21,   22,
+  118,   23,  119,  120,  121,  122,  123,  0,   25,   26,
+  124,   27,  0,   28,  125,  126,   13,  0,  0,   40,
+  0,   41,  0,   14,  0,  0,  0,  0,  0,  127,
+  128,   29,  0,  0,  0,  117,  0,  0,  0,  129,
+   31,   32,   33,   34,   35,   36,  115,   37,  130,   38,
+   39,  131,  0,   42,  0,  0,   43,   44,   45,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   15,   16,   17,   18,   19,   20,  0,  0,  0,
+  0,  0,  0,  0,  0,   21,   22,  118,   23,  119,
+  120,  121,  122,  123,  0,   25,   26,  124,   27,   12,
+   28,  125,  126,   13,  0,  0,   40,  0,   41,  0,
+   14,  0,  0,  0,  0,  0,  127,  128,   29,  0,
+  0,  0,  117,  0,  0,  115,  129,   31,   32,   33,
+   34,   35,   36,  0,   37,  130,   38,   39,  131,  0,
+   42,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+   15,   16,   17,   18,   19,   20,  0,  0,  0,  0,
+  0,  0,  0,  0,   21,   22,  118,   23,  119,  120,
+  121,  122,  123,  0,   25,   26,  124,   27,  0,   28,
+  125,  126,   13,  0,  0,   40,   12,   41,  353,   14,
+  0,  0,  0,  0,  0,  127,  128,   29,  0,  0,
+  0,  117,  0,  0,  0,  129,   31,   32,   33,   34,
+   35,   36,  0,   37,  130,   38,   39,  131,  0,   42,
+  0,  0,   43,   44,   45,  0,  0,  0,  0,  0,
+  0,  0,  115,  0,  0,  0,  0,  0,  0,  304,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   15,   16,   17,
+   18,   19,   20,  0,  0,   12,  0,  373,  0,  0,
+  0,   21,   22,  118,   23,  119,  120,  121,  122,  123,
+  0,   25,   26,  124,   27,  0,   28,  125,  126,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  127,  128,   29,  0,  0,  0,  0,  0,
+  0,  0,  129,   31,   32,   33,   34,   35,   36,  115,
+   37,  130,   38,   39,  131,  0,   42,  0,  0,   43,
+   44,   45,  0,  0,  0,  0,  124,  0,  0,  124,
+  0,  124,  0,  124,   15,   16,   17,   18,   19,   20,
+  0,  0,  0,  0,  0,  124,  0,  0,   21,   22,
+  118,   23,  119,  120,  121,  122,  123,  0,   25,   26,
+  124,   27,  0,   28,  125,  126,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  127,
+  128,   29,  0,  0,  0,  0,  0,  0,  115,  129,
+   31,   32,   33,   34,   35,   36,  0,   37,  130,   38,
+   39,  131,  0,   42,  0,  0,   43,   44,   45,  124,
+  0,  124,  0,   15,   16,   17,   18,   19,   20,  0,
+  0,  0,  0,  0,  0,  0,  0,   21,   22,  118,
+   23,  119,  120,  121,  122,  123,  0,   25,   26,  124,
+   27,  0,   28,  125,  126,   13,  0,  0,   40,  0,
+   41,  0,   14,  0,  0,  0,  0,  0,  127,  128,
+   29,  0,  0,  0,  117,  0,  0,  0,  129,   31,
+   32,   33,   34,   35,   36,  0,   37,  130,   38,   39,
+  131,  0,   42,  0,  0,   43,   44,   45,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  117,  0,  0,  117,  0,  117,  0,  117,   12,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  117,
+  0,  0,  124,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  124,  124,  124,
+  124,  124,  124,  0,  0,  0,  0,  0,  0,  0,
+  0,  124,  124,  124,  124,  124,  124,  124,  124,  124,
+  0,  124,  124,  124,  124,  0,  124,  124,  124,   27,
+  0,  0,   27,  117,   27,  0,   27,  0,  0,  0,
+  0,  0,  124,  124,  124,  0,  0,  0,   27,  0,
+  0,  0,  124,  124,  124,  124,  124,  124,  124,  0,
+  124,  124,  124,  124,  124,  0,  124,  0,  0,  124,
+  124,  124,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  115,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  194,  0,  0,  194,  0,  194,
+  0,  194,   27,  0,  0,  0,   15,   16,   17,   18,
+   19,   20,  0,  194,  0,  0,  0,  0,  0,  0,
+   21,   22,  118,   23,  119,  120,  121,  122,  123,  0,
+   25,   26,  124,   27,  0,   28,  125,  126,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  127,  128,   29,  0,  0,  117,  0,  0,  0,
+  0,  129,   31,   32,   33,   34,   35,   36,  0,   37,
+  130,   38,   39,  131,  0,   42,  0,  194,   43,   44,
+   45,  117,  117,  117,  117,  117,  117,  0,  0,  0,
+  0,  0,  0,  0,  0,  117,  117,  117,  117,  117,
+  117,  117,  117,  117,  0,  117,  117,  117,  117,  0,
+  117,  117,  117,   23,  0,  0,   23,  0,   23,  0,
+   23,  0,  0,  0,  0,  0,  117,  117,  117,  0,
+  0,  0,   23,  0,  0,   27,  117,  117,  117,  117,
+  117,  117,  117,  0,  117,  117,  117,  117,  117,  0,
+  117,  0,  0,  117,  117,  117,  0,  0,  0,  0,
+   27,   27,   27,   27,   27,   27,  0,  0,  0,  0,
+  0,  0,  0,  0,   27,   27,   27,   27,   27,   27,
+   27,   27,   27,  0,   27,   27,   27,   27,  0,   27,
+   27,   27,   21,  0,  0,   21,   23,   21,  0,   21,
+  0,  0,  0,  0,  0,   27,   27,   27,  0,  0,
+  194,   21,  0,  0,  0,   27,   27,   27,   27,   27,
+   27,   27,  0,   27,   27,   27,   27,   27,  0,   27,
+  0,  0,   27,   27,   27,  194,  194,  194,  194,  194,
+  194,  0,  0,  0,  0,  0,  0,  0,  0,  194,
+  194,  194,  194,  194,  194,  194,  194,  194,  0,  194,
+  194,  194,  194,  0,  194,  194,  194,   19,  0,  0,
+   19,  0,   19,  0,   19,   21,  0,  0,  0,  0,
+  194,  194,  194,  0,  0,  0,   19,  0,  0,  0,
+  194,  194,  194,  194,  194,  194,  194,  0,  194,  194,
+  194,  194,  194,  0,  194,  0,  0,  194,  194,  194,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   23,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   19,  0,  0,  0,   23,   23,   23,   23,   23,   23,
+  0,  0,  0,  0,  0,  0,  0,  0,   23,   23,
+   23,   23,   23,   23,   23,   23,   23,  0,   23,   23,
+   23,   23,  0,   23,   23,   23,  0,  0,  0,  0,
+  0,  0,  0,   65,  147,  0,   40,  146,   41,   23,
+   23,   23,  0,  0,  0,  0,  0,  0,   21,   23,
+   23,   23,   23,   23,   23,   23,   98,   23,   23,   23,
+   23,   23,  0,   23,  0,  0,   23,   23,   23,  0,
+  0,  0,  0,   21,   21,   21,   21,   21,   21,  0,
+  0,  0,  0,  0,  0,  0,  0,   21,   21,   21,
+   21,   21,   21,   21,   21,   21,  0,   21,   21,   21,
+   21,  0,   21,   21,   21,  0,  0,  0,  0,  0,
+   65,  0,  0,   40,  0,   41,  0,   87,   21,   21,
+   21,  0,  0,   19,  0,  0,  0,  0,   21,   21,
+   21,   21,   21,   21,   21,  0,   21,   21,   21,   21,
+   21,  0,   21,  0,  0,   21,   21,   21,   19,   19,
+   19,   19,   19,   19,  0,  0,  0,  0,  0,  0,
+  0,  0,   19,   19,   19,   19,   19,   19,   19,   19,
+   19,  0,   19,   19,   19,   19,  0,   19,   19,   19,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   19,   19,   19,  0,  0,  0,  0,
+  0,  0,  0,   19,   19,   19,   19,   19,   19,   19,
+  0,   19,   19,   19,   19,   19,  0,   19,  0,  0,
+   19,   19,   19,   65,  0,  0,   40,  0,   41,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  197,  0,  0,  0,   98,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   88,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   89,   90,   15,   16,   17,   18,   19,   20,
+  0,   91,   92,   93,   94,   95,   96,   97,   21,   22,
+  0,   23,  0,  0,  0,  0,  0,  0,   25,   26,
+  0,   27,  0,   28,  0,   65,  333,   87,   40,  146,
+   41,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   29,  0,  0,  0,  0,  0,  0,   98,   30,
+   31,   32,   33,   34,   35,   36,  0,   37,  0,   38,
+   39,  0,  0,   66,  0,  0,   43,   44,   45,  0,
+  0,   15,   16,   17,   18,   19,   20,  0,  0,  0,
+  0,  0,  0,  0,  0,   21,   22,  0,   23,  0,
+  0,  0,  0,  0,  0,   25,   26,  0,   27,  0,
+   28,  0,  0,  0,  0,  0,  0,  0,  0,   87,
+  0,  0,  0,  0,  0,  0,   65,  345,   29,   40,
+  146,   41,  0,  0,  0,  0,   30,   31,   32,   33,
+   34,   35,   36,  0,   37,  0,   38,   39,  0,   98,
+   66,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  189,  0,  0,  0,
+   88,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   89,   90,   15,   16,   17,   18,   19,   20,
+  0,   91,   92,   93,   94,   95,   96,   97,   21,   22,
+   87,   23,  0,  0,  0,  0,  0,  0,   25,   26,
+  0,   27,  0,   28,   65,  347,  0,   40,  146,   41,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   29,  0,  0,  0,  0,  0,   98,  0,   30,
+   31,   32,   33,   34,   35,   36,  0,   37,  0,   38,
+   39,  0,   88,   66,  0,  0,   43,   44,   45,  0,
+  0,  0,  0,  0,   89,   90,   15,   16,   17,   18,
+   19,   20,  0,   91,   92,   93,   94,   95,   96,   97,
+   21,   22,  0,   23,  0,  0,  0,  0,  0,  0,
+   25,   26,  0,   27,  0,   28,   65,  237,   87,   40,
+  0,   41,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   29,  0,  0,  0,  0,  0,   98,
+  0,   30,   31,   32,   33,   34,   35,   36,  0,   37,
+  0,   38,   39,  0,  0,   66,  0,  0,   43,   44,
+   45,  0,  0,   88,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   89,   90,   15,   16,   17,
+   18,   19,   20,  0,   91,   92,   93,   94,   95,   96,
+   97,   21,   22,  0,   23,  0,  0,  0,  0,  0,
+   87,   25,   26,  0,   27,  0,   28,   65,  147,  0,
+   40,  0,   41,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   29,  0,  0,  0,  0,  0,
+   98,  0,   30,   31,   32,   33,   34,   35,   36,  0,
+   37,  0,   38,   39,  0,  0,   66,  0,  0,   43,
+   44,   45,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   88,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   89,   90,   15,   16,   17,   18,   19,
+   20,  0,   91,   92,   93,   94,   95,   96,   97,   21,
+   22,   87,   23,  0,  0,  0,  0,  0,  0,   25,
+   26,  0,   27,   65,   28,  0,   40,  146,   41,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   29,  0,  0,  0,   98,  0,  0,  0,
+   30,   31,   32,   33,   34,   35,   36,  0,   37,  0,
+   38,   39,  0,   88,   66,  0,  0,   43,   44,   45,
+  0,  0,  0,  0,  0,   89,   90,   15,   16,   17,
+   18,   19,   20,  0,   91,   92,   93,   94,   95,   96,
+   97,   21,   22,  0,   23,  0,  0,  0,  0,  0,
+  0,   25,   26,  0,   27,   65,   28,   87,   40,  0,
+   41,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  250,   29,  0,  0,  0,   98,  0,
+  0,  0,   30,   31,   32,   33,   34,   35,   36,  0,
+   37,  0,   38,   39,  0,  0,   66,  0,  0,   43,
+   44,   45,  0,  0,   88,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   89,   90,   15,   16,
+   17,   18,   19,   20,  0,   91,   92,   93,   94,   95,
+   96,   97,   21,   22,  0,   23,  0,  0,  0,   87,
+  0,  0,   25,   26,  0,   27,  0,   28,   65,  288,
+  0,   40,  0,   41,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   29,  0,  0,  0,  0,
+  0,   98,  0,   30,   31,   32,   33,   34,   35,   36,
+  0,   37,  0,   38,   39,  0,  0,   66,  0,  0,
+   43,   44,   45,  0,  0,  0,  0,  0,  0,  0,
+   88,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   89,   90,   15,   16,   17,   18,   19,   20,
+  0,   91,   92,   93,   94,   95,   96,   97,   21,   22,
+  0,   23,   87,  0,  0,  0,  0,  0,   25,   26,
+  0,   27,  0,   28,   65,  327,  0,   40,  0,   41,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   29,  0,  0,  0,  0,  0,   98,  0,   30,
+   31,   32,   33,   34,   35,   36,  0,   37,  0,   38,
+   39,  0,   88,   66,  0,  0,   43,   44,   45,  0,
+  0,  0,  0,  0,   89,   90,   15,   16,   17,   18,
+   19,   20,  0,   91,   92,   93,   94,   95,   96,   97,
+   21,   22,  0,   23,  0,  0,  0,  0,  0,  0,
+   25,   26,  0,   27,  0,   28,   65,  329,   87,   40,
+  0,   41,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   29,  0,  0,  0,  0,  0,   98,
+  0,   30,   31,   32,   33,   34,   35,   36,  0,   37,
+  0,   38,   39,  0,  0,   66,  0,  0,   43,   44,
+   45,  0,  0,  0,  0,   88,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   89,   90,   15,
+   16,   17,   18,   19,   20,  0,   91,   92,   93,   94,
+   95,   96,   97,   21,   22,  0,   23,  0,  0,  0,
+   87,  0,  0,   25,   26,  0,   27,  0,   28,   65,
+  344,  0,   40,  0,   41,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   29,  0,  0,  0,
+  0,  0,   98,  0,   30,   31,   32,   33,   34,   35,
+   36,  0,   37,  0,   38,   39,  0,  0,   66,  0,
+  0,   43,   44,   45,  0,  0,  0,  0,  0,  0,
+  0,   88,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   89,   90,   15,   16,   17,   18,   19,
+   20,  0,   91,   92,   93,   94,   95,   96,   97,   21,
+   22,  0,   23,   87,  0,  0,  0,  0,  0,   25,
+   26,  0,   27,  0,   28,   65,  364,  0,   40,  0,
+   41,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   29,  0,  0,  0,  0,  0,   98,  0,
+   30,   31,   32,   33,   34,   35,   36,  0,   37,  0,
+   38,   39,  0,   88,   66,  0,  0,   43,   44,   45,
+  0,  0,  0,  0,  0,   89,   90,   15,   16,   17,
+   18,   19,   20,  0,   91,   92,   93,   94,   95,   96,
+   97,   21,   22,  0,   23,  0,  0,  0,  0,  0,
+  0,   25,   26,  0,   27,  0,   28,   65,  366,   87,
+   40,  0,   41,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   29,  0,  0,  0,  0,  0,
+   98,  0,   30,   31,   32,   33,   34,   35,   36,  0,
+   37,  0,   38,   39,  0,  0,   66,  0,  0,   43,
+   44,   45,  0,  0,  0,  0,   88,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   89,   90,
+   15,   16,   17,   18,   19,   20,  0,   91,   92,   93,
+   94,   95,   96,   97,   21,   22,  0,   23,  0,  0,
+  0,   87,  0,  0,   25,   26,  0,   27,  0,   28,
+   65,  0,  0,   40,  0,   41,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   29,  0,  368,
+  0,  0,  0,   98,  0,   30,   31,   32,   33,   34,
+   35,   36,  0,   37,  0,   38,   39,  0,  0,   66,
+  0,  0,   43,   44,   45,  0,  0,  0,  0,  0,
+  0,  0,   88,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   89,   90,   15,   16,   17,   18,
+   19,   20,  0,   91,   92,   93,   94,   95,   96,   97,
+   21,   22,  0,   23,   87,  0,  0,  0,  0,  0,
+   25,   26,  0,   27,  0,   28,   65,  377,  0,   40,
+  0,   41,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   29,  0,  0,  0,  0,  0,   98,
+  0,   30,   31,   32,   33,   34,   35,   36,  0,   37,
+  0,   38,   39,  0,   88,   66,  0,  0,   43,   44,
+   45,  0,  0,  0,  0,  0,   89,   90,   15,   16,
+   17,   18,   19,   20,  0,   91,   92,   93,   94,   95,
+   96,   97,   21,   22,  0,   23,  0,  0,  0,  0,
+  0,  0,   25,   26,  0,   27,  0,   28,   65,  0,
+   87,   40,  0,   41,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   29,  0,  0,  0,  0,
+  0,   98,  0,   30,   31,   32,   33,   34,   35,   36,
+  0,   37,  0,   38,   39,  0,  0,   66,  0,  0,
+   43,   44,   45,  0,  0,  0,  0,   88,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   89,
+   90,   15,   16,   17,   18,   19,   20,  0,   91,   92,
+   93,   94,   95,   96,   97,   21,   22,  0,   23,  0,
+  0,  0,   87,  0,  0,   25,   26,  0,   27,   99,
+   28,  0,   99,  0,   99,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   29,  0,
+  0,  0,   99,  0,  0,  0,   30,   31,   32,   33,
+   34,   35,   36,  0,   37,  0,   38,   39,  0,  0,
+   66,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+  0,  0,  0,   88,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   89,   90,   15,   16,   17,
+   18,   19,   20,  0,   91,   92,   93,   94,   95,   96,
+   97,   21,   22,   99,   23,  0,  0,  0,  0,  0,
+  0,   25,   26,  0,   27,  0,   28,   64,   64,  0,
+  0,   64,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   29,   64,   64,  0,  0,  0,
+   64,  0,   30,   31,   32,   33,   34,   35,   36,  0,
+   37,  0,   38,   39,  0,   88,   66,  0,  0,   43,
+   44,   45,  0,  0,  0,  0,  0,   89,   90,   15,
+   16,   17,   18,   19,   20,  0,   91,   92,   93,   94,
+   95,   96,   97,   21,   22,  0,   23,  0,  0,  0,
+  0,  0,  0,   25,   26,  0,   27,  0,   28,   62,
+   62,   64,  0,   62,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   29,   62,   62,  0,
+  0,  0,   62,  0,   30,   31,   32,   33,   34,   35,
+   36,  0,   37,  0,   38,   39,  0,  0,   66,  0,
+  0,   43,   44,   45,  0,  0,   99,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   99,   99,
+   99,   99,   99,   99,   99,   99,  0,   99,   99,   99,
+   99,   99,   99,   99,   99,   99,  0,   99,  0,  0,
+  0,  0,  0,   62,   99,   99,  0,   99,  0,   99,
+   65,  0,  0,   40,  0,   41,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   99,  0,  0,
+  0,  0,  0,  0,  0,   99,   99,   99,   99,   99,
+   99,   99,  0,   99,  0,   99,   99,  0,  0,   99,
+  0,  0,   99,   99,   99,  0,  0,  0,  0,  0,
+   64,  0,  0,  0,   64,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   64,   64,   64,   64,
+   64,   64,   64,   64,   64,  0,  0,   64,  0,  0,
+  0,   64,   64,   64,   87,   64,  0,  0,  0,  0,
+  0,  0,   64,   64,  0,   64,   65,   64,  0,   40,
+  0,   41,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   64,  0,  0,  0,  0,
+  0,  0,  0,   64,   64,   64,   64,   64,   64,   64,
+  0,   64,   62,   64,   64,  0,   62,   64,  0,  0,
+   64,   64,   64,  0,  0,  0,  0,  0,   62,   62,
+   62,   62,   62,   62,   62,   62,   62,  0,  0,   62,
+  0,  0,  0,   62,   62,   62,  0,   62,  0,  0,
+  0,  0,  0,  0,   62,   62,  0,   62,  0,   62,
+   87,  0,  0,   32,   32,  0,   32,  0,   32,  0,
+   32,  0,  0,  0,  0,  0,  0,   62,  0,  0,
+  0,  0,  0,  0,  0,   62,   62,   62,   62,   62,
+   62,   62,  0,   62,  0,   62,   62,  0,  0,   62,
+  0,  0,   62,   62,   62,  0,  0,   88,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   89,
+  0,   15,   16,   17,   18,   19,   20,  0,   91,   92,
+   93,   94,   95,   96,   97,   21,   22,  0,   23,  0,
+  0,  0,  0,  0,  0,   25,   26,  0,   27,  0,
+   28,  0,  0,   13,  0,  0,   40,  0,   41,  0,
+   14,  0,  0,  0,  0,  0,  0,  0,   29,  0,
+  0,  0,  0,  0,  0,  0,   30,   31,   32,   33,
+   34,   35,   36,  0,   37,  0,   38,   39,  0,  0,
+   66,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+  0,  0,  0,   88,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   15,   16,   17,
+   18,   19,   20,  0,   91,   92,   93,   94,   95,   96,
+   97,   21,   22,  0,   23,  0,   12,  0,  0,  0,
+   13,   25,   26,   40,   27,   41,   28,   14,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   29,  0,  0,  0,  0,  0,
+  0,  0,   30,   31,   32,   33,   34,   35,   36,   32,
+   37,  0,   38,   39,  0,  0,   66,  0,  0,   43,
+   44,   45,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   32,   32,   32,   32,   32,   32,
+  0,  0,  0,  0,  0,  0,  0,  0,   32,   32,
+  0,   32,   65,   32,  0,   40,  0,   41,   32,   32,
+  0,   32,  0,   32,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  274,  0,  0,  0,   32,
+   32,   32,  0,  0,  0,  0,  0,  0,  0,   32,
+   32,   32,   32,   32,   32,   32,  0,   32,  0,   32,
+   32,  0,  0,   32,   10,   11,   32,   32,   32,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   15,   16,   17,   18,   19,   20,
+  0,  0,  0,  0,  0,  0,  0,   13,   21,   22,
+   40,   23,   41,  0,   14,  0,  0,   24,   25,   26,
+  0,   27,  0,   28,  0,  0,  197,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   29,  0,  0,  0,  0,  0,  0,  0,   30,
+   31,   32,   33,   34,   35,   36,  115,   37,  0,   38,
+   39,  0,  0,   42,  0,  0,   43,   44,   45,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   15,   16,   17,   18,   19,   20,  0,   65,  0,
+  0,   40,  0,   41,  0,   21,   22,  0,   23,  0,
+  120,  0,  0,  0,  0,   25,   26,  0,   27,  0,
+   28,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  127,  128,   29,  0,
+  0,  0,  0,  0,  0,  0,   30,   31,   32,   33,
+   34,   35,   36,  0,   37,  0,   38,   39,  0,  272,
+   42,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+  0,   89,   90,   15,   16,   17,   18,   19,   20,  0,
+  0,  0,  0,  0,  0,  0,  273,   21,   22,  0,
+   23,  0,  0,  0,  0,  0,  0,   25,   26,  0,
+   27,  0,   28,   13,  153,  0,   40,  0,   41,  0,
+   14,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   29,  0,  0,  0,  0,  0,  0,  0,   30,   31,
+   32,   33,   34,   35,   36,  0,   37,  0,   38,   39,
+  189,  0,   66,  0,  0,   43,   44,   45,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   15,   16,
+   17,   18,   19,   20,  0,  0,  0,  0,  0,  0,
+  0,  0,   21,   22,  0,   23,  0,  0,  0,  0,
+  0,  0,   25,   26,  0,   27,  0,   28,   13,  161,
+  0,   40,  0,   41,  0,   14,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   29,  0,  0,  0,  0,
+  0,  0,  0,   30,   31,   32,   33,   34,   35,   36,
+  0,   37,  0,   38,   39,  272,  0,   42,  0,  0,
+   43,   44,   45,  0,  0,  0,  0,   89,  0,   15,
+   16,   17,   18,   19,   20,  0,  0,  0,  0,  0,
+  0,  0,  273,   21,   22,  0,   23,  0,  0,  0,
+  0,  0,  0,   25,   26,  0,   27,  0,   28,  0,
+   13,  0,  0,   40,  0,   41,  0,   14,  0,  0,
+  0,  0,  0,  0,  0,  0,   29,  0,  0,  0,
+  0,  0,  0,  0,   30,   31,   32,   33,   34,   35,
+   36,  0,   37,  0,   38,   39,  0,  0,   66,  0,
+  0,   43,   44,   45,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   15,   16,   17,   18,   19,   20,
+   13,  0,  0,   40,  0,   41,  0,   14,   21,   22,
+  0,   23,  0,  0,  0,  0,  0,  0,   25,   26,
+  0,   27,  0,   28,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,   29,  0,  0,  0,  0,  0,  0,  0,   30,
+   31,   32,   33,   34,   35,   36,  0,   37,  0,   38,
+   39,  0,  0,   42,  0,  0,   43,   44,   45,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   15,
+   16,   17,   18,   19,   20,   13,  0,  0,   40,  0,
+   41,  0,   14,   21,   22,  0,   23,  0,  0,  0,
+  0,  0,  0,   25,   26,  0,   27,  0,   28,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   29,  0,  0,  0,
+  0,  0,  0,  0,   30,   31,   32,   33,   34,   35,
+   36,  0,   37,  178,   38,   39,  0,  0,   42,  0,
+  0,   43,   44,   45,  0,  0,  0,  0,  0,  0,
+  0,   15,   16,   17,   18,   19,   20,  309,  0,  0,
+   40,  0,   41,  0,   14,   21,   22,  0,   23,  0,
+  0,  0,  0,  0,  0,   25,   26,  0,   27,  0,
+   28,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   29,  0,
+  0,  0,  0,  0,  0,  0,   30,   31,   32,   33,
+   34,   35,   36,  180,   37,  0,   38,   39,  0,  0,
+   42,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+  0,   15,   16,   17,   18,   19,   20,  309,  0,  0,
+   40,  0,   41,  0,   14,   21,   22,  0,   23,  0,
+  0,  0,  0,  0,  0,   25,   26,  0,   27,  0,
+   28,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,   29,  0,
+  0,  0,  0,  0,  0,  0,   30,   31,   32,   33,
+   34,   35,   36,  0,   37,  0,   38,   39,  232,  0,
+   42,  0,  0,   43,   44,   45,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,   15,   16,   17,   18,
+   19,   20,  309,  0,  0,   40,  0,   41,  0,   14,
+   21,   22,  0,   23,  0,  0,  0,  0,  0,  0,
+   25,   26,  0,   27,  0,   28,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   29,  0,  0,  0,  0,  0,  0,
+  0,   30,   31,   32,   33,   34,   35,   36,  0,   37,
+  178,   38,   39,  0,  0,   42,  0,  0,   43,   44,
+   45,  0,  0,  0,  0,  0,  0,  0,   15,   16,
+   17,   18,   19,   20,   13,  0,  0,   40,  0,   41,
+  0,   14,   21,   22,  0,   23,  0,  0,  0,  0,
+  0,  0,   25,   26,  0,   27,  0,   28,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   29,  0,  0,  0,  0,
+  0,  0,  0,   30,   31,   32,   33,   34,   35,   36,
+  180,   37,  0,   38,   39,  0,  0,   42,  0,  0,
+   43,   44,   45,  0,  0,  0,  0,  0,   15,   16,
+   17,   18,   19,   20,   65,  0,  0,   40,  0,   41,
+  0,   14,   21,   22,  0,   23,  0,  0,  0,  0,
+  0,  0,   25,   26,  0,   27,  0,   28,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,   29,  0,  0,  0,  0,
+  0,  0,  0,   30,   31,   32,   33,   34,   35,   36,
+  0,   37,  0,   38,   39,  232,  0,   42,  0,  0,
+   43,   44,   45,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,   15,   16,   17,   18,   19,   20,   65,
+  0,  0,   40,  0,   41,  0,  0,   21,   22,  0,
+   23,  0,  0,  0,  0,  0,  0,   25,   26,  0,
+   27,  0,   28,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   29,  0,  0,  0,  0,  0,  0,  0,   30,   31,
+   32,   33,   34,   35,   36,  0,   37,  0,   38,   39,
+  0,  0,   42,  0,  0,   43,   44,   45,  0,  0,
+  0,  0,  0,  0,  0,   15,   16,   17,   18,   19,
+   20,  220,  0,  0,   40,  0,   41,  0,   14,   21,
+   22,  0,   23,  0,  0,  0,  0,  0,  0,   25,
+   26,  0,   27,  0,   28,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   29,  0,  0,  0,  0,  0,  0,  0,
+   30,   31,   32,   33,   34,   35,   36,  0,   37,  0,
+   38,   39,  0,  0,   42,  0,  0,   43,   44,   45,
+  0,  0,  0,  0,  0,   15,   16,   17,   18,   19,
+   20,  309,  0,  0,   40,  0,   41,  0,   14,   21,
+   22,  0,   23,  0,  0,  0,  0,  0,  0,   25,
+   26,  0,   27,  0,   28,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,   29,  0,  0,  0,  0,  0,  0,  0,
+   30,   31,   32,   33,   34,   35,   36,  0,   37,  0,
+   38,   39,  0,  0,   42,  0,  0,   43,   44,   45,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   15,   16,   17,   18,   19,   20,   32,  0,  0,   32,
+  0,   32,  0,   32,   21,   22,  0,   23,  0,  0,
+  0,  0,  0,  0,   25,   26,  0,   27,  0,   28,
+  0,  0,  0,  0,  0,  0,  0,  0,  182,  0,
+  0,  0,  0,  0,  0,  0,  0,   29,  0,  0,
+  0,  0,  0,  0,  0,   30,   31,   32,   33,   34,
+   35,   36,  0,   37,  0,   38,   39,  0,  0,   66,
+  0,  0,   43,   44,   45,  0,  0,  0,  0,  0,
+  0,  0,   15,   16,   17,   18,   19,   20,  0,  0,
+  0,  0,  0,  0,  0,  0,   21,   22,  0,   23,
+  0,  0,  0,  0,  0,  0,   25,   26,  0,   27,
+  0,   28,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   29,
+  0,  0,  0,  0,  0,  0,  0,   30,   31,   32,
+   33,   34,   35,   36,  0,   37,  0,   38,   39,  0,
+  0,   42,  0,  0,   43,   44,   45,  0,  0,  0,
+  0,  0,   15,   16,   17,   18,   19,   20,  0,  0,
+  0,  0,  0,  0,  0,  0,   21,   22,  0,   23,
+  0,  0,  0,  0,  0,  0,   25,   26,  0,   27,
+  0,   28,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,   29,
+  0,  0,  0,  0,  0,  0,  0,   30,   31,   32,
+   33,   34,   35,   36,  0,   37,  0,   38,   39,  0,
+  0,   42,  0,  0,   43,   44,   45,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,   32,   32,   32,
+   32,   32,   32,  0,  0,  0,  0,  0,  0,  0,
+  0,   32,   32,  0,   32,  0,  0,  0,  0,  0,
+  0,   32,   32,  0,   32,  0,   32,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,   32,  0,  0,  0,  0,  0,
+  0,  0,   32,   32,   32,   32,   32,   32,   32,  0,
+   32,  0,   32,   32,  0,  0,   32,  0,  0,   32,
+   32,   32,
+};
+static const short yycheck[] = {             37,
+  0,   59,   42,   59,   42,   43,   41,   45,  7,   47,
+   37,   10,   11,   41,   91,   42,   58,   59,   59,  204,
+   47,   74,   75,   59,   59,  117,   59,   88,  220,  124,
+   44,   59,  123,   44,   91,   41,   40,   37,   41,  354,
+   40,   41,   42,   43,   44,   45,   41,   47,   41,  263,
+   49,   41,   41,   59,   44,  0,   59,   44,   58,   59,
+  375,  114,  336,   63,   59,  339,   59,  109,   58,   59,
+   59,  343,   41,   41,  119,   44,   44,  122,   40,   93,
+  125,  126,  143,  108,  129,  130,  295,   41,  133,  124,
+   44,   41,  117,   93,   44,  0,   41,   41,  140,   44,
+   44,  141,   47,  0,   40,  197,   93,  262,  0,  264,
+   40,  110,  123,   58,   59,  292,   41,  309,   63,   44,
+  353,  354,  336,  123,  124,   93,   40,  336,  337,  338,
+   40,   40,   37,  342,  124,  352,   41,   42,   43,   44,
+   45,  263,   47,   93,   41,   40,  355,   44,   93,   41,
+   47,  319,   44,   58,   59,   47,  295,  202,   63,  263,
+   41,   58,   59,   44,  209,   41,   58,   59,   44,  354,
+  344,   41,  197,  295,   44,  319,   41,   41,  123,   44,
+   44,  242,  243,   40,  339,  353,  354,   40,   93,   40,
+  375,   40,  287,   58,   59,  290,   93,  336,   63,  338,
+  256,   93,   40,  342,  257,  263,  0,  263,  300,  353,
+  354,  272,  367,   40,  336,  337,  338,  294,  123,  124,
+  342,  294,  263,   47,  379,  336,  123,  263,  263,  263,
+  263,  123,  272,  355,  274,  263,  276,  277,  278,  263,
+  280,  281,  282,   37,   91,  337,   40,   41,   42,   43,
+   44,   45,  287,   47,  253,  290,  298,  263,  329,  124,
+  263,  261,  262,  263,   58,   59,   44,  267,  263,   63,
+  263,  332,   59,  263,  263,  300,  368,  263,  263,  279,
+  280,  281,  282,  283,  284,  285,  286,  287,  288,  289,
+  290,  291,  292,  293,  294,  295,  296,  287,  298,   93,
+  290,   41,   41,  343,  304,  305,  306,  349,  308,  348,
+  310,  294,  337,   41,  352,   40,  261,  262,  263,  319,
+   41,   41,   41,   44,   44,  352,  325,   41,  328,  123,
+  124,  263,  377,   41,  279,  280,  336,  337,  338,  339,
+  340,  341,  342,  368,  344,   41,  346,  347,   44,  0,
+  350,  0,  352,  353,  354,  355,  261,  262,  263,  304,
+   59,  123,  267,  295,  261,  262,  263,   41,  191,  261,
+  262,  263,  110,   51,  279,  280,  281,  282,  283,  284,
+  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,
+  295,  296,   -1,  298,   -1,   -1,   -1,   -1,  263,  304,
+  305,  306,   -1,  308,  336,  310,  338,  304,   -1,   -1,
+  342,   -1,  304,   -1,  319,  280,  0,   -1,   -1,   -1,
+   -1,   -1,  287,  328,   -1,  290,   -1,   -1,   -1,   -1,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+   -1,  346,  347,   -1,   -1,  350,   -1,  352,  353,  354,
+  355,   -1,   -1,   37,   -1,   -1,   40,   41,   42,   43,
+   44,   45,   -1,   47,   -1,   -1,   -1,  261,  262,  263,
+   -1,   -1,   -1,  267,   58,   59,   -1,   -1,   -1,   63,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,  287,  288,  289,  290,  291,  221,  293,
+  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   93,
+  304,  305,  306,   -1,  308,   41,  310,   -1,   44,   41,
+   -1,   -1,   44,   -1,   -1,  319,  0,   -1,   -1,   -1,
+   -1,   -1,   58,   59,  328,   -1,   -1,   59,   -1,  123,
+  124,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,  352,  353,
+  354,  355,   -1,   37,   -1,  0,   40,   41,   42,   43,
+   44,   45,  272,   47,  274,   -1,  276,  277,  278,   -1,
+  280,  281,  282,  292,   58,   59,  295,  310,   -1,   63,
+  313,  314,  315,  316,  0,  318,  319,  320,  124,   -1,
+   -1,   -1,  124,   41,   -1,   -1,   44,   -1,   -1,   44,
+   -1,   -1,   47,   -1,   -1,   -1,   -1,   -1,   -1,   93,
+   58,   59,  0,   -1,   59,   63,   -1,  336,  337,  338,
+   -1,   -1,   -1,  342,   -1,   41,   -1,  360,   44,   -1,
+   -1,   47,   -1,  343,   -1,   -1,  355,   -1,   -1,  123,
+  124,   -1,   58,   59,   -1,   -1,   -1,   63,   -1,   37,
+   -1,   -1,   40,   41,   42,   43,   44,   45,   -1,   47,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,  263,
+   58,   59,   -1,  267,   -1,   63,  124,   93,  123,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   93,   -1,  123,   -1,   -1,
+  304,  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  319,  0,  263,   -1,   -1,
+   -1,  263,   -1,   -1,  328,  123,  124,   -1,   -1,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,  287,  346,  347,  290,  287,  350,   -1,  290,  353,
+  354,  355,   -1,   37,   -1,   -1,   40,   41,   42,   43,
+   44,   45,   -1,   47,   -1,   -1,   -1,  261,  262,  263,
+   -1,   -1,   -1,  267,   58,   59,   -1,   -1,   -1,   63,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,  263,  261,  262,  263,   93,
+  304,  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,
+   -1,  279,  280,   -1,   -1,  319,   -1,   -1,   -1,  287,
+   -1,   -1,  290,   -1,  328,  261,  262,  263,   -1,  123,
+  124,   -1,  336,  337,  338,  339,  340,  341,  342,  304,
+  344,   -1,  346,  347,  280,   -1,  350,   -1,   -1,  353,
+  354,  355,   41,  261,  262,  263,   -1,   -1,   -1,  267,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  304,   -1,
+   59,  279,  280,  281,  282,  283,  284,  285,  286,  287,
+  288,  289,  290,  291,  292,  293,  294,  295,  296,   41,
+  298,   41,   44,   -1,   -1,   -1,  304,  305,  306,   -1,
+  308,   97,  310,   -1,   -1,   -1,   -1,   59,   -1,   59,
+   -1,  319,  0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,  120,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,  124,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   37,
+   -1,   -1,   40,   41,   42,   43,   44,   45,   -1,   47,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,  263,
+   58,   59,  124,  267,  124,   63,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   93,   -1,   -1,  204,   -1,
+  304,  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  319,  0,   -1,   -1,   -1,
+   -1,  221,   -1,   -1,  328,  123,  124,   -1,  234,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,  247,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   37,   -1,   -1,   40,   41,   42,   43,
+   44,   45,   -1,   47,  263,   -1,   -1,  273,   -1,   -1,
+   -1,   -1,   -1,   -1,   58,   59,   -1,   -1,   -1,   63,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  287,   -1,
+   -1,  290,   -1,   -1,   -1,  301,   -1,   -1,   -1,   -1,
+   -1,  263,   -1,  263,   -1,   -1,   -1,   -1,   -1,   93,
+  310,   -1,  0,  313,  314,  315,  316,   -1,  318,  319,
+  320,   -1,   -1,   -1,   -1,  287,   -1,  287,  290,   -1,
+  290,   -1,   -1,   -1,   -1,  341,   -1,   -1,   -1,  123,
+  124,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   37,
+   -1,   -1,   40,   41,   42,   43,   44,   45,   -1,   47,
+  360,   -1,   -1,  261,  262,  263,   -1,   -1,   -1,  267,
+   58,   59,   -1,   -1,   -1,   63,   -1,   -1,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,  287,
+  288,  289,  290,  291,  292,  293,  294,  295,  296,   -1,
+  298,   -1,   -1,   -1,   -1,   93,  304,  305,  306,   -1,
+  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  319,  0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,   -1,  123,  124,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   37,
+   -1,   -1,   40,   41,   42,   43,   44,   45,   -1,   47,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,  263,
+   58,   59,   -1,  267,   -1,   63,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   93,   -1,   -1,   -1,   -1,
+  304,  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   40,  319,   -1,   43,   -1,   45,
+   -1,   -1,   -1,   -1,  328,  123,  124,   -1,   -1,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,  261,  262,  263,   -1,   -1,   -1,  267,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,  287,
+  288,  289,  290,  291,  292,  293,  294,  295,  296,   -1,
+  298,   -1,   -1,   -1,   -1,   -1,  304,  305,  306,   -1,
+  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  319,  0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   37,
+   -1,   -1,   40,   41,   42,   43,   44,   45,   -1,   47,
+   -1,   -1,   -1,  261,  262,  263,   -1,   -1,   -1,  267,
+   58,   59,   -1,   -1,   -1,   63,   -1,   -1,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,  287,
+  288,  289,  290,  291,  292,  293,  294,  295,  296,   -1,
+  298,   -1,   -1,   -1,   -1,   93,  304,  305,  306,   -1,
+  308,   -1,  310,   -1,   -1,  0,   -1,   -1,   -1,   -1,
+   -1,  319,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,   -1,  123,  124,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,  352,   40,   41,  355,   43,   44,
+   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,
+  296,   -1,  298,   58,   59,   -1,   -1,   -1,   63,  305,
+  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   93,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  0,   -1,
+  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  123,  124,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   40,   41,
+   -1,   43,   44,   45,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  261,  262,  263,   58,   59,   -1,  267,
+   -1,   63,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,   -1,
+  288,  289,  290,  291,  292,  293,  294,  295,  296,   -1,
+  298,   93,   -1,   -1,   -1,   -1,  304,  305,  306,   -1,
+  308,   -1,  310,   -1,   -1,  0,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,  123,  124,   -1,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,  352,   40,   41,  355,   -1,   44,
+   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,  263,   -1,
+   -1,   -1,  267,   58,   59,   -1,   -1,   -1,   63,   -1,
+   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,
+  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,
+  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   93,  304,
+  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  319,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,  123,  124,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,
+  355,   -1,   -1,   -1,   -1,   -1,   -1,  0,   -1,  261,
+  262,  263,   -1,   -1,   -1,  267,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  285,  286,  287,  288,  289,  290,  291,
+  292,  293,  294,  295,  296,   -1,  298,   40,   41,   -1,
+   -1,   44,  304,  305,  306,   -1,  308,   -1,  310,   -1,
+   -1,   -1,   -1,   -1,   -1,   58,   59,  319,   -1,   -1,
+   63,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,
+  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,
+   93,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  0,   -1,  261,  262,  263,   -1,
+   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  123,  124,   -1,   -1,  279,  280,  281,  282,  283,  284,
+  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,
+  295,  296,   -1,  298,   40,   41,   -1,   -1,   44,  304,
+  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,
+   -1,   -1,   58,   59,  319,   -1,   -1,   63,   -1,   -1,
+   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+   -1,  346,  347,   -1,   -1,  350,   -1,   93,  353,  354,
+  355,   -1,   -1,  0,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  123,  124,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   40,   41,   -1,   -1,   44,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,
+  263,   58,   59,   -1,  267,   -1,   63,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  285,  286,  287,  288,  289,  290,  291,  292,
+  293,  294,  295,  296,   -1,  298,   93,   -1,   -1,   -1,
+   -1,  304,  305,  306,   -1,  308,   -1,  310,   -1,   -1,
+   -1,   -1,   -1,   -1,  0,   -1,  319,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,  123,  124,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   40,   41,   -1,   -1,   44,   -1,
+   -1,   -1,   -1,   -1,   -1,  261,  262,  263,   -1,   -1,
+   -1,  267,   58,   59,   -1,   -1,   -1,   63,   -1,   -1,
+   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,  285,
+  286,  287,  288,  289,  290,  291,  292,  293,  294,  295,
+  296,   -1,  298,   -1,   -1,   -1,   -1,   93,  304,  305,
+  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  319,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  328,   -1,   -1,   -1,   -1,  123,  124,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,   -1,
+  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,  0,   -1,  261,  262,  263,   -1,   -1,   -1,
+  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  279,  280,  281,  282,  283,  284,  285,  286,
+  287,  288,  289,  290,  291,  292,  293,  294,  295,  296,
+   -1,  298,   40,   41,   -1,   -1,   44,  304,  305,  306,
+   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,
+   58,   59,  319,   -1,   -1,   63,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,   -1,  346,
+  347,   -1,   -1,  350,   -1,   93,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  261,  262,  263,   -1,   -1,
+   -1,  267,   -1,   -1,   -1,  123,  124,  0,   -1,   -1,
+   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,  285,
+  286,   -1,  288,  289,  290,  291,  292,  293,  294,  295,
+  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,  304,  305,
+  306,   -1,  308,   -1,  310,   -1,   -1,   40,   -1,   -1,
+   43,  135,   45,  137,   47,   -1,   -1,   -1,   -1,   -1,
+  144,  145,  328,   -1,   -1,   -1,   59,   -1,   -1,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,   -1,
+  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  192,   -1,
+  0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  216,   -1,  218,  219,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  261,  262,  263,   -1,   -1,   -1,  267,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,   59,
+  288,  289,  290,  291,  292,  293,  294,  295,  296,   -1,
+  298,   -1,  266,   -1,   -1,   -1,  304,  305,  306,   -1,
+  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  0,   -1,
+  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   -1,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,   -1,   41,
+   -1,  335,   44,   -1,   -1,   47,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   58,   59,  261,  262,
+  263,   63,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  363,
+   -1,   -1,   -1,   -1,   -1,  369,   -1,  0,  281,  282,
+  283,  284,  285,  286,  378,   -1,   -1,   -1,   -1,  383,
+   -1,   93,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,  304,  305,  306,   -1,  308,   -1,  310,   -1,   -1,
+   -1,   -1,   -1,  123,   -1,   -1,   -1,   40,   -1,   -1,
+   43,  123,   45,   -1,   47,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  261,  262,  263,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  295,  296,   -1,  298,   -1,
+  123,   -1,   -1,   -1,  304,  305,  306,   -1,  308,   -1,
+  310,   40,   -1,   -1,   43,   -1,   45,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   58,
+   -1,   -1,   -1,   -1,   63,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,
+  350,  261,  262,  353,  354,  355,   -1,   -1,   -1,  261,
+  262,  263,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  281,  282,  283,  284,  285,  286,  279,  280,  281,
+  282,  283,  284,  285,  286,  295,  296,   40,  298,   -1,
+   43,   -1,   45,   -1,  304,  305,  306,   -1,  308,   -1,
+  310,   -1,  304,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,  261,  262,
+  350,   -1,  344,  353,  354,  355,   -1,   -1,   -1,  0,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,
+  283,  284,  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,  304,  305,  306,   -1,  308,   37,  310,   -1,   40,
+   41,   42,   43,   44,   45,   -1,   47,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   58,   59,   -1,
+   -1,   -1,   63,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,  267,   -1,
+   -1,   -1,   93,  0,   -1,   -1,   -1,   -1,   -1,   -1,
+  279,  280,  281,  282,  283,  284,  285,  286,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  294,  295,  296,   -1,  298,
+   -1,   -1,  123,  124,   -1,   -1,  305,  306,   -1,  308,
+   -1,  310,   -1,   40,   41,   -1,   43,   44,   45,   -1,
+   47,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,
+   -1,   58,   59,   -1,   -1,   -1,   63,  336,  337,  338,
+  339,  340,  341,  342,  267,  344,   -1,  346,  347,   -1,
+   -1,  350,   -1,   -1,  353,  354,  355,   -1,  281,  282,
+  283,  284,  285,  286,   -1,   -1,   93,   -1,   -1,   -1,
+   -1,  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,   -1,  310,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  123,  124,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  261,  262,  263,   -1,   -1,   -1,  267,   -1,   -1,   -1,
+  0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  285,  286,  287,  288,  289,  290,
+  291,   -1,  293,  294,   -1,  296,   -1,  298,   -1,   -1,
+   -1,   -1,   -1,  304,  305,  306,   -1,  308,   -1,  310,
+   -1,   41,   -1,   -1,   44,   -1,   -1,   47,  319,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   58,   59,
+   -1,   -1,   -1,   63,   -1,   -1,   -1,   -1,  339,  340,
+  341,   -1,   -1,  344,   41,  346,  347,   44,   -1,  350,
+   -1,  352,  353,  354,  261,  262,  263,   -1,   -1,   -1,
+  267,   58,   59,   93,   -1,   -1,   63,   -1,   -1,   -1,
+   -1,   -1,  279,  280,  281,  282,  283,  284,  285,  286,
+   -1,  288,  289,  290,  291,  292,  293,  294,   -1,  296,
+   -1,  298,   -1,  123,  7,   -1,   -1,  304,  305,  306,
+   13,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,  124,   -1,   -1,
+   -1,   -1,  339,  340,  341,   -1,   -1,  344,   -1,  346,
+  347,   -1,   -1,  350,   -1,   -1,  353,  354,   -1,   -1,
+   63,   64,   65,   -1,   -1,   -1,   -1,   -1,   -1,   72,
+   73,   74,   75,   -1,   -1,   78,   79,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   88,   -1,   -1,   91,   92,
+   93,   94,   95,   96,   -1,   98,   -1,  100,  101,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  110,  111,   -1,
+   -1,  114,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  122,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  130,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  142,
+  143,  261,  262,  263,   -1,  148,   -1,  150,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,
+  280,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  0,  263,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  304,   -1,   -1,   -1,  191,   -1,
+   -1,   -1,  279,  280,  281,  282,  283,  284,  285,  286,
+  287,   -1,  205,  290,   -1,   -1,   -1,   -1,  211,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   41,  220,   -1,   44,
+   -1,   -1,   47,   -1,  344,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   58,   59,   -1,  239,   -1,   63,  242,
+  243,   23,   -1,   -1,   -1,  248,   -1,  250,   -1,   -1,
+   -1,   -1,   -1,   -1,  257,   37,   -1,  344,   40,   41,
+   42,   43,   44,   45,   46,   -1,   -1,   -1,   93,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   60,   -1,
+  283,  284,  285,   -1,   66,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   76,   -1,   -1,   -1,  123,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  309,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  102,  103,  104,  105,  106,  107,   -1,   -1,   -1,  332,
+   -1,  334,   -1,   -1,   -1,  338,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  132,   -1,   -1,   -1,  357,   -1,   -1,   -1,   -1,  141,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  152,   -1,  154,   -1,  156,   -1,  158,  159,   -1,   -1,
+   -1,   -1,  164,  165,  166,  167,  168,   -1,  170,  171,
+  172,  173,  174,  175,   -1,  177,   -1,  179,   -1,  181,
+  182,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  194,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  202,   -1,   -1,   -1,   -1,   -1,   -1,  209,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,  263,  221,
+   -1,   -1,   -1,   -1,   -1,  227,  228,   -1,   -1,   -1,
+   -1,  233,   -1,  235,  279,  280,  281,  282,  283,  284,
+  285,  286,   -1,   -1,   -1,   -1,  0,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  304,
+  262,   -1,  264,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  272,   -1,  274,   -1,  276,  277,  278,  279,  280,  281,
+  282,   -1,   -1,   -1,   -1,  287,   -1,   41,   -1,  291,
+   44,  293,   -1,   47,  296,  297,   -1,   -1,   -1,  344,
+   -1,   -1,   -1,   -1,   58,   59,   23,   -1,  310,   63,
+   -1,  313,  314,  315,  316,   -1,  318,  319,  320,  321,
+  322,  323,   -1,   40,   41,   42,   -1,   -1,   45,   46,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   93,
+   -1,  343,   -1,   60,  346,   -1,  348,   -1,  350,   66,
+  352,   -1,   -1,  355,   -1,   -1,   -1,   -1,  360,   76,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  370,  123,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  102,  103,  104,  105,  106,
+  107,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  132,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  141,   -1,   -1,   -1,  0,   -1,
+   -1,   -1,   -1,   -1,   -1,  152,   -1,  154,   -1,  156,
+   -1,  158,  159,   -1,   -1,   -1,   -1,  164,  165,  166,
+   -1,  168,   -1,  170,  171,  172,  173,  174,  175,   -1,
+  177,   -1,  179,   -1,  181,  182,   -1,   -1,   -1,   41,
+   -1,   -1,   44,   -1,   -1,   47,   -1,  194,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  202,   58,   59,   -1,   -1,
+   -1,   63,  209,   -1,   -1,  0,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  221,   -1,   -1,  261,  262,  263,
+  227,  228,   -1,   -1,   -1,   -1,  233,   -1,  235,   -1,
+   -1,   93,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,   -1,   -1,   -1,   41,   -1,   -1,   44,
+   -1,   -1,   47,   -1,   -1,  262,   -1,  264,   -1,   -1,
+  304,  123,   -1,   58,   59,  272,   -1,  274,   63,  276,
+  277,  278,  279,  280,  281,  282,   -1,   -1,   -1,  0,
+  287,   -1,   -1,   -1,  291,   -1,  293,   -1,   -1,  296,
+  297,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   93,   -1,
+  344,   -1,   -1,  310,   -1,   -1,  313,  314,  315,  316,
+   -1,  318,  319,  320,  321,  322,  323,   -1,   -1,   -1,
+   41,   -1,   -1,   44,   -1,   -1,   47,   -1,  123,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  343,   58,   59,   -1,
+   -1,   -1,   63,  350,   37,  352,   -1,   40,  355,   42,
+   43,   -1,   45,  360,   47,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  370,   -1,   -1,   -1,   -1,   -1,   -1,
+   63,   -1,   93,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   91,  261,
+  262,  263,  123,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  285,  286,   -1,   -1,   -1,   -1,   -1,
+   -1,  124,   -1,   37,   -1,   -1,   40,   -1,   42,   43,
+   -1,   45,  304,   47,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   59,   -1,   -1,   -1,   63,
+   -1,   -1,   -1,   -1,   -1,   -1,  261,  262,  263,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  344,   -1,  279,  280,  281,  282,  283,  284,
+  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  304,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  124,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   37,
+   -1,   -1,   40,   41,   42,   43,   44,   45,   -1,   47,
+  261,  262,  263,   -1,   -1,   -1,   -1,   -1,   -1,  344,
+   58,   59,   -1,   -1,   -1,   63,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  304,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  285,  286,   -1,  288,  289,  290,  291,  292,
+  293,  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,  124,  310,   -1,   -1,
+   -1,   -1,   -1,  344,   -1,   -1,  319,   -1,   -1,   -1,
+   -1,   -1,   -1,   40,   41,  328,   43,   -1,   45,   -1,
+   47,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   59,  346,  347,   -1,   -1,  350,   -1,  352,
+  353,  354,  355,  267,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,   -1,  288,  289,  290,  291,  292,  293,
+   -1,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,
+   -1,  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  319,  123,   -1,  125,   -1,
+   40,   -1,   -1,   43,  328,   45,   -1,   47,   -1,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   59,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,  352,  353,
+  354,  355,   -1,   -1,   -1,  263,   -1,   -1,   -1,  267,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,  287,
+   -1,   -1,  290,   -1,   -1,   -1,  294,  295,  296,   -1,
+  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,
+  308,   -1,  310,  123,   -1,  125,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   40,  352,   -1,   43,  355,   45,  256,
+   47,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   59,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  326,
+  327,  328,  329,   -1,   -1,   -1,  123,   -1,  125,  336,
+  337,  338,  339,  340,  341,  342,  256,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,   40,   -1,   -1,   43,   -1,   45,   -1,
+   47,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   59,   -1,   -1,  295,  296,  297,  298,  299,
+  300,  301,  302,  303,   -1,  305,  306,  307,  308,   -1,
+  310,  311,  312,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  326,  327,  328,  329,
+   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,  345,  346,  347,  348,   -1,
+  350,   -1,   -1,  353,  354,  355,  123,   -1,  125,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   40,   -1,   -1,   43,   -1,   45,  256,
+   47,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   59,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  326,
+  327,  328,  329,   -1,   -1,   -1,  123,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,   -1,  256,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   59,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  326,
+  327,  328,  329,  123,   -1,  125,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,  256,
+   -1,   -1,   -1,   -1,   -1,   -1,  263,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   40,   -1,   -1,   43,
+   -1,   45,   -1,   47,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   59,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  326,
+  327,  328,   -1,   -1,   60,   61,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,  123,
+   -1,  125,   -1,   -1,   -1,   -1,  256,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   40,   -1,   -1,   43,   -1,   45,   -1,
+   47,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   59,   -1,   -1,  295,  296,  297,  298,  299,
+  300,  301,  302,  303,   -1,  305,  306,  307,  308,   -1,
+  310,  311,  312,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  158,  159,   -1,   -1,  326,  327,  328,  165,
+  166,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,  345,  346,  347,  348,   -1,
+  350,   -1,   -1,  353,  354,  355,  123,   -1,  125,   -1,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   59,
+   -1,   -1,  256,   -1,   -1,   -1,  222,   -1,   -1,   -1,
+   -1,  227,  228,  229,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  241,   -1,  281,  282,  283,
+  284,  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  295,  296,  297,  298,  299,  300,  301,  302,  303,
+   -1,  305,  306,  307,  308,  271,  310,  311,  312,   40,
+   -1,   -1,   43,  123,   45,  125,   47,   -1,   -1,   -1,
+   -1,   -1,  326,  327,  328,   -1,   -1,   -1,   59,  295,
+  296,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,  345,  346,  347,  348,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,  322,  323,   -1,  256,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  342,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,  123,   -1,  125,   -1,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   40,   -1,   -1,   43,
+   -1,   45,   -1,   47,   -1,   -1,   -1,   -1,   -1,  326,
+  327,  328,   -1,   -1,   -1,   59,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,  256,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  295,  296,  297,  298,  299,
+  300,  301,  302,  303,   -1,  305,  306,  307,  308,  123,
+  310,  311,  312,   40,   -1,   -1,   43,   -1,   45,   -1,
+   47,   -1,   -1,   -1,   -1,   -1,  326,  327,  328,   -1,
+   -1,   -1,   59,   -1,   -1,  256,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,  345,  346,  347,  348,   -1,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  295,  296,  297,  298,  299,  300,
+  301,  302,  303,   -1,  305,  306,  307,  308,   -1,  310,
+  311,  312,   40,   -1,   -1,   43,  123,   45,  125,   47,
+   -1,   -1,   -1,   -1,   -1,  326,  327,  328,   -1,   -1,
+   -1,   59,   -1,   -1,   -1,  336,  337,  338,  339,  340,
+  341,  342,   -1,  344,  345,  346,  347,  348,   -1,  350,
+   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  256,   -1,   -1,   -1,   -1,   -1,   -1,  263,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,  283,
+  284,  285,  286,   -1,   -1,  123,   -1,  125,   -1,   -1,
+   -1,  295,  296,  297,  298,  299,  300,  301,  302,  303,
+   -1,  305,  306,  307,  308,   -1,  310,  311,  312,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  326,  327,  328,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,  256,
+  344,  345,  346,  347,  348,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,   40,   -1,   -1,   43,
+   -1,   45,   -1,   47,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   59,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  326,
+  327,  328,   -1,   -1,   -1,   -1,   -1,   -1,  256,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,  123,
+   -1,  125,   -1,  281,  282,  283,  284,  285,  286,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,  297,
+  298,  299,  300,  301,  302,  303,   -1,  305,  306,  307,
+  308,   -1,  310,  311,  312,   40,   -1,   -1,   43,   -1,
+   45,   -1,   47,   -1,   -1,   -1,   -1,   -1,  326,  327,
+  328,   -1,   -1,   -1,   59,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,  345,  346,  347,
+  348,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,  123,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   59,
+   -1,   -1,  256,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,  283,
+  284,  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  295,  296,  297,  298,  299,  300,  301,  302,  303,
+   -1,  305,  306,  307,  308,   -1,  310,  311,  312,   40,
+   -1,   -1,   43,  123,   45,   -1,   47,   -1,   -1,   -1,
+   -1,   -1,  326,  327,  328,   -1,   -1,   -1,   59,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,  345,  346,  347,  348,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  256,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   40,   -1,   -1,   43,   -1,   45,
+   -1,   47,  123,   -1,   -1,   -1,  281,  282,  283,  284,
+  285,  286,   -1,   59,   -1,   -1,   -1,   -1,   -1,   -1,
+  295,  296,  297,  298,  299,  300,  301,  302,  303,   -1,
+  305,  306,  307,  308,   -1,  310,  311,  312,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  326,  327,  328,   -1,   -1,  256,   -1,   -1,   -1,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+  345,  346,  347,  348,   -1,  350,   -1,  123,  353,  354,
+  355,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  295,  296,  297,  298,  299,
+  300,  301,  302,  303,   -1,  305,  306,  307,  308,   -1,
+  310,  311,  312,   40,   -1,   -1,   43,   -1,   45,   -1,
+   47,   -1,   -1,   -1,   -1,   -1,  326,  327,  328,   -1,
+   -1,   -1,   59,   -1,   -1,  256,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,  345,  346,  347,  348,   -1,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  295,  296,  297,  298,  299,  300,
+  301,  302,  303,   -1,  305,  306,  307,  308,   -1,  310,
+  311,  312,   40,   -1,   -1,   43,  123,   45,   -1,   47,
+   -1,   -1,   -1,   -1,   -1,  326,  327,  328,   -1,   -1,
+  256,   59,   -1,   -1,   -1,  336,  337,  338,  339,  340,
+  341,  342,   -1,  344,  345,  346,  347,  348,   -1,  350,
+   -1,   -1,  353,  354,  355,  281,  282,  283,  284,  285,
+  286,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,
+  296,  297,  298,  299,  300,  301,  302,  303,   -1,  305,
+  306,  307,  308,   -1,  310,  311,  312,   40,   -1,   -1,
+   43,   -1,   45,   -1,   47,  123,   -1,   -1,   -1,   -1,
+  326,  327,  328,   -1,   -1,   -1,   59,   -1,   -1,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,  345,
+  346,  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  256,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  123,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,
+  297,  298,  299,  300,  301,  302,  303,   -1,  305,  306,
+  307,  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   40,   41,   -1,   43,   44,   45,  326,
+  327,  328,   -1,   -1,   -1,   -1,   -1,   -1,  256,  336,
+  337,  338,  339,  340,  341,  342,   63,  344,  345,  346,
+  347,  348,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,  297,
+  298,  299,  300,  301,  302,  303,   -1,  305,  306,  307,
+  308,   -1,  310,  311,  312,   -1,   -1,   -1,   -1,   -1,
+   40,   -1,   -1,   43,   -1,   45,   -1,  124,  326,  327,
+  328,   -1,   -1,  256,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,  345,  346,  347,
+  348,   -1,  350,   -1,   -1,  353,  354,  355,  281,  282,
+  283,  284,  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  295,  296,  297,  298,  299,  300,  301,  302,
+  303,   -1,  305,  306,  307,  308,   -1,  310,  311,  312,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  326,  327,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,  345,  346,  347,  348,   -1,  350,   -1,   -1,
+  353,  354,  355,   40,   -1,   -1,   43,   -1,   45,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   59,   -1,   -1,   -1,   63,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  279,  280,  281,  282,  283,  284,  285,  286,
+   -1,  288,  289,  290,  291,  292,  293,  294,  295,  296,
+   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,
+   -1,  308,   -1,  310,   -1,   40,   41,  124,   43,   44,
+   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,   63,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,   -1,  346,
+  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,  281,  282,  283,  284,  285,  286,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  295,  296,   -1,  298,   -1,
+   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,
+  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  124,
+   -1,   -1,   -1,   -1,   -1,   -1,   40,   41,  328,   43,
+   44,   45,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,   63,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  263,   -1,   -1,   -1,
+  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  279,  280,  281,  282,  283,  284,  285,  286,
+   -1,  288,  289,  290,  291,  292,  293,  294,  295,  296,
+  124,  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,
+   -1,  308,   -1,  310,   40,   41,   -1,   43,   44,   45,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,   63,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,   -1,  346,
+  347,   -1,  267,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,
+  285,  286,   -1,  288,  289,  290,  291,  292,  293,  294,
+  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,
+  305,  306,   -1,  308,   -1,  310,   40,   41,  124,   43,
+   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   63,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,
+  355,   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,   -1,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,
+  124,  305,  306,   -1,  308,   -1,  310,   40,   41,   -1,
+   43,   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,
+   63,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,  285,
+  286,   -1,  288,  289,  290,  291,  292,  293,  294,  295,
+  296,  124,  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,
+  306,   -1,  308,   40,  310,   -1,   43,   44,   45,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  328,   -1,   -1,   -1,   63,   -1,   -1,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,   -1,
+  346,  347,   -1,  267,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,   -1,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,
+   -1,  305,  306,   -1,  308,   40,  310,  124,   43,   -1,
+   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   58,  328,   -1,   -1,   -1,   63,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  285,  286,   -1,  288,  289,  290,  291,  292,
+  293,  294,  295,  296,   -1,  298,   -1,   -1,   -1,  124,
+   -1,   -1,  305,  306,   -1,  308,   -1,  310,   40,   41,
+   -1,   43,   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   63,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  279,  280,  281,  282,  283,  284,  285,  286,
+   -1,  288,  289,  290,  291,  292,  293,  294,  295,  296,
+   -1,  298,  124,   -1,   -1,   -1,   -1,   -1,  305,  306,
+   -1,  308,   -1,  310,   40,   41,   -1,   43,   -1,   45,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,   63,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,   -1,  346,
+  347,   -1,  267,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,
+  285,  286,   -1,  288,  289,  290,  291,  292,  293,  294,
+  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,
+  305,  306,   -1,  308,   -1,  310,   40,   41,  124,   43,
+   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   63,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,
+  355,   -1,   -1,   -1,   -1,  267,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  285,  286,   -1,  288,  289,  290,  291,
+  292,  293,  294,  295,  296,   -1,  298,   -1,   -1,   -1,
+  124,   -1,   -1,  305,  306,   -1,  308,   -1,  310,   40,
+   41,   -1,   43,   -1,   45,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,
+   -1,   -1,   63,   -1,  336,  337,  338,  339,  340,  341,
+  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,
+   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,  285,
+  286,   -1,  288,  289,  290,  291,  292,  293,  294,  295,
+  296,   -1,  298,  124,   -1,   -1,   -1,   -1,   -1,  305,
+  306,   -1,  308,   -1,  310,   40,   41,   -1,   43,   -1,
+   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   63,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,   -1,
+  346,  347,   -1,  267,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,   -1,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,
+   -1,  305,  306,   -1,  308,   -1,  310,   40,   41,  124,
+   43,   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,
+   63,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,  267,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  285,  286,   -1,  288,  289,  290,
+  291,  292,  293,  294,  295,  296,   -1,  298,   -1,   -1,
+   -1,  124,   -1,   -1,  305,  306,   -1,  308,   -1,  310,
+   40,   -1,   -1,   43,   -1,   45,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   59,
+   -1,   -1,   -1,   63,   -1,  336,  337,  338,  339,  340,
+  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,
+   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,
+  285,  286,   -1,  288,  289,  290,  291,  292,  293,  294,
+  295,  296,   -1,  298,  124,   -1,   -1,   -1,   -1,   -1,
+  305,  306,   -1,  308,   -1,  310,   40,   41,   -1,   43,
+   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   63,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+   -1,  346,  347,   -1,  267,  350,   -1,   -1,  353,  354,
+  355,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  285,  286,   -1,  288,  289,  290,  291,  292,
+  293,  294,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,   -1,  310,   40,   -1,
+  124,   43,   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   63,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,  267,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,
+  280,  281,  282,  283,  284,  285,  286,   -1,  288,  289,
+  290,  291,  292,  293,  294,  295,  296,   -1,  298,   -1,
+   -1,   -1,  124,   -1,   -1,  305,  306,   -1,  308,   40,
+  310,   -1,   43,   -1,   45,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,
+   -1,   -1,   63,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  285,  286,   -1,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,  124,  298,   -1,   -1,   -1,   -1,   -1,
+   -1,  305,  306,   -1,  308,   -1,  310,   40,   41,   -1,
+   -1,   44,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  328,   58,   59,   -1,   -1,   -1,
+   63,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,  267,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  285,  286,   -1,  288,  289,  290,  291,
+  292,  293,  294,  295,  296,   -1,  298,   -1,   -1,   -1,
+   -1,   -1,   -1,  305,  306,   -1,  308,   -1,  310,   40,
+   41,  124,   -1,   44,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  328,   58,   59,   -1,
+   -1,   -1,   63,   -1,  336,  337,  338,  339,  340,  341,
+  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,
+   -1,  353,  354,  355,   -1,   -1,  267,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  285,  286,   -1,  288,  289,  290,
+  291,  292,  293,  294,  295,  296,   -1,  298,   -1,   -1,
+   -1,   -1,   -1,  124,  305,  306,   -1,  308,   -1,  310,
+   40,   -1,   -1,   43,   -1,   45,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,  340,
+  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,
+   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,
+  263,   -1,   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  285,  286,  287,   -1,   -1,  290,   -1,   -1,
+   -1,  294,  295,  296,  124,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,   40,  310,   -1,   43,
+   -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,  263,  346,  347,   -1,  267,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  285,  286,  287,   -1,   -1,  290,
+   -1,   -1,   -1,  294,  295,  296,   -1,  298,   -1,   -1,
+   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,  310,
+  124,   -1,   -1,   40,   41,   -1,   43,   -1,   45,   -1,
+   47,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,  340,
+  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,
+   -1,   -1,  353,  354,  355,   -1,   -1,  267,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,
+   -1,  281,  282,  283,  284,  285,  286,   -1,  288,  289,
+  290,  291,  292,  293,  294,  295,  296,   -1,  298,   -1,
+   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,
+  310,   -1,   -1,   40,   -1,   -1,   43,   -1,   45,   -1,
+   47,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  267,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,  283,
+  284,  285,  286,   -1,  288,  289,  290,  291,  292,  293,
+  294,  295,  296,   -1,  298,   -1,  123,   -1,   -1,   -1,
+   40,  305,  306,   43,  308,   45,  310,   47,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,  256,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,
+   -1,  298,   40,  300,   -1,   43,   -1,   45,  305,  306,
+   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   63,   -1,   -1,   -1,  326,
+  327,  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,   -1,  346,
+  347,   -1,   -1,  350,  261,  262,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   40,  295,  296,
+   43,  298,   45,   -1,   47,   -1,   -1,  304,  305,  306,
+   -1,  308,   -1,  310,   -1,   -1,   59,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,  256,  344,   -1,  346,
+  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  281,  282,  283,  284,  285,  286,   -1,   40,   -1,
+   -1,   43,   -1,   45,   -1,  295,  296,   -1,  298,   -1,
+  300,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,
+  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  326,  327,  328,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,  267,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+   -1,  279,  280,  281,  282,  283,  284,  285,  286,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  294,  295,  296,   -1,
+  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,
+  308,   -1,  310,   40,   41,   -1,   43,   -1,   45,   -1,
+   47,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+  263,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,
+  283,  284,  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,   -1,  310,   40,   41,
+   -1,   43,   -1,   45,   -1,   47,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,  267,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,  279,   -1,  281,
+  282,  283,  284,  285,  286,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  294,  295,  296,   -1,  298,   -1,   -1,   -1,
+   -1,   -1,   -1,  305,  306,   -1,  308,   -1,  310,   -1,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,
+  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,   -1,
+   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,
+   40,   -1,   -1,   43,   -1,   45,   -1,   47,  295,  296,
+   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,
+   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,
+  337,  338,  339,  340,  341,  342,   -1,  344,   -1,  346,
+  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,
+  282,  283,  284,  285,  286,   40,   -1,   -1,   43,   -1,
+   45,   -1,   47,  295,  296,   -1,  298,   -1,   -1,   -1,
+   -1,   -1,   -1,  305,  306,   -1,  308,   -1,  310,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,
+  342,   -1,  344,  263,  346,  347,   -1,   -1,  350,   -1,
+   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  281,  282,  283,  284,  285,  286,   40,   -1,   -1,
+   43,   -1,   45,   -1,   47,  295,  296,   -1,  298,   -1,
+   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,
+  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,  263,  344,   -1,  346,  347,   -1,   -1,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+   -1,  281,  282,  283,  284,  285,  286,   40,   -1,   -1,
+   43,   -1,   45,   -1,   47,  295,  296,   -1,  298,   -1,
+   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,
+  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,
+  340,  341,  342,   -1,  344,   -1,  346,  347,  263,   -1,
+  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,  283,  284,
+  285,  286,   40,   -1,   -1,   43,   -1,   45,   -1,   47,
+  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,
+  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  336,  337,  338,  339,  340,  341,  342,   -1,  344,
+  263,  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,
+  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,
+  283,  284,  285,  286,   40,   -1,   -1,   43,   -1,   45,
+   -1,   47,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,   -1,  310,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+  263,  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,  281,  282,
+  283,  284,  285,  286,   40,   -1,   -1,   43,   -1,   45,
+   -1,   47,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,
+   -1,   -1,  305,  306,   -1,  308,   -1,  310,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  336,  337,  338,  339,  340,  341,  342,
+   -1,  344,   -1,  346,  347,  263,   -1,  350,   -1,   -1,
+  353,  354,  355,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  281,  282,  283,  284,  285,  286,   40,
+   -1,   -1,   43,   -1,   45,   -1,   -1,  295,  296,   -1,
+  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,
+  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,
+  338,  339,  340,  341,  342,   -1,  344,   -1,  346,  347,
+   -1,   -1,  350,   -1,   -1,  353,  354,  355,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,
+  286,   40,   -1,   -1,   43,   -1,   45,   -1,   47,  295,
+  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,
+  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,   -1,
+  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,  281,  282,  283,  284,  285,
+  286,   40,   -1,   -1,   43,   -1,   45,   -1,   47,  295,
+  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,   -1,  305,
+  306,   -1,  308,   -1,  310,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  336,  337,  338,  339,  340,  341,  342,   -1,  344,   -1,
+  346,  347,   -1,   -1,  350,   -1,   -1,  353,  354,  355,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  281,  282,  283,  284,  285,  286,   40,   -1,   -1,   43,
+   -1,   45,   -1,   47,  295,  296,   -1,  298,   -1,   -1,
+   -1,   -1,   -1,   -1,  305,  306,   -1,  308,   -1,  310,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  319,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  336,  337,  338,  339,  340,
+  341,  342,   -1,  344,   -1,  346,  347,   -1,   -1,  350,
+   -1,   -1,  353,  354,  355,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  281,  282,  283,  284,  285,  286,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,   -1,  298,
+   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,
+   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,
+  339,  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,
+   -1,  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,
+   -1,   -1,  281,  282,  283,  284,  285,  286,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,  295,  296,   -1,  298,
+   -1,   -1,   -1,   -1,   -1,   -1,  305,  306,   -1,  308,
+   -1,  310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  328,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  336,  337,  338,
+  339,  340,  341,  342,   -1,  344,   -1,  346,  347,   -1,
+   -1,  350,   -1,   -1,  353,  354,  355,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  281,  282,  283,
+  284,  285,  286,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,  295,  296,   -1,  298,   -1,   -1,   -1,   -1,   -1,
+   -1,  305,  306,   -1,  308,   -1,  310,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  336,  337,  338,  339,  340,  341,  342,   -1,
+  344,   -1,  346,  347,   -1,   -1,  350,   -1,   -1,  353,
+  354,  355,
+};
+#define YYFINAL 4
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 356
+#if YYDEBUG
+static const char *yyname[] = {
+
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'%'",0,0,"'('","')'","'*'","'+'","','","'-'",0,"'/'",0,0,0,0,0,0,0,0,0,0,
+"':'","';'",0,0,0,"'?'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+"'['",0,"']'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",
+"'|'","'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"FIRSTTOKEN","PROGRAM","PASTAT","PASTAT2",
+"XBEGIN","XEND","NL","ARRAY","MATCH","NOTMATCH","MATCHOP","FINAL","DOT","ALL",
+"CCL","NCCL","CHAR","OR","STAR","QUEST","PLUS","EMPTYRE","AND","BOR","BITOR",
+"BITAND","BITCOMPL","BITLSHIFT","BITRSHIFT","BITXOR","APPEND","EQ","GE","GT",
+"LE","LT","NE","IN","ARG","BLTIN","BREAK","CLOSE","CONTINUE","DELETE","DO",
+"EXIT","FOR","FUNC","SUB","GSUB","IF","INDEX","LSUBSTR","MATCHFCN","NEXT",
+"NEXTFILE","ADD","MINUS","MULT","DIVIDE","MOD","ASSIGN","ASGNOP","ADDEQ",
+"SUBEQ","MULTEQ","DIVEQ","MODEQ","POWEQ","PRINT","PRINTF","SPRINTF","ELSE",
+"INTEST","CONDEXPR","POSTINCR","PREINCR","POSTDECR","PREDECR","VAR","IVAR",
+"VARNF","CALL","NUMBER","STRING","VARERR","REGEXPR","GETLINE","RETURN","SPLIT",
+"SUBSTR","WHILE","CAT","NOT","UMINUS","POWER","DECR","INCR","INDIRECT",
+"LASTTOKEN",
+};
+static const char *yyrule[] = {
+"$accept : program",
+"program : pas",
+"program : error",
+"and : AND",
+"and : and NL",
+"bor : BOR",
+"bor : bor NL",
+"bitwise : BITOR",
+"bitwise : BITAND",
+"bitwise : BITCOMPL",
+"bitwise : BITRSHIFT",
+"bitwise : BITLSHIFT",
+"bitwise : BITXOR",
+"comma : ','",
+"comma : comma NL",
+"do : DO",
+"do : do NL",
+"else : ELSE",
+"else : else NL",
+"$$1 :",
+"for : FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen $$1 stmt",
+"$$2 :",
+"for : FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen $$2 stmt",
+"$$3 :",
+"for : FOR '(' varname IN varname rparen $$3 stmt",
+"funcname : VAR",
+"funcname : CALL",
+"if : IF '(' pattern rparen",
+"lbrace : '{'",
+"lbrace : lbrace NL",
+"nl : NL",
+"nl : nl NL",
+"opt_nl :",
+"opt_nl : nl",
+"opt_pst :",
+"opt_pst : pst",
+"opt_simple_stmt :",
+"opt_simple_stmt : simple_stmt",
+"pas : opt_pst",
+"pas : opt_pst pa_stats opt_pst",
+"pa_pat : pattern",
+"pa_stat : pa_pat",
+"pa_stat : pa_pat lbrace stmtlist '}'",
+"pa_stat : pa_pat ',' opt_nl pa_pat",
+"pa_stat : pa_pat ',' opt_nl pa_pat lbrace stmtlist '}'",
+"pa_stat : lbrace stmtlist '}'",
+"pa_stat : XBEGIN lbrace stmtlist '}'",
+"pa_stat : XEND lbrace stmtlist '}'",
+"$$4 :",
+"pa_stat : FUNC funcname '(' varlist rparen $$4 lbrace stmtlist '}'",
+"pa_stats : pa_stat",
+"pa_stats : pa_stats opt_pst pa_stat",
+"patlist : pattern",
+"patlist : patlist comma pattern",
+"ppattern : var ASGNOP ppattern",
+"ppattern : ppattern '?' ppattern ':' ppattern",
+"ppattern : ppattern bor ppattern",
+"ppattern : ppattern and ppattern",
+"ppattern : ppattern MATCHOP reg_expr",
+"ppattern : ppattern MATCHOP ppattern",
+"ppattern : ppattern IN varname",
+"ppattern : '(' plist ')' IN varname",
+"ppattern : ppattern term",
+"ppattern : re",
+"ppattern : term",
+"pattern : var ASGNOP pattern",
+"pattern : pattern '?' pattern ':' pattern",
+"pattern : pattern bor pattern",
+"pattern : pattern and pattern",
+"pattern : pattern EQ pattern",
+"pattern : pattern GE pattern",
+"pattern : pattern GT pattern",
+"pattern : pattern LE pattern",
+"pattern : pattern LT pattern",
+"pattern : pattern NE pattern",
+"pattern : pattern MATCHOP reg_expr",
+"pattern : pattern MATCHOP pattern",
+"pattern : pattern IN varname",
+"pattern : '(' plist ')' IN varname",
+"pattern : pattern '|' GETLINE var",
+"pattern : pattern '|' GETLINE",
+"pattern : pattern term",
+"pattern : re",
+"pattern : term",
+"plist : pattern comma pattern",
+"plist : plist comma pattern",
+"pplist : ppattern",
+"pplist : pplist comma ppattern",
+"prarg :",
+"prarg : pplist",
+"prarg : '(' plist ')'",
+"print : PRINT",
+"print : PRINTF",
+"pst : NL",
+"pst : ';'",
+"pst : pst NL",
+"pst : pst ';'",
+"rbrace : '}'",
+"rbrace : rbrace NL",
+"re : reg_expr",
+"re : NOT re",
+"$$5 :",
+"reg_expr : '/' $$5 REGEXPR '/'",
+"rparen : ')'",
+"rparen : rparen NL",
+"simple_stmt : print prarg '|' term",
+"simple_stmt : print prarg APPEND ppattern",
+"simple_stmt : print prarg GT ppattern",
+"simple_stmt : print prarg",
+"simple_stmt : DELETE varname '[' patlist ']'",
+"simple_stmt : DELETE varname",
+"simple_stmt : pattern",
+"simple_stmt : error",
+"st : nl",
+"st : ';' opt_nl",
+"stmt : BREAK st",
+"stmt : CONTINUE st",
+"$$6 :",
+"$$7 :",
+"stmt : do $$6 stmt $$7 WHILE '(' pattern ')' st",
+"stmt : EXIT pattern st",
+"stmt : EXIT st",
+"stmt : for",
+"stmt : if stmt else stmt",
+"stmt : if stmt",
+"stmt : lbrace stmtlist rbrace",
+"stmt : NEXT st",
+"stmt : NEXTFILE st",
+"stmt : RETURN pattern st",
+"stmt : RETURN st",
+"stmt : simple_stmt st",
+"$$8 :",
+"stmt : while $$8 stmt",
+"stmt : ';' opt_nl",
+"stmt : VAR st",
+"stmtlist : stmt",
+"stmtlist : stmtlist stmt",
+"subop : SUB",
+"subop : GSUB",
+"term : term '/' ASGNOP term",
+"term : term '+' term",
+"term : term '-' term",
+"term : term '*' term",
+"term : term '/' term",
+"term : term '%' term",
+"term : term POWER term",
+"term : '-' term",
+"term : '+' term",
+"term : NOT term",
+"term : BLTIN '(' ')'",
+"term : BLTIN '(' patlist ')'",
+"term : BLTIN",
+"term : CALL '(' ')'",
+"term : CALL '(' patlist ')'",
+"term : CLOSE term",
+"term : DECR var",
+"term : INCR var",
+"term : var DECR",
+"term : var INCR",
+"term : GETLINE var LT term",
+"term : GETLINE LT term",
+"term : GETLINE var",
+"term : GETLINE",
+"term : bitwise '(' pattern comma pattern ')'",
+"term : BITCOMPL '(' pattern ')'",
+"term : INDEX '(' pattern comma pattern ')'",
+"term : INDEX '(' pattern comma reg_expr ')'",
+"term : '(' pattern ')'",
+"term : MATCHFCN '(' pattern comma reg_expr ')'",
+"term : MATCHFCN '(' pattern comma pattern ')'",
+"term : NUMBER",
+"term : SPLIT '(' pattern comma varname comma pattern ')'",
+"term : SPLIT '(' pattern comma varname comma reg_expr ')'",
+"term : SPLIT '(' pattern comma varname ')'",
+"term : SPRINTF '(' patlist ')'",
+"term : STRING",
+"term : subop '(' reg_expr comma pattern ')'",
+"term : subop '(' pattern comma pattern ')'",
+"term : subop '(' reg_expr comma pattern comma var ')'",
+"term : subop '(' pattern comma pattern comma var ')'",
+"term : SUBSTR '(' pattern comma pattern comma pattern ')'",
+"term : SUBSTR '(' pattern comma pattern ')'",
+"term : var",
+"var : varname",
+"var : varname '[' patlist ']'",
+"var : IVAR",
+"var : INDIRECT term",
+"varlist :",
+"varlist : VAR",
+"varlist : varlist comma VAR",
+"varname : VAR",
+"varname : ARG",
+"varname : VARNF",
+"varname : VARERR",
+"while : WHILE '(' pattern rparen",
+
+};
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+
+/* define the initial stack-sizes */
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH  YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH  500
+#endif
+#endif
+
+#define YYINITSTACKSIZE 500
+
+int    yydebug;
+int    yynerrs;
+
+typedef struct {
+  unsigned stacksize;
+  short  *s_base;
+  short  *s_mark;
+  short  *s_last;
+  YYSTYPE  *l_base;
+  YYSTYPE  *l_mark;
+} YYSTACKDATA;
+
+#define YYPURE 0
+
+int    yyerrflag;
+int    yychar;
+YYSTYPE  yyval;
+YYSTYPE  yylval;
+
+/* variables for the parser stack */
+static YYSTACKDATA yystack;
+#line 459 "awkgram.y"
+
+void setfname(Cell *p)
+{
+  if (isarr(p))
+    SYNTAX("%s is an array, not a function", p->nval);
+  else if (isfcn(p))
+    SYNTAX("you can't define function %s more than once", p->nval);
+  curfname = p->nval;
+}
+
+int constnode(Node *p)
+{
+  return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
+}
+
+char *strnode(Node *p)
+{
+  return ((Cell *)(p->narg[0]))->sval;
+}
+
+Node *notnull(Node *n)
+{
+  switch (n->nobj) {
+  case LE: case LT: case EQ: case NE: case GT: case GE:
+  case BOR: case AND: case NOT:
+    return n;
+  default:
+    return op2(NE, n, nullnode);
+  }
+}
+
+void checkdup(Node *vl, Cell *cp)  /* check if name already in list */
+{
+  char *s = cp->nval;
+  for ( ; vl; vl = vl->nnext) {
+    if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
+      SYNTAX("duplicate argument %s", s);
+      break;
+    }
+  }
+}
+#line 2815 "y.tab.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack(YYSTACKDATA *data)
+{
+  int i;
+  unsigned newsize;
+  short *newss;
+  YYSTYPE *newvs;
+
+  if ((newsize = data->stacksize) == 0)
+    newsize = YYINITSTACKSIZE;
+  else if (newsize >= YYMAXDEPTH)
+    return -1;
+  else if ((newsize *= 2) > YYMAXDEPTH)
+    newsize = YYMAXDEPTH;
+
+  i = data->s_mark - data->s_base;
+  newss = (data->s_base != 0)
+      ? (short *)realloc(data->s_base, newsize * sizeof(*newss))
+      : (short *)malloc(newsize * sizeof(*newss));
+  if (newss == 0)
+    return -1;
+
+  data->s_base  = newss;
+  data->s_mark = newss + i;
+
+  newvs = (data->l_base != 0)
+      ? (YYSTYPE *)realloc(data->l_base, newsize * sizeof(*newvs))
+      : (YYSTYPE *)malloc(newsize * sizeof(*newvs));
+  if (newvs == 0)
+    return -1;
+
+  data->l_base = newvs;
+  data->l_mark = newvs + i;
+
+  data->stacksize = newsize;
+  data->s_last = data->s_base + newsize - 1;
+  return 0;
+}
+
+#if YYPURE || defined(YY_NO_LEAKS)
+static void yyfreestack(YYSTACKDATA *data)
+{
+  free(data->s_base);
+  free(data->l_base);
+  memset(data, 0, sizeof(*data));
+}
+#else
+#define yyfreestack(data) /* nothing */
+#endif
+
+#define YYABORT  goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR  goto yyerrlab
+
+int
+YYPARSE_DECL()
+{
+  int yym, yyn, yystate;
+#if YYDEBUG
+  const char *yys;
+
+  if ((yys = getenv("YYDEBUG")) != 0)
+  {
+    yyn = *yys;
+    if (yyn >= '0' && yyn <= '9')
+      yydebug = yyn - '0';
+  }
+#endif
+
+  yynerrs = 0;
+  yyerrflag = 0;
+  yychar = YYEMPTY;
+  yystate = 0;
+
+#if YYPURE
+  memset(&yystack, 0, sizeof(yystack));
+#endif
+
+  if (yystack.s_base == NULL && yygrowstack(&yystack)) goto yyoverflow;
+  yystack.s_mark = yystack.s_base;
+  yystack.l_mark = yystack.l_base;
+  yystate = 0;
+  *yystack.s_mark = 0;
+
+yyloop:
+  if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+  if (yychar < 0)
+  {
+    if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+    if (yydebug)
+    {
+      yys = 0;
+      if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+      if (!yys) yys = "illegal-symbol";
+      printf("%sdebug: state %d, reading %d (%s)\n",
+          YYPREFIX, yystate, yychar, yys);
+    }
+#endif
+  }
+  if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+      yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+  {
+#if YYDEBUG
+    if (yydebug)
+      printf("%sdebug: state %d, shifting to state %d\n",
+          YYPREFIX, yystate, yytable[yyn]);
+#endif
+    if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+    {
+      goto yyoverflow;
+    }
+    yystate = yytable[yyn];
+    *++yystack.s_mark = yytable[yyn];
+    *++yystack.l_mark = yylval;
+    yychar = YYEMPTY;
+    if (yyerrflag > 0)  --yyerrflag;
+    goto yyloop;
+  }
+  if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+      yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+  {
+    yyn = yytable[yyn];
+    goto yyreduce;
+  }
+  if (yyerrflag) goto yyinrecovery;
+
+  yyerror("syntax error");
+
+  goto yyerrlab;
+
+yyerrlab:
+  ++yynerrs;
+
+yyinrecovery:
+  if (yyerrflag < 3)
+  {
+    yyerrflag = 3;
+    for (;;)
+    {
+      if ((yyn = yysindex[*yystack.s_mark]) && (yyn += YYERRCODE) >= 0 &&
+          yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+      {
+#if YYDEBUG
+        if (yydebug)
+          printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yystack.s_mark, yytable[yyn]);
+#endif
+        if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+        {
+          goto yyoverflow;
+        }
+        yystate = yytable[yyn];
+        *++yystack.s_mark = yytable[yyn];
+        *++yystack.l_mark = yylval;
+        goto yyloop;
+      }
+      else
+      {
+#if YYDEBUG
+        if (yydebug)
+          printf("%sdebug: error recovery discarding state %d\n",
+              YYPREFIX, *yystack.s_mark);
+#endif
+        if (yystack.s_mark <= yystack.s_base) goto yyabort;
+        --yystack.s_mark;
+        --yystack.l_mark;
+      }
+    }
+  }
+  else
+  {
+    if (yychar == 0) goto yyabort;
+#if YYDEBUG
+    if (yydebug)
+    {
+      yys = 0;
+      if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+      if (!yys) yys = "illegal-symbol";
+      printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+          YYPREFIX, yystate, yychar, yys);
+    }
+#endif
+    yychar = YYEMPTY;
+    goto yyloop;
+  }
+
+yyreduce:
+#if YYDEBUG
+  if (yydebug)
+    printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+        YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+  yym = yylen[yyn];
+  if (yym)
+    yyval = yystack.l_mark[1-yym];
+  else
+    memset(&yyval, 0, sizeof yyval);
+  switch (yyn)
+  {
+case 1:
+#line 101 "awkgram.y"
+  { if (errorflag==0)
+      winner = (Node *)stat3(PROGRAM, beginloc, yystack.l_mark[0].p, endloc); }
+break;
+case 2:
+#line 103 "awkgram.y"
+  { yyclearin; bracecheck(); SYNTAX("bailing out"); }
+break;
+case 19:
+#line 131 "awkgram.y"
+  {inloop++;}
+break;
+case 20:
+#line 132 "awkgram.y"
+  { --inloop; yyval.p = stat4(FOR, yystack.l_mark[-9].p, notnull(yystack.l_mark[-6].p), yystack.l_mark[-3].p, yystack.l_mark[0].p); }
+break;
+case 21:
+#line 133 "awkgram.y"
+  {inloop++;}
+break;
+case 22:
+#line 134 "awkgram.y"
+  { --inloop; yyval.p = stat4(FOR, yystack.l_mark[-7].p, NIL, yystack.l_mark[-3].p, yystack.l_mark[0].p); }
+break;
+case 23:
+#line 135 "awkgram.y"
+  {inloop++;}
+break;
+case 24:
+#line 136 "awkgram.y"
+  { --inloop; yyval.p = stat3(IN, yystack.l_mark[-5].p, makearr(yystack.l_mark[-3].p), yystack.l_mark[0].p); }
+break;
+case 25:
+#line 140 "awkgram.y"
+  { setfname(yystack.l_mark[0].cp); }
+break;
+case 26:
+#line 141 "awkgram.y"
+  { setfname(yystack.l_mark[0].cp); }
+break;
+case 27:
+#line 145 "awkgram.y"
+  { yyval.p = notnull(yystack.l_mark[-1].p); }
+break;
+case 32:
+#line 157 "awkgram.y"
+  { yyval.i = 0; }
+break;
+case 34:
+#line 162 "awkgram.y"
+  { yyval.i = 0; }
+break;
+case 36:
+#line 168 "awkgram.y"
+  { yyval.p = 0; }
+break;
+case 38:
+#line 173 "awkgram.y"
+  { yyval.p = 0; }
+break;
+case 39:
+#line 174 "awkgram.y"
+  { yyval.p = yystack.l_mark[-1].p; }
+break;
+case 40:
+#line 178 "awkgram.y"
+  { yyval.p = notnull(yystack.l_mark[0].p); }
+break;
+case 41:
+#line 182 "awkgram.y"
+  { yyval.p = stat2(PASTAT, yystack.l_mark[0].p, stat2(PRINT, rectonode(), NIL)); }
+break;
+case 42:
+#line 183 "awkgram.y"
+  { yyval.p = stat2(PASTAT, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 43:
+#line 184 "awkgram.y"
+  { yyval.p = pa2stat(yystack.l_mark[-3].p, yystack.l_mark[0].p, stat2(PRINT, rectonode(), NIL)); }
+break;
+case 44:
+#line 185 "awkgram.y"
+  { yyval.p = pa2stat(yystack.l_mark[-6].p, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 45:
+#line 186 "awkgram.y"
+  { yyval.p = stat2(PASTAT, NIL, yystack.l_mark[-1].p); }
+break;
+case 46:
+#line 188 "awkgram.y"
+  { beginloc = linkum(beginloc, yystack.l_mark[-1].p); yyval.p = 0; }
+break;
+case 47:
+#line 190 "awkgram.y"
+  { endloc = linkum(endloc, yystack.l_mark[-1].p); yyval.p = 0; }
+break;
+case 48:
+#line 191 "awkgram.y"
+  {infunc++;}
+break;
+case 49:
+#line 192 "awkgram.y"
+  { infunc--; curfname=0; defn((Cell *)yystack.l_mark[-7].p, yystack.l_mark[-5].p, yystack.l_mark[-1].p); yyval.p = 0; }
+break;
+case 51:
+#line 197 "awkgram.y"
+  { yyval.p = linkum(yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 53:
+#line 202 "awkgram.y"
+  { yyval.p = linkum(yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 54:
+#line 206 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 55:
+#line 208 "awkgram.y"
+  { yyval.p = op3(CONDEXPR, notnull(yystack.l_mark[-4].p), yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 56:
+#line 210 "awkgram.y"
+  { yyval.p = op2(BOR, notnull(yystack.l_mark[-2].p), notnull(yystack.l_mark[0].p)); }
+break;
+case 57:
+#line 212 "awkgram.y"
+  { yyval.p = op2(AND, notnull(yystack.l_mark[-2].p), notnull(yystack.l_mark[0].p)); }
+break;
+case 58:
+#line 213 "awkgram.y"
+  { yyval.p = op3(yystack.l_mark[-1].i, NIL, yystack.l_mark[-2].p, (Node*)makedfa(yystack.l_mark[0].s, 0)); }
+break;
+case 59:
+#line 215 "awkgram.y"
+  { if (constnode(yystack.l_mark[0].p))
+      yyval.p = op3(yystack.l_mark[-1].i, NIL, yystack.l_mark[-2].p, (Node*)makedfa(strnode(yystack.l_mark[0].p), 0));
+      else
+      yyval.p = op3(yystack.l_mark[-1].i, (Node *)1, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 60:
+#line 219 "awkgram.y"
+  { yyval.p = op2(INTEST, yystack.l_mark[-2].p, makearr(yystack.l_mark[0].p)); }
+break;
+case 61:
+#line 220 "awkgram.y"
+  { yyval.p = op2(INTEST, yystack.l_mark[-3].p, makearr(yystack.l_mark[0].p)); }
+break;
+case 62:
+#line 221 "awkgram.y"
+  { yyval.p = op2(CAT, yystack.l_mark[-1].p, yystack.l_mark[0].p); }
+break;
+case 65:
+#line 227 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 66:
+#line 229 "awkgram.y"
+  { yyval.p = op3(CONDEXPR, notnull(yystack.l_mark[-4].p), yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 67:
+#line 231 "awkgram.y"
+  { yyval.p = op2(BOR, notnull(yystack.l_mark[-2].p), notnull(yystack.l_mark[0].p)); }
+break;
+case 68:
+#line 233 "awkgram.y"
+  { yyval.p = op2(AND, notnull(yystack.l_mark[-2].p), notnull(yystack.l_mark[0].p)); }
+break;
+case 69:
+#line 234 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 70:
+#line 235 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 71:
+#line 236 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 72:
+#line 237 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 73:
+#line 238 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 74:
+#line 239 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-1].i, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 75:
+#line 240 "awkgram.y"
+  { yyval.p = op3(yystack.l_mark[-1].i, NIL, yystack.l_mark[-2].p, (Node*)makedfa(yystack.l_mark[0].s, 0)); }
+break;
+case 76:
+#line 242 "awkgram.y"
+  { if (constnode(yystack.l_mark[0].p))
+      yyval.p = op3(yystack.l_mark[-1].i, NIL, yystack.l_mark[-2].p, (Node*)makedfa(strnode(yystack.l_mark[0].p), 0));
+      else
+      yyval.p = op3(yystack.l_mark[-1].i, (Node *)1, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 77:
+#line 246 "awkgram.y"
+  { yyval.p = op2(INTEST, yystack.l_mark[-2].p, makearr(yystack.l_mark[0].p)); }
+break;
+case 78:
+#line 247 "awkgram.y"
+  { yyval.p = op2(INTEST, yystack.l_mark[-3].p, makearr(yystack.l_mark[0].p)); }
+break;
+case 79:
+#line 248 "awkgram.y"
+  { 
+      if (safe) SYNTAX("cmd | getline is unsafe");
+      else yyval.p = op3(GETLINE, yystack.l_mark[0].p, itonp(yystack.l_mark[-2].i), yystack.l_mark[-3].p); }
+break;
+case 80:
+#line 251 "awkgram.y"
+  { 
+      if (safe) SYNTAX("cmd | getline is unsafe");
+      else yyval.p = op3(GETLINE, (Node*)0, itonp(yystack.l_mark[-1].i), yystack.l_mark[-2].p); }
+break;
+case 81:
+#line 254 "awkgram.y"
+  { yyval.p = op2(CAT, yystack.l_mark[-1].p, yystack.l_mark[0].p); }
+break;
+case 84:
+#line 260 "awkgram.y"
+  { yyval.p = linkum(yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 85:
+#line 261 "awkgram.y"
+  { yyval.p = linkum(yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 87:
+#line 266 "awkgram.y"
+  { yyval.p = linkum(yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 88:
+#line 270 "awkgram.y"
+  { yyval.p = rectonode(); }
+break;
+case 90:
+#line 272 "awkgram.y"
+  { yyval.p = yystack.l_mark[-1].p; }
+break;
+case 99:
+#line 289 "awkgram.y"
+  { yyval.p = op3(MATCH, NIL, rectonode(), (Node*)makedfa(yystack.l_mark[0].s, 0)); }
+break;
+case 100:
+#line 290 "awkgram.y"
+  { yyval.p = op1(NOT, notnull(yystack.l_mark[0].p)); }
+break;
+case 101:
+#line 294 "awkgram.y"
+  {startreg();}
+break;
+case 102:
+#line 294 "awkgram.y"
+  { yyval.s = yystack.l_mark[-1].s; }
+break;
+case 105:
+#line 302 "awkgram.y"
+  { 
+      if (safe) SYNTAX("print | is unsafe");
+      else yyval.p = stat3(yystack.l_mark[-3].i, yystack.l_mark[-2].p, itonp(yystack.l_mark[-1].i), yystack.l_mark[0].p); }
+break;
+case 106:
+#line 305 "awkgram.y"
+  {
+      if (safe) SYNTAX("print >> is unsafe");
+      else yyval.p = stat3(yystack.l_mark[-3].i, yystack.l_mark[-2].p, itonp(yystack.l_mark[-1].i), yystack.l_mark[0].p); }
+break;
+case 107:
+#line 308 "awkgram.y"
+  {
+      if (safe) SYNTAX("print > is unsafe");
+      else yyval.p = stat3(yystack.l_mark[-3].i, yystack.l_mark[-2].p, itonp(yystack.l_mark[-1].i), yystack.l_mark[0].p); }
+break;
+case 108:
+#line 311 "awkgram.y"
+  { yyval.p = stat3(yystack.l_mark[-1].i, yystack.l_mark[0].p, NIL, NIL); }
+break;
+case 109:
+#line 312 "awkgram.y"
+  { yyval.p = stat2(DELETE, makearr(yystack.l_mark[-3].p), yystack.l_mark[-1].p); }
+break;
+case 110:
+#line 313 "awkgram.y"
+  { yyval.p = stat2(DELETE, makearr(yystack.l_mark[0].p), 0); }
+break;
+case 111:
+#line 314 "awkgram.y"
+  { yyval.p = exptostat(yystack.l_mark[0].p); }
+break;
+case 112:
+#line 315 "awkgram.y"
+  { yyclearin; SYNTAX("illegal statement"); }
+break;
+case 115:
+#line 324 "awkgram.y"
+  { if (!inloop) SYNTAX("break illegal outside of loops");
+          yyval.p = stat1(BREAK, NIL); }
+break;
+case 116:
+#line 326 "awkgram.y"
+  {  if (!inloop) SYNTAX("continue illegal outside of loops");
+          yyval.p = stat1(CONTINUE, NIL); }
+break;
+case 117:
+#line 328 "awkgram.y"
+  {inloop++;}
+break;
+case 118:
+#line 328 "awkgram.y"
+  {--inloop;}
+break;
+case 119:
+#line 329 "awkgram.y"
+  { yyval.p = stat2(DO, yystack.l_mark[-6].p, notnull(yystack.l_mark[-2].p)); }
+break;
+case 120:
+#line 330 "awkgram.y"
+  { yyval.p = stat1(EXIT, yystack.l_mark[-1].p); }
+break;
+case 121:
+#line 331 "awkgram.y"
+  { yyval.p = stat1(EXIT, NIL); }
+break;
+case 123:
+#line 333 "awkgram.y"
+  { yyval.p = stat3(IF, yystack.l_mark[-3].p, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 124:
+#line 334 "awkgram.y"
+  { yyval.p = stat3(IF, yystack.l_mark[-1].p, yystack.l_mark[0].p, NIL); }
+break;
+case 125:
+#line 335 "awkgram.y"
+  { yyval.p = yystack.l_mark[-1].p; }
+break;
+case 126:
+#line 336 "awkgram.y"
+  { if (infunc)
+        SYNTAX("next is illegal inside a function");
+        yyval.p = stat1(NEXT, NIL); }
+break;
+case 127:
+#line 339 "awkgram.y"
+  { if (infunc)
+        SYNTAX("nextfile is illegal inside a function");
+        yyval.p = stat1(NEXTFILE, NIL); }
+break;
+case 128:
+#line 342 "awkgram.y"
+  { yyval.p = stat1(RETURN, yystack.l_mark[-1].p); }
+break;
+case 129:
+#line 343 "awkgram.y"
+  { yyval.p = stat1(RETURN, NIL); }
+break;
+case 131:
+#line 345 "awkgram.y"
+  {inloop++;}
+break;
+case 132:
+#line 345 "awkgram.y"
+  { --inloop; yyval.p = stat2(WHILE, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 133:
+#line 346 "awkgram.y"
+  { yyval.p = 0; }
+break;
+case 134:
+#line 347 "awkgram.y"
+  { yyval.p = 0;}
+break;
+case 136:
+#line 352 "awkgram.y"
+  { yyval.p = linkum(yystack.l_mark[-1].p, yystack.l_mark[0].p); }
+break;
+case 139:
+#line 360 "awkgram.y"
+  { yyval.p = op2(DIVEQ, yystack.l_mark[-3].p, yystack.l_mark[0].p); }
+break;
+case 140:
+#line 361 "awkgram.y"
+  { yyval.p = op2(ADD, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 141:
+#line 362 "awkgram.y"
+  { yyval.p = op2(MINUS, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 142:
+#line 363 "awkgram.y"
+  { yyval.p = op2(MULT, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 143:
+#line 364 "awkgram.y"
+  { yyval.p = op2(DIVIDE, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 144:
+#line 365 "awkgram.y"
+  { yyval.p = op2(MOD, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 145:
+#line 366 "awkgram.y"
+  { yyval.p = op2(POWER, yystack.l_mark[-2].p, yystack.l_mark[0].p); }
+break;
+case 146:
+#line 367 "awkgram.y"
+  { yyval.p = op1(UMINUS, yystack.l_mark[0].p); }
+break;
+case 147:
+#line 368 "awkgram.y"
+  { yyval.p = yystack.l_mark[0].p; }
+break;
+case 148:
+#line 369 "awkgram.y"
+  { yyval.p = op1(NOT, notnull(yystack.l_mark[0].p)); }
+break;
+case 149:
+#line 370 "awkgram.y"
+  { yyval.p = op2(BLTIN, itonp(yystack.l_mark[-2].i), rectonode()); }
+break;
+case 150:
+#line 371 "awkgram.y"
+  { yyval.p = op2(BLTIN, itonp(yystack.l_mark[-3].i), yystack.l_mark[-1].p); }
+break;
+case 151:
+#line 372 "awkgram.y"
+  { yyval.p = op2(BLTIN, itonp(yystack.l_mark[0].i), rectonode()); }
+break;
+case 152:
+#line 373 "awkgram.y"
+  { yyval.p = op2(CALL, celltonode(yystack.l_mark[-2].cp,CVAR), NIL); }
+break;
+case 153:
+#line 374 "awkgram.y"
+  { yyval.p = op2(CALL, celltonode(yystack.l_mark[-3].cp,CVAR), yystack.l_mark[-1].p); }
+break;
+case 154:
+#line 375 "awkgram.y"
+  { yyval.p = op1(CLOSE, yystack.l_mark[0].p); }
+break;
+case 155:
+#line 376 "awkgram.y"
+  { yyval.p = op1(PREDECR, yystack.l_mark[0].p); }
+break;
+case 156:
+#line 377 "awkgram.y"
+  { yyval.p = op1(PREINCR, yystack.l_mark[0].p); }
+break;
+case 157:
+#line 378 "awkgram.y"
+  { yyval.p = op1(POSTDECR, yystack.l_mark[-1].p); }
+break;
+case 158:
+#line 379 "awkgram.y"
+  { yyval.p = op1(POSTINCR, yystack.l_mark[-1].p); }
+break;
+case 159:
+#line 380 "awkgram.y"
+  { yyval.p = op3(GETLINE, yystack.l_mark[-2].p, itonp(yystack.l_mark[-1].i), yystack.l_mark[0].p); }
+break;
+case 160:
+#line 381 "awkgram.y"
+  { yyval.p = op3(GETLINE, NIL, itonp(yystack.l_mark[-1].i), yystack.l_mark[0].p); }
+break;
+case 161:
+#line 382 "awkgram.y"
+  { yyval.p = op3(GETLINE, yystack.l_mark[0].p, NIL, NIL); }
+break;
+case 162:
+#line 383 "awkgram.y"
+  { yyval.p = op3(GETLINE, NIL, NIL, NIL); }
+break;
+case 163:
+#line 385 "awkgram.y"
+  { yyval.p = op2(yystack.l_mark[-5].i, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 164:
+#line 387 "awkgram.y"
+  { yyval.p = op2(BITCOMPL, yystack.l_mark[-1].p, yystack.l_mark[-1].p); }
+break;
+case 165:
+#line 389 "awkgram.y"
+  { yyval.p = op2(INDEX, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 166:
+#line 391 "awkgram.y"
+  { SYNTAX("index() doesn't permit regular expressions");
+      yyval.p = op2(INDEX, yystack.l_mark[-3].p, (Node*)yystack.l_mark[-1].s); }
+break;
+case 167:
+#line 393 "awkgram.y"
+  { yyval.p = yystack.l_mark[-1].p; }
+break;
+case 168:
+#line 395 "awkgram.y"
+  { yyval.p = op3(MATCHFCN, NIL, yystack.l_mark[-3].p, (Node*)makedfa(yystack.l_mark[-1].s, 1)); }
+break;
+case 169:
+#line 397 "awkgram.y"
+  { if (constnode(yystack.l_mark[-1].p))
+      yyval.p = op3(MATCHFCN, NIL, yystack.l_mark[-3].p, (Node*)makedfa(strnode(yystack.l_mark[-1].p), 1));
+      else
+      yyval.p = op3(MATCHFCN, (Node *)1, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 170:
+#line 401 "awkgram.y"
+  { yyval.p = celltonode(yystack.l_mark[0].cp, CCON); }
+break;
+case 171:
+#line 403 "awkgram.y"
+  { yyval.p = op4(SPLIT, yystack.l_mark[-5].p, makearr(yystack.l_mark[-3].p), yystack.l_mark[-1].p, (Node*)STRING); }
+break;
+case 172:
+#line 405 "awkgram.y"
+  { yyval.p = op4(SPLIT, yystack.l_mark[-5].p, makearr(yystack.l_mark[-3].p), (Node*)makedfa(yystack.l_mark[-1].s, 1), (Node *)REGEXPR); }
+break;
+case 173:
+#line 407 "awkgram.y"
+  { yyval.p = op4(SPLIT, yystack.l_mark[-3].p, makearr(yystack.l_mark[-1].p), NIL, (Node*)STRING); }
+break;
+case 174:
+#line 408 "awkgram.y"
+  { yyval.p = op1(yystack.l_mark[-3].i, yystack.l_mark[-1].p); }
+break;
+case 175:
+#line 409 "awkgram.y"
+  { yyval.p = celltonode(yystack.l_mark[0].cp, CCON); }
+break;
+case 176:
+#line 411 "awkgram.y"
+  { yyval.p = op4(yystack.l_mark[-5].i, NIL, (Node*)makedfa(yystack.l_mark[-3].s, 1), yystack.l_mark[-1].p, rectonode()); }
+break;
+case 177:
+#line 413 "awkgram.y"
+  { if (constnode(yystack.l_mark[-3].p))
+      yyval.p = op4(yystack.l_mark[-5].i, NIL, (Node*)makedfa(strnode(yystack.l_mark[-3].p), 1), yystack.l_mark[-1].p, rectonode());
+      else
+      yyval.p = op4(yystack.l_mark[-5].i, (Node *)1, yystack.l_mark[-3].p, yystack.l_mark[-1].p, rectonode()); }
+break;
+case 178:
+#line 418 "awkgram.y"
+  { yyval.p = op4(yystack.l_mark[-7].i, NIL, (Node*)makedfa(yystack.l_mark[-5].s, 1), yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 179:
+#line 420 "awkgram.y"
+  { if (constnode(yystack.l_mark[-5].p))
+      yyval.p = op4(yystack.l_mark[-7].i, NIL, (Node*)makedfa(strnode(yystack.l_mark[-5].p), 1), yystack.l_mark[-3].p, yystack.l_mark[-1].p);
+      else
+      yyval.p = op4(yystack.l_mark[-7].i, (Node *)1, yystack.l_mark[-5].p, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 180:
+#line 425 "awkgram.y"
+  { yyval.p = op3(SUBSTR, yystack.l_mark[-5].p, yystack.l_mark[-3].p, yystack.l_mark[-1].p); }
+break;
+case 181:
+#line 427 "awkgram.y"
+  { yyval.p = op3(SUBSTR, yystack.l_mark[-3].p, yystack.l_mark[-1].p, NIL); }
+break;
+case 184:
+#line 433 "awkgram.y"
+  { yyval.p = op2(ARRAY, makearr(yystack.l_mark[-3].p), yystack.l_mark[-1].p); }
+break;
+case 185:
+#line 434 "awkgram.y"
+  { yyval.p = op1(INDIRECT, celltonode(yystack.l_mark[0].cp, CVAR)); }
+break;
+case 186:
+#line 435 "awkgram.y"
+  { yyval.p = op1(INDIRECT, yystack.l_mark[0].p); }
+break;
+case 187:
+#line 439 "awkgram.y"
+  { arglist = yyval.p = 0; }
+break;
+case 188:
+#line 440 "awkgram.y"
+  { arglist = yyval.p = celltonode(yystack.l_mark[0].cp,CVAR); }
+break;
+case 189:
+#line 441 "awkgram.y"
+  {
+      checkdup(yystack.l_mark[-2].p, yystack.l_mark[0].cp);
+      arglist = yyval.p = linkum(yystack.l_mark[-2].p,celltonode(yystack.l_mark[0].cp,CVAR)); }
+break;
+case 190:
+#line 447 "awkgram.y"
+  { yyval.p = celltonode(yystack.l_mark[0].cp, CVAR); }
+break;
+case 191:
+#line 448 "awkgram.y"
+  { yyval.p = op1(ARG, itonp(yystack.l_mark[0].i)); }
+break;
+case 192:
+#line 449 "awkgram.y"
+  { yyval.p = op1(VARNF, (Node *) yystack.l_mark[0].cp); }
+break;
+case 193:
+#line 450 "awkgram.y"
+  { yyval.p = op1(VARERR, (Node *) yystack.l_mark[0].cp); }
+break;
+case 194:
+#line 455 "awkgram.y"
+  { yyval.p = notnull(yystack.l_mark[-1].p); }
+break;
+#line 3628 "y.tab.c"
+  }
+  yystack.s_mark -= yym;
+  yystate = *yystack.s_mark;
+  yystack.l_mark -= yym;
+  yym = yylhs[yyn];
+  if (yystate == 0 && yym == 0)
+  {
+#if YYDEBUG
+    if (yydebug)
+      printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+    yystate = YYFINAL;
+    *++yystack.s_mark = YYFINAL;
+    *++yystack.l_mark = yyval;
+    if (yychar < 0)
+    {
+      if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+      if (yydebug)
+      {
+        yys = 0;
+        if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+        if (!yys) yys = "illegal-symbol";
+        printf("%sdebug: state %d, reading %d (%s)\n",
+            YYPREFIX, YYFINAL, yychar, yys);
+      }
+#endif
+    }
+    if (yychar == 0) goto yyaccept;
+    goto yyloop;
+  }
+  if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+      yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+    yystate = yytable[yyn];
+  else
+    yystate = yydgoto[yym];
+#if YYDEBUG
+  if (yydebug)
+    printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yystack.s_mark, yystate);
+#endif
+  if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+  {
+    goto yyoverflow;
+  }
+  *++yystack.s_mark = (short) yystate;
+  *++yystack.l_mark = yyval;
+  goto yyloop;
+
+yyoverflow:
+  yyerror("yacc stack overflow");
+
+yyabort:
+  yyfreestack(&yystack);
+  return (1);
+
+yyaccept:
+  yyfreestack(&yystack);
+  return (0);
+}
diff --git a/lib/bunzip.c b/lib/bunzip.c
new file mode 100644 (file)
index 0000000..fd40c14
--- /dev/null
@@ -0,0 +1,633 @@
+/* micro-bunzip, a small, simple bzip2 decompression implementation.
+ *
+ * Copyright 2003, 2006 by Rob Landley (rob@landley.net).
+ *
+ * Based on a close reading (but not the actual code) of the original bzip2
+ * decompression code by Julian R Seward (jseward@acm.org), which also
+ * acknowledges contributions by Mike Burrows, David Wheeler, Peter Fenwick,
+ * Alistair Moffat, Radford Neal, Ian H. Witten, Robert Sedgewick, and
+ * Jon L. Bentley.
+ */
+
+#include "toys.h"
+
+#define THREADS 1
+
+// Constants for huffman coding
+#define MAX_GROUPS               6
+#define GROUP_SIZE               50     /* 64 would have been more efficient */
+#define MAX_HUFCODE_BITS         20     /* Longest huffman code allowed */
+#define MAX_SYMBOLS              258    /* 256 literals + RUNA + RUNB */
+#define SYMBOL_RUNA              0
+#define SYMBOL_RUNB              1
+
+// Other housekeeping constants
+#define IOBUF_SIZE               4096
+
+// Status return values
+#define RETVAL_LAST_BLOCK        (-100)
+#define RETVAL_NOT_BZIP_DATA     (-1)
+#define RETVAL_DATA_ERROR        (-2)
+#define RETVAL_OBSOLETE_INPUT    (-3)
+
+char *bunzip_errors[]={
+  NULL,
+  "Not bzip data",
+  "Data error",
+  "Obsolete (pre 0.9.5) bzip format not supported."
+};
+
+// This is what we know about each huffman coding group
+struct group_data {
+  int limit[MAX_HUFCODE_BITS+1], base[MAX_HUFCODE_BITS], permute[MAX_SYMBOLS];
+  char minLen, maxLen;
+};
+
+// Data for burrows wheeler transform
+
+struct bwdata {
+  unsigned int origPtr;
+  int byteCount[256];
+  // State saved when interrupting output
+  int writePos, writeRun, writeCount, writeCurrent;
+  unsigned int dataCRC, headerCRC;
+  unsigned int *dbuf;
+};
+
+// Structure holding all the housekeeping data, including IO buffers and
+// memory that persists between calls to bunzip
+struct bunzip_data {
+  // Input stream, input buffer, input bit buffer
+  int in_fd, inbufCount, inbufPos;
+  char *inbuf;
+  unsigned int inbufBitCount, inbufBits;
+
+  // Output buffer
+  char outbuf[IOBUF_SIZE];
+  int outbufPos;
+
+  unsigned int totalCRC;
+
+  // First pass decompression data (Huffman and MTF decoding)
+  char selectors[32768];                  // nSelectors=15 bits
+  struct group_data groups[MAX_GROUPS];   // huffman coding tables
+  int symTotal, groupCount, nSelectors;
+  unsigned char symToByte[256], mtfSymbol[256];
+
+  // The CRC values stored in the block header and calculated from the data
+  unsigned int crc32Table[256];
+
+  // Second pass decompression data (burrows-wheeler transform)
+  unsigned int dbufSize;
+  struct bwdata bwdata[THREADS];
+};
+
+// Return the next nnn bits of input.  All reads from the compressed input
+// are done through this function.  All reads are big endian.
+static unsigned int get_bits(struct bunzip_data *bd, char bits_wanted)
+{
+  unsigned int bits = 0;
+
+  // If we need to get more data from the byte buffer, do so.  (Loop getting
+  // one byte at a time to enforce endianness and avoid unaligned access.)
+  while (bd->inbufBitCount < bits_wanted) {
+
+    // If we need to read more data from file into byte buffer, do so
+    if (bd->inbufPos == bd->inbufCount) {
+      if (0 >= (bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)))
+        error_exit("input EOF");
+      bd->inbufPos = 0;
+    }
+
+    // Avoid 32-bit overflow (dump bit buffer to top of output)
+    if (bd->inbufBitCount>=24) {
+      bits = bd->inbufBits&((1<<bd->inbufBitCount)-1);
+      bits_wanted -= bd->inbufBitCount;
+      bits <<= bits_wanted;
+      bd->inbufBitCount = 0;
+    }
+
+    // Grab next 8 bits of input from buffer.
+    bd->inbufBits = (bd->inbufBits<<8) | bd->inbuf[bd->inbufPos++];
+    bd->inbufBitCount += 8;
+  }
+
+  // Calculate result
+  bd->inbufBitCount -= bits_wanted;
+  bits |= (bd->inbufBits>>bd->inbufBitCount) & ((1<<bits_wanted)-1);
+
+  return bits;
+}
+
+/* Read block header at start of a new compressed data block.  Consists of:
+ *
+ * 48 bits : Block signature, either pi (data block) or e (EOF block).
+ * 32 bits : bw->headerCRC
+ * 1  bit  : obsolete feature flag.
+ * 24 bits : origPtr (Burrows-wheeler unwind index, only 20 bits ever used)
+ * 16 bits : Mapping table index.
+ *[16 bits]: symToByte[symTotal] (Mapping table.  For each bit set in mapping
+ *           table index above, read another 16 bits of mapping table data.
+ *           If correspondig bit is unset, all bits in that mapping table
+ *           section are 0.)
+ *  3 bits : groupCount (how many huffman tables used to encode, anywhere
+ *           from 2 to MAX_GROUPS)
+ * variable: hufGroup[groupCount] (MTF encoded huffman table data.)
+ */
+
+static int read_block_header(struct bunzip_data *bd, struct bwdata *bw)
+{
+  struct group_data *hufGroup;
+  int hh, ii, jj, kk, symCount, *base, *limit;
+  unsigned char uc;
+
+  // Read in header signature and CRC (which is stored big endian)
+  ii = get_bits(bd, 24);
+  jj = get_bits(bd, 24);
+  bw->headerCRC = get_bits(bd,32);
+
+  // Is this the EOF block with CRC for whole file?  (Constant is "e")
+  if (ii==0x177245 && jj==0x385090) return RETVAL_LAST_BLOCK;
+
+  // Is this a valid data block?  (Constant is "pi".)
+  if (ii!=0x314159 || jj!=0x265359) return RETVAL_NOT_BZIP_DATA;
+
+  // We can add support for blockRandomised if anybody complains.
+  if (get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT;
+  if ((bw->origPtr = get_bits(bd,24)) > bd->dbufSize) return RETVAL_DATA_ERROR;
+
+  // mapping table: if some byte values are never used (encoding things
+  // like ascii text), the compression code removes the gaps to have fewer
+  // symbols to deal with, and writes a sparse bitfield indicating which
+  // values were present.  We make a translation table to convert the symbols
+  // back to the corresponding bytes.
+  hh = get_bits(bd, 16);
+  bd->symTotal = 0;
+  for (ii=0; ii<16; ii++) {
+    if (hh & (1 << (15 - ii))) {
+      kk = get_bits(bd, 16);
+      for (jj=0; jj<16; jj++)
+        if (kk & (1 << (15 - jj)))
+          bd->symToByte[bd->symTotal++] = (16 * ii) + jj;
+    }
+  }
+
+  // How many different huffman coding groups does this block use?
+  bd->groupCount = get_bits(bd,3);
+  if (bd->groupCount<2 || bd->groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR;
+
+  // nSelectors: Every GROUP_SIZE many symbols we switch huffman coding
+  // tables.  Each group has a selector, which is an index into the huffman
+  // coding table arrays.
+  //
+  // Read in the group selector array, which is stored as MTF encoded
+  // bit runs.  (MTF = Move To Front.  Every time a symbol occurs it's moved
+  // to the front of the table, so it has a shorter encoding next time.)
+  if (!(bd->nSelectors = get_bits(bd, 15))) return RETVAL_DATA_ERROR;
+  for (ii=0; ii<bd->groupCount; ii++) bd->mtfSymbol[ii] = ii;
+  for (ii=0; ii<bd->nSelectors; ii++) {
+
+    // Get next value
+    for(jj=0;get_bits(bd,1);jj++)
+      if (jj>=bd->groupCount) return RETVAL_DATA_ERROR;
+
+    // Decode MTF to get the next selector, and move it to the front.
+    uc = bd->mtfSymbol[jj];
+    memmove(bd->mtfSymbol+1, bd->mtfSymbol, jj);
+    bd->mtfSymbol[0] = bd->selectors[ii] = uc;
+  }
+
+  // Read the huffman coding tables for each group, which code for symTotal
+  // literal symbols, plus two run symbols (RUNA, RUNB)
+  symCount = bd->symTotal+2;
+  for (jj=0; jj<bd->groupCount; jj++) {
+    unsigned char length[MAX_SYMBOLS];
+    unsigned temp[MAX_HUFCODE_BITS+1];
+    int minLen, maxLen, pp;
+
+    // Read lengths
+    hh = get_bits(bd, 5);
+    for (ii = 0; ii < symCount; ii++) {
+      for(;;) {
+        // !hh || hh > MAX_HUFCODE_BITS in one test.
+        if (MAX_HUFCODE_BITS-1 < (unsigned)hh-1) return RETVAL_DATA_ERROR;
+        // Grab 2 bits instead of 1 (slightly smaller/faster).  Stop if
+        // first bit is 0, otherwise second bit says whether to
+        // increment or decrement.
+        kk = get_bits(bd, 2);
+        if (kk & 2) hh += 1 - ((kk&1)<<1);
+        else {
+          bd->inbufBitCount++;
+          break;
+        }
+      }
+      length[ii] = hh;
+    }
+
+    // Find largest and smallest lengths in this group
+    minLen = maxLen = length[0];
+    for (ii = 1; ii < symCount; ii++) {
+      if(length[ii] > maxLen) maxLen = length[ii];
+      else if(length[ii] < minLen) minLen = length[ii];
+    }
+
+    /* Calculate permute[], base[], and limit[] tables from length[].
+     *
+     * permute[] is the lookup table for converting huffman coded symbols
+     * into decoded symbols.  It contains symbol values sorted by length.
+     *
+     * base[] is the amount to subtract from the value of a huffman symbol
+     * of a given length when using permute[].
+     *
+     * limit[] indicates the largest numerical value a symbol with a given
+     * number of bits can have.  It lets us know when to stop reading.
+     *
+     * To use these, keep reading bits until value <= limit[bitcount] or
+     * you've read over 20 bits (error).  Then the decoded symbol
+     * equals permute[hufcode_value - base[hufcode_bitcount]].
+     */
+    hufGroup = bd->groups+jj;
+    hufGroup->minLen = minLen;
+    hufGroup->maxLen = maxLen;
+
+    // Note that minLen can't be smaller than 1, so we adjust the base
+    // and limit array pointers so we're not always wasting the first
+    // entry.  We do this again when using them (during symbol decoding).
+    base = hufGroup->base-1;
+    limit = hufGroup->limit-1;
+
+    // zero temp[] and limit[], and calculate permute[]
+    pp = 0;
+    for (ii = minLen; ii <= maxLen; ii++) {
+      temp[ii] = limit[ii] = 0;
+      for (hh = 0; hh < symCount; hh++)
+        if (length[hh] == ii) hufGroup->permute[pp++] = hh;
+    }
+
+    // Count symbols coded for at each bit length
+    for (ii = 0; ii < symCount; ii++) temp[length[ii]]++;
+
+    /* Calculate limit[] (the largest symbol-coding value at each bit
+     * length, which is (previous limit<<1)+symbols at this level), and
+     * base[] (number of symbols to ignore at each bit length, which is
+     * limit minus the cumulative count of symbols coded for already). */
+    pp = hh = 0;
+    for (ii = minLen; ii < maxLen; ii++) {
+      pp += temp[ii];
+      limit[ii] = pp-1;
+      pp <<= 1;
+      base[ii+1] = pp-(hh+=temp[ii]);
+    }
+    limit[maxLen] = pp+temp[maxLen]-1;
+    limit[maxLen+1] = INT_MAX;
+    base[minLen] = 0;
+  }
+
+  return 0;
+}
+
+/* First pass, read block's symbols into dbuf[dbufCount].
+ *
+ * This undoes three types of compression: huffman coding, run length encoding,
+ * and move to front encoding.  We have to undo all those to know when we've
+ * read enough input.
+ */
+
+static int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
+{
+  struct group_data *hufGroup;
+  int hh, ii, jj, kk, runPos, dbufCount, symCount, selector, nextSym,
+    *byteCount, *base, *limit;
+  unsigned int *dbuf = bw->dbuf;
+  unsigned char uc;
+
+  // We've finished reading and digesting the block header.  Now read this
+  // block's huffman coded symbols from the file and undo the huffman coding
+  // and run length encoding, saving the result into dbuf[dbufCount++] = uc
+
+  // Initialize symbol occurrence counters and symbol mtf table
+  byteCount = bw->byteCount;
+  for(ii=0; ii<256; ii++) {
+    byteCount[ii] = 0;
+    bd->mtfSymbol[ii] = ii;
+  }
+
+  // Loop through compressed symbols.  This is the first "tight inner loop"
+  // that needs to be micro-optimized for speed.  (This one fills out dbuf[]
+  // linearly, staying in cache more, so isn't as limited by DRAM access.)
+  runPos = dbufCount = symCount = selector = 0;
+  // Some unnecessary initializations to shut gcc up.
+  base = limit = 0;
+  hufGroup = 0;
+  hh = 0;
+
+  for (;;) {
+    // Have we reached the end of this huffman group?
+    if (!(symCount--)) {
+      // Determine which huffman coding group to use.
+      symCount = GROUP_SIZE-1;
+      if (selector >= bd->nSelectors) return RETVAL_DATA_ERROR;
+      hufGroup = bd->groups + bd->selectors[selector++];
+      base = hufGroup->base-1;
+      limit = hufGroup->limit-1;
+    }
+
+    // Read next huffman-coded symbol (into jj).
+    ii = hufGroup->minLen;
+    jj = get_bits(bd, ii);
+    while (jj > limit[ii]) {
+      // if (ii > hufGroup->maxLen) return RETVAL_DATA_ERROR;
+      ii++;
+
+      // Unroll get_bits() to avoid a function call when the data's in
+      // the buffer already.
+      kk = bd->inbufBitCount
+        ? (bd->inbufBits >> --(bd->inbufBitCount)) & 1 : get_bits(bd, 1);
+      jj = (jj << 1) | kk;
+    }
+    // Huffman decode jj into nextSym (with bounds checking)
+    jj-=base[ii];
+
+    if (ii > hufGroup->maxLen || (unsigned)jj >= MAX_SYMBOLS)
+      return RETVAL_DATA_ERROR;
+    nextSym = hufGroup->permute[jj];
+
+    // If this is a repeated run, loop collecting data
+    if ((unsigned)nextSym <= SYMBOL_RUNB) {
+      // If this is the start of a new run, zero out counter
+      if(!runPos) {
+        runPos = 1;
+        hh = 0;
+      }
+
+      /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
+         each bit position, add 1 or 2 instead. For example,
+         1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
+         You can make any bit pattern that way using 1 less symbol than
+         the basic or 0/1 method (except all bits 0, which would use no
+         symbols, but a run of length 0 doesn't mean anything in this
+         context). Thus space is saved. */
+      hh += (runPos << nextSym); // +runPos if RUNA; +2*runPos if RUNB
+      runPos <<= 1;
+      continue;
+    }
+
+    /* When we hit the first non-run symbol after a run, we now know
+       how many times to repeat the last literal, so append that many
+       copies to our buffer of decoded symbols (dbuf) now. (The last
+       literal used is the one at the head of the mtfSymbol array.) */
+    if (runPos) {
+      runPos = 0;
+      if (dbufCount+hh >= bd->dbufSize) return RETVAL_DATA_ERROR;
+
+      uc = bd->symToByte[bd->mtfSymbol[0]];
+      byteCount[uc] += hh;
+      while (hh--) dbuf[dbufCount++] = uc;
+    }
+
+    // Is this the terminating symbol?
+    if (nextSym>bd->symTotal) break;
+
+    /* At this point, the symbol we just decoded indicates a new literal
+       character. Subtract one to get the position in the MTF array
+       at which this literal is currently to be found. (Note that the
+       result can't be -1 or 0, because 0 and 1 are RUNA and RUNB.
+       Another instance of the first symbol in the mtf array, position 0,
+       would have been handled as part of a run.) */
+    if (dbufCount>=bd->dbufSize) return RETVAL_DATA_ERROR;
+    ii = nextSym - 1;
+    uc = bd->mtfSymbol[ii];
+    // On my laptop, unrolling this memmove() into a loop shaves 3.5% off
+    // the total running time.
+    while(ii--) bd->mtfSymbol[ii+1] = bd->mtfSymbol[ii];
+    bd->mtfSymbol[0] = uc;
+    uc = bd->symToByte[uc];
+
+    // We have our literal byte.  Save it into dbuf.
+    byteCount[uc]++;
+    dbuf[dbufCount++] = (unsigned int)uc;
+  }
+
+  // Now we know what dbufCount is, do a better sanity check on origPtr.
+  if (bw->origPtr >= (bw->writeCount = dbufCount)) return RETVAL_DATA_ERROR;
+
+  return 0;
+}
+
+// Flush output buffer to disk
+void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
+{
+  if (bd->outbufPos) {
+    if (write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos)
+      error_exit("output EOF");
+    bd->outbufPos = 0;
+  }
+}
+
+void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
+{
+  int ii, jj;
+  unsigned int *dbuf = bw->dbuf;
+  int *byteCount = bw->byteCount;
+
+  // Technically this part is preparation for the burrows-wheeler
+  // transform, but it's quick and convenient to do here.
+
+  // Turn byteCount into cumulative occurrence counts of 0 to n-1.
+  jj = 0;
+  for (ii=0; ii<256; ii++) {
+    int kk = jj + byteCount[ii];
+    byteCount[ii] = jj;
+    jj = kk;
+  }
+
+  // Use occurrence counts to quickly figure out what order dbuf would be in
+  // if we sorted it.
+  for (ii=0; ii < bw->writeCount; ii++) {
+    unsigned char uc = dbuf[ii];
+    dbuf[byteCount[uc]] |= (ii << 8);
+    byteCount[uc]++;
+  }
+
+  // blockRandomised support would go here.
+
+  // Using ii as position, jj as previous character, hh as current character,
+  // and uc as run count.
+  bw->dataCRC = 0xffffffffL;
+
+  /* Decode first byte by hand to initialize "previous" byte. Note that it
+     doesn't get output, and if the first three characters are identical
+     it doesn't qualify as a run (hence uc=255, which will either wrap
+     to 1 or get reset). */
+  if (bw->writeCount) {
+    bw->writePos = dbuf[bw->origPtr];
+    bw->writeCurrent = (unsigned char)bw->writePos;
+    bw->writePos >>= 8;
+    bw->writeRun = -1;
+  }
+}
+
+// Decompress a block of text to intermediate buffer
+int read_bunzip_data(struct bunzip_data *bd)
+{
+  int rc = read_block_header(bd, bd->bwdata);
+  if (!rc) rc=read_huffman_data(bd, bd->bwdata);
+
+  // First thing that can be done by a background thread.
+  burrows_wheeler_prep(bd, bd->bwdata);
+
+  return rc;
+}
+
+// Undo burrows-wheeler transform on intermediate buffer to produce output.
+// If !len, write up to len bytes of data to buf.  Otherwise write to out_fd.
+// Returns len ? bytes written : 0.  Notice all errors are negative #'s.
+//
+// Burrows-wheeler transform is described at:
+// http://dogma.net/markn/articles/bwt/bwt.htm
+// http://marknelson.us/1996/09/01/bwt/
+
+int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw, int out_fd, char *outbuf, int len)
+{
+  unsigned int *dbuf = bw->dbuf;
+  int count, pos, current, run, copies, outbyte, previous, gotcount = 0;
+
+  for (;;) {
+    // If last read was short due to end of file, return last block now
+    if (bw->writeCount < 0) return bw->writeCount;
+
+    // If we need to refill dbuf, do it.
+    if (!bw->writeCount) {
+      int i = read_bunzip_data(bd);
+      if (i) {
+        if (i == RETVAL_LAST_BLOCK) {
+          bw->writeCount = i;
+          return gotcount;
+        } else return i;
+      }
+    }
+
+    // loop generating output
+    count = bw->writeCount;
+    pos = bw->writePos;
+    current = bw->writeCurrent;
+    run = bw->writeRun;
+    while (count) {
+
+      // If somebody (like tar) wants a certain number of bytes of
+      // data from memory instead of written to a file, humor them.
+      if (len && bd->outbufPos>=len) goto dataus_interruptus;
+      count--;
+
+      // Follow sequence vector to undo Burrows-Wheeler transform.
+      previous = current;
+      pos = dbuf[pos];
+      current = pos&0xff;
+      pos >>= 8;
+
+      // Whenever we see 3 consecutive copies of the same byte,
+      // the 4th is a repeat count
+      if (run++ == 3) {
+        copies = current;
+        outbyte = previous;
+        current = -1;
+      } else {
+        copies = 1;
+        outbyte = current;
+      }
+
+      // Output bytes to buffer, flushing to file if necessary
+      while (copies--) {
+        if (bd->outbufPos == IOBUF_SIZE) flush_bunzip_outbuf(bd,out_fd);
+        bd->outbuf[bd->outbufPos++] = outbyte;
+        bw->dataCRC = (bw->dataCRC << 8)
+                ^ bd->crc32Table[(bw->dataCRC >> 24) ^ outbyte];
+      }
+      if (current!=previous) run=0;
+    }
+
+    // decompression of this block completed successfully
+    bw->dataCRC = ~(bw->dataCRC);
+    bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bw->dataCRC;
+
+    // if this block had a crc error, force file level crc error.
+    if (bw->dataCRC != bw->headerCRC) {
+      bd->totalCRC = bw->headerCRC+1;
+
+      return RETVAL_LAST_BLOCK;
+    }
+dataus_interruptus:
+    bw->writeCount = count;
+    if (len) {
+      gotcount += bd->outbufPos;
+      memcpy(outbuf, bd->outbuf, len);
+
+      // If we got enough data, checkpoint loop state and return
+      if ((len-=bd->outbufPos)<1) {
+        bd->outbufPos -= len;
+        if (bd->outbufPos) memmove(bd->outbuf, bd->outbuf+len, bd->outbufPos);
+        bw->writePos = pos;
+        bw->writeCurrent = current;
+        bw->writeRun = run;
+
+        return gotcount;
+      }
+    }
+  }
+}
+
+// Allocate the structure, read file header. If !len, src_fd contains
+// filehandle to read from. Else inbuf contains data.
+int start_bunzip(struct bunzip_data **bdp, int src_fd, char *inbuf, int len)
+{
+  struct bunzip_data *bd;
+  unsigned int i;
+
+  // Figure out how much data to allocate.
+  i = sizeof(struct bunzip_data);
+  if (!len) i += IOBUF_SIZE;
+
+  // Allocate bunzip_data. Most fields initialize to zero.
+  bd = *bdp = xzalloc(i);
+  if (len) {
+    bd->inbuf = inbuf;
+    bd->inbufCount = len;
+    bd->in_fd = -1;
+  } else {
+    bd->inbuf = (char *)(bd+1);
+    bd->in_fd = src_fd;
+  }
+
+  crc_init(bd->crc32Table, 0);
+
+  // Ensure that file starts with "BZh".
+  for (i=0;i<3;i++) if (get_bits(bd,8)!="BZh"[i]) return RETVAL_NOT_BZIP_DATA;
+
+  // Next byte ascii '1'-'9', indicates block size in units of 100k of
+  // uncompressed data. Allocate intermediate buffer for block.
+  i = get_bits(bd, 8);
+  if (i<'1' || i>'9') return RETVAL_NOT_BZIP_DATA;
+  bd->dbufSize = 100000*(i-'0')*THREADS;
+  for (i=0; i<THREADS; i++)
+    bd->bwdata[i].dbuf = xmalloc(bd->dbufSize * sizeof(int));
+
+  return 0;
+}
+
+// Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
+// not end of file.)
+void bunzipStream(int src_fd, int dst_fd)
+{
+  struct bunzip_data *bd;
+  char *bunzip_errors[]={NULL, "not bzip", "bad data", "old format"};
+  int i, j;
+
+  if (!(i = start_bunzip(&bd,src_fd,0,0))) {
+    i = write_bunzip_data(bd,bd->bwdata,dst_fd,0,0);
+    if (i==RETVAL_LAST_BLOCK && bd->bwdata[0].headerCRC==bd->totalCRC) i = 0;
+  }
+  flush_bunzip_outbuf(bd,dst_fd);
+  for (j=0; j<THREADS; j++) free(bd->bwdata[j].dbuf);
+  free(bd);
+  if (i) error_exit(bunzip_errors[-i]);
+}
diff --git a/lib/dirtree.c b/lib/dirtree.c
new file mode 100644 (file)
index 0000000..0fc4431
--- /dev/null
@@ -0,0 +1,180 @@
+/* dirtree.c - Functions for dealing with directory trees.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+static int notdotdot(char *name)
+{
+  if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 0;
+
+  return 1;
+}
+
+// Default callback, filters out "." and "..".
+
+int dirtree_notdotdot(struct dirtree *catch)
+{
+  // Should we skip "." and ".."?
+  return notdotdot(catch->name) ? DIRTREE_SAVE|DIRTREE_RECURSE : 0;
+}
+
+// Create a dirtree node from a path, with stat and symlink info.
+// (This doesn't open directory filehandles yet so as not to exhaust the
+// filehandle space on large trees, dirtree_handle_callback() does that.)
+
+struct dirtree *dirtree_add_node(struct dirtree *parent, char *name,
+  int symfollow)
+{
+  struct dirtree *dt = NULL;
+  struct stat st;
+  char buf[4096];
+  int len = 0, linklen = 0;
+
+  if (name) {
+    // open code this because haven't got node to call dirtree_parentfd() on yet
+    int fd = parent ? parent->data : AT_FDCWD;
+
+    if (fstatat(fd, name, &st, symfollow ? 0 : AT_SYMLINK_NOFOLLOW)) goto error;
+    if (S_ISLNK(st.st_mode) && symfollow) {
+      if (0>(linklen = readlinkat(fd, name, buf, 4095))) goto error;
+      buf[linklen++]=0;
+    }
+    len = strlen(name);
+  }
+  dt = xzalloc((len = sizeof(struct dirtree)+len+1)+linklen);
+  dt->parent = parent;
+  if (name) {
+    memcpy(&(dt->st), &st, sizeof(struct stat));
+    strcpy(dt->name, name);
+
+    if (linklen) {
+      dt->symlink = memcpy(len+(char *)dt, buf, linklen);
+      dt->data = --linklen;
+    }
+  }
+
+  return dt;
+
+error:
+  if (notdotdot(name)) {
+    char *path = parent ? dirtree_path(parent, 0) : "";
+    perror_msg("%s%s%s",path, parent ? "/" : "", name);
+  }
+  if (parent) parent->symlink = (char *)1;
+  free(dt);
+  return 0;
+}
+
+// Return path to this node, assembled recursively.
+
+char *dirtree_path(struct dirtree *node, int *plen)
+{
+  char *path;
+  int len;
+
+  if (!node || !node->name) {
+    path = xmalloc(*plen);
+    *plen = 0;
+    return path;
+  }
+
+  len = (plen ? *plen : 0)+strlen(node->name)+1;
+  path = dirtree_path(node->parent, &len);
+  if (len && path[len-1] != '/') path[len++]='/';
+  len = (stpcpy(path+len, node->name) - path);
+  if (plen) *plen = len;
+
+  return path;
+}
+
+int dirtree_parentfd(struct dirtree *node)
+{
+  return node->parent ? node->parent->data : AT_FDCWD;
+}
+
+// Handle callback for a node in the tree. Returns saved node(s) or NULL.
+//
+// By default, allocates a tree of struct dirtree, not following symlinks
+// If callback==NULL, or callback always returns 0, allocate tree of struct
+// dirtree and return root of tree.  Otherwise call callback(node) on each
+// hit, free structures after use, and return NULL.
+//
+
+struct dirtree *dirtree_handle_callback(struct dirtree *new,
+          int (*callback)(struct dirtree *node))
+{
+  int flags, dir = S_ISDIR(new->st.st_mode);
+
+  if (!callback) callback = dirtree_notdotdot;
+
+  flags = callback(new);
+
+  if (dir) {
+    if (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)) {
+      new->data = openat(dirtree_parentfd(new), new->name, 0);
+      dirtree_recurse(new, callback, flags & DIRTREE_SYMFOLLOW);
+      if (flags & DIRTREE_COMEAGAIN) flags = callback(new);
+    }
+  }
+
+  // If this had children, it was callback's job to free them already.
+  if (!(flags & DIRTREE_SAVE)) {
+    free(new);
+    new = NULL;
+  }
+
+  return (flags & DIRTREE_ABORT)==DIRTREE_ABORT ? DIRTREE_ABORTVAL : new;
+}
+
+// Recursively read/process children of directory node (with dirfd in data),
+// filtering through callback().
+
+void dirtree_recurse(struct dirtree *node,
+          int (*callback)(struct dirtree *node), int symfollow)
+{
+  struct dirtree *new, **ddt = &(node->child);
+  struct dirent *entry;
+  DIR *dir;
+
+  if (!(dir = fdopendir(node->data))) {
+    char *path = dirtree_path(node, 0);
+    perror_msg("No %s", path);
+    free(path);
+    close(node->data);
+
+    return;
+  }
+
+  // according to the fddir() man page, the filehandle in the DIR * can still
+  // be externally used by things that don't lseek() it.
+
+  // The extra parentheses are to shut the stupid compiler up.
+  while ((entry = readdir(dir))) {
+    if (!(new = dirtree_add_node(node, entry->d_name, symfollow)))
+      continue;
+    new = dirtree_handle_callback(new, callback);
+    if (new == DIRTREE_ABORTVAL) break;
+    if (new) {
+      *ddt = new;
+      ddt = &((*ddt)->next);
+    }
+  }
+
+  // This closes filehandle as well, so note it
+  closedir(dir);
+  node->data = -1;
+}
+
+// Create dirtree from path, using callback to filter nodes.
+// If callback == NULL allocate a tree of struct dirtree nodes and return
+// pointer to root node.
+// symfollow is just for the top of tree, callback return code controls children
+
+struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
+{
+  struct dirtree *root = dirtree_add_node(0, path, 0);
+
+  return root ? dirtree_handle_callback(root, callback) : DIRTREE_ABORTVAL;
+}
diff --git a/lib/e2fs.c b/lib/e2fs.c
new file mode 100644 (file)
index 0000000..477623c
--- /dev/null
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4 : */\r
+/* e2fs.c - Linux second extended file system.\r
+ *\r
+ * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>\r
+ */\r
+\r
+#include "toys.h"\r
+\r
+/*\r
+ * Get file version on a Linux second extended file system.\r
+ */\r
+int get_e2fs_version(const int fd, unsigned long *version)\r
+{\r
+  return (ioctl(fd, EXT2_IOC_GETVERSION, (void*)version));\r
+}\r
+\r
+/*\r
+ * Set file version on a Linux second extended file system.\r
+ */\r
+int set_e2fs_version(const int fd, unsigned long version)\r
+{\r
+  return (ioctl(fd, EXT2_IOC_SETVERSION, (void*)&version));\r
+}\r
+\r
+/*\r
+ * Get the file flags on a Linux second extended file system.\r
+ */\r
+int get_e2fs_flag(const int fd, struct stat *sb, unsigned long *flagval)\r
+{\r
+  int status = -1;\r
+  if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {\r
+    errno = EOPNOTSUPP;\r
+    return -1;\r
+  }\r
+  status = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)flagval);\r
+  return status;\r
+}\r
+\r
+/*\r
+ * Set the file flags on a Linux second extended file system.\r
+ */\r
+int set_e2fs_flag(const int fd, struct stat *sb, unsigned long flagval)\r
+{\r
+  int status = -1;\r
+  if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {\r
+    errno = EOPNOTSUPP;\r
+    return -1;\r
+  }\r
+  status = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&flagval);\r
+  return status;\r
+}\r
diff --git a/lib/getmountlist.c b/lib/getmountlist.c
new file mode 100644 (file)
index 0000000..28a8406
--- /dev/null
@@ -0,0 +1,42 @@
+/* getmountlist.c - Get a linked list of mount points, with stat information.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+#include <mntent.h>
+
+// Get list of mounted filesystems, including stat and statvfs info.
+// Returns a reversed list, which is good for finding overmounts and such.
+
+struct mtab_list *xgetmountlist(void)
+{
+  struct mtab_list *mtlist, *mt;
+  struct mntent *me;
+  FILE *fp;
+
+  if (!(fp = setmntent("/proc/mounts", "r"))) perror_exit("bad /proc/mounts");
+
+  // The "test" part of the loop is done before the first time through and
+  // again after each "increment", so putting the actual load there avoids
+  // duplicating it. If the load was NULL, the loop stops.
+
+  for (mtlist = 0; (me = getmntent(fp)); mtlist = mt) {
+    mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
+      strlen(me->mnt_dir) + strlen(me->mnt_type) + 3);
+    mt->next = mtlist;
+
+    // Collect details about mounted filesystem (don't bother for /etc/fstab).
+    stat(me->mnt_dir, &(mt->stat));
+    statvfs(me->mnt_dir, &(mt->statvfs));
+
+    // Remember information from /proc/mounts
+    mt->dir = stpcpy(mt->type, me->mnt_type) + 1;
+    mt->device = stpcpy(mt->dir, me->mnt_dir) + 1;
+    strcpy(mt->device, me->mnt_fsname);
+  }
+  endmntent(fp);
+
+  return mtlist;
+}
diff --git a/lib/help.c b/lib/help.c
new file mode 100644 (file)
index 0000000..6a0c7b4
--- /dev/null
@@ -0,0 +1,33 @@
+// Function to display help text
+
+#include "toys.h"
+
+#if !CFG_TOYBOX_HELP
+void show_help(void) {;}
+#else
+#include "generated/help.h"
+
+#undef NEWTOY
+#undef OLDTOY
+#define NEWTOY(name,opt,flags) help_##name "\0"
+#define OLDTOY(name,oldname,opts,flags) "\xff" #oldname "\0"
+static char *help_data =
+#include "generated/newtoys.h"
+;
+
+void show_help(void)
+{
+  int i = toys.which-toy_list;
+  char *s;
+
+  for (;;) {
+    s = help_data;
+    while (i--) s += strlen(s) + 1;
+    // If it's an alias, restart search for real name
+    if (*s != 255) break;
+    i = toy_find(++s)-toy_list;
+  }
+
+  fprintf(toys.exithelp ? stderr : stdout, "%s", s);
+}
+#endif
diff --git a/lib/lib.c b/lib/lib.c
new file mode 100644 (file)
index 0000000..fa1572a
--- /dev/null
+++ b/lib/lib.c
@@ -0,0 +1,1549 @@
+/* lib.c - reusable stuff.
+ *
+ * Functions with the x prefix are wrappers for library functions.  They either
+ * succeed or kill the program with an error message, but never return failure.
+ * They usually have the same arguments and return value as the function they
+ * wrap.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+// Strcpy with size checking: exit if there's not enough space for the string.
+void xstrncpy(char *dest, char *src, size_t size)
+{
+  if (strlen(src)+1 > size) error_exit("xstrcpy");
+  strcpy(dest, src);
+}
+
+void xexit(void)
+{
+  if (toys.rebound) longjmp(*toys.rebound, 1);
+  else exit(toys.exitval);
+}
+void verror_msg(char *msg, int err, va_list va)
+{
+  char *s = ": %s";
+
+  fprintf(stderr, "%s: ", toys.which->name);
+  if (msg) vfprintf(stderr, msg, va);
+  else s+=2;
+  if (err) fprintf(stderr, s, strerror(err));
+  putc('\n', stderr);
+  if (!toys.exitval) toys.exitval++;
+}
+
+void error_msg(char *msg, ...)
+{
+  va_list va;
+
+  va_start(va, msg);
+  verror_msg(msg, 0, va);
+  va_end(va);
+}
+
+void perror_msg(char *msg, ...)
+{
+  va_list va;
+
+  va_start(va, msg);
+  verror_msg(msg, errno, va);
+  va_end(va);
+}
+
+// Die with an error message.
+void error_exit(char *msg, ...)
+{
+  va_list va;
+
+  if (CFG_TOYBOX_HELP && toys.exithelp) show_help();
+
+  va_start(va, msg);
+  verror_msg(msg, 0, va);
+  va_end(va);
+
+  xexit();
+}
+
+
+// Die with an error message and strerror(errno)
+void perror_exit(char *msg, ...)
+{
+  va_list va;
+
+  va_start(va, msg);
+  verror_msg(msg, errno, va);
+  va_end(va);
+
+  xexit();
+}
+
+// Die unless we can allocate memory.
+void *xmalloc(size_t size)
+{
+  void *ret = malloc(size);
+  if (!ret) error_exit("xmalloc");
+
+  return ret;
+}
+
+// Die unless we can allocate prezeroed memory.
+void *xzalloc(size_t size)
+{
+  void *ret = xmalloc(size);
+  memset(ret, 0, size);
+  return ret;
+}
+
+// Die unless we can change the size of an existing allocation, possibly
+// moving it.  (Notice different arguments from libc function.)
+void *xrealloc(void *ptr, size_t size)
+{
+  ptr = realloc(ptr, size);
+  if (!ptr) error_exit("xrealloc");
+
+  return ptr;
+}
+
+// Die unless we can allocate a copy of this many bytes of string.
+char *xstrndup(char *s, size_t n)
+{
+  char *ret = xmalloc(++n);
+  strncpy(ret, s, n);
+  ret[--n]=0;
+
+  return ret;
+}
+
+// Die unless we can allocate a copy of this string.
+char *xstrdup(char *s)
+{
+  return xstrndup(s, strlen(s));
+}
+
+// Die unless we can allocate enough space to sprintf() into.
+char *xmsprintf(char *format, ...)
+{
+  va_list va, va2;
+  int len;
+  char *ret;
+
+  va_start(va, format);
+  va_copy(va2, va);
+
+  // How long is it?
+  len = vsnprintf(0, 0, format, va);
+  len++;
+  va_end(va);
+
+  // Allocate and do the sprintf()
+  ret = xmalloc(len);
+  vsnprintf(ret, len, format, va2);
+  va_end(va2);
+
+  return ret;
+}
+
+void xprintf(char *format, ...)
+{
+  va_list va;
+  va_start(va, format);
+
+  vprintf(format, va);
+  if (ferror(stdout)) perror_exit("write");
+}
+
+void xputs(char *s)
+{
+  if (EOF == puts(s) || fflush(stdout)) perror_exit("write");
+}
+
+void xputc(char c)
+{
+  if (EOF == fputc(c, stdout) || fflush(stdout)) perror_exit("write");
+}
+
+void xflush(void)
+{
+  if (fflush(stdout)) perror_exit("write");;
+}
+
+// Die unless we can exec argv[] (or run builtin command).  Note that anything
+// with a path isn't a builtin, so /bin/sh won't match the builtin sh.
+void xexec(char **argv)
+{
+  toy_exec(argv);
+  execvp(argv[0], argv);
+
+  perror_exit("exec %s", argv[0]);
+}
+
+void xaccess(char *path, int flags)
+{
+  if (access(path, flags)) perror_exit("Can't access '%s'", path);
+}
+
+// Die unless we can delete a file.  (File must exist to be deleted.)
+void xunlink(char *path)
+{
+  if (unlink(path)) perror_exit("unlink '%s'", path);
+}
+
+// Die unless we can open/create a file, returning file descriptor.
+int xcreate(char *path, int flags, int mode)
+{
+  int fd = open(path, flags, mode);
+  if (fd == -1) perror_exit("%s", path);
+  return fd;
+}
+
+// Die unless we can open a file, returning file descriptor.
+int xopen(char *path, int flags)
+{
+  return xcreate(path, flags, 0);
+}
+
+void xclose(int fd)
+{
+  if (close(fd)) perror_exit("xclose");
+}
+
+int xdup(int fd)
+{
+  if (fd != -1) {
+    fd = dup(fd);
+    if (fd == -1) perror_exit("xdup");
+  }
+  return fd;
+}
+
+// Die unless we can open/create a file, returning FILE *.
+FILE *xfopen(char *path, char *mode)
+{
+  FILE *f = fopen(path, mode);
+  if (!f) perror_exit("No file %s", path);
+  return f;
+}
+
+// Keep reading until full or EOF
+ssize_t readall(int fd, void *buf, size_t len)
+{
+  size_t count = 0;
+
+  while (count<len) {
+    int i = read(fd, buf+count, len-count);
+    if (!i) break;
+    if (i<0) return i;
+    count += i;
+  }
+
+  return count;
+}
+
+// Keep writing until done or EOF
+ssize_t writeall(int fd, void *buf, size_t len)
+{
+  size_t count = 0;
+  while (count<len) {
+    int i = write(fd, buf+count, len-count);
+    if (i<1) return i;
+    count += i;
+  }
+
+  return count;
+}
+
+// Die if there's an error other than EOF.
+size_t xread(int fd, void *buf, size_t len)
+{
+  ssize_t ret = read(fd, buf, len);
+  if (ret < 0) perror_exit("xread");
+
+  return ret;
+}
+
+void xreadall(int fd, void *buf, size_t len)
+{
+  if (len != readall(fd, buf, len)) perror_exit("xreadall");
+}
+
+// There's no xwriteall(), just xwrite().  When we read, there may or may not
+// be more data waiting.  When we write, there is data and it had better go
+// somewhere.
+
+void xwrite(int fd, void *buf, size_t len)
+{
+  if (len != writeall(fd, buf, len)) perror_exit("xwrite");
+}
+
+// Die if lseek fails, probably due to being called on a pipe.
+
+off_t xlseek(int fd, off_t offset, int whence)
+{
+  offset = lseek(fd, offset, whence);
+  if (offset<0) perror_exit("lseek");
+
+  return offset;
+}
+
+off_t lskip(int fd, off_t offset)
+{
+  off_t and = lseek(fd, offset, SEEK_CUR);
+
+  if (and != -1 && offset >= lseek(fd, offset, SEEK_END)
+    && offset+and == lseek(fd, offset+and, SEEK_SET)) return 0;
+  else {
+    char buf[4096];
+    while (offset>0) {
+      int try = offset>sizeof(buf) ? sizeof(buf) : offset, or;
+
+      or = readall(fd, buf, try);
+      if (or < 0) perror_msg("lskip to %lld", (long long)offset);
+      else offset -= try;
+      if (or < try) break;
+    }
+
+    return offset;
+  }
+}
+
+char *xgetcwd(void)
+{
+  char *buf = getcwd(NULL, 0);
+  if (!buf) perror_exit("xgetcwd");
+
+  return buf;
+}
+
+void xstat(char *path, struct stat *st)
+{
+  if(stat(path, st)) perror_exit("Can't stat %s", path);
+}
+
+// Split a path into linked list of components, tracking head and tail of list.
+// Filters out // entries with no contents.
+struct string_list **splitpath(char *path, struct string_list **list)
+{
+  char *new = path;
+
+  *list = 0;
+  do {
+    int len;
+
+    if (*path && *path != '/') continue;
+    len = path-new;
+    if (len > 0) {
+      *list = xmalloc(sizeof(struct string_list) + len + 1);
+      (*list)->next = 0;
+      strncpy((*list)->str, new, len);
+      (*list)->str[len] = 0;
+      list = &(*list)->next;
+    }
+    new = path+1;
+  } while (*path++);
+
+  return list;
+}
+
+// Cannonicalize path, even to file with one or more missing components at end.
+// if exact, require last path component to exist
+char *xabspath(char *path, int exact) 
+{
+  struct string_list *todo, *done = 0;
+  int try = 9999, dirfd = open("/", 0);;
+  char buf[4096], *ret;
+
+  // If this isn't an absolute path, start with cwd.
+  if (*path != '/') {
+    char *temp = xgetcwd();
+
+    splitpath(path, splitpath(temp, &todo));
+    free(temp);
+  } else splitpath(path, &todo);
+
+  // Iterate through path components
+  while (todo) {
+    struct string_list *new = llist_pop(&todo), **tail;
+    ssize_t len;
+
+    if (!try--) {
+      errno = ELOOP;
+      goto error;
+    }
+
+    // Removable path componenents.
+    if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) {
+      int x = new->str[1];
+
+      free(new);
+      if (x) {
+        if (done) free(llist_pop(&done));
+        len = 0;
+      } else continue;
+
+    // Is this a symlink?
+    } else len=readlinkat(dirfd, new->str, buf, 4096);
+
+    if (len>4095) goto error;
+    if (len<1) {
+      int fd;
+      char *s = "..";
+
+      // For .. just move dirfd
+      if (len) {
+        // Not a symlink: add to linked list, move dirfd, fail if error
+        if ((exact || todo) && errno != EINVAL) goto error;
+        new->next = done;
+        done = new;
+        if (errno == EINVAL && !todo) break;
+        s = new->str;
+      }
+      fd = openat(dirfd, s, 0);
+      if (fd == -1 && (exact || todo || errno != ENOENT)) goto error;
+      close(dirfd);
+      dirfd = fd;
+      continue;
+    }
+
+    // If this symlink is to an absolute path, discard existing resolved path
+    buf[len] = 0;
+    if (*buf == '/') {
+      llist_traverse(done, free);
+      done=0;
+      close(dirfd);
+      dirfd = open("/", 0);
+    }
+    free(new);
+
+    // prepend components of new path. Note symlink to "/" will leave new NULL
+    tail = splitpath(buf, &new);
+
+    // symlink to "/" will return null and leave tail alone
+    if (new) {
+      *tail = todo;
+      todo = new;
+    }
+  }
+  close(dirfd);
+
+  // At this point done has the path, in reverse order. Reverse list while
+  // calculating buffer length.
+
+  try = 2;
+  while (done) {
+    struct string_list *temp = llist_pop(&done);;
+
+    if (todo) try++;
+    try += strlen(temp->str);
+    temp->next = todo;
+    todo = temp;
+  }
+
+  // Assemble return buffer
+
+  ret = xmalloc(try);
+  *ret = '/';
+  ret [try = 1] = 0;
+  while (todo) {
+    if (try>1) ret[try++] = '/';
+    try = stpcpy(ret+try, todo->str) - ret;
+    free(llist_pop(&todo));
+  }
+
+  return ret;
+
+error:
+  close(dirfd);
+  llist_traverse(todo, free);
+  llist_traverse(done, free);
+
+  return NULL;
+}
+
+// Resolve all symlinks, returning malloc() memory.
+char *xrealpath(char *path)
+{
+  char *new = realpath(path, NULL);
+  if (!new) perror_exit("realpath '%s'", path);
+  return new;
+}
+
+void xchdir(char *path)
+{
+  if (chdir(path)) error_exit("chdir '%s'", path);
+}
+
+// Ensure entire path exists.
+// If mode != -1 set permissions on newly created dirs.
+// Requires that path string be writable (for temporary null terminators).
+void xmkpath(char *path, int mode)
+{
+  char *p, old;
+  mode_t mask;
+  int rc;
+  struct stat st;
+
+  for (p = path; ; p++) {
+    if (!*p || *p == '/') {
+      old = *p;
+      *p = rc = 0;
+      if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
+        if (mode != -1) {
+          mask=umask(0);
+          rc = mkdir(path, mode);
+          umask(mask);
+        } else rc = mkdir(path, 0777);
+      }
+      *p = old;
+      if(rc) perror_exit("mkpath '%s'", path);
+    }
+    if (!*p) break;
+  }
+}
+
+// setuid() can fail (for example, too many processes belonging to that user),
+// which opens a security hole if the process continues as the original user.
+
+void xsetuid(uid_t uid)
+{
+  if (setuid(uid)) perror_exit("xsetuid");
+}
+
+
+// Find all file in a colon-separated path with access type "type" (generally
+// X_OK or R_OK).  Returns a list of absolute paths to each file found, in
+// order.
+
+struct string_list *find_in_path(char *path, char *filename)
+{
+  struct string_list *rlist = NULL, **prlist=&rlist;
+  char *cwd = xgetcwd();
+
+  for (;;) {
+    char *next = path ? strchr(path, ':') : NULL;
+    int len = next ? next-path : strlen(path);
+    struct string_list *rnext;
+    struct stat st;
+
+    rnext = xmalloc(sizeof(void *) + strlen(filename)
+      + (len ? len : strlen(cwd)) + 2);
+    if (!len) sprintf(rnext->str, "%s/%s", cwd, filename);
+    else {
+      char *res = rnext->str;
+      strncpy(res, path, len);
+      res += len;
+      *(res++) = '/';
+      strcpy(res, filename);
+    }
+
+    // Confirm it's not a directory.
+    if (!stat(rnext->str, &st) && S_ISREG(st.st_mode)) {
+      *prlist = rnext;
+      rnext->next = NULL;
+      prlist = &(rnext->next);
+    } else free(rnext);
+
+    if (!next) break;
+    path += len;
+    path++;
+  }
+  free(cwd);
+
+  return rlist;
+}
+
+// Convert unsigned int to ascii, writing into supplied buffer.  A truncated
+// result contains the first few digits of the result ala strncpy, and is
+// always null terminated (unless buflen is 0).
+void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+{
+  int i, out = 0;
+
+  if (buflen) {
+    for (i=1000000000; i; i/=10) {
+      int res = n/i;
+
+      if ((res || out || i == 1) && --buflen>0) {
+        out++;
+        n -= res*i;
+        *buf++ = '0' + res;
+      }
+    }
+    *buf = 0;
+  }
+}
+
+// Convert signed integer to ascii, using utoa_to_buf()
+void itoa_to_buf(int n, char *buf, unsigned buflen)
+{
+  if (buflen && n<0) {
+    n = -n;
+    *buf++ = '-';
+    buflen--;
+  }
+  utoa_to_buf((unsigned)n, buf, buflen);
+}
+
+// This static buffer is used by both utoa() and itoa(), calling either one a
+// second time will overwrite the previous results.
+//
+// The longest 32 bit integer is -2 billion plus a null terminator: 12 bytes.
+// Note that int is always 32 bits on any remotely unix-like system, see
+// http://www.unix.org/whitepapers/64bit.html for details.
+
+static char itoa_buf[12];
+
+// Convert unsigned integer to ascii, returning a static buffer.
+char *utoa(unsigned n)
+{
+  utoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
+
+  return itoa_buf;
+}
+
+char *itoa(int n)
+{
+  itoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
+
+  return itoa_buf;
+}
+
+// atol() with the kilo/mega/giga/tera/peta/exa extensions.
+// (zetta and yotta don't fit in 64 bits.)
+// FIXME: removed bkmgtpe parsing --madhur
+long atolx(char *numstr)
+{
+  char *c;// *suffixes="bkmgtpe", *end;
+  long val = strtol(numstr, &c, 0);
+
+//  if (*c) {
+//    if (c != numstr && (end = strchr(suffixes, tolower(*c)))) {
+//      int shift = end-suffixes;
+//      if (shift--) val *= 1024L<<(shift*10);
+//    } else {
+//      while (isspace(*c)) c++;
+//      if (*c) error_exit("not integer: %s", numstr);
+//    }
+//  }
+
+  if(*c != '\0') error_exit("not integer: %s", numstr);
+
+
+  return val;
+}
+
+int numlen(long l)
+{
+  int len = 0;
+  while (l) {
+     l /= 10;
+     len++;
+  }
+  return len;
+}
+
+int stridx(char *haystack, char needle)
+{
+  char *off;
+
+  if (!needle) return -1;
+  off = strchr(haystack, needle);
+  if (!off) return -1;
+
+  return off-haystack;
+}
+
+// Return how long the file at fd is, if there's any way to determine it.
+off_t fdlength(int fd)
+{
+  off_t bottom = 0, top = 0, pos, old;
+  int size;
+
+  // If the ioctl works for this, return it.
+
+  if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
+
+  // If not, do a binary search for the last location we can read.  (Some
+  // block devices don't do BLKGETSIZE right.)  This should probably have
+  // a CONFIG option...
+
+  old = lseek(fd, 0, SEEK_CUR);
+  do {
+    char temp;
+
+    pos = bottom + (top - bottom) / 2;
+
+    // If we can read from the current location, it's bigger.
+
+    if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) {
+      if (bottom == top) bottom = top = (top+1) * 2;
+      else bottom = pos;
+
+      // If we can't, it's smaller.
+
+    } else {
+      if (bottom == top) {
+        if (!top) return 0;
+        bottom = top/2;
+      } else top = pos;
+    }
+  } while (bottom + 1 != top);
+
+  lseek(fd, old, SEEK_SET);
+
+  return pos + 1;
+}
+
+// This can return null (meaning file not found).  It just won't return null
+// for memory allocation reasons.
+char *xreadlink(char *name)
+{
+  int len, size = 0;
+  char *buf = 0;
+
+  // Grow by 64 byte chunks until it's big enough.
+  for(;;) {
+    size +=64;
+    buf = xrealloc(buf, size);
+    len = readlink(name, buf, size);
+
+    if (len<0) {
+      free(buf);
+      return 0;
+    }
+    if (len<size) {
+      buf[len]=0;
+      return buf;
+    }
+  }
+}
+
+
+// This might be of use or might not.  Unknown yet...
+
+// Read contents of file as a single freshly allocated nul-terminated string.
+char *readfile(char *name)
+{
+  off_t len;
+  int fd;
+  char *buf;
+
+  fd = open(name, O_RDONLY);
+  if (fd == -1) return 0;
+  len = fdlength(fd);
+  buf = xmalloc(len+1);
+  buf[readall(fd, buf, len)] = 0;
+  
+  xclose(fd);
+  return buf;
+}
+
+char *xreadfile(char *name)
+{
+  char *buf = readfile(name);
+  if (!buf) perror_exit("xreadfile %s", name);
+  return buf;
+}
+
+
+
+
+// Sleep for this many thousandths of a second
+void msleep(long miliseconds)
+{
+  struct timespec ts;
+
+  ts.tv_sec = miliseconds/1000;
+  ts.tv_nsec = (miliseconds%1000)*1000000;
+  nanosleep(&ts, &ts);
+}
+
+int xioctl(int fd, int request, void *data)
+{
+  int rc;
+
+  errno = 0;
+  rc = ioctl(fd, request, data);
+  if (rc == -1 && errno) perror_exit("ioctl %x", request);
+
+  return rc;
+}
+
+int64_t peek(void *ptr, int size)
+{
+  if (size & 8) {
+    int64_t *p = (int64_t *)ptr;
+    return *p;
+  } else if (size & 4) {
+    int *p = (int *)ptr;
+    return *p;
+  } else if (size & 2) {
+    short *p = (short *)ptr;
+    return *p;
+  } else {
+    char *p = (char *)ptr;
+    return *p;
+  }
+}
+
+void poke(void *ptr, uint64_t val, int size)
+{
+  if (size & 8) {
+    uint64_t *p = (uint64_t *)ptr;
+    *p = val;
+  } else if (size & 4) {
+    int *p = (int *)ptr;
+    *p = val;
+  } else if (size & 2) {
+    short *p = (short *)ptr;
+    *p = val;
+  } else {
+    char *p = (char *)ptr;
+    *p = val;
+  }
+}
+
+// Open a /var/run/NAME.pid file, dying if we can't write it or if it currently
+// exists and is this executable.
+void xpidfile(char *name)
+{
+  char pidfile[256], spid[32];
+  int i, fd;
+  pid_t pid;
+
+  sprintf(pidfile, "/var/run/%s.pid", name);
+  // Try three times to open the sucker.
+  for (i=0; i<3; i++) {
+    fd = open(pidfile, O_CREAT|O_EXCL, 0644);
+    if (fd != -1) break;
+
+    // If it already existed, read it.  Loop for race condition.
+    fd = open(pidfile, O_RDONLY);
+    if (fd == -1) continue;
+
+    // Is the old program still there?
+    spid[xread(fd, spid, sizeof(spid)-1)] = 0;
+    close(fd);
+    pid = atoi(spid);
+    if (pid < 1 || kill(pid, 0) == ESRCH) unlink(pidfile);
+
+    // An else with more sanity checking might be nice here.
+  }
+
+  if (i == 3) error_exit("xpidfile %s", name);
+
+  xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
+  close(fd);
+}
+
+// Iterate through an array of files, opening each one and calling a function
+// on that filehandle and name.  The special filename "-" means stdin if
+// flags is O_RDONLY, stdout otherwise.  An empty argument list calls
+// function() on just stdin/stdout.
+//
+// Note: read only filehandles are automatically closed when function()
+// returns, but writeable filehandles must be close by function()
+void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+  void (*function)(int fd, char *name))
+{
+  int fd;
+
+  // If no arguments, read from stdin.
+  if (!*argv) function(flags ? 1 : 0, "-");
+  else do {
+    // Filename "-" means read from stdin.
+    // Inability to open a file prints a warning, but doesn't exit.
+
+    if (!strcmp(*argv,"-")) fd=0;
+    else if (0>(fd = open(*argv, flags, permissions)) && !failok) {
+      perror_msg("%s", *argv);
+      toys.exitval = 1;
+      continue;
+    }
+    function(fd, *argv);
+    if (flags == O_RDONLY) close(fd);
+  } while (*++argv);
+}
+
+// Call loopfiles_rw with O_RDONLY and !failok (common case).
+void loopfiles(char **argv, void (*function)(int fd, char *name))
+{
+  loopfiles_rw(argv, O_RDONLY, 0, 0, function);
+}
+
+// Slow, but small.
+
+char *get_rawline(int fd, long *plen, char end)
+{
+  char c, *buf = NULL;
+  long len = 0;
+
+  for (;;) {
+    if (1>read(fd, &c, 1)) break;
+    if (!(len & 63)) buf=xrealloc(buf, len+65);
+    if ((buf[len++]=c) == end) break;
+  }
+  if (buf) buf[len]=0;
+  if (plen) *plen = len;
+
+  return buf;
+}
+
+char *get_line(int fd)
+{
+  long len;
+  char *buf = get_rawline(fd, &len, '\n');
+
+  if (buf && buf[--len]=='\n') buf[len]=0;
+
+  return buf;
+}
+
+// Copy the rest of in to out and close both files.
+
+void xsendfile(int in, int out)
+{
+  long len;
+  char buf[4096];
+
+  if (in<0) return;
+  for (;;) {
+    len = xread(in, buf, 4096);
+    if (len<1) break;
+    xwrite(out, buf, len);
+  }
+}
+
+int wfchmodat(int fd, char *name, mode_t mode)
+{
+  int rc = fchmodat(fd, name, mode, 0);
+
+  if (rc) {
+    perror_msg("chmod '%s' to %04o", name, mode);
+    toys.exitval=1;
+  }
+  return rc;
+}
+
+static char *tempfile2zap;
+static void tempfile_handler(int i)
+{
+  if (1 < (long)tempfile2zap) unlink(tempfile2zap);
+  _exit(1);
+}
+
+// Open a temporary file to copy an existing file into.
+int copy_tempfile(int fdin, char *name, char **tempname)
+{
+  struct stat statbuf;
+  int fd;
+
+  *tempname = xstrndup(name, strlen(name)+6);
+  strcat(*tempname,"XXXXXX");
+  if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file");
+  if (!tempfile2zap) sigatexit(tempfile_handler);
+  tempfile2zap = *tempname;
+
+  // Set permissions of output file
+
+  fstat(fdin, &statbuf);
+  fchmod(fd, statbuf.st_mode);
+
+  return fd;
+}
+
+// Abort the copy and delete the temporary file.
+void delete_tempfile(int fdin, int fdout, char **tempname)
+{
+  close(fdin);
+  close(fdout);
+  unlink(*tempname);
+  tempfile2zap = (char *)1;
+  free(*tempname);
+  *tempname = NULL;
+}
+
+// Copy the rest of the data and replace the original with the copy.
+void replace_tempfile(int fdin, int fdout, char **tempname)
+{
+  char *temp = xstrdup(*tempname);
+
+  temp[strlen(temp)-6]=0;
+  if (fdin != -1) {
+    xsendfile(fdin, fdout);
+    xclose(fdin);
+  }
+  xclose(fdout);
+  rename(*tempname, temp);
+  tempfile2zap = (char *)1;
+  free(*tempname);
+  free(temp);
+  *tempname = NULL;
+}
+
+// Create a 256 entry CRC32 lookup table.
+
+void crc_init(unsigned int *crc_table, int little_endian)
+{
+  unsigned int i;
+
+  // Init the CRC32 table (big endian)
+  for (i=0; i<256; i++) {
+    unsigned int j, c = little_endian ? i : i<<24;
+    for (j=8; j; j--)
+      if (little_endian) c = (c&1) ? (c>>1)^0xEDB88320 : c>>1;
+      else c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
+    crc_table[i] = c;
+  }
+}
+
+// Quick and dirty query size of terminal, doesn't do ANSI probe fallback.
+// set *x=0 and *y=0 before calling to detect failure to set either, or
+// x=80 y=25 to provide defaults
+
+void terminal_size(unsigned *x, unsigned *y)
+{
+  struct winsize ws;
+  int i;
+
+  //memset(&ws, 0, sizeof(ws));
+  for (i=0; i<3; i++) {
+    if (ioctl(i, TIOCGWINSZ, &ws)) continue;
+    if (x) *x = ws.ws_col;
+    if (y) *y = ws.ws_row;
+  }
+  if (x) {
+    char *s = getenv("COLUMNS");
+
+    i = s ? atoi(s) : 0;
+    if (i>0) *x = i;
+  }
+  if (y) {
+    char *s = getenv("ROWS");
+
+    i = s ? atoi(s) : 0;
+    if (i>0) *y = i;
+  }
+}
+
+int yesno(char *prompt, int def)
+{  
+  char buf;
+  int done = 0;
+
+  fprintf(stderr, "%s (%c/%c):", prompt, def ? 'Y' : 'y', def ? 'n' : 'N');
+  fflush(stderr);
+  while (fread(&buf, 1, 1, stdin)) {                                                                                                                                            
+    int new;
+
+    if (isblank(buf)) continue;
+    if (isspace(buf)) break;
+    if(!done) {
+      if (-1 != (new = stridx("ny", tolower(buf)))) def = new;
+      done = 1;
+    }
+  }
+
+  return def;
+}
+
+
+// Execute a callback for each PID that matches a process name from a list.
+void for_each_pid_with_name_in(char **names, int (*callback)(pid_t pid, char *name))
+{
+  DIR *dp;
+  struct dirent *entry;
+  char cmd[sizeof(toybuf)], path[64];
+  char **curname;
+
+  if (!(dp = opendir("/proc"))) perror_exit("opendir");
+
+  while ((entry = readdir(dp))) {
+    int fd, n;
+
+    if (!isdigit(*entry->d_name)) continue;
+
+    if (sizeof(path) <= snprintf(path, sizeof(path), "/proc/%s/cmdline",
+      entry->d_name)) continue;
+
+    if (-1 == (fd=open(path, O_RDONLY))) continue;
+    n = read(fd, cmd, sizeof(cmd));
+    close(fd);
+    if (n<1) continue;
+
+    for (curname = names; *curname; curname++)
+      if (!strcmp(basename(cmd), *curname)) 
+          if (!callback(atol(entry->d_name), *curname)) goto done;
+  }
+done:
+  closedir(dp);
+}
+
+struct signame {
+  int num;
+  char *name;
+};
+
+/*  Signals required by POSIX 2008:
+*   http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
+*   Madhur Verma 17 Apr 2013 <mad.flexi@gmail.com>
+*     modified to print numbers too in order.
+*/
+#define SIGNIFY(x) {SIG##x, #x}
+
+static struct signame signames[] = {
+    SIGNIFY(HUP),   SIGNIFY(INT),   SIGNIFY(QUIT),  SIGNIFY(ILL),
+    SIGNIFY(TRAP),  SIGNIFY(ABRT),  SIGNIFY(BUS),   SIGNIFY(FPE),
+    SIGNIFY(KILL),  SIGNIFY(USR1),  SIGNIFY(SEGV),  SIGNIFY(USR2),
+    SIGNIFY(PIPE),  SIGNIFY(ALRM),  SIGNIFY(TERM),  SIGNIFY(STKFLT),
+    SIGNIFY(CHLD),  SIGNIFY(CONT),  SIGNIFY(STOP),  SIGNIFY(TSTP),
+    SIGNIFY(TTIN),  SIGNIFY(TTOU),  SIGNIFY(URG),   SIGNIFY(XCPU),
+    SIGNIFY(XFSZ),  SIGNIFY(VTALRM),SIGNIFY(PROF),  SIGNIFY(WINCH),
+    SIGNIFY(POLL),  SIGNIFY(PWR),   SIGNIFY(SYS),
+    { __SIGRTMIN, "RTMIN" },
+    { __SIGRTMAX, "RTMAX" }
+};
+
+// Install the same handler on every signal that defaults to killing the process
+void sigatexit(void *handler)
+{
+  int i;
+  for (i=0; signames[i].num != SIGCHLD; i++) signal(signames[i].num, handler);
+}
+// Convert name to signal number.  If name == NULL print names.
+int sig_to_num(char *pidstr)
+{
+  int i;
+
+  if (pidstr) {
+    char *s;
+    i = strtol(pidstr, &s, 10);
+    if (!*s) return i;
+    if (!strncasecmp(pidstr, "sig", 3)) pidstr+=3;
+  }
+  for (i = 0; i < (sizeof(signames)/sizeof(struct signame)); i++)
+    if (!pidstr) xprintf("%2u) %s\n", signames[i].num, signames[i].name);
+    else if (!strncasecmp(pidstr, "RTMIN", 5)) {
+      if (!pidstr[5]) return __SIGRTMIN;
+      if (pidstr[5] == '+') {
+        i = strtoul(pidstr + 6, NULL, 10);
+        if (!errno && i <= __SIGRTMAX - __SIGRTMIN)
+          return __SIGRTMIN + i;
+      }
+    } else if (!strncasecmp(pidstr, "RTMAX", 5)) {
+      if (!pidstr[5]) return __SIGRTMAX;
+      if (pidstr[5] == '-') {
+        i = strtoul(pidstr + 6, NULL, 10);
+        if (!errno && i <= __SIGRTMAX - __SIGRTMIN)
+          return __SIGRTMAX - i;
+      }
+    } else if (!strcasecmp(pidstr, signames[i].name)) return signames[i].num;
+  return -1;
+}
+
+char *num_to_sig(int sig)
+{
+  int i;
+
+  for (i=0; i<sizeof(signames)/sizeof(struct signame); i++)
+    if (signames[i].num == sig) return signames[i].name;
+  return NULL;
+}
+
+// premute mode bits based on posix mode strings.
+mode_t string_to_mode(char *modestr, mode_t mode)
+{
+  char *whos = "ogua", *hows = "=+-", *whats = "xwrstX", *whys = "ogu";
+  char *s, *str = modestr;
+
+  // Handle octal mode
+  if (isdigit(*str)) {
+    mode = strtol(str, &s, 8);
+    if (*s || (mode & ~(07777))) goto barf;
+
+    return mode;
+  }
+
+  // Gaze into the bin of permission...
+  for (;;) {
+    int i, j, dowho, dohow, dowhat, amask;
+
+    dowho = dohow = dowhat = amask = 0;
+
+    // Find the who, how, and what stanzas, in that order
+    while (*str && (s = strchr(whos, *str))) {
+      dowho |= 1<<(s-whos);
+      str++;
+    }
+    // If who isn't specified, like "a" but honoring umask.
+    if (!dowho) {
+      dowho = 8;
+      umask(amask=umask(0));
+    }
+    if (!*str || !(s = strchr(hows, *str))) goto barf;
+    dohow = *(str++);
+
+    if (!dohow) goto barf;
+    while (*str && (s = strchr(whats, *str))) {
+      dowhat |= 1<<(s-whats);
+      str++;
+    }
+
+    // Convert X to x for directory or if already executable somewhere
+    if ((dowhat&32) &&  (S_ISDIR(mode) || (mode&0111))) dowhat |= 1;
+
+    // Copy mode from another category?
+    if (!dowhat && *str && (s = strchr(whys, *str))) {
+      dowhat = (mode>>(3*(s-whys)))&7;
+      str++;
+    }
+
+    // Are we ready to do a thing yet?
+    if (*str && *(str++) != ',') goto barf;
+
+    // Ok, apply the bits to the mode.
+    for (i=0; i<4; i++) {
+      for (j=0; j<3; j++) {
+        mode_t bit = 0;
+        int where = 1<<((3*i)+j);
+
+        if (amask & where) continue;
+
+        // Figure out new value at this location
+        if (i == 3) {
+          // suid/sticky bit.
+          if (j) {
+            if ((dowhat & 8) && (dowho&(8|(1<<i)))) bit++;
+          } else if (dowhat & 16) bit++;
+        } else {
+          if (!(dowho&(8|(1<<i)))) continue;
+          if (dowhat&(1<<j)) bit++;
+        }
+
+        // When selection active, modify bit
+
+        if (dohow == '=' || (bit && dohow == '-')) mode &= ~where;
+        if (bit && dohow != '-') mode |= where;
+      }
+    }
+
+    if (!*str) break;
+  }
+  return mode;
+barf:
+  error_exit("bad mode '%s'", modestr);
+}
+
+// Format access mode into a drwxrwxrwx string
+void mode_to_string(mode_t mode, char *buf)
+{
+  char c, d;
+  int i, bit;
+
+  buf[10]=0;
+  for (i=0; i<9; i++) {
+    bit = mode & (1<<i);
+    c = i%3;
+    if (!c && (mode & (1<<((d=i/3)+9)))) {
+      c = "tss"[d];
+      if (!bit) c &= ~0x20;
+    } else c = bit ? "xwr"[c] : '-';
+    buf[9-i] = c;
+  }
+
+  if (S_ISDIR(mode)) c = 'd';
+  else if (S_ISBLK(mode)) c = 'b';
+  else if (S_ISCHR(mode)) c = 'c';
+  else if (S_ISLNK(mode)) c = 'l';
+  else if (S_ISFIFO(mode)) c = 'p';
+  else if (S_ISSOCK(mode)) c = 's';
+  else c = '-';
+  *buf = c;
+}
+
+
+int m_parse(char *optstr, mode_t *current_mode)
+{
+#define USER    0700
+#define GRP     070
+#define OTH     07
+#define ALL     (USER | GRP | OTH | S_ISUID | S_ISGID | S_ISVTX)
+  static const mode_t who_mask[] = {
+    S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
+    S_ISUID | S_IRWXU,           /* u */
+    S_ISGID | S_IRWXG,           /* g */
+    S_IRWXO                      /* o */
+  }; 
+
+  static const mode_t perm_mask[] = {
+    S_IRUSR | S_IRGRP | S_IROTH,
+    S_IWUSR | S_IWGRP | S_IWOTH,
+    S_IXUSR | S_IXGRP | S_IXOTH,
+    S_IXUSR | S_IXGRP | S_IXOTH, //X option
+    S_ISUID | S_ISGID,
+    S_ISVTX
+  };
+  static const char who_chars[] = "augo";
+  static const char perms[] = "rwxXst";
+
+  const char *p;
+  char *e;
+  char op;
+  int i;
+  unsigned long tmp;
+  unsigned int who;
+  mode_t new;
+  mode_t permissions;
+
+  if((unsigned char)(*optstr -'0') < 8)
+  {
+    tmp = strtoul(optstr, &e, 8);
+    if(*e || tmp > 07777UL)
+      return 0;
+    *current_mode = tmp;
+    return 1;
+  }
+
+  new = *current_mode;
+  permissions = 0;
+  while (*optstr) 
+  {
+    if (*optstr == ',') 
+    { 
+      ++optstr;
+      continue;
+    }
+
+    who = 0;
+    p = who_chars;
+WHO_LIST:
+  do
+  {
+    if(*p == *optstr)
+    {
+      who |= who_mask[p - who_chars];
+      if(!*++optstr)
+        return 0;
+      else
+      {
+        p = who_chars;
+        goto WHO_LIST;
+      }
+
+    }
+  }while(*++p);
+
+  do
+  {
+    if((*optstr != '+') && (*optstr != '-'))
+    {
+      if(*optstr != '=')
+        return 0; //this is invalid mode
+      permissions = ~who;
+      new &= permissions;
+    }
+
+    op = *optstr++;
+
+    /*copy perms from others, other than 'a' option */
+    p = who_chars + 1;
+    do          
+    {           
+      if(*p == *optstr) 
+      {         
+        permissions = who_mask[p - who_chars]
+                & new & (S_IRWXU | S_IRWXG | S_IRWXO);         
+        for(i = 0; i < 3; i++)
+          if(permissions & perm_mask[i])
+            permissions |= perm_mask[i];
+        ++optstr;
+        goto PERFORM_ACTION;
+      }         
+    }while(*++p);
+
+    p = perms;
+    permissions = 0;
+PERMISSIONS:
+    do
+    {
+      if(*p == *optstr)
+      {
+        permissions |= perm_mask[p - perms];
+        if(!*++optstr)
+          break;
+        else
+        {
+          p = perms;
+          goto PERMISSIONS;
+        }
+      }
+    }while(*p++);
+
+PERFORM_ACTION:
+
+    if(permissions)
+    {
+      tmp = who;
+      if(!who)
+      {
+        mode_t u_mask = umask(0);
+        umask(u_mask);
+        tmp = ~u_mask;
+      }
+      permissions &= tmp;
+
+      if(op == '-')
+        new &= ~permissions;
+      else
+        new |= permissions;
+    }
+    }while(*optstr && (*optstr != ','));
+  }
+
+  *current_mode = new;
+  return 1;
+}
+
+int go_daemon(int flags) {
+       int fd;
+
+       if (flags & CHDIR_ROOT)
+               chdir("/");
+
+       if (flags & DEVNULL_STDIO) {
+               close(0);
+               close(1);
+               close(2);
+       }
+
+       fd = open("/dev/null", O_RDWR);
+       if (fd < 0) {
+               fd = open("/", O_RDONLY, 0666);
+       }
+
+       dup2(fd, 0);
+       dup2(fd, 1);
+       dup2(fd, 2);
+
+       if (!(flags & ONLY_SIMULATE)) {
+               pid_t pid = fork();
+
+               if(pid < 0){
+                       printf("DAEMON: fail to fork");
+                       return -1;
+               }
+               if (pid)
+                       exit(EXIT_SUCCESS);
+
+               setsid();
+               dup2(fd, 0);
+               dup2(fd, 1);
+               dup2(fd, 2);
+       }
+       while (fd > 2) {
+               close(fd--);
+               if (!(flags & CLOSE_FDS))
+                       return 0;
+       }
+
+       return 0;
+}
+
+char* make_human_readable(unsigned long long size, unsigned long unit)
+{
+  unsigned int frac = 0;
+  if(unit) {
+    size = (size/(unit)) + (size%(unit)?1:0);
+    return xmsprintf("%llu", size);
+  }
+  else {
+    static char units[] = {'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
+    int index = 0;
+    while(size >= 1024) {
+      frac = size%1024;
+      size /= 1024;
+      index++;
+    }
+    frac = (frac/102) + ((frac%102)?1:0);
+    if(frac >= 10) {
+      size += 1;
+      frac = 0;
+    }
+    if(frac) return xmsprintf("%llu.%u%c", size, frac, units[index]);
+    else return xmsprintf("%llu%c", size, units[index]);
+  }
+  return NULL; //not reached
+}
+
+// strtoul with exit on error
+unsigned long xstrtoul(const char *nptr, char **endptr, int base)
+{
+    unsigned long l;
+    errno = 0;
+    l = strtoul(nptr, endptr, base);
+    if (errno)
+        perror_exit("xstrtoul");
+    return l;
+}
+
+/*
+ * used to get the interger value.
+ */
+unsigned long get_int_value(const char *numstr, unsigned lowrange, unsigned highrange)
+{
+  unsigned long rvalue = 0;
+  char *ptr;
+  if(*numstr == '-' || *numstr == '+' || isspace(*numstr)) perror_exit("invalid number '%s'", numstr);
+  errno = 0;
+  rvalue = strtoul(numstr, &ptr, 10);
+  if(errno || numstr == ptr) perror_exit("invalid number '%s'", numstr);
+   if(*ptr) perror_exit("invalid number '%s'", numstr);
+   if(rvalue >= lowrange && rvalue <= highrange) return rvalue;
+   else {
+         perror_exit("invalid number '%s'", numstr);
+         return rvalue; //Not reachable; to avoid waring message.
+   }
+}
+
+#include <regex.h>
+void is_valid_username(const char *name)
+{
+  char *regex = "^[_.A-Za-z0-9][-_.A-Za-z0-9]*"; //User name REGEX
+  regex_t rp;
+  regmatch_t rm[1];
+  int eval;
+
+  /* compile regular expression */
+  if ((eval = regcomp(&rp, regex, REG_NEWLINE)) != 0) {
+    error_exit("Regex compile fail");
+  } 
+
+  /* compare string against pattern --  remember that patterns 
+   *        are anchored to the beginning of the line */
+  if (regexec(&rp, name, 1, rm, 0) == 0 && rm[0].rm_so == 0 ) {
+    int len = strlen(name);
+    if((rm[0].rm_eo == len) ||
+        (rm[0].rm_eo == len - 1 && name[len - 1] == '$')) {
+      if (len >= LOGIN_NAME_MAX)                  
+        error_exit("name is too long");
+      else return;
+    }
+  }
+  error_exit("'%s', not a valid %sname",name, ((toys.which->name[3] == 'g')? "group" : "user"));
+}
+
+char* get_shell(void)
+{
+#define DEFAULT_SHELL "/bin/sh"
+  char *shell = getenv("SHELL");
+  if(!shell) {
+    struct passwd *pw;
+    pw = getpwuid(getuid());
+    if(pw && pw->pw_shell && pw->pw_shell[0])
+      shell = pw->pw_shell;
+    else shell = DEFAULT_SHELL;
+  }
+  return xstrdup(shell);
+#undef DEFAULT_SHELL
+}
diff --git a/lib/lib.h b/lib/lib.h
new file mode 100644 (file)
index 0000000..af1dbb1
--- /dev/null
+++ b/lib/lib.h
@@ -0,0 +1,222 @@
+/* lib.h - header file for lib directory
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+// Unfortunately, sizeof() doesn't work in a preprocessor test.  TODO.
+
+//#if sizeof(double) <= sizeof(long)
+//typedef double FLOAT;
+//#else
+typedef float FLOAT;
+//#endif
+
+// libc generally has this, but the headers are screwed up
+ssize_t getline(char **lineptr, size_t *n, FILE *stream);
+
+// llist.c
+
+// All these list types can be handled by the same code because first element
+// is always next pointer, so next = (mytype *)&struct. (The payloads are
+// named differently to catch using the wrong type early.)
+
+struct string_list {
+  struct string_list *next;
+  char str[0];
+};
+
+struct arg_list {
+  struct arg_list *next;
+  char *arg;
+};
+
+struct double_list {
+  struct double_list *next, *prev;
+  char *data;
+};
+
+void llist_traverse(void *list, void (*using)(void *data));
+void *llist_pop(void *list);  // actually void **list, but the compiler's dumb
+void dlist_add_nomalloc(struct double_list **list, struct double_list *new);
+struct double_list *dlist_add(struct double_list **list, char *data);
+
+// args.c
+void get_optflags(void);
+
+// dirtree.c
+
+// Values returnable from callback function (bitfield, or them together)
+// Default with no callback is 0
+
+// Add this node to the tree
+#define DIRTREE_SAVE         1
+// Recurse into children
+#define DIRTREE_RECURSE      2
+// Call again after handling all children of this directory
+// (Ignored for non-directories, sets linklen = -1 before second call.)
+#define DIRTREE_COMEAGAIN    4
+// Follow symlinks to directories
+#define DIRTREE_SYMFOLLOW    8
+// Don't look at any more files in this directory.
+#define DIRTREE_ABORT      256
+
+#define DIRTREE_ABORTVAL ((struct dirtree *)1)
+
+struct dirtree {
+  struct dirtree *next, *parent, *child;
+  long extra; // place for user to store their stuff (can be pointer)
+  struct stat st;
+  char *symlink;
+  int data;  // dirfd for directory, linklen for symlink, -1 = comeagain
+  char name[];
+};
+
+struct dirtree *dirtree_add_node(struct dirtree *p, char *name, int symfollow);
+char *dirtree_path(struct dirtree *node, int *plen);
+int dirtree_notdotdot(struct dirtree *catch);
+int dirtree_parentfd(struct dirtree *node);
+struct dirtree *dirtree_handle_callback(struct dirtree *new,
+  int (*callback)(struct dirtree *node));
+void dirtree_recurse(struct dirtree *node,
+  int (*callback)(struct dirtree *node), int symfollow);
+struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node));
+
+// help.c
+
+void show_help(void);
+
+// lib.c
+void xstrncpy(char *dest, char *src, size_t size);
+void xexit(void) noreturn;
+void verror_msg(char *msg, int err, va_list va);
+void error_msg(char *msg, ...);
+void perror_msg(char *msg, ...);
+void error_exit(char *msg, ...) noreturn;
+void perror_exit(char *msg, ...) noreturn;
+void *xmalloc(size_t size);
+void *xzalloc(size_t size);
+void *xrealloc(void *ptr, size_t size);
+char *xstrndup(char *s, size_t n);
+char *xstrdup(char *s);
+char *xmsprintf(char *format, ...);
+void xprintf(char *format, ...);
+void xputs(char *s);
+void xputc(char c);
+void xflush(void);
+void xexec(char **argv);
+void xaccess(char *path, int flags);
+void xunlink(char *path);
+int xcreate(char *path, int flags, int mode);
+int xopen(char *path, int flags);
+void xclose(int fd);
+int xdup(int fd);
+FILE *xfopen(char *path, char *mode);
+ssize_t readall(int fd, void *buf, size_t len);
+ssize_t writeall(int fd, void *buf, size_t len);
+size_t xread(int fd, void *buf, size_t len);
+void xreadall(int fd, void *buf, size_t len);
+void xwrite(int fd, void *buf, size_t len);
+off_t xlseek(int fd, off_t offset, int whence);
+off_t lskip(int fd, off_t offset);
+char *readfile(char *name);
+char *xreadfile(char *name);
+void msleep(long miliseconds);
+int xioctl(int fd, int request, void *data);
+int64_t peek(void *ptr, int size);
+void poke(void *ptr, uint64_t val, int size);
+char *xgetcwd(void);
+void xstat(char *path, struct stat *st);
+char *xabspath(char *path, int exact);
+char *xrealpath(char *path);
+void xchdir(char *path);
+void xmkpath(char *path, int mode);
+void xsetuid(uid_t uid);
+struct string_list *find_in_path(char *path, char *filename);
+void utoa_to_buf(unsigned n, char *buf, unsigned buflen);
+void itoa_to_buf(int n, char *buf, unsigned buflen);
+char *utoa(unsigned n);
+char *itoa(int n);
+long atolx(char *c);
+int numlen(long l);
+int stridx(char *haystack, char needle);
+off_t fdlength(int fd);
+char *xreadlink(char *name);
+void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+  void (*function)(int fd, char *name));
+void loopfiles(char **argv, void (*function)(int fd, char *name));
+char *get_rawline(int fd, long *plen, char end);
+char *get_line(int fd);
+void xsendfile(int in, int out);
+int wfchmodat(int rc, char *name, mode_t mode);
+int copy_tempfile(int fdin, char *name, char **tempname);
+void delete_tempfile(int fdin, int fdout, char **tempname);
+void replace_tempfile(int fdin, int fdout, char **tempname);
+void crc_init(unsigned int *crc_table, int little_endian);
+void terminal_size(unsigned *x, unsigned *y);
+int yesno(char *prompt, int def);
+void for_each_pid_with_name_in(char **names, int (*callback)(pid_t pid, char *name));
+unsigned long xstrtoul(const char *nptr, char **endptr, int base);
+
+// net.c
+int xsocket(int domain, int type, int protocol);
+
+// getmountlist.c
+struct mtab_list {
+  struct mtab_list *next;
+  struct stat stat;
+  struct statvfs statvfs;
+  char *dir;
+  char *device;
+  char type[0];
+};
+
+struct mtab_list *xgetmountlist(void);
+
+void bunzipStream(int src_fd, int dst_fd);
+
+// signal
+
+void sigatexit(void *handler);
+int sig_to_num(char *pidstr);
+char *num_to_sig(int sig);
+
+mode_t string_to_mode(char *mode_str, mode_t base);
+void mode_to_string(mode_t mode, char *buf);
+int m_parse(char *optstr, mode_t *mode);
+
+// password helper functions
+#define MAX_SALT_LEN  20 //3 for id, 16 for key, 1 for '\0'
+#define SYS_FIRST_ID  100
+#define SYS_LAST_ID   999
+int get_salt(char *salt, char * algo);
+int read_password(char * buff, int buflen, char* mesg);
+int update_password(char *filename, char* username, char* encrypted);
+int update_group(struct group *grp, char *filename);
+void is_valid_username(const char *name);
+int add_user( char *filename, char *entry);
+char* get_shell(void);
+
+// du helper functions
+char* make_human_readable(unsigned long long size, unsigned long unit);
+
+// useful tools
+#define min(a,b) (a)<(b) ? (a) : (b)
+#define max(a,b) (a)>(b) ? (a) : (b)
+
+// cut helper functions
+unsigned long get_int_value(const char *numstr, unsigned lowrange, unsigned highrange);
+
+/*
+ * Defination for daemonizing
+ */
+
+#define CHDIR_ROOT     1
+#define DEVNULL_STDIO  2
+#define ONLY_SIMULATE  3
+#define CLOSE_FDS      4
+int go_daemon(int flags);
+
+/*loop device helper functions */
+int get_loopdevice(char *loopdev);
+int set_loopdevice(char* loopdev, char *file, off_t offset);
+int delete_loopdevice(char *loopdev);
\ No newline at end of file
diff --git a/lib/llist.c b/lib/llist.c
new file mode 100644 (file)
index 0000000..7b41fd0
--- /dev/null
@@ -0,0 +1,56 @@
+/* llist.c - Linked list functions
+ *
+ * Linked list structures have a next pointer as their first element.
+ */
+
+#include "toys.h"
+
+// Call a function (such as free()) on each element of a linked list.
+void llist_traverse(void *list, void (*using)(void *data))
+{
+  void *old = list;
+
+  while (list) {
+    void *pop = llist_pop(&list);
+    using(pop);
+
+    // End doubly linked list too.
+    if (old == list) break;
+  }
+}
+
+// Return the first item from the list, advancing the list (which must be called
+// as &list)
+void *llist_pop(void *list)
+{
+  // I'd use a void ** for the argument, and even accept the typecast in all
+  // callers as documentation you need the &, except the stupid compiler
+  // would then scream about type-punned pointers.  Screw it.
+  void **llist = (void **)list;
+  void **next = (void **)*llist;
+  *llist = *next;
+
+  return (void *)next;
+}
+
+void dlist_add_nomalloc(struct double_list **list, struct double_list *new)
+{
+  if (*list) {
+    new->next = *list;
+    new->prev = (*list)->prev;
+    (*list)->prev->next = new;
+    (*list)->prev = new;
+  } else *list = new->next = new->prev = new;
+}
+
+
+// Add an entry to the end of a doubly linked list
+struct double_list *dlist_add(struct double_list **list, char *data)
+{
+  struct double_list *new = xmalloc(sizeof(struct double_list));
+
+  new->data = data;
+  dlist_add_nomalloc(list, new);
+
+  return new;
+}
diff --git a/lib/loop.c b/lib/loop.c
new file mode 100644 (file)
index 0000000..4657dd0
--- /dev/null
@@ -0,0 +1,115 @@
+/* loop.c - loop device helper functions.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ */
+#include "toys.h"
+#include <linux/loop.h>
+
+#define NUM_OF_LOOP_DEVS  1048576 // 2^20 = 20 is the number of bits for minor number 
+
+/*get a free loopdev */
+int get_loopdevice(char *loopdev)
+{       
+  char *tmploop;
+  int n = 0, fd;
+  struct loop_info info;
+
+  for(n = 0; n < NUM_OF_LOOP_DEVS; n++) { //loop thru all loop devices, untill free device found
+    tmploop = xmsprintf("/dev/loop%d",n);
+    fd = open(tmploop, O_RDONLY);
+    if(fd < 0) {
+      free(tmploop);
+      if(errno == ENOENT) return 0;
+      else perror_exit("/dev/loop<N>");                                                                   
+    }
+    else {   
+      if(ioctl(fd, LOOP_GET_STATUS, &info) && (errno == ENXIO)) {
+        /* ENXIO is returned if the loop device is not bind */
+        strncpy(loopdev, tmploop, LO_NAME_SIZE);
+        loopdev[LO_NAME_SIZE - 1] = '\0';
+        xclose(fd);
+        free(tmploop);
+        return 1;
+      }
+    }   
+    xclose(fd);
+    free(tmploop);
+  }
+
+  return 0;
+}
+
+/*
+ * Set the loop device on a given file/device at the supplied offset
+ */
+int set_loopdevice(char *loopdev, char *file, off_t offset)
+{
+  int file_fd, device_fd;
+  struct loop_info info;
+
+  file_fd = open(file, O_RDWR);
+  if (file_fd < 0) {
+    file_fd = open(file, O_RDONLY);
+    if(file_fd < 0 ) {
+      perror_msg("%s: open backing file failed",file);
+      return 1;
+    }
+  }
+  device_fd = open(loopdev, O_RDWR);
+  if (device_fd < 0) {
+    device_fd = open(loopdev, O_RDONLY);
+    if(device_fd < 0) {
+      perror_msg("open loop device failed");
+      xclose(file_fd);
+      return 1;
+    }
+  }
+  if(ioctl(device_fd, LOOP_GET_STATUS, &info) && errno == ENXIO) { //device is free;
+    if (ioctl(device_fd, LOOP_SET_FD, file_fd) ==0) {
+      memset(&info, 0, sizeof(info));
+      strncpy(info.lo_name, file, LO_NAME_SIZE);
+      info.lo_name[LO_NAME_SIZE - 1] = '\0';
+      info.lo_offset = offset;
+      if(ioctl(device_fd, LOOP_SET_STATUS, &info)) goto LOOP_ERROR;
+    }
+    else {
+LOOP_ERROR:
+      perror_msg("ioctl LOOP_SET_FD failed");
+      xclose(file_fd);
+      xclose(device_fd);
+      return 1;
+    }
+  } else if (strcmp(file, (char *)info.lo_name) != 0
+        || offset != info.lo_offset
+      ) {
+    xclose(file_fd);
+    xclose(device_fd);
+    perror_msg("%s: device is busy", loopdev);
+    return 1;
+  }
+
+  xclose(file_fd);
+  xclose(device_fd);
+  return 0;
+}
+
+/*
+ * delete the setup loop device
+ */
+int delete_loopdevice(char *loopdev)
+{
+  int loop_fd = open(loopdev, O_RDONLY);
+  if (loop_fd < 0) {
+    perror_msg("%s: open loop device failed", loopdev);
+    return 1;
+  }  
+  if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+    perror_msg("%s: ioctl LOOP_CLR_FD failed", loopdev);
+    xclose(loop_fd);
+    return 1;
+  }
+
+  xclose(loop_fd);
+  return 0;
+}
diff --git a/lib/net.c b/lib/net.c
new file mode 100644 (file)
index 0000000..f4b2643
--- /dev/null
+++ b/lib/net.c
@@ -0,0 +1,10 @@
+#include "toys.h"
+#include "toynet.h"
+
+int xsocket(int domain, int type, int protocol)
+{
+  int fd = socket(domain, type, protocol);
+
+  if (fd < 0) perror_exit("socket %x %x", type, protocol);
+  return fd;
+}
diff --git a/lib/password.c b/lib/password.c
new file mode 100644 (file)
index 0000000..c67a676
--- /dev/null
@@ -0,0 +1,370 @@
+/* pwdutils.c - password read/update helper functions.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ */
+
+#include "toys.h"
+#include <time.h>
+
+#define URANDOM_PATH    "/dev/urandom"
+static unsigned int random_number_generator(int fd)
+{      
+  unsigned int randnum;
+  xreadall(fd, &randnum, sizeof(randnum));
+  return randnum;
+}      
+       
+static char inttoc(int i)
+{      
+  // salt value uses 64 chracters in "./0-9a-zA-Z"
+  const char character_set[]="./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  i &= 0x3f; // masking for using 10 bits only
+  return character_set[i];
+}      
+       
+int get_salt(char *salt, char *algo)
+{      
+  int i, salt_length = 0, offset;
+  int randfd;
+  if(!strcmp(algo,"des")){
+    // 2 bytes salt value is used in des
+    salt_length = 2;
+    offset = 0;
+  } else {
+    *salt++ = '$';
+    if(!strcmp(algo,"md5")){
+      *salt++ = '1';
+      // 8 bytes salt value is used in md5
+      salt_length = 8;
+    } else if(!strcmp(algo,"sha256")){
+      *salt++ = '5';
+      // 16 bytes salt value is used in sha256
+      salt_length = 16;
+    } else if(!strcmp(algo,"sha512")){
+      *salt++ = '6';
+      // 16 bytes salt value is used in sha512
+      salt_length = 16;
+    } else return -1;
+       
+    *salt++ = '$';
+    offset = 3;
+  }    
+       
+  randfd = xopen(URANDOM_PATH, O_RDONLY);
+  for(i=0; i<salt_length; i++)
+    salt[i] = inttoc(random_number_generator(randfd));
+  salt[salt_length+1] = '\0';
+  xclose(randfd);
+       
+  return offset;
+}
+void handle(int signo)
+{
+}
+
+int read_password(char * buff, int buflen, char* mesg)
+{
+  int i = 0;
+  struct termios termio, oldtermio;
+  struct sigaction sa, oldsa;
+  tcgetattr(0, &oldtermio);
+  tcflush(0, TCIFLUSH);
+  termio = oldtermio;
+
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_handler = handle;
+  sigaction(SIGINT, &sa, &oldsa);
+
+  termio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
+  termio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
+  tcsetattr(0, TCSANOW, &termio);
+
+  fputs(mesg, stdout);
+  fflush(stdout);
+
+  while (1) {
+    int ret = read(0, &buff[i], 1);
+    if ( ret < 0 ) {
+      buff[0] = 0;
+      sigaction(SIGINT, &oldsa, NULL);
+      tcsetattr(0, TCSANOW, &oldtermio);
+      xputc('\n');
+      fflush(stdout);
+      return 1;
+    } else if (ret == 0 || buff[i] == '\n' || buff[i] == '\r' || buflen == i+1)
+    {
+      buff[i] = '\0';
+      break;
+    }
+    i++;
+  }
+  sigaction(SIGINT, &oldsa, NULL);
+  tcsetattr(0, TCSANOW, &oldtermio);
+  puts("");
+  fflush(stdout);
+  return 0;
+}
+
+static char *get_nextcolon(const char *line, char delim)
+{
+  char *current_ptr = NULL;
+  if((current_ptr = strchr(line, ':')) == NULL) error_exit("Invalid Entry\n");
+  return current_ptr;
+}
+
+int update_password(char *filename, char* username, char* encrypted)
+{
+  char *filenamesfx = NULL, *namesfx = NULL;
+  char *shadow = NULL, *sfx = NULL;
+  FILE *exfp, *newfp;
+  int ret = -1; //fail
+  struct flock lock;
+  char *line = NULL;
+
+  shadow = strstr(filename, "shadow");
+  filenamesfx = xmsprintf("%s+", filename);
+  sfx = strchr(filenamesfx, '+');
+
+  exfp = fopen(filename, "r+");
+  if(!exfp) {
+    perror_msg("Couldn't open file %s",filename);
+    goto free_storage;
+  }
+
+  *sfx = '-';
+  ret = unlink(filenamesfx);
+  ret = link(filename, filenamesfx);
+  if(ret < 0) error_msg("can't create backup file");
+
+  *sfx = '+';
+  lock.l_type = F_WRLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = 0;
+  lock.l_len = 0;
+
+  ret = fcntl(fileno(exfp), F_SETLK, &lock);
+  if(ret < 0) perror_msg("Couldn't lock file %s",filename);
+
+  lock.l_type = F_UNLCK; //unlocking at a later stage
+
+  newfp = fopen(filenamesfx, "w+");
+  if(!newfp) {
+    error_msg("couldn't open file for writing");
+    ret = -1;
+    fclose(exfp);
+    goto free_storage;
+  }
+
+  ret = 0;
+  namesfx = xmsprintf("%s:",username);
+  while((line = get_line(fileno(exfp))) != NULL)
+  {
+    if(strncmp(line, namesfx, strlen(namesfx)) != 0)
+      fprintf(newfp, "%s\n", line);
+    else {
+      char *current_ptr = NULL;
+      fprintf(newfp, "%s%s:",namesfx,encrypted);
+      current_ptr = get_nextcolon(line, ':'); //past username
+      current_ptr++; //past colon ':' after username
+      current_ptr = get_nextcolon(current_ptr, ':'); //past passwd
+      current_ptr++; //past colon ':' after passwd
+      if(shadow) {
+        fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
+        current_ptr = get_nextcolon(current_ptr, ':');
+        current_ptr++; //past time stamp colon.
+        fprintf(newfp, "%s\n",current_ptr);
+      }
+      else fprintf(newfp, "%s\n",current_ptr);
+    }
+
+    free(line);
+  }
+  free(namesfx);
+  fcntl(fileno(exfp), F_SETLK, &lock);
+  fclose(exfp);
+
+  errno = 0;
+  fflush(newfp);
+  fsync(fileno(newfp));
+  fclose(newfp);
+  rename(filenamesfx, filename);
+  if(errno) {
+    perror_msg("File Writing/Saving failed: ");
+    unlink(filenamesfx);
+    ret = -1;
+  }
+
+free_storage:
+  free(filenamesfx);
+  return ret;
+}
+
+int add_user( char *filename, char *entry)
+{
+  char *filenamesfx = NULL;
+  char *sfx = NULL;
+  FILE *exfp, *newfp;
+  int ret = -1; //fail
+  struct flock lock;
+  char *line = NULL;
+
+  filenamesfx = xmsprintf("%s+", filename);
+  sfx = strchr(filenamesfx, '+');
+
+  exfp = fopen(filename, "r+");
+  if(!exfp) {
+    perror_msg("Couldn't open file %s",filename);
+    goto free_storage;
+  }
+
+  *sfx = '-';
+  ret = unlink(filenamesfx);
+  ret = link(filename, filenamesfx);
+  if(ret < 0) error_msg("can't create backup file");
+
+  *sfx = '+';
+  lock.l_type = F_WRLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = 0;
+  lock.l_len = 0;
+
+  ret = fcntl(fileno(exfp), F_SETLK, &lock);
+  if(ret < 0) perror_msg("Couldn't lock file %s",filename);
+
+  lock.l_type = F_UNLCK; //unlocking at a later stage
+
+  newfp = fopen(filenamesfx, "w+");
+  if(!newfp) {
+    error_msg("couldn't open file for writing");
+    ret = -1;
+    fclose(exfp);
+    goto free_storage;
+  }
+  while((line = get_line(fileno(exfp))) != NULL) {
+    fprintf(newfp, "%s\n", line);
+    free(line);
+  }
+
+  ret = 0;
+  fprintf(newfp, "%s\n", entry); //adding the entry
+
+  fcntl(fileno(exfp), F_SETLK, &lock);
+  fclose(exfp);
+
+  errno = 0;
+  fflush(newfp);
+  fsync(fileno(newfp));
+  fclose(newfp);
+  rename(filenamesfx, filename);
+  if(errno) {
+    perror_msg("File Writing/Saving failed: ");
+    unlink(filenamesfx);
+    ret = -1;
+  }
+
+free_storage:
+  free(filenamesfx);
+  return ret;
+}
+
+int update_group(struct group *gr, char *filename)
+{
+  char *filenamesfx = NULL, *namesfx = NULL;
+  char *shadow = NULL, *sfx = NULL;
+  FILE *exfp, *newfp;
+  int ret = -1; //fail
+  int found = 0;
+  struct flock lock;
+  char *line = NULL;
+
+  shadow = strstr(filename, "gshadow");
+  filenamesfx = xmsprintf("%s+", filename);
+  sfx = strchr(filenamesfx, '+');
+
+  exfp = fopen(filename, "r+");
+  if(!exfp) {
+    perror_msg("Couldn't open file %s",filename);
+    goto free_storage;
+  }
+
+  *sfx = '-';
+  ret = unlink(filenamesfx);
+  ret = link(filename, filenamesfx);
+  if(ret < 0) error_msg("can't create backup file");
+
+  *sfx = '+';
+  lock.l_type = F_WRLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = 0;
+  lock.l_len = 0;
+
+  ret = fcntl(fileno(exfp), F_SETLK, &lock);
+  if(ret < 0) perror_msg("Couldn't lock file %s",filename);
+
+  lock.l_type = F_UNLCK; //unlocking at a later stage
+
+  newfp = fopen(filenamesfx, "w+");
+  if(!newfp) {
+    error_msg("couldn't open file for writing");
+    ret = -1;
+    fclose(exfp);
+    goto free_storage;
+  }
+
+  ret = found = 0;
+  namesfx = xmsprintf("%s:",gr->gr_name);
+  while((line = get_line(fileno(exfp))) != NULL)
+  {
+    if(strncmp(line, namesfx, strlen(namesfx)) != 0)
+      fprintf(newfp, "%s\n", line);
+    else {
+      char *current_ptr = NULL;
+      int i = 0;
+      found = 1;
+      current_ptr = get_nextcolon(line, ':'); //past groupname
+      current_ptr++; //past colon ':' after groupname
+      current_ptr = get_nextcolon(current_ptr, ':'); //past passwd
+      current_ptr++; //past colon ':' after passwd
+      current_ptr = get_nextcolon(current_ptr, ':'); //past gid or admin list
+      current_ptr++; //past colon ':' after gid
+      *current_ptr = '\0';
+      fprintf(newfp, "%s", line);
+      while(gr->gr_mem && gr->gr_mem[i]) {
+        if(i > 0) fprintf(newfp, "%c", ',');
+        fprintf(newfp, "%s", gr->gr_mem[i++]);
+      }
+      fprintf(newfp,"\n");
+    }
+    free(line);
+  }
+
+  if(!found) {
+    int i = 0;
+    fprintf(newfp, "%s:%s:", gr->gr_name, gr->gr_passwd);
+    if(shadow) fprintf(newfp, ":");
+    else fprintf(newfp, "%d:", gr->gr_gid);
+    while(gr->gr_mem && gr->gr_mem[i]) {
+      if(i > 0) fprintf(newfp, "%c", ',');
+      fprintf(newfp, "%s", gr->gr_mem[i++]);
+    }
+    fprintf(newfp, "\n");
+  }
+  free(namesfx);
+  fcntl(fileno(exfp), F_SETLK, &lock);
+  fclose(exfp);
+
+  errno = 0;
+  fflush(newfp);
+  fsync(fileno(newfp));
+  fclose(newfp);
+  rename(filenamesfx, filename);
+  if(errno) {
+    perror_msg("File Writing/Saving failed: ");
+    unlink(filenamesfx);
+    ret = -1;
+  }
+
+free_storage:
+  free(filenamesfx);
+  return ret;
+}
diff --git a/lib/portability.c b/lib/portability.c
new file mode 100644 (file)
index 0000000..d901a4b
--- /dev/null
@@ -0,0 +1,72 @@
+/* portability.c - code to workaround the deficiencies of various platforms.
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ * Copyright 2012 Georgi Chorbadzhiyski <gf@unixsol.org>
+ */
+
+#include "toys.h"
+
+#if defined(__APPLE__) || defined(__ANDROID__)
+ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream)
+{
+  int ch;
+  size_t new_len;
+  ssize_t i = 0;
+  char *line, *new_line;
+
+  // Invalid input
+  if (!linep || !np) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (*linep == NULL || *np == 0) {
+    *np = 1024;
+    *linep = calloc(1, *np);
+    if (*linep == NULL) return -1;
+  }
+  line = *linep;
+
+  while ((ch = getc(stream)) != EOF) {
+    if (i > *np) {
+      // Need more space
+      new_len = *np + 1024;
+      new_line = realloc(*linep, new_len);
+      if (!new_line) return -1;
+      *np = new_len;
+      *linep = new_line;
+    }
+
+    line[i] = ch;
+    if (ch == delim) break;
+    i += 1;
+  }
+
+  if (i > *np) {
+    // Need more space
+    new_len  = i + 2;
+    new_line = realloc(*linep, new_len);
+    if (!new_line) return -1;
+    *np = new_len;
+    *linep = new_line;
+  }
+  line[i + 1] = '\0';
+
+  return i > 0 ? i : -1;
+}
+
+ssize_t getline(char **linep, size_t *np, FILE *stream)
+{
+  return getdelim(linep, np, '\n', stream);
+}
+#endif
+
+#if defined(__APPLE__)
+extern char **environ;
+
+int clearenv(void)
+{
+  *environ = NULL;
+  return 0;
+}
+#endif
diff --git a/lib/portability.h b/lib/portability.h
new file mode 100644 (file)
index 0000000..745f8cd
--- /dev/null
@@ -0,0 +1,149 @@
+// Workarounds for horrible build environment idiosyncrasies.
+
+// Instead of polluting the code with strange #ifdefs to work around bugs
+// in specific compiler, library, or OS versions, localize all that here
+// and in portability.c
+
+// The tendency of gcc to produce stupid warnings continues with
+// warn_unused_result, which warns about things like ignoring the return code
+// of nice(2) (which is completely useless since -1 is a legitimate return
+// value on success and even the man page tells you to use errno instead).
+
+// This makes it stop.
+
+#undef _FORTIFY_SOURCE
+
+// Test for gcc (using compiler builtin #define)
+
+#ifdef __GNUC__
+#define noreturn       __attribute__((noreturn))
+#else
+#define noreturn
+#endif
+
+// Always use long file support.
+#define _FILE_OFFSET_BITS 64
+
+// This isn't in the spec, but it's how we determine what libc we're using.
+
+#include <features.h>
+
+// Various constants old build environments might not have even if kernel does
+
+#ifndef AT_FDCWD
+#define AT_FDCWD -100
+#endif
+
+#ifndef AT_SYMLINK_NOFOLLOW
+#define AT_SYMLINK_NOFOLLOW 0x100
+#endif
+
+#ifndef AT_REMOVEDIR
+#define AT_REMOVEDIR 0x200
+#endif
+
+// We don't define GNU_dammit because we're not part of the gnu project, and
+// don't want to get any FSF on us. Unfortunately glibc (gnu libc)
+// won't give us Linux syscall wrappers without claiming to be part of the
+// gnu project (because Stallman's "GNU owns Linux" revisionist history
+// crusade includes the kernel, even though Linux was inspired by Minix).
+
+// We use most non-posix Linux syscalls directly through the syscall() wrapper,
+// but even many posix-2008 functions aren't provided by glibc unless you
+// claim it's in the name of Gnu.
+
+#if defined(__GLIBC__)
+// "Function prototypes shall be provided." but aren't.
+// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html
+char *crypt(const char *key, const char *salt);
+
+// see http://pubs.opengroup.org/onlinepubs/9699919799/functions/strptime.html
+#include <time.h>
+char *strptime(const char *buf, const char *format, struct tm *tm);
+
+// uClibc pretends to be glibc and copied a lot of its bugs, but has a few more
+#if defined(__UCLIBC__)
+#include <unistd.h>
+#include <stdio.h>
+ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
+
+// When building under obsolete glibc (Ubuntu 8.04-ish), hold its hand a bit.
+#elif __GLIBC__ == 2 && __GLIBC_MINOR__ < 10
+#define fstatat fstatat64
+int fstatat64(int dirfd, const char *pathname, void *buf, int flags);
+int readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
+char *stpcpy(char *dest, const char *src);
+#include <sys/stat.h>
+int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
+int openat(int dirfd, const char *pathname, int flags, ...);
+#include <dirent.h>
+DIR *fdopendir(int fd);
+#include <unistd.h>
+int fchownat(int dirfd, const char *pathname,
+                    uid_t owner, gid_t group, int flags);
+int isblank(int c);
+int unlinkat(int dirfd, const char *pathname, int flags);
+#include <stdio.h>
+ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
+
+// Straight from posix-2008, things old glibc had but didn't prototype
+
+int faccessat(int fd, const char *path, int amode, int flag);
+int linkat(int fd1, const char *path1, int fd2, const char *path2, int flag);
+int mkdirat(int fd, const char *path, mode_t mode);
+int symlinkat(const char *path1, int fd, const char *path2);
+int mknodat(int fd, const char *path, mode_t mode, dev_t dev);
+#include <sys/time.h>
+int futimens(int fd, const struct timespec times[2]);
+int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
+#endif
+
+#endif
+
+// Work out how to do endianness
+
+#ifndef __APPLE__
+#include <byteswap.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define IS_BIG_ENDIAN 1
+#else
+#define IS_BIG_ENDIAN 0
+#endif
+
+int clearenv(void);
+#else
+
+#ifdef __BIG_ENDIAN__
+#define IS_BIG_ENDIAN 1
+#else
+#define IS_BIG_ENDIAN 0
+#endif
+
+#endif
+
+#if IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 0
+#define SWAP_BE16(x) (x)
+#define SWAP_BE32(x) (x)
+#define SWAP_BE64(x) (x)
+#define SWAP_LE16(x) bswap_16(x)
+#define SWAP_LE32(x) bswap_32(x)
+#define SWAP_LE64(x) bswap_64(x)
+#else
+#define IS_LITTLE_ENDIAN 1
+#define SWAP_BE16(x) bswap_16(x)
+#define SWAP_BE32(x) bswap_32(x)
+#define SWAP_BE64(x) bswap_64(x)
+#define SWAP_LE16(x) (x)
+#define SWAP_LE32(x) (x)
+#define SWAP_LE64(x) (x)
+#endif
+
+#if defined(__APPLE__) || defined(__ANDROID__)
+ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
+ssize_t getline(char **lineptr, size_t *n, FILE *stream);
+#endif
+
+#include "generated/portability.h"
diff --git a/lib/proto.h b/lib/proto.h
new file mode 100644 (file)
index 0000000..0b112ca
--- /dev/null
@@ -0,0 +1,197 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+extern  int  yywrap(void);
+extern  void  setfname(Cell *);
+extern  int  constnode(Node *);
+extern  char  *strnode(Node *);
+extern  Node  *notnull(Node *);
+extern  int  yyparse(void);
+
+extern  int  yylex(void);
+extern  void  startreg(void);
+extern  int  input(void);
+extern  void  unput(int);
+extern  void  unputstr(const char *);
+extern  int  yylook(void);
+extern  int  yyback(int *, int);
+extern  int  yyinput(void);
+
+extern  fa  *makedfa(const char *, int);
+extern  fa  *mkdfa(const char *, int);
+extern  int  makeinit(fa *, int);
+extern  void  penter(Node *);
+extern  void  freetr(Node *);
+extern  int  hexstr(uschar **);
+extern  int  quoted(uschar **);
+extern  char  *cclenter(const char *);
+extern  void  overflo(const char *);
+extern  void  cfoll(fa *, Node *);
+extern  int  first(Node *);
+extern  void  follow(Node *);
+extern  int  member(int, const char *);
+extern  int  match(fa *, const char *);
+extern  int  pmatch(fa *, const char *);
+extern  int  nematch(fa *, const char *);
+extern  Node  *reparse(const char *);
+extern  Node  *regexp(void);
+extern  Node  *primary(void);
+extern  Node  *concat(Node *);
+extern  Node  *alt(Node *);
+extern  Node  *unary(Node *);
+extern  int  relex(void);
+extern  int  cgoto(fa *, int, int);
+extern  void  freefa(fa *);
+
+extern  int  pgetc(void);
+extern  char  *cursource(void);
+
+extern  Node  *nodealloc(int);
+extern  Node  *exptostat(Node *);
+extern  Node  *node1(int, Node *);
+extern  Node  *node2(int, Node *, Node *);
+extern  Node  *node3(int, Node *, Node *, Node *);
+extern  Node  *node4(int, Node *, Node *, Node *, Node *);
+extern  Node  *stat3(int, Node *, Node *, Node *);
+extern  Node  *op2(int, Node *, Node *);
+extern  Node  *op1(int, Node *);
+extern  Node  *stat1(int, Node *);
+extern  Node  *op3(int, Node *, Node *, Node *);
+extern  Node  *op4(int, Node *, Node *, Node *, Node *);
+extern  Node  *stat2(int, Node *, Node *);
+extern  Node  *stat4(int, Node *, Node *, Node *, Node *);
+extern  Node  *celltonode(Cell *, int);
+extern  Node  *rectonode(void);
+extern  Node  *makearr(Node *);
+extern  Node  *pa2stat(Node *, Node *, Node *);
+extern  Node  *linkum(Node *, Node *);
+extern  void  defn(Cell *, Node *, Node *);
+extern  int  isarg(const char *);
+extern  char  *tokname(int);
+extern  Cell  *(*proctab[])(Node **, int);
+extern  int  ptoi(void *);
+extern  Node  *itonp(int);
+
+extern  void  syminit(void);
+extern  void  arginit(int, char **);
+extern  void  envinit(char **);
+extern  Array  *makesymtab(int);
+extern  void  freesymtab(Cell *);
+extern  void  freeelem(Cell *, const char *);
+extern  Cell  *setsymtab(const char *, const char *, double, unsigned int, Array *);
+extern  int  hash(const char *, int);
+extern  void  rehash(Array *);
+extern  Cell  *lookup(const char *, Array *);
+extern  double  setfval(Cell *, double);
+extern  void  funnyvar(Cell *, const char *);
+extern  char  *setsval(Cell *, const char *);
+extern  double  getfval(Cell *);
+extern  char  *getsval(Cell *);
+extern  char  *getpssval(Cell *);   /* for print */
+extern  char  *tostring(const char *);
+extern  char  *qstring(const char *, int);
+
+extern  void  recinit(unsigned int);
+extern  void  initgetrec(void);
+extern  void  makefields(int, int);
+extern  void  growfldtab(int n);
+extern  int  getrec(char **, int *, int);
+extern  void  nextfile(void);
+extern  int  readrec(char **buf, int *bufsize, FILE *inf);
+extern  char  *getargv(int);
+extern  void  setclvar(char *);
+extern  void  fldbld(void);
+extern  void  cleanfld(int, int);
+extern  void  newfld(int);
+extern  int  refldbld(const char *, const char *);
+extern  void  recbld(void);
+extern  Cell  *fieldadr(int);
+extern  void  yyerror(const char *);
+extern  void  fpecatch(int);
+extern  void  bracecheck(void);
+extern  void  bcheck2(int, int, int);
+extern  void  SYNTAX(const char *, ...);
+extern  void  FATAL(const char *, ...);
+extern  void  WARNING(const char *, ...);
+extern  void  error(void);
+extern  void  eprint(void);
+extern  void  bclass(int);
+extern  double  errcheck(double, const char *);
+extern  int  isclvar(const char *);
+extern  int  is_number(const char *);
+
+extern  int  adjbuf(char **pb, int *sz, int min, int q, char **pbp, const char *what);
+extern  void  run(Node *);
+extern  Cell  *execute(Node *);
+extern  Cell  *program(Node **, int);
+extern  Cell  *call(Node **, int);
+extern  Cell  *copycell(Cell *);
+extern  Cell  *arg(Node **, int);
+extern  Cell  *jump(Node **, int);
+extern  Cell  *awkgetline(Node **, int);
+extern  Cell  *getnf(Node **, int);
+extern  Cell  *geterrno(Node **, int);
+extern  Cell  *array(Node **, int);
+extern  Cell  *awkdelete(Node **, int);
+extern  Cell  *intest(Node **, int);
+extern  Cell  *matchop(Node **, int);
+extern  Cell  *boolop(Node **, int);
+extern  Cell  *bitops(Node **, int);
+extern  Cell  *relop(Node **, int);
+extern  void  tfree(Cell *);
+extern  Cell  *gettemp(void);
+extern  Cell  *field(Node **, int);
+extern  Cell  *indirect(Node **, int);
+extern  Cell  *substr(Node **, int);
+extern  Cell  *sindex(Node **, int);
+extern  int  format(char **, int *, const char *, Node *);
+extern  Cell  *awksprintf(Node **, int);
+extern  Cell  *awkprintf(Node **, int);
+extern  Cell  *arith(Node **, int);
+extern  double  ipow(double, int);
+extern  Cell  *incrdecr(Node **, int);
+extern  Cell  *assign(Node **, int);
+extern  Cell  *cat(Node **, int);
+extern  Cell  *pastat(Node **, int);
+extern  Cell  *dopa2(Node **, int);
+extern  Cell  *split(Node **, int);
+extern  Cell  *condexpr(Node **, int);
+extern  Cell  *ifstat(Node **, int);
+extern  Cell  *whilestat(Node **, int);
+extern  Cell  *dostat(Node **, int);
+extern  Cell  *forstat(Node **, int);
+extern  Cell  *instat(Node **, int);
+extern  Cell  *bltin(Node **, int);
+extern  Cell  *printstat(Node **, int);
+extern  Cell  *nullproc(Node **, int);
+extern  FILE  *redirect(int, Node *);
+extern  FILE  *openfile(int, const char *);
+extern  const char  *filename(FILE *);
+extern  Cell  *closefile(Node **, int);
+extern  void  closeall(void);
+extern  Cell  *sub(Node **, int);
+extern  Cell  *gsub(Node **, int);
+
+extern  FILE  *popen(const char *, const char *);
+extern  int  pclose(FILE *);
diff --git a/lib/xregcomp.c b/lib/xregcomp.c
new file mode 100644 (file)
index 0000000..ec7d1b7
--- /dev/null
@@ -0,0 +1,22 @@
+/* Call regcomp() and handle errors.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * This is a separate file so environments that haven't got regular expression
+ * support can configure this out and avoid a build break.
+ */
+
+#include "toys.h"
+#include "xregcomp.h"
+
+void xregcomp(regex_t *preg, char *regex, int cflags)
+{
+  int rc = regcomp(preg, regex, cflags);
+
+  if (rc) {
+    char msg[256];
+    regerror(rc, preg, msg, 255);
+    msg[255]=0;
+    error_exit("xregcomp: %s", msg);
+  }
+}
diff --git a/lib/xregcomp.h b/lib/xregcomp.h
new file mode 100644 (file)
index 0000000..fa929fa
--- /dev/null
@@ -0,0 +1,6 @@
+/* This is a separate file so libc doesn't always need regex support. */
+
+#include <sys/types.h>
+#include <regex.h>
+
+void xregcomp(regex_t *preg, char *rexec, int cflags);
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..b0b2bb2
--- /dev/null
+++ b/main.c
@@ -0,0 +1,165 @@
+/* Toybox infrastructure.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+// Populate toy_list[].
+
+#undef NEWTOY
+#undef OLDTOY
+#define NEWTOY(name, opts, flags) {#name, name##_main, opts, flags},
+#define OLDTOY(name, oldname, opts, flags) {#name, oldname##_main, opts, flags},
+
+struct toy_list toy_list[] = {
+#include "generated/newtoys.h"
+};
+
+// global context for this command.
+
+struct toy_context toys;
+union global_union this;
+char toybuf[4096];
+
+struct toy_list *toy_find(char *name)
+{
+  int top, bottom, middle;
+
+  // If the name starts with "toybox" accept that as a match.  Otherwise
+  // skip the first entry, which is out of order.
+
+  if (!strncmp(name,"toybox",6)) return toy_list;
+  bottom = 1;
+
+  // Binary search to find this command.
+
+  top = ARRAY_LEN(toy_list)-1;
+  for (;;) {
+    int result;
+
+    middle = (top+bottom)/2;
+    if (middle<bottom || middle>top) return NULL;
+    result = strcmp(name,toy_list[middle].name);
+    if (!result) return toy_list+middle;
+    if (result<0) top=--middle;
+    else bottom = ++middle;
+  }
+}
+
+// Figure out whether or not anything is using the option parsing logic,
+// because the compiler can't figure out whether or not to optimize it away
+// on its' own.  NEED_OPTIONS becomes a constant allowing if() to optimize
+// stuff out via dead code elimination.
+
+#undef NEWTOY
+#undef OLDTOY
+#define NEWTOY(name, opts, flags) opts ||
+#define OLDTOY(name, oldname, opts, flags) opts ||
+static const int NEED_OPTIONS =
+#include "generated/newtoys.h"
+0;  // Ends the opts || opts || opts...
+
+// Setup toybox global state for this command.
+
+void toy_init(struct toy_list *which, char *argv[])
+{
+  // Drop permissions for non-suid commands.
+
+  if (CFG_TOYBOX_SUID) {
+    uid_t uid = getuid(), euid = geteuid();
+
+    if (!(which->flags & TOYFLAG_STAYROOT)) {
+      if (uid != euid) xsetuid(euid=uid);
+    } else if (CFG_TOYBOX_DEBUG && uid && which != toy_list)
+      error_msg("Not installed suid root");
+
+    if ((which->flags & TOYFLAG_NEEDROOT) && euid) error_exit("Not root");
+  }
+
+  // Free old toys contents (to be reentrant), but leave rebound if any
+
+  if (toys.optargs != toys.argv+1) free(toys.optargs);
+  memset(&toys, 0, offsetof(struct toy_context, rebound));
+
+  toys.which = which;
+  toys.argv = argv;
+  if (NEED_OPTIONS && which->options) get_optflags();
+  else {
+    toys.optargs = argv+1;
+    for (toys.optc=0; toys.optargs[toys.optc]; toys.optc++);
+  }
+  toys.old_umask = umask(0);
+  if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask);
+}
+
+// Like exec() but runs an internal toybox command instead of another file.
+// Only returns if it can't find the command, otherwise exit() when done.
+void toy_exec(char *argv[])
+{
+  struct toy_list *which;
+
+  if (!(which = toy_find(argv[0]))) return;
+  toy_init(which, argv);
+  toys.which->toy_main();
+  if (fflush(NULL) || ferror(stdout)) perror_exit("write");
+  exit(toys.exitval);
+}
+
+// Multiplexer command, first argument is command to run, rest are args to that.
+// If first argument starts with - output list of command install paths.
+
+void toybox_main(void)
+{
+  static char *toy_paths[]={"usr/","bin/","sbin/",0};
+  int i, len = 0;
+
+  toys.which = toy_list;
+  if (toys.argv[1]) {
+    if (CFG_TOYBOX_HELP && !strcmp(toys.argv[1], "--help")) {
+      if (toys.argv[2]) toys.which = toy_find(toys.argv[2]);
+      if (toys.which) {
+        show_help();
+        return;
+      }
+    } else {
+      toy_exec(toys.argv+1);
+      if (toys.argv[1][0] == '-') goto list;
+    }
+    
+    error_exit("Unknown command %s",toys.argv[1]);
+  }
+
+list:
+  // Output list of command.
+  for (i=1; i<ARRAY_LEN(toy_list); i++) {
+    int fl = toy_list[i].flags;
+    if (fl & TOYMASK_LOCATION) {
+      if (toys.argv[1]) {
+        int j;
+        for (j=0; toy_paths[j]; j++)
+          if (fl & (1<<j)) len += printf("%s", toy_paths[j]);
+      }
+      len += printf("%s ",toy_list[i].name);
+      if (len>65) {
+        xputc('\n');
+        len=0;
+      }
+    }
+  }
+  xputc('\n');
+}
+
+int main(int argc, char *argv[])
+{
+  if (CFG_TOYBOX_I18N) setlocale(LC_ALL, "");
+
+  // Trim path off of command name
+  *argv = basename(*argv);
+
+  // Call the multiplexer, adjusting this argv[] to be its' argv[1].
+  // (It will adjust it back before calling toy_exec().)
+  toys.argv = argv-1;
+  toybox_main();
+  return 0;
+}
diff --git a/packaging/bin.links b/packaging/bin.links
new file mode 100644 (file)
index 0000000..33dbb17
--- /dev/null
@@ -0,0 +1,2 @@
+mount
+umount
diff --git a/packaging/klogd.service b/packaging/klogd.service
new file mode 100644 (file)
index 0000000..03df845
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=Run syslog
+DefaultDependencies=no
+After=syslogd.service
+ConditionKernelCommandLine=|!sec_debug.enable=0
+ConditionKernelCommandLine=|!sec_debug.enable_user=0
+
+[Service]
+Type=forking
+ExecStart=/sbin/klogd
+OOMScoreAdjust=-1000
+Restart=always
+RestartSec=0
+
+[Install]
+WantedBy=basic.target
diff --git a/packaging/sbin.links b/packaging/sbin.links
new file mode 100644 (file)
index 0000000..ff4418a
--- /dev/null
@@ -0,0 +1,2 @@
+klogd
+syslogd
diff --git a/packaging/syslogd.manifest b/packaging/syslogd.manifest
new file mode 100644 (file)
index 0000000..70bcf4e
--- /dev/null
@@ -0,0 +1,14 @@
+<manifest>
+       <define>
+               <domain name="syslogd"/>
+               <permit>
+                       <smack permit="_" type="w"/>
+               </permit>
+       </define>
+        <request>
+                <domain name="_"/>
+        </request>
+        <assign>
+               <filesystem path="/sbin/syslogd" exec_label="syslogd" />
+        </assign>
+</manifest>
diff --git a/packaging/syslogd.service b/packaging/syslogd.service
new file mode 100644 (file)
index 0000000..7b10609
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=Run syslog
+DefaultDependencies=no
+After=local-fs.target
+ConditionKernelCommandLine=|!sec_debug.enable=0
+ConditionKernelCommandLine=|!sec_debug.enable_user=0
+
+[Service]
+Type=forking
+ExecStart=/sbin/syslogd -b 5 -B 99
+OOMScoreAdjust=-1000
+Restart=always
+RestartSec=0
+
+[Install]
+WantedBy=basic.target
diff --git a/packaging/toybox.manifest b/packaging/toybox.manifest
new file mode 100644 (file)
index 0000000..c922afb
--- /dev/null
@@ -0,0 +1,8 @@
+<manifest>
+        <request>
+                <domain name="_"/>
+        </request>
+        <assign>
+                <filesystem path="/bin/*" exec_label="none" />
+        </assign>
+</manifest>
diff --git a/packaging/toybox.spec b/packaging/toybox.spec
new file mode 100644 (file)
index 0000000..cacfefa
--- /dev/null
@@ -0,0 +1,147 @@
+Summary: Single binary providing simplified versions of system commands
+Name: toybox
+Version: 0.4.4
+Release: 1
+License: BSD
+Group: System/Shells
+Source: http://www.landley.net/toybox/downloads/%{name}-%{version}.tar.bz2
+Source1: toybox_tizen.config
+Source2: bin.links
+Source3: sbin.links
+Source4: usrbin.links
+Source5: usrsbin.links
+Source101: klogd.service
+Source102: syslogd.service
+Source1001: toybox.manifest
+Source1002: syslogd.manifest
+
+URL: http://www.landley.net/toybox/
+
+%description 
+Toybox is a single binary which includes versions of a large number
+of system commands, including a shell.  This package can be very
+useful for recovering from certain types of system failures,
+particularly those involving broken shared libraries.
+
+%package symlinks-klogd
+Group: tools
+Summary: ToyBox symlinks to provide 'klogd'
+Requires: %{name} = %{version}-%{release}
+
+%description symlinks-klogd
+ToyBox symlinks for utilities corresponding to 'klogd' package.
+
+%package symlinks-sysklogd
+Group: tools
+Summary: ToyBox symlinks to provide 'sysklogd'
+Requires: %{name} = %{version}-%{release}
+
+%description symlinks-sysklogd
+ToyBox symlinks for utilities corresponding to 'sysklogd' package.
+
+%package symlinks-udhcpc
+Group: tools
+Summary: ToyBox symlinks to provide 'udhcpc'
+Requires: %{name} = %{version}-%{release}
+
+%description symlinks-udhcpc
+ToyBox symlinks for utilities corresponding to 'udhcpc' package.
+
+%package symlinks-udhcpd
+Group: tools
+Summary: ToyBox symlinks to provide 'udhcpd'
+Requires: %{name} = %{version}-%{release}
+
+%description symlinks-udhcpd
+ToyBox symlinks for utilities corresponding to 'udhcpd' package.
+
+%prep
+%setup -q
+
+%build
+cp %{SOURCE1001} .
+cp %{SOURCE1002} .
+# create dynamic toybox - the executable is toybox
+make defconfig
+cp packaging/toybox_tizen.config .config
+# In case of user image, TIZEN_SECURE_MOUNT will be defined for secure mount.
+%if "%{?sec_build_project_name}" == "redwood8974_jpn_dcm"
+%if 0%{?tizen_build_binary_release_type_eng} != 1
+export CFLAGS+=" -DTIZEN_SECURE_MOUNT"
+%endif
+%endif
+make -j 4 CC="gcc $RPM_OPT_FLAGS"
+cp toybox toybox-dynamic
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/bin
+install -m 755 toybox-dynamic $RPM_BUILD_ROOT/bin/toybox
+
+# debian/toybox.links
+pushd %{buildroot}
+mkdir -p usr/bin usr/sbin sbin
+#cd bin
+#for f in `cat %SOURCE2` ; do ln -s toybox $f ; done
+cd sbin
+for f in `cat %SOURCE3` ; do ln -s ../bin/toybox $f ; done
+cd ../usr/bin
+for f in `cat %SOURCE4` ; do ln -s ../../bin/toybox $f ; done
+cd ../../usr/sbin
+for f in `cat %SOURCE5` ; do ln -s ../../bin/toybox $f ; done
+popd
+
+# install systemd service files for syslogd and klogd
+mkdir -p %{buildroot}%{_libdir}/systemd/system/basic.target.wants
+install -m 644 %SOURCE101 %{buildroot}%{_libdir}/systemd/system/klogd.service
+ln -s ../klogd.service %{buildroot}%{_libdir}/systemd/system/basic.target.wants/klogd.service
+install -m 644 %SOURCE102 %{buildroot}%{_libdir}/systemd/system/syslogd.service
+ln -s ../syslogd.service %{buildroot}%{_libdir}/systemd/system/basic.target.wants/syslogd.service
+rm -rf $RPM_BUILD_ROOT/sbin/syslogd
+cp -f $RPM_BUILD_ROOT/bin/toybox $RPM_BUILD_ROOT/sbin/syslogd
+
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/license
+cat LICENSE > $RPM_BUILD_ROOT%{_datadir}/license/toybox
+cat LICENSE > $RPM_BUILD_ROOT%{_datadir}/license/toybox-symlinks-klogd
+cat LICENSE > $RPM_BUILD_ROOT%{_datadir}/license/toybox-symlinks-sysklogd
+cat LICENSE > $RPM_BUILD_ROOT%{_datadir}/license/toybox-symlinks-udhcpc
+cat LICENSE > $RPM_BUILD_ROOT%{_datadir}/license/toybox-symlinks-udhcpd
+
+%files
+%defattr(-,root,root,-)
+%doc LICENSE
+%{_datadir}/license/toybox
+/bin/toybox
+#/bin/mount
+#/bin/umount
+%manifest toybox.manifest
+
+%files symlinks-klogd
+%defattr(-,root,root,-)
+%{_datadir}/license/toybox-symlinks-klogd
+/sbin/klogd
+%{_libdir}/systemd/system/klogd.service
+%{_libdir}/systemd/system/basic.target.wants/klogd.service
+%manifest toybox.manifest
+
+%files symlinks-sysklogd
+%defattr(-,root,root,-)
+%{_datadir}/license/toybox-symlinks-sysklogd
+/sbin/syslogd
+%{_libdir}/systemd/system/syslogd.service
+%{_libdir}/systemd/system/basic.target.wants/syslogd.service
+%manifest syslogd.manifest
+
+%files symlinks-udhcpc
+%defattr(-,root,root,-)
+%{_datadir}/license/toybox-symlinks-udhcpc
+%{_bindir}/udhcpc
+%manifest toybox.manifest
+
+%files symlinks-udhcpd
+%defattr(-,root,root,-)
+%{_datadir}/license/toybox-symlinks-udhcpd
+%{_bindir}/dumpleases
+%{_sbindir}/udhcpd
+%manifest toybox.manifest
+
diff --git a/packaging/toybox_tizen.config b/packaging/toybox_tizen.config
new file mode 100644 (file)
index 0000000..c5e7158
--- /dev/null
@@ -0,0 +1,241 @@
+#
+# Automatically generated make config: don't edit
+# ToyBox version: KCONFIG_VERSION
+# Mon Jun 24 21:29:31 2013
+#
+CONFIG_TOYBOX_CONTAINER=y
+
+#
+# LSB commands
+#
+# CONFIG_ADDGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DMESG is not set
+# CONFIG_HOSTNAME is not set
+# CONFIG_KILLALL is not set
+# CONFIG_MD5SUM is not set
+# CONFIG_MD5SUM_SHA1SUM is not set
+# CONFIG_MKNOD is not set
+# CONFIG_MKTEMP is not set
+# CONFIG_MOUNT is not set
+# CONFIG_PASSWD is not set
+# CONFIG_PIDOF is not set
+# CONFIG_SEQ is not set
+# CONFIG_SYNC is not set
+# CONFIG_UMOUNT is not set
+
+#
+# Other commands
+#
+# CONFIG_ARPING is not set
+# CONFIG_BZCAT is not set
+# CONFIG_CATV is not set
+# CONFIG_CHATTR is not set
+# CONFIG_CHROOT is not set
+# CONFIG_CHVT is not set
+# CONFIG_CLEAR is not set
+# CONFIG_COUNT is not set
+# CONFIG_CTTYHACK is not set
+# CONFIG_DEPMOD is not set
+# CONFIG_DOS2UNIX is not set
+# CONFIG_DUMPKMAP is not set
+# CONFIG_BUSYBOX_CMPTBL is not set
+CONFIG_DUMPLEASES=y
+# CONFIG_EJECT is not set
+# CONFIG_ENVDIR is not set
+# CONFIG_EXPR is not set
+# CONFIG_FDISK is not set
+# CONFIG_FIND is not set
+# CONFIG_FREE is not set
+# CONFIG_FSCK is not set
+# CONFIG_FSYNC is not set
+# CONFIG_FTPGET is not set
+# CONFIG_GETTY is not set
+# CONFIG_HALT is not set
+# CONFIG_HELLO is not set
+CONFIG_HELP=y
+# CONFIG_HEXDUMP is not set
+# CONFIG_HOSTID is not set
+# CONFIG_IFCONFIG is not set
+# CONFIG_IFUP is not set
+# CONFIG_INIT is not set
+# CONFIG_INOTIFYD is not set
+# CONFIG_INSMOD is not set
+# CONFIG_IOSTAT is not set
+CONFIG_KLOGD=y
+CONFIG_KLOGD_SOURCE_RING_BUFFER=y
+# CONFIG_LOGIN is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSATTR is not set
+# CONFIG_LSMOD is not set
+# CONFIG_LSUSB is not set
+# CONFIG_MDEV is not set
+# CONFIG_MDEV_CONF is not set
+# CONFIG_MESG is not set
+# CONFIG_MESG_ONLY_GROUP is not set
+# CONFIG_MKPASSWD is not set
+# CONFIG_MKSWAP is not set
+# CONFIG_MODINFO is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_NETCAT is not set
+# CONFIG_NETCAT_LISTEN is not set
+# CONFIG_NETSTAT is not set
+# CONFIG_NSLOOKUP is not set
+# CONFIG_ONEIT is not set
+# CONFIG_PGREP is not set
+# CONFIG_PING is not set
+# CONFIG_PING6 is not set
+# CONFIG_PRINTENV is not set
+# CONFIG_READAHEAD is not set
+# CONFIG_READLINK is not set
+# CONFIG_REALPATH is not set
+# CONFIG_REBOOT is not set
+# CONFIG_REV is not set
+# CONFIG_RMMOD is not set
+# CONFIG_ROUTE is not set
+# CONFIG_SETSID is not set
+# CONFIG_STAT is not set
+# CONFIG_SWAPOFF is not set
+# CONFIG_SWAPON is not set
+# CONFIG_SWITCH_ROOT is not set
+CONFIG_SYSLOGD=y
+# CONFIG_TAC is not set
+# CONFIG_TASKSET is not set
+# CONFIG_TELNET is not set
+# CONFIG_TFTP is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_TRUNCATE is not set
+CONFIG_UDHCPC=y
+CONFIG_UDHCPD=y
+CONFIG_DEBUG_DHCP=y
+# CONFIG_UNSHARE is not set
+# CONFIG_UPTIME is not set
+# CONFIG_USLEEP is not set
+# CONFIG_VCONFIG is not set
+# CONFIG_VMSTAT is not set
+# CONFIG_W is not set
+# CONFIG_WGET is not set
+# CONFIG_WHICH is not set
+# CONFIG_WHOAMI is not set
+# CONFIG_YES is not set
+
+#
+# pending
+#
+# CONFIG_MKE2FS is not set
+# CONFIG_MKE2FS_JOURNAL is not set
+# CONFIG_MKE2FS_GEN is not set
+# CONFIG_MKE2FS_LABEL is not set
+# CONFIG_MKE2FS_EXTENDED is not set
+# CONFIG_NBD_CLIENT is not set
+# CONFIG_RESET is not set
+# CONFIG_SED is not set
+# CONFIG_SH is not set
+# CONFIG_SH_TTY is not set
+# CONFIG_SH_PROFILE is not set
+# CONFIG_SH_JOBCTL is not set
+# CONFIG_SH_FLOWCTL is not set
+# CONFIG_SH_QUOTES is not set
+# CONFIG_SH_WILDCARDS is not set
+# CONFIG_SH_PROCARGS is not set
+# CONFIG_SH_ENVVARS is not set
+# CONFIG_SH_LOCALS is not set
+# CONFIG_SH_ARRAYS is not set
+# CONFIG_SH_PIPES is not set
+# CONFIG_SH_BUILTINS is not set
+# CONFIG_EXIT is not set
+# CONFIG_CD is not set
+# CONFIG_CD_P is not set
+# CONFIG_XZCAT is not set
+
+#
+# Posix commands
+#
+# CONFIG_ASH is not set
+# CONFIG_ASH_TEST is not set
+# CONFIG_ASH_PRINTF is not set
+# CONFIG_ASH_KILL is not set
+# CONFIG_AWK is not set
+# CONFIG_BASENAME is not set
+# CONFIG_CAL is not set
+# CONFIG_CAT is not set
+# CONFIG_CHGRP is not set
+# CONFIG_CHMOD is not set
+# CONFIG_CKSUM is not set
+# CONFIG_CMP is not set
+# CONFIG_COMM is not set
+# CONFIG_CP is not set
+# CONFIG_CP_MORE is not set
+# CONFIG_CUT is not set
+# CONFIG_DATE is not set
+# CONFIG_DD is not set
+# CONFIG_DF is not set
+# CONFIG_DF_PEDANTIC is not set
+# CONFIG_DIRNAME is not set
+# CONFIG_DU is not set
+# CONFIG_ECHO is not set
+# CONFIG_EGREP is not set
+# CONFIG_ENV is not set
+# CONFIG_EXPAND is not set
+# CONFIG_FALSE is not set
+# CONFIG_FGREP is not set
+# CONFIG_GREP is not set
+# CONFIG_HEAD is not set
+# CONFIG_ID is not set
+# CONFIG_ID_GROUPS is not set
+# CONFIG_KILL is not set
+# CONFIG_LINK is not set
+# CONFIG_LN is not set
+# CONFIG_LOGGER is not set
+# CONFIG_LOGNAME is not set
+# CONFIG_LS is not set
+# CONFIG_MKDIR is not set
+# CONFIG_MKFIFO is not set
+# CONFIG_MV is not set
+# CONFIG_NICE is not set
+# CONFIG_NOHUP is not set
+# CONFIG_OD is not set
+# CONFIG_PATCH is not set
+# CONFIG_PRINTF is not set
+# CONFIG_PWD is not set
+# CONFIG_RM is not set
+# CONFIG_RMDIR is not set
+# CONFIG_SLEEP is not set
+# CONFIG_SLEEP_FLOAT is not set
+# CONFIG_SORT is not set
+# CONFIG_SORT_BIG is not set
+# CONFIG_SORT_FLOAT is not set
+# CONFIG_SPLIT is not set
+# CONFIG_STTY is not set
+# CONFIG_TAIL is not set
+# CONFIG_TAIL_SEEK is not set
+# CONFIG_TEE is not set
+# CONFIG_TEST is not set
+# CONFIG_TIME is not set
+# CONFIG_TOUCH is not set
+# CONFIG_TRUE is not set
+# CONFIG_TTY is not set
+# CONFIG_UNAME is not set
+# CONFIG_UNIQ is not set
+# CONFIG_UNLINK is not set
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+# CONFIG_WC is not set
+# CONFIG_WHO is not set
+# CONFIG_XARGS is not set
+
+#
+# 
+#
+
+#
+# Toybox global settings
+#
+# CONFIG_TOYBOX is not set
+CONFIG_TOYBOX_SUID=y
+CONFIG_TOYBOX_FLOAT=y
+CONFIG_TOYBOX_HELP=y
+CONFIG_TOYBOX_I18N=y
+# CONFIG_TOYBOX_FREE is not set
+# CONFIG_TOYBOX_DEBUG is not set
diff --git a/packaging/usrbin.links b/packaging/usrbin.links
new file mode 100644 (file)
index 0000000..66d567d
--- /dev/null
@@ -0,0 +1,2 @@
+dumpleases
+udhcpc
diff --git a/packaging/usrsbin.links b/packaging/usrsbin.links
new file mode 100644 (file)
index 0000000..baf2f78
--- /dev/null
@@ -0,0 +1 @@
+udhcpd
diff --git a/scripts/bloatcheck b/scripts/bloatcheck
new file mode 100644 (file)
index 0000000..a5a28f1
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+if [ $# -ne 2 ]
+then
+  echo "usage: bloatcheck old new"
+  exit 1
+fi
+
+addline()
+{
+  NEXT="$(printf "%s% $((50-${#LASTNAME}))d% 10d %10d" "$LASTNAME" "$OLD" "$NEW" "$DELTA")"
+  [ -z "$STUFF" ] &&
+    STUFF="$NEXT" ||
+    STUFF="$(printf "%s\n%s" "$STUFF" "$NEXT")"
+}
+
+diff <(nm --size-sort "$1" | sort -k3,3) \
+     <(nm --size-sort "$2" | sort -k3,3) | grep '^[<>]' | sort -k4,4 | \
+(
+LASTNAME=
+DELTA=0
+TOTAL=0
+OLD=0
+NEW=0
+STUFF=
+
+printf "name% 46s% 10s% 11s\n" old new delta
+echo "-----------------------------------------------------------------------"
+while read a b c d
+do
+  THISNAME=$(echo " $d" | sed 's/[.][0-9]*$//')
+
+  if [ "$LASTNAME" != "$THISNAME" ]
+  then
+    TOTAL=$(($TOTAL+$DELTA))
+    [ $DELTA -ne 0 ] && addline
+    LASTNAME="$THISNAME"
+    DELTA=0
+    OLD=0
+    NEW=0
+  fi
+
+  SIZE=$(printf "%d" "0x$b")
+  if [ "$a" == "<" ]
+  then
+    OLD=$(($OLD+$SIZE))
+    SIZE=$((-1*$SIZE))
+  else
+    NEW=$(($NEW+$SIZE))
+  fi
+  DELTA=$(($DELTA+$SIZE))
+done
+
+TOTAL=$(($TOTAL+$DELTA))
+[ $DELTA -ne 0 ] && addline
+
+echo "$STUFF" | sort -k4,4nr
+echo "-----------------------------------------------------------------------"
+printf "% 71d total\n" "$TOTAL"
+)
diff --git a/scripts/config2help.py b/scripts/config2help.py
new file mode 100755 (executable)
index 0000000..2573d08
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+import os,sys
+
+def zapquotes(str):
+  if str[0]=='"': str = str[1:str.rfind('"')]
+  return str
+
+def escapequotes(str):
+  return str.strip().replace("\\","\\\\").replace('"','\\"')
+
+helplen = morelines = 0
+out = sys.stdout
+
+def readfile(filename):
+  global helplen, morelines
+  #sys.stderr.write("Reading %s\n" % filename)
+  try:
+    lines = open(filename).read().split("\n")
+  except IOError:
+    sys.stderr.write("File %s missing\n" % filename)
+    return
+  config = None
+  description = None
+  for i in lines:
+    if helplen:
+      i = i.expandtabs()
+      if not len(i) or i[:helplen].isspace():
+        if morelines: out.write('\\n')
+        morelines = 1
+        out.write(escapequotes(i))
+        continue
+      else:
+        helplen = morelines = 0
+        out.write('"\n')
+
+    words = i.strip().split(None,1)
+    if not len(words): continue
+
+    if words[0] in ("config", "menuconfig"):
+      config = words[1]
+      description = ""
+    elif words[0] in ("bool", "boolean", "tristate", "string", "hex", "int"):
+       if len(words)>1: description = zapquotes(words[1])
+    elif words[0]=="prompt":
+      description = htmlescape(zapquotes(words[1]))
+    elif words[0] in ("help", "---help---"):
+      out.write('#define help_%s "' % config.lower())
+      helplen = len(i[:i.find(words[0])].expandtabs())
+    elif words[0] == "source": readfile(zapquotes(words[1]))
+    elif words[0] in ("default","depends", "select", "if", "endif", "#", "comment", "menu", "endmenu"): pass
+
+readfile(sys.argv[1])
+if helplen: out.write('"\n')
diff --git a/scripts/config2help.sh b/scripts/config2help.sh
new file mode 100755 (executable)
index 0000000..8f96e39
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash -e
+
+function firstmajor() {
+       declare -i n j=$1
+       test $j -gt 0 || return -1
+       for j in $@; do
+               if [ $j -gt $1 ]; then
+                       echo $j
+                       return 0
+               fi
+       done
+       return 1
+}; export -f firstmajor
+
+function print_h() {
+       declare -i i c=$2 s=$3 e=$4
+       local str="$(echo "$1" | head -n$c | tail -n1 | sed -e "s,config[\t ]*,,")"
+       echo -n "#define help_"$str" \"" | tr [A-Z] [a-z]
+       echo -n "$1\\n" | head -n$e | tail -n$[e-s+1] | sed -e "s,\$,\r," | tr \\n\\r n\\
+       echo \"
+}; export -f print_h
+
+file="$1"
+if test "$0" != "bash"; then
+       if test -r "$file"; then
+#              echo "$file..." >&2
+               filetxt="$(sed -e "s,^[\t ]*,," -e "s,\([\"\\\\]\),\\\\\\1,g" "$file")"
+               helps=$(echo "$filetxt" | egrep -ne "^help *" -e "^---help--- *" | cut -d\:  -f1)
+               configs=$(echo "$filetxt" | egrep -ne "^config *" | cut -d\:  -f1)
+               endmenus=$(echo "$filetxt" | egrep -ne "^endmenu *" | cut -d\:  -f1)
+               let last=$(echo "$filetxt" | wc -l)+2
+
+               declare -i i c s e
+               for i in $configs; do
+#                      echo -n "c:$i" >&2
+                       s=$(firstmajor $i $helps)
+                       test $s -gt 0
+                       e=$(firstmajor $s $configs || firstmajor $s $endmenus $last)
+                       let s++ e-=2
+                       test $e -ge $s
+#                      echo " s:$s e:$e" >&2
+                       print_h "$filetxt" $i $s $e
+               done
+               for fle in $(cat "$file" | egrep -e "^[ \t]*source " | sed -e "s,^[ \t]*source *\(.*\),\\1,"); do
+                       $0 $fle
+               done
+       else
+               echo
+               echo "USAGE EXAMPLE: $(basename $0) Config.in > generated/help.h"
+               echo
+               false
+       fi | sed -e "s,\\\\n\\\\n\"$,\\\\n\","
+fi
+
diff --git a/scripts/findglobals.sh b/scripts/findglobals.sh
new file mode 100755 (executable)
index 0000000..2c63164
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Quick and dirty check to see if anybody's leaked global variables.
+# We should have this, toy_list, toybuf, and toys.
+
+nm toybox_unstripped | grep '[0-9A-Fa-f]* [BCDGRS]' | cut -d ' ' -f 3
diff --git a/scripts/genconfig.sh b/scripts/genconfig.sh
new file mode 100755 (executable)
index 0000000..71bc609
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# This has to be a separate file from scripts/make.sh so it can be called
+# before menuconfig.  (It's called again from scripts/make.sh just to be sure.)
+
+mkdir -p generated
+
+source configure
+
+probeconfig()
+{
+  # Probe for container support on target
+
+  echo -e "# container support\nconfig TOYBOX_CONTAINER\n\tbool" || return 1
+  ${CROSS_COMPILE}${CC} $CFLAGS -xc -o /dev/null - 2>/dev/null << EOF
+    #include <linux/sched.h>
+    int x=CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET;
+
+    int main(int argc, char *argv[]) { return unshare(x); }
+EOF
+  [ $? -eq 0 ] && DEFAULT=y || DEFAULT=n
+  rm a.out 2>/dev/null
+  echo -e "\tdefault $DEFAULT\n" || return 1
+}
+
+genconfig()
+{
+  # I could query the directory here, but I want to control the order
+  # and capitalization in the menu
+  for j in toys/*/README
+  do
+    DIR="$(dirname "$j")"
+
+    [ $(ls "$DIR" | wc -l) -lt 2 ] && continue
+
+    echo "menu \"$(head -n 1 $j)\""
+    echo
+
+    # extract config stanzas from each source file, in alphabetical order
+    for i in $(ls -1 $DIR/*.c)
+    do
+      # Grab the config block for Config.in
+      echo "# $i"
+      sed -n '/^\*\//q;/^config [A-Z]/,$p' $i || return 1
+      echo
+    done
+
+    echo endmenu
+  done
+}
+
+headerprobes()
+{
+  ${CROSS_COMPILE}${CC} $CFLAGS -xc -o /dev/null - 2>/dev/null << EOF
+    #include <fcntl.h>
+    #ifndef O_NOFOLLOW
+    #error posix 2008 was a while ago now
+    #endif
+EOF
+  if [ $? -ne 0 ]
+  then
+    rm -f a.out
+    ${CROSS_COMPILE}${CC} $CFLAGS -xc - 2>/dev/null << EOF
+      #include <stdio.h>
+      #include <sys/types.h>
+      #include <asm/fcntl.h>
+
+      int main(int argc, char *argv[])
+      {
+        printf("0x%x\n", O_NOFOLLOW);
+      }
+EOF
+    X=$(./a.out) 2>/dev/null
+    rm -f a.out
+    echo "#define O_NOFOLLOW ${X:-0}"
+  fi
+}
+
+probeconfig > generated/Config.probed || rm generated/Config.probed
+genconfig > generated/Config.in || rm generated/Config.in
+headerprobes > generated/portability.h || rm generated/portability.h
diff --git a/scripts/install.c b/scripts/install.c
new file mode 100644 (file)
index 0000000..cda8fc2
--- /dev/null
@@ -0,0 +1,39 @@
+/* Wrapper to make installation easier with cross-compiling.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "toys.h"
+
+#undef NEWTOY
+#undef OLDTOY
+#define NEWTOY(name, opts, flags) {#name, 0, opts, flags},
+#define OLDTOY(name, oldname, opts, flags) {#name, 0, opts, flags},
+
+// Populate toy_list[].
+
+struct toy_list toy_list[] = {
+#include "generated/newtoys.h"
+};
+
+#define TOY_LIST_LEN (sizeof(toy_list)/sizeof(struct toy_list))
+
+int main(int argc, char *argv[])
+{
+  static char *toy_paths[]={"usr/","bin/","sbin/",0};
+  int i, len = 0;
+
+  // Output list of applets.
+  for (i=1; i<TOY_LIST_LEN; i++) {
+    int fl = toy_list[i].flags;
+    if (fl & TOYMASK_LOCATION) {
+      if (argc>1) {
+        int j;
+        for (j=0; toy_paths[j]; j++)
+          if (fl & (1<<j)) len += printf("%s", toy_paths[j]);
+      }
+      len += printf("%s\n",toy_list[i].name);
+    }
+  }
+  return 0;
+}
diff --git a/scripts/install.sh b/scripts/install.sh
new file mode 100755 (executable)
index 0000000..d9b5656
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# Grab default values for $CFLAGS and such.
+
+source ./configure
+
+# Parse command line arguments.
+
+[ -z "$PREFIX" ] && PREFIX="."
+
+LONG_PATH=""
+while [ ! -z "$1" ]
+do
+  # Create symlinks instead of hardlinks?
+
+  [ "$1" == "--symlink" ] && LINK_TYPE="-s"
+
+  # Uninstall?
+
+  [ "$1" == "--uninstall" ] && UNINSTALL=1
+
+  # Delete destination command if it exists?
+
+  [ "$1" == "--force" ] && DO_FORCE="-f"
+
+  # Use {,usr}/{bin,sbin} paths instead of all files in one directory?
+
+  if [ "$1" == "--long" ]
+  then
+    LONG_PATH="bin/"
+  fi
+
+  shift
+done
+
+echo "Compile instlist..."
+
+$DEBUG $HOSTCC -I . scripts/install.c -o instlist || exit 1
+COMMANDS="$(./instlist $LONG_PATH)"
+
+echo "Install commands..."
+
+# Copy toybox itself
+
+if [ -z "$UNINSTALL" ]
+then
+  mkdir -p ${PREFIX}/${LONG_PATH} || exit 1
+  cp toybox ${PREFIX}/${LONG_PATH} || exit 1
+else
+  rm "${PREFIX}/${LONG_PATH}/toybox" 2>/dev/null
+  rmdir "${PREFIX}/${LONG_PATH}" 2>/dev/null
+fi
+cd "${PREFIX}"
+
+# Make links to toybox
+
+for i in $COMMANDS
+do
+  # Figure out target of link
+
+  if [ -z "$LONG_PATH" ]
+  then
+    DOTPATH=""
+  else
+    # Create subdirectory for command to go in (if necessary)
+
+    DOTPATH="$(echo $i | sed 's@\(.*/\).*@\1@')"
+    if [ -z "$UNINSTALL" ]
+    then
+      mkdir -p "$DOTPATH" || exit 1
+    else
+      rmdir "$DOTPATH" 2>/dev/null
+    fi
+
+    if [ -z "$LINK_TYPE" ]
+    then
+      dotpath="bin/"
+    else
+      if [ "$DOTPATH" != "$LONG_PATH" ]
+      then
+        DOTPATH="$(echo $DOTPATH | sed -e 's@[^/]*/@../@g')"$LONG_PATH
+      else
+        DOTPATH=""
+      fi
+    fi
+  fi
+
+  # Create link
+  [ -z "$UNINSTALL" ] &&
+    ln $DO_FORCE $LINK_TYPE ${DOTPATH}toybox $i ||
+    rm $i 2>/dev/null
+done
diff --git a/scripts/make.sh b/scripts/make.sh
new file mode 100755 (executable)
index 0000000..7feb6d7
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+# Grab default values for $CFLAGS and such.
+
+export LANG=c
+source ./configure
+
+if [ -z ".config" ]
+then
+  echo "No .config (see "make help" for configuration options)."
+  exit 1
+fi
+
+echo "Make generated/config.h from .config."
+
+# This long and roundabout sed invocation is to make old versions of sed happy.
+# New ones have '\n' so can replace one line with two without all the branches
+# and tedious mucking about with hold space.
+
+sed -n \
+  -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \
+  -e 't notset' \
+  -e 's/^CONFIG_\(.*\)=y.*/\1/' \
+  -e 't isset' \
+  -e 's/^CONFIG_\([^=]*\)=\(.*\)/#define CFG_\1 \2/p' \
+  -e 'd' \
+  -e ':notset' \
+  -e 'h' \
+  -e 's/.*/#define CFG_& 0/p' \
+  -e 'g' \
+  -e 's/.*/#define USE_&(...)/p' \
+  -e 'd' \
+  -e ':isset' \
+  -e 'h' \
+  -e 's/.*/#define CFG_& 1/p' \
+  -e 'g' \
+  -e 's/.*/#define USE_&(...) __VA_ARGS__/p' \
+  .config > generated/config.h || exit 1
+
+
+echo "Extract configuration information from toys/*.c files..."
+scripts/genconfig.sh
+
+echo "Generate headers from toys/*/*.c..."
+
+# Create a list of all the applets toybox can provide.  Note that the first
+# entry is out of order on purpose (the toybox multiplexer applet must be the
+# first element of the array).  The rest must be sorted in alphabetical order
+# for fast binary search.
+
+echo "generated/newtoys.h"
+
+echo "NEWTOY(toybox, NULL, TOYFLAG_STAYROOT)" > generated/newtoys.h
+sed -n -e 's/^USE_[A-Z0-9_]*(/&/p' toys/*/*.c \
+       | sed 's/\(.*TOY(\)\([^,]*\),\(.*\)/\2 \1\2,\3/' | sort -k 1,1 \
+       | sed 's/[^ ]* //'  >> generated/newtoys.h
+
+# Extract list of command letters from processed header file
+
+function getflags()
+{
+  sed -n -e "s/.*TOY($1"',[ \t]*"\([^"]*\)"[ \t]*,.*)/\1/' \
+         -e 't keep;d;:keep' -e 's/^[<>=][0-9]//' -e 's/[?&^]//' \
+         -e 't keep' -e 's/[><=][0-9][0-9]*//g' -e 's/+.//g' \
+         -e 's/([^)]*)//g' -e 's/\[[^]]*\]//g' -e 's/[-?^:&#|@*]//g' -e 'p'
+}
+
+# Extract global structure definitions and flag definitions from toys/*/*.c
+
+function getglobals()
+{
+  # Run newtoys.h through the compiler's preprocessor to resolve USE macros
+  # against current config.
+  NEWTOYS="$(cat generated/config.h generated/newtoys.h | $CC -E - | sed 's/" *"//g')"
+
+  # Grab allyesconfig for comparison
+  ALLTOYS="$((sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h && cat generated/newtoys.h) | $CC -E - | sed 's/" *"//g')"
+
+  for i in toys/*/*.c
+  do
+    NAME="$(echo $i | sed 's@.*/\(.*\)\.c@\1@')"
+
+    echo -e "// $i\n"
+    sed -n -e '/^GLOBALS(/,/^)/b got;b;:got' \
+        -e 's/^GLOBALS(/struct '"$NAME"'_data {/' \
+        -e 's/^)/};/' -e 'p' $i
+
+    FLAGS="$(echo "$NEWTOYS" | getflags "$NAME")"
+    ZFLAGS="$(echo "$ALLTOYS" | getflags "$NAME" | sed 's/[-'"$FLAGS"']//g')"
+
+    echo "#ifdef FOR_${NAME}"
+    X=$((${#FLAGS}-1))
+    C=0
+    while [ $X -ge 0 ]
+    do
+      if [ "${FLAGS:$X:1}" != " " ]; then
+        echo -ne "#define FLAG_${FLAGS:$X:1}\t"
+        X=$(($X-1))
+        echo "(1<<$(($C)))"
+        C=$(($C+1))
+      else
+        X=$(($X-1))
+      fi
+    done
+    X=0
+    while [ $X -lt ${#ZFLAGS} ]
+    do
+      echo "#define FLAG_${ZFLAGS:$X:1} 0"
+      X=$(($X+1))
+    done
+    echo "#define TT this.${NAME}"
+    echo "#endif"
+  done
+}
+
+echo "generated/globals.h"
+
+GLOBSTRUCT="$(getglobals)"
+(
+  echo "$GLOBSTRUCT"
+  echo
+  echo "extern union global_union {"
+  echo "$GLOBSTRUCT" | sed -n 's/struct \(.*\)_data {/ struct \1_data \1;/p'
+  echo "} this;"
+) > generated/globals.h
+
+echo "generated/help.h"
+# Only recreate generated/help.h if python is installed
+if [ ! -z "$(which python)" ] && [ ! -z "$(grep 'CONFIG_TOYBOX_HELP=y' .config)" ]
+then
+  echo "Extract help text from Config.in."
+  scripts/config2help.py Config.in > generated/help.h || exit 1
+fi
+
+# Extract a list of toys/*/*.c files to compile from the data in ".config":
+
+# 1) Get a list of C files in toys/* and glue them together into a regex we can
+# feed to grep that will match any one of them (whole word, not substring).
+TOYFILES="^$(ls toys/*/*.c | sed -n 's@^.*/\(.*\)\.c$@\1@;s/-/_/g;H;${g;s/\n//;s/\n/$|^/gp}')\$"
+
+# 2) Grab the XXX part of all CONFIG_XXX entries, removing everything after the
+# second underline
+# 3) Sort the list, keeping only one of each entry.
+# 4) Convert to lower case.
+# 5) Remove any config symbol not recognized as a filename from step 1.
+# 6) Add "toys/*/" prefix and ".c" suffix.
+
+TOYFILES=$(sed -nre 's/^CONFIG_(.*)=y/\1/p' < .config \
+  | sort -u | tr A-Z a-z | grep -E "$TOYFILES" | sed 's@\(.*\)@toys/\*/\1.c@')
+
+echo "Library probe..."
+
+# We trust --as-needed to remove each library if we don't use any symbols
+# out of it, this loop is because the compiler has no way to ignore a library
+# that doesn't exist, so we have to detect and skip nonexistent libraries
+# for it.
+
+OPTLIBS="$(for i in util crypt m tirpc pthread; do echo "int main(int argc, char *argv[]) {return 0;}" | ${CROSS_COMPILE}${CC} -xc - -o /dev/null -Wl,--as-needed -l$i > /dev/null 2>/dev/null && echo -l$i; done)"
+
+echo "Compile toybox..."
+
+do_loudly()
+{
+  [ ! -z "$V" ] && echo "$@"
+  "$@"
+}
+
+do_loudly ${CROSS_COMPILE}${CC} $CFLAGS -I . -o toybox_unstripped $OPTIMIZE \
+  main.c lib/*.c $TOYFILES -Wl,--as-needed $OPTLIBS  || exit 1
+do_loudly ${CROSS_COMPILE}${STRIP} toybox_unstripped -o toybox || exit 1
+# gcc 4.4's strip command is buggy, and doesn't set the executable bit on
+# its output the way SUSv4 suggests it do so.
+do_loudly chmod +x toybox || exit 1
diff --git a/scripts/minicom.sh b/scripts/minicom.sh
new file mode 100755 (executable)
index 0000000..1a9b0d8
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# If you want to use toybox netcat to talk to a serial port, use this.
+
+if [ ! -c "$1" ]
+then
+  echo "Usage: minicom.sh /dev/ttyS0"
+  exit 1
+fi
+
+SPEED="$2"
+[ -z "$SPEED" ] && SPEED=115200
+
+stty $SPEED -F "$1"
+stty raw -echo -ctlecho -F "$1"
+stty raw -echo  # Need to do it on stdin, too.
+./toybox netcat -f "$1"
+stty cooked echo  # Put stdin back.
diff --git a/scripts/mkstatus.py b/scripts/mkstatus.py
new file mode 100755 (executable)
index 0000000..ec225ed
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+
+# Create status.html
+
+import subprocess,sys
+
+def readit(args):
+  ret={}
+  arr=[]
+  blob=subprocess.Popen(args, stdout=subprocess.PIPE, shell=False)
+  for i in blob.stdout.read().split("\n"):
+    if not i: continue
+    i=i.split()
+    ret[i[0]]=i[1:]
+    arr.extend(i)
+  return ret,arr
+
+stuff,blah=readit(["sed","-n", 's/<span id=\\([a-z_]*\\)>/\\1 /;t good;d;:good;h;:loop;n;s@</span>@@;t out;H;b loop;:out;g;s/\\n/ /g;p', "www/roadmap.html", "www/status.html"])
+
+blah,toystuff=readit(["./toybox"])
+
+reverse={}
+for i in stuff:
+  for j in stuff[i]:
+    try: reverse[j].append(i)
+    except: reverse[j]=[i]
+
+for i in toystuff:
+  try:
+    if ("ready" in reverse[i]) or ("pending" in reverse[i]): continue
+  except: pass
+  print i
+
+pending=[]
+done=[]
+
+print "all commands=%s" % len(reverse)
+
+outfile=open("www/status.gen", "w")
+outfile.write("<a name=all><h2><a href=#all>All commands</a></h2><blockquote><p>\n")
+
+blah=list(reverse)
+blah.sort()
+for i in blah:
+  out=i
+  if "posix" in reverse[i]: out='[<a href="http://pubs.opengroup.org/onlinepubs/9699919799/utilities/%s.html">%s</a>]' % (i,out)
+  elif "lsb" in reverse[i]: out='&lt;<a href="http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/%s.html">%s</a>&gt;' % (i,out)
+  elif "development" in reverse[i]: out='(<a href="http://linux.die.net/man/1/%s">%s</a>)' % (i,out)
+  elif "toolbox" in reverse[i]: out='{%s}' % out
+  elif "klibc_cmd" in reverse[i]: out='=%s=' % out
+  elif "sash_cmd" in reverse[i]: out='#%s#' % out
+  elif "sbase_cmd" in reverse[i]: out='@%s@' % out
+  elif "beastiebox_cmd" in reverse[i]: out='*%s*' % out
+  elif "request" in reverse[i]: out='+<a href="http://linux.die.net/man/1/%s">%s</a>+' % (i,out)
+  elif "ready" in reverse[i]: pass
+  else: sys.stderr.write("unknown %s %s\n" % (i, reverse[i]))
+  if "ready" in reverse[i] or "pending" in reverse[i]:
+    done.append(out)
+    out='<strike>%s</strike>' % out
+  else: pending.append(out)
+
+  outfile.write(out+"\n")
+
+print "done=%s" % len(done)
+outfile.write("</p></blockquote>\n")
+
+outfile.write("<a name=todo><h2><a href=#todo>TODO</a></h2><blockquote><p>%s</p></blockquote>\n" % "\n".join(pending))
+outfile.write("<a name=done><h2><a href=#done>Done</a></h2><blockquote><p>%s</p></blockquote>\n" % "\n".join(done))
diff --git a/scripts/showasm b/scripts/showasm
new file mode 100644 (file)
index 0000000..75a6efd
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Copyright 2006 Rob Landley <rob@landley.net>
+
+# Dumb little utility function to print out the assembly dump of a single
+# function, or list the functions so dumpable in an executable.  You'd think
+# there would be a way to get objdump to do this, but I can't find it.
+
+[ $# -lt 1 ] || [ $# -gt 2 ] && { echo "usage: showasm file function"; exit 1; }
+
+[ ! -f $1 ] && { echo "File $1 not found"; exit 1; }
+
+if [ $# -eq 1 ]
+then
+  objdump -d $1 | sed -n -e 's/^[0-9a-fA-F]* <\(.*\)>:$/\1/p'
+  exit 0
+fi
+
+objdump -d $1 | sed -n -e '/./{H;$!d}' -e "x;/^.[0-9a-fA-F]* <$2>:/p"
+
diff --git a/scripts/test.sh b/scripts/test.sh
new file mode 100755 (executable)
index 0000000..19f860b
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+[ -z "$TOPDIR" ] && TOPDIR="$(pwd)"
+
+rm -rf testdir
+mkdir -p testdir
+
+if [ -z "$TEST_HOST" ]
+then
+  make install_flat PREFIX=testdir || exit 1
+fi
+
+cd testdir
+PATH="$(pwd):$PATH"
+
+. "$TOPDIR"/scripts/test/testing.sh
+[ -f "$TOPDIR/generated/config.h" ] && export OPTIONFLAGS=:$(echo $(sed -nr 's/^#define CFG_(.*) 1/\1/p' "$TOPDIR/generated/config.h") | sed 's/ /:/g')
+
+if [ $# -ne 0 ]
+then
+  for i in "$@"
+  do
+    ln -sf toybox $i
+    . "$TOPDIR"/scripts/test/$i.test
+  done
+else
+  for i in "$TOPDIR"/scripts/test/*.test
+  do
+    CMDNAME="$(echo "$i" | sed 's@.*/\(.*\)\.test@\1@')"
+    if [ -h $CMDNAME ] || [ ! -z "$TEST_HOST" ]
+    then
+      . $i
+    else
+      echo "$CMDNAME disabled"
+    fi
+  done
+fi
diff --git a/scripts/test/basename.test b/scripts/test/basename.test
new file mode 100644 (file)
index 0000000..2f7a2ec
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Removal of extra /'s
+testing "basename /-only" "basename ///////" "/\n" "" ""
+testing "basename trailing /" "basename a//////" "a\n" "" ""
+testing "basename combined" "basename /////a///b///c///d/////" "d\n" "" ""
+
+# Standard suffix behavior.
+testing "basename suffix" "basename a/b/c/d.suffix .suffix" "d\n" "" ""
+
+# A suffix cannot be the entire result.
+testing "basename suffix=result" "basename .txt .txt" ".txt\n" "" ""
+
+# Deal with suffix appearing in the filename
+testing "basename reappearing suffix 1" "basename a.txt.txt .txt" "a.txt\n" "" ""
+testing "basename reappearing suffix 2" "basename a.txt.old .txt" "a.txt.old\n" "" ""
+
+# A suffix should be a real suffix, only a the end.
+testing "basename invalid suffix" "basename isthisasuffix? suffix" "isthisasuffix?\n" "" ""
diff --git a/scripts/test/cat.test b/scripts/test/cat.test
new file mode 100644 (file)
index 0000000..3d5842a
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+echo "one" > file1
+echo "two" > file2
+testing "cat" "cat && echo yes" "oneyes\n" "" "one"
+testing "cat -" "cat - && echo yes" "oneyes\n" "" "one"
+testing "cat file1 file2" "cat file1 file2" "one\ntwo\n"  "" ""
+testing "cat - file"      "cat - file1"     "zero\none\n" "" "zero\n"
+testing "cat file -"      "cat file1 -"     "one\nzero\n" "" "zero\n"
+
+testing "cat file1 notfound file2" \
+        "cat file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
+        "one\ntwo\ncat: notfound: No such file or directory\n" "" ""
+
+testing "cat file1" \
+        "cat /proc/self/exe > file1 && cmp /proc/self/exe file1 && echo yes" \
+        "yes\n" "" ""
+
+testing "cat - file1" \
+        "cat - file1 | diff -a -U 0 - file1 | tail -n 1" \
+        "-hello\n" "" "hello\n"
+
+testing "cat > /dev/full" \
+        "cat - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
+        "cat: xwrite: No space left on device\n" "" "zero\n"
+
+rm file1 file2
\ No newline at end of file
diff --git a/scripts/test/chgrp.test b/scripts/test/chgrp.test
new file mode 100644 (file)
index 0000000..2af9385
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+if [ "$(id -u)" -ne 0 ]
+then
+  echo "SKIPPED: chgrp (not root)"
+  continue 2>/dev/null
+  exit
+fi
+
+# We chgrp between "root" and the last group in /etc/group.
+
+GRP="$(sed -n '$s/:.*//p' /etc/group)"
+
+# Set up a little testing hierarchy
+
+rm -rf testdir &&
+mkdir -p testdir/dir/dir/dir testdir/dir2 &&
+touch testdir/dir/file &&
+ln -s ../dir/dir testdir/dir2/dir &&
+ln -s ../dir/file testdir/dir2/file || exit 1
+
+# Wrapper to reset groups and return results
+
+IN="cd testdir && chgrp -R $GRP dir dir2 &&"
+OUT="&& cd .. && echo \$(ls -lR testdir | awk '{print \$4}')"
+
+# The groups returned by $OUT are, in order:
+# dir dir2 dir/dir dir/file dir/dir/dir dir2/dir dir2/file
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Basic smoketest
+testing "chgrp dir" "$IN chgrp root dir $OUT" \
+       "root $GRP $GRP $GRP $GRP $GRP $GRP\n" "" ""
+testing "chgrp file" "$IN chgrp root dir/file $OUT" \
+       "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
+testing "chgrp dir and file" "$IN chgrp root dir dir/file $OUT" \
+       "root $GRP $GRP root $GRP $GRP $GRP\n" "" ""
+
+# symlinks (affect target, not symlink)
+testing "chgrp symlink->file" "$IN chgrp root dir2/file $OUT" \
+       "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
+testing "chgrp symlink->dir" "$IN chgrp root dir2/dir $OUT" \
+       "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
+testing "chgrp -h symlink->dir" "$IN chgrp -h root dir2/dir $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
+
+# What does -h do (affect symlink, not target)
+testing "chgrp -h symlink->file" "$IN chgrp -h root dir2/file $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP $GRP root\n" "" ""
+testing "chgrp -h symlink->dir" "$IN chgrp -h root dir2/dir $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
+
+# chgrp -R (note, -h is implied by -R)
+
+testing "chgrp -R dir" "$IN chgrp -R root dir $OUT" \
+       "root $GRP root root root $GRP $GRP\n" "" ""
+testing "chgrp -R dir2" "$IN chgrp -R root dir2 $OUT" \
+       "$GRP root $GRP $GRP $GRP root root\n" "" ""
+testing "chgrp -R symlink->dir" "$IN chgrp -R root dir2/dir $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
+testing "chgrp -R symlink->file" "$IN chgrp -R root dir2/file $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP $GRP root\n" "" ""
+
+# chgrp -RP (same as -R by itself)
+
+testing "chgrp -RP dir2" "$IN chgrp -RP root dir2 $OUT" \
+       "$GRP root $GRP $GRP $GRP root root\n" "" ""
+testing "chgrp -RP symlink->dir" "$IN chgrp -RP root dir2/dir $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
+testing "chgrp -RP symlink->file" "$IN chgrp -RP root dir2/file $OUT" \
+       "$GRP $GRP $GRP $GRP $GRP $GRP root\n" "" ""
+
+# chgrp -RH (change target but only recurse through symlink->dir on cmdline)
+
+testing "chgrp -RH dir2" "$IN chgrp -RH root dir2 $OUT" \
+       "$GRP root root root $GRP $GRP $GRP\n" "" ""
+testing "chgrp -RH symlink->dir" "$IN chgrp -RH root dir2/dir $OUT" \
+       "$GRP $GRP root $GRP root $GRP $GRP\n" "" ""
+testing "chgrp -RH symlink->file" "$IN chgrp -RH root dir2/file $OUT" \
+       "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
+
+# chgrp -RL (change target and always recurse through symlink->dir)
+
+testing "chgrp -RL dir2" "$IN chgrp -RL root dir2 $OUT" \
+       "$GRP root root root root $GRP $GRP\n" "" ""
+testing "chgrp -RL symlink->dir" "$IN chgrp -RL root dir2/dir $OUT" \
+       "$GRP $GRP root $GRP root $GRP $GRP\n" "" ""
+testing "chgrp -RL symlink->file" "$IN chgrp -RL root dir2/file $OUT" \
+       "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
+
+# -HLP are NOPs without -R
+testing "chgrp -H without -R" "$IN chgrp -H root dir2/dir $OUT" \
+       "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
+testing "chgrp -L without -R" "$IN chgrp -L root dir2/dir $OUT" \
+       "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
+testing "chgrp -P without -R" "$IN chgrp -P root dir2/dir $OUT" \
+       "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
+
+rm -rf testdir
diff --git a/scripts/test/cksum.test b/scripts/test/cksum.test
new file mode 100644 (file)
index 0000000..152dc12
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Default behavior on stdin and on files.
+testing "cksum on stdin" "echo -n hello | cksum" "3287646509 5\n" "" ""
+echo -n "hello" > tmpfile
+testing "cksum on file" "cksum tmpfile" "3287646509 5 tmpfile\n" "" ""
+rm -f tmpfile
+touch one two
+testing "cksum on multiple files" "cksum one two" "4294967295 0 one\n4294967295 0 two\n" "" ""
+rm -f one two
+
+# Check the length suppression, both calculate the CRC on 'abc' but the second
+# option has length suppression on and has the length concatenated to 'abc'.
+testing "cksum on abc including length" "echo -n 'abc' | cksum" "1219131554 3\n" "" ""
+testing "cksum on abc excluding length" "echo -ne 'abc\x3' | cksum -N" "1219131554 4\n" "" ""
+
+# cksum on no contents gives 0xffffffff (=4294967295)
+testing "cksum on no data post-inversion" "echo -n "" | cksum" "4294967295 0\n" "" ""
+# If we do preinversion we will then get 0.
+testing "cksum on no data pre+post-inversion" "echo -n "" | cksum -P" "0 0\n" "" ""
+# If we skip the post-inversion we also get 0
+testing "cksum on no data no inversion" "echo -n "" | cksum -I" "0 0\n" "" ""
+# Two wrongs make a right.
+testing "cksum on no data pre-inversion" "echo -n "" | cksum -PI" "4294967295 0\n" "" ""
diff --git a/scripts/test/cmp.test b/scripts/test/cmp.test
new file mode 100644 (file)
index 0000000..ef53b37
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+testing "cmp not enough arguments [fail]" "cmp input 2>/dev/null || echo yes" "yes\n" "foo" ""
+testing "cmp missing file1 [fail]" "cmp file1 input 2>/dev/null || echo yes" "yes\n" "foo" ""
+
+#mkdir dir
+#testing "cmp directory [fail]" "cmp dir dir 2>/dev/null || echo yes" \
+       #"yes\n" "" ""
+#rmdir dir
+
+echo "ab
+c" > input2
+
+testing "cmp identical files, stdout" "cmp input input2" "" "ab\nc\n" ""
+testing "cmp identical files, return code" "cmp input input2 && echo yes" "yes\n" "ab\nc\n" ""
+
+testing "cmp EOF, stderr" "cmp input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
+testing "cmp EOF, return code" "cmp input input2 2>/dev/null || echo yes" "yes\n" "ab\nc\nx" ""
+# The gnu/dammit version fails this because posix says "char" and they don't.
+testing "cmp diff, stdout" "cmp input input2" "input input2 differ: char 4, line 2\n" "ab\nx\nx" ""
+testing "cmp diff, return code" "cmp input input2 > /dev/null || echo yes" "yes\n" "ab\nx\nx" ""
+
+testing "cmp -s EOF, return code" "cmp -s input input2 || echo yes"  "yes\n" "ab\nc\nx" ""
+testing "cmp -s diff, return code" "cmp -s input input2 || echo yes" "yes\n" "ab\nx\nx" ""
+
+testing "cmp -l EOF, stderr" "cmp -l input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
+testing "cmp -l diff and EOF, stdout and stderr" "cmp -l input input2 2>&1 | sort" "4 170 143\ncmp: EOF on input2\n" "ab\nx\nx" ""
+
+rm input2
+
+testing "cmp stdin and file" "cmp input -" "input - differ: char 4, line 2\n" "ab\nc\n" "ab\nx\n"
+#testing "cmp stdin and stdin" "cmp input -" "" "" "ab\nc\n"
+
diff --git a/scripts/test/cp.test b/scripts/test/cp.test
new file mode 100644 (file)
index 0000000..eea0471
--- /dev/null
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+# Create test file
+dd if=/dev/urandom of=random bs=64 count=1 2> /dev/null
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "cp not enough arguments [fail]" "cp one 2>/dev/null || echo yes" \
+       "yes\n" "" ""
+testing "cp -missing source [fail]" "cp missing two 2>/dev/null || echo yes" \
+       "yes\n" "" ""
+testing "cp file->file" "cp random two && cmp random two && echo yes" \
+       "yes\n" "" ""
+rm two
+
+mkdir two
+testing "cp file->dir" "cp random two && cmp random two/random && echo yes" \
+       "yes\n" "" ""
+rm two/random
+testing "cp file->dir/file" \
+       "cp random two/random && cmp random two/random && echo yes" \
+       "yes\n" "" ""
+testing "cp -r dir->missing" \
+       "cp -r two three && cmp random three/random && echo yes" \
+       "yes\n" "" ""
+touch walrus
+testing "cp -r dir->file [fail]" \
+       "cp -r two walrus 2>/dev/null || echo yes" "yes\n" "" ""
+touch two/three
+testing "cp -r dir hits file." \
+       "cp -r three two 2>/dev/null || echo yes" "yes\n" "" ""
+rm -rf two three walrus
+
+touch two
+chmod 000 two
+testing "cp file->inaccessable [fail]" \
+       "cp random two 2>/dev/null || echo yes" "yes\n" "" ""
+rm -f two
+
+touch two
+chmod 000 two
+testing "cp -f file->inaccessable" \
+       "cp -f random two && cmp random two && echo yes" "yes\n" "" ""
+mkdir sub
+chmod 000 sub
+testing "cp file->inaccessable_dir [fail]" \
+       "cp random sub 2>/dev/null || echo yes" "yes\n" "" ""
+rm two
+rmdir sub
+
+mkdir dir
+touch file
+testing "cp -rf dir file [fail]" "cp -rf dir file 2>/dev/null || echo yes" \
+       "yes\n" "" ""
+rm -rf dir file
+
+touch one two
+testing "cp file1 file2 missing [fail]" \
+       "cp one two missing 2>/dev/null || echo yes" "yes\n" "" ""
+mkdir dir
+testing "cp dir file missing [fail]" \
+       "cp dir two missing 2>/dev/null || echo yes" "yes\n" "" ""
+testing "cp -rf dir file missing [fail]" \
+       "cp dir two missing 2>/dev/null || echo yes" "yes\n" "" ""
+testing "cp file1 file2 file [fail]" \
+       "cp random one two 2>/dev/null || echo yes" "yes\n" "" ""
+testing "cp file1 file2 dir" \
+       "cp random one dir && cmp random dir/random && cmp one dir/one && echo yes" \
+       "yes\n" "" ""
+rm one two random
+rm -rf dir
+
+mkdir -p one/two/three/four
+touch one/two/three/five
+touch one/{six,seven,eight}
+testing "cp -r /abspath dest" \
+       "cp -r \"$(readlink -f one)\" dir && diff -r one dir && echo yes" \
+       "yes\n" "" ""
+testing "cp -r dir again" "cp -r one/. dir && diff -r one dir && echo yes" \
+       "yes\n" "" ""
+mkdir dir2
+testing "cp -r dir1/* dir2" \
+       "cp -r one/* dir2 && diff -r one dir2 && echo yes" "yes\n" "" ""
+rm -rf one dir dir2
+
+# cp -r ../source destdir
+# cp -r one/two/three missing
+# cp -r one/two/three two
+# mkdir one; touch one/two; ln -s two one/three
+# cp file1 file2 dir
+# cp file1 missing file2 -> dir
+
+# Make sure it's truncating existing file
+# copy with -d at top level, with -d in directory, without -d at top level,
+#      without -d in directory
diff --git a/scripts/test/dirname.test b/scripts/test/dirname.test
new file mode 100644 (file)
index 0000000..a007538
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "dirname /-only" "dirname ///////" "/\n" "" ""
+testing "dirname trailing /" "dirname a//////" ".\n" "" ""
+testing "dirname combined" "dirname /////a///b///c///d/////" "/////a///b///c\n" "" ""
+testing "dirname /a/" "dirname /////a///" "/\n" "" ""
diff --git a/scripts/test/echo.test b/scripts/test/echo.test
new file mode 100644 (file)
index 0000000..3f562fd
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+# This one's tricky both because echo is a shell builtin (so $PATH is
+# irrelevant) and because the "result" field is parsed with echo -e.
+# To make it work, "$CMD" is an explicit path to the command being tested,
+# so "result" keeps using the shell builtin but we test the one in toybox.
+
+CMD="$(which echo)"
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "echo" "$CMD && echo yes" "\nyes\n" "" ""
+testing "echo 1 2 3" "$CMD one  two    three" "one two three\n" "" ""
+testing "echo with spaces" "$CMD 'one  two     three'" \
+       "one  two       three\n" "" ""
+testing "echo -n" "$CMD -n" "" "" ""
+testing "echo -n one" "$CMD -n one" "one" "" ""
+testing "echo one -n" "$CMD one -n" "one -n\n" "" ""
+testing "echo -en" "$CMD -en 'one\ntwo'" "one\ntwo" "" ""
+testing "echo --hello" "$CMD --hello" "--hello\n" "" ""
+testing "echo -e all" "$CMD -e '\a\b\c\f\n\r\t\v\\\0123'" \
+       "\a\b\c\f\n\r\t\v\\\0123\n" "" ""
+testing "echo -nex hello" "$CMD -nex hello" "-nex hello\n" "" ""
+
+# Octal formatting tests
+testing "echo -e octal values" \
+       "$CMD -ne '\01234 \0060 \060 \0130\0131\0132 \077\012'" \
+       "S4 0 0 XYZ ?\n" "" ""
+
+# Hexadecimal value tests
+testing "echo -e hexadecimal values" \
+       "$CMD -ne '\x534 \x30 \x58\x59\x5a \x3F\x0A'"\
+       "S4 0 XYZ ?\n" "" ""
+
+testing "echo -e \p" "$CMD -e '\\p'" "\\p\n" "" ""
diff --git a/scripts/test/expand.test b/scripts/test/expand.test
new file mode 100644 (file)
index 0000000..510f89f
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# POSIX 2008 compliant expand tests.
+# Copyright 2012 by Jonathan Clairembault <jonathan@clairembault.fr>
+
+[ -f testing.sh ] && . testing.sh
+
+# some basic tests
+
+testing "expand default" "expand input" "        foo     bar\n" "\tfoo\tbar\n" ""
+testing "expand default stdin" "expand"  "        foo     bar\n" "" "\tfoo\tbar\n"
+testing "expand single" "expand -t 2 input" "  foo bar\n" "\tfoo\tbar\n" ""
+testing "expand tablist" "expand -t 5,10,12 input" "     foo  bar foo\n" "\tfoo\tbar\tfoo\n" ""
+testing "expand backspace" "expand input" "foobarfoo\b\b bar\n" "foobarfoo\b\b\tbar\n" ""
+
+# advanced tests
+
+POW=15
+TABSTOP=1
+BIGTAB=" "
+for i in $(seq $POW); do
+    BIGTAB=$BIGTAB$BIGTAB
+    TABSTOP=$[$TABSTOP*2]
+done
+testing "expand long tab single" "expand -t $TABSTOP input" "${BIGTAB}foo\n" "\tfoo\n" ""
+testing "expand long tab tablist" "expand -t $TABSTOP,$[TABSTOP+5] input" \
+        "${BIGTAB}foo  bar\n" "\tfoo\tbar\n" ""
+
+testing "expand multiline single" "expand -t 4 input" "foo \n    bar\n" "foo\t\n\tbar\n" ""
+testing "expand multiline tablist" "expand -t 4,8 input" \
+        "foo     bar\n    bar foo\n" "foo\t\tbar\n\tbar\tfoo\n" ""
+POW=15
+BIGLINE="foo "
+for i in $(seq $POW); do
+    BIGLINE=$BIGLINE$BIGLINE
+done
+if [ $POW -gt 0 ]; then
+    EXPANDLINE="${BIGLINE}        foo\n"
+else
+    EXPANDLINE="${BIGLINE}    foo\n"
+fi
+BIGLINE="${BIGLINE}\tfoo\n"
+testing "expand long line single" "expand input" \
+        "${EXPANDLINE}" "$BIGLINE" ""
diff --git a/scripts/test/expr.test b/scripts/test/expr.test
new file mode 100644 (file)
index 0000000..cce7d9d
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+testing "expr integer" "expr 5" "5\n" "" ""
+testing "expr integer negative" "expr -5" "-5\n" "" ""
+testing "expr string" "expr astring" "astring\n" "" ""
+testing "expr 1 + 3" "expr 1 + 3" "4\n" "" ""
+testing "expr 5 + 6 * 3" "expr 5 + 6 \* 3" "23\n" "" ""
+testing "expr ( 5 + 6 ) * 3" "expr \( 5 + 6 \) \* 3" "33\n" "" ""
diff --git a/scripts/test/find.test b/scripts/test/find.test
new file mode 100644 (file)
index 0000000..cbbce5f
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+mkdir dir
+cd dir
+touch file
+mkfifo fifo
+ln -s fifo link
+cd ..
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Testing operators
+
+testing "find -type l -a -type d -o -type p" \
+       "find dir -type l -a -type d -o -type p" "dir/fifo\n" "" ""
+testing "find -type l -type d -o -type p" "find dir -type l -type d -o -type p" \
+       "dir/fifo\n" "" ""
+testing "find -type l -o -type d -a -type p" \
+       "find dir -type l -o -type d -a -type p" "dir/link\n" "" ""
+testing "find -type l -o -type d -type p" "find dir -type l -o -type d -type p" \
+       "dir/link\n" "" ""
+testing "find -type l ( -type d -o -type l )" \
+       "find dir -type l \( -type d -o -type l \)" "dir/link\n" "" ""
+testing "find extra parantheses" \
+       "find dir \( \( -type l \) \( -type d -o \( \( -type l \) \) \) \)" \
+       "dir/link\n" "" ""
+testing "find ( -type p -o -type d ) -type p" \
+       "find dir \( -type p -o -type d \) -type p" "dir/fifo\n" "" ""
+testing "find -type l -o -type d -type p -o -type f" \
+       "find dir -type l -o -type d -type p -o -type f | sort" \
+       "dir/file\ndir/link\n" "" ""
+
+# Testing short-circuit evaluations
+
+testing "find -type f -a -print" \
+       "find dir -type f -a -print" "dir/file\n" "" ""
+testing "find -print -o -print" \
+       "find dir -type f -a \( -print -o -print \)" "dir/file\n" "" ""
+
+rm -rf dir
diff --git a/scripts/test/head.test b/scripts/test/head.test
new file mode 100644 (file)
index 0000000..eeb07e0
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "head, stdin" "head -n 1 && echo yes" "one\nyes\n" "" "one\ntwo"
+testing "head, stdin via -" "head -n 1 - && echo yes" "one\nyes\n" "" "one\ntwo"
+testing "head, file" "head input -n 1 && echo yes" "one\nyes\n" "one\ntwo" ""
+testing "head, default lines" "head" "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" "" "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12"
+
+echo "foo
+bar
+baz" > file1
+testing "head, multiple files" "head -n 2 input file1" "==> input <==\none\ntwo\n\n==> file1 <==\nfoo\nbar\n" "one\ntwo\nthree\n" ""
+rm file1
+
diff --git a/scripts/test/losetup.test b/scripts/test/losetup.test
new file mode 100644 (file)
index 0000000..fe1e520
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+if [ "$(id -u)" -ne 0 ]
+then
+  echo "SKIPPED: losetup (not root)"
+  continue 2>/dev/null
+  exit
+fi
+
+#testing "name" "command" "result" "infile" "stdin"
+
+truncate -s 1M blah.img &&
+FILE="$(readlink -f blah.img)"
+DEV="$(printf '%04d' $(stat -t blah.img | awk '{print $7}'))"
+NODE="$(awk '{print $7}')"
+
+losetup -f 
+losetup -f -s
+losetup -f file
+testing "cat" "cat && echo yes" "oneyes\n" "" "one"
+testing "cat -" "cat - && echo yes" "oneyes\n" "" "one"
+testing "cat file1 file2" "cat file1 file2" "one\ntwo\n"  "" ""
+testing "cat - file"      "cat - file1"     "zero\none\n" "" "zero\n"
+testing "cat file -"      "cat file1 -"     "one\nzero\n" "" "zero\n"
+
+testing "cat file1 notfound file2" \
+        "cat file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
+        "one\ntwo\ncat: notfound: No such file or directory\n" "" ""
+
+testing "cat file1" \
+        "cat /proc/self/exe > file1 && cmp /proc/self/exe file1 && echo yes" \
+        "yes\n" "" ""
+
+testing "cat - file1" \
+        "cat - file1 | diff -a -U 0 - file1 | tail -n 1" \
+        "-hello\n" "" "hello\n"
+
+testing "cat > /dev/full" \
+        "cat - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
+        "cat: xwrite: No space left on device\n" "" "zero\n"
+
+losetup -d
+
+rm blah.img
diff --git a/scripts/test/md5sum.test b/scripts/test/md5sum.test
new file mode 100644 (file)
index 0000000..741ec62
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# These tests are from RFC 1321 appendix 5, reshuffled slightly to test
+# varying argument numbers
+
+testing "md5sum ''" "md5sum" "d41d8cd98f00b204e9800998ecf8427e  -\n" "" ""
+testing "md5sum infile" "md5sum input" \
+  "0cc175b9c0f1b6a831c399e269772661  input\n" "a" ""
+testing "md5sum two files" "md5sum - input" \
+  "900150983cd24fb0d6963f7d28e17f72  -\nf96b697d7cb7938d525a2f31aaf161d0  input\n" \
+  "message digest" "abc"
+testing "md5sum 4" "md5sum" "c3fcd3d76192e4007dfb496cca67e13b  -\n" \
+  "" "abcdefghijklmnopqrstuvwxyz"
+testing "md5sum 5" "md5sum" "d174ab98d277d9f5a5611c2c9f419d9f  -\n" \
+  "" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+testing "md5sum 6" "md5sum" "57edf4a22be3c955ac49da2e2107b67a  -\n" \
+  "" "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+
diff --git a/scripts/test/mkdir.test b/scripts/test/mkdir.test
new file mode 100644 (file)
index 0000000..035d613
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "mkdir" "mkdir one && [ -d one ] && echo yes" "yes\n" "" ""
+rmdir one
+
+touch existing
+testing "mkdir existing" \
+       "mkdir existing 2> /dev/null || [ -f existing ] && echo yes" "yes\n" "" ""
+rm existing
+
+testing "mkdir one two" \
+       "mkdir one two && [ -d one ] && [ -d two ] && echo yes" "yes\n" "" ""
+rmdir one two
+
+testing "mkdir missing/one" \
+       "mkdir missing/one 2> /dev/null || [ ! -d missing ] && echo yes" "yes\n" "" ""
+
+testing "mkdir -p" \
+       "mkdir -p one/two/three && [ -d one/two/three ] && echo yes" "yes\n" "" ""
+rm -rf one
+
+mkdir existing
+testing "mkdir -p existing" "mkdir -p existing && echo yes" "yes\n" "" ""
+rmdir existing
+
+umask 123
+testing "mkdir (default permissions)" \
+       "mkdir one && stat -c %a one" "654\n" "" ""
+rmdir one
+
+umask 000
+
+testing "mkdir -m 124" \
+       "mkdir -m 124 one && stat -c %a one" "124\n" "" ""
+rmdir one
+
+testing "mkdir -p -m 653" \
+       "mkdir -p -m 653 one/two && stat -c %a one && stat -c %a one/two" \
+       "777\n653\n" "" ""
+rm -rf one
diff --git a/scripts/test/mkfifo.test b/scripts/test/mkfifo.test
new file mode 100644 (file)
index 0000000..b0253fe
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "mkfifo" "mkfifo one && [ -p one ] && echo yes" "yes\n" "" ""
+rm one
+
+touch existing
+testing "mkfifo existing" \
+       "mkfifo existing 2> /dev/null || [ -f existing ] && echo yes" "yes\n" "" ""
+rm existing
+
+testing "mkfifo one two" \
+       "mkfifo one two && [ -p one ] && [ -p two ] && echo yes" "yes\n" "" ""
+rm one two
+
+umask 123
+testing "mkfifo (default permissions)" \
+       "mkfifo one && stat -c %a one" "644\n" "" ""
+rm one
+
+umask 000
+
+testing "mkfifo -m 124" \
+       "mkfifo -m 124 one && stat -c %a one" "124\n" "" ""
+rm -f one
diff --git a/scripts/test/modinfo.test b/scripts/test/modinfo.test
new file mode 100644 (file)
index 0000000..ac94ce0
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+[ -e /proc/modules ] || { echo "Skipping test because modules are not supported"; exit 1; }
+
+# modinfo does not need to output fields in a specified order.
+# Instead, there are labelled fields.  We can use sort to make up for this.
+# Other issues to beware of are the volatile nature of srcversion and vermagic,
+# which change from kernel to kernel and can be disabled. 
+# We grep to remove these.
+
+#We expect they have ne2k-pci as a module.
+
+testing "modinfo gets right number of fields" "modinfo ne2k-pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
+testing "modinfo treats - and _ as equivalent" "modinfo ne2k_pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
+
+# Output of -F filename should be an absolute path to the module.
+# Otherwise, initrd generating scripts will break.
+
+testing "modinfo -F filename gets absolute path" "[ -e `modinfo -F filename ne2k-pci` ] && echo ne2k-pci " "ne2k-pci\n" "" ""
+
+testing "modinfo supports multiple modules" "modinfo -F filename ne2k-pci 8390 | wc -l" "2\n" "" ""
+
+testing "modinfo does not output filename for bad module" "modinfo -F filename zxcvbnm__9753" "" "" ""
+
+
+
diff --git a/scripts/test/pwd.test b/scripts/test/pwd.test
new file mode 100644 (file)
index 0000000..b02055b
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+#TODO: Find better tests
+
+testing "pwd" "[ $(stat -c %i "$(pwd)") = $(stat -c %i .) ] && echo yes" \
+       "yes\n" "" ""
+testing "pwd -P" "[ $(stat -c %i "$(pwd -P)") = $(stat -c %i .) ] && echo yes" \
+       "yes\n" "" ""
+
+
+ln -s . sym
+cd sym
+testing "pwd" "[ $(stat -c %i "$(pwd)") = $(stat -c %i "$PWD") ] && echo yes" \
+       "yes\n" "" ""
+testing "pwd -P" "[ $(stat -c %i "$(pwd -P)") = $(stat -c %i "$PWD") ] || echo yes" \
+       "yes\n" "" ""
+cd ..
+rm sym
+
+export PWD=walrus
+testing "pwd (bad PWD)" "[ "$(pwd)" = "$(cd . ; pwd)" ] && echo yes" \
+       "yes\n" "" ""
diff --git a/scripts/test/readlink.test b/scripts/test/readlink.test
new file mode 100644 (file)
index 0000000..6c7b147
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+APWD="$(pwd -P)"
+
+testing "readlink missing" "readlink notfound || echo yes" "yes\n" "" ""
+
+# simple tests on a file
+
+touch file
+testing "readlink file" "readlink file || echo yes" "yes\n" "" ""
+testing "readlink -f dir" "readlink -f ." "$APWD\n" "" ""
+testing "readlink -f missing" "readlink -f notfound" "$APWD/notfound\n" "" ""
+
+ln -sf notfound link
+testing "readlink link" "readlink link" "notfound\n" "" ""
+testing "readlink link->missing" "readlink -f link" "$APWD/notfound\n" "" ""
+ln -sf ../../ link
+testing "readlink stays relative" "readlink link" "../../\n" "" ""
+rm link
+ln -sf file link
+testing "readlink -f link->file" "readlink -f link" "$APWD/file\n" "" ""
+ln -sf . link
+testing "readlink -f link->dir" "readlink -f link" "$APWD\n" "" ""
+ln -snf link link
+testing "readlink link->link (recursive)" "readlink link" "link\n" "" ""
+testing "readlink -f link->link (recursive)" \
+  "readlink -f link 2>/dev/null || echo yes" "yes\n" "" ""
+
+testing "readlink -q notlink" "readlink -q file || echo yes" "yes\n" "" ""
+testing "readlink -q link" "readlink -q link && echo yes" "yes\n" "" ""
+testing "readlink -q notfound" "readlink -q notfound || echo yes" "yes\n" "" ""
+testing "readlink -e found" "readlink -e file" "$APWD/file\n" "" ""
+testing "readlink -e notfound" \
+  "readlink -e notfound 2>/dev/null || echo yes" "yes\n" "" ""
+testing "readlink -nf ." "readlink -nf ." "$APWD" "" ""
+
+mkdir sub &&
+ln -s . here &&
+ln -s ./sub dir &&
+touch sub/bang || exit 1
+testing "readlink -f multi" "readlink -f dir/../here/dir/bang" \
+  "$APWD/sub/bang\n" "" ""
+testing "readlink -f link/missing" "readlink -f dir/boing" \
+  "$APWD/sub/boing\n" "" ""
+testing "readlink -f /dev/null/file" \
+  "readlink -f /dev/null/file 2>/dev/null || echo yes" "yes\n" "" ""
+ln -sf / link || exit 1
+testing "readlink -f link->/" "readlink -e link/dev" "/dev\n" "" ""
+testing "readlink -f /dev/null/.." \
+  "readlink -f link/null/.. 2>/dev/null || echo yes" "yes\n" "" ""
+rm -f link && ln -sf link link || exit 1
+testing "readlink recurse" "readlink link" "link\n" "" ""
+
+rm file link sub/bang dir here
+rmdir sub
+
+# Make sure circular links don't run away.
+
+ln -s link1 link2
+ln -s link2 link1
+testing "readlink follow recursive2" "readlink -f link1 || echo yes" \
+       "yes\n" "" ""
+rm link1 link2
diff --git a/scripts/test/rev.test b/scripts/test/rev.test
new file mode 100644 (file)
index 0000000..c7622b9
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+echo -e "one" > file1 
+echo -e "two" > file2
+testing "rev" "rev && echo yes" "orez\nyes\n" "" "zero\n"
+testing "rev -" "rev - && echo yes" "orez\nyes\n" "" "zero\n"
+testing "rev file1 file2" "rev file1 file2" "eno\nowt\n" "" ""
+testing "rev - file"      "rev - file1"     "orez\neno\n" "" "zero\n"
+testing "rev file -"      "rev file1 -"     "eno\norez\n" "" "zero\n"
+testing "rev no trailing newline" "rev -" "cba\nfed\n" "" "abc\ndef"
+
+testing "rev file1 notfound file2" \
+        "rev file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
+        "eno\nowt\nrev: notfound: No such file or directory\n" "" ""
+
+testing "rev different input sizes"\
+        "rev"\
+        "\n1\n21\n321\n4321\n54321\n4321\n321\n21\n1\n\n"\
+        "" "\n1\n12\n123\n1234\n12345\n1234\n123\n12\n1\n\n"
+
+rm file1 file2
\ No newline at end of file
diff --git a/scripts/test/rmdir.test b/scripts/test/rmdir.test
new file mode 100644 (file)
index 0000000..a7b027e
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+mkdir one
+testing "rmdir" "rmdir one && [ ! -d one ] && echo yes" "yes\n" "" ""
+
+touch walrus
+testing "rmdir file" \
+       "rmdir walrus 2> /dev/null || [ -f walrus ] && echo yes" "yes\n" "" ""
+
+mkdir one two
+testing "rmdir one two" \
+       "rmdir one two 2> /dev/null && [ ! -d one ] && [ ! -d two ] && echo yes" \
+       "yes\n" "" ""
+
+mkdir one two three
+testing "rmdir one missing two file three" \
+       "rmdir one missing two walrus three 2> /dev/null || [ ! -d three ] && echo yes" \
+       "yes\n" "" ""
+rm walrus
+
+mkdir one
+chmod 000 one
+testing "rmdir mode 000" "rmdir one && [ ! -d one ] && echo yes" "yes\n" "" ""
+
+mkdir temp
+touch temp/thing
+testing "rmdir non-empty" \
+       "rmdir temp 2>/dev/null || [ -d temp ] && echo yes" "yes\n" "" ""
+testing "rmdir -p dir/file" \
+       "rmdir -p temp/thing 2>/dev/null || [ -f temp/thing ] && echo yes" \
+       "yes\n" "" ""
+
+mkdir -p temp/one/two/three
+testing "rmdir -p part of path" \
+       "rmdir -p temp/one/two/three 2>/dev/null || [ -d temp ] && [ ! -e temp/one ] && echo yes" \
+       "yes\n" "" ""
+rm -rf temp
+
+
+mkdir -p one/two/three
+testing "rmdir -p one/two/three" \
+       "rmdir -p one/two/three && [ ! -e one ] && echo yes" "yes\n" "" ""
+
+mkdir -p one/two/three
+testing "rmdir -p one/two/three/" \
+       "rmdir -p one/two/three/ && [ ! -e one ] && echo yes" "yes\n" "" ""
+
+#mkdir -p one/two/three
+#chmod 000 one/two/three one/two one
+#testing "rmdir -p one/two/three" \
+#      "rmdir -p one/two/three && [ ! -e one ] && echo yes" "yes\n" "" ""
diff --git a/scripts/test/seq.test b/scripts/test/seq.test
new file mode 100644 (file)
index 0000000..3f3b409
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "seq (exit with error)" "seq 2> /dev/null || echo yes" "yes\n" "" ""
+testing "seq (exit with error)" "seq 1 2 3 4 2> /dev/null || echo yes" \
+        "yes\n" "" ""
+testing "seq one argument" "seq 3" "1\n2\n3\n" "" ""
+testing "seq two arguments" "seq 5 7" "5\n6\n7\n" "" ""
+testing "seq two arguments reversed" "seq 7 5" "" "" ""
+testing "seq two arguments equal" "seq 3 3" "3\n" "" ""
+testing "seq two arguments equal, arbitrary negative step" "seq 1 -15 1" \
+        "1\n" "" ""
+testing "seq two arguments equal, arbitrary positive step" "seq 1 +15 1" \
+        "1\n" "" ""
+testing "seq count up by 2" "seq 4 2 8" "4\n6\n8\n" "" ""
+testing "seq count down by 2" "seq 8 -2 4" "8\n6\n4\n" "" ""
+testing "seq count wrong way #1" "seq 4 -2 8" "" "" ""
+testing "seq count wrong way #2" "seq 8 2 4" "" "" ""
+testing "seq count by .3" "seq 3 .3 4" "3\n3.3\n3.6\n3.9\n" "" ""
+testing "seq count by -.9" "seq .7 -.9 -2.2" "0.7\n-0.2\n-1.1\n-2\n" "" ""
+testing "seq count by zero" "seq 4 0 8 | head -n 10" "" "" ""
+testing "seq separator -" "seq -s - 1 3" "1-2-3\n" "" ""
+testing "seq format string" 'seq -f %+01g -10 5 10' "-10\n-5\n+0\n+5\n+10\n" "" ""
+testing "seq separator and format string" "seq -f \%03g -s \; 5 -1 0" "005;004;003;002;001;000\n" "" ""
+
diff --git a/scripts/test/sha1sum.test b/scripts/test/sha1sum.test
new file mode 100644 (file)
index 0000000..cd621fd
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# These tests are based on RFC3174 which were based on FIPS PUB 180-1
+
+testing "sha1sum TEST1" \
+        "sha1sum" \
+        "a9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
+        "" "abc"
+
+testing "sha1sum TEST2" \
+        "sha1sum" \
+        "84983e441c3bd26ebaae4aa1f95129e5e54670f1  -\n" \
+        "" "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+
+testing "sha1sum TEST3" \
+        'dd if=/dev/zero bs=1000 count=1000 2>/dev/null | tr \\0 a | sha1sum' \
+        "34aa973cd4c4daa4f61eeb2bdbad27316534016f  -\n" \
+        "" ""
+
+testing "sha1sum TEST4" \
+        'for i in `seq 1 10`; do echo -n 0123456701234567012345670123456701234567012345670123456701234567 ; done | sha1sum' \
+        "dea356a2cddd90c7a7ecedc5ebb563934f460452  -\n" \
+        "" ""
+
+echo -n "abc" > file1
+echo -n "def" > file2
+testing "sha1sum" \
+        "sha1sum" \
+        "a9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
+        "" "abc"
+
+testing "sha1sum -" \
+        "sha1sum -" \
+        "a9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
+        "" "abc"
+
+testing "sha1sum file" \
+        "sha1sum file1" \
+        "a9993e364706816aba3e25717850c26c9cd0d89d  file1\n" \
+        "" ""
+
+testing "sha1sum file1 file2" \
+        "sha1sum file1 file2" \
+        "a9993e364706816aba3e25717850c26c9cd0d89d  file1\n589c22335a381f122d129225f5c0ba3056ed5811  file2\n" \
+        "" ""
+
+testing "sha1sum file1 file2 -" \
+        "sha1sum file1 file2 -" \
+        "a9993e364706816aba3e25717850c26c9cd0d89d  file1\n589c22335a381f122d129225f5c0ba3056ed5811  file2\na9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
+        "" "abc"
+
+rm -f file1 file2
+
diff --git a/scripts/test/sort.test b/scripts/test/sort.test
new file mode 100644 (file)
index 0000000..7bd413f
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/bash
+
+# SUSv4 compliant sort tests.
+# Copyright 2005, 2012 by Rob Landley <rob@landley.net>
+
+[ -f testing.sh ] && . testing.sh
+
+# The basic tests.  These should work even with the small config.
+
+testing "sort" "sort input" "a\nb\nc\n" "c\na\nb\n" ""
+testing "sort #2" "sort input" "010\n1\n3\n" "3\n1\n010\n" ""
+testing "sort stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n"
+testing "sort numeric" "sort -n input" "1\n3\n010\n" "3\n1\n010\n" ""
+testing "sort reverse" "sort -r input" "wook\nwalrus\npoint\npabst\naargh\n" \
+       "point\nwook\npabst\naargh\nwalrus\n" ""
+
+# These tests require the full option set.
+
+optional SORT_BIG
+# Longish chunk of data re-used by the next few tests.  The expected output
+# varies, but the input (this) is the same.
+
+data="42       1       3       woot
+42     1       010     zoology
+egg    1       2       papyrus
+7      3       42      soup
+999    3       0       algebra
+"
+
+# Sorting with keys
+
+testing "sort one key" "sort -k4,4 input" \
+"999   3       0       algebra
+egg    1       2       papyrus
+7      3       42      soup
+42     1       3       woot
+42     1       010     zoology
+" "$data" ""
+
+# The numeric sort orders field 2, ignores field 3 (because numeric sort stops
+# at the whitespace), then the global fallback sort does an alpha sort on
+# the whole string (starting at the beginning of the line).
+
+testing "sort key range with numeric option" "sort -k2,3n input" \
+"42    1       010     zoology
+42     1       3       woot
+egg    1       2       papyrus
+7      3       42      soup
+999    3       0       algebra
+" "$data" ""
+
+# Numeric sort on field 2 (again, ignore field 3 because it's numeric),
+# then do a _reversed_ alpha sort on the whole line as a tiebreaker.
+
+testing "sort key range with numeric option and global reverse" \
+"sort -k2,3n -r input" \
+"egg   1       2       papyrus
+42     1       3       woot
+42     1       010     zoology
+999    3       0       algebra
+7      3       42      soup
+" "$data" ""
+
+# Reversed numeric sort on field 2 (numeric ignores field 3), then
+# break ties with alpha sort on whole line.
+
+testing "sort key range with multiple options" "sort -k2,3rn input" \
+"7     3       42      soup
+999    3       0       algebra
+42     1       010     zoology
+42     1       3       woot
+egg    1       2       papyrus
+" "$data" ""
+
+testing "sort key doesn't strip leading blanks, disables fallback global sort" \
+"sort -n -k2 -t ' '" " a \n 1 \n 2 \n" "" " 2 \n 1 \n a \n"
+
+# Test case contributed by Joey Hess:
+
+testing "sort key edge case with -t" "sort -n -k4 -t/" \
+"/usr/lib/finish-install.d/1
+/usr/lib/finish-install.d/4
+/usr/lib/prebaseconfig.d/2
+/usr/lib/prebaseconfig.d/6
+" "" "/usr/lib/finish-install.d/1
+/usr/lib/prebaseconfig.d/2
+/usr/lib/finish-install.d/4
+/usr/lib/prebaseconfig.d/6
+"
+
+testing "sort -x" "sort -x" "010\na0\n 0c0\n" "" "a0\n010\n 0c0\n"
+
+optional SORT_FLOAT
+
+# not numbers < NaN < -infinity < numbers < +infinity
+testing "sort -g" "sort -g" \
+  "bork\nNaN\n-inf\n0.4\n1.222\n01.37\n2.1\n+infinity\n" "" \
+  "01.37\n1.222\n2.1\n0.4\nNaN\nbork\n-inf\n+infinity\n"
+
+
diff --git a/scripts/test/split.test b/scripts/test/split.test
new file mode 100644 (file)
index 0000000..d86b025
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "split" "seq 1 12345 | split && ls xa[a-z] | wc -l" "13\n" "" ""
+rm xa[a-z]
+
+testing "split -" "seq 1 12345 | split - && ls xa[a-z] | wc -l" "13\n" "" ""
+rm xa[a-z]
+
+seq 1 12345 > file
+testing "split file" "split file && ls xa[a-z] | wc -l" "13\n" "" ""
+rm xa[a-z]
+
+testing "split -l" "split file -l 10k && wc -l xab" "2105 xab\n" "" ""
+rm xa[ab]
+
+testing "split suffix exhaustion" \
+  "split file -l 10 -a 1 walrus 2>/dev/null || ls walrus* | wc -l" "26\n" "" ""
+rm walrus*
+
+testing "split bytes" \
+  "toybox seq 1 20000 | split -b 100 -a 3 - whang && ls whang* | wc -l && wc -c whangbpw" "1089\n94 whangbpw\n" "" ""
+
+testing "split reassembly" \
+  'diff -u <(ls whang* | sort | xargs cat) <(seq 1 20000) && echo yes' \
+  "yes\n" "" ""
+
+rm file whang*
diff --git a/scripts/test/tac.test b/scripts/test/tac.test
new file mode 100644 (file)
index 0000000..96f2531
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+
+echo -e "one-A\none-B" > file1 
+echo -e "two-A\ntwo-B" > file2
+testing "tac" "tac && echo yes" "one-B\none-A\nyes\n" "" "one-A\none-B\n"
+testing "tac -" "tac - && echo yes" "one-B\none-A\nyes\n" "" "one-A\none-B\n"
+testing "tac file1 file2" "tac file1 file2" "one-B\none-A\ntwo-B\ntwo-A\n"  "" ""
+testing "tac - file"      "tac - file1"     "zero-B\nzero-A\none-B\none-A\n" "" "zero-A\nzero-B\n"
+testing "tac file -"      "tac file1 -"     "one-B\none-A\nzero-B\nzero-A\n" "" "zero-A\nzero-B\n"
+
+testing "tac file1 notfound file2" \
+        "tac file1 notfound file2 2>stderr && echo ok ; tac stderr; rm stderr" \
+        "one-B\none-A\ntwo-B\ntwo-A\ntac: notfound: No such file or directory\n" "" ""
+
+testing "tac no trailing newline" "tac -" "defabc\n" "" "abc\ndef"
+
+# xputs used by tac does not propagate this error condition properly. 
+#testing "tac > /dev/full" \
+#        "tac - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
+#        "tac: write: No space left on device\n" "" "zero\n"
+
+# 
+
+rm file1 file2
\ No newline at end of file
diff --git a/scripts/test/tail.test b/scripts/test/tail.test
new file mode 100644 (file)
index 0000000..77e572e
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+BIGTEST="one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n"
+echo -ne "$BIGTEST" > file1
+testing "tail" "tail && echo yes" "oneyes\n" "" "one"
+testing "tail file" "tail file1" \
+       "two\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n" "" ""
+testing "tail -n in bounds" "tail -n 3 file1" "nine\nten\neleven\n" "" ""
+testing "tail -n out of bounds" "tail -n 999 file1" "$BIGTEST" "" ""
+testing "tail -n+ in bounds" "tail -n +3 file1" \
+       "three\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n" "" ""
+testing "tail -n+ outof bounds" "tail -n +999 file1" "" "" ""
+testing "tail -c in bounds" "tail -c 27 file1" \
+       "even\neight\nnine\nten\neleven\n" "" ""
+testing "tail -c out of bounds" "tail -c 999 file1" "$BIGTEST" "" ""
+testing "tail -c+ in bounds" "tail -c +27 file1" \
+       "x\nseven\neight\nnine\nten\neleven\n" "" ""
+testing "tail -c+ out of bonds" "tail -c +999 file1" "" "" ""
+rm file1
+
+testing "tail stdin no trailing newline" "tail -n 1 - " "c" "" "a\nb\nc"
+testing "tail file no trailing newline" "tail -n 1 input" "c" "a\nb\nc" ""
+
+optional TAIL_SEEK
+testing "tail noseek -n in bounds" "tail -n 3" "nine\nten\neleven\n" \
+       "" "$BIGTEST"
+testing "tail noseek -n out of bounds" "tail -n 999" "$BIGTEST" "" "$BIGTEST"
+testing "tail noseek -n+ in bounds" "tail -n +3" \
+       "three\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n" "" \
+       "$BIGTEST"
+testing "tail noseek -n+ outof bounds" "tail -n +999" "" "" "$BIGTEST"
+testing "tail noseek -c in bounds" "tail -c 27" \
+       "even\neight\nnine\nten\neleven\n" "" "$BIGTEST"
+testing "tail noseek -c out of bounds" "tail -c 999" "$BIGTEST" "" "$BIGTEST"
+testing "tail noseek -c+ in bounds" "tail -c +27" \
+       "x\nseven\neight\nnine\nten\neleven\n" "" "$BIGTEST"
+testing "tail noseek -c+ out of bonds" "tail -c +999" "" "" "$BIGTEST"
diff --git a/scripts/test/testing.sh b/scripts/test/testing.sh
new file mode 100644 (file)
index 0000000..b7eacc6
--- /dev/null
@@ -0,0 +1,156 @@
+# Simple test harness infrastructure
+#
+# Copyright 2005 by Rob Landley
+
+# This file defines two functions, "testing" and "optionflag"
+
+# The following environment variables enable optional behavior in "testing":
+#    DEBUG - Show every command run by test script.
+#    VERBOSE - Print the diff -u of each failed test case.
+#    SKIP - do not perform this test (this is set by "optionflag")
+#
+# The "testing" function takes five arguments:
+#      $1) Description to display when running command
+#      $2) Command line arguments to command
+#      $3) Expected result (on stdout)
+#      $4) Data written to file "input"
+#      $5) Data written to stdin
+#
+# The exit value of testing is the exit value of the command it ran.
+#
+# The environment variable "FAILCOUNT" contains a cumulative total of the
+# number of failed tests.
+
+# The "optional" function is used to skip certain tests, ala:
+#   optionflag CFG_THINGY
+#
+# The "optional" function checks the environment variable "OPTIONFLAGS",
+# which is either empty (in which case it always clears SKIP) or
+# else contains a colon-separated list of features (in which case the function
+# clears SKIP if the flag was found, or sets it to 1 if the flag was not found).
+
+export FAILCOUNT=0
+export SKIP=
+
+# Helper functions
+
+# Check config to see if option is enabled, set SKIP if not.
+
+optional()
+{
+  option=`echo "$OPTIONFLAGS" | egrep "(^|:)$1(:|\$)"`
+  # Not set?
+  if [ -z "$1" ] || [ -z "$OPTIONFLAGS" ] || [ ${#option} -ne 0 ]
+  then
+    SKIP=""
+    return
+  fi
+  SKIP=1
+}
+
+# The testing function
+
+testing ()
+{
+  NAME="$1"
+  [ -z "$1" ] && NAME=$2
+
+  if [ $# -ne 5 ]
+  then
+    echo "Test $NAME has the wrong number of arguments ($# $*)" >&2
+    exit
+  fi
+
+  [ -n "$DEBUG" ] && set -x
+
+  if [ -n "$SKIP" ]
+  then
+    [ ! -z "$VERBOSE" ] && echo "SKIPPED: $NAME"
+    return 0
+  fi
+
+  echo -ne "$3" > expected
+  echo -ne "$4" > input
+  echo -ne "$5" | eval "$2" > actual
+  RETVAL=$?
+
+  cmp expected actual > /dev/null 2>&1
+  if [ $? -ne 0 ]
+  then
+    FAILCOUNT=$[$FAILCOUNT+1]
+    echo "FAIL: $NAME"
+    if [ -n "$VERBOSE" ]
+    then
+      echo "echo '$5' | $2"
+      diff -u expected actual
+    fi
+  else
+    echo "PASS: $NAME"
+  fi
+  rm -f input expected actual
+
+  [ -n "$DEBUG" ] && set +x
+
+  return $RETVAL
+}
+
+# Recursively grab an executable and all the libraries needed to run it.
+# Source paths beginning with / will be copied into destpath, otherwise
+# the file is assumed to already be there and only its library dependencies
+# are copied.
+
+function mkchroot
+{
+  [ $# -lt 2 ] && return
+
+  echo -n .
+
+  dest=$1
+  shift
+  for i in "$@"
+  do
+    [ "${i:0:1}" == "/" ] || i=$(which $i)
+    [ -f "$dest/$i" ] && continue
+    if [ -e "$i" ]
+    then
+      d=`echo "$i" | grep -o '.*/'` &&
+      mkdir -p "$dest/$d" &&
+      cat "$i" > "$dest/$i" &&
+      chmod +x "$dest/$i"
+    else
+      echo "Not found: $i"
+    fi
+    mkchroot "$dest" $(ldd "$i" | egrep -o '/.* ')
+  done
+}
+
+# Set up a chroot environment and run commands within it.
+# Needed commands listed on command line
+# Script fed to stdin.
+
+function dochroot
+{
+  mkdir tmpdir4chroot
+  mount -t ramfs tmpdir4chroot tmpdir4chroot
+  mkdir -p tmpdir4chroot/{etc,sys,proc,tmp,dev}
+  cp -L testing.sh tmpdir4chroot
+
+  # Copy utilities from command line arguments
+
+  echo -n "Setup chroot"
+  mkchroot tmpdir4chroot $*
+  echo
+
+  mknod tmpdir4chroot/dev/tty c 5 0
+  mknod tmpdir4chroot/dev/null c 1 3
+  mknod tmpdir4chroot/dev/zero c 1 5
+
+  # Copy script from stdin
+
+  cat > tmpdir4chroot/test.sh
+  chmod +x tmpdir4chroot/test.sh
+  chroot tmpdir4chroot /test.sh
+  umount -l tmpdir4chroot
+  rmdir tmpdir4chroot
+}
+
diff --git a/scripts/test/touch.test b/scripts/test/touch.test
new file mode 100644 (file)
index 0000000..03ab8ce
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "touch" "touch walrus && [ -e walrus ] && echo yes" "yes\n" "" ""
+testing "touch 1 2 3" "touch one two three && rm one two three && echo yes" "yes\n" \
+  "" ""
+testing "touch -c" "touch -c walrus && [ -e walrus ] && echo yes" "yes\n" "" ""
+testing "touch -c missing" "touch -c warrus && [ ! -e warrus ] && echo yes" \
+  "yes\n" "" ""
+
+# This isn't testing fraction of a second because I dunno how to read it back
+
+testing "touch -d" \
+  "touch -d 2009-02-13T23:31:30.12Z walrus && date -r walrus +%s.%N" "1234567890\n" \
+  "" ""
+#testing "touch -t" "touch -t 200902132331.42
+#testing "touch -r"
+#testing "touch -a"
+#testing "touch -m"
+#testing "touch -am"
+rm walrus
diff --git a/scripts/test/uudecode.test b/scripts/test/uudecode.test
new file mode 100644 (file)
index 0000000..b9d7d01
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "uudecode uu empty file" "uudecode -o /dev/stdout && echo yes" \
+       "yes\n" "" "begin 744 test\n\`\nend\n"
+testing "uudecode uu 1-char" "uudecode -o /dev/stdout" "a" "" \
+       "begin 744 test\n!80  \n\`\nend\n"
+testing "uudecode uu 2-char" "uudecode -o /dev/stdout" "ab" "" \
+       "begin 744 test\n\"86( \n\`\nend\n"
+testing "uudecode uu 3-char" "uudecode -o /dev/stdout" "abc" "" \
+       "begin 744 test\n#86)C\n\`\nend\n" 
+
+testing "uudecode b64 empty file" "uudecode -o /dev/stdout && echo yes" \
+        "yes\n" "" "begin-base64 744 test\n====\n" 
+testing "uudecode b64 1-char" "uudecode -o /dev/stdout" "a" "" \
+       "begin-base64 744 test\nYQ==\n====\n"
+testing "uudecode b64 2-char" "uudecode -o /dev/stdout" "ab" "" \
+       "begin-base64 744 test\nYWI=\n====\n"
+testing "uudecode b64 3-char" "uudecode -o /dev/stdout" "abc" "" \
+       "begin-base64 744 test\nYWJj\n====\n"
+
+testing "uudecode filename" "uudecode && echo -ne 'abc' | cmp uudecode-fn-test /dev/stdin && echo -ne yes && rm uudecode-fn-test" \
+       "yes" "" "begin-base64 744 uudecode-fn-test\nYWJj\n====\n"
+
diff --git a/scripts/test/uuencode.test b/scripts/test/uuencode.test
new file mode 100644 (file)
index 0000000..7c19faa
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "uuencode not enough args [fail]" "uuencode 2>/dev/null" "" "" ""
+
+testing "uuencode uu empty file" "uuencode test" \
+       "begin 744 test\nend\n" "" "" 
+testing "uuencode uu 1-char" "uuencode test" \
+       "begin 744 test\n!80\`\`\nend\n" "" "a" 
+testing "uuencode uu 2-char" "uuencode test" \
+       "begin 744 test\n\"86(\`\nend\n" "" "ab" 
+testing "uuencode uu 3-char" "uuencode test" \
+       "begin 744 test\n#86)C\nend\n" "" "abc" 
+
+testing "uuencode b64 empty file" "uuencode -m test" \
+       "begin-base64 744 test\n====\n" "" "" 
+testing "uuencode b64 1-char" "uuencode -m test" \
+       "begin-base64 744 test\nYQ==\n====\n" "" "a" 
+testing "uuencode b64 2-char" "uuencode -m test" \
+       "begin-base64 744 test\nYWI=\n====\n" "" "ab" 
+testing "uuencode b64 3-char" "uuencode -m test" \
+       "begin-base64 744 test\nYWJj\n====\n" "" "abc" 
+
diff --git a/scripts/test/wc.test b/scripts/test/wc.test
new file mode 100644 (file)
index 0000000..4089132
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+cat >file1 <<EOF
+some words     . 
+
+some
+lines
+EOF
+
+testing "wc" "wc >/dev/null && echo yes" "yes\n" "" ""
+testing "wc empty file" "wc" "0 0 0\n" "" ""
+testing "wc standard input" "wc" "1 3 5\n" "" "a b\nc"
+testing "wc -c" "wc -c file1" "26 file1\n" "" ""
+testing "wc -l" "wc -l file1" "4 file1\n" "" ""
+testing "wc -w" "wc -w file1" "5 file1\n" "" ""
+testing "wc format" "wc file1" "4 5 26 file1\n" "" ""
+testing "wc multiple files" "wc input - file1" \
+        "1 2 3 input\n0 2 3 -\n4 5 26 file1\n5 9 32 total\n" "a\nb" "a b"
+
+optional TOYBOX_I18N
+
+#Tests for wc -m
+if printf "%s" "$LANG" | grep -q UTF-8
+then
+
+printf " " > file1
+for i in $(seq 1 8192)
+do
+  printf "ü" >> file1
+done
+testing "wc -m" "wc -m file1" "8193 file1\n" "" ""
+printf " " > file1
+for i in $(seq 1 8192)
+do
+  printf "ü" >> file1
+done
+testing "wc -m (invalid chars)" "wc -m file1" "8193 file1\n" "" ""
+testing "wc -mlw" "wc -mlw input" "1 2 11 input\n" "hello, ä¸–ç•Œ!\n" ""
+
+else
+printf "skipping tests for wc -m"
+fi
+
+rm file1
diff --git a/scripts/test/xargs.test b/scripts/test/xargs.test
new file mode 100644 (file)
index 0000000..e49445d
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "xargs" "xargs && echo yes" "hello\nyes\n" "" "hello"
+testing "xargs spaces" "xargs" \
+       "one two three four\n" "" "one two\tthree  \nfour\n\n"
+
+testing "xargs -n 0" "xargs -n 0 2>/dev/null || echo ok" "ok\n" \
+       "" "one \ntwo\n three"
+testing "xargs -n 2" "xargs -n 2" "one two\nthree\n" "" "one \ntwo\n three"
+testing "xargs -n exact match" "xargs -n 3" "one two three\n" "" "one two three"
+testing "xargs2" "xargs -n2" "one two\nthree four\nfive\n" "" \
+       "one two three four five"
+testing "xargs -s too long" "xargs -s 9 echo 2>/dev/null || echo ok" \
+       "one\ntwo\nok\n" "" "one two three"
+testing "xargs -s 13" "xargs -s 13 echo" "one two\nthree\n" "" "one \ntwo\n three"
+testing "xargs -s 12" "xargs -s 12 echo" "one\ntwo\nthree\n" "" "one \ntwo\n three"
+
+touch one two three
+testing "xargs command -opt" "xargs -n2 ls -1" "one\ntwo\nthree\n" "" \
+       "one two three"
+rm one two three
+
+exit
+
+testing "xargs -n exact match"
+testing "xargs -s exact match"
+testing "xargs -s 0"
+testing "xargs -s impossible"
+
+# xargs command_not_found - returns 127
+# xargs false - returns 1
diff --git a/toynet.h b/toynet.h
new file mode 100644 (file)
index 0000000..586db2e
--- /dev/null
+++ b/toynet.h
@@ -0,0 +1,17 @@
+// Included after toys.h, for network stuff.  Some build environments
+// don't include network support, so we shouldn't include it unless we're
+// going to build it.
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netpacket/packet.h>
+
diff --git a/toys.h b/toys.h
new file mode 100644 (file)
index 0000000..70e728f
--- /dev/null
+++ b/toys.h
@@ -0,0 +1,122 @@
+/* Toybox infrastructure.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include "generated/config.h"
+
+#include "lib/portability.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <libgen.h>
+#include <math.h>
+#include <pty.h>
+#include <pwd.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <sched.h>
+#include <shadow.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/sysinfo.h>
+#include <sys/swap.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include <utmpx.h>
+
+// Internationalization support
+
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "lib/lib.h"
+#include "toys/e2fs.h"
+
+#include "toynet.h"
+// Get list of function prototypes for all enabled command_main() functions.
+
+#define NEWTOY(name, opts, flags) void name##_main(void);
+#define OLDTOY(name, oldname, opts, flags)
+#include "generated/newtoys.h"
+#include "generated/globals.h"
+
+// These live in main.c
+
+struct toy_list *toy_find(char *name);
+void toy_init(struct toy_list *which, char *argv[]);
+void toy_exec(char *argv[]);
+
+// Flags describing command behavior.
+
+#define TOYFLAG_USR      (1<<0)
+#define TOYFLAG_BIN      (1<<1)
+#define TOYFLAG_SBIN     (1<<2)
+#define TOYMASK_LOCATION ((1<<4)-1)
+
+// This is a shell built-in function, running in the same process context.
+#define TOYFLAG_NOFORK   (1<<4)
+
+// Start command with a umask of 0 (saves old umask in this.old_umask)
+#define TOYFLAG_UMASK    (1<<5)
+
+// This command runs as root.
+#define TOYFLAG_STAYROOT (1<<6)
+#define TOYFLAG_NEEDROOT (1<<7)
+#define TOYFLAG_ROOTONLY (TOYFLAG_STAYROOT|TOYFLAG_NEEDROOT)
+
+// Array of available commands
+
+extern struct toy_list {
+  char *name;
+  void (*toy_main)(void);
+  char *options;
+  int flags;
+} toy_list[];
+
+// Global context shared by all commands.
+
+extern struct toy_context {
+  struct toy_list *which;  // Which entry in toy_list is this one?
+  int exitval;             // Value error_exit feeds to exit()
+  char **argv;             // Original command line arguments
+  unsigned optflags;       // Command line option flags from get_optflags()
+  char **optargs;          // Arguments left over from get_optflags()
+  int optc;                // Count of optargs
+  int exithelp;            // Should error_exit print a usage message first?
+  int old_umask;           // Old umask preserved by TOYFLAG_UMASK
+  jmp_buf *rebound;        // longjmp here instead of exit when do_rebound set
+} toys;
+
+// One big temporary buffer, for use by commands (not library functions).
+
+extern char toybuf[4096];
+
+#define GLOBALS(...)
+
+#define ARRAY_LEN(array) (sizeof(array)/sizeof(*array))
diff --git a/toys/e2fs.h b/toys/e2fs.h
new file mode 100644 (file)
index 0000000..7dcc03f
--- /dev/null
@@ -0,0 +1,181 @@
+/* mke2fs.h - Headers for ext2
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ */
+
+#include <linux/fs.h>
+
+// Stuff defined in linux/ext2_fs.h
+
+#define EXT2_SUPER_MAGIC  0xEF53
+
+struct ext2_superblock {
+  uint32_t inodes_count;      // Inodes count
+  uint32_t blocks_count;      // Blocks count
+  uint32_t r_blocks_count;    // Reserved blocks count
+  uint32_t free_blocks_count; // Free blocks count
+  uint32_t free_inodes_count; // Free inodes count
+  uint32_t first_data_block;  // First Data Block
+  uint32_t log_block_size;    // Block size
+  uint32_t log_frag_size;     // Fragment size
+  uint32_t blocks_per_group;  // Blocks per group
+  uint32_t frags_per_group;   // Fragments per group
+  uint32_t inodes_per_group;  // Inodes per group
+  uint32_t mtime;             // Mount time
+  uint32_t wtime;             // Write time
+  uint16_t mnt_count;         // Mount count
+  uint16_t max_mnt_count;     // Maximal mount count
+  uint16_t magic;             // Magic signature
+  uint16_t state;             // File system state
+  uint16_t errors;            // Behaviour when detecting errors
+  uint16_t minor_rev_level;   // minor revision level
+  uint32_t lastcheck;         // time of last check
+  uint32_t checkinterval;     // max. time between checks
+  uint32_t creator_os;        // OS
+  uint32_t rev_level;         // Revision level
+  uint16_t def_resuid;        // Default uid for reserved blocks
+  uint16_t def_resgid;        // Default gid for reserved blocks
+  uint32_t first_ino;         // First non-reserved inode
+  uint16_t inode_size;        // size of inode structure
+  uint16_t block_group_nr;    // block group # of this superblock
+  uint32_t feature_compat;    // compatible feature set
+  uint32_t feature_incompat;  // incompatible feature set
+  uint32_t feature_ro_compat; // readonly-compatible feature set
+  char     uuid[16];          // 128-bit uuid for volume
+  char     volume_name[16];   // volume name
+  char     last_mounted[64];  // directory where last mounted
+  uint32_t alg_usage_bitmap;  // For compression
+  // For EXT2_COMPAT_PREALLOC
+  uint8_t  prealloc_blocks;   // Nr of blocks to try to preallocate
+  uint8_t  prealloc_dir_blocks; //Nr to preallocate for dirs
+  uint16_t padding1;
+  // For EXT3_FEATURE_COMPAT_HAS_JOURNAL
+  uint8_t  journal_uuid[16];   // uuid of journal superblock
+  uint32_t journal_inum;       // inode number of journal file
+  uint32_t journal_dev;        // device number of journal file
+  uint32_t last_orphan;        // start of list of inodes to delete
+  uint32_t hash_seed[4];       // HTREE hash seed
+  uint8_t  def_hash_version;   // Default hash version to use
+  uint8_t  padding2[3];
+  uint32_t default_mount_opts;
+  uint32_t first_meta_bg;      // First metablock block group
+  uint32_t mkfs_time;          // Creation timestamp
+  uint32_t jnl_blocks[17];     // Backup of journal inode
+  // uint32_t reserved[172];      // Padding to the end of the block
+};
+
+struct ext2_group
+{
+  uint32_t block_bitmap;       // Block number of block bitmap
+  uint32_t inode_bitmap;       // Block number of inode bitmap
+  uint32_t inode_table;        // Block number of inode table
+  uint16_t free_blocks_count;  // How many free blocks in this group?
+  uint16_t free_inodes_count;  // How many free inodes in this group?
+  uint16_t used_dirs_count;    // How many directories?
+  uint16_t reserved[7];        // pad to 32 bytes
+};
+
+struct ext2_dentry {
+  uint32_t inode;         // Inode number
+  uint16_t rec_len;       // Directory entry length
+  uint8_t  name_len;      // Name length
+  uint8_t  file_type;
+  char     name[0];     // File name
+};
+
+struct ext2_inode {
+  uint16_t mode;        // File mode
+  uint16_t uid;         // Low 16 bits of Owner Uid
+  uint32_t size;        // Size in bytes
+  uint32_t atime;       // Access time
+  uint32_t ctime;       // Creation time
+  uint32_t mtime;       // Modification time
+  uint32_t dtime;       // Deletion Time
+  uint16_t gid;         // Low 16 bits of Group Id
+  uint16_t links_count; // Links count
+  uint32_t blocks;      // Blocks count
+  uint32_t flags;       // File flags
+  uint32_t reserved1;
+  uint32_t block[15];   // Pointers to blocks
+  uint32_t generation;  // File version (for NFS)
+  uint32_t file_acl;    // File ACL
+  uint32_t dir_acl;     // Directory ACL (or top bits of file length)
+  uint32_t faddr;       // Last block in file
+  uint8_t  frag;        // Fragment number
+  uint8_t  fsize;       // Fragment size
+  uint16_t pad1;
+  uint16_t uid_high;    // High bits of uid
+  uint16_t gid_high;    // High bits of gid
+  uint32_t reserved2;
+};
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC       0x0001
+#define EXT2_FEATURE_COMPAT_IMAGIC_INODES      0x0002
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x0004
+#define EXT2_FEATURE_COMPAT_EXT_ATTR           0x0008
+#define EXT2_FEATURE_COMPAT_RESIZE_INO         0x0010
+#define EXT2_FEATURE_COMPAT_DIR_INDEX          0x0020
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER    0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE      0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR       0x0004
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION      0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE         0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER          0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x0008
+#define EXT2_FEATURE_INCOMPAT_META_BG          0x0010
+
+#define EXT2_NAME_LEN 255
+
+// Ext2 directory file types.  Only the low 3 bits are used.  The
+// other bits are reserved for now.
+
+enum {
+  EXT2_FT_UNKNOWN,
+  EXT2_FT_REG_FILE,
+  EXT2_FT_DIR,
+  EXT2_FT_CHRDEV,
+  EXT2_FT_BLKDEV,
+  EXT2_FT_FIFO,
+  EXT2_FT_SOCK,
+  EXT2_FT_SYMLINK,
+  EXT2_FT_MAX
+};
+
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL                      0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL                       0x00000002 /* Undelete */
+#define EXT2_COMPR_FL                      0x00000004 /* Compress file */
+#define EXT2_SYNC_FL                       0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL                0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL                   0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL                   0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL                          0x00000080 /* do not update atime */
+#define EXT2_INDEX_FL                      0x00001000 /* hash-indexed directory */
+#define EXT3_JOURNAL_DATA_FL   0x00004000 /* file data should be journaled */
+#define EXT2_NOTAIL_FL                   0x00008000 /* file tail should not be merged */
+#define EXT2_DIRSYNC_FL                          0x00010000 /* Synchronous directory modifications */
+#define EXT2_TOPDIR_FL                   0x00020000 /* Top of directory hierarchies*/
+
+/*
+ * ioctl commands
+ */
+#define        EXT2_IOC_GETFLAGS               FS_IOC_GETFLAGS
+#define        EXT2_IOC_GETVERSION     FS_IOC_GETVERSION
+#define EXT2_IOC_SETVERSION    FS_IOC_SETVERSION
+#define EXT2_IOC_SETFLAGS              FS_IOC_SETFLAGS
+
+typedef struct _ext2_attrs {
+       char *name;
+       unsigned long inode_flag;
+       char optchar;
+}EXT2_ATTRS;
+
+int get_e2fs_flag(const int fd, struct stat *sb, unsigned long *flagval);
+int set_e2fs_flag(const int fd, struct stat *sb, unsigned long flagval);
+int get_e2fs_version(const int fd, unsigned long *version);
+int set_e2fs_version(const int fd, unsigned long version);
diff --git a/toys/lsb/README b/toys/lsb/README
new file mode 100644 (file)
index 0000000..5381b8a
--- /dev/null
@@ -0,0 +1,7 @@
+LSB commands
+
+Commands defined in the Linux Standard Base 4.1:
+http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cmdbehav.html
+
+Downloadable as one big file from:
+http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic.html
diff --git a/toys/lsb/addgroup.c b/toys/lsb/addgroup.c
new file mode 100644 (file)
index 0000000..a6f2c7d
--- /dev/null
@@ -0,0 +1,111 @@
+/* addgroup.c - create a new group
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/groupadd.html
+
+USE_ADDGROUP(NEWTOY(addgroup, "<1>2g#<0S", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+
+config ADDGROUP
+  bool "addgroup"
+  default y
+  help
+    usage: addgroup [-g GID] [USER] GROUP
+
+    Add a group or add a user to a group
+    
+      -g GID  Group id
+      -S      Create a system group
+*/
+
+#define FOR_addgroup
+#include "toys.h"
+
+#define GROUP_PATH        "/etc/group"
+#define SECURE_GROUP_PATH "/etc/gshadow"
+
+GLOBALS(
+  long gid;
+)
+
+/* Add a new group to the system, if GID is given then that is validated
+ * to be free, else a free GID is choosen by self.
+ * SYSTEM IDs are considered in the range 100 ... 999
+ * update_group(), updates the entries in /etc/group, /etc/gshadow files
+ */
+static void new_group()
+{
+  struct group grp;
+  int max = INT_MAX;
+
+  grp.gr_name = *toys.optargs;
+  grp.gr_passwd = (char *)"x";
+  
+  if(toys.optflags & FLAG_g) {
+    if(TT.gid > INT_MAX) error_exit("gid should be less than  '%d' ", INT_MAX);
+    if(getgrgid(TT.gid)) error_exit("group '%ld' is in use", TT.gid);
+    grp.gr_gid = TT.gid;
+  } else {
+    if(toys.optflags & FLAG_S) {
+      TT.gid = SYS_FIRST_ID;
+      max = SYS_LAST_ID;
+    } else {
+      TT.gid = SYS_LAST_ID + 1; //i.e. starting from 1000
+      max = 60000; // as per config file on Linux desktop
+    }
+    //find unused gid
+    while(TT.gid <= max) {
+      if(!getgrgid(TT.gid)) break;
+      if(TT.gid == max) error_exit("no more free gids left");
+      TT.gid++;
+    }
+    grp.gr_gid = TT.gid;
+  }
+  grp.gr_mem = NULL;
+
+  update_group(&grp, GROUP_PATH);
+  grp.gr_passwd = (char *)"!!";
+  update_group(&grp, SECURE_GROUP_PATH); //update the /etc/gshadow file
+}
+
+void addgroup_main(void)
+{
+  struct group *grp = NULL;
+  if(toys.optflags && toys.optc == 2) {
+    toys.exithelp = 1;
+    error_exit("options and user, group can't be together");
+  }
+
+  if(toys.optc == 2) {
+    //add user to group
+    //toys.optargs[0]- user, toys.optargs[1] - group
+    if(!getpwnam(toys.optargs[0])) error_exit("user '%s' does not exist", toys.optargs[0]);
+    if(!(grp = getgrnam(toys.optargs[1]))) error_exit("group '%s' does not exist", toys.optargs[1]);
+    if(!grp->gr_mem) {
+      grp->gr_mem = xzalloc(2 * sizeof(char*));
+      grp->gr_mem[0] = toys.optargs[0];
+    } else {
+      int i = 0, j = 0;
+      char **mem = NULL;
+      while(grp->gr_mem[i]) {
+        if(!strcmp(grp->gr_mem[i], toys.optargs[0])) return;
+        i++;
+      }
+      mem = xrealloc(mem, sizeof(char*) * (i+2)); //1 for name, 1 for NULL
+      mem[i] = toys.optargs[0];
+      mem[i+1] = NULL;
+      for(j = 0; j < i; j++) mem[j] = grp->gr_mem[j];
+
+      grp->gr_mem = mem;
+    }
+    update_group(grp, GROUP_PATH);
+    grp->gr_passwd = (char *)"!!"; //by default group passwd is NOT set.
+    update_group(grp, SECURE_GROUP_PATH); //update the /etc/gshadow file
+  } else {    //new group to be created
+    /* investigate the group to be created */
+    if((grp = getgrnam(*toys.optargs))) error_exit("group '%s' is in use", *toys.optargs);
+    setlocale(LC_ALL, "C");
+    is_valid_username(*toys.optargs);
+    new_group();
+  }
+}
diff --git a/toys/lsb/adduser.c b/toys/lsb/adduser.c
new file mode 100644 (file)
index 0000000..f4a7439
--- /dev/null
@@ -0,0 +1,234 @@
+/* adduser.c - add a new user
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/useradd.html
+
+USE_ADDUSER(NEWTOY(adduser, "<1>2u#<0G:s:g:h:SDH", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+
+config ADDUSER
+  bool "adduser"
+  default y
+  help
+    usage: adduser [OPTIONS] USER [GROUP]
+
+    Create new user, or add USER to GROUP
+    
+    -h DIR    Home directory
+    -g GECOS  GECOS field
+    -s SHELL  Login shell
+    -G GRP    Add user to existing group
+    -S        Create a system user
+    -D        Don't assign a password
+    -H        Don't create home directory
+    -u UID    User id
+*/
+
+#define FOR_adduser
+#include "toys.h"
+
+GLOBALS(
+  char *dir;
+  char *gecos;
+  char *shell;
+  char *u_grp;
+  long uid;
+  long gid;
+)
+
+/* exec_wait() function does a fork(), and exec the command,
+ * waits for the child to exit and return the status to parent
+ */
+static int exec_wait(char **args)
+{
+  int status = 0;
+  pid_t pid = fork();
+
+  if(pid == 0) xexec(args);
+  else if(pid > 0) waitpid(pid, &status, 0);
+  else error_exit("creating a process failed");
+  return WEXITSTATUS(status);
+}
+
+/* create_copy_skel(), This function will create the home directory of the
+ * user, by copying /etc/skel/ contents to /home/<username>.
+ * Then change the ownership of home dir to the UID and GID of new user,
+ * and Mode to 0700, i.e. rwx------ for user.
+ */
+int create_copy_skel(char *skel, char *hdir)
+{
+  char *args[5];
+  struct stat sb;
+  int ret;
+
+  if(toys.optflags & FLAG_H) return 0;
+
+  umask(0);
+  if((ret = stat(hdir, &sb)) != 0) {
+    args[0] = "cp";
+    args[1] = "-R";
+    args[2] = skel;
+    args[3] = hdir;
+    args[4] = NULL;
+    /* Copy /etc/skel to home dir */
+    toys.exitval = exec_wait(args);
+
+    args[0] = "chown";
+    args[1] = "-R";
+    args[2] = xmsprintf("%u:%u", TT.uid, TT.gid);
+    args[3] = hdir;
+    args[4] = NULL;
+    /*Change ownership to that of UID and GID of new user*/
+    toys.exitval = exec_wait(args);
+
+  }
+  else xprintf("Warning: home directory for the user already exists\n"
+      "Not copying any file from skel directory into it.\n");
+  if(chown(hdir, TT.uid, TT.gid)
+      || chmod(hdir, 0700)) {
+    perror_exit("chown/chmod failed for '%s'", hdir);
+  }
+  return 0;
+}
+
+/* Add a new group to the system, if UID is given then that is validated
+ * to be free, else a free UID is choosen by self.
+ * SYSTEM IDs are considered in the range 100 ... 999
+ * add_user(), add a new entry in /etc/passwd, /etc/shadow files
+ */
+static void new_user()
+{
+  struct passwd pwd;
+  char *entry, *args[5];
+  int max = INT_MAX;
+
+  pwd.pw_name = *toys.optargs;
+  pwd.pw_passwd = (char *)"x";
+
+  if(toys.optflags & FLAG_u) {
+    if(TT.uid > INT_MAX) error_exit("uid should be less than  '%d' ", INT_MAX);
+    if(getpwuid(TT.uid)) error_exit("user '%ld' is in use", TT.uid);
+    pwd.pw_uid = TT.uid;
+  } else {
+    if(toys.optflags & FLAG_S) {
+      TT.uid = SYS_FIRST_ID;
+      max = SYS_LAST_ID;
+    } else {
+      TT.uid = SYS_LAST_ID + 1; //i.e. starting from 1000
+      max = 60000; // as per config file on Linux desktop
+    }
+    //find unused uid
+    while(TT.uid <= max) {
+      if(!getpwuid(TT.uid)) break;
+      if(TT.uid == max) error_exit("no more free uids left");
+      TT.uid++;
+    }
+    pwd.pw_uid = TT.uid;
+  }
+
+  if(toys.optflags & FLAG_G) {
+    struct group *gr = getgrnam(TT.u_grp);
+    if(!gr) error_exit("The group '%s' doesn't exist", TT.u_grp);
+    TT.gid = gr->gr_gid;
+  } else {
+    /* Set the GID for the user, if not specified */
+    if(toys.optflags & FLAG_S) {
+      TT.gid = SYS_FIRST_ID;
+      max = SYS_LAST_ID;
+    } else TT.gid = ((TT.uid > SYS_LAST_ID) ? TT.uid : SYS_LAST_ID + 1);
+    if(getgrnam(pwd.pw_name)) error_exit("group '%s' is in use", pwd.pw_name);
+    //find unused gid
+    while(TT.gid <= max) {
+      if(!getgrgid(TT.gid)) break;
+      if(TT.gid == max) error_exit("no more free gids left");
+      TT.gid++;   
+    }
+  }
+  pwd.pw_gid = TT.gid;
+  if(!(toys.optflags & FLAG_g)) pwd.pw_gecos = "Linux User,";
+  else pwd.pw_gecos = TT.gecos;
+  if(!(toys.optflags & FLAG_h)) pwd.pw_dir = xmsprintf("/home/%s", *toys.optargs);
+  else pwd.pw_dir = TT.dir;
+  if(!(toys.optflags & FLAG_s)) pwd.pw_shell = get_shell();
+  else pwd.pw_shell = TT.shell;
+
+  if(!(toys.optflags & FLAG_G)) {
+    /* Create a new group for user */
+    /*add group, invoke addgroup command */
+    args[0] = "addgroup";
+    args[1] = toys.optargs[0];
+    args[2] = "-g";
+    args[3] = xmsprintf("%ld", pwd.pw_gid);
+    args[4] = NULL;
+    if(exec_wait(args)) error_msg("addgroup fail");
+  }
+
+  /*add user to system 
+   * 1. add an entry to /etc/passwd and /etcshadow file
+   * 2. Copy /etc/skel dir contents to use home dir
+   * 3. update the user passwd by running 'passwd' utility
+   */
+
+  /* 1. add an entry to /etc/passwd and /etcshadow file */
+  entry = xmsprintf("%s:%s:%ld:%ld:%s:%s:%s", pwd.pw_name, pwd.pw_passwd,
+      pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell);
+  if(add_user( "/etc/passwd", entry)) error_exit("updating passwd file failed");
+  free(entry);
+
+  if(toys.optflags & FLAG_S) 
+  entry = xmsprintf("%s:!!:%u::::::", pwd.pw_name, 
+      (unsigned)(time(NULL))/(24*60*60)); //passwd is not set initially
+  else entry = xmsprintf("%s:!!:%u:%ld:%ld:%ld:::", pwd.pw_name, 
+            (unsigned)(time(NULL))/(24*60*60), 0, 99999, 7); //passwd is not set initially
+  add_user( "/etc/shadow", entry);
+  free(entry);
+
+  /* craete home dir & copy skel dir to home */
+  if(!(toys.optflags & FLAG_S)) create_copy_skel("/etc/skel", pwd.pw_dir);
+
+  /*3. update the user passwd by running 'passwd' utility */
+  if(!(toys.optflags & FLAG_D)) {
+    args[0] = "passwd";
+    args[1] = pwd.pw_name;
+    args[2] = NULL;
+    if(exec_wait(args)) error_exit("changing user passwd failed");
+  }
+  if(toys.optflags & FLAG_G) {
+    /*add user to the existing group, invoke addgroup command */
+    args[0] = "addgroup";
+    args[1] = toys.optargs[0];
+    args[2] = TT.u_grp;
+    args[3] = NULL;
+    if(exec_wait(args)) error_exit("adding user to group Failed");
+  }
+}
+
+/* Entry point for adduser feature 
+ * Specifying options and User, Group at cmdline is treated as error.
+ * If only 2 parameters (Non-Option) are given, then User is added to the 
+ * Group
+ */
+void adduser_main(void)
+{
+  struct passwd *pwd = NULL;
+  if(toys.optflags && toys.optc == 2) {
+    toys.exithelp = 1;
+    error_exit("options and user, group can't be together");
+  }
+
+  if(toys.optc == 2) {
+    //add user to group
+    //toys.optargs[0]- user, toys.optargs[1] - group
+    char *args[4];
+    args[0] = "addgroup";
+    args[1] = toys.optargs[0];
+    args[2] = toys.optargs[1];
+    args[3] = NULL;
+    toys.exitval = exec_wait(args);
+  } else {    //new user to be created
+    /* investigate the user to be created */
+    if((pwd = getpwnam(*toys.optargs))) error_exit("user '%s' is in use", *toys.optargs);
+    is_valid_username(*toys.optargs); //validate the user name
+    new_user();
+  }
+}
diff --git a/toys/lsb/dmesg.c b/toys/lsb/dmesg.c
new file mode 100644 (file)
index 0000000..1003256
--- /dev/null
@@ -0,0 +1,55 @@
+/* dmesg.c - display/control kernel ring buffer.
+ *
+ * Copyright 2006, 2007 Rob Landley <rob@landley.net>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
+
+USE_DMESG(NEWTOY(dmesg, "s#n#c", TOYFLAG_BIN))
+
+config DMESG
+  bool "dmesg"
+  default y
+  help
+    usage: dmesg [-n level] [-s bufsize] | -c
+
+    Print or control the kernel ring buffer.
+
+    -n Set kernel logging level (1-9).
+    -s Size of buffer to read (in bytes), default 16384.
+    -c Clear the ring buffer after printing.
+*/
+
+#define FOR_dmesg
+#include "toys.h"
+#include <sys/klog.h>
+
+GLOBALS(
+  long level;
+  long size;
+)
+
+void dmesg_main(void)
+{
+  // For -n just tell kernel to which messages to keep.
+  if (toys.optflags & 2) {
+    if (klogctl(8, NULL, TT.level)) error_exit("klogctl");
+  } else {
+    int size, i, last = '\n';
+    char *data;
+
+    // Figure out how much data we need, and fetch it.
+    size = TT.size;
+    if (size<2) size = 16384;
+    data = xmalloc(size);
+    size = klogctl(3 + (toys.optflags&1), data, size);
+    if (size < 0) error_exit("klogctl");
+
+    // Display data, filtering out level markers.
+    for (i=0; i<size; ) {
+      if (last=='\n' && data[i]=='<') i += 3;
+      else xputc(last = data[i++]);
+    }
+    if (last!='\n') xputc('\n');
+    if (CFG_TOYBOX_FREE) free(data);
+  }
+}
diff --git a/toys/lsb/hostname.c b/toys/lsb/hostname.c
new file mode 100644 (file)
index 0000000..c8a15ba
--- /dev/null
@@ -0,0 +1,151 @@
+/* hostname.c - Get/Set the hostname
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gmail.com> 
+ *    Added support for "Fdifs" flags.
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/hostname.html
+
+USE_HOSTNAME(NEWTOY(hostname, "F:dfis", TOYFLAG_BIN))
+
+config HOSTNAME
+  bool "hostname"
+  default y
+  help
+    usage: hostname [newname]
+    -s  Short
+    -i  Addresses for the hostname
+    -d  DNS domain name
+    -f  Fully qualified domain name
+    -F FILE Use FILE's content as hostname
+    Get/Set the current hostname/DNS Domain name
+*/
+#define FOR_hostname
+
+#include "toys.h"
+
+GLOBALS(
+  char *file_name;
+)
+
+#ifndef _GNU_SOURCE
+/*
+ * locate character in string.
+ */
+static char *strchrnul(char *s, int c)
+{
+  while(*s != '\0' && *s != c) s++;
+  return (char*)s;
+}
+#endif
+
+/*
+ * Parse the given file to extract hostname,
+ * first word of last line with continuation
+ * and "#" (comment) taking care of*/
+void parse_get_hostname()
+{
+  char *file_bak = NULL;
+  char *ptr, *tmp;
+  int fd = xopen(TT.file_name, O_RDONLY);
+
+  while((ptr = get_line(fd)) != NULL) {
+      char *myline = NULL;
+validate:
+      tmp = ptr;
+
+      while(*ptr && *ptr <= ' ') ptr++; //skip space
+      if(*ptr == '\0') {
+          free(tmp);
+          continue;
+      }
+      if(ptr[strlen(ptr) - 1] == '\\') {
+          char *p = strrchr(ptr, '\\');
+          *p = '\0';      
+          myline = xstrdup(ptr);
+          free(tmp);
+          tmp = NULL;
+          while((ptr = get_line(fd)) != NULL) {
+              int  contin = 0;
+              if(ptr[strlen(ptr) - 1] == '\\') {
+                  p = strrchr(ptr, '\\');
+                  *p = '\0';      
+                  contin = 1;
+              }
+              myline = realloc(myline,strlen(ptr) + strlen(myline) + 1);
+              strcat(myline, ptr);
+              free(ptr);
+              if(!contin) break;
+          }
+          ptr = myline;
+          goto validate;
+      } else myline = xstrdup(ptr);
+
+      if(*myline == '#') {
+        free(myline);
+        myline = NULL;
+        continue;
+      }
+
+      file_bak = xstrdup(myline);
+      free(myline);
+      if(tmp) free(tmp);
+
+      if(file_bak) {
+        char *p = strchr(file_bak, ' ');
+        if(p) *p = '\0';
+        *(strchrnul(file_bak, '#')) = '\0';
+        if(sethostname(file_bak, strlen(file_bak))) 
+          perror_exit("set failed");
+      }
+  }
+}
+/*
+ * main for hostname
+ */
+void hostname_main(void)
+{
+  const char *hostname = NULL;
+  if(toys.optargs[0]) hostname = toys.optargs[0];
+  char *host_name = NULL;
+  gethostname(toybuf, sizeof(toybuf));
+  host_name = toybuf;
+  if((toys.optflags & FLAG_s) || (toys.optflags & FLAG_i) ||(toys.optflags & FLAG_f) || (toys.optflags & FLAG_d)) {
+      struct hostent *hostnt;
+      char *p;
+      h_errno = 0;
+      hostnt = gethostbyname(host_name);
+      if(!hostnt) perror_exit("%s %s", host_name, hstrerror(h_errno)); //return may be NULL.
+
+      p = strchrnul(hostnt->h_name, '.');
+      if (FLAG_f & toys.optflags) {
+          puts(hostnt->h_name);
+      } else if (toys.optflags & FLAG_s) {
+          *p = '\0';
+          puts(hostnt->h_name);
+      } else if (toys.optflags & FLAG_d) {
+          if (*p)
+              puts(p + 1);
+      } else {
+          if (hostnt->h_length == sizeof(struct in_addr)) {
+              struct in_addr **hostnt_addr_list = (struct in_addr **)hostnt->h_addr_list;
+              while (*hostnt_addr_list) {
+                  xprintf("%s ", inet_ntoa(**hostnt_addr_list));
+                  hostnt_addr_list++;
+              }
+              xputc('\n');
+          }
+      }
+  } 
+  else if(toys.optflags & FLAG_F) {
+      parse_get_hostname();
+  }
+  else if(hostname) { 
+      if(sethostname(hostname, strlen(hostname)))
+          perror_exit("set failed '%s'", hostname);
+  } else {
+      if(gethostname(toybuf, sizeof(toybuf)))
+          perror_exit("get failed");
+      xputs(toybuf);
+  }
+}
diff --git a/toys/lsb/killall.c b/toys/lsb/killall.c
new file mode 100644 (file)
index 0000000..fe43328
--- /dev/null
@@ -0,0 +1,82 @@
+/* killall.c - Send signal (default: TERM) to all processes with given names.
+ *
+ * Copyright 2012 Andreas Heck <aheck@gmx.de>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/killall.html
+
+USE_KILLALL(NEWTOY(killall, "<1?lqvi", TOYFLAG_USR|TOYFLAG_BIN))
+
+config KILLALL
+  bool "killall"
+  default y
+  help
+    usage: killall [-l] [-iqv] [-SIG] PROCESS_NAME...
+
+    Send a signal (default: TERM) to all processes with the given names.
+
+    -i ask for confirmation before killing
+    -l print list of all available signals
+    -q don't print any warnings or error messages
+    -v report if the signal was successfully sent
+*/
+
+#define FOR_killall
+#include "toys.h"
+
+GLOBALS(
+  int signum;
+  pid_t cur_pid;
+)
+
+static int kill_process(pid_t pid, char *name)
+{
+  int ret;
+
+  if (pid == TT.cur_pid) return 1;
+
+  if (toys.optflags & FLAG_i) {
+    sprintf(toybuf, "Signal %s(%d) ?", name, pid);
+    if (yesno(toybuf, 0) == 0) return 1;
+  }
+
+  toys.exitval = 0;
+
+  ret = kill(pid, TT.signum);
+  if (toys.optflags & FLAG_v)
+    printf("Killed %s(%d) with signal %d\n", name, pid, TT.signum);
+
+  if (ret == -1 && !(toys.optflags & FLAG_q)) perror("kill");
+  return 1;
+}
+
+void killall_main(void)
+{
+  char **names = toys.optargs;
+
+  TT.signum = SIGTERM;
+  toys.exitval++;
+
+  if (toys.optflags & FLAG_l) {
+    sig_to_num(NULL);
+    return;
+  }
+
+  if (**names == '-') {
+    if (0 > (TT.signum = sig_to_num((*names)+1))) {
+      if (toys.optflags & FLAG_q) exit(1);
+      error_exit("Invalid signal");
+    }
+    names++;
+  }
+
+  if (!*names) {
+    toys.exithelp++;
+    error_exit("Process name missing!");
+  }
+
+  TT.cur_pid = getpid();
+
+  for_each_pid_with_name_in(names, kill_process);
+
+  if (toys.exitval && !(toys.optflags & FLAG_q)) error_exit("No such process");
+}
diff --git a/toys/lsb/md5sum.c b/toys/lsb/md5sum.c
new file mode 100644 (file)
index 0000000..3b5571b
--- /dev/null
@@ -0,0 +1,237 @@
+/* md5sum.c - Calculate RFC 1321 md5 hash and sha1 hash.
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/md5sum.html
+ * and http://www.ietf.org/rfc/rfc1321.txt
+ *
+ * They're combined this way to share infrastructure, and because md5sum is
+ * and LSB standard command, sha1sum is just a good idea.
+
+USE_MD5SUM(NEWTOY(md5sum, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_MD5SUM_SHA1SUM(OLDTOY(sha1sum, md5sum, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config MD5SUM
+  bool "md5sum"
+  default y
+  help
+    usage: md5sum [FILE]...
+
+    Calculate md5 hash for each input file, reading from stdin if none.
+    Output one hash (16 hex digits) for each input file, followed by
+    filename.
+
+config MD5SUM_SHA1SUM
+  bool "sha1sum"
+  default y
+  depends on MD5SUM
+  help
+    usage: sha1sum [FILE]...
+
+    calculate sha1 hash for each input file, reading from stdin if one.
+    Output one hash (20 hex digits) for each input file, followed by
+    filename.
+*/
+
+#define FOR_md5sum
+#include "toys.h"
+
+GLOBALS(
+  unsigned state[5];
+  unsigned oldstate[5];
+  uint64_t count;
+  union {
+    char c[64];
+    unsigned i[16];
+  } buffer;
+)
+
+// for(i=0; i<64; i++) md5table[i] = abs(sin(i+1))*(1<<32);  But calculating
+// that involves not just floating point but pulling in -lm (and arguing with
+// C about whether 1<<32 is a valid thing to do on 32 bit platforms) so:
+
+static uint32_t md5table[64] = {
+  0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
+  0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+  0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
+  0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+  0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
+  0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+  0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
+  0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+  0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
+  0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+  0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+};
+
+// Mix next 64 bytes of data into md5 hash
+
+static void md5_transform(void)
+{
+  unsigned x[4], *b = (unsigned *)TT.buffer.c;
+  int i;
+
+  memcpy(x, TT.state, sizeof(x));
+
+  for (i=0; i<64; i++) {
+    unsigned int in, a, rot, temp;
+
+    a = (-i)&3;
+    if (i<16) {
+      in = i;
+      rot = 7+(5*(i&3));
+      temp = x[(a+1)&3];
+      temp = (temp & x[(a+2)&3]) | ((~temp) & x[(a+3)&3]);
+    } else if (i<32) {
+      in = (1+(5*i))&15;
+      temp = (i&3)+1;
+      rot = temp*5;
+      if (temp&2) rot--;
+      temp = x[(a+3)&3];
+      temp = (x[(a+1)&3] & temp) | (x[(a+2)&3] & ~temp);
+    } else if (i<48) {
+      in = (5+(3*(i&15)))&15;
+      rot = i&3;
+      rot = 4+(5*rot)+((rot+1)&6);
+      temp = x[(a+1)&3] ^ x[(a+2)&3] ^ x[(a+3)&3];
+    } else {
+      in = (7*(i&15))&15;
+      rot = (i&3)+1;
+      rot = (5*rot)+(((rot+2)&2)>>1);
+      temp = x[(a+2)&3] ^ (x[(a+1)&3] | ~x[(a+3)&3]);
+    }
+    temp += x[a] + b[in] + md5table[i];
+    x[a] = x[(a+1)&3] + ((temp<<rot) | (temp>>(32-rot)));
+  }
+  for (i=0; i<4; i++) TT.state[i] += x[i];
+}
+
+// Mix next 64 bytes of data into sha1 hash.
+
+static const unsigned rconsts[]={0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6};
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static void sha1_transform(void)
+{
+  int i, j, k, count;
+  unsigned *block = TT.buffer.i;
+  unsigned *rot[5], *temp;
+
+  // Copy context->state[] to working vars
+  for (i=0; i<5; i++) {
+    TT.oldstate[i] = TT.state[i];
+    rot[i] = TT.state + i;
+  }
+  // 4 rounds of 20 operations each.
+  for (i=count=0; i<4; i++) {
+    for (j=0; j<20; j++) {
+      unsigned work;
+
+      work = *rot[2] ^ *rot[3];
+      if (!i) work = (work & *rot[1]) ^ *rot[3];
+      else {
+        if (i==2) work = ((*rot[1]|*rot[2])&*rot[3])|(*rot[1]&*rot[2]);
+        else work ^= *rot[1];
+      }
+
+      if (!i && j<16)
+        work += block[count] = (rol(block[count],24)&0xFF00FF00)
+                             | (rol(block[count],8)&0x00FF00FF);
+      else
+        work += block[count&15] = rol(block[(count+13)&15]
+              ^ block[(count+8)&15] ^ block[(count+2)&15] ^ block[count&15], 1);
+      *rot[4] += work + rol(*rot[0],5) + rconsts[i];
+      *rot[1] = rol(*rot[1],30);
+
+      // Rotate by one for next time.
+      temp = rot[4];
+      for (k=4; k; k--) rot[k] = rot[k-1];
+      *rot = temp;
+      count++;
+    }
+  }
+  // Add the previous values of state[]
+  for (i=0; i<5; i++) TT.state[i] += TT.oldstate[i];
+}
+
+// Fill the 64-byte working buffer and call transform() when full.
+
+static void hash_update(char *data, unsigned int len, void (*transform)(void))
+{
+  unsigned int i, j;
+
+  j = TT.count & 63;
+  TT.count += len;
+
+  // Enough data to process a frame?
+  if ((j + len) > 63) {
+    i = 64-j;
+    memcpy(TT.buffer.c + j, data, i);
+    transform();
+    for ( ; i + 63 < len; i += 64) {
+      memcpy(TT.buffer.c, data + i, 64);
+      transform();
+    }
+    j = 0;
+  } else i = 0;
+  // Grab remaining chunk
+  memcpy(TT.buffer.c + j, data + i, len - i);
+}
+
+// Callback for loopfiles()
+
+static void do_hash(int fd, char *name)
+{
+  uint64_t count;
+  int i, sha1=toys.which->name[0]=='s';;
+  char buf;
+  void (*transform)(void);
+
+  /* SHA1 initialization constants  (md5sum uses first 4) */
+  TT.state[0] = 0x67452301;
+  TT.state[1] = 0xEFCDAB89;
+  TT.state[2] = 0x98BADCFE;
+  TT.state[3] = 0x10325476;
+  TT.state[4] = 0xC3D2E1F0;
+  TT.count = 0;
+
+  transform = sha1 ? sha1_transform : md5_transform;
+  for (;;) {
+    i = read(fd, toybuf, sizeof(toybuf));
+    if (i<1) break;
+    hash_update(toybuf, i, transform);
+  }
+
+  count = TT.count << 3;
+
+  // End the message by appending a "1" bit to the data, ending with the
+  // message size (in bits, big endian), and adding enough zero bits in
+  // between to pad to the end of the next 64-byte frame.
+  //
+  // Since our input up to now has been in whole bytes, we can deal with
+  // bytes here too.
+
+  buf = 0x80;
+  do {
+    hash_update(&buf, 1, transform);
+    buf = 0;
+  } while ((TT.count & 63) != 56);
+  if (sha1) count=bswap_64(count);
+  for (i = 0; i < 8; i++) TT.buffer.c[56+i] = count >> (8*i);
+  transform();
+
+  if (sha1)
+    for (i = 0; i < 20; i++)
+      printf("%02x", 255&(TT.state[i>>2] >> ((3-(i & 3)) * 8)));
+  else for (i=0; i<4; i++) printf("%08x", SWAP_BE32(TT.state[i]));
+
+  // Wipe variables.  Cryptographer paranoia.
+  memset(&TT, 0, sizeof(TT));
+
+  printf("  %s\n", name);
+}
+
+void md5sum_main(void)
+{
+  loopfiles(toys.optargs, do_hash);
+}
diff --git a/toys/lsb/mknod.c b/toys/lsb/mknod.c
new file mode 100644 (file)
index 0000000..bf9288a
--- /dev/null
@@ -0,0 +1,41 @@
+/* mknod.c - make block or character special file
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mknod.html
+
+USE_MKNOD(NEWTOY(mknod, "<2>4", TOYFLAG_BIN))
+
+config MKNOD
+  bool "mknod"
+  default y
+  help
+    usage: mknod NAME TYPE [MAJOR MINOR]
+
+    Create a special file NAME with a given type, possible types are
+    b  block device
+    c or u     character device
+    p  named pipe (ignores MAJOR/MINOR)
+*/
+
+#define FOR_mknod
+#include "toys.h"
+
+void mknod_main(void)
+{
+  mode_t modes[] = {S_IFIFO, S_IFCHR, S_IFCHR, S_IFBLK};
+  int major=0, minor=0, type;
+  int mode = 0660;
+
+  type = stridx("pcub", *toys.optargs[1]);
+  if (type == -1) perror_exit("bad type '%c'", *toys.optargs[1]);
+  if (type) {
+    if (toys.optc != 4) perror_exit("need major/minor");
+
+    major = atoi(toys.optargs[2]);
+    minor = atoi(toys.optargs[3]);
+  }
+
+  if (mknod(toys.optargs[0], mode | modes[type], makedev(major, minor)))
+    perror_exit("mknod %s failed", toys.optargs[0]);
+}
diff --git a/toys/lsb/mktemp.c b/toys/lsb/mktemp.c
new file mode 100644 (file)
index 0000000..86c2033
--- /dev/null
@@ -0,0 +1,52 @@
+/* mktemp.c - Create a temporary file or directory.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mktemp.html
+
+USE_MKTEMP(NEWTOY(mktemp, ">1q(directory)d(tmpdir)p:", TOYFLAG_BIN))
+
+config MKTEMP
+  bool "mktemp"
+  default y
+  help
+    usage: mktemp [-dq] [-p DIR] [TEMPLATE]
+
+    Safely create new file and print its name. Default TEMPLATE is
+    /tmp/tmp.XXXXXX and each trailing X is replaced with random char.
+
+    -d, --directory        Create directory instead of file
+    -p DIR, --tmpdir=DIR   Put new file in DIR
+    -q                     Quiet
+*/
+
+#define FOR_mktemp
+#include "toys.h"
+
+GLOBALS(
+  char * tmpdir;
+)
+
+void mktemp_main(void)
+{
+  int  d_flag = toys.optflags & FLAG_d;
+  char *tmp;
+
+  tmp = *toys.optargs;
+
+  if (!tmp) {
+    if (!TT.tmpdir) TT.tmpdir = "/tmp";
+    tmp = "tmp.xxxxxx";
+  }
+  if (TT.tmpdir) tmp = xmsprintf("%s/%s", TT.tmpdir ? TT.tmpdir : "/tmp",
+    *toys.optargs ? *toys.optargs : "tmp.XXXXXX");
+
+  if (d_flag ? mkdtemp(tmp) == NULL : mkstemp(tmp) == -1)
+    if (toys.optflags & FLAG_q)
+      perror_exit("Failed to create temporary %s",
+        d_flag ? "directory" : "file");
+
+  xputs(tmp);
+
+  if (CFG_TOYBOX_FREE && TT.tmpdir) free(tmp);
+}
diff --git a/toys/lsb/mount.c b/toys/lsb/mount.c
new file mode 100644 (file)
index 0000000..3fe2777
--- /dev/null
@@ -0,0 +1,1494 @@
+/* mount.c - A mount program.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *       -- Added Support for mounting NFS
+ *
+ * Not in SUSv4.
+
+USE_MOUNT(NEWTOY(mount, ">2o*t:O:farwv", TOYFLAG_BIN))
+
+config MOUNT
+  bool "mount"
+  default y
+  help
+    usage: mount [-arw] [-t type] [-o OPT] [-O OPT] [dev] [dir]
+
+    -a        Mount all filesystems in fstab.
+    -f        Dry run.
+    -v        Verbose
+    -t FSTYPE filesystem type
+    -r        Read-only mount
+    -w        Read-Write mount(default)
+    -o OPT    options
+    -O OPT    options, mount only filestsystems with these options (used with -a only)
+
+    OPT:
+    loop    loop device
+    [a]sync   Writes are [a]synchronous
+    [no]atime   Disable/enable updates to inode access times
+    [no]diratime  Disable/enable atime updates to directories
+    [no]relatime  Disable/enable atime updates relative to modification time
+    [no]dev   (Dis)allow use of special device files
+    [no]exec  (Dis)allow use of executable files
+    [no]suid  (Dis)allow set-user-id-root programs
+    [r]shared   Convert [recursively] to a shared subtree
+    [r]slave  Convert [recursively] to a slave subtree
+    [r]private  Convert [recursively] to a private subtree
+    [un]bindable  Make mount point [un]able to be bind mounted
+    [r]bind   Bind a file or directory [recursively] to another location
+    move    Relocate an existing mount point
+    remount   Remount a mounted filesystem, changing flags
+    ro/rw     Same as -r/-w
+
+*/
+
+#define FOR_mount
+#include "toys.h"
+#ifdef TIZEN_SECURE_MOUNT
+#include <sys/reboot.h>
+#endif
+
+GLOBALS(
+  char *O_options;
+  char *type;
+  struct arg_list *pkt_opt;
+  char *o_options;
+)
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <linux/loop.h>
+#include <mntent.h>
+
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC (1 << 7)
+#endif
+
+#ifndef MS_UNION
+# define MS_UNION       (1 << 8)
+#endif
+
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
+#ifndef MS_SILENT
+#define MS_SILENT (1 << 15)
+#endif
+
+#ifndef MS_UNBINDABLE
+#define MS_UNBINDABLE (1 << 17)
+#endif
+
+#ifndef MS_PRIVATE
+#define MS_PRIVATE (1 << 18)
+#endif
+#ifndef MS_SLAVE
+#define MS_SLAVE (1 << 19)
+#endif
+
+#ifndef MS_SHARED
+#define MS_SHARED (1 << 20)
+#endif
+
+#ifndef MS_RELATIME
+#define MS_RELATIME  (1 << 21)
+#endif
+
+#ifndef MS_STRICTATIME
+#define MS_STRICTATIME  (1 << 24)
+#endif
+
+#ifndef MS_NOAUTO
+#define MS_NOAUTO (1 << 29)
+#endif
+
+#ifndef MS_USER
+#define MS_USER (1 << 28)
+#endif
+
+struct mount_opts {
+  const char str[16];
+  unsigned long rwmask;
+  unsigned long rwset;
+  unsigned long rwnoset;
+};
+
+struct extra_opts {
+  char *str;
+  char *end;
+  int used_size;
+  int alloc_size;
+};
+
+/*
+ * These options define the function of "mount(2)".
+ */
+#define MS_TYPE  (MS_REMOUNT|MS_BIND|MS_MOVE)
+
+
+static const struct mount_opts options[] = {
+  /* name      mask    set    noset    */
+  { "defaults",    0,  0,    0  },
+  { "async",    MS_SYNCHRONOUS,  0,    MS_SYNCHRONOUS  },
+  { "atime",    MS_NOATIME,  0,    MS_NOATIME  },
+  { "bind",    MS_TYPE,  MS_BIND,  0,    },
+  { "dev",    MS_NODEV,  0,    MS_NODEV  },
+  { "diratime",  MS_NODIRATIME,  0,    MS_NODIRATIME  },
+  { "dirsync",  MS_DIRSYNC,  MS_DIRSYNC,  0    },
+  { "exec",    MS_NOEXEC,  0,    MS_NOEXEC  },
+  { "move",    MS_TYPE,  MS_MOVE,  0    },
+  { "mand",    MS_MANDLOCK, MS_MANDLOCK, 0    },
+  { "relatime",    MS_RELATIME, MS_RELATIME,  0    },
+  { "strictatime", MS_STRICTATIME, MS_STRICTATIME,  0    },
+  { "rbind",    MS_TYPE,  MS_BIND|MS_REC,  0,    },
+  { "recurse",  MS_REC,    MS_REC,    0    },
+  { "remount",  MS_TYPE,  MS_REMOUNT,  0    },
+  { "ro",      MS_RDONLY,  MS_RDONLY,  0    },
+  { "rw",      MS_RDONLY,  0,    MS_RDONLY  },
+  { "suid",    MS_NOSUID,  0,    MS_NOSUID  },
+  { "sync",    MS_SYNCHRONOUS,  MS_SYNCHRONOUS,  0    },
+  { "shared",    MS_SHARED,  MS_SHARED,  0    },
+  { "slave",    MS_SLAVE,  MS_SLAVE,  0    },
+  { "private",  MS_PRIVATE,  MS_PRIVATE,  0    },
+  { "bindable",  MS_UNBINDABLE,  0, MS_UNBINDABLE  },
+  { "rshared",  MS_SHARED,  MS_SHARED|MS_REC,  0    },
+  { "rslave",    MS_SLAVE,  MS_SLAVE|MS_REC,  0    },
+  { "rprivate",  MS_PRIVATE,  MS_PRIVATE|MS_REC,  0    },
+  { "unbindable",  MS_UNBINDABLE,  MS_UNBINDABLE, 0  },
+  { "verbose",  MS_SILENT,  MS_SILENT,  0    },
+  { "loud",  MS_SILENT,  0, MS_SILENT    },
+  { "user",  MS_USER,   MS_USER, 0   },
+  { "users",  MS_USER,   MS_USER, 0   },
+  { "auto",  MS_NOAUTO,  0,  MS_NOAUTO   },
+  { "union",  MS_UNION,  MS_UNION, 0   },
+};
+
+static int daemonize(void) {
+  int fd;
+
+  fd = open("/dev/null", O_RDWR);
+  if (fd < 0) fd = open("/", O_RDONLY, 0666);
+
+  pid_t pid = fork();
+
+  if(pid < 0){
+    printf("DAEMON: fail to fork");
+    return -1;
+  }
+  if (pid) exit(EXIT_SUCCESS);
+
+  setsid();
+  dup2(fd, 0);
+  dup2(fd, 1);
+  dup2(fd, 2);
+  close(fd);
+  return 0;
+}
+
+static void add_extra_option(struct extra_opts *extra, char *s)
+{
+  int len = strlen(s);
+  int newlen;
+
+  if (extra->str) len++;      /* +1 for ',' */
+
+  newlen = extra->used_size + len;
+  if (newlen >= extra->alloc_size) {
+    char *new;
+    new = (char *)xrealloc(extra->str, newlen + 1);  /* +1 for NUL */
+    extra->str = new;
+    extra->end = extra->str + extra->used_size;
+    extra->alloc_size = newlen;
+  }
+
+  if (extra->used_size) {
+    *extra->end = ',';
+    extra->end++;
+  }
+  strcpy(extra->end, s);
+  extra->used_size += len;
+}
+
+static unsigned long parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char **loopdev)
+{
+  char *s, *p;
+
+  if (strcmp(arg, "defaults") == 0) return rwflag;
+  else p = arg;
+
+  *loop = 0;
+  while ((s = strsep(&p, ",")) != NULL) {
+    char *opt = s;
+    unsigned int i;
+    int res, no = s[0] == 'n' && s[1] == 'o';
+
+    if (no) s += 2;
+    if (strncmp(s, "loop=", 5) == 0) {
+      *loop = 1;
+      *loopdev = xstrdup(s+5);
+      continue;
+    }
+
+    if (strcmp(s, "loop") == 0) {
+      *loop = 1;
+      continue;
+    }
+    for (i = 0, res = 1; i < ARRAY_LEN(options); i++) {
+      res = strcmp(s, options[i].str);
+
+      if (res == 0) {
+        rwflag &= ~options[i].rwmask;
+        if (no) rwflag |= options[i].rwnoset;
+        else rwflag |= options[i].rwset;
+        break;
+      }
+    }
+
+    if (res != 0 && s[0])
+      add_extra_option(extra, opt);
+  }
+
+  return rwflag;
+}
+
+/* NFS Mount implementation ..... */
+
+#define NFSD   100003
+#define NFSV  0
+
+#define MOUNTD  100005
+#define MOUNTV  0
+#define MOUNTPORT 635
+
+#define MOUNTP_NULL    0
+#define MOUNTP_MNT    1
+#define MOUNTP_DUMP    2
+#define MOUNTP_UMNT    3
+#define MOUNTP_UMNTALL  4
+#define MOUNTP_EXPORT  5
+#define MOUNTP_EXPALL  6
+#define MOUNTP_PATHCONF 7
+
+
+#define MOUNTP3_NULL  0
+#define MOUNTP3_MNT    1
+#define MOUNTP3_DUMP  2
+#define MOUNTP3_UMNT  3
+#define MOUNTP3_UMNTALL  4
+#define MOUNTP3_EXPORT  5
+
+#define MNTPATHLEN    1024
+#define FHSIZE      32
+#define FHSIZE3      64
+
+typedef char fhandle[FHSIZE];
+
+typedef struct {
+  unsigned int fhandle3_len;
+  char *fhandle3_val;
+} fhandle3;
+
+typedef enum mountstat3 {
+  MNT_OK = 0,
+  MNT3ERR_PERM = 1,
+  MNT3ERR_NOENT = 2,
+  MNT3ERR_IO = 5,
+  MNT3ERR_ACCES = 13,
+  MNT3ERR_NOTDIR = 20,
+  MNT3ERR_INVAL = 22,
+  MNT3ERR_NAMETOOLONG = 63,
+  MNT3ERR_NOTSUPP = 10004,
+  MNT3ERR_SERVERFAULT = 10006,
+} mountstat3;
+
+typedef struct fhstatus {
+  unsigned int fhs_status;
+  union {
+    fhandle fhs_fhandle;
+  } fhstatus_u;
+} fhstatus;
+
+typedef struct mountres3_ok {
+  fhandle3 fhandle;
+  struct {
+    unsigned int auth_flavours_len;
+    char *auth_flavours_val;
+  } auth_flavours;
+} mountres3_ok;
+
+typedef struct mountres3 {
+  mountstat3 fhs_status;
+  union {
+    mountres3_ok mountinfo;
+  } mountres3_u;
+} mountres3;
+
+static bool_t decode_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+  if (!xdr_u_int(xdrs, &objp->fhs_status))
+    return FALSE;
+  if (objp->fhs_status == 0)
+    return xdr_opaque(xdrs, objp->fhstatus_u.fhs_fhandle, FHSIZE);
+  return TRUE;
+}
+
+static bool_t encode_dirpath(XDR *xdrs, char **objp)
+{
+  return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+static bool_t decode_mountres(XDR *xdrs, mountres3 *objp)
+{
+  if (!xdr_enum(xdrs, (enum_t *) &objp->fhs_status))
+    return FALSE;
+  if (objp->fhs_status == MNT_OK) {
+    if (!xdr_bytes(xdrs,
+        (char **) &objp->mountres3_u.mountinfo.fhandle.fhandle3_val,
+        (unsigned int *) &objp->mountres3_u.mountinfo.fhandle.fhandle3_len,
+        FHSIZE3))
+      return FALSE;
+    return xdr_array(xdrs,
+        &(objp->mountres3_u.mountinfo.auth_flavours.auth_flavours_val),
+        &(objp->mountres3_u.mountinfo.auth_flavours.auth_flavours_len),
+        ~0, sizeof(int), (xdrproc_t) xdr_int);
+
+  }
+  return TRUE;
+}
+
+enum {
+  NFS_SOFT      = (1 << 0),
+  NFS_INTR      = (1 << 1),
+  NFS_SECURE    = (1 << 2),
+  NFS_POSIX     = (1 << 3),
+  NFS_NOCTO     = (1 << 4),
+  NFS_NOAC      = (1 << 5),
+  NFS_TCP       = (1 << 6),
+  NFS_VER3      = (1 << 7),
+  NFS_KERBEROS  = (1 << 8),
+  NFS_NONLM     = (1 << 9),
+  NFS_NORDIRPLUS  = (1 << 14),
+};
+
+#define MAX_NFSV ((opt.version >= 4) ? 3 : 2)
+
+static const char  __attribute__((aligned(1))) nfs_optvals[]=
+/* 0 */ "rsize\0"
+/* 1 */ "wsize\0"
+/* 2 */ "timeo\0"
+/* 3 */ "retrans\0"
+/* 4 */ "acregmin\0"
+/* 5 */ "acregmax\0"
+/* 6 */ "acdirmin\0"
+/* 7 */ "acdirmax\0"
+/* 8 */ "actimeo\0"
+/* 9 */ "retry\0"
+/* 10 */ "port\0"
+/* 11 */ "mountport\0"
+/* 12 */ "mounthost\0"
+/* 13 */ "mountprog\0"
+/* 14 */ "mountvers\0"
+/* 15 */ "nfsprog\0"
+/* 16 */ "nfsvers\0"
+/* 17 */ "vers\0"
+/* 18 */ "proto\0"
+/* 19 */ "namlen\0"
+/* 20 */ "addr\0";
+
+static const char __attribute__((aligned(1))) nfs_opts[]=
+"bg\0"
+"fg\0"
+"soft\0"
+"hard\0"
+"intr\0"
+"posix\0"
+"cto\0"
+"ac\0"
+"tcp\0"
+"udp\0"
+"lock\0"
+"rdirplus\0";
+
+
+typedef struct optval_s {
+  char* mountd;
+  int daemonized;
+  int port;
+  int mountport;
+  int proto;
+  int bg;
+  int retry;
+  int mountprog;
+  int mountvers;
+  int nfsprog;
+  int nfsvers;
+  int tcp;
+  int soft;
+  int intr;
+  int posix;
+  int nocto;
+  int noac;
+  int nordirplus;
+  int nolock;
+  int version;
+} optval;
+
+typedef struct filehdrv2_s{
+  char          data[32];
+} filehdrv2_t;
+
+typedef struct filehdrv3_s {
+  unsigned short      size;
+  unsigned char       data[64];
+} filehdrv3_t;
+
+typedef struct nfsdata_s {
+  int      version;
+  int      fd;
+  filehdrv2_t  old_root;
+  int      flags;
+  int      rsize;
+  int      wsize;
+  int      timeo;
+  int      retrans;
+  int      acregmin;
+  int      acregmax;
+  int      acdirmin;
+  int      acdirmax;
+  struct sockaddr_in  addr;
+  char    hostname[256];
+  int      namlen;
+  unsigned int  bsize;
+  filehdrv3_t  root;
+} nfsdata_t;
+
+static void rpc_msg(const char *msg)
+{
+  int len;
+  while (msg[0] == ' ' || msg[0] == ':')
+    msg++;
+  len = strlen(msg);
+  while (len && msg[len - 1] == '\n')
+    len--;
+  error_msg("%.*s", len, msg);
+}
+
+static int getindex(const char *search, char *key)
+{
+  int idx =0;
+  while (*search) {
+    if (strcmp(search, key) == 0)
+      return idx;
+    search += strlen(search) + 1;
+    idx++;
+  }
+  return -1;
+}
+
+static int parse_options(char* opts, optval* optv, nfsdata_t *nfsdata)
+{
+  char *opt;
+  if (!opts) return 0;
+
+  for (opt = strtok(opts, ","); opt; opt = strtok(NULL, ",")) {
+    char *opteq = strchr(opt, '=');
+    if (opteq) {
+      int val, idx = 0;
+      *opteq++ = '\0';
+
+      idx = getindex(nfs_optvals, opt);
+      switch (idx) {
+        case 12: // "mounthost"
+          optv->mountd = xstrndup(opteq, strcspn(opteq, " \t\n\r,"));
+          continue;
+        case 18: // "proto"
+          if (!strncmp(opteq, "tcp", 3))
+            optv->tcp = 1;
+          else if (!strncmp(opteq, "udp", 3))
+            optv->tcp = 0;
+          else
+            error_msg("Unrecognized Protocol.");
+          continue;
+        case 20: // "addr" - ignore
+          continue;
+        case -1: // unknown
+          continue;
+        default: break;
+      }
+
+      val = atoi(opteq);
+      switch (idx) {
+        case 0: // "rsize"
+          nfsdata->rsize = val;
+          continue;
+        case 1: // "wsize"
+          nfsdata->wsize = val;
+          continue;
+        case 2: // "timeo"
+          nfsdata->timeo = val;
+          continue;
+        case 3: // "retrans"
+          nfsdata->retrans = val;
+          continue;
+        case 4: // "acregmin"
+          nfsdata->acregmin = val;
+          continue;
+        case 5: // "acregmax"
+          nfsdata->acregmax = val;
+          continue;
+        case 6: // "acdirmin"
+          nfsdata->acdirmin = val;
+          continue;
+        case 7: // "acdirmax"
+          nfsdata->acdirmax = val;
+          continue;
+        case 8: // "actimeo"
+          nfsdata->acregmin = val;
+          nfsdata->acregmax = val;
+          nfsdata->acdirmin = val;
+          nfsdata->acdirmax = val;
+          continue;
+        case 9: // "retry"
+          optv->retry = val;
+          continue;
+        case 10: // "port"
+          optv->port = val;
+          continue;
+        case 11: // "mountport"
+          optv->mountport = val;
+          continue;
+        case 13: // "mountprog"
+          optv->mountprog = val;
+          continue;
+        case 14: // "mountvers"
+          optv->mountvers = val;
+          continue;
+        case 15: // "nfsprog"
+          optv->nfsprog = val;
+          continue;
+        case 16: // "nfsvers" /*FALL_THROUGH*/
+        case 17: // "vers"
+          optv->nfsvers = val;
+          continue;
+        case 19: // "namlen"
+          nfsdata->namlen = val;
+          continue;
+        default:
+          error_exit("unknown nfs mount parameter: %s=%d", opt, val);
+          break;
+      }
+    } else { /* not of the form opt=val */
+      int val = 1;
+      if (!strncmp(opt, "no", 2)) {
+        val = 0;
+        opt += 2;
+      }
+
+      switch (getindex(nfs_opts, opt)) {
+        case 0: // "bg"
+          optv->bg = val;
+          break;
+        case 1: // "fg"s
+          optv->bg = !val;
+          break;
+        case 2: // "soft"
+          optv->soft = val;
+          break;
+        case 3: // "hard"
+          optv->soft = !val;
+          break;
+        case 4: // "intr"
+          optv->intr = val;
+          break;
+        case 5: // "posix"
+          optv->posix = val;
+          break;
+        case 6: // "cto"
+          optv->nocto = !val;
+          break;
+        case 7: // "ac"
+          optv->noac = !val;
+          break;
+        case 8: // "tcp"
+          optv->tcp = val;
+          break;
+        case 9: // "udp"
+          optv->tcp = !val;
+          break;
+        case 10: // "lock"
+          if (optv->version >= 3) optv->nolock = !val;
+          else error_msg("warning: option nolock is not supported");
+          break;
+        case 11: //rdirplus
+          optv->nordirplus = !val;
+          break;
+        default:
+          error_exit("unknown nfs mount option: %s%s", val ? "" : "no",
+              opt);
+          break;
+      }
+    }
+  }
+
+  return 0;
+}
+
+static int do_nfsmount(char *dev, char *dir, unsigned long rwflag, char *opts)
+{
+  union {
+    fhstatus nfsv2;
+    mountres3 nfsv3;
+  } rpcstatus;
+
+  int ret = -ETIMEDOUT;
+  int msock = -1, fsock = -1;
+  char *tmp;
+  char *hostname, *pathname;
+  CLIENT *rpcclient = NULL;
+  nfsdata_t nfs_data;
+  struct sockaddr_in nfsd;
+  struct sockaddr_in mountd;
+  struct hostent *hent;
+
+  struct pmap pm_data;
+  struct pmaplist *list;
+  struct timeval to_total;
+  struct timeval to_retry;
+  time_t current;
+  time_t previous;
+  time_t timeout;
+  optval opt;
+
+  opt.daemonized = 0;
+  opt.mountd = NULL;
+  opt.bg = 0;
+  opt.version = 4;
+
+  hostname = xstrdup(dev);
+  tmp = strchr(hostname, ':');
+  pathname = tmp + 1;
+  *tmp = '\0';
+
+  memset(&nfsd, 0, sizeof(nfsd));
+  nfsd.sin_family = AF_INET;
+
+  if (!inet_aton(hostname, &nfsd.sin_addr)) {
+    hent = gethostbyname(hostname);
+    if (hent == NULL) perror_exit("Can't resolve Host.");
+    if (hent->h_length != (int)sizeof(struct in_addr))
+      perror_exit("Only IPv4 Supported.");
+    memcpy(&nfsd.sin_addr, hent->h_addr_list[0], sizeof(struct in_addr));
+  }
+
+  memcpy(&mountd, &nfsd, sizeof(mountd));
+  memset(&nfs_data, 0, sizeof(nfs_data));
+  nfs_data.retrans  = 3;
+  nfs_data.acregmin = 3;
+  nfs_data.acregmax = 60;
+  nfs_data.acdirmin = 30;
+  nfs_data.acdirmax = 60;
+  nfs_data.namlen   = NAME_MAX;
+
+  opt.soft = 0;
+  opt.intr = 0;
+  opt.posix = 0;
+  opt.nocto = 0;
+  opt.nolock = 0;
+  opt.noac = 0;
+  opt.nordirplus = 0;
+  opt.retry = 10000;
+  opt.tcp = 1;
+  opt.mountprog = MOUNTD;
+  opt.mountvers = 0;
+  opt.port = 0;
+  opt.mountport = 0;
+  opt.nfsprog = NFSD;
+  opt.nfsvers = 0;
+
+  parse_options(opts, &opt, &nfs_data);
+
+  opt.proto = (opt.tcp)? IPPROTO_TCP : IPPROTO_UDP;
+
+  nfs_data.flags = (opt.soft ? NFS_SOFT : 0)
+      | (opt.intr ? NFS_INTR : 0)
+      | (opt.posix ? NFS_POSIX : 0)
+      | (opt.nocto ? NFS_NOCTO : 0)
+      | (opt.noac ? NFS_NOAC : 0)
+      | (opt.nordirplus ? NFS_NORDIRPLUS : 0);
+    if (opt.version >= 2)
+      nfs_data.flags |= (opt.tcp ? NFS_TCP : 0);
+    if (opt.version >= 3)
+      nfs_data.flags |= (opt.nolock ? NFS_NONLM : 0);
+    if (opt.nfsvers > MAX_NFSV || opt.mountvers > MAX_NFSV)
+      error_exit("NFSv%d not supported", opt.nfsvers);
+    if (opt.nfsvers && !opt.mountvers)
+      opt.mountvers = (opt.nfsvers < 3) ? 1 : opt.nfsvers;
+    if (opt.nfsvers && opt.nfsvers < opt.mountvers)
+      opt.mountvers = opt.nfsvers;
+
+    /* Adjust options if none specified */
+    if (!nfs_data.timeo) nfs_data.timeo = opt.tcp ? 70 : 7;
+    nfs_data.version = opt.version;
+
+    if (opt.mountd) {
+    if (opt.mountd[0] >= '0' && opt.mountd[0] <= '9') {
+      mountd.sin_family = AF_INET;
+      mountd.sin_addr.s_addr = inet_addr(hostname);
+    } else {
+      hent = gethostbyname(opt.mountd);
+      if (hent == NULL) perror_exit("Can't find %s", opt.mountd);
+      if (hent->h_length != (int) sizeof(struct in_addr))
+        perror_exit("only IPv4 is supported");
+
+      mountd.sin_family = AF_INET;
+      memcpy(&mountd.sin_addr, hent->h_addr_list[0],
+          sizeof(struct in_addr));
+    }
+  }
+
+  (void)memset(&pm_data, 0, sizeof(struct pmap));
+
+  to_retry.tv_sec = 3;
+  to_retry.tv_usec = 0;
+  to_total.tv_sec = 20;
+  to_total.tv_usec = 0;
+
+  timeout = time(NULL) + (60 * opt.retry);
+  previous = 0;
+  current = 30;
+
+recall:
+  if (current - previous < 30)
+    sleep(30);
+
+  nfsd.sin_port = PMAPPORT;
+  list = pmap_getmaps(&nfsd);
+
+  while(list) {
+    if (list->pml_map.pm_prog != MOUNTD) {
+      list = list->pml_next;
+      continue;
+    }
+    if (opt.mountvers > list->pml_map.pm_vers) {
+      list = list->pml_next;
+      continue;
+    }
+    if (opt.mountvers > 2 && list->pml_map.pm_vers != opt.mountvers) {
+      list = list->pml_next;
+      continue;
+    }
+    if (opt.mountvers && opt.mountvers < 2 && list->pml_map.pm_vers > 2) {
+      list = list->pml_next;
+      continue;
+    }
+    if ((list->pml_map.pm_vers > MAX_NFSV)
+      ||( opt.proto && list->pml_map.pm_prot != opt.proto)
+      ||( opt.port && list->pml_map.pm_port != opt.port)) {
+      list = list->pml_next;
+      continue;
+    }
+
+    memcpy(&pm_data, &list->pml_map, sizeof(pm_data));
+    list = list->pml_next;
+  }
+
+  if (!pm_data.pm_vers) pm_data.pm_vers = MOUNTV;
+  if (!pm_data.pm_port) pm_data.pm_port = MOUNTPORT;
+  if (!pm_data.pm_prot) pm_data.pm_prot = IPPROTO_TCP;
+
+  opt.nfsvers = (pm_data.pm_vers < 2)? 2 : pm_data.pm_vers;
+
+  mountd.sin_port = htons(pm_data.pm_port);
+  msock = RPC_ANYSOCK;
+
+  switch (pm_data.pm_prot) {
+  case IPPROTO_UDP:
+    rpcclient = clntudp_create(&mountd, pm_data.pm_prog, pm_data.pm_vers,
+        to_retry, &msock);
+    if (rpcclient) break;
+    mountd.sin_port = htons(pm_data.pm_port);
+    msock = RPC_ANYSOCK;
+  case IPPROTO_TCP:
+    rpcclient = clnttcp_create(&mountd, pm_data.pm_prog, pm_data.pm_vers,
+        &msock, 0, 0);
+    if (rpcclient) break;
+    mountd.sin_port = htons(pm_data.pm_port);
+    msock = RPC_ANYSOCK;
+  default:
+    rpcclient = NULL;
+  }
+
+  if (!rpcclient) {
+    if (!opt.daemonized && previous == 0)
+      rpc_msg(clnt_spcreateerror(" "));
+  } else {
+    enum clnt_stat rpcstat;
+    rpcclient->cl_auth = authunix_create_default();
+
+    memset(&rpcstatus, 0, sizeof(rpcstatus));
+
+    if (pm_data.pm_vers == 3)
+      rpcstat = clnt_call(rpcclient, MOUNTP3_MNT,
+          (xdrproc_t) encode_dirpath,
+          (caddr_t) &pathname,
+          (xdrproc_t) decode_mountres,
+          (caddr_t) &rpcstatus,
+          to_total);
+    else rpcstat = clnt_call(rpcclient, MOUNTP_MNT,
+          (xdrproc_t) encode_dirpath,
+          (caddr_t) &pathname,
+          (xdrproc_t) decode_fhstatus,
+          (caddr_t) &rpcstatus,
+          to_total);
+
+    if (rpcstat == RPC_SUCCESS) goto kernel;
+
+    if (errno != ECONNREFUSED) {
+      rpc_msg(clnt_sperror(rpcclient, " "));
+      goto die;
+    }
+
+    if (!opt.daemonized && previous == 0)
+      rpc_msg(clnt_sperror(rpcclient, " "));
+    auth_destroy(rpcclient->cl_auth);
+    clnt_destroy(rpcclient);
+    rpcclient = NULL;
+    close(msock);
+    msock = -1;
+  }
+
+  if (!opt.bg) goto die;
+  if (!opt.daemonized) {
+    opt.daemonized = (daemonize() == 0) ? 1 : 0;
+    if (opt.daemonized <= 0) { /* parent or error */
+      ret = -opt.daemonized;
+      goto ret;
+    }
+  }
+  previous = current;
+  current = time(NULL);
+  if (current >= timeout) goto die;
+  goto recall;
+
+kernel:
+  if (opt.nfsvers == 2) {
+    if (rpcstatus.nfsv2.fhs_status != 0) {
+      error_msg("%s:%s failed, server error code: %d", hostname,
+          pathname, rpcstatus.nfsv2.fhs_status);
+      goto die;
+    }
+    memcpy(nfs_data.root.data, (char *) rpcstatus.nfsv2.fhstatus_u.fhs_fhandle,
+        FHSIZE);
+    nfs_data.root.size = FHSIZE;
+    memcpy(nfs_data.old_root.data, (char *) rpcstatus.nfsv2.fhstatus_u.fhs_fhandle,
+        FHSIZE);
+  } else {
+    fhandle3 *fhandle;
+    if (rpcstatus.nfsv3.fhs_status != 0) {
+      error_msg("%s:%s failed, server error code : %d", hostname,
+          pathname, rpcstatus.nfsv3.fhs_status);
+      goto die;
+    }
+    fhandle = &rpcstatus.nfsv3.mountres3_u.mountinfo.fhandle;
+    memset(nfs_data.old_root.data, 0, FHSIZE);
+    memset(&nfs_data.root, 0, sizeof(nfs_data.root));
+    nfs_data.root.size = fhandle->fhandle3_len;
+    memcpy(nfs_data.root.data, (char *) fhandle->fhandle3_val,
+        fhandle->fhandle3_len);
+
+    nfs_data.flags |= NFS_VER3;
+  }
+
+  /* Create nfs socket for kernel */
+  if (opt.tcp) {
+    if (opt.version < 3) {
+      error_msg("NFS over TCP is not supported");
+      goto die;
+    }
+    fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  } else fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (fsock < 0) {
+    perror_msg("nfs socket creation failed.");
+    goto die;
+  }
+  if (bindresvport(fsock, 0) < 0) {
+    perror_msg("nfs bindresvport failed.");
+    goto die;
+  }
+  if (opt.port == 0) {
+    nfsd.sin_port = PMAPPORT;
+    opt.port = pmap_getport(&nfsd, opt.nfsprog, opt.nfsvers,
+          opt.tcp ? IPPROTO_TCP : IPPROTO_UDP);
+    if (opt.port == 0)
+      opt.port = 2049;   /* NFS port */
+  }
+  nfsd.sin_port = htons(opt.port);
+
+  nfs_data.fd = fsock;
+  memcpy((char *) &nfs_data.addr, (char *) &nfsd, sizeof(nfs_data.addr));
+  strncpy(nfs_data.hostname, hostname, sizeof(nfs_data.hostname));
+
+  if (opt.bg) {
+    /* We must wait until mount directory is available */
+    struct stat statbuf;
+    int delay = 1;
+    while (stat(dir, &statbuf) == -1) {
+      if (!opt.daemonized) {
+        opt.daemonized = (daemonize() == 0) ? 1 : 0;
+        if (opt.daemonized <= 0) {
+          ret = -opt.daemonized;
+          goto ret;
+        }
+      }
+      sleep(delay);
+      delay *= 2;
+      if (delay > 30) delay = 30;
+    }
+  }
+
+  if ((ret = mount(dev, dir, "nfs", rwflag, (void*)&nfs_data)) == -1) {
+    perror_msg("%s on %s failed", dev, dir);
+    return 255;
+  }
+
+  /* Abort */
+die:
+  if (msock >= 0) {
+    if (rpcclient) {
+      auth_destroy(rpcclient->cl_auth);
+      clnt_destroy(rpcclient);
+    }
+    close(msock);
+  }
+  if (fsock >= 0) close(fsock);
+
+ret:
+   free(hostname);
+   free(opt.mountd);
+   free(opts);
+   hostname = NULL;
+   opt.mountd = NULL;
+   opts = NULL;
+   return ret;
+}
+/* End of nfs mount implementation */
+
+static char* get_device_name(char *label, int uuid)
+{
+  struct stat st;
+  char *s = NULL, *ret = NULL;
+  struct dirent *entry = NULL;
+  char *path = xmsprintf("/dev/disk/by-%s", ((uuid)? "uuid" : "label"));
+  DIR *dp = opendir(path);
+  if (!dp) perror_exit("opendir '%s'", path);
+
+  while((entry = readdir(dp))) {
+    if (!strcmp(label, entry->d_name)) {
+      char *fname = xmsprintf("%s/%s", path, entry->d_name);
+      if (!lstat(fname, &st) && S_ISLNK(st.st_mode)) {
+        errno = 0;
+        s = xreadlink(fname);
+        if (s) {
+          free(fname);
+          fname = xmsprintf("%s/%s", path, s);
+          free(s);
+          ret = xabspath(fname, 1);
+        }
+      }
+      free(fname);
+      break;
+    }
+  } // while
+  closedir(dp);
+  free(path);
+  return ret;
+}
+
+static char* resolve_mount_dev(char *dev)
+{
+  char *tmp = dev;
+  if (!strncmp(dev, "UUID=", 5))
+    tmp = get_device_name(dev + 5, 1);
+  if (!strncmp(dev, "LABEL=", 6))
+    tmp = get_device_name(dev + 6, 0);
+
+  if (!tmp) perror_exit("%s", dev);
+  return tmp;
+}
+
+static struct extra_opts extra;
+static unsigned long rwflag;
+
+/*
+ * Set the loop device on a given file/device at the supplied offset
+ */
+static int set_loop(char **loopdev, char *file, off_t offset)
+{
+  int file_fd, device_fd;
+  struct loop_info info;
+  int rc = -1, n;
+  char *tmploop = NULL;
+
+  file_fd = open(file, O_RDWR);
+  if (file_fd < 0) {
+    file_fd = open(file, O_RDONLY);
+    if (file_fd < 0 ) {
+      perror_msg("%s: open backing file failed",file);
+      return 1;
+    }
+  }
+  for(n = 0; rc && n < 1048576; n++) {
+    if (loopdev && *loopdev) tmploop = *loopdev;
+    else tmploop = xmsprintf("/dev/loop%d",n);
+    device_fd = open(tmploop, O_RDWR);
+    if (device_fd < 0) {
+      device_fd = open(tmploop, O_RDONLY);
+      if (device_fd < 0) {
+        perror_msg("open loop device failed");
+        xclose(file_fd);
+        return 1;
+      }
+    }
+    if ((rc = ioctl(device_fd, LOOP_GET_STATUS, &info)) && errno == ENXIO) { //device is free;
+      if (ioctl(device_fd, LOOP_SET_FD, file_fd) ==0) {
+        memset(&info, 0, sizeof(info));
+        strncpy(info.lo_name, file, LO_NAME_SIZE);
+        info.lo_name[LO_NAME_SIZE - 1] = '\0';
+        info.lo_offset = offset;
+        if (!ioctl(device_fd, LOOP_SET_STATUS, &info)) rc = 0;
+        else ioctl(device_fd, LOOP_CLR_FD, 0);
+      }
+    } else if (strcmp(file, (char *)info.lo_name) != 0
+        || offset != info.lo_offset
+        ) {
+      rc = -1;
+    }
+    xclose(device_fd);
+    if (*loopdev) break;
+  }
+
+  xclose(file_fd);
+  if (!rc && loopdev && !*loopdev)
+    *loopdev = xstrdup(tmploop);
+  return rc;
+}
+
+#ifdef TIZEN_SECURE_MOUNT
+static const char *allow_white_list[] = {
+  "/sbin/init",
+  "/usr/bin/tizen-boot.sh",
+  "/etc/init.d/smack_pre_labeling_priv",
+  "/etc/rc.d/init.d/rw-update-main.sh",
+  "/usr/bin/run-factory-reset.sh",
+};
+
+static void check_allowed_proc(pid_t ppid)
+{
+  char *ppid_cmdline = xmsprintf("/proc/%d/cmdline", ppid);
+  FILE *pp_cmdline, *fp;
+  char *arg = 0;
+  size_t size = 0;
+  int i;
+
+  pp_cmdline = fopen(ppid_cmdline, "r");
+  free(ppid_cmdline);
+  if (pp_cmdline == NULL)
+    exit(EXIT_FAILURE);
+
+  while(getdelim(&arg, &size, 0, pp_cmdline) != -1)
+  {
+    for (i=0; i < sizeof(allow_white_list) / sizeof(allow_white_list[0]); i++)
+      if (!strncmp(arg, allow_white_list[i], strlen(allow_white_list[i]))) {
+        free(arg);
+         fclose(pp_cmdline);
+         return;
+      }
+  }
+  free(arg);
+  fclose(pp_cmdline);
+  fp = fopen("/proc/sys/fs/binfmt_misc", "r");
+  if (fp == NULL)
+         reboot(RB_AUTOBOOT);
+  else
+         fclose(fp);
+  return;
+}
+#endif
+
+static int do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop,
+     char *loopdev)
+{
+  char *s;
+  int error = 0;
+  FILE* fp = NULL;
+  struct stat st;
+  char fstype[80] = {'\0'}, mytype[80] = {'\0'};
+
+#ifdef TIZEN_SECURE_MOUNT
+  pid_t ppid = getppid();
+  if (ppid > 1)
+    check_allowed_proc(ppid);
+#endif
+
+  if (toys.optflags & FLAG_f) {
+    if (toys.optflags & FLAG_v)
+      xprintf("mount: %s on %s\n", dev, dir);
+    return 0; //Dry run
+  }
+
+  dev = resolve_mount_dev(dev);
+  if (loop && !stat(dev, &st) && S_ISREG(st.st_mode)) {
+    if (set_loop(&loopdev, dev, 0)) return 1;
+    dev = loopdev;
+  }
+
+
+  if ((strchr(dev, ':') != NULL)  && type == NULL)
+      type = "nfs";
+
+  if (type && strcmp(type ,"nfs") == 0)
+    return do_nfsmount(dev, dir, rwflag, (char*)data);
+
+  if (type == NULL) {//if type not specified on command line
+    fp = fopen("/proc/filesystems", "r");
+    if (fp) {
+      while(fgets(fstype, 80, fp)) {
+        if (!strstr(fstype, "nodev")) {
+          int sz = 0;
+          sscanf(fstype, "%s", mytype);
+          if (!type) type = xstrdup("");
+          sz = strlen(type) + strlen(mytype)  + 2;
+          type = (char *)xrealloc(type, sz); //+2 .. 1 for ',' and 1 for '\0'
+          strcat(type, mytype);
+          strcat(type, ",");
+        }
+
+        strcpy(fstype, "");
+      }
+      fclose(fp);
+      fp = NULL;
+    }
+    if (type) type[strlen(type) - 1] = '\0';
+  }
+
+  while ((s = strsep(&type, ",")) != NULL) {
+retry:
+    if (rwflag & (MS_TYPE| MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) s = NULL;
+    if (mount(dev, dir, s, rwflag, data) == -1) {
+      error = errno;
+
+      if (error == ENODEV || error == EINVAL)
+        continue;
+
+      if (error == EACCES &&
+          (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
+        rwflag |= MS_RDONLY;
+        goto retry;
+      }
+    }
+    else {
+      error = 0;
+      break;
+    }
+  }
+
+  if (error) {
+    errno = error;
+    perror_msg("mount %s on %s failed", dev, dir);
+    return 255;
+  }
+
+  return 0;
+}
+
+
+
+static void free_mnt(struct mntent *tmp)
+{
+  free(tmp->mnt_fsname);
+  free(tmp->mnt_dir);
+  free(tmp->mnt_opts);
+  free(tmp->mnt_type);
+  free(tmp);
+}
+
+static struct mntent* get_mounts_dev_dir(char *arg, char **dev, char **dir, int check_fstab)
+{
+  FILE *f;
+  char *abs_name = NULL;
+  struct mntent *mnt = NULL, *tmp = NULL;
+  char *file = (check_fstab)? "/etc/fstab" : "/proc/mounts";
+
+  arg = resolve_mount_dev(arg);
+  abs_name = xabspath(arg, 1);
+
+  f = setmntent(file, "r");
+  if (!f) perror_exit("%s", file);
+
+  while((mnt = getmntent(f)) != NULL) {
+    if (abs_name) {
+      if ((strcmp(arg, mnt->mnt_fsname) != 0)
+          && (strcmp(arg, mnt->mnt_dir) != 0)
+          && (strcmp(abs_name, mnt->mnt_fsname) != 0)
+          && (strcmp(abs_name, mnt->mnt_dir) != 0))
+        continue;
+    } else {
+      if ((strcmp(arg, mnt->mnt_fsname) != 0)
+          && (strcmp(arg, mnt->mnt_dir) != 0))
+        continue;
+    }
+
+    if (tmp) {
+      free(*dev);
+      free(*dir);
+      free_mnt(tmp);
+      *dev = *dir = NULL;
+      tmp = NULL;
+    }
+    *dev = xstrdup(mnt->mnt_fsname);
+    *dir = xstrdup(mnt->mnt_dir);
+    tmp = xmalloc(sizeof(struct mntent));
+    tmp->mnt_fsname = xstrdup(mnt->mnt_fsname);
+    tmp->mnt_dir = xstrdup(mnt->mnt_dir);
+    tmp->mnt_type = xstrdup(mnt->mnt_type);
+    tmp->mnt_opts = xstrdup(mnt->mnt_opts);
+  }
+
+  endmntent(f);
+  f = NULL;
+  return tmp;
+}
+
+static int get_optskip(char *opt, char* mntopt)
+{
+  char *s, *p, *p_list;
+  int skip = 0, match =0;
+  while((s=strsep(&opt,",")) != NULL) {
+    p_list = xstrdup(mntopt);
+    while((p = strsep(&p_list, ",")) != NULL) {
+      if (strncmp(s, "no", 2) == 0) {
+        if (strcmp(s+2, p) == 0) {
+          skip = 1;
+          match = 1;
+          break;
+        } else skip = 0;
+      } else {
+        if (strcmp(s, p) == 0) {
+          skip = 0;
+          match = 1;
+          break;
+        }
+        else skip = 1;
+      }
+    }
+    if (match == 1) break;
+  }
+  return skip;
+}
+
+static int get_typeskip(char *type, char *mnt_type)
+{
+  char *s;
+  int skip_mount = 0;
+  while ((s = strsep(&type, ","))) {
+    if (!strncmp(s, "no", 2)) {
+      if (!strcmp(s+2, mnt_type)) {
+        skip_mount = 1;
+        break;
+      }
+      else skip_mount = 0;
+    }
+    else {
+      if (!strcmp(s, mnt_type)) {
+        skip_mount = 0;
+        break;
+      }
+      else skip_mount = 1;
+    }
+  }
+  return skip_mount;
+}
+
+static int fs_mounted(struct mntent *mnt)
+{
+  struct mtab_list *mntlist, *mnts;
+  struct stat st;
+  int block = 0;
+  if (stat(mnt->mnt_dir, &st)) return 0;
+  if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) block = 1;
+
+  mnts = xgetmountlist();
+  for ( mntlist = mnts; mntlist; mntlist = mntlist->next) {
+    if (!strcmp(mnt->mnt_dir, mntlist->dir)
+        || !strcmp(mnt->mnt_fsname, mntlist->device)){ /* String match. */
+      return 1;
+    }
+    if (!block) continue;
+
+    if (mntlist->stat.st_dev == st.st_dev) return 1;
+  }
+  return 0;
+}
+
+static int print_mounts()
+{
+  FILE* f;
+  int type_skip = 0;
+  struct mntent mtpair[2];
+  char *type = NULL;
+
+  f = setmntent("/proc/mounts", "r");
+  if (!f) {
+    error_msg("could not open /proc/mounts");
+    return -1;
+  }
+
+  while (getmntent_r(f, &mtpair[0], toybuf, sizeof(toybuf))) {
+    if (toys.optflags & FLAG_t) {
+      type = xstrdup(TT.type);
+      type_skip = get_typeskip(type, mtpair->mnt_type);
+    }
+    if (!type_skip)
+      printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
+          mtpair->mnt_dir, mtpair->mnt_type,
+          mtpair->mnt_opts);
+  }
+  endmntent(f);
+  return 0;
+}
+
+void mount_main(void)
+{
+  char *type = NULL, *opts, *mntopts;
+  char *dev = NULL, *device = NULL;
+  char *dir = NULL, *direc = NULL;
+  int loop = 0, free_dev = 0, free_direc = 0;
+  char *loopdev = NULL;
+
+  FILE* fs_ptr;
+  int type_skip = 0, opt_skip = 0;
+  struct mntent* mnt = NULL, *mt = NULL;
+
+  rwflag = 0;//MS_SILENT;
+
+  // mount with no arguments is equivalent to "cat /proc/mounts"
+  if (!toys.optc && (!toys.optflags || !(toys.optflags & FLAG_a))) {
+    print_mounts();
+    return;
+  }
+  if (toys.optflags & FLAG_O && !(toys.optflags & FLAG_a))
+    perror_exit("-O option is used along with -a option only\n");
+  if (toys.optflags & FLAG_t) {
+    type = TT.type;
+    if (!strcmp(type, "auto")) type = NULL;
+  }
+
+  if (toys.optflags & FLAG_o) {
+    TT.o_options = xstrdup("");
+    for( ;TT.pkt_opt; TT.pkt_opt = TT.pkt_opt->next) {
+      TT.o_options = xrealloc(TT.o_options, strlen(TT.o_options)
+          + strlen(TT.pkt_opt->arg) + 2); //1 for ',' + NULL
+      strcat(TT.o_options, TT.pkt_opt->arg);
+      if (TT.pkt_opt->next) strcat(TT.o_options, ",");
+    }
+    rwflag = parse_mount_options(xstrdup(TT.o_options), rwflag, &extra, &loop, &loopdev);
+  }
+
+  if (toys.optflags & FLAG_r) rwflag |= MS_RDONLY;
+  if (toys.optflags & FLAG_w) rwflag &= ~MS_RDONLY;
+
+  if (2 == toys.optc) {
+    dev = toys.optargs[0];
+    dir = toys.optargs[1];
+  } else if (1 == toys.optc && rwflag & MS_REMOUNT) {
+    if (!(mt = get_mounts_dev_dir(toys.optargs[0], &dev, &dir, 0))) {
+      error_msg("Entry not found in /proc/mounts\n");
+      return;
+    }
+  }
+  else if (1 == toys.optc) {
+    if (!(rwflag & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))) {
+      if ((mt = get_mounts_dev_dir(toys.optargs[0], &dev, &dir, 1))) {
+        type = mt->mnt_type;
+        rwflag = parse_mount_options(xstrdup(mt->mnt_opts), rwflag, &extra, &loop, &loopdev);
+        rwflag |= ((TT.o_options) ? parse_mount_options(xstrdup(TT.o_options), rwflag, &extra, &loop, &loopdev) : 0);
+      } else {
+        error_msg("No entry found for '%s' in /etc/fstab\n", toys.optargs[0]);
+        return;
+      }
+    }
+    else {
+      dev = "";
+      dir = toys.optargs[0];
+    }
+  }
+  else if (toys.optflags & FLAG_a) { //mount -a option
+    if (geteuid() != 0) error_exit("Need to be root");
+
+    fs_ptr = setmntent("/etc/fstab", "r");
+    if (!fs_ptr) perror_exit("/etc/fstab file is not present\n");
+
+    while((mnt = getmntent(fs_ptr))) {
+      if (strstr(mnt->mnt_opts, "noauto"))
+        continue;
+      if (!strcmp(mnt->mnt_dir, "/")
+          || !strcmp(mnt->mnt_dir, "root")
+          || !strcmp(mnt->mnt_type, "swap"))
+        continue;
+      if (fs_mounted(mnt)) {
+        if (toys.optflags & FLAG_v)
+          xprintf("according to /proc/mounts, %s is already mounted on %s\n", mnt->mnt_fsname, mnt->mnt_dir);
+        continue;
+      }
+      type_skip = opt_skip = 0;
+      memset(&extra, 0, sizeof(extra));
+
+      if (toys.optflags & FLAG_t) {
+        type = xstrdup(TT.type);
+        type_skip = get_typeskip(type, mnt->mnt_type);
+      }
+      if (toys.optflags & FLAG_O) {
+        opts = xstrdup(TT.O_options);
+        mntopts = xstrdup(mnt->mnt_opts);
+        opt_skip = get_optskip(opts, mntopts);
+      }
+
+      if (!type_skip && !opt_skip) {
+        unsigned long flag = parse_mount_options(xstrdup(mnt->mnt_opts), rwflag, &extra, &loop, &loopdev);
+        toys.exitval = do_mount(mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, flag, extra.str, loop, loopdev);
+      }
+    }
+
+    endmntent(fs_ptr);
+    toys.exitval = 0;
+    return;
+  }
+
+  if ((device = realpath(dev, NULL)))
+    free_dev = 1;
+  else device = dev;
+  if ((direc = realpath(dir, NULL)))
+    free_direc = 1;
+  else direc = dir;
+
+  toys.exitval = do_mount(device, direc, type, rwflag, extra.str, loop, loopdev);
+
+  if (free_dev) {
+    free(device);
+    device = NULL;
+  }
+  if (free_direc) {
+    free(direc);
+    direc = NULL;
+  }
+}
diff --git a/toys/lsb/passwd.c b/toys/lsb/passwd.c
new file mode 100644 (file)
index 0000000..93c9e58
--- /dev/null
@@ -0,0 +1,209 @@
+/* passwd.c - Program to update user password.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ * Modified 2012 Jason Kyungwan Han <asura321@gmail.com>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/passwd.html
+
+USE_PASSWD(NEWTOY(passwd, ">1a:dlu", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
+
+config PASSWD
+  bool "passwd"
+  default y
+  help
+    usage: passwd [-a ALGO] [-d] [-l] [-u] <account name>
+
+    update user’s authentication tokens. Default : current user
+
+    -a ALGO    Encryption method (des, md5, sha256, sha512) default: des
+    -d         Set password to ''
+    -l         Lock (disable) account
+    -u         Unlock (enable) account
+*/
+
+#define FOR_passwd
+#include "toys.h"
+#include <time.h>
+
+GLOBALS(
+  char *algo;
+)
+
+#ifndef _GNU_SOURCE
+char *strcasestr(const char *haystack, const char *needle);
+#endif
+
+static int str_check(char *s, char *p)
+{
+  if((strcasestr(s, p) != NULL) || (strcasestr(p, s) != NULL))
+    return 1;
+  return 0;
+}
+
+static void strength_check(char *newp, char *oldp, char *user)
+{
+  char *msg = NULL;
+  if(strlen(newp) < 6) { //Min passwd len
+    msg = "too short";
+    xprintf("BAD PASSWORD: %s\n",msg);
+  }
+  if(!newp[0])
+    return; //passwd is empty
+
+  if(str_check(newp, user)) {
+    msg = "user based password";
+    xprintf("BAD PASSWORD: %s\n",msg);
+  }
+
+  if(oldp[0] && str_check(newp, oldp)) {
+    msg = "based on old passwd";
+    xprintf("BAD PASSWORD: %s\n",msg);
+  }
+}
+
+static int verify_passwd(char * pwd)
+{
+  char * pass;
+
+  if (!pwd) return 1;
+  if (pwd[0] == '!' || pwd[0] == '*') return 1;
+
+  pass = crypt(toybuf, pwd);
+  if (pass != NULL && strcmp(pass, pwd)==0)
+    return 0;
+
+  return 1;
+}
+
+static char *new_password(char *oldp, char *user)
+{
+  char *newp = NULL;
+
+  if(read_password(toybuf, sizeof(toybuf), "New password:"))
+    return NULL; //may be due to Ctrl-C
+
+  newp = xstrdup(toybuf);
+  strength_check(newp, oldp, user);
+  if(read_password(toybuf, sizeof(toybuf), "Retype password:")) {
+    free(newp);
+    return NULL; //may be due to Ctrl-C
+  }
+
+  if(strcmp(newp, toybuf) == 0)
+    return newp;
+  else error_msg("Passwords do not match.\n");
+  /*Failure Case */
+  free(newp);
+  return NULL;
+}
+
+
+void passwd_main(void)
+{
+  uid_t myuid;
+  struct passwd *pw;
+  struct spwd *sp;
+  char *name = NULL;
+  char *pass = NULL, *encrypted = NULL, *newp = NULL;
+  char *orig = (char *)"";
+  char salt[MAX_SALT_LEN];
+  int ret = -1;
+
+  myuid = getuid();
+  if((myuid != 0) && (toys.optflags & (FLAG_l | FLAG_u | FLAG_d)))
+    error_exit("You need to be root to do these actions\n");
+
+  pw = getpwuid(myuid);
+
+  if(!pw)
+    error_exit("Unknown uid '%u'",myuid);
+
+  if(toys.optargs[0])
+    name = toys.optargs[0];
+  else
+    name = xstrdup(pw->pw_name);
+
+  pw = getpwnam(name);
+  if(!pw) error_exit("Unknown user '%s'",name);
+
+  if(myuid != 0 && (myuid != pw->pw_uid))
+    error_exit("You need to be root to change '%s' password\n", name);
+
+  pass = pw->pw_passwd;
+  if(pw->pw_passwd[0] == 'x') {
+    /*get shadow passwd */
+    sp = getspnam(name);
+    if(sp)
+      pass = sp->sp_pwdp;
+  }
+
+
+  if(!(toys.optflags & (FLAG_l | FLAG_u | FLAG_d))) {
+    printf("Changing password for %s\n",name);
+    if(myuid != 0 && pass[0] == '!')
+      error_exit("Can't change, password is locked for %s",name);
+    if(myuid != 0) {
+      /*Validate user */
+
+      if(read_password(toybuf, sizeof(toybuf), "Origial password:")) {
+        if(!toys.optargs[0]) free(name);
+        return;
+      }
+      orig = toybuf;
+      if(verify_passwd(pass))
+        error_exit("Authentication failed\n");
+    }
+
+    orig = xstrdup(orig);
+
+    /*Get new password */
+    newp = new_password(orig, name);
+    if(!newp) {
+      free(orig);
+      if(!toys.optargs[0]) free(name);
+      return; //new password is not set well.
+    }
+
+    /*Encrypt the passwd */
+    if(!(toys.optflags & FLAG_a)) TT.algo = "des";
+
+    if(get_salt(salt, TT.algo) == -1)
+      error_exit("Error: Unkown encryption algorithm\n");
+
+    encrypted = crypt(newp, salt);
+    free(newp);
+    free(orig);
+  }
+  else if(toys.optflags & FLAG_l) {
+    if(pass[0] == '!')
+      error_exit("password is already locked for %s",name);
+    printf("Locking password for %s\n",name);
+    encrypted = xmsprintf("!%s",pass);
+  }
+  else if(toys.optflags & FLAG_u) {
+    if(pass[0] != '!')
+      error_exit("password is already unlocked for %s",name);
+
+    printf("Unlocking password for %s\n",name);
+    encrypted = xstrdup(&pass[1]);
+  }
+  else if(toys.optflags & FLAG_d) {
+    printf("Deleting password for %s\n",name);
+    encrypted = (char*)xzalloc(sizeof(char)*2); //1 = "", 2 = '\0'
+  }
+
+  /*Update the passwd */
+  if(pw->pw_passwd[0] == 'x')
+    ret = update_password("/etc/shadow", name, encrypted);
+  else
+    ret = update_password("/etc/passwd", name, encrypted);
+
+  if((toys.optflags & (FLAG_l | FLAG_u | FLAG_d)))
+    free(encrypted);
+
+  if(!toys.optargs[0]) free(name);
+  if(!ret)
+    error_msg("Success");
+  else
+    error_msg("Failure");
+}
diff --git a/toys/lsb/pidof.c b/toys/lsb/pidof.c
new file mode 100644 (file)
index 0000000..7285b6a
--- /dev/null
@@ -0,0 +1,54 @@
+/* pidof.c - Print the Process IDs of all processes with the given names.
+ *
+ * Copyright 2012 Andreas Heck <aheck@gmx.de>
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/pidof.html
+
+USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PIDOF
+  bool "pidof"
+  default y
+  help
+    usage: pidof [-s] [-o omitpid[,omitpid..]] [NAME]...
+
+    Print the PIDs of all processes with the given names.
+    -s single shot, only return one pid.
+    -o omits processes with specified PID
+*/
+
+#define FOR_pidof
+#include "toys.h"
+
+GLOBALS(
+  char *omit;
+)
+
+static int print_pid(pid_t pid, char * name)
+{
+  char * res;
+  int len;
+
+  sprintf(toybuf, "%d", pid);
+  len = strlen(toybuf);
+
+  // Check omit string
+  if (toys.optflags & FLAG_o)
+  {
+    res = strstr(TT.omit, toybuf);
+    if (res && (res == TT.omit || res[-1] == ',') &&
+      (res[len] == ',' || res[len] == 0)) return 1;
+  }
+  xprintf("%*s", len+(!toys.exitval), toybuf);
+  toys.exitval = 0;
+
+  return !(toys.optflags & FLAG_s);
+}
+
+void pidof_main(void)
+{
+  toys.exitval = 1;
+  for_each_pid_with_name_in(toys.optargs, print_pid);
+  if (!toys.exitval) xputc('\n');
+}
diff --git a/toys/lsb/seq.c b/toys/lsb/seq.c
new file mode 100644 (file)
index 0000000..bf5cab1
--- /dev/null
@@ -0,0 +1,62 @@
+/* seq.c - Count from first to last, by increment.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/seq.html
+
+USE_SEQ(NEWTOY(seq, "<1>3?f:s:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SEQ
+  bool "seq"
+  depends on TOYBOX_FLOAT
+  default y
+  help
+    usage: seq [-f fmt_str] [-s sep_str] [first] [increment] last
+
+    Count from first to last, by increment. Omitted arguments default
+    to 1. Two arguments are used as first and last. Arguments can be
+    negative or floating point.
+
+    -f Use fmt_str as a floating point format string
+    -s Use sep_str as separator, default is a newline character
+*/
+
+#define FOR_seq
+#include "toys.h"
+
+GLOBALS(
+  char *sep;
+  char *fmt;
+)
+
+void seq_main(void)
+{
+  double first, increment, last, dd;
+  char *sep_str = "\n";
+  char *fmt_str = "%g";
+  int output = 0;
+
+  // Parse command line arguments, with appropriate defaults.
+  // Note that any non-numeric arguments are treated as zero.
+  first = increment = 1;
+  switch (toys.optc) {
+    case 3: increment = atof(toys.optargs[1]);
+    case 2: first = atof(*toys.optargs);
+    default: last = atof(toys.optargs[toys.optc-1]);
+  }
+
+  if (toys.optflags & FLAG_f) fmt_str = TT.fmt;
+  if (toys.optflags & FLAG_s) sep_str = TT.sep;
+
+  // Yes, we're looping on a double.  Yes rounding errors can accumulate if
+  // you use a non-integer increment.  Deal with it.
+  for (dd=first; (increment>0 && dd<=last) || (increment<0 && dd>=last);
+    dd+=increment)
+  {
+    if (dd != first) printf("%s", sep_str);
+    printf(fmt_str, dd);
+    output = 1;
+  }
+
+  if (output) printf("\n");
+}
diff --git a/toys/lsb/sync.c b/toys/lsb/sync.c
new file mode 100644 (file)
index 0000000..b12a8cc
--- /dev/null
@@ -0,0 +1,23 @@
+/* sync.c - Write all pending data to disk.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/sync.html
+
+USE_SYNC(NEWTOY(sync, NULL, TOYFLAG_BIN))
+
+config SYNC
+  bool "sync"
+  default y
+  help
+    usage: sync
+
+    Write pending cached data to disk (synchronize), blocking until done.
+*/
+
+#include "toys.h"
+
+void sync_main(void)
+{
+  sync();
+}
diff --git a/toys/lsb/umount.c b/toys/lsb/umount.c
new file mode 100644 (file)
index 0000000..e9ccb34
--- /dev/null
@@ -0,0 +1,212 @@
+/* umount.c -  umount filesystems.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_UMOUNT(NEWTOY(umount, "varlfdt:", TOYFLAG_BIN))
+
+config UMOUNT
+  bool "umount"
+  default y
+  help
+    usage: umount [-varlfd] [-t fstypes] [dir...]
+
+    -a  Unmount all file systems
+    -v  Verbose mode
+    -r  Try to remount devices as read-only if mount is busy
+    -l  Lazy umount (detach filesystem)
+    -f  Force umount (i.e., unreachable NFS server)
+    -d  Free loop device if it has been used
+    -t FSTYPE Filesystem types to be acted upon (comma separated)
+
+    unmount the filesystem.
+*/
+
+#define FOR_umount
+#include "toys.h"
+#include <sys/param.h>
+#include <linux/loop.h>
+
+GLOBALS(
+    char *types;
+    struct mtab_list *mnts;
+    int fflag;
+)
+
+#define LOOPDEV_MAXLEN 64
+typedef enum { MNTANY, MNTON, MNTFROM } mntwhat;
+
+static int checkvfsname(char *vfs_name, char **vfs_list)
+{   
+  int skipvfs = 0;
+  if (!vfs_list) return skipvfs;
+  while (*vfs_list) {
+    if (*vfs_list[0] == 'n' && *vfs_list[1] == 'o') skipvfs = 1;  
+    if (!strcmp(vfs_name, *vfs_list)) return (skipvfs);
+    ++vfs_list;
+  }
+  return (!skipvfs);
+}
+
+static char **makevfslist(char *fslist)
+{   
+  char **av;
+  size_t i;
+  char *nextcp, *fsl;
+
+  if (!fslist) return NULL;
+
+  fsl = xstrdup(fslist);
+  for (i = 0, nextcp = fsl; *nextcp; nextcp++)
+    if (*nextcp == ',') i++;
+
+  av = xmalloc((i + 2) * sizeof(char *));
+  nextcp = fsl;
+  i = 0;
+  av[i++] = nextcp;
+  while ((nextcp = strchr(nextcp, ','))) {
+    *nextcp++ = '\0';
+    av[i++] = nextcp;
+  }
+  av[i++] = NULL;
+  return av;
+}   
+
+static int is_loop(char *dev)
+{        
+  struct stat st;
+
+  if (!stat(dev, &st) && S_ISBLK(st.st_mode)
+      && (major(st.st_rdev) == 7)) return 1;
+
+  return 0;  
+}        
+
+static int is_loop_mount(char* path, char *loopdev)
+{        
+  struct mtab_list *mnts = NULL;
+  struct mtab_list *mntlist;
+
+  mnts = TT.mnts;
+  if (!mnts) {
+    perror_msg("getmntinfo");
+    return 0;
+  }
+  for (mntlist = mnts; mntlist; mntlist = mntlist->next) {
+    if (is_loop(mntlist->device) && !strcmp(path, mntlist->dir)) {
+      strncpy(loopdev, mntlist->device, LOOPDEV_MAXLEN);
+      loopdev[LOOPDEV_MAXLEN-1] = '\0';
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static char *getmntname(char *name, mntwhat what)
+{
+  struct mtab_list *mntlist, *mnts = NULL;
+
+  mnts = TT.mnts;
+  if (!mnts) {
+    perror_msg("getmntinfo");
+    return (NULL);
+  }
+  for (mntlist = mnts; mntlist; mntlist = mntlist->next) {
+    if (!strcmp(mntlist->device, name)
+        || !strcmp(mntlist->dir, name))
+      return ((what == MNTON)? mntlist->dir: mntlist->device);
+  }
+  return (NULL);
+}
+
+//  delete the setup loop device
+static int delete_loopdev(char *loopdev)
+{
+  int loop_fd = open(loopdev, O_RDONLY);
+  if (loop_fd < 0) {
+    perror_msg("%s: open loop device failed", loopdev);
+    return 1;
+  }  
+  ioctl(loop_fd, LOOP_CLR_FD, 0);
+  xclose(loop_fd);
+  return 0;
+} 
+
+static int umountfs(char *name, int raw)
+{
+  char *mntpt, rname[MAXPATHLEN], *dev = NULL, loopdev[LOOPDEV_MAXLEN];
+  int loop, status;
+
+  if (raw) mntpt = name;
+  else {
+    if (realpath(name, rname)) name = rname;
+    if (!(mntpt = getmntname(name, MNTON))) {
+      error_msg("%s: not currently mounted", name);
+      return (1);
+    }
+  }
+
+  if (toys.optflags & FLAG_v) xprintf("unmount %s\n", mntpt);
+
+  loop = is_loop_mount(mntpt, loopdev);
+  status = umount(mntpt);
+
+  if (status && (TT.fflag)) status = umount2(mntpt, TT.fflag);
+
+  if (status) {
+    if (errno == EBUSY && toys.optflags & FLAG_r) {
+      //remount as read-only
+      if ((dev = getmntname(mntpt, MNTFROM))) {
+        if (mount(dev, mntpt, NULL, MS_REMOUNT|MS_RDONLY, NULL))
+          perror_msg("Can't remount %s read-only",dev);
+        else {
+          printf("%s busy - remounted read-only\n",dev);
+          return 0;
+        }
+      }
+    }
+    else perror_msg("can't umount %s",mntpt);
+    return 1;
+  }
+  if ((toys.optflags & FLAG_d) && loop) return delete_loopdev(loopdev);
+  return 0;
+}
+
+
+void umount_main()
+{
+  int errs = 0, raw = 0, i =0;
+  struct mtab_list *mntlist, *mnts;
+  char **typelist = NULL;
+
+  /* Start disks transferring immediately. */
+  sync();
+
+  if (toys.optflags & FLAG_f) TT.fflag = MNT_FORCE;
+  if (toys.optflags & FLAG_l) TT.fflag = MNT_DETACH;
+
+  mnts = xgetmountlist();
+  TT.mnts = mnts;
+
+  if (toys.optflags & FLAG_t) typelist = makevfslist(TT.types);
+
+  if (toys.optflags & FLAG_a) {
+    if (!mnts) {
+      perror_msg("getmntinfo");
+      errs = 1;
+    }
+    for (errs = 0, mntlist = mnts; mntlist; mntlist = mntlist->next) {
+      if (checkvfsname(mntlist->type, typelist))
+        continue;
+      errs = umountfs(mntlist->dir, 1);
+    }
+  } else if (!toys.optc) {
+    toys.exithelp = 1;
+    error_exit("");
+  } else {
+    for (errs = 0, i = 0; toys.optargs[i]; i++)
+      errs = umountfs(toys.optargs[i], raw);
+  }
+  toys.exitval = errs;
+}
diff --git a/toys/other/README b/toys/other/README
new file mode 100644 (file)
index 0000000..e16cd6f
--- /dev/null
@@ -0,0 +1,13 @@
+Other commands
+
+These are commands not present in Posix or LSB.
+
+Most of them are necessary to provide a development environment capable of
+booting a system image and building Linux From Scratch under it.
+
+Tested with Aboriginal Linux system image and the lfs-bootstrap.hdc automated
+build control image for Linux From Scratch 6.8):
+
+  http://landley.net/aboriginal
+
+  http://landley.net/aboriginal/control-images
diff --git a/toys/other/arping.c b/toys/other/arping.c
new file mode 100644 (file)
index 0000000..c9faab8
--- /dev/null
@@ -0,0 +1,351 @@
+/* arping - send ARP REQUEST to a neighbour host.
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config ARPING
+  bool "arping"
+  default y
+  help
+    Usage: arping [-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP
+
+    Send ARP requests/replies
+
+    -f         Quit on first ARP reply
+    -q         Quiet
+    -b         Keep broadcasting, don't go unicast
+    -D         Duplicated address detection mode
+    -U         Unsolicited ARP mode, update your neighbors
+    -A         ARP answer mode, update your neighbors
+    -c N       Stop after sending N ARP requests
+    -w TIMEOUT Time to wait for ARP reply, seconds
+    -I IFACE   Interface to use (default eth0)
+    -s SRC_IP  Sender IP address
+      DST_IP    Target IP address
+*/
+#define FOR_arping
+#include "toys.h"
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+
+extern void *mempcpy(void *dest, const void *src, size_t n);
+
+GLOBALS(
+    long count;
+    unsigned long time_out;
+    char *iface;
+    char *src_ip;
+
+    char *dest_ip;
+    int sockfd;
+    struct in_addr source_addr;
+    struct in_addr dest_addr;
+    unsigned start;
+    unsigned end;
+    unsigned sent_at;
+    unsigned sent_nr;
+    unsigned rcvd_nr;
+    unsigned brd_sent;
+    unsigned rcvd_req;
+    unsigned brd_rcv;
+    unsigned unicast_flag;
+)
+struct sockaddr_ll src_pk; 
+struct sockaddr_ll dst_pk; 
+struct timeval t1, t2;
+
+/*
+ * gets information of INTERFACE and updates IFINDEX, MAC and IP
+ */
+static int get_interface(const char *interface, int *ifindex, uint32_t *oip, uint8_t *mac)
+{
+  struct ifreq req;
+  int fd;
+  struct sockaddr_in *ip;
+
+  fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+  if (fd < 0) {
+    error_msg("IFACE : fail to get interface. ERROR : %d\n", fd);
+    return fd;
+  }
+  req.ifr_addr.sa_family = AF_INET;
+  strncpy(req.ifr_name, interface, IFNAMSIZ);
+  req.ifr_name[IFNAMSIZ-1] = '\0';
+
+  if (ioctl(fd, SIOCGIFFLAGS, &req) != 0) {
+    error_msg("Is interface %s configured ?\n", interface);
+    close(fd);
+    return -1;
+  }
+  if (!(req.ifr_flags & IFF_UP)) return -1;
+
+  if (oip) {
+    if (ioctl(fd, SIOCGIFADDR, &req) != 0){
+      error_msg("Is interface %s configured ?\n", interface);
+      close(fd);
+      return -1;
+    }
+    ip = (struct sockaddr_in*) &req.ifr_addr;
+    *oip = ntohl(ip->sin_addr.s_addr);
+  }
+  if (ifindex) {
+    if (ioctl(fd, SIOCGIFINDEX, &req) != 0) {
+      error_msg("IFACE : NO INDEX interface %s\n", interface);
+      close(fd);
+      return -1;
+    }
+    *ifindex = req.ifr_ifindex;
+  }
+  if (mac) {
+    if (ioctl(fd, SIOCGIFHWADDR, &req) != 0) {
+      error_msg("IFACE : NO MAC interface %s\n", interface);
+      close(fd);
+      return -1;
+    }
+    memcpy(mac, req.ifr_hwaddr.sa_data, 6);
+  }
+  close(fd);
+  return 0;
+}
+
+/*
+ * SIGINT handler, Print Number
+ * of Packets send or receive details.
+ */
+static void done(int sig)
+{
+  if(!(toys.optflags & FLAG_q)) {
+    xprintf("Sent %u probe(s) (%u broadcast(s))\n", TT.sent_nr, TT.brd_sent);
+    xprintf("Received %u repl%s (%u request(s), %u broadcast(s))\n", TT.rcvd_nr,
+        TT.rcvd_nr == 1 ? "y":"ies", TT.rcvd_req, TT.brd_rcv);
+  }
+  if(toys.optflags & FLAG_D) exit(!!TT.rcvd_nr);
+  if(toys.optflags & FLAG_U) exit(EXIT_SUCCESS); //In-U mode, No replies is expected so always true
+  exit(!TT.rcvd_nr);
+}
+/*
+ * Update exit value on exit
+ * with corresponding option set/Unset
+ */
+static void exit_t(void) 
+{
+  if(toys.optflags & FLAG_D) toys.exitval = 0;
+  else toys.exitval = 2;
+  exit(toys.exitval);
+}
+/*
+ * Create and Send Packet 
+ */
+static void send_packet()
+{
+  int ret;
+  unsigned char toybuf_tmp[256] = {0,};
+  struct arphdr *arp_h = (struct arphdr *) toybuf_tmp;
+  unsigned char *ptr = (unsigned char *)(arp_h + 1);
+
+  arp_h->ar_hrd = htons(ARPHRD_ETHER);
+  arp_h->ar_pro = htons(ETH_P_IP);
+  arp_h->ar_hln = src_pk.sll_halen;
+  arp_h->ar_pln = 4;  
+  arp_h->ar_op = (toys.optflags & FLAG_A) ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
+
+  ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen);
+  ptr = mempcpy(ptr, &TT.source_addr, 4);
+  if(toys.optflags & FLAG_A) ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen);
+  else ptr = mempcpy(ptr, &dst_pk.sll_addr, src_pk.sll_halen);
+
+  ptr = mempcpy(ptr, &TT.dest_addr, 4);
+  ret = sendto(TT.sockfd, toybuf_tmp, ptr - toybuf_tmp, 0, (struct sockaddr *)&dst_pk, sizeof(dst_pk));
+  if(ret == ptr - toybuf_tmp) {
+    gettimeofday(&t1, NULL);
+    TT.sent_at = (t1.tv_sec * 1000000ULL + (t1.tv_usec));
+    TT.sent_nr++;
+    if(!TT.unicast_flag) TT.brd_sent++;
+  }
+}
+/*
+ * Receive Packet and filter
+ * with valid checks.
+ */
+static int recv_from(unsigned char *recv_pk, struct sockaddr_ll *from, int *recv_len)
+{
+  struct arphdr *arp_hdr;
+  unsigned char *p;
+  struct in_addr s_ip, d_ip;
+  arp_hdr = (struct arphdr *)recv_pk;
+  p = (unsigned char *)(arp_hdr + 1);
+
+  if(arp_hdr->ar_op != htons(ARPOP_REQUEST) && arp_hdr->ar_op != htons(ARPOP_REPLY))
+    return 1;       
+
+  if(from->sll_pkttype != PACKET_HOST                 
+      && from->sll_pkttype != PACKET_BROADCAST            
+      && from->sll_pkttype != PACKET_MULTICAST)           
+    return 1;       
+
+
+  if(arp_hdr->ar_pro != htons(ETH_P_IP)                    
+      || (arp_hdr->ar_pln != 4)        
+      || (arp_hdr->ar_hln != src_pk.sll_halen)                     
+      || (*recv_len < (int)(sizeof(*arp_hdr) + 2 * (4 + arp_hdr->ar_hln))))
+    return 1;       
+
+  memcpy(&s_ip.s_addr, p + arp_hdr->ar_hln, 4);
+  memcpy(&d_ip.s_addr, p + arp_hdr->ar_hln + 4 + arp_hdr->ar_hln, 4); 
+
+  if(TT.dest_addr.s_addr != s_ip.s_addr) return 1;
+  if(toys.optflags & FLAG_D) {
+    if(TT.source_addr.s_addr && TT.source_addr.s_addr != d_ip.s_addr) return 1;
+    if(memcmp(p, &src_pk.sll_addr, src_pk.sll_halen) == 0) return 1;
+  }
+  else {
+    if(TT.source_addr.s_addr != d_ip.s_addr ) return 1;
+  }
+  if(!(toys.optflags & FLAG_q)) {
+    printf("%scast re%s from %s [%s]",
+        from->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
+        arp_hdr->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
+        inet_ntoa(s_ip), ether_ntoa((struct ether_addr *) p));
+    if (TT.sent_at) {                                                                                                                                                                     
+      unsigned delta;
+      gettimeofday(&t2, NULL);
+      delta = (t2.tv_sec * 1000000ULL + (t2.tv_usec)) - TT.sent_at;
+      xprintf(" %u.%03ums\n", delta / 1000, delta % 1000);
+      xflush();
+    }
+  }
+
+  TT.rcvd_nr++;
+  if(from->sll_pkttype != PACKET_HOST) TT.brd_rcv++;
+  if(arp_hdr->ar_op == htons(ARPOP_REQUEST)) TT.rcvd_req++;
+  if(toys.optflags & FLAG_f) done(0);
+  if(!(toys.optflags & FLAG_b)) {
+    memcpy(dst_pk.sll_addr, p, src_pk.sll_halen); //if not FLAG_b, now unicast.
+    TT.unicast_flag = 1;
+  }
+  return 0;
+}
+/* Alarm signal Handle,
+ * send packets in one sec.
+ * interval.
+ */
+static void send_signal(int sig)
+{
+  struct timeval start;
+  
+  gettimeofday(&start, NULL);
+  if(!TT.start) TT.end = TT.start = start.tv_sec * 1000 + start.tv_usec / 1000; //In milisecounds.
+  else TT.end = start.tv_sec*1000 + start.tv_usec / 1000; //In milisecounds.
+  if(toys.optflags & FLAG_c){
+    if(!TT.count) done(0);
+    TT.count--; //Decremented before sending, anyhow we are here iff, we have count.(if count = 0, already have exited)
+  }
+  if((toys.optflags & FLAG_w) && ((TT.end - TT.start) > ((TT.time_out)*1000)))
+    done(0);
+  send_packet();
+  alarm(1);
+}
+/*
+ * arping main function. Parse args 
+ * and options.
+ */
+void arping_main(void)
+{
+  struct ifreq ifr;
+  int if_index;
+  int recv_len;
+  struct sockaddr_ll from;
+  unsigned char *recv_pk;
+  socklen_t len;
+  
+  TT.brd_sent = TT.sent_nr = TT.rcvd_nr = TT.rcvd_req = 0;
+  TT.unicast_flag = 0;
+
+  if(toys.optflags & FLAG_D) toys.optflags |= FLAG_f;
+  if(toys.optflags & FLAG_A) toys.optflags |= FLAG_U;
+
+  if(!(toys.optflags & FLAG_I)) TT.iface = "eth0";
+
+  TT.sockfd = socket(AF_PACKET, SOCK_DGRAM, 0);
+  if (TT.sockfd < 0) perror_exit("Socket");
+
+  TT.dest_ip = toys.optargs[0]; //Destination addr, already exited if this is NULL.
+  memset(&ifr, 0, sizeof(ifr));
+
+  strncpy(ifr.ifr_name, TT.iface, sizeof(ifr.ifr_name));
+  get_interface(TT.iface, &if_index, NULL, NULL);
+  src_pk.sll_ifindex = if_index;
+
+  if((ioctl(TT.sockfd, SIOCGIFFLAGS, (char*)&ifr)) < 0) perror_exit("SIOCGIFFLAGS");
+  if (!(ifr.ifr_flags & IFF_UP)) 
+    if (!(toys.optflags & FLAG_q)) perror_exit("Interface \"%s\" is down\n", TT.iface);
+  if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
+    if (!(toys.optflags & FLAG_q)) {
+      xprintf("Interface \"%s\" is not ARPable\n", TT.iface);
+      exit_t();
+    }
+  }
+
+  if(inet_aton(TT.dest_ip, &TT.dest_addr) == 0) {
+    struct hostent *hp;
+    hp = gethostbyname2(TT.dest_ip, AF_INET);
+    if (!hp) perror_exit("arping: unknown host %s\n", TT.dest_ip);
+    memcpy(&TT.dest_addr, hp->h_addr, 4);
+  }
+
+  if((toys.optflags & FLAG_s) && !(inet_aton(TT.src_ip, &TT.source_addr))) perror_exit("Bad source address");
+  if(!(toys.optflags & FLAG_D) && (toys.optflags & FLAG_U) && TT.source_addr.s_addr == 0) TT.source_addr = TT.dest_addr;
+
+  if(!(toys.optflags & FLAG_D) || TT.source_addr.s_addr) {
+    struct sockaddr_in saddr;
+    int p_fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if(p_fd < 0) perror_exit("Socket");
+    if (setsockopt(p_fd, SOL_SOCKET, SO_BINDTODEVICE, TT.iface, strlen(TT.iface)) == -1) perror_exit("setsockopt");
+    memset(&saddr, 0, sizeof(saddr));
+    saddr.sin_family = AF_INET;
+    if(TT.source_addr.s_addr) {
+      saddr.sin_addr = TT.source_addr;
+      if(bind(p_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) perror_exit("bind");
+    }
+    else {
+      uint32_t oip;
+      saddr.sin_port = htons(1025);
+      saddr.sin_addr = TT.dest_addr;
+      if(connect(p_fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
+        perror_exit("cannot connect to remote host");
+      get_interface(TT.iface, NULL, &oip, NULL);
+      TT.source_addr.s_addr = htonl(oip);
+    }
+    xclose(p_fd);
+  }
+  src_pk.sll_family = AF_PACKET;
+  src_pk.sll_protocol = htons(ETH_P_ARP);
+  if(bind(TT.sockfd, (struct sockaddr *)&src_pk, sizeof(src_pk))) perror_exit("bind");
+  socklen_t alen = sizeof(src_pk);
+  getsockname(TT.sockfd, (struct sockaddr *)&src_pk, &alen);
+  if(src_pk.sll_halen == 0) {
+    perror_msg("src is not arpable");
+    exit_t();
+  }
+  if (!(toys.optflags & FLAG_q)) {
+    xprintf("ARPING to %s", inet_ntoa(TT.dest_addr));
+    xprintf(" from %s via %s\n", inet_ntoa(TT.source_addr), TT.iface);
+  }
+
+  dst_pk = src_pk;
+  memset(dst_pk.sll_addr, -1, dst_pk.sll_halen); //First packet always broadcast.
+  signal(SIGINT, done);
+  signal(SIGALRM, send_signal);
+
+  recv_pk = xmalloc(4096);
+  send_signal(0); // Send first Brodcast message.
+  while(1) {
+    len = sizeof(from);
+    recv_len = recvfrom(TT.sockfd, recv_pk, 4096, 0, (struct sockaddr *)&from, &len);
+    if(recv_len < 0) continue;
+    recv_from(recv_pk, &from, &recv_len);
+  }
+}
diff --git a/toys/other/bzcat.c b/toys/other/bzcat.c
new file mode 100644 (file)
index 0000000..8266484
--- /dev/null
@@ -0,0 +1,26 @@
+/* bzcat.c - decompress stdin to stdout using bunzip2.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+
+USE_BZCAT(NEWTOY(bzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config BZCAT
+  bool "bzcat"
+  default y
+  help
+    usage: bzcat [filename...]
+
+    Decompress listed files to stdout. Use stdin if no files listed.
+*/
+
+#include "toys.h"
+
+static void do_bzcat(int fd, char *name)
+{
+  bunzipStream(fd, 1);
+}
+
+void bzcat_main(void)
+{
+  loopfiles(toys.optargs, do_bzcat);
+}
diff --git a/toys/other/catv.c b/toys/other/catv.c
new file mode 100644 (file)
index 0000000..62520c4
--- /dev/null
@@ -0,0 +1,67 @@
+/* cat -v implementation for toybox
+ *
+ * Copyright (C) 2006, 2007 Rob Landley <rob@landley.net>
+ *
+ * See "Cat -v considered harmful" at
+ *   http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz
+
+USE_CATV(NEWTOY(catv, "vte", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CATV
+  bool "catv"
+  default y
+  help
+    usage: catv [-evt] [filename...]
+
+    Display nonprinting characters as escape sequences. Use M-x for
+    high ascii characters (>127), and ^x for other nonprinting chars.
+
+    -e Mark each newline with $
+    -t Show tabs as ^I
+    -v Don't use ^x or M-x escapes.
+*/
+
+#define FOR_catv
+#include "toys.h"
+
+// Callback function for loopfiles()
+
+static void do_catv(int fd, char *name)
+{
+  for(;;) {
+    int i, len;
+
+    len = read(fd, toybuf, sizeof(toybuf));
+    if (len < 0) toys.exitval = EXIT_FAILURE;
+    if (len < 1) break;
+    for (i=0; i<len; i++) {
+      char c=toybuf[i];
+
+      if (c > 126 && (toys.optflags & FLAG_v)) {
+        if (c > 127) {
+          printf("M-");
+          c -= 128;
+        }
+        if (c == 127) {
+          printf("^?");
+          continue;
+        }
+      }
+      if (c < 32) {
+        if (c == 10) {
+          if (toys.optflags & FLAG_e) xputc('$');
+        } else if (toys.optflags & (c==9 ? FLAG_t : FLAG_v)) {
+          printf("^%c", c+'@');
+          continue;
+        }
+      }
+      xputc(c);
+    }
+  }
+}
+
+void catv_main(void)
+{
+  toys.optflags ^= FLAG_v;
+  loopfiles(toys.optargs, do_catv);
+}
diff --git a/toys/other/chattr.c b/toys/other/chattr.c
new file mode 100644 (file)
index 0000000..ee2865e
--- /dev/null
@@ -0,0 +1,249 @@
+/* chattr.c - Change file attributes on a Linux second extended file system.
+ *
+ * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ * 
+USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN))
+
+config CHATTR
+  bool "chattr"
+  default y
+  help
+    usage: chattr [-R] [-+=AacDdijsStTu] [-v version] [Files...]
+
+    Change file attributes on a Linux second extended file system.
+
+    Operators:
+      '-' Remove attributes.
+      '+' Add attributes.
+      '=' Set attributes.
+
+    Attributes:
+      A  Don't track atime.
+      a  Append mode only.
+      c  Enable compress.
+      D  Write dir contents synchronously.
+      d  Don't backup with dump.
+      i  Cannot be modified (immutable).
+      j  Write all data to journal first.
+      s  Zero disk storage when deleted.
+      S  Write file contents synchronously.
+      t  Disable tail-merging of partial blocks with other files.
+      u  Allow file to be undeleted.
+      -R Recurse.
+      -v Set the file's version/generation number.
+*/
+#define FOR_chattr
+
+#include "toys.h"
+
+typedef struct _chattr_params {
+  unsigned long add_attr_val;
+  unsigned long rem_attr_val;
+  unsigned long set_attr_val;
+  unsigned long version;
+  unsigned char add_operator;
+  unsigned char rem_operator;
+  unsigned char set_operator;
+  unsigned char version_flag;
+  unsigned char recursive;
+}CHATTR_PARAMS;
+
+CHATTR_PARAMS chattr_param_info;
+
+static const EXT2_ATTRS toys_ext2_attrs[] = {
+  {"Secure_Deletion",               EXT2_SECRM_FL,        's'},
+  {"Undelete",                      EXT2_UNRM_FL,         'u'},
+  {"Compression_Requested",         EXT2_COMPR_FL,        'c'},
+  {"Synchronous_Updates",           EXT2_SYNC_FL,         'S'},
+  {"Immutable",                     EXT2_IMMUTABLE_FL,    'i'},
+  {"Append_Only",                   EXT2_APPEND_FL,       'a'},
+  {"No_Dump",                       EXT2_NODUMP_FL,       'd'},
+  {"No_Atime",                      EXT2_NOATIME_FL,      'A'},
+  {"Indexed_directory",             EXT2_INDEX_FL,        'I'},
+  {"Journaled_Data",                EXT3_JOURNAL_DATA_FL, 'j'},
+  {"No_Tailmerging",                EXT2_NOTAIL_FL,       't'},
+  {"Synchronous_Directory_Updates", EXT2_DIRSYNC_FL,      'D'},
+  {"Top_of_Directory_Hierarchies",  EXT2_TOPDIR_FL,       'T'},
+  {NULL,                            -1,                     0},
+};
+
+/*
+ * Display help info and exit from application.
+ */
+static void show_chattr_help(void)
+{
+  toys.exithelp++;
+  error_exit("Invalid Argument");
+}
+
+/*
+ * Return flag value.
+ */
+static unsigned long get_flag_val(const char ch)
+{
+  const EXT2_ATTRS *ptr = toys_ext2_attrs;
+  while(ptr->name) {
+    if(ptr->optchar == ch) return ptr->inode_flag;
+    ptr++;
+  }
+  return 0;
+}
+
+/*
+ * Parse command line argument and fill the chattr_param_info structure.
+ */
+static void parse_cmdline_arg(char ***argv)
+{
+  char *arg = **argv;
+
+  if(!arg) show_chattr_help();
+  else if(arg && (!strcmp(arg, "--help"))) show_chattr_help();
+
+  while(arg) {
+    unsigned long flag_val;
+    char *ptr = NULL;
+
+    switch(arg[0]) {
+      case '-': 
+        {
+          for(ptr = ++arg; *ptr; ptr++) {
+            if(*ptr == 'R') {
+              chattr_param_info.recursive = 1;
+              continue;
+            }
+            else if(*ptr == 'v') {//after -v, get the version from the next cmdline input.
+              char *endptr;
+              arg = *(*argv += 1);
+              if(!arg) show_chattr_help();
+              if(*arg == '-') perror_exit("Invalid Number '%s'", arg);
+              chattr_param_info.version = xstrtoul(arg, &endptr, 0);
+              if(*endptr) perror_exit("bad version '%s'", arg);
+              chattr_param_info.version_flag = 1;
+              continue;
+            }
+            else {
+              if((flag_val = get_flag_val(*ptr)) == 0) show_chattr_help();
+              chattr_param_info.rem_attr_val |= flag_val;
+              chattr_param_info.rem_operator = 1;
+            }
+          }//End of for loop.
+        }
+        break;
+      case '+': 
+        {
+          chattr_param_info.add_operator = 1;
+          for(ptr = ++arg; *ptr; ptr++) {
+            if((flag_val = get_flag_val(*ptr)) == 0) show_chattr_help();
+            chattr_param_info.add_attr_val |= flag_val;
+          }
+        }
+        break;
+      case '=': 
+        {
+          chattr_param_info.set_operator = 1;
+          for(ptr = ++arg; *ptr; ptr++) {
+            if((flag_val = get_flag_val(*ptr)) == 0) show_chattr_help();
+            chattr_param_info.set_attr_val |= flag_val;
+          }
+        }
+        break;
+      default:
+        return;
+    }//end of switch case.
+               arg = *(*argv += 1);
+       }//end of while loop.
+       return;
+}
+
+/*
+ * Update attribute of given file.
+ */
+static int update_attr(struct dirtree *root)
+{
+  unsigned long flag_val = 0;
+  char *fpath = NULL;
+  int fd = -1;
+
+  if(!dirtree_notdotdot(root)) return 0;
+
+  //if file is a link and recursive is set then escape the file.
+  //else if file is not regular+link+dir(like fifo or dev file) then escape the file.
+  if( (S_ISLNK(root->st.st_mode) && chattr_param_info.recursive)
+    || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode) && !S_ISDIR(root->st.st_mode)) )
+    return 0;
+
+  fpath = dirtree_path(root, NULL);
+
+  if(-1 == (fd=open(fpath, O_RDONLY | O_NONBLOCK))) {
+    free(fpath);
+    return DIRTREE_ABORT;
+  }
+
+  //Get current attr of file.
+  if(get_e2fs_flag(fd, &root->st, &flag_val) == -1) {
+    perror_msg("while reading flags on '%s'", fpath);
+    free(fpath);
+    xclose(fd);
+    return DIRTREE_ABORT;
+  }
+
+  //either '=' option will be there in cmdline or '-' / '+'.
+  if(chattr_param_info.set_operator) { //for '=' operator.
+    if(set_e2fs_flag(fd, &root->st, chattr_param_info.set_attr_val) == -1)
+      perror_msg("while setting flags on '%s'", fpath);
+  }
+  else { //for '-' / '+' operator.
+    //remove attributes from existing attribute.
+    if(chattr_param_info.rem_operator)
+      flag_val &= ~(chattr_param_info.rem_attr_val);
+    //add attributes with existing attribute.
+    if(chattr_param_info.add_operator)
+      flag_val |= chattr_param_info.add_attr_val;
+    if(!S_ISDIR(root->st.st_mode))
+      flag_val &= ~EXT2_DIRSYNC_FL;
+    if(set_e2fs_flag(fd, &root->st, flag_val) == -1)
+      perror_msg("while setting flags on '%s'", fpath);
+  }
+  //set file version (if version flag is set).
+  if(chattr_param_info.version_flag) {
+    if(set_e2fs_version(fd, chattr_param_info.version) == -1)
+      perror_msg("while setting version on '%s'", fpath);
+  }
+  free(fpath);
+  xclose(fd);
+  //go for directory (if any).
+  if(S_ISDIR(root->st.st_mode) && chattr_param_info.recursive)
+    return DIRTREE_RECURSE;
+  return 0;
+}
+
+/*
+ * chattr main function.
+ */
+void chattr_main(void)
+{
+  char **argv = toys.optargs;
+  memset(&chattr_param_info, 0, sizeof(CHATTR_PARAMS));
+
+  parse_cmdline_arg(&argv);
+  if(!*argv) show_chattr_help();
+  if(chattr_param_info.set_operator && (chattr_param_info.add_operator || chattr_param_info.rem_operator))
+    error_exit("'=' is incompatible with '-' and '+'");
+
+  if((chattr_param_info.rem_attr_val & chattr_param_info.add_attr_val) != 0)
+    error_exit("Can't both set and unset same flag.");
+
+  if( !(chattr_param_info.add_operator
+        || chattr_param_info.rem_operator
+        || chattr_param_info.set_operator
+        || chattr_param_info.version_flag) )
+    error_exit(("Must use '-v', '=', '-' or '+'"));
+
+  for(; *argv; argv++)
+    dirtree_read(*argv, update_attr);
+  toys.exitval = 0; //always set success at this point.
+  return;
+}
diff --git a/toys/other/chroot.c b/toys/other/chroot.c
new file mode 100644 (file)
index 0000000..c9ff10c
--- /dev/null
@@ -0,0 +1,23 @@
+/* chroot.c - Run command in new root directory.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+
+USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config CHROOT
+  bool "chroot"
+  default y
+  help
+    usage: chroot NEWPATH [commandline...]
+
+    Run command within a new root directory. If no command, run /bin/sh.
+*/
+
+#include "toys.h"
+
+void chroot_main(void)
+{
+  char *binsh[] = {"/bin/sh", "-i", 0};
+  if (chdir(*toys.optargs) || chroot(".")) perror_exit("%s", *toys.optargs);
+  xexec(toys.optargs[1] ? toys.optargs+1 : binsh);
+}
diff --git a/toys/other/chvt.c b/toys/other/chvt.c
new file mode 100644 (file)
index 0000000..7a0119c
--- /dev/null
@@ -0,0 +1,47 @@
+/* chvt.c - switch virtual terminals
+ *
+ * Copyright (C) 2008 David Anders <danders@amltd.com>
+
+USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config CHVT
+  bool "chvt"
+  default y
+  help
+    usage: chvt N
+
+    Change to virtual terminal number N. (This only works in text mode.)
+
+    Virtual terminals are the Linux VGA text mode displays, ordinarily
+    switched between via alt-F1, alt-F2, etc. Use ctrl-alt-F1 to switch
+    from X to a virtual terminal, and alt-F6 (or F7, or F8) to get back.
+*/
+
+#include "toys.h"
+
+/* Note: get_console_fb() will need to be moved into a seperate lib section */
+int get_console_fd()
+{
+  int fd;
+  char *consoles[]={"/dev/console", "/dev/vc/0", "/dev/tty", NULL}, **cc;
+
+  cc = consoles;
+  while (*cc) {
+    fd = open(*cc++, O_RDWR);
+    if (fd >= 0) return fd;
+  }
+
+  return -1;
+}
+
+void chvt_main(void)
+{
+  int vtnum, fd;
+
+  vtnum=atoi(*toys.optargs);
+
+  fd=get_console_fd();
+  // These numbers are VT_ACTIVATE and VT_WAITACTIVE from linux/vt.h
+  if (fd < 0 || ioctl(fd, 0x5606, vtnum) || ioctl(fd, 0x5607, vtnum))
+    perror_exit(NULL);
+}
diff --git a/toys/other/clear.c b/toys/other/clear.c
new file mode 100644 (file)
index 0000000..2515f73
--- /dev/null
@@ -0,0 +1,19 @@
+/* clear.c - clear the screen
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+
+USE_CLEAR(NEWTOY(clear, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config CLEAR
+  bool "clear"
+  default y
+  help
+    Clear the screen.
+*/
+
+#include "toys.h"
+
+void clear_main(void)
+{
+  write(1, "\e[2J\e[H", 7);
+}
diff --git a/toys/other/count.c b/toys/other/count.c
new file mode 100644 (file)
index 0000000..f3b6f82
--- /dev/null
@@ -0,0 +1,32 @@
+/* count.c - Progress indicator from stdin to stdout
+ *
+ * Copyright 2002 Rob Landley <rob@landley.net>
+
+USE_COUNT(NEWTOY(count, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config COUNT
+  bool "count"
+  default y
+  help
+    usage: count
+
+    Copy stdin to stdout, displaying simple progress indicator to stderr.
+*/
+
+#include "toys.h"
+
+void count_main(void)
+{
+  uint64_t size = 0;
+  int len;
+  char buf[32];
+
+  for (;;) {
+    len = xread(0, toybuf, sizeof(toybuf));
+    if (!len) break;
+    size += len;
+    xwrite(1, toybuf, len);
+    xwrite(2, buf, sprintf(buf, "%"PRIu64" bytes\r", size));
+  }
+  xwrite(2, "\n", 1);
+}
diff --git a/toys/other/cttyhack.c b/toys/other/cttyhack.c
new file mode 100644 (file)
index 0000000..c64be7c
--- /dev/null
@@ -0,0 +1,84 @@
+/* cttyhack.c - Program to get the controlling TTY
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_CTTYHACK(NEWTOY(cttyhack, NULL, TOYFLAG_BIN))
+
+config CTTYHACK
+  bool "cttyhack"
+  default y
+  help
+    usage: cttyhack PROG ARGS
+
+    cttyhack will try to get a controlling terminal and execute the PROG.
+*/
+
+#define FOR_cttyhack
+#include "toys.h"
+#include <linux/vt.h>
+#include <linux/serial.h>
+
+
+void cttyhack_main(void)
+{
+  int fd;
+  char terminal[88] = {0,};
+  char buf[80];
+  struct vt_stat vstate;
+  struct serial_struct serial;
+  int s = 0;
+
+  if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
+      xstrncpy(terminal, "/dev/tty", sizeof("/dev/tty"));
+      xclose(fd);
+  }
+  else {
+    do {
+      if ((fd = open("/sys/class/tty/console/active", O_RDONLY)) >= 0) {
+        if ((s = read(fd, buf, 80)) > 0) {
+          buf[s] = '\0'; //cause the name will have \n at the end.
+          sprintf(terminal, "/dev/%s", buf);
+        }
+        xclose(fd);
+        break;
+      }
+      if (ioctl(0, VT_GETSTATE, &vstate) == 0) {
+        sprintf(terminal, "/dev/ttyS%d", vstate.v_active);
+        break;
+      }
+      if (ioctl(0, TIOCGSERIAL, &serial) == 0) {
+        sprintf(terminal, "/dev/ttyS%d", serial.line);
+        break;
+      }
+      goto execute;
+    } while(0);
+
+    if ((fd = open(terminal, O_RDWR)) < 0) goto execute;
+
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    while(fd > 2) {
+      close(fd);
+      fd--;
+    }
+    //set the terminal as Controlling terminal
+    ioctl(0, TIOCSCTTY, 1);
+  }
+
+execute:
+  if (toys.optargs[0]) {
+    int i = 0;
+    errno = 0;
+    char **arg = xzalloc((toys.optc+1) * sizeof(char*));
+    for(i = 0; i < toys.optc; i++)
+      arg[i] = xstrdup(toys.optargs[i]);
+    execvp(arg[0], arg);
+    if(errno) error_exit("can't execute %s", arg[0]);
+  } else {
+    if(terminal[0] == '\0') error_exit(NULL);
+    xprintf("%s\n", terminal);
+  }
+}
diff --git a/toys/other/depmod.c b/toys/other/depmod.c
new file mode 100644 (file)
index 0000000..d59ed97
--- /dev/null
@@ -0,0 +1,360 @@
+/* depmod.c - Outputs a dependancy list file suitable for the modprobe utility.
+ * 
+ * Copyright 2012 Sandeep Sharma <sandeep_2756@yahoo.com>
+ *
+ * Not in SUSv4.
+
+USE_DEPMOD(NEWTOY(depmod, "b:na",TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+
+config DEPMOD
+       bool "depmod"
+       default y
+       help
+          usage: depmod [-a][-n] [-b basedir] version MODULE...
+          -a          Probe all modules, Default
+          -n          Write the dependency file on stdout only 
+          -b Basedir  Basedir needed  when modules are in staging state
+*/
+#define FOR_depmod
+#include "toys.h"
+#include <sys/utsname.h> /* For uname()--version */
+/*
+ * Single module strcuture.
+ */
+struct module {
+  struct module *next;
+  char *name, *modname;
+  struct double_list *deps;
+  struct double_list *aliases;
+  struct double_list *sym;
+  struct module *next_dep; //updated while ordering deps.
+};
+
+/* Macros */
+#define MODULE_NAME_LEN 256
+#define DFT_BASE_DIR "/lib/modules/"
+
+GLOBALS(
+    char *b_options;
+    struct module *modules;
+)
+
+/*
+ * Get base name from the input name.
+ */
+static const char *get_basename(char *name)
+{
+  const char *c = strrchr(name, '/');
+  if (c) return c + 1;
+  return name;
+}
+/* Get last path component with no strip.
+ * e.g.
+ * "/"    -> "/"
+ * "abc"    -> "abc"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> ""
+ */
+static char *get_last_path_component_withnostrip(char *path)
+{
+  char *slash = strrchr(path, '/');
+  if (!slash || (slash == path && !slash[1])) return (char*)path;
+  return slash + 1;
+}
+/*
+ * maximum module name length
+ */
+#define MODNAME_LEN        256
+/*
+ * Converts path name FILE to module name also allocates memory
+ * for holding the string if MOD is NULL,
+ * Returns the pointer to the string.
+ *
+ */
+static char *path2mod(char *file, char *mod)
+{
+       int i;
+       char *from;
+
+       if (!file) return NULL;
+       if (!mod) mod = xmalloc(MODNAME_LEN);
+       from = get_last_path_component_withnostrip(file);
+       for (i = 0; i < (MODNAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
+               mod[i] = (from[i] == '-') ? '_' : from[i];
+       mod[i] = '\0';
+       return mod;
+}
+
+/*
+ * free all the list nodes on error OR on exit
+ */
+static void free_list(struct double_list* list)
+{      
+  struct double_list *tmp = NULL;
+  if(!list) return;
+  list->prev->next = NULL;
+  tmp = list;
+  while(tmp) {
+    list=list->next;
+    if(tmp->data) free(tmp->data);
+    free(tmp);
+    tmp = list;
+  }    
+  return;  
+}   
+
+
+/*
+ * Find any module from the module list.
+ */
+static struct module *find_module(struct module *modules, const char *modname)
+{
+  struct module *m;
+  for (m = modules; m != NULL; m = m->next) {
+    if (strcmp(m->modname, modname) == 0)
+      return m;
+  }
+  return NULL;
+}
+/*
+ * Update the dep list by updating next_dep pointer.
+ * List deps one after other and if next dep is already present in existing
+ * dep list unlink it from list and add it to tail.
+ */
+static struct module* reorder_deps(struct module *modules, struct module *start, struct module *deps_mod)
+{   
+  struct module *m, *temp;
+  struct double_list *n;
+  n = deps_mod->deps;
+  n->prev->next = NULL; //Break double list so that we traverse like singly linked list
+  for(; n != NULL; n = n->next) {
+    m = find_module(modules, n->data);
+    if(m == NULL) return deps_mod;
+    if(m->next_dep != NULL) {  //already in the list
+      temp = start; //start from start. 
+      while(temp->next_dep) { //Find already present, unlink it.
+        if(temp->next_dep == m) {
+          temp->next_dep = m->next_dep;
+          m->next_dep = NULL;
+          break;
+        }
+        temp = temp->next_dep;
+      }
+    }
+    deps_mod->next_dep = m; //Update dep pointer to link it in dep list.
+    m->next_dep = NULL;
+    deps_mod = m;
+    deps_mod = reorder_deps(modules, start, deps_mod); //recurse over deps.
+  }
+  return deps_mod;
+}
+/*
+ * Seprate string with "," as token and create double list of tokens.
+ */
+static int string_tokenise(char *str, struct double_list **llist, char *token)
+{
+  char *ptr, *start;
+  char *val = NULL;
+  int token_lenth = 0;
+
+  if(!str) {
+    perror_msg("error: ");
+    return 0;
+  }  
+  start = str;
+  while((ptr = strsep(&start, token)) != NULL) {  
+    val = xstrdup(ptr);
+    dlist_add(llist, val);
+    token_lenth += strlen(ptr);
+  }
+  return token_lenth;
+}
+/*
+ * Read each module and create dep,sym and alias list for each module.
+ */
+static void  parse_module(struct dirtree *new)
+{
+  char modname[MODULE_NAME_LEN];
+  char *image, *temptr;
+  int fd;
+  char *image_index;
+  struct module *info;
+  off_t len;
+  char *fname, *full_path;
+
+  struct module  **first = &TT.modules; //List each module.
+  full_path = dirtree_path(new, NULL);
+  fname = full_path;
+
+  if(*fname == '.') fname += 2; // skip .// else keep path as /path/to/modules/
+  fd = open(full_path, O_RDONLY); // Don't want to die if we are not able to open few modules.
+  if(fd < 0) {
+    perror_msg("error:");
+    return ;
+  }
+  len = fdlength(fd); //calculate length of module
+  xclose(fd);
+  image = xreadfile(full_path); // readfile or die 
+
+  info = xzalloc(sizeof(struct module));
+  info->next = *first;
+  *first = info;
+  info->next_dep = NULL; //keep dep pointer NULL, will modify at ordering time.
+  info->name = xstrdup(fname);
+  info->modname = xstrdup(path2mod(fname, modname));
+  image_index = image + len;
+  for (temptr = image; temptr < image_index; temptr++) {
+    if (strncmp(temptr, "depends=", 8) == 0) { // search for keyword "depends="  
+      char *p;
+      temptr += 8;
+      for (p = temptr; *p; p++)
+        if (*p == '-')
+          *p = '_';
+      temptr += string_tokenise(temptr, &info->deps, ","); //list the deps as string tokens
+    }
+    if(strncmp(temptr, "alias=", 6) == 0) { //list aliases
+      dlist_add(&info->aliases, xstrdup(temptr + 6));
+      temptr += strlen(temptr);
+    } 
+    if(strncmp(temptr, "__ksymtab_", 10) == 0) { //list symbols
+      temptr += 10;
+      if (strncmp(temptr, "gpl", 3) == 0 || strcmp(temptr, "strings") == 0) continue;
+      dlist_add(&info->sym, xstrdup(temptr));
+      temptr += strlen(temptr);
+    }
+  }
+  free(image);
+  image = NULL;
+  return ;
+}
+/*
+ * Callback function. Return if not module else parse.
+ */
+static int check_module(struct dirtree *new)
+{
+  if(strstr(new->name, ".ko") != NULL) parse_module(new);
+  if(dirtree_notdotdot(new)) return DIRTREE_RECURSE; //Don't want DIRTREE_SAVE
+  return 0;
+}
+/*
+ * concatenate string and path
+ */
+static char *concat_file_path(char *path, char *default_path)
+{
+  char *str;
+  if('/' == path[strlen(path) - 1]) {
+    while(*default_path == '/') ++default_path;
+    str = xmsprintf("%s%s", path, default_path);
+  }
+  else if(*default_path != '/') str = xmsprintf("%s/%s", path, default_path); 
+  else str = xmsprintf("%s%s", path, default_path); 
+  if(str) return str;
+  else perror_msg("error: "); 
+  return NULL;
+}
+/*
+ * Depmod main function.
+ */
+void depmod_main(void)
+{
+  struct module *m, *dep, *temp_ptr;
+  struct double_list *llist, *ptr; 
+  char *version;
+  char *base_dir, *mod_dir;
+  int temp, i;
+  struct utsname uts_name;
+
+  base_dir = DFT_BASE_DIR; //set the basedirectory to default base dir
+  if(toys.optargs[0] != NULL && sscanf(toys.optargs[0], "%u.%u.%u", &temp, &temp, &temp) == 3) version = toys.optargs[0];
+  else { 
+    uname(&uts_name);
+    version = uts_name.release;
+  }
+  if(toys.optflags & FLAG_b) {
+    if(!(base_dir = concat_file_path(TT.b_options, DFT_BASE_DIR)))  // -b, Add staging state dir to DFT_BASE_DIR.
+      perror_exit("error:");
+  }
+  if(!(mod_dir = concat_file_path(base_dir, version))) perror_exit("error:");
+  else xchdir(mod_dir);
+
+  if(toys.optargs[0] != NULL && sscanf(toys.optargs[0], "%u.%u.%u", &temp, &temp, &temp) != 3) {
+    for(i = 0; toys.optargs[i] != NULL; i++) {
+      if(toys.optargs[0][0] != '/') perror_exit("%s is a relative path\n", toys.optargs[0]);
+      else dirtree_read(toys.optargs[i], check_module);
+    }
+  }
+  else if(toys.optargs[1] != NULL) { 
+    for(i = 1;toys.optargs[i] != NULL; i++) {
+      if(toys.optargs[1][0] != '/') perror_exit("%s is a relative path\n", toys.optargs[1]);
+      else dirtree_read(toys.optargs[i], check_module);
+    }
+  }
+  else dirtree_read("./", check_module); //Recurse from present dir
+
+  /*generate deps*/
+  if(!(toys.optflags & FLAG_n)) {
+    if(freopen("modules.dep", "w", stdout) == NULL) {
+      perror_msg("Could not open modules.dep: ");
+      goto CLEAN_EXIT;
+    }
+  }
+  for(m = TT.modules; m != NULL; m = m->next) {
+    printf("%s:", m->name);
+    temp_ptr = m;
+    reorder_deps(TT.modules, m, m);
+    while (temp_ptr->next_dep != NULL) {
+      dep = temp_ptr->next_dep;
+      printf(" %s", dep->name);
+      temp_ptr->next_dep = NULL; //Unlink just printed dep 
+      temp_ptr = dep;
+    }
+    xputc('\n');
+  }
+  /*genrate aliases*/
+  if(!(toys.optflags & FLAG_n)) {
+    if(freopen("modules.alias", "w", stdout) == NULL) {
+      perror_msg("Could not open modules.alias: ");
+      goto CLEAN_EXIT;
+    }
+  }
+  for(m = TT.modules; m != NULL; m = m->next) {
+    llist = m->aliases;
+    ptr = llist;
+    while(llist) {
+      const char *f = get_basename(m->name);
+      int l = strchr(f, '.') - f;
+      printf("alias %s %.*s\n", ptr->data, l, f);
+      ptr = ptr->next;
+      if(ptr == llist) break;
+    }
+  }      
+  /*generate symbols*/
+  if(!(toys.optflags & FLAG_n)) {
+    if(freopen("modules.sym", "w", stdout) == NULL) {
+      perror_msg("Could not open modules.sym: ");
+      goto CLEAN_EXIT;
+    }
+  }
+  for(m = TT.modules; m != NULL; m = m->next) {
+    llist = m->sym;
+    ptr = llist;
+    while(llist) {
+      const char *f = get_basename(m->name);
+      int l = strchr(f, '.') - f;
+      printf("alias symbol %s %.*s\n", ptr->data, l, f);
+      ptr = ptr->next;
+      if(ptr == llist) break;
+    }
+  }
+CLEAN_EXIT:
+  while(TT.modules) {
+    struct module *old = TT.modules;
+    TT.modules = TT.modules->next;
+    free_list(old->deps);
+    free_list(old->aliases);
+    free_list(old->sym);
+    free(old->name);
+    free(old->modname);
+    free(old);
+  }
+}
diff --git a/toys/other/dos2unix.c b/toys/other/dos2unix.c
new file mode 100644 (file)
index 0000000..3e1feb0
--- /dev/null
@@ -0,0 +1,62 @@
+/* dos2unix.c - convert newline format
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+
+USE_DOS2UNIX(NEWTOY(dos2unix, NULL, TOYFLAG_BIN))
+USE_DOS2UNIX(OLDTOY(unix2dos, dos2unix, NULL, TOYFLAG_BIN))
+
+config DOS2UNIX
+  bool "dos2unix/unix2dos"
+  default y
+  help
+    usage: dos2unix/unix2dos [file...]
+
+    Convert newline format between dos (\r\n) and unix (just \n)
+    If no files listed copy from stdin, "-" is a synonym for stdin.
+*/
+
+#define FOR_dos2unix
+#include "toys.h"
+
+GLOBALS(
+  char *tempfile;
+)
+
+static void do_dos2unix(int fd, char *name)
+{
+  char c = toys.which->name[0];
+  int outfd = 1, catch = 0;
+
+  if (fd) outfd = copy_tempfile(fd, name, &TT.tempfile);
+
+  for (;;) {
+    int len, in, out;
+
+    len = read(fd, toybuf+(sizeof(toybuf)/2), sizeof(toybuf)/2);
+    if (len<0) perror_msg("%s",name);
+    if (len<1) break;
+
+    for (in = out = 0; in < len; in++) {
+      char x = toybuf[in+sizeof(toybuf)/2];
+
+      // Drop \r only if followed by \n in dos2unix mode
+      if (catch) {
+        if (c == 'u' || x != '\n') toybuf[out++] = '\r';
+        catch = 0;
+      // Add \r only if \n not after \r in unix2dos mode
+      } else if (c == 'u' && x == '\n') toybuf[out++] = '\r';
+
+      if (x == '\r') catch++;
+      else toybuf[out++] = x;
+    }
+    xwrite(outfd, toybuf, out);
+  }
+  if (catch) xwrite(outfd, "\r", 1);
+
+  if (fd) replace_tempfile(-1, outfd, &TT.tempfile);
+}
+
+void dos2unix_main(void)
+{
+  loopfiles(toys.optargs, do_dos2unix);
+}
diff --git a/toys/other/dumpkmap.c b/toys/other/dumpkmap.c
new file mode 100644 (file)
index 0000000..4267995
--- /dev/null
@@ -0,0 +1,105 @@
+/* dumpkmap.c - dumpkmap implementation.
+ *
+ * Copyright 2013 Madhur Verma <mad.flexi@gmail.com>
+ *
+
+USE_DUMPKMAP(NEWTOY(dumpkmap, ">0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config DUMPKMAP
+       bool "dumpkmap"
+       default y
+       help
+         usage: dumpkmap > keymap
+
+         Print a binary keyboard translation table to stdout.
+
+config BUSYBOX_CMPTBL
+  bool "output similar to busybox. (USES NR_KEYS = 128)"
+  default n
+  depends on DUMPKMAP
+*/
+
+#define FOR_dumpkmap
+#include "toys.h"
+
+#if CFG_BUSYBOX_CMPTBL==1
+# define NR_KEYS 128
+# define MAX_NR_KEYMAPS 256
+# define KDGKBENT 0x4B46
+# define KDGKBTYPE 0x4B33
+struct kbentry {
+  unsigned char kb_table;
+  unsigned char kb_index;
+  unsigned short kb_value;
+};
+#else
+# include <linux/kd.h>
+# include <linux/keyboard.h>
+#endif
+
+static const char * const console_names[] = {
+    "/dev/console",
+    "/dev/tty0",
+    "/dev/tty"
+};
+
+/*
+ * get fd for console from various checks.
+ */
+static int get_console(void)
+{
+  int fd;
+  for (fd = 2; fd >= 0; fd--) {
+    int nfd, cfd;
+    char ptr;
+
+    nfd = open(console_names[fd], O_RDWR);
+    if (nfd < 0 && errno == EACCES)
+      nfd = open(console_names[fd], O_RDONLY);
+
+    if (nfd < 0 && errno == EACCES)
+      nfd = open(console_names[fd], O_WRONLY);
+
+verify:
+    cfd = (nfd >= 0 ? nfd : fd);
+    ptr = 0;
+    if (ioctl(cfd, KDGKBTYPE, &ptr) == 0)
+      return cfd;
+    if (nfd >= 0) {
+      close(nfd);
+      nfd = -1;
+      goto verify;
+    }
+  }
+  error_exit("can't open console");
+  return fd;
+}
+
+void dumpkmap_main(void)
+{
+  struct kbentry ke;
+  int i, j, fd, ret;
+  char *maps = xzalloc(MAX_NR_KEYMAPS);
+
+  fd = get_console();
+  write(STDOUT_FILENO, "bkeymap", 7);
+
+  memset(maps, 0x01, 13);
+  maps[3] = maps[7] = maps[11] = 0;
+
+  write(STDOUT_FILENO, maps, MAX_NR_KEYMAPS);
+
+  for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+    if (maps[i] == 1) {
+      for (j = 0; j < NR_KEYS; j++) {
+        ke.kb_index = j;
+        ke.kb_table = i;
+        ret = ioctl(fd, KDGKBENT, &ke);
+        if (ret < 0) perror_exit("IOCTL: failed with %s, %s, %p", (char*)&ke.kb_index, (char*)&ke.kb_table, &ke.kb_value);
+        if (!ret) write(STDOUT_FILENO, (void*)&ke.kb_value, 2);
+      }
+    }
+  }
+  close(fd);
+  free(maps);
+}
diff --git a/toys/other/dumpleases.c b/toys/other/dumpleases.c
new file mode 100644 (file)
index 0000000..864f4ad
--- /dev/null
@@ -0,0 +1,83 @@
+/* dumpleases.c - Dump the leases granted by udhcpd.
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ *
+
+USE_DUMPLEASES(NEWTOY(dumpleases, ">0arf:[!ar]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config DUMPLEASES
+  bool "dumpleases"
+  default y
+  help
+  Usage: dumpleases [-r|-a] [-f LEASEFILE]
+
+    Display DHCP leases granted by udhcpd
+    -f FILE,  Lease file
+    -r        Show remaining time
+    -a        Show expiration time
+*/
+
+#define FOR_dumpleases
+#include "toys.h"
+
+#define DEF_LEASE_FILE "/var/lib/misc/udhcpd.leases"
+
+GLOBALS(
+    char *file;
+)
+/*
+ * lease structure
+ */
+struct lease {                                                                                                                                                                                   
+  uint32_t expires;
+  uint32_t lease_nip;
+  uint8_t lease_mac[6];
+  char hostname[20];
+  uint8_t pad[2]; //Padding
+};
+/*
+ * Dumpleases main function.
+ */
+void dumpleases_main(void)
+{
+  int fd, i;
+  struct in_addr addr;
+  struct lease lease_struct;
+  int64_t written_time , current_time, exp;
+  if(!(toys.optflags & FLAG_f)) TT.file = DEF_LEASE_FILE;
+
+  fd = xopen(TT.file, O_RDONLY);
+  xprintf("Mac Address       IP Address      Host Name           Expires %s\n", (toys.optflags & FLAG_a) ? "at" : "in");
+  xread(fd, &written_time, sizeof(written_time));
+  current_time = time(NULL);
+  written_time = SWAP_BE64(written_time);
+  if(current_time < written_time) written_time = current_time;
+
+  while(sizeof(lease_struct) == (readall(fd, &lease_struct, sizeof(lease_struct)))) {
+    const char *fmt = ":%02x" + 1;
+    for (i = 0; i < 6; i++) {
+      printf(fmt, lease_struct.lease_mac[i]);
+      fmt = ":%02x";
+    }
+    addr.s_addr = lease_struct.lease_nip;
+    lease_struct.hostname[19] = '\0';
+    xprintf(" %-16s%-20s", inet_ntoa(addr),lease_struct.hostname );
+    exp = ntohl(lease_struct.expires) + written_time;
+    if (exp <= current_time) {
+      xputs("expired");
+      continue;
+    }
+    if (!(toys.optflags & FLAG_a)) { 
+      unsigned dt, hr, m;
+      unsigned expires = exp - current_time;
+      dt = expires / (24*60*60); expires %= (24*60*60);
+      hr = expires / (60*60); expires %= (60*60);
+      m = expires / 60; expires %= 60;
+      if (dt) xprintf("%u days ", dt);
+      xprintf("%02u:%02u:%02u\n", hr, m, (unsigned)expires);
+    } else {
+      fputs(ctime((const time_t*)&exp), stdout);
+    }
+  }
+  xclose(fd);
+}
diff --git a/toys/other/eject.c b/toys/other/eject.c
new file mode 100644 (file)
index 0000000..66c8f69
--- /dev/null
@@ -0,0 +1,89 @@
+/* eject.c - eject device.
+ *
+ * Copyright 2012 Harvind Singh <harvindsingh1981@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_EJECT(NEWTOY(eject, ">1stT[!tT]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config EJECT
+  bool "eject"
+  default y
+  help
+    usage: eject [-s] [-t] [-T] [...]
+
+    Eject DEVICE or default /dev/cdrom
+
+    -s  SCSI device
+    -t  Close tray
+    -T  Open/close tray (toggle).
+*/
+
+#define FOR_eject
+#include "toys.h"
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+#define CDROM_DRIVE_STATUS 0x5326 //Get tray position, etc.
+#define CDROM_CLOSE_TRAY 0x5319   //Pendant of CDROM_EJECT.
+#define CDROM_EJECT 0x5309        //Ejects the cdrom media.
+#define DRIVE_NOT_READY 3         //Drive is busy.
+
+/*
+ * Remove SCSI Device.
+ */
+static void remove_scsi(int dscptr)
+{
+  char sg_driver_cmd[3][6] = {
+    { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0 }, //allow medium removal
+    { START_STOP, 0, 0, 0, 1, 0 }, //start the motor
+    { START_STOP, 0, 0, 0, 2, 0 } //eject the media
+  };
+
+  unsigned i;
+  unsigned char buffer1[32],buffer2[2];
+  sg_io_hdr_t in_out_header;
+
+  if ((ioctl(dscptr, SG_GET_VERSION_NUM, &i) < 0) || (i < 30000))
+    error_exit("not a sg device or old sg driver");
+
+  memset(&in_out_header, 0, sizeof(sg_io_hdr_t));
+  in_out_header.interface_id = 'S';
+  in_out_header.cmd_len = 6;
+  in_out_header.mx_sb_len = sizeof(buffer1);
+  in_out_header.dxfer_direction = SG_DXFER_NONE;
+  in_out_header.dxferp = buffer2;
+  in_out_header.sbp = buffer1;
+  in_out_header.timeout = 2000;
+
+  for (i = 0; i < 3; i++) {
+    in_out_header.cmdp = (void *)sg_driver_cmd[i];
+    xioctl(dscptr, SG_IO, (void *)&in_out_header);
+  }
+  /* force kernel to reread partition table when new disc is inserted */
+  ioctl(dscptr, BLKRRPART);
+}
+
+/*
+ * eject main function.
+ */
+void eject_main(void)
+{
+  int fd, out = 0, rc = 0;
+  char *device_name;
+  if (!toys.optc) device_name = "/dev/cdrom";
+  else device_name = toys.optargs[0];
+
+  fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
+  if (!toys.optflags) xioctl(fd, CDROM_EJECT, &out);
+  else if (toys.optflags & FLAG_s) remove_scsi(fd);
+  else {
+    if (toys.optflags & FLAG_T || toys.optflags & FLAG_t) {
+      rc = ioctl(fd, CDROM_DRIVE_STATUS, &out);
+      if (toys.optflags & FLAG_t || rc == 2) //CDS_TRAY_OPEN = 2
+        xioctl(fd, CDROM_CLOSE_TRAY, &out);
+      else xioctl(fd, CDROM_EJECT, &out);
+    }
+  }
+  xclose(fd);
+}
diff --git a/toys/other/envdir.c b/toys/other/envdir.c
new file mode 100644 (file)
index 0000000..2adae5d
--- /dev/null
@@ -0,0 +1,65 @@
+/* envdir.c - A envdir used to setup environmental variables for a service.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_ENVDIR(NEWTOY(envdir, "<2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config ENVDIR
+  bool "envdir"
+  default y
+  help
+    usage: envdir [d] [child]
+    
+    d is a single argument. child consists of one or more arguments.
+    
+    envdir sets various environment variables as specified by files
+    in the directory named d. It then runs child.
+*/
+#define _GNU_SOURCE
+#define FOR_envdir
+#include "toys.h"
+
+void envdir_main(void) {
+  struct dirent *pDirent;
+  DIR *pDir;
+  FILE *fr;
+  char *str = NULL;
+  char **aargv;
+  int sz = 0;
+  pDir = opendir(toys.argv[1]);
+  if (pDir == NULL) {
+    error_msg("Cannot open directory '%s'\n", toys.argv[1]);
+    return;
+  }
+
+  while ((pDirent = readdir(pDir)) != NULL) {
+    str = (char *) xmalloc(
+        strlen(toys.argv[1]) + strlen(pDirent->d_name) + 2);
+    if(str && (pDirent->d_name[0] != '.')) { //ignoring [.] & [..]
+      strcpy(str, toys.argv[1]);
+      strcat(str, "/");
+      strcat(str, pDirent->d_name);
+      if ((fr = fopen(str, "r")) != NULL) {
+        if ((sz = fread(toybuf, 1, sizeof(toybuf), fr)) != 0)
+        {
+          *strchrnul(toybuf, '\n') = '\0';
+          setenv(pDirent->d_name, toybuf, 1);
+        }
+        else
+          unsetenv(pDirent->d_name);
+        memset(toybuf, 0, sizeof(toybuf));
+        fclose(fr);
+        fr = NULL;
+      } else
+        perror_exit("%s : Unable to open the file", pDirent->d_name);
+    }
+    free(str);
+    str = NULL;
+  }
+  
+  closedir(pDir);
+  aargv = &toys.argv[2];
+  xexec(aargv);
+}
diff --git a/toys/other/expr.c b/toys/other/expr.c
new file mode 100644 (file)
index 0000000..cdf4438
--- /dev/null
@@ -0,0 +1,1171 @@
+/* expr.c - expr implementation
+ *
+ * Copyright 2013 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
+
+USE_EXPR(NEWTOY(expr, "?", TOYFLAG_USR|TOYFLAG_BIN))
+
+config EXPR
+  bool "expr"
+  default y
+  help
+    usage: expr EXPRESSION
+
+    Print the value of EXPRESSION to stdout
+    EXPRESSION may be:
+            ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2
+            ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0
+            ARG1 < ARG2 1 if ARG1 is less than ARG2, else 0. Similarly:
+            ARG1 <= ARG2
+            ARG1 = ARG2
+            ARG1 != ARG2
+            ARG1 >= ARG2
+            ARG1 > ARG2
+            ARG1 + ARG2 Sum of ARG1 and ARG2. Similarly:
+            ARG1 - ARG2
+            ARG1 * ARG2
+            ARG1 / ARG2
+            ARG1 % ARG2
+            STRING : REGEXP   Anchored pattern match of REGEXP in STRING
+            match STRING REGEXP Same as STRING : REGEXP
+            substr STRING POS LENGTH Substring of STRING, POS counted from 1
+            index STRING CHARS  Index in STRING where any CHARS is found, or 0
+            length STRING   Length of STRING
+            quote TOKEN   Interpret TOKEN as a string, even if
+                          it is a keyword like 'match' or an
+                          operator like '/'
+            (EXPRESSION)  Value of EXPRESSION
+
+    Beware that many operators need to be escaped or quoted for shells.
+    Comparisons are arithmetic if both ARGs are numbers, else
+    lexicographical. Pattern matches return the string matched between
+    \( and \) or null; if \( and \) are not used, they return the number
+    of characters matched or 0.
+*/
+
+#define FOR_expr
+#include "toys.h"
+
+#ifndef lint
+static const char yysccsid[] = "@(#)yaccpar    1.9 (Berkeley) 02/21/93";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYPATCH 20100216
+
+#define YYEMPTY        (-1)
+#define yyclearin      (yychar = YYEMPTY)
+#define yyerrok        (yyerrflag = 0)
+#define YYRECOVERING() (yyerrflag != 0)
+
+
+#ifndef yyparse
+#define yyparse    exprparse
+#endif /* yyparse */
+
+#ifndef yylex
+#define yylex      exprlex
+#endif /* yylex */
+
+#ifndef yyerror
+#define yyerror    exprerror
+#endif /* yyerror */
+
+#ifndef yychar
+#define yychar     exprchar
+#endif /* yychar */
+
+#ifndef yyval
+#define yyval      exprval
+#endif /* yyval */
+
+#ifndef yylval
+#define yylval     exprlval
+#endif /* yylval */
+
+#ifndef yydebug
+#define yydebug    exprdebug
+#endif /* yydebug */
+
+#ifndef yynerrs
+#define yynerrs    exprnerrs
+#endif /* yynerrs */
+
+#ifndef yyerrflag
+#define yyerrflag  exprerrflag
+#endif /* yyerrflag */
+
+#ifndef yylhs
+#define yylhs      exprlhs
+#endif /* yylhs */
+
+#ifndef yylen
+#define yylen      exprlen
+#endif /* yylen */
+
+#ifndef yydefred
+#define yydefred   exprdefred
+#endif /* yydefred */
+
+#ifndef yydgoto
+#define yydgoto    exprdgoto
+#endif /* yydgoto */
+
+#ifndef yysindex
+#define yysindex   exprsindex
+#endif /* yysindex */
+
+#ifndef yyrindex
+#define yyrindex   exprrindex
+#endif /* yyrindex */
+
+#ifndef yygindex
+#define yygindex   exprgindex
+#endif /* yygindex */
+
+#ifndef yytable
+#define yytable    exprtable
+#endif /* yytable */
+
+#ifndef yycheck
+#define yycheck    exprcheck
+#endif /* yycheck */
+
+#ifndef yyname
+#define yyname     exprname
+#endif /* yyname */
+
+#ifndef yyrule
+#define yyrule     exprrule
+#endif /* yyrule */
+#define YYPREFIX "expr"
+
+/* compatibility with bison */
+#ifdef YYPARSE_PARAM
+/* compatibility with FreeBSD */
+#ifdef YYPARSE_PARAM_TYPE
+#define YYPARSE_DECL() yyparse(YYPARSE_PARAM_TYPE YYPARSE_PARAM)
+#else
+#define YYPARSE_DECL() yyparse(void *YYPARSE_PARAM)
+#endif
+#else
+#define YYPARSE_DECL() yyparse(void)
+#endif /* YYPARSE_PARAM */
+
+extern int YYPARSE_DECL();
+
+#line 2 "expr.y"
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char **av;
+
+static void yyerror(const char *, ...);
+static int yylex(void);
+static int is_zero_or_null(const char *);
+static int is_integer(const char *);
+static int64_t perform_arith_op(const char *, const char *, const char *);
+
+#define YYSTYPE        const char *
+
+#line 139 "expr.c"
+#define STRING 257
+#define SPEC_OR 258
+#define SPEC_AND 259
+#define COMPARE 260
+#define ADD_SUB_OPERATOR 261
+#define MUL_DIV_MOD_OPERATOR 262
+#define SPEC_REG 263
+#define LENGTH 264
+#define SUBSTR 265
+#define MATCH 266
+#define INDEX 267
+#define LEFT_PARENT 268
+#define RIGHT_PARENT 269
+#define YYERRCODE 256
+static const short exprlhs[] = {                         -1,
+    0,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+    1,    1,    1,    1,    1,    1,    1,    1,
+};
+static const short exprlen[] = {                          2,
+    1,    1,    1,    1,    1,    1,    1,    3,    3,    3,
+    3,    3,    3,    3,    2,    4,    3,    3,
+};
+static const short exprdefred[] = {                       0,
+    2,    3,    4,    5,    6,    7,    0,    0,    0,    0,
+    0,    0,    0,   15,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+   17,   18,   14,    0,    0,    0,    0,    0,   10,   16,
+};
+static const short exprdgoto[] = {                       12,
+   34,
+};
+static const short exprsindex[] = {                    -198,
+    0,    0,    0,    0,    0,    0, -198, -198, -198, -198,
+ -198,    0, -255,    0, -216, -216, -216, -147, -198, -198,
+ -198, -198, -198, -198, -184, -170, -158, -139, -133, -216,
+    0,    0,    0, -226, -206, -161, -247, -263,    0,    0,
+};
+static const short exprrindex[] = {                       0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,   20,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,   88,   86,   79,   72,   65,    0,
+    0,    0,    0,   53,   40,   27,   14,    1,    0,    0,
+};
+static const short exprgindex[] = {                       0,
+    2,
+};
+#define YYTABLESIZE 357
+static const short exprtable[] = {                       24,
+   12,   13,   19,   20,   21,   22,   23,   24,   14,   15,
+   16,   17,   18,   11,   23,   24,   30,   31,   32,    1,
+    0,   35,   36,   37,   38,   39,   13,   35,   36,   37,
+   38,   40,   20,   21,   22,   23,   24,    0,    0,    9,
+    1,   25,   26,   27,   28,   29,   24,    7,    8,    9,
+   10,   11,    8,   21,   22,   23,   24,    0,    1,    2,
+    3,    4,    5,    6,    7,    7,    8,    9,   10,   11,
+    0,    6,    1,    0,    3,    4,    5,    6,    5,    7,
+    8,    9,   10,   11,    0,    4,    1,    3,    0,    4,
+    5,    6,    0,    7,    8,    9,   10,   11,    1,   22,
+   23,   24,    5,    6,    0,    7,    8,    9,   10,   11,
+   19,   20,   21,   22,   23,   24,    0,    1,    0,    0,
+    0,   33,    6,    1,    7,    8,    9,   10,   11,    0,
+    7,    8,    9,   10,   11,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,   12,   12,   12,
+   12,   12,   12,    0,   12,   12,   12,   12,   12,   12,
+   11,   11,   11,   11,   11,    0,    0,   11,   11,   11,
+   11,   11,   11,   13,   13,   13,   13,    0,    0,    0,
+   13,   13,   13,   13,   13,   13,    9,    9,    9,    0,
+    0,    0,    0,    9,    9,    9,    9,    9,    9,    8,
+    8,    0,    0,    0,    0,    0,    8,    8,    8,    8,
+    8,    8,    7,    7,    7,    7,    7,    7,    0,    6,
+    6,    6,    6,    7,    6,    0,    5,    5,    5,    0,
+    6,    5,    0,    4,    4,    3,    0,    5,    4,    0,
+    3,    0,    0,    0,    4,    0,    3,
+};
+static const short exprcheck[] = {                      263,
+    0,    0,  258,  259,  260,  261,  262,  263,    7,    8,
+    9,   10,   11,    0,  262,  263,   15,   16,   17,    0,
+   -1,   20,   21,   22,   23,   24,    0,   26,   27,   28,
+   29,   30,  259,  260,  261,  262,  263,   -1,   -1,    0,
+  257,  258,  259,  260,  261,  262,  263,  264,  265,  266,
+  267,  268,    0,  260,  261,  262,  263,   -1,  257,  258,
+  259,  260,  261,  262,    0,  264,  265,  266,  267,  268,
+   -1,    0,  257,   -1,  259,  260,  261,  262,    0,  264,
+  265,  266,  267,  268,   -1,    0,  257,    0,   -1,  260,
+  261,  262,   -1,  264,  265,  266,  267,  268,  257,  261,
+  262,  263,  261,  262,   -1,  264,  265,  266,  267,  268,
+  258,  259,  260,  261,  262,  263,   -1,  257,   -1,   -1,
+   -1,  269,  262,  257,  264,  265,  266,  267,  268,   -1,
+  264,  265,  266,  267,  268,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  257,  258,  259,
+  260,  261,  262,   -1,  264,  265,  266,  267,  268,  269,
+  257,  258,  259,  260,  261,   -1,   -1,  264,  265,  266,
+  267,  268,  269,  257,  258,  259,  260,   -1,   -1,   -1,
+  264,  265,  266,  267,  268,  269,  257,  258,  259,   -1,
+   -1,   -1,   -1,  264,  265,  266,  267,  268,  269,  257,
+  258,   -1,   -1,   -1,   -1,   -1,  264,  265,  266,  267,
+  268,  269,  258,  259,  260,  261,  262,  263,   -1,  258,
+  259,  260,  261,  269,  263,   -1,  258,  259,  260,   -1,
+  269,  263,   -1,  258,  259,  258,   -1,  269,  263,   -1,
+  263,   -1,   -1,   -1,  269,   -1,  269,
+};
+#define YYFINAL 12
+#ifndef YYDEBUG
+#define YYDEBUG 1
+#endif
+#define YYMAXTOKEN 269
+#if YYDEBUG
+static const char *yyname[] = {
+
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"STRING","SPEC_OR","SPEC_AND",
+"COMPARE","ADD_SUB_OPERATOR","MUL_DIV_MOD_OPERATOR","SPEC_REG","LENGTH",
+"SUBSTR","MATCH","INDEX","LEFT_PARENT","RIGHT_PARENT",
+};
+static const char *yyrule[] = {
+"$accept : exp",
+"exp : expr",
+"expr : STRING",
+"expr : SPEC_OR",
+"expr : SPEC_AND",
+"expr : COMPARE",
+"expr : ADD_SUB_OPERATOR",
+"expr : MUL_DIV_MOD_OPERATOR",
+"expr : expr SPEC_OR expr",
+"expr : expr SPEC_AND expr",
+"expr : expr SPEC_REG expr",
+"expr : expr ADD_SUB_OPERATOR expr",
+"expr : expr MUL_DIV_MOD_OPERATOR expr",
+"expr : expr COMPARE expr",
+"expr : LEFT_PARENT expr RIGHT_PARENT",
+"expr : LENGTH expr",
+"expr : SUBSTR expr expr expr",
+"expr : MATCH expr expr",
+"expr : INDEX expr expr",
+};
+#endif
+#ifndef YYSTYPE
+typedef int YYSTYPE;
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+
+/* define the initial stack-sizes */
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH  YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH  500
+#endif
+#endif
+
+#define YYINITSTACKSIZE 500
+
+int      yydebug;
+int      yynerrs;
+
+typedef struct {
+    unsigned stacksize;
+    short    *s_base;
+    short    *s_mark;
+    short    *s_last;
+    YYSTYPE  *l_base;
+    YYSTYPE  *l_mark;
+} YYSTACKDATA;
+
+#define YYPURE 0
+
+int      yyerrflag;
+int      yychar;
+YYSTYPE  yyval;
+YYSTYPE  yylval;
+
+/* variables for the parser stack */
+static YYSTACKDATA yystack;
+#line 278 "expr.y"
+
+/*
+ * Returns 1 if the string is empty or contains only numeric zero.
+ */
+static int is_zero_or_null(const char *str)
+{
+       char *endptr;
+       return str[0] == '\0'
+               || ( strtoll(str, &endptr, 10) == 0LL
+                       && endptr[0] == '\0');
+}
+
+/*
+ * Returns 1 if the string is an integer.
+ */
+static int is_integer(const char *str)
+{
+       char *endptr;
+       (void) strtoll(str, &endptr, 10);
+       /* note we treat empty string as valid number */
+       return (endptr[0] == '\0');
+}
+
+static int64_t perform_arith_op(const char *left, const char *op, const char *right)
+{
+       int64_t res, sign, l, r;
+       u_int64_t temp;
+
+       res = 0;
+
+       if (!is_integer(left)) {
+               yyerror("non-integer argument '%s'", left);
+               /* NOTREACHED */
+       }
+       if (!is_integer(right)) {
+               yyerror("non-integer argument '%s'", right);
+               /* NOTREACHED */
+       }
+
+       errno = 0;
+       l = strtoll(left, NULL, 10);
+       if (errno == ERANGE) {
+               yyerror("value '%s' is %s is %lld", left,
+                   (l > 0) ? "too big, maximum" : "too small, minimum",
+                   (l > 0) ? LLONG_MAX : LLONG_MIN);
+               /* NOTREACHED */
+       }
+
+       errno = 0;
+       r = strtoll(right, NULL, 10);
+       if (errno == ERANGE) {
+               yyerror("value '%s' is %s is %lld", right,
+                   (l > 0) ? "too big, maximum" : "too small, minimum",
+                   (l > 0) ? LLONG_MAX : LLONG_MIN);
+               /* NOTREACHED */
+       }
+
+       switch(op[0]) {
+       case '+':
+               /* 
+                * Do the op into an unsigned to avoid overflow and then cast
+                * back to check the resulting signage. 
+                */
+               temp = l + r;
+               res = (int64_t) temp;
+               /* very simplistic check for over-& underflow */
+               if ((res < 0 && l > 0 && r > 0)
+                   || (res > 0 && l < 0 && r < 0)) 
+                       yyerror("integer overflow or underflow occurred for "
+                            "operation '%s %s %s'", left, op, right);
+               break;
+       case '-':
+               /* 
+                * Do the op into an unsigned to avoid overflow and then cast
+                * back to check the resulting signage. 
+                */
+               temp = l - r;
+               res = (int64_t) temp;
+               /* very simplistic check for over-& underflow */
+               if ((res < 0 && l > 0 && l > r)
+                   || (res > 0 && l < 0 && l < r) ) 
+                       yyerror("integer overflow or underflow occurred for "
+                           "operation '%s %s %s'", left, op, right);
+               break;
+       case '/':
+               if (r == 0) 
+                       yyerror("second argument to '%s' must not be zero", op);
+               res = l / r;
+                       
+               break;
+       case '%':
+               if (r == 0)
+                       yyerror("second argument to '%s' must not be zero", op);
+               res = l % r;
+               break;
+       case '*':
+               /* shortcut */
+               if ((l == 0) || (r == 0)) {
+                       res = 0;
+                       break;
+               }
+                               
+               sign = 1;
+               if (l < 0)
+                       sign *= -1;
+               if (r < 0)
+                       sign *= -1;
+
+               res = l * r;
+               /*
+                * XXX: not the most portable but works on anything with 2's
+                * complement arithmetic. If the signs don't match or the
+                * result was 0 on 2's complement this overflowed.
+                */
+               if ((res < 0 && sign > 0) || (res > 0 && sign < 0) || 
+                   (res == 0))
+                       yyerror("integer overflow or underflow occurred for "
+                           "operation '%s %s %s'", left, op, right);
+                       /* NOTREACHED */
+               break;
+       }
+       return res;
+}
+
+static int handle_ddash = 1;
+static const char *x = "|&=<>+-*/%:()";
+static const int x_token[] = {
+       SPEC_OR, SPEC_AND, COMPARE, COMPARE, COMPARE, ADD_SUB_OPERATOR,
+       ADD_SUB_OPERATOR, MUL_DIV_MOD_OPERATOR, MUL_DIV_MOD_OPERATOR, 
+       MUL_DIV_MOD_OPERATOR, SPEC_REG, LEFT_PARENT, RIGHT_PARENT
+};
+
+/*
+ * Lexer for expr.
+ */
+int yylex(void)
+{
+  const char *p = *av++;
+  int retval;
+
+  if (!p) retval = 0;
+  else if (!p[0]) retval = STRING;
+  else if (p[1] == '\0') {
+    const char *w = strchr(x, p[0]);
+    if (w)retval = x_token[w-x];
+    else retval = STRING;
+  } else if (p[1] == '=' && p[2] == '\0' && (p[0] == '>' || p[0] == '<' || p[0] == '!' || p[0] == '='))
+    retval = COMPARE;
+  else if (handle_ddash && p[0] == '-' && p[1] == '-' && p[2] == '\0') {
+    /* ignore "--" if passed as first argument and isn't followed
+     * by another STRING */
+    retval = yylex();
+    if (retval != STRING && retval != LEFT_PARENT && retval != RIGHT_PARENT) {
+      retval = STRING; /* is not followed by string or parenthesis, use as STRING */
+      av--; /* was increased in call to yylex() above */
+      p = "--";
+    } else p = yylval; /* "--" is to be ignored */
+  } else if (strcmp(p, "quote") == 0) {
+      retval = yylex();
+      if(!retval) yyerror("Syntax error");
+      retval = STRING;
+      p = yylval;
+  } else if (strcmp(p, "length") == 0) retval = LENGTH;
+  else if (strcmp(p, "match") == 0) retval = MATCH;
+  else if (strcmp(p, "index") == 0) retval = INDEX;
+  else if (strcmp(p, "substr") == 0) retval = SUBSTR;
+  else retval = STRING;
+
+  handle_ddash = 0;
+  yylval = p;
+
+  return retval;
+}
+
+/*
+ * Print error message and exit with error 2 (syntax error).
+ */
+static void yyerror(const char *fmt, ...)
+{
+       va_list arg;
+       va_start(arg, fmt);
+       verrx(2, fmt, arg);
+       va_end(arg);
+}
+
+void expr_main(void)
+{
+  if(toys.optc < 1) yyerror("too few arguments");
+  (void)setlocale(LC_ALL, "");
+       av = toys.optargs;
+       toys.exitval = yyparse();
+       /* NOTREACHED */
+}
+#line 529 "expr.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack(YYSTACKDATA *data)
+{
+    int i;
+    unsigned newsize;
+    short *newss;
+    YYSTYPE *newvs;
+
+    if ((newsize = data->stacksize) == 0)
+        newsize = YYINITSTACKSIZE;
+    else if (newsize >= YYMAXDEPTH)
+        return -1;
+    else if ((newsize *= 2) > YYMAXDEPTH)
+        newsize = YYMAXDEPTH;
+
+    i = data->s_mark - data->s_base;
+    newss = (data->s_base != 0)
+          ? (short *)realloc(data->s_base, newsize * sizeof(*newss))
+          : (short *)malloc(newsize * sizeof(*newss));
+    if (newss == 0)
+        return -1;
+
+    data->s_base  = newss;
+    data->s_mark = newss + i;
+
+    newvs = (data->l_base != 0)
+          ? (YYSTYPE *)realloc(data->l_base, newsize * sizeof(*newvs))
+          : (YYSTYPE *)malloc(newsize * sizeof(*newvs));
+    if (newvs == 0)
+        return -1;
+
+    data->l_base = newvs;
+    data->l_mark = newvs + i;
+
+    data->stacksize = newsize;
+    data->s_last = data->s_base + newsize - 1;
+    return 0;
+}
+
+#if YYPURE || defined(YY_NO_LEAKS)
+static void yyfreestack(YYSTACKDATA *data)
+{
+    free(data->s_base);
+    free(data->l_base);
+    memset(data, 0, sizeof(*data));
+}
+#else
+#define yyfreestack(data) /* nothing */
+#endif
+
+#define YYABORT  goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR  goto yyerrlab
+
+int
+YYPARSE_DECL()
+{
+    int yym, yyn, yystate;
+#if YYDEBUG
+    const char *yys;
+
+    if ((yys = getenv("YYDEBUG")) != 0)
+    {
+        yyn = *yys;
+        if (yyn >= '0' && yyn <= '9')
+            yydebug = yyn - '0';
+    }
+#endif
+
+    yynerrs = 0;
+    yyerrflag = 0;
+    yychar = YYEMPTY;
+    yystate = 0;
+
+#if YYPURE
+    memset(&yystack, 0, sizeof(yystack));
+#endif
+
+    if (yystack.s_base == NULL && yygrowstack(&yystack)) goto yyoverflow;
+    yystack.s_mark = yystack.s_base;
+    yystack.l_mark = yystack.l_base;
+    yystate = 0;
+    *yystack.s_mark = 0;
+
+yyloop:
+    if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+    if (yychar < 0)
+    {
+        if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, reading %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+    }
+    if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: state %d, shifting to state %d\n",
+                    YYPREFIX, yystate, yytable[yyn]);
+#endif
+        if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+        {
+            goto yyoverflow;
+        }
+        yystate = yytable[yyn];
+        *++yystack.s_mark = yytable[yyn];
+        *++yystack.l_mark = yylval;
+        yychar = YYEMPTY;
+        if (yyerrflag > 0)  --yyerrflag;
+        goto yyloop;
+    }
+    if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+        yyn = yytable[yyn];
+        goto yyreduce;
+    }
+    if (yyerrflag) goto yyinrecovery;
+
+    yyerror("syntax error");
+
+    goto yyerrlab;
+
+yyerrlab:
+    ++yynerrs;
+
+yyinrecovery:
+    if (yyerrflag < 3)
+    {
+        yyerrflag = 3;
+        for (;;)
+        {
+            if ((yyn = yysindex[*yystack.s_mark]) && (yyn += YYERRCODE) >= 0 &&
+                    yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yystack.s_mark, yytable[yyn]);
+#endif
+                if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+                {
+                    goto yyoverflow;
+                }
+                yystate = yytable[yyn];
+                *++yystack.s_mark = yytable[yyn];
+                *++yystack.l_mark = yylval;
+                goto yyloop;
+            }
+            else
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: error recovery discarding state %d\n",
+                            YYPREFIX, *yystack.s_mark);
+#endif
+                if (yystack.s_mark <= yystack.s_base) goto yyabort;
+                --yystack.s_mark;
+                --yystack.l_mark;
+            }
+        }
+    }
+    else
+    {
+        if (yychar == 0) goto yyabort;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+        yychar = YYEMPTY;
+        goto yyloop;
+    }
+
+yyreduce:
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+                YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+    yym = yylen[yyn];
+    if (yym)
+        yyval = yystack.l_mark[1-yym];
+    else
+        memset(&yyval, 0, sizeof yyval);
+    switch (yyn)
+    {
+case 1:
+#line 40 "expr.y"
+       {
+                printf("%s\n", yystack.l_mark[0]);
+               return (is_zero_or_null(yystack.l_mark[0]));
+               }
+break;
+case 2:
+#line 46 "expr.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+case 3:
+#line 46 "expr.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+case 4:
+#line 46 "expr.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+case 5:
+#line 46 "expr.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+case 6:
+#line 46 "expr.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+case 7:
+#line 46 "expr.y"
+       { yyval = yystack.l_mark[0]; }
+break;
+
+case 8:
+#line 47 "expr.y"
+       {
+               /*
+                * Return evaluation of first expression if it is neither
+                * an empty string nor zero; otherwise, returns the evaluation
+                * of second expression.
+                */
+               if (!is_zero_or_null(yystack.l_mark[-2]))
+                       yyval = yystack.l_mark[-2];
+               else
+                       yyval = yystack.l_mark[0];
+               }
+break;
+case 9:
+#line 58 "expr.y"
+       {
+               /*
+                * Returns the evaluation of first expr if neither expression
+                * evaluates to an empty string or zero; otherwise, returns
+                * zero.
+                */
+               if (!is_zero_or_null(yystack.l_mark[-2]) && !is_zero_or_null(yystack.l_mark[0]))
+                       yyval = yystack.l_mark[-2];
+               else
+                       yyval = "0";
+               }
+break;
+case 10:
+#line 69 "expr.y"
+       {
+               /*
+                * The ``:'' operator matches first expr against the second,
+                * which must be a regular expression.
+                */
+               regex_t rp;
+               regmatch_t rm[2];
+               int eval;
+
+               /* compile regular expression */
+               if ((eval = regcomp(&rp, yystack.l_mark[0], 0)) != 0) {
+                       char errbuf[256];
+                       (void)regerror(eval, &rp, errbuf, sizeof(errbuf));
+                       yyerror("%s", errbuf);
+                       /* NOT REACHED */
+               }
+               
+               /* compare string against pattern --  remember that patterns 
+                  are anchored to the beginning of the line */
+               if (regexec(&rp, yystack.l_mark[-2], 2, rm, 0) == 0 && rm[0].rm_so == 0) {
+                       char *val;
+                       if (rm[1].rm_so >= 0) {
+                                val = xmsprintf("%.*s",
+                                       (int) (rm[1].rm_eo - rm[1].rm_so),
+                                       yystack.l_mark[-2] + rm[1].rm_so);
+                       } else {
+                               val = xmsprintf("%d",
+                                       (int)(rm[0].rm_eo - rm[0].rm_so));
+                       }
+                       if (val == NULL)
+                               err(1, NULL);
+                       yyval = val;
+               } else {
+                       if (rp.re_nsub == 0) {
+                               yyval = "0";
+                       } else {
+                               yyval = "";
+                       }
+               }
+
+               }
+break;
+case 11:
+#line 110 "expr.y"
+       {
+               /* Returns the results of addition, subtraction */
+               char *val;
+               int64_t res;
+               
+               res = perform_arith_op(yystack.l_mark[-2], yystack.l_mark[-1], yystack.l_mark[0]);
+               val = xmsprintf("%lld", (long long int) res);
+               if (val == NULL)
+                       err(1, NULL);
+               yyval = val;
+
+        }
+break;
+case 12:
+#line 122 "expr.y"
+       {
+               /* 
+                * Returns the results of multiply, divide or remainder of 
+                * numeric-valued arguments.
+                */
+               char *val;
+               int64_t res;
+
+               res = perform_arith_op(yystack.l_mark[-2], yystack.l_mark[-1], yystack.l_mark[0]);
+               val = xmsprintf("%lld", (long long int) res);
+               if (val == NULL)
+                       err(1, NULL);
+               yyval = val;
+
+               }
+break;
+case 13:
+#line 137 "expr.y"
+       {
+               /*
+                * Returns the results of integer comparison if both arguments
+                * are integers; otherwise, returns the results of string
+                * comparison using the locale-specific collation sequence.
+                * The result of each comparison is 1 if the specified relation
+                * is true, or 0 if the relation is false.
+                */
+
+               int64_t l, r;
+               int res;
+
+               res = 0;
+
+               /*
+                * Slight hack to avoid differences in the compare code
+                * between string and numeric compare.
+                */
+               if (is_integer(yystack.l_mark[-2]) && is_integer(yystack.l_mark[0])) {
+                       /* numeric comparison */
+                       l = strtoll(yystack.l_mark[-2], NULL, 10);
+                       r = strtoll(yystack.l_mark[0], NULL, 10);
+               } else {
+                       /* string comparison */
+                       l = strcoll(yystack.l_mark[-2], yystack.l_mark[0]);
+                       r = 0;
+               }
+
+               switch(yystack.l_mark[-1][0]) { 
+               case '=': /* equal */
+                       res = (l == r);
+                       break;
+               case '>': /* greater or greater-equal */
+                       if (yystack.l_mark[-1][1] == '=')
+                               res = (l >= r);
+                       else
+                               res = (l > r);
+                       break;
+               case '<': /* lower or lower-equal */
+                       if (yystack.l_mark[-1][1] == '=')
+                               res = (l <= r);
+                       else
+                               res = (l < r);
+                       break;
+               case '!': /* not equal */
+                       /* the check if this is != was done in yylex() */
+                       res = (l != r);
+               }
+
+               yyval = (res) ? "1" : "0";
+
+               }
+break;
+case 14:
+#line 189 "expr.y"
+       { yyval = yystack.l_mark[-1]; }
+break;
+case 15:
+#line 190 "expr.y"
+       {
+               /*
+                * Return length of 'expr' in bytes.
+                */
+               char *ln;
+
+               ln = xmsprintf("%ld", (long) strlen(yystack.l_mark[0]));
+               if (ln == NULL)
+                       err(1, NULL);
+               yyval = ln;
+               }
+break;
+case 16:
+#line 201 "expr.y"
+  {
+    char *ln = NULL;
+    if(!is_integer(yystack.l_mark[0]) || (!is_integer(yystack.l_mark[-1])))
+      errx(1, "Index or length can only be integer.");
+    long long elen = strtoll(yystack.l_mark[0], NULL, 10);
+    long long sind = strtoll(yystack.l_mark[-1], NULL, 10);
+    if(sind <= 0 || elen <= 0)
+      errx(1, "Index or length can't be 0 or neg");
+    ln = xmsprintf("%s", yystack.l_mark[-2]);
+    if(sind <= strlen(yystack.l_mark[-2]))
+      ln += sind-1;
+    else ln[0] = '\0';
+    if (ln == NULL)
+      errx(1, NULL);
+    if(elen < strlen(ln))
+      ln[elen] = '\0';
+    yyval = ln;
+  }
+break;
+case 17:
+#line 211 "expr.y"
+       {
+               /*
+                * The ``:'' operator matches first expr against the second,
+                * which must be a regular expression.
+                */
+               regex_t rp;
+               regmatch_t rm[2];
+               int eval;
+
+               /* compile regular expression */
+               if ((eval = regcomp(&rp, yystack.l_mark[0], 0)) != 0) {
+                       char errbuf[256];
+                       (void)regerror(eval, &rp, errbuf, sizeof(errbuf));
+                       yyerror("%s", errbuf);
+                       /* NOT REACHED */
+               }
+               
+               /* compare string against pattern --  remember that patterns 
+                  are anchored to the beginning of the line */
+               if (regexec(&rp, yystack.l_mark[-1], 2, rm, 0) == 0 && rm[0].rm_so == 0) {
+                       char *val;
+                       if (rm[1].rm_so >= 0) {
+                               val = xmsprintf("%.*s", (int) (rm[1].rm_eo - rm[1].rm_so), yystack.l_mark[-1] + rm[1].rm_so);
+                       } else {
+                               val = xmsprintf("%d", (int)(rm[0].rm_eo - rm[0].rm_so));
+                       }
+                       if (val == NULL) errx(1, NULL);
+                       yyval = val;
+               } else {
+                       if (rp.re_nsub == 0) {
+                               yyval = "0";
+                       } else {
+                               yyval = "";
+                       }
+               }
+               }
+break;
+case 18:
+#line 252 "expr.y"
+{
+  char *ptr = (char*)yystack.l_mark[0];
+  char *res, *str = NULL;
+  long count = -1;
+  str = (char*)yystack.l_mark[-1];
+  while(*ptr != '\0'){
+    res = strchr(str, (int)(*ptr));
+    if(res && (count > res - str || count == -1) ) count = res - str;
+    ptr++;
+  }
+  res = NULL;
+  res = xmsprintf("%ld", count+1);
+  if (res == NULL)
+    errx(1, NULL);
+  yyval = res;
+}
+break;
+#line 1004 "expr.c"
+    }
+    yystack.s_mark -= yym;
+    yystate = *yystack.s_mark;
+    yystack.l_mark -= yym;
+    yym = yylhs[yyn];
+    if (yystate == 0 && yym == 0)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+        yystate = YYFINAL;
+        *++yystack.s_mark = YYFINAL;
+        *++yystack.l_mark = yyval;
+        if (yychar < 0)
+        {
+            if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+            if (yydebug)
+            {
+                yys = 0;
+                if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+                if (!yys) yys = "illegal-symbol";
+                printf("%sdebug: state %d, reading %d (%s)\n",
+                        YYPREFIX, YYFINAL, yychar, yys);
+            }
+#endif
+        }
+        if (yychar == 0) goto yyaccept;
+        goto yyloop;
+    }
+    if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+        yystate = yytable[yyn];
+    else
+        yystate = yydgoto[yym];
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yystack.s_mark, yystate);
+#endif
+    if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack))
+    {
+        goto yyoverflow;
+    }
+    *++yystack.s_mark = (short) yystate;
+    *++yystack.l_mark = yyval;
+    goto yyloop;
+
+yyoverflow:
+    yyerror("yacc stack overflow");
+
+yyabort:
+    yyfreestack(&yystack);
+    return (1);
+
+yyaccept:
+    yyfreestack(&yystack);
+    return (0);
+}
+
+/* $NetBSD: expr.y,v 1.38 2012/03/15 02:02:20 joerg Exp $ */
+
+/*_
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jaromir Dolecek <jdolecek@NetBSD.org> and J.T. Conklin <jtc@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/toys/other/fdisk.c b/toys/other/fdisk.c
new file mode 100644 (file)
index 0000000..bf84715
--- /dev/null
@@ -0,0 +1,1632 @@
+/* fdisk.c -  fdisk program to modify partitions on disk.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
+
+config FDISK
+  bool "fdisk"
+  default y
+  help
+    usage: fdisk [-lu] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SECTSZ] DISK
+
+    Change partition table
+
+    -u            Start and End are in sectors (instead of cylinders)
+    -l            Show partition table for each DISK, then exit
+    -b size       sector size (512, 1024, 2048 or 4096)
+    -C CYLINDERS  Set number of cylinders/heads/sectors
+    -H HEADS
+    -S SECTORS
+*/
+
+#define FOR_fdisk
+#include "toys.h"
+#include <linux/hdreg.h>
+
+GLOBALS(
+  long sect_sz;
+  long sectors;
+  long heads;
+  long cylinders;
+)
+
+#define EXTENDED        0x05                                                                      
+#define WIN98_EXTENDED  0x0f
+#define LINUX_PARTITION 0x81
+#define LINUX_SWAP      0x82
+#define LINUX_NATIVE    0x83
+#define LINUX_EXTENDED  0x85
+#define LINUX_LVM       0x8e
+#define LINUX_RAID      0xfd
+
+#define SECTOR_SIZE 512
+#define ONE_K       1024
+
+//partition max is modifiable
+#define PARTITION_MAX  60 
+#define IS_EXTENDED(i) ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+
+typedef off_t sector_t;
+
+struct partition {
+  unsigned char boot_ind, head, sector, cyl, sys_ind, end_head,
+                end_sector, end_cyl, start4[4], size4[4];
+};
+
+struct part_entry {
+  struct partition *part;
+  char *sec_buffer;
+  sector_t  start_offset;
+  int modified;
+};
+
+struct dos_types {
+  int id;
+  char type[PATH_MAX];
+} sys_types[] = {
+  {0x00, "Empty"},
+  {0x01, "FAT12"},
+  {0x04, "FAT16 <32M"},
+  {0x05, "Extended"},     
+  {0x06, "FAT16"},     
+  {0x07, "HPFS/NTFS"},  
+  {0x0a, "OS/2 Boot Manager"},
+  {0x0b, "Win95 FAT32"},
+  {0x0c, "Win95 FAT32 (LBA)"},
+  {0x0e, "Win95 FAT16 (LBA)"},
+  {0x0f, "Win95 Ext'd (LBA)"},
+  {0x11, "Hidden FAT12"},
+  {0x12, "Compaq diagnostics"},
+  {0x14, "Hidden FAT16 <32M"},
+  {0x16, "Hidden FAT16"},
+  {0x17, "Hidden HPFS/NTFS"},
+  {0x1b, "Hidden Win95 FAT32"},
+  {0x1c, "Hidden W95 FAT32 (LBA)"},
+  {0x1e, "Hidden W95 FAT16 (LBA)"},
+  {0x3c, "Part.Magic recovery"},
+  {0x41, "PPC PReP Boot"},
+  {0x42, "SFS"},
+  {0x63, "GNU HURD or SysV"},
+  {0x80, "Old Minix"},    
+  {0x81, "Minix / old Linux"},
+  {0x82, "Linux swap"},    
+  {0x83, "Linux"},
+  {0x84, "OS/2 hidden C: drive"},
+  {0x85, "Linux extended"},
+  {0x86, "NTFS volume set"},
+  {0x87, "NTFS volume set"},
+  {0x8e, "Linux LVM"},
+  {0x9f, "BSD/OS"},     
+  {0xa0, "Thinkpad hibernation"},
+  {0xa5, "FreeBSD"},     
+  {0xa6, "OpenBSD"},
+  {0xa8, "Darwin UFS"},
+  {0xa9, "NetBSD"},
+  {0xab, "Darwin boot"},
+  {0xb7, "BSDI fs"},
+  {0xb8, "BSDI swap"},
+  {0xbe, "Solaris boot"},
+  {0xeb, "BeOS fs"},
+  {0xee, "EFI GPT"},    
+  {0xef, "EFI (FAT-12/16/32)"},    
+  {0xf0, "Linux/PA-RISC boot"},   
+  {0xf2, "DOS secondary"},     
+  {0xfd, "Linux raid autodetect"},
+};
+
+static int num_parts, disp_unit_cyl, dos_flag, dev_fd = 3;
+static long g_cylinders, g_heads, g_sectors, g_sect_size;
+static sector_t total_number_sectors, extended_offset;
+char MBRbuf[2048], *disk_device;
+
+struct part_entry partitions[PARTITION_MAX];
+
+static struct partition* part_offset(char *secbuf, int i)
+{
+  return (struct partition*)(secbuf + 0x1be + i*(sizeof(struct partition)));
+}
+
+static unsigned char sector(unsigned char s)
+{
+  return (s & 0x3f);
+}
+
+static uint32_t cylinder( unsigned char s, unsigned char c)
+{
+  return (c | ((s & 0xc0) << 2));
+}
+
+static void set_levalue(unsigned char *cp, sector_t value )
+{
+  void *ptr;
+  uint32_t val = htole32(value);
+  ptr = (void*)&val;
+  memcpy(cp, ptr, 4);
+}
+
+static void set_hsc(struct partition *p, sector_t start, sector_t end)
+{
+  if (dos_flag && (start / (g_sectors * g_heads) > 1023))
+    start = g_heads * g_sectors * ONE_K - 1;
+  p->sector = (start % g_sectors) + 1;
+  start /= g_sectors;
+  p->head = start % g_heads;
+  start /= g_heads;
+  p->cyl = start & 0xFF;
+  p->sector |= (start >> 2) & 0xc0;
+
+  if (dos_flag && (end / (g_sectors * g_heads) > 1023))
+    end = g_heads * g_sectors * ONE_K - 1;
+  p->end_sector = (end % g_sectors) + 1;
+  end /= g_sectors;
+  p->end_head = end % g_heads;
+  end /= g_heads;
+  p->end_cyl = end & 0xFF;
+  p->end_sector |= (end >> 2) & 0xc0;
+}
+
+static void list_types(void)
+{
+  int i, size, adjust;
+
+  size = ARRAY_LEN(sys_types);
+  if(size % 2) adjust = 1;
+  else adjust = 0;
+
+  for (i = 0; i < (size - adjust); i+=2)
+    printf("%2x %-22s\t\t%2x %-22.22s\n", sys_types[i].id, sys_types[i].type,
+        sys_types[i+1].id, sys_types[i+1].type);
+  if (adjust) printf("%2x %-22s\n",sys_types[size-1].id, sys_types[size-1].type);
+  xputc('\n');
+}
+
+static int valid(long size)
+{
+  switch (size) {
+    case SECTOR_SIZE: /*FALL_THROUGH*/
+    case 1024:       /*FALL_THROUGH*/
+    case 2048:       /*FALL_THROUGH*/
+    case 4096:
+      return 1;
+      /*Not Reached*/
+    default:
+      toys.exithelp = 1;
+      error_exit("");
+      /*Not Reached */
+  }
+  /*Not Reached */
+  return 0;
+}
+
+static void read_sec_sz()
+{
+  int arg;       
+  if (ioctl(dev_fd, BLKSSZGET, &arg) == 0) g_sect_size = arg;
+  if (toys.optflags & FLAG_b && valid(TT.sect_sz)) g_sect_size = TT.sect_sz;
+}
+
+static sector_t read_size()
+{
+  uint64_t sec64 = 0;
+  unsigned long sectors = 0;
+  if (ioctl(dev_fd, BLKGETSIZE64, &sec64) == 0) {
+    sec64 = sec64 >> 9; //convert to 512 block size.
+    if (sec64 != (uint32_t) sec64) {
+      perror_msg("device has more than 2^32 sectors, can't use all of them");
+      sec64 = (uint32_t) - 1L;
+    }
+    return sec64;
+  }
+  if (ioctl(dev_fd, BLKGETSIZE, &sectors) == 0)
+    if (sizeof(long) > sizeof(sector_t) && sectors != (sector_t)sectors)
+      sectors = (uint32_t) - 1L;
+  return sectors;
+}
+
+
+static int validate_part_buff(char *buffer)
+{
+  if ((buffer[510] != 0x55) || (buffer[511] != 0xAA)) return 0;
+  return 1;
+}
+
+static int is_partition_clear(struct partition* p)
+{
+  int i = 0;
+  unsigned char res = 0;
+  const char *ptr = (const char*)p;
+
+  for (i =0; i < sizeof(struct partition); i++)
+    res |= (unsigned char)ptr[i];
+  return (res == 0x00);
+}
+
+static uint32_t swap_le32toh(unsigned char *cp)
+{
+  uint32_t val;
+  void* ptr = (void*)&val;
+  memcpy(ptr, cp, 4);
+  return le32toh(val);
+}
+
+static int check_order(void)    
+{                              
+  sector_t first[num_parts], last_seen_val = 0;
+  int i;
+  struct part_entry *pe;       
+  struct partition *px;
+
+  for (i = 0; i < num_parts; i++) {                                                                                                                                             
+    if (i == 4) last_seen_val = 0;
+    pe = &partitions[i];       
+    px = pe->part;             
+//    if (!is_partition_clear(px)) {
+    if (px->sys_ind) {
+      first[i] = swap_le32toh(px->start4) + pe->start_offset;
+      if (last_seen_val > first[i]) return 1;
+      last_seen_val = first[i];
+    }
+  }
+  return 0;
+}
+
+static void read_geometry(struct hd_geometry *disk)
+{
+  struct hd_geometry geometry;
+
+  if (ioctl(dev_fd, HDIO_GETGEO, &geometry) == 0) {
+    disk->heads = geometry.heads;
+    disk->sectors = geometry.sectors;
+  }
+}
+
+static void read_ebr(int idx)
+{
+  char *sec_buf = NULL;
+  sector_t offset = 0, local_start_off = 0;
+  struct partition *p, *q;
+
+  q = p = partitions[idx].part;
+  local_start_off = swap_le32toh(p->start4);
+
+  if (!extended_offset) extended_offset = local_start_off;
+  do {
+    sec_buf = xzalloc(g_sect_size);
+    partitions[num_parts].part = part_offset(sec_buf, 0);
+    partitions[num_parts].sec_buffer = sec_buf;
+    offset = swap_le32toh(q->start4);
+
+    if (num_parts > 4) offset += local_start_off;
+
+
+    partitions[num_parts].start_offset = offset;
+    xlseek(dev_fd, (off_t)(offset * g_sect_size), SEEK_SET);
+
+    if (g_sect_size != readall(dev_fd, sec_buf, g_sect_size)) {
+      close(dev_fd);
+      error_exit("Couldn't read sector zero\n");
+    }
+    num_parts++; //extended partions present.
+    q = part_offset(sec_buf, 1);
+  } while (!is_partition_clear(q));
+}
+
+static void physical_HS(int* h, int *s)
+{  
+  const unsigned char *bufp = (const unsigned char *)MBRbuf;
+  struct partition *p;
+  int i, end_h, end_s, e_hh = 0, e_ss = 0, ini = 1, dirty = 0;
+
+  if (!(validate_part_buff((char*)bufp))) return;
+
+  for (i = 0; i < 4; i++) {
+    p = part_offset((char*)bufp, i);
+    if (p->sys_ind != 0) {
+      end_h = p->end_head + 1;
+      end_s = (p->end_sector & 077);
+      if (ini) {
+        e_hh = end_h;
+        e_ss = end_s;
+        ini = 0;
+      } else if (e_hh !=end_h || e_ss != end_s)
+        dirty = 1;
+    }
+  }
+  if (!dirty && !ini) {
+    *h = e_hh;
+    *s = e_ss;
+  }
+}
+
+static void reset_boot(int change)
+{
+  int i;
+  for(i = 0; i < 4; i++) {
+    struct part_entry *pe = &partitions[i];
+    pe->part = part_offset(MBRbuf, i);
+    pe->start_offset = 0;
+    pe->sec_buffer = MBRbuf;
+    pe->modified = change;
+  }
+}
+
+static inline void write_table_flag(char *buf)
+{
+  buf[510] = 0x55;
+  buf[511] = 0xaa;
+}
+
+static void free_bufs(void)
+{
+  int i = 0;
+  for (i = 4; i < num_parts; i++)
+    free(partitions[i].sec_buffer);
+}
+
+static void create_empty_doslabel(void)
+{
+  printf("Building a new DOS Disklabel. The changes will\n"
+      "remain in memory only, until you write it.\n");
+
+  num_parts = 4;
+  extended_offset = 0;
+  memset(&MBRbuf[510 - 4*16], 0, 4*16);
+  write_table_flag(MBRbuf);
+  partitions[0].modified = 1;
+  reset_boot(1);
+}
+static int read_mbr(char *device, int validate)
+{
+  int fd, sector_fac, i, h = 0, s = 0;
+  struct hd_geometry disk;
+  fd = open(device, O_RDWR);
+  if(fd == -1) {
+    perror_msg("can't open '%s'",device);
+    return 1;
+  }
+
+  disk_device = strdup(device);
+  if(fd != dev_fd) {
+    if(dup2(fd, dev_fd) != dev_fd) perror_exit("Can't dup2");
+    close(fd);
+  }
+
+  /*read partition table - MBR*/
+  if (SECTOR_SIZE != readall(dev_fd, MBRbuf, SECTOR_SIZE)) {
+    close(dev_fd);
+    perror_exit("Couldn't read sector zero\n");
+  }
+  if (validate && !validate_part_buff(MBRbuf)) {
+    printf("Device contains neither a valid DOS "
+        "partition table, nor Sun, SGI, OSF or GPT "
+        "disklabel\n");
+    create_empty_doslabel();
+  }
+
+  disk.heads = disk.sectors = 0;
+  read_geometry(&disk); //CHS values
+  total_number_sectors = read_size(); //Device size
+  read_sec_sz();
+  sector_fac = g_sect_size/SECTOR_SIZE; //512 is hardware sector size.
+  physical_HS(&h, &s); //physical dimensions may be diferent from HDIO_GETGEO
+  g_sectors = (toys.optflags & FLAG_S && TT.sectors)? TT.sectors :  s? s : disk.sectors?disk.sectors : 63;
+  g_heads = (toys.optflags & FLAG_H && TT.heads)? TT.heads : h? h : disk.heads? disk.heads : 255;
+  g_cylinders = total_number_sectors/(g_heads * g_sectors * sector_fac);
+
+  if (!g_cylinders) g_cylinders = toys.optflags & FLAG_C? TT.cylinders : 0;
+  if ((g_cylinders > ONE_K) && !(toys.optflags & (FLAG_l | FLAG_S)))
+    printf("\nThe number of cylinders for this disk is set to %lu.\n"
+        "There is nothing wrong with that, but this is larger than 1024,\n"
+        "and could in certain setups cause problems.\n", g_cylinders);
+  for (i = 0; i < num_parts; i++) {
+    if (IS_EXTENDED(partitions[i].part->sys_ind)) {
+      read_ebr(i);
+      break;
+    }
+  }
+
+  return 0;
+}
+
+static char* get_type(int sys_ind)
+{
+  int i, size = ARRAY_LEN(sys_types);
+  for (i = 0; i < size; i++)
+    if (sys_ind == sys_types[i].id)
+      return sys_types[i].type;
+  return "Unknown";
+}
+
+static void consistency_check(const struct partition *p, int partition)                                                           
+{        
+  unsigned physbc, physbh, physbs, physec, physeh, physes;
+  unsigned lbc, lbh, lbs, lec, leh, les;
+  sector_t start, end;
+
+  if (!g_heads || !g_sectors || (partition >= 4)) return;
+  /* physical beginning c, h, s */
+  physbc = cylinder(p->sector,p->cyl);
+  physbh = p->head;
+  physbs = sector(p->sector);
+  /* physical ending c, h, s */
+  physec = cylinder(p->end_sector, p->end_cyl);
+  physeh = p->end_head;
+  physes = sector(p->end_sector);
+  /* logical begin and end CHS values */
+  start = swap_le32toh((unsigned char*)(p->start4));
+  end = start + swap_le32toh((unsigned char*)(p->size4)) -1;
+
+  lbc = start/(g_sectors * g_heads);
+  lbh = (start/g_sectors) % g_heads;
+  lbs = (start % g_sectors) + 1;
+
+  lec = end/(g_sectors * g_heads);
+  leh = (end/g_sectors) % g_heads;
+  les = (end % g_sectors) + 1;
+
+  /*Logical and Physical diff */
+  if (g_cylinders <= ONE_K && (physbc != lbc || physbh != lbh || physbs != lbs)) {
+    printf("Partition %u has different physical/logical beginings (Non-Linux?): \n", partition+1);
+    printf("phys = (%u %u %u) ",physbc, physbh, physbs);
+    printf("logical = (%u %u %u)\n", lbc, lbh, lbs);
+  }
+  if (g_cylinders <= ONE_K && (physec != lec || physeh != leh || physes != les)) {
+    printf("Partition %u has different physical/logical endings: \n", partition+1);
+    printf("phys = (%u %u %u) ",physec, physeh, physes);
+    printf("logical = (%u %u %u)\n", lec, leh, les);
+  }
+  /* Ending on cylinder boundary? */
+  if (physeh != (g_heads - 1) || physes != g_sectors)
+    printf("Partition %u does not end on cylinder boundary\n", partition + 1);
+}
+
+
+static void list_partitions(int validate)
+{
+  int i = 0, len, odds = 0;
+  struct partition *p;
+  char boot, lastchar, *dev;
+  uint32_t start_cyl, end_cyl, start_sec, end_sec, blocks, secs;
+
+  if (validate && !validate_part_buff(MBRbuf)) {
+    close(dev_fd);
+    toys.exitval = 1;
+    printf("Device %s: doesn't contain a valid partition table\n", disk_device);
+    return;
+  }
+  dev = disk_device;
+  len = strlen(dev);
+  if (isdigit(dev[len - 1])) lastchar = 'p';
+  else lastchar = '\0';
+
+  printf("%*s Boot      Start         End      Blocks  Id System\n", len+1, "Device");
+  for (i = 0; i < num_parts; i++) {
+    p = partitions[i].part;
+    if (is_partition_clear(p)) continue;
+
+    boot = p->boot_ind == 0x80?'*':' ';
+    start_sec = swap_le32toh(p->start4) + partitions[i].start_offset; //sector(p->sector);
+    secs = swap_le32toh(p->size4);
+
+    if ((start_sec + secs) == 0) end_sec = 0;
+    else end_sec = start_sec + secs -1;
+    start_cyl = start_sec/(g_heads * g_sectors) + 1;
+    end_cyl = end_sec/(g_heads * g_sectors) + 1;//cylinder(p->end_sector, p->end_cyl);
+    blocks = secs;
+    if (g_sect_size < ONE_K) {
+      blocks /= (ONE_K/g_sect_size);
+      odds = secs %(ONE_K/g_sect_size);
+    } else if (g_sect_size > ONE_K) blocks *= (g_sect_size/ONE_K);
+
+    if (lastchar) printf("%s%c%d",dev, lastchar, i+1);
+    else printf("%s%d",dev, i+1);
+
+    printf("   %c %11u %11u %11u%c %2x %s\n",
+        boot,
+        disp_unit_cyl == 0? start_sec: start_cyl, /*start */
+        disp_unit_cyl == 0? end_sec: end_cyl,   /*end */
+        blocks,odds?'+':' ', p->sys_ind, get_type(p->sys_ind));
+
+    consistency_check(p, i);
+  }
+  if (check_order()) printf("\nPartition table entries are not in disk order");
+}
+
+static void print_mbr(int validate)
+{
+  unsigned long long bytes = ((unsigned long long)total_number_sectors << 9);
+  long mbytes = bytes/1000000;
+
+  if (mbytes < 10000) printf("Disk %s: %lu MB, %llu bytes\n", disk_device, mbytes, bytes);
+  else printf("Disk %s: %lu.%lu GB, %llu bytes\n", disk_device, mbytes/1000, (mbytes/100)%10, bytes);
+  printf("%ld heads, %ld sectors/track, %ld cylinders", g_heads, g_sectors, g_cylinders);
+  if (!disp_unit_cyl) {
+    printf(", total %lld sectors\n", total_number_sectors/(g_sect_size/SECTOR_SIZE));
+    printf("Units = sectors of 1 * %ld = %ld bytes\n",g_sect_size, g_sect_size);
+  } else {
+    xputc('\n');
+    printf("Units = cylinders of %ld * %ld = %ld bytes\n\n",g_heads * g_sectors, g_sect_size, g_heads * g_sectors * g_sect_size);
+  }
+  list_partitions(validate);
+  xputc('\n');
+}
+
+static void init_members(void)
+{
+  int i = 0;
+  num_parts = 4; //max of primaries in a part table
+  disp_unit_cyl = dos_flag = 1;
+  extended_offset = 0;
+  g_sect_size = SECTOR_SIZE;
+  for (i = 0; i < num_parts; i++) {
+    partitions[i].part = part_offset(MBRbuf, i);
+    partitions[i].sec_buffer = MBRbuf;
+    partitions[i].modified = 0;
+    partitions[i].start_offset = 0;
+  }
+}
+
+static int read_input(char *mesg, char *outp)
+{
+  char str[80], *p; //max 80 chars
+  int size = 0;
+redo:
+  printf("%s", mesg);
+  fgets(str, 80, stdin);
+
+  size = strlen(str);
+  if (size <= 0) exit(0);
+  if (str[size-1] == '\n') str[--size] = '\0';
+  if (size == 0) goto redo;
+
+  p = (char *)&str[0];
+  while (*p != '\0' && *p <= ' ') p++;
+  if (outp) memcpy(outp, p, strlen(p) + 1); //1 for nul
+  return *p;
+}
+
+static int read_hex(char *mesg)
+{
+  int val;
+  char input[80], *endp;
+  while (1) {
+    read_input(mesg, input);
+    if ((*input | 0x20) == 'l') {
+      list_types();
+      memset(input, 0, 80);
+      continue;
+    }
+    val = strtoul(input, &endp, 16);
+    if (endp && *endp) continue;
+    if (val <= 0xff) return val;
+  }
+}
+
+static void clear_partition(struct partition *p)
+{
+  memset(p, 0, sizeof(struct partition));
+}
+
+void delete_partition(int i)
+{
+  int sys_id, looper = 0;
+  struct partition *p, *q, *ext_p, *ext_q;
+  struct part_entry *pe;
+  sector_t new_start;
+  void *ptr = NULL;
+  pe = &partitions[i];
+  p = pe->part;
+  sys_id = p->sys_ind;
+
+  if (!sys_id) printf("Partition %u is empty\n", i+1);
+
+  if (i < 4 && !IS_EXTENDED(sys_id)) {
+    clear_partition(p);
+    pe->modified = 1;
+  } else if (i < 4 && IS_EXTENDED(sys_id)) {
+    clear_partition(p);
+    pe->modified = 1;
+    for (looper = 4; looper < num_parts; looper++) {
+      pe = &partitions[looper];
+      p = pe->part; 
+      if (is_partition_clear(p)) break;
+      else {
+        clear_partition(p);
+        pe->modified = 1;
+        free(pe->sec_buffer);
+      }
+    }
+    extended_offset = 0;
+    num_parts = 4;
+  } else {
+    //only logical is delete, need to move the rest of them backwards
+    if (i == 4) {
+      //move partiton# 6 to 5.
+      partitions[i].modified = 1;
+      if (num_parts > i+1) {
+        q = partitions[i + 1].part;
+        *p = *q; //copy the part table
+        ext_p = part_offset(partitions[i].sec_buffer, 1);
+        ext_q = part_offset(partitions[i + 1].sec_buffer, 1);
+        *ext_p = *ext_q; //copy the extended info pointer
+        /* change the start of the 4th partiton. */
+        new_start = partitions[i + 1].start_offset + swap_le32toh(q->start4) - extended_offset;
+        new_start = htole32(new_start);
+        ptr = (void *)&new_start;
+        memcpy(p->start4, ptr, 4);
+      } else {
+        clear_partition(partitions[i].part);
+        return; //only logical
+      }
+    } else if (i > 4) {
+      ext_p = part_offset(partitions[i-1].sec_buffer, 1);
+      ext_q = part_offset(partitions[i].sec_buffer, 1);
+      memcpy((void*)ext_p, (void *)ext_q, sizeof(struct partition));
+      partitions[i-1].modified = 1;
+    }
+    if (i == 4) looper = i+2;
+    else if (i > 4) looper = i+1;
+    for (; looper < num_parts; looper++) {
+      partitions[looper-1] = partitions[looper];
+    }
+    num_parts--;
+  }
+}
+
+static int ask_partition(int num_parts)
+{
+  char *fmt = "Partition (%u - %u):", str[80];
+  int val;
+  while (1) {
+    do {
+      printf(fmt, 1, num_parts);
+      fgets(str, 80, stdin);
+    } while (!isdigit(*str));
+    val = atoi(str);
+    if (val > 0 && val <= num_parts) return val;
+    else printf("Invalid number entered\n");
+  }
+}
+
+static void toggle_active_flag(int i)
+{
+  struct partition *p = partitions[i].part;
+  if (is_partition_clear(p)) printf("Partition %u is empty\n", i+1);
+  
+  if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+    printf("WARNING: Partition %u is an extended partition\n", i + 1);
+  p->boot_ind = p->boot_ind == 0x80?0 : 0x80;
+  partitions[i].modified = 1;
+}
+
+
+void write_table(void)
+{
+  /* Write changes to disk */
+  int i =0;
+  struct part_entry *pe;
+  sector_t offset;
+
+  for (i = 0; i < 4; i++)
+    if (partitions[i].modified) partitions[3].modified = 1;
+
+  for (i = 3; i < num_parts; i++) {
+    pe = &partitions[i];
+    write_table_flag(pe->sec_buffer);
+    offset = pe->start_offset;
+    if (pe->modified == 1) {
+      xlseek(dev_fd, offset * g_sect_size, SEEK_SET);
+      xwrite(dev_fd, pe->sec_buffer, g_sect_size);
+    }
+  }
+  printf("The partition table has been altered.\n");
+  printf("Calling ioctl() to re-read partition table\n");
+  sync();
+  for (i = 4; i < num_parts; i++)
+    free(partitions[i].sec_buffer);
+  if(ioctl(dev_fd, BLKRRPART, NULL) < 0)
+    perror_exit("WARNING: rereading partition table failed, kernel still uses old table");
+
+}
+
+static int get_non_free_partition(int max)
+{       
+  int num = -1, i = 0;
+
+  for (i = 0; i < max; i++) {
+    struct part_entry *pe = &partitions[i];
+    struct partition *p = pe->part;
+    if (!is_partition_clear(p)) {
+      if (num >= 0) goto get_from_user;
+      num = i;
+    }
+  }
+  if (num >= 0) {
+    printf("Selected partition %d\n",num+1);
+    return num;
+  }     
+  printf("No partition is defined yet!\n");
+  return -1;
+get_from_user:
+  return ask_partition(num_parts)-1;
+}
+
+static int get_free_partition(int max)
+{
+  int num = -1, i = 0;
+
+  for (i = 0; i < max; i++) {
+    struct part_entry *pe = &partitions[i];
+    struct partition *p = pe->part;
+    if (is_partition_clear(p)) {
+      if (num >= 0) goto get_from_user;
+      num = i;
+    }
+  }
+  if (num >= 0) {
+    printf("Selected partition %d\n",num+1);
+    return num;
+  }
+  printf("All primary partitions have been defined already!\n");
+  return -1;
+get_from_user:
+  return ask_partition(4)-1;
+}
+
+static uint32_t ask_value(char *mesg, sector_t left, sector_t right, sector_t defalt)
+{       
+  char buf[80], *str;
+  uint32_t val;
+  int use_default = 1;
+  str = buf;
+  while (1) {
+    use_default = 1;
+    do {
+      printf("%s",mesg);
+      fgets(str, 80, stdin);
+    } while (!isdigit(*str) && (*str != '\n')
+        && (*str != '-') && (*str != '+') && (!isblank(*str)));
+    while (isblank(*str)) str++; //remove leading white spaces
+    if (*str == '+' || *str == '-') {
+      int minus = (*str == '-');
+      int absolute = 0;
+
+      val = atoi(str + 1);
+
+      /* (1) if 2nd char is digit, use_default = 0.
+       * (2) move line_ptr to first non-digit. */
+      while (isdigit(*++str))
+        use_default = 0;
+
+      switch (*str) {
+        case 'c':
+        case 'C':
+          if (!disp_unit_cyl)
+            val *= g_heads * g_sectors;
+          break;
+        case 'K':
+          absolute = 1024;
+          break;
+        case 'k':
+          absolute = 1000;
+          break;
+        case 'm':
+        case 'M':
+          absolute = 1000000;
+          break;
+        case 'g':
+        case 'G':
+          absolute = 1000000000;
+          break;
+        default:
+          break;
+      }
+      if (absolute) {
+        unsigned long long bytes;
+        unsigned long unit;
+
+        bytes = (unsigned long long) val * absolute;
+        unit = (disp_unit_cyl && (g_heads * g_sectors))? g_heads * g_sectors : 1;
+        unit = unit * g_sect_size;
+        bytes += unit/2; /* round */
+        bytes /= unit;
+        val = bytes;
+      }
+      if (minus)
+        val = -val;
+      val += left;
+    } else {
+      val = atoi(str);
+      while (isdigit(*str)) {
+        str++;
+        use_default = 0;
+      }
+    }
+    if(use_default) {
+      val = defalt;
+      printf("Using default value %lld\n", defalt);
+    }
+    if (val >= left && val <= right) return val;
+    else printf("Value out of range\n");
+  }
+}
+
+static int validate(int start_index, sector_t* begin,sector_t* end, sector_t start)
+{
+  int i, valid = 0;
+  for (i = start_index; i < num_parts; i++) {
+    if (start >= begin[i] && start <= end[i]) {
+      printf("Sector %lld is alread allocated\n",start);
+      valid = 0;
+      break;
+    } else valid = 1;
+  }
+  return valid;
+}
+
+static sector_t ask_start_sector(int idx, sector_t* begin, sector_t* end, int ext_idx)
+{
+  sector_t start, limit, offset, temp = 0, start_cyl, limit_cyl;
+  char mesg[256];
+  int i, asked = 0, valid = 0, start_index = 0;
+
+  if (dos_flag) offset = g_sectors;
+  else offset = 1;
+  start = offset;
+  if (disp_unit_cyl) limit = (sector_t)g_sectors * g_heads * g_cylinders - 1;
+  else limit = total_number_sectors - 1;
+
+  if (disp_unit_cyl) /*make the begin of every partition to cylnder boundar */
+    for (i = 0; i < num_parts; i++)
+      begin[i] = (begin[i]/(g_heads* g_sectors)) * (g_heads* g_sectors);
+
+  if (idx >= 4) {
+    if (!begin[ext_idx] && extended_offset) begin[ext_idx] = extended_offset;
+    start = begin[ext_idx] + offset;
+    limit = end[ext_idx];
+    start_index = 4;
+  }
+  do {
+    if (asked) {
+      for (i = start_index; i < num_parts; i++) {
+        if (start >= begin[i] && start <= end[i]) {
+          valid = 0;
+          printf("Sector %lld is alread allocated\n",start);
+          break;
+        } else valid = 1;
+      }
+    }
+    if (valid) break;
+
+find_start_again:
+    for (i = start_index; i < num_parts; i++) {
+      if (start >= begin[i] && start <= end[i]) {
+        start = end[i] + 1 + ((idx >= 4)? offset : 0);
+      }
+    }
+    if (!validate(start_index, begin, end, start)) goto find_start_again;
+    start_cyl = start/(g_sectors * g_heads) + 1;
+    limit_cyl = limit/(g_sectors * g_heads) + 1;
+
+    if (start > limit) break;
+    sprintf(mesg, "First %s (%lld - %lld, default %lld): ", disp_unit_cyl? "cylinder" : "sector",
+        disp_unit_cyl? start_cyl : start, 
+        disp_unit_cyl? limit_cyl : limit,
+        disp_unit_cyl? start_cyl : start);
+    temp = ask_value(mesg, disp_unit_cyl? start_cyl : start, 
+        disp_unit_cyl? limit_cyl : limit, disp_unit_cyl? start_cyl : start);
+    asked = 1;
+
+    if (disp_unit_cyl) {
+      /* point to the cylinder start sector*/
+      temp = (temp-1) * g_heads * g_sectors;
+      if (temp < start) //the boundary is falling in the already used sectors.
+        temp = start;
+    }
+    start = temp;
+  } while (asked && !valid);
+  return start;
+}
+
+static sector_t ask_end_sector(int idx, sector_t* begin, sector_t* end, int ext_idx, sector_t start_sec)
+{
+  sector_t start, limit, temp = 0, start_cyl, limit_cyl;
+  char mesg[256];
+  int i;
+
+  start = start_sec;
+  if (disp_unit_cyl) limit = (sector_t)g_sectors * g_heads * g_cylinders - 1;
+  else limit = total_number_sectors - 1;
+
+  if (disp_unit_cyl) /*make the begin of every partition to cylnder boundar */
+    for (i = 0; i < num_parts; i++)
+      begin[i] = (begin[i]/(g_heads* g_sectors)) * (g_heads* g_sectors);
+
+  if (idx >= 4) limit = end[ext_idx];
+
+  for (i = 0; i < num_parts; i++)
+    if (start < begin[i] && limit >= begin[i]) limit = begin[i] - 1;
+
+  start_cyl = start/(g_sectors * g_heads) + 1;
+  limit_cyl = limit/(g_sectors * g_heads) + 1;
+  if (limit < start) { //the boundary is falling in the already used sectors.
+    printf("No Free sectors available\n");
+    return 0;
+  }
+  sprintf(mesg, "Last %s or +size or +sizeM or +sizeK (%lld - %lld, default %lld): ", disp_unit_cyl? "cylinder" : "sector",
+      disp_unit_cyl? start_cyl : start, 
+      disp_unit_cyl? limit_cyl : limit,
+      disp_unit_cyl? limit_cyl : limit);
+  temp = ask_value(mesg, disp_unit_cyl? start_cyl : start, 
+      disp_unit_cyl? limit_cyl : limit, disp_unit_cyl? limit_cyl : limit);
+
+  if (disp_unit_cyl) { /* point to the cylinder start sector*/
+    temp = temp * g_heads * g_sectors - 1;
+    if (temp > limit) temp = limit;
+  }
+  if (temp < start) { //the boundary is falling in the already used sectors.
+    printf("No Free sectors available\n");
+    return 0;
+  }
+  return temp;
+}
+
+static int add_partition(int idx, int sys_id)
+{
+  int i, ext_idx = -1;
+  struct part_entry *pe;
+  struct partition *p;
+  sector_t start, end, begin_sec[num_parts], end_sec[num_parts];
+
+  pe = &partitions[idx];
+  p = pe->part;
+  if (p && !is_partition_clear(p)) {
+    printf("Partition %u is already defined, delete it to re-add\n", idx+1);
+    return 0;
+  }
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (is_partition_clear(p)) {
+      begin_sec[i] = 0xffffffff;
+      end_sec[i] = 0;
+    } else {
+      begin_sec[i] = swap_le32toh(p->start4) + pe->start_offset;
+      end_sec[i] = begin_sec[i] + swap_le32toh(p->size4) - 1;
+    }
+    if (IS_EXTENDED(p->sys_ind)) ext_idx = i;
+  }
+  start = ask_start_sector(idx, begin_sec, end_sec, ext_idx);
+  end = ask_end_sector(idx, begin_sec, end_sec, ext_idx, start);
+  if (end == 0) return 0;
+  /*Populate partition table entry  - 16 bytes*/
+  pe = &partitions[idx];
+  p = pe->part;
+
+  if (idx > 4) {
+    if (dos_flag) pe->start_offset = start - (sector_t)g_sectors;
+    else pe->start_offset = start - 1;
+    if (pe->start_offset == extended_offset) pe->start_offset++;
+    if (!dos_flag) start++;
+  }
+  set_levalue(p->start4, start - pe->start_offset);
+  set_levalue(p->size4, end - start + 1);
+  set_hsc(p, start, end);
+  p->boot_ind = 0;
+  p->sys_ind = sys_id;
+  pe->modified = 1;
+
+  if (idx > 4) {
+    p = partitions[idx-1].part + 1; //extended pointer for logical partitions
+    set_levalue(p->start4, pe->start_offset - extended_offset);
+    set_levalue(p->size4, end - start + 1 + (dos_flag? g_sectors: 1));
+    set_hsc(p, start, end);
+    p->boot_ind = 0;  
+    p->sys_ind = EXTENDED;
+    partitions[idx-1].modified = 1;   
+  }
+  if (IS_EXTENDED(sys_id)) {
+    pe = &partitions[4];
+    pe->modified = 1; 
+    pe->sec_buffer = xzalloc(g_sect_size);
+    pe->part = part_offset(pe->sec_buffer, 0);
+    pe->start_offset = extended_offset = start;
+    num_parts = 5;
+  }
+  return 1;
+}
+
+static void add_logical_partition(void)
+{
+  struct part_entry *pe;
+  if (num_parts > 5 || !is_partition_clear(partitions[4].part)) {
+    pe = &partitions[num_parts];
+    pe->modified = 1;
+    pe->sec_buffer = xzalloc(g_sect_size);
+    pe->part = part_offset(pe->sec_buffer, 0);
+    pe->start_offset = 0;
+    num_parts ++;
+    if (!add_partition(num_parts - 1, LINUX_NATIVE)) {
+      num_parts--;
+      free(pe->sec_buffer);
+    }
+  } 
+  else add_partition(num_parts -1, LINUX_NATIVE);
+}
+
+static void add_new_partition(void)
+{
+  int choice, idx, i, free_part = 0;
+  char *msg = NULL;
+
+  for (i = 0; i < 4; i++) {      
+    struct part_entry *pe = &partitions[i];                                                                                                                                     
+    struct partition *p = pe->part;
+    if (is_partition_clear(p)) free_part++;
+  }
+
+  if (!free_part && num_parts >= 60) {
+    printf("The maximum number of partitions has been created\n");
+    return;       
+  }
+  if (!free_part) {
+    if (extended_offset) add_logical_partition();
+    else printf("You must delete some partition and add "
+          "an extended partition first\n");
+    return;
+  }
+
+  msg = xmsprintf("  %s\n  p  primary partition(1-4)\n", extended_offset? "l  logical (5 or over)" : "e  extended");
+
+  choice = 0x20 | read_input(msg, NULL);
+  free(msg);
+  if (choice == 'p') {
+    idx = get_free_partition(4);
+    if (idx >= 0) add_partition(idx, LINUX_NATIVE);
+    return;
+  }
+  if (choice =='l' && extended_offset) {
+    add_logical_partition();
+    return;
+  }
+  if (choice == 'e' && !extended_offset) {
+    idx = get_free_partition(4);   
+    if (idx >= 0) add_partition(idx, EXTENDED);
+    return;
+  }
+}
+
+static int get_sysid(void)
+{
+  return (read_hex("Hex code (L to list codes): "));
+}
+
+static void change_systype(void )
+{
+  int i, sys_id;
+  struct partition *p;
+  struct part_entry *pe;
+
+  i = ask_partition(num_parts);
+  pe = &partitions[i-1];
+  p = pe->part;
+  if (is_partition_clear(p)) {
+    printf("Partition %d doesn't exist yet!\n", i);
+    return;
+  }
+  sys_id = get_sysid();
+  if ((IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(sys_id)) ||
+      (!IS_EXTENDED(p->sys_ind) && IS_EXTENDED(sys_id))) {
+    printf("you can't change a  partition to an extended or vice-versa\n");
+    return;
+  }
+
+  printf("Changed system type of partition %u to %0x (%s)\n",i, sys_id, get_type(sys_id));
+  p->sys_ind = sys_id;
+  pe->modified = 1;
+}
+
+static void check(int n, unsigned h, unsigned s, unsigned c, sector_t start)
+{   
+  sector_t total, real_s, real_c;
+
+  real_s = sector(s) - 1;
+  real_c = cylinder(s, c);
+  total = (real_c * g_sectors + real_s) * g_heads + h;
+  if (!total) printf("Partition %u contains sector 0\n", n);
+  if (h >= g_heads)
+    printf("Partition %u: head %u greater than maximum %lu\n", n, h + 1, g_heads);
+  if (real_s >= g_sectors)
+    printf("Partition %u: sector %u greater than maximum %lu\n", n, s, g_sectors);
+  if (real_c >= g_cylinders)
+    printf("Partition %u: cylinder %lld greater than maximum %lu\n", n, real_c + 1, g_cylinders);
+  if (g_cylinders <= ONE_K && start != total)
+    printf("Partition %u: previous sectors %lld disagrees with total %lld\n", n, start, total);
+}
+
+static void verify_table(void)
+{
+  int i, j, ext_idx = -1;
+  sector_t begin_sec[num_parts], end_sec[num_parts], total = 1;
+  struct part_entry *pe;
+  struct partition *p;
+
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (is_partition_clear(p) || IS_EXTENDED(p->sys_ind)) {
+      begin_sec[i] = 0xffffffff;
+      end_sec[i] = 0;
+    } else {
+      begin_sec[i] = swap_le32toh(p->start4) + pe->start_offset;
+      end_sec[i] = begin_sec[i] + swap_le32toh(p->size4) - 1;
+    }
+    if (IS_EXTENDED(p->sys_ind)) ext_idx = i;
+  }
+  //  fill_bounds(first, last);   
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
+      consistency_check(p, i);
+      if ((swap_le32toh(p->start4) + pe->start_offset) < begin_sec[i])
+        printf("Warning: bad start-of-data in partition %u\n", i + 1);
+      check(i + 1, p->end_head, p->end_sector, p->end_cyl, end_sec[i]);
+      total += end_sec[i] + 1 - begin_sec[i];
+      for (j = 0; j < i; j++) {
+        if ((begin_sec[i] >= begin_sec[j] && begin_sec[i] <= end_sec[j])
+            || ((end_sec[i] <= end_sec[j] && end_sec[i] >= begin_sec[j]))) {
+          printf("Warning: partition %u overlaps partition %u\n", j + 1, i + 1);
+          total += begin_sec[i] >= begin_sec[j] ? begin_sec[i] : begin_sec[j];
+          total -= end_sec[i] <= end_sec[j] ? end_sec[i] : end_sec[j];
+        }
+      }
+    }
+  }  
+  if (extended_offset) {
+    struct part_entry *pex = &partitions[ext_idx];
+    sector_t e_last = swap_le32toh(pex->part->start4) +
+      swap_le32toh(pex->part->size4) - 1;
+
+    for (i = 4; i < num_parts; i++) {
+      total++;
+      p = partitions[i].part;
+      if (!p->sys_ind) {
+        if (i != 4 || i + 1 < num_parts)
+          printf("Warning: partition %u is empty\n", i + 1);
+      } else if (begin_sec[i] < extended_offset || end_sec[i] > e_last)
+        printf("Logical partition %u not entirely in partition %u\n", i + 1, ext_idx + 1);
+    }
+  }
+  if (total > g_heads * g_sectors * g_cylinders)
+    printf("Total allocated sectors %lld greater than the maximum "
+        "%lu\n", total, g_heads * g_sectors * g_cylinders);
+  else {
+    total = g_heads * g_sectors * g_cylinders - total;
+    if (total != 0) printf("%lld unallocated sectors\n", total);
+  }
+}
+
+static void move_begning(int idx)
+{
+  struct part_entry *pe;
+  struct partition *p;
+  sector_t start, num, new_start, end;
+  char mesg[256];
+
+  pe = &partitions[idx];
+  p = pe->part;
+
+  start = swap_le32toh(p->start4) + pe->start_offset;
+  num = swap_le32toh(p->size4);
+  end = start + num -1;
+
+  if (!num || IS_EXTENDED(p->sys_ind)) {
+    printf("Partition %u doesn't have data area\n", idx+1);
+    return;
+  }
+  sprintf(mesg, "New begining of data (0 - %lld, default %lld): ", end, start);
+  new_start = ask_value(mesg, 0, end, start);
+  if (new_start != start) {
+    set_levalue(p->start4, new_start - pe->start_offset);
+    set_levalue(p->size4, end - new_start +1);
+    if ((read_input("Recalculate C/H/S (Y/n): ", NULL) | 0x20) == 'y')
+      set_hsc(p, new_start, end);
+    pe->modified = 1;
+  }
+}
+
+static void print_raw_sectors()
+{
+  int i, j;
+  struct part_entry *pe;
+
+  printf("Device: %s\n", disk_device);
+  for (i = 3; i < num_parts; i++) {
+    pe = &partitions[i];
+    for (j = 0; j < g_sect_size; j++) {
+      if (!(j % 16)) printf("\n0x%03X: ",j);
+      printf("%02X ",pe->sec_buffer[j]);
+    }
+    printf("\n");
+  }
+}
+
+static void print_partitions_list(int ext)
+{
+  int i;                                                                                    
+  struct part_entry *pe;
+  struct partition *p;
+
+  printf("Disk %s: %lu heads, %lu sectors, %lu cylinders\n\n", disk_device, g_heads, g_sectors, g_cylinders);
+  printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
+
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    p = pe->part;
+    if (p != NULL) {
+      if (ext && (i >= 4)) p = pe->part + 1;
+      if(ext && i < 4 && !IS_EXTENDED(p->sys_ind)) continue;
+
+      printf("%2u %02x%4u%4u%5u%4u%4u%5u%11u%11u %02x\n",
+          i+1, p->boot_ind, p->head,
+          sector(p->sector), cylinder(p->sector, p->cyl),
+          p->end_head,           
+          sector(p->end_sector), cylinder(p->end_sector, p->end_cyl),
+          swap_le32toh(p->start4),
+          swap_le32toh(p->size4),
+          p->sys_ind);
+      if (p->sys_ind) consistency_check(p, i);
+    }
+  }
+}
+
+static void fix_order(void)
+{
+  sector_t first[num_parts], min;
+  int i, j, oj, ojj, sj, sjj;
+  struct part_entry *pe;
+  struct partition *px, *py, temp, *pj, *pjj, tmp;
+
+  for (i = 0; i < num_parts; i++) {
+    pe = &partitions[i];
+    px = pe->part;
+    if (is_partition_clear(px)) {
+      first[i] = 0xffffffff;
+    } else {
+      first[i] = swap_le32toh(px->start4) + pe->start_offset;
+    }
+  }
+  
+  if (!check_order()) {
+    printf("Ordering is already correct\n\n");
+    return;
+  }
+  for (i = 0; i < 4; i++) {
+    for (j = 0; j < 3; j++) {
+      if (first[j] > first[j+1]) {
+        py = partitions[j+1].part;
+        px = partitions[j].part;
+        memcpy(&temp, py, sizeof(struct partition));
+        memcpy(py, px, sizeof(struct partition));
+        memcpy(px, &temp, sizeof(struct partition));
+        min = first[j+1];
+        first[j+1] = first[j];
+        first[j] = min;
+        partitions[j].modified = 1;
+      }
+    }
+  }
+  for (i = 5; i < num_parts; i++) {
+    for (j = 5; j < num_parts - 1; j++) {
+      oj = partitions[j].start_offset;
+      ojj = partitions[j+1].start_offset;
+      if (oj > ojj) {
+        partitions[j].start_offset = ojj;
+        partitions[j+1].start_offset = oj;
+        pj = partitions[j].part;
+        set_levalue(pj->start4, swap_le32toh(pj->start4)+oj-ojj);
+        pjj = partitions[j+1].part;
+        set_levalue(pjj->start4, swap_le32toh(pjj->start4)+ojj-oj);
+        set_levalue((partitions[j-1].part+1)->start4, ojj-extended_offset);
+        set_levalue((partitions[j].part+1)->start4, oj-extended_offset);
+      }
+    }
+  }
+  for (i = 4; i < num_parts; i++) {
+    for (j = 4; j < num_parts - 1; j++) {
+      pj = partitions[j].part;
+      pjj = partitions[j+1].part;
+      sj = swap_le32toh(pj->start4);
+      sjj = swap_le32toh(pjj->start4);
+      oj = partitions[j].start_offset;
+      ojj = partitions[j+1].start_offset;
+      if (oj+sj > ojj+sjj) {
+        tmp = *pj;
+        *pj = *pjj;
+        *pjj = tmp;
+        set_levalue(pj->start4, ojj+sjj-oj);
+        set_levalue(pjj->start4, oj+sj-ojj);
+      }  
+    }    
+  }
+  /* If anything changed */
+  for (j = 4; j < num_parts; j++) partitions[j].modified = 1;
+  printf("Done!\n");
+}
+
+static void print_menu(void)
+{
+  printf("a\ttoggle a bootable flag\n");
+  printf("b\tedit bsd disklabel\n");
+  printf("c\ttoggle the dos compatibility flag\n");
+  printf("d\tdelete a partition\n");
+  printf("l\tlist known partition types\n");
+  printf("n\tadd a new partition\n");
+  printf("o\tcreate a new empty DOS partition table\n");
+  printf("p\tprint the partition table\n");
+  printf("q\tquit without saving changes\n");
+  printf("s\tcreate a new empty Sun disklabel\n");  /* sun */
+  printf("t\tchange a partition's system id\n");
+  printf("u\tchange display/entry units\n");
+  printf("v\tverify the partition table\n");
+  printf("w\twrite table to disk and exit\n");
+  printf("x\textra functionality (experts only)\n");
+}
+
+static void print_xmenu(void)
+{
+  printf("b\tmove beginning of data in a partition\n");
+  printf("c\tchange number of cylinders\n");
+  printf("d\tprint the raw data in the partition table\n");
+  printf("e\tlist extended partitions\n");
+  printf("f\tfix partition order\n");    
+  printf("h\tchange number of heads\n");
+  printf("p\tprint the partition table\n");
+  printf("q\tquit without saving changes\n");
+  printf("r\treturn to main menu\n");
+  printf("s\tchange number of sectors/track\n");
+  printf("v\tverify the partition table\n");
+  printf("w\twrite table to disk and exit\n");
+}
+
+static void expert_menu(void)
+{
+  int choice, idx;
+  sector_t value;
+  char mesg[256];
+
+  while (1) {
+    xputc('\n');
+    char *msg = "Expert Command ('m' for help): ";
+    choice = 0x20 | read_input(msg, NULL);
+    switch (choice) {
+      case 'b': //move data begining in partition
+        idx = ask_partition(num_parts);
+        move_begning(idx - 1);
+        break;
+      case 'c': //change cylinders
+          sprintf(mesg, "Number of cylinders (1 - 1048576, default %lu): ", g_cylinders);
+          value = ask_value(mesg, 1, 1048576, g_cylinders);
+          g_cylinders = TT.cylinders = value;
+          toys.optflags |= FLAG_C;
+          if(g_cylinders > ONE_K)
+            printf("\nThe number of cylinders for this disk is set to %lu.\n"
+                "There is nothing wrong with that, but this is larger than 1024,\n"
+                "and could in certain setups cause problems.\n", g_cylinders);
+        break;
+      case 'd': //print raw data in part tables
+        print_raw_sectors();
+        break;
+      case 'e': //list extended partitions
+        print_partitions_list(1);
+        break;
+      case 'f': //fix part order
+        fix_order();
+        break;
+      case 'h': //change number of heads
+          sprintf(mesg, "Number of heads (1 - 256, default %lu): ", g_heads);
+          value = ask_value(mesg, 1, 256, g_heads);
+          g_heads = TT.heads = value;
+          toys.optflags |= FLAG_H;
+        break;
+      case 'p': //print partition table
+        print_partitions_list(0);
+        break;
+      case 'q':
+        free_bufs();
+        close(dev_fd);
+        printf("\n");
+        exit(0);
+        break;
+      case 'r':
+        return;
+        /*Not reachable */
+        break;
+      case 's': //change sector/track
+          sprintf(mesg, "Number of sectors (1 - 63, default %lu): ", g_sectors);
+          value = ask_value(mesg, 1, 63, g_sectors);
+          g_sectors = TT.sectors = value;
+          toys.optflags |= FLAG_H;
+        break;
+      case 'v':
+        verify_table();
+        break;
+      case 'w':
+        write_table();
+        toys.exitval = 0;
+        exit(0);
+        /*Not reached */
+        break;
+      case 'm':
+        print_xmenu();
+        break;
+      default:
+        printf("Unknown command '%c'\n",choice);
+        print_xmenu();
+        break;
+    }
+  } //while(1)
+}
+
+static int disk_proper(const char *device)
+{
+  unsigned length;
+  int fd = open(device, O_RDONLY);
+
+  if (fd != -1) {
+    struct hd_geometry dev_geo;
+    dev_geo.heads = 0;
+    dev_geo.sectors = 0;
+    int err = ioctl(fd, HDIO_GETGEO, &dev_geo);
+    close(fd);
+    if (!err) return (dev_geo.start == 0);
+  }
+  length = strlen(device);
+  if (length != 0 && isdigit(device[length - 1])) return 0;
+  return 1;
+}
+
+static void reset_entries()
+{
+  int i;
+
+  memset(MBRbuf, 0, sizeof(MBRbuf));
+  for (i = 4; i < num_parts; i++)
+    memset(&partitions[i], 0, sizeof(struct part_entry));
+}
+
+static void move_fd()
+{
+  int fd = xopen("/dev/null", O_RDONLY);
+  if(fd != dev_fd) {
+    if(dup2(fd, dev_fd) != dev_fd) perror_exit("Can't dup2");
+    close(fd);
+  }
+}
+
+static void read_and_print_parts()
+{
+  unsigned int ma, mi, sz;
+  char name[100], buffer[100], device[120];
+  FILE* fp = xfopen("/proc/partitions", "r");
+
+  while (fgets(buffer, sizeof(buffer), fp)) {
+    reset_entries();
+    num_parts = 4;
+    memset(name, 0, sizeof(name));
+    if (sscanf(buffer, " %u %u %u %[^\n ]", &ma, &mi, &sz, name) != 4)
+      continue;
+      
+    sprintf(device,"/dev/%s",name);
+    if (disk_proper(device)) {
+      if (read_mbr(device, 0)) continue;
+      print_mbr(1);
+      move_fd();
+    }
+  }
+  fclose(fp);
+}
+
+void fdisk_main(void)
+{
+  int choice, p;
+  init_members();
+
+  move_fd();
+  if (TT.heads >= 256) TT.heads = 0;
+  if (TT.sectors >= 64) TT.sectors = 0;
+  if (toys.optflags & FLAG_u) disp_unit_cyl = 0;
+  if (toys.optflags & FLAG_l) {
+    if (toys.optc == 0) read_and_print_parts();
+    else {
+      while(*toys.optargs){
+        if (read_mbr(*toys.optargs, 0)) {
+          toys.optargs++;
+          continue;
+        }
+        print_mbr(1);
+        move_fd();
+        toys.optargs++;
+      }
+    }
+    toys.exitval = 0;
+    return;
+  } else {
+    if (!toys.optc || toys.optc > 1 ) {
+      toys.exitval = toys.exithelp = 1;
+      show_help();
+      return;
+    }
+    if (read_mbr(toys.optargs[0], 1)) return;
+    while (1) {
+      xputc('\n');
+      char *msg = "Command ('m' for help): ";
+      choice = 0x20 | read_input(msg, NULL);
+      switch (choice) {
+        case 'a':
+          p = ask_partition(num_parts);
+          toggle_active_flag(p - 1); //partition table index start from 0.
+          break;
+        case 'b':
+          break;
+        case 'c':
+          dos_flag = !dos_flag;
+          printf("Dos compatible flag is %s\n", dos_flag?"Set" : "Not set");
+          break;
+        case 'd':
+          p = get_non_free_partition(num_parts); //4 was here
+          if(p >= 0) delete_partition(p);
+          break;
+        case 'l':
+          list_types();
+          break;
+        case 'n': //add new partition
+          add_new_partition();
+          break;
+        case 'o':
+          create_empty_doslabel();
+          break;
+        case 'p':
+          print_mbr(0);
+          break;
+        case 'q':
+          free_bufs();
+          close(dev_fd);
+          printf("\n");
+          exit(0);
+          break;
+        case 's':
+          break;
+        case 't':
+          change_systype();
+          break;
+        case 'u':
+          disp_unit_cyl = !disp_unit_cyl;
+          printf("Changing Display/Entry units to %s\n",disp_unit_cyl?"cylinders" : "sectors");
+          break;
+        case 'v':
+          verify_table();
+          break;
+        case 'w':
+          write_table();
+          toys.exitval = 0;
+          return;
+          break;
+        case 'x':
+          expert_menu();
+          break;
+        case 'm':
+          print_menu();
+          break;
+        default:
+          printf("%c: Unknown command\n",choice);
+          break;
+      }
+    } //while(1)
+  }
+}
diff --git a/toys/other/find.c b/toys/other/find.c
new file mode 100644 (file)
index 0000000..fb3be5b
--- /dev/null
@@ -0,0 +1,1054 @@
+/* find.c - search for files in a directory hierarchy and take corresponding address. 
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html 
+
+USE_FIND(NEWTOY(find, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config FIND
+  bool "find"
+  default y
+  help
+    Usage: find [PATH]... [OPTIONS] [ACTIONS]
+
+       Search for files and perform actions on them.
+       First failed action stops processing of current file.
+       Defaults: PATH is current directory, action is '-print'
+
+       -follow             Follow symlinks
+
+       -xdev               Don't descend directories on other filesystems
+
+       -maxdepth N         Descend at most N levels. -maxdepth 0 applies
+                       actions to command line arguments only
+
+       -mindepth N         Don't act on first N levels
+
+       -depth               Act on directory after traversing it
+
+       Actions:
+       ( ACTIONS )         Group actions for -o / -a
+
+       ! ACT               Invert ACT's success/failure
+
+       ACT1 [-a] ACT2      If ACT1 fails, stop, else do ACT2
+
+       ACT1 -o ACT2        If ACT1 succeeds, stop, else do ACT2
+
+       -name PATTERN       Match file name (w/o directory name) to PATTERN
+
+       -iname PATTERN      Case insensitive -name
+
+       -path PATTERN       Match path to PATTERN
+
+       -ipath PATTERN      Case insensitive -path
+
+       -regex PATTERN      Match path to regex PATTERN
+
+       -type X             File type is X (one of: f,d,l,b,c,...)
+
+       -perm MASK          At least one mask bit (+MASK), all bits (-MASK),
+                                   or exactly MASK bits are set in file's mode
+
+       -mtime DAYS         mtime is greater than (+N), less than (-N),
+                                   or exactly N days in the past
+
+       -mmin MINS          mtime is greater than (+N), less than (-N),
+                           or exactly N minutes in the past
+
+       -newer FILE         mtime is more recent than FILE's
+
+       -inum N             File has inode number N
+
+       -user NAME/ID       File is owned by given user
+
+       -group NAME/ID      File is owned by given group
+
+       -size N[bck]        File size is N (c:bytes,k:kbytes,b:512 bytes(def.))
+                                   +/-N: file size is bigger/smaller than N
+
+       -links N            Number of links is greater than (+N), less than (-N),
+                                   or exactly N
+
+       -prune              If current file is directory, don't descend into it
+                                   If none of the following actions is specified, -print is assumed
+
+       -print              Print file name
+
+       -print0             Print file name, NUL terminated
+
+       -exec CMD ARG ;     Run CMD with all instances of {} replaced by
+                                       file name. Fails if CMD exits with nonzero
+
+       -delete             Delete current file/directory. Turns on -depth option
+*/
+
+#define FOR_find
+#include "toys.h"
+#include <fnmatch.h>
+#include <regex.h>
+
+#define LONE_CHAR(s,c)     ((s)[0] == (c) && !(s)[1])
+#ifndef FNM_CASEFOLD    //To use fnmatch() as case insensitive.
+#define FNM_CASEFOLD (1<<4)
+#endif
+GLOBALS(
+  char *cur_path;
+  int is_print;
+  int min_depth;
+  int max_depth;
+  int depth;
+  int mm_depth_flag;
+  int depth_flag;     //fist files and then directory [action modifier]
+  int follo_symlinks; //Default donot follow symlinks.
+  int prune_flag; 
+  int xdev_flag;
+  int dev_count;
+  int invert_flag; // ! [Invert the actions result]
+  char *userpath;
+)
+
+dev_t *xdev_t;      //on which we have to recurse only.
+
+/*Any action has corresponding action_node */
+typedef struct _action_node {
+  int (*action_func)(struct _action_node *, struct dirtree *);
+  struct _action_node *next;
+  int not_flag;
+  char plminus;
+  int iname_flag;
+  int ipath_flag;
+  struct _action_node **sub_expr;
+  union {
+    char *pattern;
+    char **e_argv;
+    int e_argc;
+    regex_t reg_pattern;
+    int mask_type;
+    mode_t mode;
+    time_t time;
+    ino_t inode_num;
+    unsigned int ugid;
+    unsigned size;
+    unsigned long link_nr;
+  }un;
+}ACTION_NODE;
+
+enum node_type { INAME = 0, AND, OR, EXEC, PAREN, PATH, MINDEPTH, MAXDEPTH, DEPTH
+                   ,FOLLOW, PRUNE, PRINT, DELETE, REGEX, XDEV, TYPE, PERM, MTIME, MMIN,
+                    NAME, IPATH, PRINT0, NEWER, INUM, USER, GROUP, SIZE, LINKS, INVERT
+                };
+
+
+struct option_list {
+  char *option_name;
+  enum node_type type;
+};
+
+static struct option_list list[] = {
+  { "-iname", INAME},
+  { "-a", AND},
+  { "-o", OR},
+  {"-exec", EXEC},
+  {"(", PAREN},
+  {"-path", PATH},
+  {"-mindepth", MINDEPTH},
+  {"-maxdepth", MAXDEPTH},
+  {"-depth", DEPTH},
+  {"-follow", FOLLOW},
+  {"-prune", PRUNE},
+  {"-print", PRINT},
+  {"-delete", DELETE},
+  {"-regex", REGEX},
+  {"-xdev",XDEV},
+  {"-type", TYPE},
+  {"-perm", PERM},
+  {"-mtime", MTIME},
+  {"-mmin", MMIN},
+  {"-name", NAME},
+  {"-ipath", IPATH},
+  {"-print0",PRINT0},
+  {"-newer", NEWER},
+  {"-inum", INUM},
+  {"-user", USER},
+  {"-group", GROUP},
+  {"-size", SIZE},
+  {"-links", LINKS},
+  {"!", INVERT},
+  { NULL, 0 },
+};
+
+ACTION_NODE **action_array_ptr; //GLOBAL LIST OF GROUP OF ACTIONS
+
+// Find out if the last character of a string matches with the given one.
+// Don't underrun the buffer if the string length is 0.
+static char *find_last_char(char *str, int c)
+{
+  if (str && *str) {
+    size_t sz = strlen(str) - 1;
+    str += sz;
+    if ( (unsigned char)*str == c) return (char*)str;
+  }
+  return NULL;
+}
+/* Get last path component with no strip.
+ * e.g.
+ * "/"    -> "/"
+ * "abc"    -> "abc"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> ""
+ */
+static char *get_last_path_component_withnostrip(char *path)
+{
+  char *slash = strrchr(path, '/');
+  if (!slash || (slash == path && !slash[1])) return (char*)path;
+  return slash + 1;
+}
+/*
+ * Get last path component with strip.
+ * e.g.
+ * "/"    -> "/"
+ * "abc"    -> "abc"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> "def"
+ */
+static char *get_last_path_component_withstrip(char *path)
+{
+  char *slash = find_last_char(path, '/');
+  if (slash)
+    while (*slash == '/' && slash != path) *slash-- = '\0';
+  return get_last_path_component_withnostrip(path);
+}
+
+static int strtol_range(char *str, int min, int max)
+{
+  char *endptr = NULL;
+  errno = 0;
+  long ret_value = strtol(str, &endptr, 10);
+
+  if(errno) perror_exit("Invalid num %s", str);
+  else if(endptr && (*endptr != '\0' || endptr == str))
+    perror_exit("Not a valid num %s", str);
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else perror_exit("Number %s is not in valid [%d-%d] Range\n", str, min, max);
+}
+static unsigned long strtoul_range(char *str, unsigned long min, unsigned long max)
+{
+  char *endptr = NULL;
+  errno = 0;
+  unsigned long ret_value = strtoul(str, &endptr, 10);
+
+  if(errno) perror_exit("Invalid num %s", str);
+  else if(endptr && (*endptr != '\0' || endptr == str))
+    perror_exit("Not a valid num %s", str);
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else perror_exit("Number %s is not in valid [%d-%d] Range\n", str, min, max);
+}
+/*
+ * replace '{}' in arg to current file name for exec command.
+ */
+static void substitute_name(ACTION_NODE * node, struct dirtree *new)
+{
+  char *src = NULL;
+  char *tail = src;
+  char *dest;
+  int count = 0 ,i = 0;
+  char *needle; //For count computation only.
+  int len = strlen(TT.cur_path); 
+  while(node->un.e_argv[i]) {
+    needle = src = tail = node->un.e_argv[i];
+    count = 0;
+    while((needle = strstr(needle, "{}")) != NULL) {
+      needle += 2;
+      count++;
+    }
+    if(!count) {
+      i++;
+      continue;
+    }
+    char *temp = node->un.e_argv[i];
+    node->un.e_argv[i] = dest = xzalloc((len * count) + (strlen(src) - (2 * count)) + 1); //sizeof of final string
+    while((tail = strstr(src, "{}"))) {
+      memcpy(dest, src, (tail - src));
+      dest += tail - src;
+      strcpy(dest, TT.cur_path);
+      src = tail + 2;
+      dest += len;
+    }
+    strcpy(dest, src);
+    free(temp);
+    i++;
+    count = 0;
+  }
+}
+/*
+ * execute action. Invert retun status with not_flag (!)
+ */
+static int exec_actions(ACTION_NODE **array, struct dirtree *new) 
+{
+  ACTION_NODE *ptr;
+  int i = 0;
+  int ret = 0, not_flg = 0;
+  if(!array || !*array) return 1;
+
+  while(array[i]) {
+      ptr = array[i];
+    while(1) {
+      not_flg = ptr->not_flag;
+      ret = ptr->action_func(ptr, new);
+      if(not_flg) {   //Invert the result for ! .
+        if(ret) ret = 0;
+        else ret = 1;
+      }
+      if(!ret) break;  //if we fail, try next action in next action group
+      else ptr = ptr->next; //Success, move to next action group [act1 -a act2]
+      if(!ptr) return ret; //no more actions, return status.
+    }
+    i++;  //next action group [act1 -o act2]
+  }
+  return ret; 
+}
+/***********************************************************/
+         /***Action Function Pionters****/
+
+static int f_iname(ACTION_NODE * node, struct dirtree *new)
+{
+  char *path_component = get_last_path_component_withstrip(new->name);
+  return(!fnmatch(node->un.pattern, path_component, node->iname_flag ? FNM_CASEFOLD : 0 ));
+}
+/*
+ * exec for each argument in find search path.
+ */
+static int f_exec(ACTION_NODE * node, struct dirtree *new)
+{
+  int status;
+  pid_t pid = fork();
+  if(pid == 0) {
+    if(S_ISDIR(new->st.st_mode)) {
+      if(TT.depth_flag) {
+        if(new->data == -1) {
+          substitute_name(node, new);
+          xexec(node->un.e_argv);
+          return -1; //NOT REACHABLE
+        }
+        else exit(0);
+      }
+      else if(new->data == 0){
+        substitute_name(node, new);
+        xexec(node->un.e_argv);
+        return -1; //NOT REACHABLE
+      }
+      else exit(0);
+    }
+    else {
+      substitute_name(node, new);
+      xexec(node->un.e_argv);
+      return -1; //NOT REACHABLE
+    }
+  }
+  else if(pid > 0){ 
+    waitpid(pid, &status, 0);
+    return (WEXITSTATUS(status) == 0);
+  }
+  else perror_exit("Fork failed");
+}
+/*
+ * called for each parenthesis [ \( \) ], occured on command line.
+ */
+static int f_paren(ACTION_NODE *node, struct dirtree *new)
+{
+  return exec_actions(node->sub_expr, new);
+}
+
+static int dotdot(char *name)                                                                                                                                                                     
+{
+    if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1;
+     
+      return 0;
+}
+
+static int f_path(ACTION_NODE *node, struct dirtree *new)
+{
+  char *path;
+  int ret;
+  path = TT.cur_path;
+  if((strlen(path) - strlen(TT.userpath)) == 1) {
+    if(dotdot(TT.userpath))
+      path = TT.userpath;
+  }
+  ret = !fnmatch(node->un.pattern, path, node->ipath_flag ? FNM_CASEFOLD : 0);
+  return ret; 
+}
+static int f_prune(ACTION_NODE *node, struct dirtree *new)
+{
+  TT.prune_flag = 1; 
+  return 1; 
+}
+/*
+ * print action Taking care of depth flag
+ */
+static int f_print(ACTION_NODE *node, struct dirtree *new)
+{
+  if(S_ISDIR(new->st.st_mode)) {
+    if(TT.depth_flag) {
+      if(new->data == -1) 
+        puts(TT.cur_path);
+      return 1;
+    }
+    else {
+      if(new->data == 0) 
+        puts(TT.cur_path);
+      return 1;
+    }
+  }
+  else puts(TT.cur_path);
+  return 1;
+}
+static int f_print0(ACTION_NODE *node, struct dirtree *new)
+{
+  if(S_ISDIR(new->st.st_mode)) {
+    if(TT.depth_flag) {
+      if(new->data == -1) 
+        xprintf("%s%c", TT.cur_path, '\0');
+      return 1;
+    }
+    else {
+      if(new->data == 0) 
+        xprintf("%s%c", TT.cur_path, '\0');
+      return 1;
+    }
+  }
+  else xprintf("%s%c", TT.cur_path, '\0');
+  return 1;
+}
+/*
+ * Delete action, taking care of depth flag as delete action
+ * turn on the depth flag. (-delete action - BE CAREFUL)
+ */
+static int f_delete(ACTION_NODE *node, struct dirtree *new)
+{
+ if(!new->data && S_ISDIR(new->st.st_mode)) return 1; //we will delete it in DIRTREE_COMEAGAIN
+ int ret;
+ if(S_ISDIR(new->st.st_mode))
+   ret = rmdir(TT.cur_path);
+ else
+   ret = unlink(TT.cur_path);
+ if(ret < 0) perror_msg("%s", new->name);
+ return 1; //Always successful.
+}
+/*
+ * execute the compiled regex pattern
+ */
+static int f_regex(ACTION_NODE *node, struct dirtree *new)
+{
+  regmatch_t match;
+  char *path = TT.cur_path;
+  if (regexec(&node->un.reg_pattern, path, 1, &match, 0)) return 0; 
+  if (match.rm_so) return 0; 
+  if (path[match.rm_eo]) return 0;
+  return 1;
+}
+static int f_type(ACTION_NODE *node, struct dirtree *new)
+{
+ return((new->st.st_mode & S_IFMT) == node->un.mask_type);
+}
+static int f_perm(ACTION_NODE *node, struct dirtree *new)
+{
+  if(node->plminus == '+') return (new->st.st_mode & node->un.mode) != 0;
+  if(node->plminus == '-') return (new->st.st_mode & node->un.mode) == node->un.mode;
+  return (new->st.st_mode & 07777) == node->un.mode;
+}
+static int f_mtime(ACTION_NODE *node, struct dirtree *new)
+{
+  time_t file_age = time(NULL) - new->st.st_mtime;
+  time_t time_sec = node->un.time*24*60*60;
+  if(node->plminus == '+') return (file_age >= (time_sec + 24*60*60));
+  if(node->plminus == '-') return file_age < time_sec;
+  return file_age >= time_sec && file_age < (time_sec + 24*60*60);
+}
+static int f_mmin(ACTION_NODE *node, struct dirtree *new)
+{
+  time_t file_age = time(NULL) - new->st.st_mtime;
+  time_t time_sec = node->un.time*60;
+  if(node->plminus == '+') return (file_age >= (time_sec + 60));
+  if(node->plminus == '-') return file_age < time_sec;
+  return file_age >= time_sec && file_age < (time_sec + 60);
+}
+static int f_newer(ACTION_NODE *node, struct dirtree *new)
+{
+  return (node->un.time < new->st.st_mtime);
+}
+static int f_inum(ACTION_NODE *node, struct dirtree *new)
+{
+  return (new->st.st_ino == node->un.inode_num);
+}
+static int f_user(ACTION_NODE *node, struct dirtree *new)
+{
+  return (new->st.st_uid == node->un.ugid);
+}
+static int f_group(ACTION_NODE *node, struct dirtree *new)
+{
+  return (new->st.st_gid == node->un.ugid);
+}
+static int f_size(ACTION_NODE *node, struct dirtree *new)
+{
+  if(node->plminus == '+') return new->st.st_size > node->un.size;
+  if(node->plminus == '-') return new->st.st_size < node->un.size;
+  return new->st.st_size == node->un.size;
+}
+static int f_links(ACTION_NODE *node, struct dirtree *new)
+{   
+    if(node->plminus == '-') return (new->st.st_nlink <  node->un.link_nr);
+    if(node->plminus == '+')return (new->st.st_nlink >  node->un.link_nr);
+    return (new->st.st_nlink == node->un.link_nr);
+}  
+
+/******** Helper Functions *******************************/
+/*
+ * Return the corresponding action node option
+ * else error.
+ */
+enum node_type find_option(char *option) {
+  struct option_list *ptr;
+  ptr = list;
+  while(ptr->option_name) {
+    if(strcmp(ptr->option_name, option) == 0) return ptr->type;
+    else ptr++;
+  }
+  perror_exit("Unrecognised option %s", option);
+}
+/*
+ * alloacte a action node. Ecah action action has 
+ * one action node, correpondingly.
+ */
+ACTION_NODE *allocate_node(ACTION_NODE *node) {
+  if(!node) {                                  
+    node = xzalloc(sizeof(ACTION_NODE));
+    node->next = NULL;
+  }
+  else {                                            
+    node->next = xzalloc(sizeof(ACTION_NODE));
+    node = node->next;
+    node->next = NULL;
+  }
+  node->not_flag = TT.invert_flag;  //Handle '!' for each action.
+  TT.invert_flag = 0;
+  return node;
+}
+unsigned find_multiplier(char *size_str)
+{
+  char *p ;
+  unsigned mul;
+  p = size_str;
+  p += (strlen(size_str) - 1);
+  switch(*p) {
+    case 'c':
+      mul = 1;
+      *p = '\0';
+      break;
+    case 'w':
+      mul = 2;
+      *p = '\0';
+      break;
+    case 'b':
+      mul = 512;
+      *p = '\0';
+      break;
+    case 'k':
+      mul = 1024;
+      *p = '\0';
+      break;
+    default:
+      mul = 512;
+      break;
+  }
+  mul *= strtoul_range(size_str, 0, ULONG_MAX);
+  return mul;
+}
+/*
+ * Make dirtree to understand . as ./
+ * and .. as ../ .
+ */
+static char *make_pathproper(char *str)
+{
+  char *path = str;
+  switch(strlen(str)) {
+    case 1:
+      if(str[0] == '.') path = xstrdup("./");
+      break;
+    case 2:
+      if(str[0] == '.' && str[1] == '.') path = xstrdup("../");
+      break;
+    default:
+      break;
+  }
+  return path;
+}
+/*
+ * Return mask for given argument for
+ * type action.
+ */
+static int get_mask(char *type)
+{
+  switch(*type) {
+  case 'b':
+     return S_IFBLK;
+  case 'c':
+     return S_IFCHR;
+  case 'd':
+     return S_IFDIR;
+  case 'p':
+     return S_IFIFO;
+  case 'f':
+     return S_IFREG;
+  case 'l':
+     return S_IFLNK;
+  case 's':
+     return S_IFSOCK;
+  default:
+     perror_exit("Invalid -type argument");
+   }
+}
+/*
+ * Parse args and form list of action node accordingly.
+ * This function is Heart of the Implemntaion.
+ */
+static ACTION_NODE** parse_args(char ** argv)
+{
+  ACTION_NODE *action_ptr = NULL;
+  ACTION_NODE **action_array;
+  int current_group = 1;
+  char *endptr = NULL; //For xstrtoul only
+  TT.invert_flag = 0;
+  action_array = xzalloc(2 * sizeof(ACTION_NODE *));
+  while(*argv) {
+    enum node_type option_no = find_option(*argv);
+    switch(option_no) {                          //we are here only if we have valid option.
+      case INAME:
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->iname_flag = 1;
+        action_ptr->un.pattern = (*argv);
+        action_ptr->action_func = f_iname;
+        break;
+      case AND:
+        break;
+      case OR: // "-o" start new group of actions.
+        TT.invert_flag = 0;
+        action_array = xrealloc(action_array, (current_group + 2) * sizeof(ACTION_NODE *));
+        action_array[current_group + 1] = NULL;
+        current_group++;
+        action_ptr = NULL; // new group with new actions
+        break;
+      case EXEC:
+        TT.is_print = 0;
+        argv++; //after -exec --args till--; or +
+        int i = 0, j = 0;
+        char **tmp = argv;
+        if(!*argv) perror_exit(" -exec need argument[s]"); //exit before any allocation.
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_exec;
+        while(1) {
+          if(!*argv) perror_exit("No terminating ; or +");
+          if(((argv[0][0] == ';' || argv[0][0] == '+') && argv[0][1] == '\0')) break;
+          argv++;
+          i++;
+        }
+        j = i;
+        if(i == 0) perror_exit("-exec need argument[s]");
+        action_ptr->un.e_argv = xzalloc((i + 1) * sizeof(char*)); 
+        int p = 0;
+        while(i) {
+          action_ptr->un.e_argv[p++] = xstrdup(*tmp);
+          i--;
+          tmp++;
+        }
+        action_ptr->un.e_argv[j] = NULL; //Overwrite ';' or '+' by NULL
+        break;
+      case PAREN:
+        {
+          char **argvp = argv;
+          int nest = 1;
+          while(1) {
+            if(!*++argvp) perror_exit("Umatched '(' ");
+            if(LONE_CHAR(*argvp, '(')) {
+              nest++;
+            }
+            else if(LONE_CHAR(*argvp, ')') && !--nest) {
+              *argvp = NULL;
+              break;
+            }
+          }
+          if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+          else action_ptr = allocate_node(action_ptr);
+          current_group = 1; //we are in sub_expr so we are at first action group
+          action_ptr->sub_expr = parse_args(argv + 1);
+          *argvp = (char*) ")"; /* restore NULLed parameter */                                                                                                                                    
+          argv = argvp; 
+          action_ptr->action_func = f_paren;
+          break;
+        }
+      case PATH:
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->ipath_flag = 0;
+        action_ptr->un.pattern = (*argv);
+        action_ptr->action_func = f_path;
+        break;
+      case MINDEPTH:
+        TT.mm_depth_flag = 1;
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        TT.min_depth = strtol_range(*argv, 0, LONG_MAX); 
+        break;
+      case MAXDEPTH:
+        TT.mm_depth_flag = 1;
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        TT.max_depth = strtol_range(*argv, 0, LONG_MAX);
+        break;
+      case DEPTH:
+        TT.depth_flag = 1;
+        TT.prune_flag = 0;
+        break;
+      case FOLLOW:
+        TT.follo_symlinks = 1;
+        break;
+      case PRUNE:
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_prune;
+        break;
+      case PRINT:
+        TT.is_print = 0;
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_print;
+        break;
+      case DELETE: 
+        TT.is_print = 0; //don't try to print which you have deleted, Obviously.
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        TT.depth_flag = 1; // -delete set depth.
+        action_ptr->action_func = f_delete;
+        break;
+      case REGEX:
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        if(regcomp(&action_ptr->un.reg_pattern, *argv, 0) != 0)
+          perror_msg("Bad Regex: %s", *argv);
+        action_ptr->action_func = f_regex;
+        break;
+      case XDEV:
+        TT.xdev_flag = 1;
+        break;
+      case TYPE:
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        if(strlen(*argv) > 1) perror_exit("Invalid arg '%s' to -type", *argv);
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->un.mask_type = get_mask(*argv);
+        action_ptr->action_func = f_type;
+        break;
+      case PERM:
+        argv++;
+        char *pp = NULL;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        pp = *argv;
+        if(*pp == '+' || *pp == '-') {
+          action_ptr->plminus = *pp;
+          pp++;
+        }
+        if(!m_parse(pp, &action_ptr->un.mode))
+          error_exit("Bad mode '%s'", *argv);
+        action_ptr->action_func = f_perm;
+        break;
+      case MTIME:
+        argv++;
+        endptr = NULL;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        pp = *argv;
+        if(*pp == '+' || *pp == '-') {
+          action_ptr->plminus = *pp;
+          pp++;
+        }
+        action_ptr->un.time = (time_t)strtol_range(pp, 0, LONG_MAX);
+        action_ptr->action_func = f_mtime;
+        break;
+        case MMIN:
+        argv++;
+        endptr = NULL;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        pp = *argv;
+        if(*pp == '+' || *pp == '-') {
+          action_ptr->plminus = *pp;
+          pp++;
+        }
+        action_ptr->un.time = (time_t)strtol_range(pp, 0, LONG_MAX);
+        action_ptr->action_func = f_mmin;
+        break;
+        case NAME:
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->iname_flag = 0;
+        action_ptr->un.pattern = (*argv);
+        action_ptr->action_func = f_iname;
+        break;
+        case IPATH:
+        argv++;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->ipath_flag = 1;
+        action_ptr->un.pattern = (*argv);
+        action_ptr->action_func = f_path;
+        break;
+        case PRINT0:
+        TT.is_print = 0;
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_print0;
+        break;
+        case NEWER:
+        argv++; 
+        struct stat st;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_newer;
+        if(stat(*argv, &st)) perror_exit("%s", *argv);
+        action_ptr->un.time = st.st_mtime;
+        break;
+        case INUM:
+        argv++;
+        errno = 0;
+        endptr = NULL;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_inum;
+        if( *argv[0] == '-' || *argv[0] == '+') error_exit("Invalid num %s",*argv);
+        action_ptr->un.inode_num = strtoul_range(*argv, 0, ULONG_MAX); 
+        break;
+        case USER:
+        argv++; 
+        errno = 0;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_user;
+        action_ptr->un.ugid = xstrtoul(*argv, &endptr, 10);
+        if(errno || *endptr != '\0' || *argv[0] == '-' || *argv[0] == '+') {
+        struct passwd *user;
+        user = getpwnam(*argv);
+        if(!user) error_exit("Unknown user '%s'", *argv);
+        action_ptr->un.ugid = user->pw_uid;
+        }
+        break;
+        case GROUP:
+        argv++; 
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        action_ptr->action_func = f_group;
+        action_ptr->un.ugid = xstrtoul(*argv, &endptr, 10);
+        if(errno || *endptr != '\0' || *argv[0] == '-' || *argv[0] == '+') {
+        struct group *group;
+        group = getgrnam(*argv);
+        if(!group) error_exit("Unknown group '%s'", *argv);
+        action_ptr->un.ugid = group->gr_gid;
+        }
+        break;
+        case SIZE:
+        argv++;
+        endptr = NULL;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        pp = *argv;
+        if(*pp == '+' || *pp == '-') {
+          action_ptr->plminus = *pp;
+          pp++;
+        }
+        if(*pp == '+' || *pp == '-') perror_exit("Invalid arg '%s'", *argv);
+        action_ptr->un.size = find_multiplier(pp);
+        action_ptr->action_func = f_size;
+        break;
+        case LINKS:
+        argv++;
+        endptr = NULL;
+        if(!*argv) perror_exit("need argument[s]");
+        if(!action_ptr) action_ptr = action_array[current_group - 1] = allocate_node(action_ptr);
+        else action_ptr = allocate_node(action_ptr);
+        pp = *argv;
+        if(*pp == '+' || *pp == '-') {
+          action_ptr->plminus = *pp;
+          pp++;
+        }
+        action_ptr->action_func = f_links;
+        action_ptr->un.link_nr = strtoul_range(pp, 0, ULONG_MAX); 
+        break;
+        case INVERT:
+        TT.invert_flag ^= 1;
+        break;
+        default:
+        // NOT REACHABLE as we have already exited for invalid option(case match)
+        break;
+    }
+    argv++; //Next action in command line args.
+  }
+  return action_array;
+}
+
+
+/*
+ * Dirtree callback function. Take actions and print files and dirs
+ * corresponding to maxdepth, mindepth and depth on commandline.
+ */
+static int do_find(struct dirtree *new)
+{
+  int ret = 0;
+  if(dirtree_notdotdot(new) == 0) return 0;
+
+  if(TT.cur_path) {
+    free(TT.cur_path);
+    TT.cur_path = NULL;
+  }
+  TT.cur_path = dirtree_path(new, NULL);
+
+  if(TT.xdev_flag && S_ISDIR(new->st.st_mode)) {
+    int i = 0, j, found = 0;
+    j = TT.dev_count;
+    while(j--) {
+      if(xdev_t[i++] == new->st.st_dev) {
+        found = 1;
+        break;                                                                                                                                                                                       
+      } else found = 0;
+    }
+    if(!found) return 0;
+  }
+
+  if(TT.mm_depth_flag) {
+    if(TT.depth < TT.min_depth || (TT.depth == TT.min_depth && new->data == -1)) goto ret_t1;
+  }
+
+  ret = exec_actions(action_array_ptr, new); //Take Actions.
+  
+
+  if(ret && TT.is_print) {
+    if(TT.depth_flag && S_ISDIR(new->st.st_mode)) { //Process directories "after" traversing them.
+      if(new->data == -1) 
+       puts(TT.cur_path); 
+       goto ret_t1;
+    }
+    if(S_ISDIR(new->st.st_mode)) {
+      if(new->data == 0) puts(TT.cur_path); //Process directories and then stuff in it.
+      else goto ret_t1;
+    }
+    else puts(TT.cur_path); 
+  }
+
+  if(TT.prune_flag) { //we have ancountered -prune for this action group.
+    TT.prune_flag = 0;
+    return 0; //Dont desend into this DIR
+  }
+
+ret_t1:
+
+  if(S_ISDIR(new->st.st_mode)) {
+    if(new->data == -1) TT.depth--;
+    else TT.depth++;
+  }
+  if(TT.mm_depth_flag) {
+    if(TT.depth  > TT.max_depth) {
+      if(TT.depth_flag && S_ISDIR(new->st.st_mode) && new->data == 0) {
+        puts(TT.cur_path);
+      }
+      TT.depth--;
+      return 0;
+    }
+  } 
+  return (DIRTREE_COMEAGAIN |(TT.follo_symlinks ? DIRTREE_SYMFOLLOW : 0));
+}
+/*
+ * Find main function.
+ */
+static void free_action_nodes(void)
+{
+  ACTION_NODE *ptr, *temp;
+  int i = 0;
+  while(action_array_ptr[i]) {
+    temp = ptr = action_array_ptr[i];
+    while(ptr) {
+      temp = ptr->next;
+      free(ptr);
+      ptr = temp;
+    }
+    i++;
+  }
+}
+void find_main(void)
+{
+  char **paths, **p;
+  char **argv;
+  int i = 0;
+  argv = toys.argv;
+  argv++;
+  TT.is_print = 1;
+  TT.max_depth = INT_MAX;
+  TT.cur_path = NULL;
+  p = paths = xmalloc((toys.optc + 1) * sizeof(char *)); //Store paths here .
+  while(*argv != NULL) {
+    if(argv[0][0] == '-') break;
+    if((argv[0][0] == '!' || argv[0][0] == '(') && argv[0][1] == '\0') break;
+    *p++ = *argv++;
+    TT.dev_count++;
+  }
+  if(p == paths) {
+    p[0] = (char*)"./";
+    p++;
+    TT.dev_count++;
+  }
+  *p = NULL;
+  action_array_ptr = parse_args(argv);
+  if(TT.xdev_flag) {
+    struct stat st_buf;
+    xdev_t = xzalloc(TT.dev_count * sizeof(xdev_t[0]));
+    for(i = 0; paths[i]; i++) {
+      if(stat(paths[i], &st_buf) == 0) {
+        xdev_t[i] = st_buf.st_dev;
+      }
+    }
+  }
+  i = 0;
+  while(paths[i]) {
+    TT.userpath = paths[i];
+    char *path = make_pathproper(paths[i]);
+    dirtree_read(path, do_find);
+    i++;
+  }
+  if(CFG_TOYBOX_FREE){
+    if(TT.cur_path) free(TT.cur_path);
+    free(paths);
+    free_action_nodes();
+    free(action_array_ptr);
+  }
+}
diff --git a/toys/other/free.c b/toys/other/free.c
new file mode 100644 (file)
index 0000000..3281d79
--- /dev/null
@@ -0,0 +1,57 @@
+/* free.c - Display amount of free and used memory in the system.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_FREE(NEWTOY(free, "gmkb", TOYFLAG_USR|TOYFLAG_BIN))
+
+config FREE
+  bool "free"
+  default y
+  help
+    usage: free [-bkmg]
+
+    Display the total, free and used amount of physical memory and
+    swap space.
+
+    -bkmg      Output in bytes (default), KB, MB or GB
+*/
+
+#define FOR_free
+#include "toys.h"
+
+static unsigned long long convert(unsigned long d, unsigned int iscale,
+        unsigned int oscale)
+{
+  return ((unsigned long long)d*iscale)>>oscale;
+}
+
+void free_main(void)
+{
+  struct sysinfo info;
+  unsigned int iscale = 1;
+  unsigned int oscale = 0;
+
+  sysinfo(&info);
+  if (info.mem_unit) iscale = info.mem_unit;
+  if (toys.optflags & FLAG_b) oscale = 0;
+  if (toys.optflags & FLAG_k) oscale = 10;
+  if (toys.optflags & FLAG_m) oscale = 20;
+  if (toys.optflags & FLAG_g) oscale = 30;
+
+  xprintf("\t\ttotal        used        free      shared     buffers\n");
+  xprintf("Mem:%17llu%12llu%12llu%12llu%12llu\n",
+    convert(info.totalram, iscale, oscale),
+    convert(info.totalram-info.freeram, iscale, oscale),
+    convert(info.freeram, iscale, oscale),
+    convert(info.sharedram, iscale, oscale),
+    convert(info.bufferram, iscale, oscale));
+
+  xprintf("-/+ buffers/cache:%15llu%12llu\n",
+    convert(info.totalram - info.freeram - info.bufferram, iscale, oscale),
+    convert(info.freeram + info.bufferram, iscale, oscale));
+
+  xprintf("Swap:%16llu%12llu%12llu\n",
+    convert(info.totalswap, iscale, oscale),
+    convert(info.totalswap - info.freeswap, iscale, oscale),
+    convert(info.freeswap, iscale, oscale));
+}
diff --git a/toys/other/fsck.c b/toys/other/fsck.c
new file mode 100644 (file)
index 0000000..3e1ee9b
--- /dev/null
@@ -0,0 +1,727 @@
+/* fsck.c - fsck program to check and repair filesystems.
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+USE_FSCK(NEWTOY(fsck, "?t:ANPRTV", TOYFLAG_SBIN))
+
+config FSCK
+  bool "fsck"
+  default y
+  help
+    usage: fsck [-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]...
+
+    -A  Walk /etc/fstab and check all filesystems
+    -N  Don't execute, just show what would be done
+    -P  With -A, check filesystems in parallel
+    -R  With -A, skip the root filesystem
+    -T  Don't show title on startup
+    -V  Verbose
+    -C n  Write status information to specified filedescriptor
+    -t TYPE  List of filesystem types to check
+*/
+
+#define FOR_fsck
+#include "toys.h"
+
+GLOBALS(
+  char *type;
+  long prog_fd;
+)
+
+/*  $NetBSD: fsck.c,v 1.44 2006/10/16 02:44:46 christos Exp $  */
+
+/*
+ * Copyright (c) 1996 Christos Zoulas. All rights reserved.
+ * Copyright (c) 1980, 1989, 1993, 1994
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)mount.c  8.19 (Berkeley) 4/19/94
+ * From: NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp 
+ *
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#define FSTYPENAMES
+#define FSCKNAMES
+
+#include <fstab.h>
+#include <paths.h>
+#include <signal.h>
+#include <mntent.h>
+
+#ifndef MFSNAMELEN /* NetBSD > 4.0 */
+# define MFSNAMELEN 16 /* length of fs type name, including nul */
+#endif
+
+#define  BADTYPE(mnt)              \
+  ((hasmntopt(mnt, FSTAB_RO) == NULL) &&          \
+    (hasmntopt(mnt, FSTAB_RW) == NULL) && \
+    (hasmntopt(mnt, FSTAB_RQ) == NULL) && \
+    strcmp(mnt->mnt_opts, "defaults"))
+
+#define CHECK_PREEN    256
+#define CHECK_VERBOSE   512
+#define CHECK_DEBUG     1024
+#define CHECK_FORCE     2048
+#define CHECK_PROGRESS  4096
+#define CHECK_NOFIX     8192
+#define _PATH_SBIN  "/sbin"
+#define _PATH_USRSBIN   "/usr/sbin"
+
+static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
+
+TAILQ_HEAD(fstypelist, entry) opthead, selhead;
+
+struct entry {
+  char *type;
+  char *options;
+  TAILQ_ENTRY(entry) entries;
+};
+
+struct partentry {
+  TAILQ_ENTRY(partentry)   p_entries;
+  char        *p_devname;  /* device name */
+  char      *p_mntpt;  /* mount point */
+  char        *p_type;  /* file system type */
+  void      *p_auxarg;  /* auxiliary argument */
+};
+
+TAILQ_HEAD(part, partentry) badh;
+
+struct diskentry {
+  TAILQ_ENTRY(diskentry)     d_entries;
+  char         *d_name;  /* disk base name */
+  TAILQ_HEAD(prt, partentry)  d_part;  /* list of partitions on disk */
+  int        d_pid;  /* 0 or pid of fsck proc */
+};
+
+TAILQ_HEAD(diskinfo, diskentry) diskh;
+
+static int maxrun = 0;
+static char *options = NULL;
+static int flags = 0;
+static int hot = 0;
+static int nrun = 0, ndisks = 0;
+
+static int selected(const char *type)
+{
+  struct entry *e;
+  TAILQ_FOREACH(e, &selhead, entries)   /* If no type specified, it's always selected. */
+  if (!strncmp(e->type, type, MFSNAMELEN)) return which == IN_LIST ? 1 : 0;
+  return which == IN_LIST ? 0 : 1;
+}
+
+static void *isok(struct mntent *me)
+{
+  if (!(me->mnt_passno)) return NULL;
+  if (BADTYPE(me)) return NULL;
+  if (!selected(me->mnt_type)) return NULL;
+  return me;
+}
+
+static const char *getoptions(const char *type)
+{
+  struct entry *e;
+  TAILQ_FOREACH(e, &opthead, entries)
+  if (!strncmp(e->type, type, MFSNAMELEN)) return e->options;
+  return "";
+}
+
+static void catopt(char **sp, const char *o)
+{
+  char *s;
+  size_t i, j;
+
+  s = *sp;
+  if (s) {
+    i = strlen(s);
+    j = i + 1 + strlen(o) + 1;
+    s = xrealloc(s, j);
+    (void)snprintf(s + i, j, ",%s", o);
+  } else s = strdup(o);
+  *sp = s;
+}
+
+static void mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
+{
+  char *p, *s;
+  int argc, maxargc;
+  const char **argv;
+
+  argc = *argcp;
+  argv = *argvp;
+  maxargc = *maxargcp;
+  for (s = opts; (p = strsep(&s, ","));) {
+    /* Always leave space for one more argument and the NULL. */
+    if (argc >= maxargc - 3) {
+      maxargc <<= 1;
+      argv = xrealloc(argv, maxargc * sizeof(char *));
+    }
+    if (*p != '\0')  {
+      if (*p == '-') {
+        argv[argc++] = p;
+        p = strchr(p, '=');
+        if (p) {
+          *p = '\0';
+          argv[argc++] = p+1;
+        }
+      } else argv[argc++] = p;
+    }
+  }
+  *argcp = argc;
+  *argvp = argv;
+  *maxargcp = maxargc;
+}
+
+
+static struct diskentry *finddisk(const char *name)
+{
+  const char *p;
+  size_t len, dlen;
+  struct diskentry *d;
+  for (dlen = len = strlen(name), p = name + len - 1; p >= name; --p)
+    if (isdigit((unsigned char)*p)) {
+      len = p - name + 1;
+      break;
+    }
+  if (p < name) len = dlen;
+
+  TAILQ_FOREACH(d, &diskh, d_entries)
+    if (!strncmp(d->d_name, name, len) && d->d_name[len] == 0)
+      return d;
+
+  d = xmalloc(sizeof(*d));
+  d->d_name = strdup(name);
+  d->d_name[len] = '\0';
+  TAILQ_INIT(&d->d_part);
+  d->d_pid = 0;
+
+  TAILQ_INSERT_TAIL(&diskh, d, d_entries);
+  ndisks++;
+
+  return d;
+}
+
+static void printpart(void)
+{
+  struct diskentry *d;
+  struct partentry *p;
+
+  TAILQ_FOREACH(d, &diskh, d_entries) {
+    (void) printf("disk %s:", d->d_name);
+    TAILQ_FOREACH(p, &d->d_part, p_entries)
+      (void) printf(" %s", p->p_devname);
+    (void) printf("\n");
+  }
+}
+
+static void addpart(const char *type, const char *dev, const char *mntpt, void *auxarg)
+{
+  struct diskentry *d = finddisk(dev);
+  struct partentry *p;
+
+  TAILQ_FOREACH(p, &d->d_part, p_entries)
+    if (!strcmp(p->p_devname, dev)) {
+      error_msg("%s in fstab more than once!", dev);
+      return;
+    }
+
+  p = xmalloc(sizeof(*p));
+  p->p_devname = strdup(dev);
+  p->p_mntpt = strdup(mntpt);
+  p->p_type = strdup(type);
+  p->p_auxarg = auxarg;
+
+  TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
+}
+
+static int startdisk(struct diskentry *d,
+  int (*checkit)(const char *, const char *, const char *, void *, pid_t *))
+{
+  struct partentry *p = TAILQ_FIRST(&d->d_part);
+  int rv;
+
+  while ((rv = (*checkit)(p->p_type, p->p_devname, p->p_mntpt,
+    p->p_auxarg, &d->d_pid)) && nrun > 0)
+    sleep(10);
+
+  if (!rv) nrun++;
+  return rv;
+}
+
+const char *unrawname(const char *name)
+{
+  static char unrawbuf[MAXPATHLEN];
+  const char *dp;
+  struct stat stb;
+
+  if (!(dp = strrchr(name, '/'))) return (name);
+  if (stat(name, &stb) < 0) return (name);
+  if (!S_ISCHR(stb.st_mode)) return (name);
+  if (dp[1] != 'r') return (name);
+  (void)snprintf(unrawbuf, sizeof(unrawbuf), "%.*s/%s",
+    (int)(dp - name), name, dp + 2);
+  return (unrawbuf);
+}
+
+const char *rawname(const char *name)
+{
+  static char rawbuf[MAXPATHLEN];
+  const char *dp;
+
+  if (!(dp = strrchr(name, '/'))) return (0);
+  (void)snprintf(rawbuf, sizeof(rawbuf), "%.*s/r%s",
+    (int)(dp - name), name, dp + 1);
+  return (rawbuf);
+}
+
+const char *blockcheck(const char *origname)
+{
+  struct stat stslash, stblock, stchar;
+  const char *newname, *raw;
+  struct fstab *fsp;
+  int retried = 0;
+
+
+  hot = 0;
+  if (stat("/", &stslash) < 0) {
+    printf("Can't stat `/'\n");
+    return (origname);
+  }
+  newname = origname;
+retry:
+  if (stat(newname, &stblock) < 0) {
+    return (origname);
+  }
+  if (S_ISBLK(stblock.st_mode)) {
+    if (stslash.st_dev == stblock.st_rdev)
+      hot++;
+    raw = rawname(newname);
+    if(!raw) return (origname);
+    if (stat(raw, &stchar) < 0) return (origname);
+    if (S_ISCHR(stchar.st_mode)) return (raw);
+    else {
+      printf("%s is not a character device\n", raw);
+      return (origname);
+    }
+  } else if (S_ISCHR(stblock.st_mode) && !retried) {
+    newname = unrawname(newname);
+    retried++;
+    goto retry;
+  } else if ((fsp = getfsfile(newname)) != 0 && !retried) {
+    newname = fsp->fs_spec;
+    retried++;
+    goto retry;
+  }
+  /*
+   * Not a block or character device, just return name and
+   * let the user decide whether to use it.
+   */
+  return (origname);
+}
+
+int checkfstab(int flags, int maxrun, void *(*docheck)(struct mntent *),
+  int (*checkit)(const char *, const char *, const char *, void *, pid_t *))
+{
+  struct diskentry *d, *nextdisk;
+  struct partentry *p;
+  int ret, pid, retcode, passno, sumstatus, status;
+  void *auxarg;
+  const char *name;
+
+  FILE *fp;  
+  struct mntent *me = (struct mntent*)xmalloc(sizeof(struct mntent));
+  char evilbuf[2*PATH_MAX];
+
+  char* path_mounts = "/etc/fstab";
+  int error = 0;
+
+  TAILQ_INIT(&badh);
+  TAILQ_INIT(&diskh);
+
+  sumstatus = 0;
+
+  for (passno = 1; passno <= 2; passno++) {
+    if (!(fp = setmntent(path_mounts, "r"))) {
+      error_msg("Can't open checklist file: %s", _PATH_FSTAB);
+      if(me) free(me);
+      return (8);
+    }
+    while (getmntent_r(fp, me, evilbuf, sizeof(evilbuf))) {
+      if (!(auxarg = (*docheck)(me))) continue;
+
+      name = blockcheck(me->mnt_fsname);
+      if (((flags & CHECK_PREEN) == 0 ||
+            (passno == 1 && me->mnt_passno == 1)) && !(flags & 8) ){
+        if (!name) {
+          if (flags & CHECK_PREEN) return 8;
+          else continue;
+        }
+        if(me->mnt_passno == 1 && flags & 4) continue;
+        sumstatus = (*checkit)(me->mnt_type,
+            name, me->mnt_dir, auxarg, NULL);
+
+        if (sumstatus) {
+          if ((flags & CHECK_NOFIX) == 0) return (sumstatus);
+          else error |= sumstatus;
+        }
+      } else if(flags & 8 && 1 == passno && me->mnt_passno == 1 && !(flags & 4)) {
+        sumstatus = (*checkit)(me->mnt_type,
+            name, me->mnt_dir, auxarg, NULL);
+
+        if (sumstatus) {         
+          if ((flags & CHECK_NOFIX) == 0) return (sumstatus);    
+          else error |= sumstatus;
+        }
+      } else if (passno == 2 && me->mnt_passno > 1) {
+        if (!name) {
+          (void) fprintf(stderr,
+              "BAD DISK NAME %s\n", me->mnt_fsname);
+          sumstatus |= 8;
+          continue;
+        }
+        if(flags & 16) (*checkit)(me->mnt_type, name, me->mnt_dir, auxarg, NULL); //FLAG_N
+        else addpart(me->mnt_type, name, me->mnt_dir, auxarg);
+      }
+    }
+  }
+  if(flags & 16) return 0;
+
+  if (flags & CHECK_DEBUG) printpart();
+
+  if (flags & CHECK_PREEN) {
+    if (maxrun == 0) maxrun = ndisks;
+    if (maxrun > ndisks) maxrun = ndisks;
+
+    nextdisk = TAILQ_FIRST(&diskh);
+    for (passno = 0; passno < maxrun; ++passno) {
+      if ((ret = startdisk(nextdisk, checkit)) != 0) {
+        if ((flags & CHECK_NOFIX) == 0) return ret;
+        else error |= ret;
+      }
+      nextdisk = TAILQ_NEXT(nextdisk, d_entries);
+    }
+
+    while ((pid = wait(&status)) != -1) {
+      TAILQ_FOREACH(d, &diskh, d_entries)
+        if (d->d_pid == pid) break;
+
+      if (!d) {
+        error_msg("Unknown pid %d", pid);
+        continue;
+      }
+
+      if (WIFEXITED(status)) retcode = WEXITSTATUS(status);
+      else retcode = 0;
+
+      p = TAILQ_FIRST(&d->d_part);
+      if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
+        (void) printf("done %s: %s (%s) = 0x%x\n",
+            p->p_type, p->p_devname, p->p_mntpt,
+            status);
+
+      if (WIFSIGNALED(status)) {
+        (void) fprintf(stderr,
+            "%s: %s (%s): EXITED WITH SIGNAL %d\n",
+            p->p_type, p->p_devname, p->p_mntpt,
+            WTERMSIG(status));
+        retcode = 8;
+      }
+
+      TAILQ_REMOVE(&d->d_part, p, p_entries);
+
+      if (retcode != 0) {
+        TAILQ_INSERT_TAIL(&badh, p, p_entries);
+        sumstatus |= retcode;
+      } else {
+        free(p->p_type);
+        free(p->p_devname);
+        free(p);
+      }
+      d->d_pid = 0;
+      nrun--;
+
+      if (TAILQ_FIRST(&d->d_part) == NULL) ndisks--;
+
+      if (nextdisk == NULL) {
+        if (TAILQ_FIRST(&d->d_part) != NULL) {
+          if ((ret = startdisk(d, checkit)) != 0) {
+            if ((flags & CHECK_NOFIX) == 0) return ret;
+            else error |= ret;
+          }
+        }
+      } else if (nrun < maxrun && nrun < ndisks) {
+        for ( ;; ) {
+          nextdisk = TAILQ_NEXT(nextdisk, d_entries);
+          if (nextdisk == NULL) nextdisk = TAILQ_FIRST(&diskh);
+          if (TAILQ_FIRST(&nextdisk->d_part)
+              != NULL && nextdisk->d_pid == 0)
+            break;
+        }
+        if ((ret = startdisk(nextdisk, checkit)) != 0) {
+          if ((flags & CHECK_NOFIX) == 0) return ret;
+          else error |= ret;
+        }
+      }
+    }
+  }
+  if (sumstatus) {
+    p = TAILQ_FIRST(&badh);
+    if (p == NULL) return (sumstatus);
+
+    (void) fprintf(stderr,
+        "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+        TAILQ_NEXT(p, p_entries) ? "S" : "",
+        "UNEXPECTED INCONSISTENCY:");
+
+    TAILQ_FOREACH(p, &badh, p_entries)
+      (void) fprintf(stderr,
+          "%s: %s (%s)%s", p->p_type, p->p_devname,
+          p->p_mntpt, TAILQ_NEXT(p, p_entries) ? ", " : "\n");
+
+    return sumstatus;
+  }
+  (void) endmntent(fp);
+  return error;
+}
+
+static int checkfs(const char *vfstype, const char *spec, const char *mntpt, void *auxarg, pid_t *pidp)
+{
+  /* List of directories containing fsck_xxx subcommands. */
+  static const char *edirs[] = {
+    _PATH_SBIN,
+    _PATH_USRSBIN,
+    NULL
+  };
+  const char ** volatile argv, **edir;
+  pid_t pid;
+  int argc, i, status, maxargc;
+  char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
+  const char *extra = getoptions("t");
+
+  (void) &optbuf;   /* Avoid vfork clobbering */
+  (void) &vfstype;
+
+  optbuf = NULL;
+  if (options) catopt(&optbuf, options);
+  if (extra) catopt(&optbuf, extra);
+  
+  maxargc = 64;
+  argv = xmalloc(sizeof(char *) * maxargc);
+
+  (void) snprintf(execbase, sizeof(execbase), "fsck.%s", vfstype);
+  argc = 0;
+  argv[argc++] = execbase;
+  if (optbuf) mangle(optbuf, &argc, &argv, &maxargc);
+  argv[argc++] = spec;
+  argv[argc] = NULL;
+
+  if (flags & FLAG_V || flags & FLAG_N) {
+    (void)printf("start %s %swait", mntpt, pidp ? "no" : "");
+    for (i = 0; i < argc; i++) (void)printf(" %s ", argv[i]);
+    (void)printf("\n");
+  }
+  if(flags & FLAG_N){
+    free(argv);
+    return 0;
+  }
+  switch (pid = vfork()) {
+  case -1:        /* Error. */
+    perror_msg("vfork");
+    if (optbuf) free(optbuf);
+    free(argv);
+    return (1);
+  case 0:          /* Child. */
+    /* Go find an executable. */
+    edir = edirs;
+    do {
+      (void)snprintf(execname, sizeof(execname), "%s/%s", *edir, execbase);
+      execv(execname, (char * const *)(argv));
+      if (errno != ENOENT) {
+        if (spec) perror_msg("exec %s for %s", execname, spec);
+        else perror_msg("exec %s", execname);
+      }
+    } while (*++edir != NULL);
+    if (errno == ENOENT) {
+      if (spec) perror_msg("exec %s for %s", execname, spec);
+      else perror_msg("exec %s", execname);
+    }
+    _exit(1);
+    /* NOTREACHED */
+  default:        /* Parent. */
+    if (optbuf) free(optbuf);
+    free(argv);
+
+    if (pidp) {
+      *pidp = pid;
+      return 0;
+    }
+    if (waitpid(pid, &status, 0) < 0) {
+      perror_msg("waitpid");
+      return (1);
+    }
+    if (WIFEXITED(status)) {
+      if (WEXITSTATUS(status) != 0) return (WEXITSTATUS(status));
+    } else if (WIFSIGNALED(status)) {
+      error_msg("%s: %s", spec, strsignal(WTERMSIG(status)));
+      return (1);
+    }
+    break;
+  }
+  return (0);
+}
+
+static void addentry(struct fstypelist *list, const char *type, const char *opts)
+{
+  struct entry *e;
+  e = (struct entry*)xmalloc(sizeof(struct entry));
+  e->type = strdup(type);
+  e->options = strdup(opts);
+  TAILQ_INSERT_TAIL(list, e, entries);
+}
+
+static void addoption(char *newoptions)
+{
+  struct entry *e;
+  char *optstr = "t";
+  TAILQ_FOREACH(e, &opthead, entries)
+  if (!strncmp(e->type, optstr, MFSNAMELEN)) {
+    catopt(&e->options, newoptions);
+    return;
+  }
+  addentry(&opthead, optstr, newoptions);
+}
+
+static void maketypelist(char *fslist)
+{
+  char *ptr;
+  if (!fslist || (fslist[0] == '\0'))
+    error_exit("empty type list");
+  if (fslist[0] == 'n' && fslist[1] == 'o') {
+    fslist += 2;
+    which = NOT_IN_LIST;
+  } else which = IN_LIST;
+  while ((ptr = strsep(&fslist, ",")))
+    addentry(&selhead, ptr, "");
+}
+
+static struct mntent* getfstabent(const char* spec)
+{
+  struct mntent *mnt;
+  char evilbuf[2*PATH_MAX];
+  FILE* fp = NULL;
+  char *path_mounts = "/etc/fstab";
+  mnt = (struct mntent*)xmalloc(sizeof(struct mntent));
+  if (!(fp = setmntent(path_mounts, "r"))) printf("cannot open %s\n", path_mounts);
+  else {
+    while (getmntent_r(fp, mnt, evilbuf, sizeof(evilbuf))) {
+      if(strcmp(spec, mnt->mnt_fsname) == 0 || strcmp(spec, mnt->mnt_dir) == 0)
+        return mnt;
+    }
+  }
+  free(mnt);
+  return NULL;
+}
+
+void fsck_main(void)
+{
+  struct mntent *me;
+  int j, rval = 0, all_for_spec = 0;
+  const char *vfstype = NULL;
+  int dev_count = 0, count;
+  char *devices[64]; //64 devices is assumed..... :TODO
+  char **args_p, *arg, **dev;
+
+  TAILQ_INIT(&selhead);
+  TAILQ_INIT(&opthead);
+
+  flags = toys.optflags;
+  flags |= CHECK_PREEN;
+  count = toys.optc;
+  args_p = toys.optargs;
+
+  for (j = 0; j < count; j++) {
+    arg = args_p[j];
+    if ((arg[0] == '/' && !all_for_spec) || strchr(arg, '=')) {
+      devices[dev_count++] = arg;
+      continue;
+    }
+    if (all_for_spec) { //add to args for specific filesystem
+      addoption(arg);
+      continue;
+    }
+    if (arg[0] == '-' && arg[1] == '-' && !arg[2]) all_for_spec = 1;
+    addoption(arg);
+  }
+  if (flags & FLAG_t) {
+    if (TAILQ_FIRST(&selhead) != NULL)
+      error_exit("only one -t option may be specified.");
+    maketypelist(TT.type);
+    vfstype = TT.type;
+  }
+  /* Don't do progress meters if we're debugging. */
+  if (flags & CHECK_DEBUG) flags &= ~CHECK_PROGRESS;
+  /*
+   * If progress meters are being used, force max parallel to 1
+   * so the progress meter outputs don't interfere with one another.
+   */
+  if (flags & CHECK_PROGRESS) maxrun = 1;
+  if (!dev_count || flags & FLAG_A ) {
+    toys.exitval = checkfstab(flags, 1/*maxrun*/, isok, checkfs);
+    return;
+  }
+  dev = devices;
+  for (; dev_count--; dev++) {
+    const char *spec, *type, *cp;
+    int dup_free = 0;
+    char  device[MAXPATHLEN];
+
+    spec = *dev;
+    cp = strrchr(spec, '/');
+    if (!cp) {
+      (void)snprintf(device, sizeof(device), "%s%s", _PATH_DEV, spec);
+      spec = device;
+    }
+    if ((me = getfstabent(spec)) == NULL) {
+      if (!vfstype) vfstype = "auto";
+      type = xstrdup((char *)vfstype);
+      dup_free = 1;
+    } else {
+      spec = me->mnt_fsname;
+      type = me->mnt_type;
+      if (BADTYPE(me)) error_exit("%s has unknown file system type.", spec);
+    }
+    rval |= checkfs(type, blockcheck(spec), *dev, NULL, NULL);
+    if(dup_free) free((void*)type);
+  }
+  toys.exitval = rval;
+  return;
+}
diff --git a/toys/other/fsync.c b/toys/other/fsync.c
new file mode 100644 (file)
index 0000000..80ad7a8
--- /dev/null
@@ -0,0 +1,42 @@
+/* fsync.c - Synchronize a file's in-core state with storage device.
+ *
+ * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
+
+USE_FSYNC(NEWTOY(fsync, "<1d", TOYFLAG_BIN))
+
+config FSYNC
+  bool "fsync"
+  default y
+  help
+    usage: fsync [d] [FILE...]
+
+    Synchronize a file's in-core state with storage device.
+
+    -d Avoid syncing metadata.
+*/
+
+#define FOR_fsync
+
+#include "toys.h"
+
+/*
+ * fsync main function.
+ */
+void fsync_main(void)
+{
+  for(; *toys.optargs; toys.optargs++) {
+    int fd;
+#ifdef O_NOATIME
+    if( (fd = open(*toys.optargs, O_RDONLY | O_NOATIME | O_NOCTTY )) == -1) {
+#else
+    if( (fd = open(*toys.optargs, O_RDONLY | O_NOCTTY )) == -1) {
+#endif
+      perror_msg("can't open '%s'", *toys.optargs);
+      continue;
+    }
+    if( ((toys.optflags & FLAG_d) ? fdatasync(fd) : fsync(fd)) == -1)
+      perror_msg("can't Sync '%s'", *toys.optargs);
+         xclose(fd);
+  }
+  return;
+}
diff --git a/toys/other/ftpget.c b/toys/other/ftpget.c
new file mode 100644 (file)
index 0000000..651c851
--- /dev/null
@@ -0,0 +1,600 @@
+/* ftpget.c - Get a remote file from FTP.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ * 
+USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P:", TOYFLAG_BIN))
+USE_FTPGET(OLDTOY(ftpput,ftpget, "<2vu:p:P:", TOYFLAG_BIN))
+
+config FTPGET
+  bool "ftpget/ftpput"
+  default y
+  help
+    usage: ftpget [-c -v -u username -p password P PortNumber] HOST_NAME [LOCAL_FILENAME] REMOTE_FILENAME
+    usage: ftpput [-v -u username -p password P PortNumber] HOST_NAME [REMOTE_FILENAME] LOCAL_FILENAME
+
+    ftpget - Get a remote file from FTP.
+    ftpput - Upload a local file on remote machine through FTP.
+
+    -c Continue previous transfer.
+    -v Verbose.
+    -u User name.
+    -p Password.
+    -P Port Number.
+*/
+#define FOR_ftpget
+#include "toys.h"
+
+GLOBALS(
+  char *port;
+  char *password;
+  char *username;
+  FILE *sockfd;
+  int c;
+  int isget;
+)
+
+#define DATACONNECTION_OPENED   125
+#define FTPFILE_STATUSOKAY      150
+#define FTP_COMMAND_OKAY        200
+#define FTPFILE_STATUS          213
+#define FTPSERVER_READY         220
+#define CLOSE_DATACONECTION     226
+#define PASSIVE_MODE            227
+#define USERLOGGED_SUCCESS      230
+#define PASSWORD_REQUEST        331
+#define REQUESTED_PENDINGACTION 350
+
+#define HTTP_DEFAULT_PORT 80
+#define FTP_DEFAULT_PORT  21
+
+#ifndef MAX_PORT_VALUE
+#define MAX_PORT_VALUE 65535
+#endif
+
+typedef struct sockaddr_with_len {
+  union {
+    struct sockaddr sock;
+    struct sockaddr_in sock_in;
+    struct sockaddr_in6 sock_in6;
+  }sock_u;
+  socklen_t socklen;
+} sockaddr_with_len;
+
+sockaddr_with_len *socwl;
+
+static void get_file(const char *l_filename, char *r_filename);
+static void put_file(const char *r_filename, char *l_filename);
+static void close_stream(const char *msg_str);
+
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, const char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+/*
+ * used to converts string into int and validate the input str for invalid int value or out-of-range.
+ */
+static unsigned get_strtou(const char *str, char **endp, int base)
+{
+  unsigned long uli;
+  char *endptr;
+
+  if(!isalnum(str[0])) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+  errno = 0;
+  uli = strtoul(str, &endptr, base);
+  if(uli > UINT_MAX) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+
+  if(endp) *endp = endptr;
+  if(endptr[0]) {
+    if(isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
+      errno = ERANGE;
+      return UINT_MAX;
+    }
+    errno = EINVAL;
+  }
+  return uli;
+}
+
+/*
+ * verify the host is local unix path.
+ * if so, set the swl input param accordingly.
+ */
+static int is_host_unix(const char *host, sockaddr_with_len **swl)
+{
+  if(strncmp(host, "local:", 6) == 0) {
+    struct sockaddr_un *sockun;
+    *swl = xzalloc(sizeof(struct sockaddr_with_len));
+    (*swl)->socklen = sizeof(struct sockaddr_un);
+    (*swl)->sock_u.sock.sa_family = AF_UNIX;
+    sockun = (struct sockaddr_un *)&(*swl)->sock_u.sock;
+    safe_strncpy(sockun->sun_path, host + 6, sizeof(sockun->sun_path));
+    return 1;
+  }
+  return 0;
+}
+
+/*
+ * validate the input param (host) for valid ipv6 ip and extract port number (if there).
+ */
+static void get_host_and_port(char **host, int *port)
+{
+  char *ch_ptr;
+  const char *org_host = *host;
+  if(*host[0] == '[') {
+    (*host)++;
+    ch_ptr = strchr(*host, ']');
+    if(!ch_ptr || (ch_ptr[1] != ':' && ch_ptr[1] != '\0'))
+      error_exit("bad address '%s'", org_host);
+  }
+  else {
+    ch_ptr = strrchr(*host, ':');
+    //There is more than one ':' like "::1"
+    if(ch_ptr && strchr(*host, ':') != ch_ptr)
+      ch_ptr = NULL;
+  }
+  if(ch_ptr) { //pointer to ":" or "]:"
+    int size = ch_ptr - (*host) + 1;
+    safe_strncpy(*host, *host, size);
+    if(*ch_ptr != ':') {
+      ch_ptr++; //skip ']'
+      //[nn] without port
+      if(*ch_ptr == '\0')
+        return;
+    }
+    ch_ptr++; //skip ':' to get the port number.
+    *port = get_strtou(ch_ptr, NULL, 10);
+    if(errno || (unsigned)*port > MAX_PORT_VALUE)
+      error_exit("bad port spec '%s'", org_host);
+   }
+  return;
+}
+
+/*
+ * used to extract the address info from the given host ip
+ * and update the swl param accordingly.
+ */
+static int get_socket_stream(const char *host, sa_family_t af, sockaddr_with_len **swl)
+{
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
+  int status = 0;
+
+  memset(&hints, 0 , sizeof(struct addrinfo));
+  hints.ai_family = af;
+  hints.ai_socktype = SOCK_STREAM;
+
+  if((status = getaddrinfo(host, NULL, &hints, &result)) != 0) {
+    perror_exit("bad address '%s' : %s", host, gai_strerror(status));
+    return status;
+  }
+
+  for(rp = result; rp != NULL; rp = rp->ai_next) {
+    if( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) {
+      *swl = xmalloc(sizeof(struct sockaddr_with_len));
+      (*swl)->socklen = rp->ai_addrlen;
+      memcpy(&((*swl)->sock_u.sock), rp->ai_addr, rp->ai_addrlen);
+      break;
+    }
+  }
+  freeaddrinfo(result);
+  return ((!rp)? -1: status);
+}
+
+/*
+ * used to set the port number for ipv4 / ipv6 addresses.
+ */
+static void setport(struct sockaddr *sock, unsigned port_num)
+{
+  //for ipv4
+  if(sock->sa_family == AF_INET) {
+    struct sockaddr_in *sock_in = (void*)sock;
+    sock_in->sin_port = port_num;
+  }
+  //for ipv6
+  else if(sock->sa_family == AF_INET6) {
+    struct sockaddr_in6 *sock_in6 = (void*)sock;
+    sock_in6->sin6_port = port_num;
+  }
+  return;
+}
+
+/*
+ * use to get the socket address with the given host ip.
+ */
+static sockaddr_with_len *get_sockaddr(const char *host, int port, sa_family_t af)
+{
+  sockaddr_with_len *swl = NULL;
+  int status = 0;
+
+  //for unix
+  int is_unix = is_host_unix(host, &swl);
+  if(is_unix && swl) return swl;
+
+  //[IPV6_ip]:port_num
+  if(host[0] == '[' || strrchr(host, ':')) get_host_and_port((char **)&host, &port);
+
+  //for the socket streams.
+  status = get_socket_stream(host, af, &swl);
+  if(status) return NULL;
+
+  setport(&swl->sock_u.sock, htons(port));
+  return swl;
+}
+
+/*
+ * get the numeric hostname and service name, for a given socket address.
+ */
+static char *address_to_name(const struct sockaddr *sock)
+{
+  //man page of getnameinfo.
+  char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
+  int status = 0;
+  if(sock->sa_family == AF_INET) {
+    socklen_t len = sizeof(struct sockaddr_in);
+    if((status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) == 0)
+      return xmsprintf("%s:%s", hbuf, sbuf);
+    else {
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(status));
+      return NULL;
+    }
+  }
+  else if(sock->sa_family == AF_INET6) {
+    socklen_t len = sizeof(struct sockaddr_in6);
+    if((status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) == 0) {
+      //verification for resolved hostname.
+      if(strchr(hbuf, ':')) return xmsprintf("[%s]:%s", hbuf, sbuf);
+      else return xmsprintf("%s:%s", hbuf, sbuf);
+    }
+    else {
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(status));
+      return NULL;
+    }
+  }
+  else if(sock->sa_family == AF_UNIX) {
+    struct sockaddr_un *sockun = (void*)sock;
+    return xmsprintf("local:%.*s", (int) sizeof(sockun->sun_path), sockun->sun_path);
+  }
+  return NULL;
+}
+
+/*
+ * used to get the port number for ftp and http.
+ */
+static unsigned getport(const char *port, const char *protocol, unsigned defport)
+{
+#define RANGE_STR "Port Number should be in [1-65535] range"
+  long v;
+  char *endptr = NULL;
+
+  unsigned portnum = defport;
+  if(port) {
+    errno = 0;
+    v = strtol(port, &endptr, 10);
+    if((v > 0) && (!*endptr)) {//for numeric like 123
+      portnum = v;
+      if(portnum > MAX_PORT_VALUE) error_exit("Invalid Port Number '%s' "RANGE_STR, port);
+    }
+    else if((v == 0) && (endptr != NULL)) {
+      switch(defport) {
+        case FTP_DEFAULT_PORT:
+          if(strcmp(endptr, "ftp") == 0) portnum = defport; //for "ftp" string.
+          else goto ERROR_EXIT;
+          break;
+        case HTTP_DEFAULT_PORT:
+          if(strcmp(endptr, "http") == 0) portnum = defport;//for "HTTP" string.
+          else goto ERROR_EXIT;
+          break;
+        default:
+ERROR_EXIT:
+          error_exit("Invalid Port");
+          break;
+      }
+    }
+    else perror_exit("Invalid Port Number: '%s' "RANGE_STR, port);
+  }
+#undef RANGE_STR
+  return (uint16_t)portnum;
+}
+
+/*
+ * used to connect with the socket.
+ */
+static int connect_to_stream(const sockaddr_with_len *swl)
+{
+  int sockfd;
+  if((sockfd = socket(swl->sock_u.sock.sa_family, SOCK_STREAM, 0)) < 0)
+    perror_exit("cannot open control socket");
+  if(connect(sockfd, &swl->sock_u.sock, swl->socklen) < 0) {
+    close(sockfd);
+    perror_exit("can't connect to remote host");
+  }
+  return sockfd;
+}
+
+/*
+ * send command to ftp and get return status.
+ */
+static int get_ftp_response(const char *command, const char *param)
+{
+  unsigned cmd_status = 0;
+  char *fmt = "%s %s\r\n";
+  if(command) {
+    if(!param) fmt += 3;
+    fprintf(TT.sockfd, fmt, command, param);
+    fflush(TT.sockfd);
+    if(toys.optflags & FLAG_v) fprintf(stderr, "FTP Request: %s %s\r\n", command, param);
+  }
+
+  do {
+    if(fgets(toybuf, sizeof(toybuf)-1, TT.sockfd) == NULL) close_stream(NULL);
+  }while(!isdigit(toybuf[0]) || toybuf[3] != ' ');
+
+  toybuf[3] = '\0';
+  cmd_status = get_int_value(toybuf, 0, INT_MAX);
+  toybuf[3] = ' ';
+  return cmd_status;
+}
+
+/*
+ * send request to ftp  for login.
+ */
+static void send_requests(void)
+{
+  int cmd_status = 0;
+  //FTP connection request.
+  if(get_ftp_response(NULL, NULL) != FTPSERVER_READY) close_stream(NULL);
+
+  //230 User authenticated, password please; 331 Password request.
+  cmd_status = get_ftp_response("USER", TT.username);
+  if(cmd_status == PASSWORD_REQUEST) {//user logged in. Need Password.
+    if(get_ftp_response("PASS", TT.password) != USERLOGGED_SUCCESS) close_stream("PASS");
+  }
+  else if(cmd_status == USERLOGGED_SUCCESS) {/*do nothing*/;}
+  else close_stream("USER");
+  //200 Type Binary. Command okay.
+  if(get_ftp_response("TYPE I", NULL) != FTP_COMMAND_OKAY)
+    close_stream("TYPE I");
+  return;
+}
+
+/*
+ * login to ftp.
+ */
+static void login_to_ftp(void)
+{
+  int fd = connect_to_stream(socwl);
+  TT.sockfd = fdopen(fd, "r+");
+  if(!TT.sockfd) perror_exit("unable to connect with ftp:");
+  return;
+}
+
+/*
+ * ftpget/ftpput main function.
+ */
+void ftpget_main(void)
+{
+  const char *port = "ftp";
+  char **argv = toys.optargs; //host name + file name.
+  unsigned long portnum;
+  TT.isget = toys.which->name[3] == 'g';
+
+  //if user name is not specified.
+  if(!(toys.optflags & FLAG_u) && (toys.optflags & FLAG_p)) error_exit("Missing username:");
+  //if user name and password is not specified in command line.
+  if(!(toys.optflags & FLAG_u) && !(toys.optflags & FLAG_p)) {
+    TT.username = "anonymous";
+    TT.password = "anonymous";
+  }
+  
+  if(!(toys.optflags & FLAG_P)) TT.port = (char *)port; //use default port for ftp i.e. 21.
+  
+  //if continue is not in the command line argument.
+  if(TT.isget) {
+    if(!(toys.optflags & FLAG_c)) TT.c = 0;
+    else TT.c = 1;
+  }
+
+  portnum = getport(TT.port, "tcp", 21); //Get port number.
+  socwl = get_sockaddr(argv[0], portnum, AF_UNSPEC);
+  if(!socwl) error_exit("error in resolving host name");
+  if(toys.optflags & FLAG_v) {
+    char *str = address_to_name(&socwl->sock_u.sock);
+    if(str != NULL) {
+      fprintf(stderr, "Connecting to %s (%s)\n", argv[0], str);
+      free(str);
+      str = NULL;
+    }
+  }
+
+  login_to_ftp();
+  send_requests();
+
+  if(TT.isget) get_file(argv[1], argv[2] ? argv[2] : argv[1]); //argv[1] = local file name; argv[2] = remote file name
+  else put_file(argv[1], argv[2] ? argv[2] : argv[1]); //argv[1] = remote file name; argv[2] = local file name
+  return;
+}
+
+/*
+ * send commands to ftp fo PASV mode.
+ */
+static void verify_pasv_mode(const char *r_filename)
+{
+  char *pch;
+  unsigned portnum;
+
+  //vsftpd reply like:- "227 Entering Passive Mode (125,19,39,117,43,39)".
+  if(get_ftp_response("PASV", NULL) != PASSIVE_MODE) close_stream("PASV");
+
+//Response is "NNN <some text> (N1,N2,N3,N4,P1,P2) garbage.
+//Server's IP is N1.N2.N3.N4
+//Server's port for data connection is P1*256+P2.
+  pch = strrchr(toybuf, ')');
+  if(pch) *pch = '\0';
+
+  pch = strrchr(toybuf, ',');
+  if(pch) *pch = '\0';
+  portnum = get_int_value(pch + 1, 0, 255);
+
+  pch = strrchr(toybuf, ',');
+  if(pch) *pch = '\0';
+  portnum = portnum + (get_int_value(pch + 1, 0, 255) * 256);
+  setport(&socwl->sock_u.sock, htons(portnum));
+
+  if(TT.isget) {
+    if(get_ftp_response("SIZE", r_filename) != FTPFILE_STATUS) TT.c = 0;
+  }
+  return;
+}
+
+/*
+ * verify the local file presence.
+ * if present, get the size of the file.
+ */
+static void is_localfile_present(const char *l_filename)
+{
+  struct stat sb;
+  if(stat(l_filename, &sb) < 0) perror_exit("stat"); //there is no local file with the given name.
+  //if local file present, then request for pending file action.
+  if(sb.st_size > 0) {
+    sprintf(toybuf, "REST %lu", (unsigned long) sb.st_size);
+    if(get_ftp_response(toybuf, NULL) != REQUESTED_PENDINGACTION) TT.c = 0;
+  }
+  else TT.c = 0;
+  return;
+}
+
+/*
+ * transfer the file from ftp to local or from local to ftp.
+ */
+static void transfer_file(int local_fd, int remote_fd)
+{
+  if( (local_fd < 0) || (remote_fd < 0)) {
+    toys.exitval = 1;
+    perror_exit("Error in file creation:");
+  }
+  if(TT.isget) {
+    for(;;) {
+      int len = 0;
+      len = xread(remote_fd, toybuf, sizeof(toybuf)-1);
+      if(!len) break;
+      xwrite(local_fd, toybuf, len);
+    }
+  }
+  else {
+    for(;;) {
+      int len = 0;
+      len = xread(local_fd, toybuf, sizeof(toybuf)-1);
+      if(!len) break;
+      xwrite(remote_fd, toybuf, len);
+    }
+  }
+  return;
+}
+
+/*
+ * get the file from ftp.
+ */
+static void get_file(const char *l_filename, char *r_filename)
+{
+#define IS_DASH(s) ((s)[0] == '-' && !(s)[1])
+  int local_fd = -1;
+  int remote_fd;
+
+  verify_pasv_mode(r_filename);
+  remote_fd = connect_to_stream(socwl); //Connect to data socket.
+
+  //if local file name will be '-' then local fd will be stdout.
+  if(IS_DASH(l_filename)) {
+    local_fd = 1; //file descriptor will become stdout.
+    TT.c = 0;
+  }
+
+  //if continue, check for local file existance.
+  if(TT.c) is_localfile_present(l_filename);
+
+  //verify the remote file presence.
+  if(get_ftp_response("RETR", r_filename) > FTPFILE_STATUSOKAY) close_stream("RETR");
+
+  //if local fd is not stdout, create a file descriptor.
+  if(local_fd == -1) {
+    int flags;
+    if(TT.c) flags = O_APPEND | O_WRONLY;
+    else flags = O_CREAT | O_TRUNC | O_WRONLY;
+    local_fd = xcreate((char *)l_filename, flags, 0666);
+  }
+  transfer_file(local_fd, remote_fd);
+  xclose(remote_fd);
+  xclose(local_fd);
+  if(get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL);
+  get_ftp_response("QUIT", NULL);
+  toys.exitval = EXIT_SUCCESS;
+#undef IS_DASH
+  return;
+}
+
+/*
+ * put file to ftp.
+ */
+static void put_file(const char *r_filename, char *l_filename)
+{
+#define IS_DASH(s) ((s)[0] != '-' || (s)[1])
+  int local_fd = 0; //stdin.
+  int remote_fd;
+  unsigned cmd_status = 0;
+
+  verify_pasv_mode(r_filename);
+  remote_fd = connect_to_stream(socwl); //Connect to data socket.
+
+  //open the local file for transfer.
+  if(IS_DASH(l_filename)) local_fd = xcreate((char *)l_filename, O_RDONLY, 0666);
+
+  //verify for the remote file status, Data Connection already open; transfer starting.
+  cmd_status = get_ftp_response("STOR", r_filename);
+  if( (cmd_status == DATACONNECTION_OPENED) || (cmd_status == FTPFILE_STATUSOKAY)) {
+    transfer_file(local_fd, remote_fd);
+    xclose(remote_fd);
+    xclose(local_fd);
+    if(get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL);
+    get_ftp_response("QUIT", NULL);
+    toys.exitval = EXIT_SUCCESS;
+  }
+  else {
+    xclose(remote_fd);
+    xclose(local_fd);
+    toys.exitval = EXIT_FAILURE;
+    close_stream("STOR");
+  }
+#undef IS_DASH
+  return;
+}
+
+/*
+ * close ftp connection and print the message.
+ */
+static void close_stream(const char *msg_str)
+{
+  char *str;
+  //toybuf holds response data.
+  //Remove garbage chars (from ' ' space to '\x7f') DEL remote server response.
+  for(str = toybuf; *str >= 0x20 && *str < 0x7f; str++) { /**/;}
+  *str = '\0';
+  if(TT.sockfd) fclose(TT.sockfd);
+  if(socwl != NULL) {
+    free(socwl);
+    socwl = NULL;
+  }
+  if(msg_str) error_exit("%s server response: %s", msg_str, toybuf);
+  else error_exit("server response: %s", toybuf);
+}
diff --git a/toys/other/getty.c b/toys/other/getty.c
new file mode 100644 (file)
index 0000000..e4314b0
--- /dev/null
@@ -0,0 +1,377 @@
+/* getty.c - A getty program to get controlling terminal.
+ *
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gamil.com>
+ *
+ * Not in SUSv4.
+
+USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN))
+
+config GETTY
+  bool "getty"
+  default y
+  help
+    Usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]
+    -h    Enable hardware RTS/CTS flow control
+    -L    Set CLOCAL (ignore Carrier Detect state)
+    -m    Get baud rate from modem's CONNECT status message
+    -n    Don't prompt for login name
+    -w    Wait for CR or LF before sending /etc/issue
+    -i    Don't display /etc/issue
+    -f ISSUE_FILE  Display ISSUE_FILE instead of /etc/issue
+    -l LOGIN  Invoke LOGIN instead of /bin/login
+    -t SEC    Terminate after SEC if no login name is read
+    -I INITSTR  Send INITSTR before anything else
+    -H HOST    Log HOST into the utmp file as the hostname
+*/
+
+#define FOR_getty
+#include "toys.h"
+#include <termios.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <paths.h>
+GLOBALS(
+  char *issue_str;
+  char *login_str;
+  char *init_str;
+  char *host_str; 
+  long timeout;
+  
+  char *tty_name;  
+  int  speeds[20];
+  int sc;              //BREAK: make speeds as circular.[speed counter(sp)]
+  struct termios termios;
+  char buff[128];
+)
+#define CTL(x)      ((x) ^ 0100)  
+#define DEF_INTR    CTL('C')    
+#define DEF_QUIT    CTL('\\')     
+#define DEF_KILL    CTL('U')    
+#define DEF_EOF     CTL('D')    
+#define DEF_EOL     '\n'
+
+#define MAX_NR   10
+#define HOSTNAME_SIZE 32
+typedef void (*sighandler_t)(int);
+struct speed_mapper {
+  long speed;
+  speed_t code;
+};
+struct speed_mapper speedtab[] = {
+  {50, B50},
+  {75, B75},
+  {110, B110},
+  {134, B134},
+  {150, B150},
+  {200, B200},
+  {300, B300},
+  {600, B600},
+  {1200, B1200},
+  {1800, B1800},
+  {2400, B2400},
+  {4800, B4800},
+  {9600, B9600},
+#ifdef  B19200
+  {19200, B19200},
+#endif
+#ifdef  B38400
+  {38400, B38400},
+#endif
+#ifdef  EXTA
+  {19200, EXTA},
+#endif
+#ifdef  EXTB
+  {38400, B38400},
+#endif
+#ifdef B57600
+  {57600, B57600},
+#endif
+#ifdef B115200
+  {115200, B115200},
+#endif
+#ifdef B230400
+  {230400, B230400},
+#endif
+  {0, 0},
+};
+/* 
+ * convert str to long with 
+ * error check, TODO:need to move to lib
+ */
+static int strtol_no_range(char *str)
+{
+  char *endptr = NULL;
+  errno = 0;
+  long ret_value = strtol(str, &endptr, 10);
+  if(errno) perror_exit("Invalid num %s", str);
+  else {
+    if((endptr && (*endptr != '\0' || endptr == str)) || (ret_value < 0))
+      perror_exit("Not a valid num %s", str);
+  }
+  return ret_value;
+}
+/* 
+ * Find speed from mapper array 
+ */
+static speed_t encode(char *s)
+{
+  const struct speed_mapper *sp;
+  long speed = strtol_no_range(s);
+  if(speed == 0) return 0;
+  for (sp = speedtab; sp->speed; sp++)
+    if (sp->speed == speed) return sp->code;
+  return (speed_t) -1;
+}
+/*
+ * Get speed and return it
+ */
+static void get_speed(char *sp)
+{
+  char *ptr;
+  TT.sc = 0;
+  while((ptr = strsep(&sp, ",")) != NULL) {
+    TT.speeds[TT.sc] = encode(ptr);
+    if(TT.speeds[TT.sc] < 0) perror_exit("Bad Speed");
+    TT.sc++;
+    if(TT.sc > MAX_NR) perror_exit("Too many alternate speeds, Max is 10");
+  }
+}
+/* 
+ * Parse args and set TERM env. variable
+ */
+static void parse_arguments(void)
+{
+  if(isdigit(**toys.optargs)) {
+    get_speed(*toys.optargs);
+    if(*++toys.optargs) TT.tty_name = xmsprintf("%s", *toys.optargs);
+  }
+  else {
+    TT.tty_name = xmsprintf("%s", *toys.optargs);
+    if(*++toys.optargs) get_speed(*toys.optargs);
+  }  
+  if(*++toys.optargs) setenv("TERM", *toys.optargs, 1);
+}
+/* 
+ * get controlling terminal and redirect stdio 
+ */
+static void open_tty(void)
+{
+  pid_t sid;
+  if(strcmp(TT.tty_name, "-") != 0) {
+    if(*(TT.tty_name) != '/') TT.tty_name = xmsprintf("/dev/%s", TT.tty_name);
+    sighandler_t sig = signal(SIGHUP, SIG_IGN); //Sends SIGHUP to all foreground process if Session leader...don't die, Ignore.
+    ioctl(0, TIOCNOTTY, 0); //Giveup if there is any controlling terminal
+    signal(SIGHUP, sig);
+    if(setsid() < 0 ) { //seems we are session leader
+      sid = getpid(); 
+      if(sid != getsid(0)) perror_exit("setsid");
+    }
+    xclose(0);
+    xopen(TT.tty_name, O_RDWR|O_NDELAY);
+    fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); //Block read
+    dup2(0, 1);
+    dup2(0, 2);
+    if(ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)");
+    if(!isatty(0)) perror_exit("/dev/%s:Not a character device", TT.tty_name); //Sanity check
+    chown(TT.tty_name, 0, 0); //change ownership..Hope login will change this
+    chmod(TT.tty_name, 0620);
+  }
+  else {//We already have open TTY
+    if(setsid() < 0) perror_msg("setsid:failed");
+    if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)  //check w/r permission.
+      perror_exit("opened tty don't have r/w perm.");
+  }
+}
+/*
+ * Intialise terminal settings
+ */
+static void termios_init(void)
+{
+  if(tcgetattr(STDIN_FILENO, &TT.termios) < 0) perror_exit("tcgetattr error:");
+  tcflush(STDIN_FILENO, TCIOFLUSH); // Flush input and output queues, important for modems! 
+
+  TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD);
+#ifdef CRTSCTS
+  if(toys.optflags & FLAG_h) TT.termios.c_cflag |= CRTSCTS;
+#endif
+  if(toys.optflags & FLAG_L) TT.termios.c_cflag |= CLOCAL;
+  TT.termios.c_cc[VTIME] = 0;
+  TT.termios.c_cc[VMIN] = 1;
+  TT.termios.c_oflag = OPOST|ONLCR;
+  TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
+  TT.termios.c_iflag = 0;
+  TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE; // login will disable echo for passwd.
+  TT.termios.c_cc[VINTR] = DEF_INTR;
+  TT.termios.c_cc[VQUIT] = DEF_QUIT;
+  TT.termios.c_cc[VEOF] = DEF_EOF;
+  TT.termios.c_cc[VEOL] = DEF_EOL;
+  TT.termios.c_cc[VKILL] = DEF_KILL;
+  TT.termios.c_cc[VERASE] = CERASE;
+  TT.termios.c_iflag |= ICRNL|IXON|IXOFF;
+  if(TT.speeds[0] != B0) cfsetspeed(&TT.termios, TT.speeds[0]); // set non-zero baud rate. Zero baud rate left it unchanged. 
+  if(tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr error:");
+}
+/* 
+ * Get the baud rate from modems CONNECT mesage
+ * It is of form <junk><BAUD><Junk>
+ */
+static void sense_baud(void)
+{
+  int vmin;
+  ssize_t size;
+  char *ptr;
+  speed_t speed;
+  vmin = TT.termios.c_cc[VMIN]; //store old
+  TT.termios.c_cc[VMIN] = 0; //No block even queue is empty.
+  if(tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr error:");
+
+  size = readall(STDIN_FILENO, TT.buff, sizeof(TT.buff)-1);
+  if(size > 0) {
+    for(ptr = TT.buff; ptr < TT.buff+size; ptr++) {
+      if(isdigit(*ptr)) {
+        speed = encode(ptr);
+        if(speed > 0) cfsetspeed(&TT.termios,speed);
+        break;
+      }
+    }
+  }
+  TT.termios.c_cc[VMIN] = vmin; //restore old value
+  if(tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr error:");
+}
+/*
+ * Just prompt for login name 
+ */
+void print_prompt(void)
+{
+  char *hostname;
+  struct utsname uts;
+  uname(&uts);
+  hostname = xstrdup(uts.nodename);
+  fputs(hostname,stdout);
+  fputs(" Login: ",stdout);
+  fflush(NULL);
+  free(hostname);
+  hostname = NULL;
+}
+/* 
+ * Print /etc/isuue with taking care of 
+ * each escape sequence.
+ */
+void write_issue(char *file)
+{
+  int size;
+  char buff[20] = {0,};
+  struct utsname u;
+  uname(&u);
+  int fd = open(TT.issue_str, O_RDONLY);
+  if(fd < 0) return ;
+  while((size = readall(fd, buff, 1)) > 0) {
+    char *ch = buff;
+    if(*ch == '\\' || *ch == '%') {
+      if(readall(fd, buff, 1) <= 0) perror_exit("readall!");
+      if(*ch == 's') fputs(u.sysname, stdout);
+      if(*ch == 'n'|| *ch == 'h') fputs(u.nodename, stdout);
+      if(*ch == 'r') fputs(u.release, stdout);
+      if(*ch == 'm') fputs(u.machine, stdout);
+      if(*ch == 'l') fputs(TT.tty_name, stdout);
+    } 
+    else xputc(*ch);
+  }
+}
+/*
+ * Read login name and print prompt and Issue file. 
+ */
+static int read_login_name(void)
+{
+  tcflush(STDIN_FILENO, TCIFLUSH); //Fush pending speed switches..
+  int i = 0;
+  while(1) {
+    if(!(toys.optflags & FLAG_i)) write_issue(TT.issue_str); // Option -i will overide -f 
+    print_prompt();
+    TT.buff[0] = getchar();
+    if(TT.buff[0] == '\0' && TT.sc > 1) return 0; //switch speed
+    if(TT.buff[0] == '\n')
+      continue;
+    if (TT.buff[0] != '\n')
+      if(!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin))
+        _exit(1);
+    while(i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++;
+    TT.buff[i] = 0;
+    break;
+  }
+  return 1;
+}
+/*
+ * Put hostname entry in utmp file
+ */
+static void utmp_entry(void)
+{
+  struct utmp entry;
+  struct utmp *utp_ptr;
+  pid_t pid = getpid();
+  utmpname(_PATH_UTMP);
+  setutent(); //start from start
+  while((utp_ptr = getutent()) != NULL) {
+    if(utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break;
+  }
+  if(!utp_ptr) { 
+    entry.ut_type = LOGIN_PROCESS;
+    entry.ut_pid = getpid();
+    xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE);
+    time(&entry.ut_time);
+    xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
+    if(strlen(TT.host_str) > UT_HOSTSIZE) perror_msg("Can't make utmp entry,Host length is greater than UT_HOSTSIZE(256)");
+    else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
+    setutent();
+    pututline(&entry);
+    return;
+  }
+  xstrncpy(entry.ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"), UT_LINESIZE);
+  xstrncpy(entry.ut_user, "LOGIN", UT_NAMESIZE);
+  if(strlen(TT.host_str) > UT_HOSTSIZE) perror_msg("Can't make utmp entry,Host length is greater than UT_HOSTSIZE(256)");
+  else xstrncpy(entry.ut_host, TT.host_str, UT_HOSTSIZE);
+  time(&entry.ut_time);
+  setutent();
+  pututline(&entry);
+}
+/*
+ * getty main function
+ */
+void getty_main(void)
+{
+  pid_t pid;
+  pid = getpid();
+  char *ptr[2] = {"/bin/login", NULL};
+  if(!(toys.optflags & FLAG_f)) TT.issue_str = "/etc/issue";
+  if(toys.optflags & FLAG_l) ptr[0] = TT.login_str;
+  parse_arguments(); //First parse speeds etc.
+  open_tty();
+  termios_init();
+  tcsetpgrp(STDIN_FILENO, pid);
+  if(toys.optflags & FLAG_H) utmp_entry();
+  if(toys.optflags & FLAG_I) writeall(STDOUT_FILENO,TT.init_str,strlen(TT.init_str));
+  if(toys.optflags & FLAG_m) sense_baud();
+  if(toys.optflags & FLAG_t) alarm(TT.timeout);
+
+  if(toys.optflags & FLAG_w) {
+    char ch;
+    while(readall(STDIN_FILENO, &ch, 1) != 1) {        
+      if(ch == '\n' || ch == '\r') break;
+    }
+  }
+
+  if(!(toys.optflags & FLAG_n)) {
+    int index = 1; //0th we already set.
+    while(1) {
+      int l = read_login_name();
+      if(l) break;
+      index = index % TT.sc;
+      cfsetspeed(&TT.termios, TT.speeds[index]); //Select from multiple speeds
+      //Necessary after cfsetspeed
+      if(tcsetattr(STDIN_FILENO, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr error:"); 
+    }
+  }
+  if(toys.optflags & FLAG_n) execlp(*ptr, *ptr ,NULL); //-n: login command will ask for login name
+  else execlp(*ptr, *ptr, TT.buff, NULL); 
+  perror_exit("error:%d",errno); //exec will return only if error
+}
diff --git a/toys/other/halt.c b/toys/other/halt.c
new file mode 100644 (file)
index 0000000..085af41
--- /dev/null
@@ -0,0 +1,77 @@
+/* halt.c - halt utility.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_HALT(NEWTOY(halt, "d#<0nfw", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HALT
+  bool "halt"
+  default y
+  help
+    usage: halt [-d delay] [-n] [-f][-w]
+
+    halt utility to halt the sytem.
+    -d DELAY  waits DELAY seconds before halt.
+    -n    overrides sync() call before halt.
+    -f    Forces system HALT and bypasses init process.
+    -w    Logs message in system logs.
+*/
+
+#define FOR_halt
+#include "toys.h"
+
+#include <sys/reboot.h>
+#include <err.h>
+#include <signal.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <utmp.h>
+
+GLOBALS(
+  long delay_number;
+)
+
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, const char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+/*
+ * Put hostname entry in utmp file
+ */
+static void log_wtmp(void)
+{
+  struct utmp utmp;
+  struct utsname uts;
+
+  memset(&utmp, 0, sizeof(utmp));
+  utmp.ut_tv.tv_sec = time(NULL);
+  strcpy(utmp.ut_user, "halt");
+  utmp.ut_type = RUN_LVL;
+  utmp.ut_id[0] = '~'; utmp.ut_id[1] = '~';
+  utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~';
+  uname(&uts);
+  safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host));
+  updwtmp("/var/log/wtmp", &utmp);
+}
+
+void halt_main(void)
+{
+  if (toys.optflags & FLAG_d)  sleep((unsigned int)TT.delay_number);
+  if ((toys.optflags & FLAG_n)?0:1) sync();
+  if (toys.optflags & FLAG_w) {
+    log_wtmp();
+    exit(0); 
+  }
+  if (toys.optflags & FLAG_f) reboot(RB_HALT_SYSTEM);
+  else {
+    kill(1,SIGUSR1);
+    reboot(RB_POWER_OFF);
+  }
+}
diff --git a/toys/other/hello.c b/toys/other/hello.c
new file mode 100644 (file)
index 0000000..b0fc538
--- /dev/null
@@ -0,0 +1,50 @@
+/* hello.c - A hello world program. (Template for new commands.)
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/
+ * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cmdbehav.html
+
+USE_HELLO(NEWTOY(hello, "e@d*c#b:a", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HELLO
+  bool "hello"
+  default n
+  help
+    usage: hello [-a] [-b string] [-c number] [-d list] [-e count] [...]
+
+    A hello world program.  You don't need this.
+
+    Mostly used as an example/skeleton file for adding new commands,
+    occasionally nice to test kernel booting via "init=/bin/hello".
+*/
+
+#define FOR_hello
+#include "toys.h"
+
+// Hello doesn't use these globals, they're here for example/skeleton purposes.
+
+GLOBALS(
+  char *b_string;
+  long c_number;
+  struct arg_list *d_list;
+  long e_count;
+
+  int more_globals;
+)
+
+void hello_main(void)
+{
+  printf("Hello world\n");
+
+  if (toys.optflags & FLAG_a) printf("Saw a\n");
+  if (toys.optflags & FLAG_b) printf("b=%s\n", TT.b_string);
+  if (toys.optflags & FLAG_c) printf("c=%ld\n", TT.c_number);
+  while (TT.d_list) {
+    printf("d=%s\n", TT.d_list->arg);
+    TT.d_list = TT.d_list->next;
+  }
+  if (TT.e_count) printf("e was seen %ld times", TT.e_count);
+
+  while (*toys.optargs) printf("optarg=%s\n", *(toys.optargs++));
+}
diff --git a/toys/other/help.c b/toys/other/help.c
new file mode 100644 (file)
index 0000000..e16abaa
--- /dev/null
@@ -0,0 +1,30 @@
+/* help.c - Show help for toybox commands
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Often a shell builtin.
+
+USE_HELP(NEWTOY(help, "<1", TOYFLAG_BIN))
+
+config HELP
+  bool "help"
+  default y
+  depends on TOYBOX_HELP
+  help
+    usage: help [command]
+
+    Show usage information for toybox commands.
+    Run "toybox" with no arguments for a list of available commands.
+*/
+
+
+#include "toys.h"
+
+void help_main(void)
+{
+  struct toy_list *t = toy_find(*toys.optargs);
+
+  if (!t) error_exit("Unknown command '%s'", *toys.optargs);
+  toys.which = t;
+  show_help();
+}
diff --git a/toys/other/hexdump.c b/toys/other/hexdump.c
new file mode 100644 (file)
index 0000000..a2dc643
--- /dev/null
@@ -0,0 +1,1049 @@
+/* hexdump.c - A program to dump any file into hexadecimal, octal, decimal or character format.
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+
+USE_HEXDUMP(NEWTOY(hexdump, "RbCcde:f:n#<0os:vx", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HEXDUMP
+       bool "hexdump"
+       default y 
+       help
+
+         usage: hexdump [-bCcdovxR] [-e format_string] [-f format_file] [-n length] [-s skip] [file ...]
+
+         A program to dump any file into hexadecimal, octal, decimal or character format.
+
+         options:
+               -b                One-byte octal display
+               -C                Canonical hex+ASCII, 16 bytes per line
+               -c                One-byte character display
+               -d                Two-byte decimal display
+               -e format_string  Specify a format string to be used for displaying data
+               -f format_file    Specify a file that contains one or more newline separated format strings
+               -n length         Interpret only length bytes of input
+               -o                Two-byte octal display
+               -s OFFSET         Skip OFFSET bytes from the beginning of the input
+               -v                Display all input data
+               -x                Two-byte hexadecimal display
+               -R                Reverse of 'hexdump -Cv'
+
+*/
+
+/*
+ * Copyright (c) 1989, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define FOR_hexdump
+#include "toys.h"
+
+#define F_IGNORE        0x01            /* %_A */
+#define F_SETREP        0x02            /* rep count set, not default */
+
+#define F_ADDRESS       0x001           /* print offset */
+#define F_BPAD          0x002           /* blank pad */
+#define F_C             0x004           /* %_c */
+#define F_CHAR          0x008           /* %c */
+#define F_DBL           0x010           /* %[EefGf] */
+#define F_INT           0x020           /* %[di] */
+#define F_P             0x040           /* %_p */
+#define F_STR           0x080           /* %s */
+#define F_U             0x100           /* %_u */
+#define F_UINT          0x200           /* %[ouXx] */
+#define F_TEXT          0x400           /* no conversions */
+
+#ifndef MIN     
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+typedef struct _PR {
+  struct _PR *nextpr;             /* next print unit */
+  unsigned flags;                 /* flag values */
+  int bcnt;                       /* byte count */
+  char *cchar;                    /* conversion character */
+  char *fmt;                      /* printf format */
+  char *nospace;                  /* no whitespace version */
+} PR;
+
+typedef struct _FU {
+  struct _FU *nextfu;             /* next format unit */
+  struct _PR *nextpr;             /* next print unit */
+  unsigned flags;                 /* flag values */
+  int reps;                       /* repetition count */
+  int bcnt;                       /* byte count */
+  char *fmt;                      /* format string */
+} FU;
+
+typedef struct _FS {                    /* format strings */
+  struct _FS *nextfs;             /* linked list of format strings */
+  struct _FU *nextfu;             /* linked list of format units */
+  int bcnt;
+} FS;
+
+enum dump_vflag { ALL, DUP, FIRST, WAIT };  /* -v values */
+
+FU *endfu;                             /* format at end-of-data */
+enum dump_vflag v_flag = FIRST;
+static off_t address;                   /* address/offset in stream */
+static off_t eaddress;                  /* end address */
+static char **_argv;
+
+FS *fshead;                             /* head of format strings */  
+int blocksize;                          /* data block size */         
+int length = -1;                        /* max bytes to read */
+off_t skip;                             /* bytes to skip */
+
+static const char *index_str = ".#-+ 0123456789";
+
+static const char conv_str[]=
+       "\0\\0\0"
+       "\007\\a\0"     /* \a */
+       "\b\\b\0"
+       "\f\\f\0"
+       "\n\\n\0"
+       "\r\\r\0"
+       "\t\\t\0"
+       "\v\\v\0"
+       ;
+
+static void add(const char *fmt)
+{
+  const char *p;
+  static FS **nextfs;
+  FS *tfs;
+  FU *tfu, **nextfu;
+  const char *savep;
+  char *p1, *p2;
+
+  /* start new linked list of format units */
+  tfs = xzalloc(sizeof(FS));
+  if (!fshead) fshead = tfs;
+  else *nextfs = tfs;
+  nextfs = &tfs->nextfs;
+  nextfu = &tfs->nextfu;
+
+  /* take the format string and break it up into format units */
+  p = fmt;
+  for (;;) {
+    /* skip leading white space */
+    for (; isspace(*p); ++p);
+    if (!*p) break;
+
+    /* allocate a new format unit and link it in */
+    tfu = xzalloc(sizeof(FU));
+    *nextfu = tfu;
+    nextfu = &tfu->nextfu;
+    tfu->reps = 1;
+
+    /* if leading digit, repetition count */
+    if (isdigit(*p)) {
+      for (savep = p; isdigit(*p); ++p);
+      if (!isspace(*p) && *p != '/')
+        error_exit("\"%s\": bad format", fmt);
+      /* may overwrite either white space or slash */
+      tfu->reps = atoi(savep);
+      tfu->flags = F_SETREP;
+      /* skip trailing white space */
+      for (++p; isspace(*p); ++p);
+    }
+
+    /* skip slash and trailing white space */
+    if (*p == '/')
+      while (isspace(*++p));
+
+    /* byte count */
+    if (isdigit(*p)) {
+      for (savep = p; isdigit(*p); ++p);
+      if (!isspace(*p))        error_exit("\"%s\": bad format", fmt);
+      tfu->bcnt = atoi(savep);
+      /* skip trailing white space */
+      for (++p; isspace(*p); ++p);
+    }
+
+    /* format */
+    if (*p != '"') error_exit("\"%s\": bad format", fmt);
+    for (savep = ++p; *p != '"';)
+      if (*p++ == 0) error_exit("\"%s\": bad format", fmt);
+    tfu->fmt = xstrndup((char *)savep, p - savep);
+
+    /*escape(tfu->fmt)*/
+
+    p1 = tfu->fmt;
+
+    /* alphabetic escape sequences have to be done in place */
+    for (p2 = p1;; ++p1, ++p2) {
+      if (*p1 == '\0') {
+        *p2 = *p1;
+        break;
+      }
+      if (*p1 == '\\') {
+        const char *str = conv_str + 4;
+        ++p1;
+        *p2 = *p1;
+        do {
+          if (*p1 == str[2]) {
+            *p2 = str[0];
+            break;
+          }
+          str += 4;
+        } while (*str);
+      }
+    }
+    p++;
+  }
+}
+
+static void addfile(char *name)
+{
+       char *p;
+       FILE *fp;
+       int ch;
+       char buf[2048 + 1];
+
+       fp = xfopen(name, "r");
+       while (fgets(buf, sizeof(buf), fp)) {
+               if (!(p = strchr(buf, '\n'))) {
+                       perror_msg("line too long.");
+                       while ((ch = getchar()) != '\n' && ch != EOF);    /*take input for further process*/
+                       continue;
+               }
+               *p = '\0';
+               for (p = buf; *p && isspace((unsigned char)*p); ++p);     /*skip whitespaces*/
+               if (!*p || *p == '#')
+                       continue;
+               add(p);
+       }
+       fclose(fp);
+}
+
+static int size(FS *fs)
+{
+       FU *fu;
+       int bcnt, cur_size;
+       char *fmt;
+       int prec;
+
+       /* figure out the data block size needed for each format unit */
+       for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+               if (fu->bcnt) {
+                       cur_size += fu->bcnt * fu->reps;
+                       continue;
+               }
+    for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+      if (*fmt != '%') continue;
+      /*
+       * skip any special chars -- save precision in
+       * case it's a %s format.
+       */
+      while (strchr(index_str + 1, *++fmt));
+      if (*fmt == '.' && isdigit(*++fmt)) {
+        prec = atoi(fmt);
+        while (isdigit(*++fmt));
+      }
+      switch(*fmt) {
+        case 'c':
+          bcnt += 1;
+          break;
+        case 'd': case 'i': case 'o': case 'u':
+        case 'x': case 'X':
+          bcnt += 4;
+          break;
+        case 'e': case 'E': case 'f': case 'g': case 'G':
+          bcnt += 8;
+          break;
+        case 's':
+          bcnt += prec;
+          break;
+        case '_':
+          switch(*++fmt) {
+            case 'c': case 'p': case 'u':
+              bcnt += 1;
+              break;
+          }
+      }
+    }
+               cur_size += bcnt * fu->reps;
+       }
+       return cur_size;
+}
+
+static void rewrite(FS *fs)
+{
+  enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+  PR *pr, **nextpr;
+  FU *fu;
+  char *p1, *p2;
+  char savech, *fmtp, cs[sizeof(PRId64)];
+  int nconv, prec=0;
+
+  for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+    /*
+     * Break each format unit into print units; each conversion
+     * character gets its own.
+     */
+    nextpr = &fu->nextpr;
+    for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+      pr = xzalloc(sizeof(*pr));
+      *nextpr = pr;
+
+      /* Skip preceding text and up to the next % sign. */
+      for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
+
+      /* Only text in the string. */
+      if (!*p1) {
+        pr->fmt = fmtp;
+        pr->flags = F_TEXT;
+        break;
+      }
+
+      /*
+       * Get precision for %s -- if have a byte count, don't
+       * need it.
+       */
+      if (fu->bcnt) {
+        sokay = USEBCNT;
+        /* Skip to conversion character. */
+        for (++p1; *p1 && strchr(index_str, *p1); ++p1);
+      } else {
+        /* Skip any special chars, field width. */
+        while (*++p1 && strchr(index_str + 1, *p1));
+        if (*p1 == '.' && isdigit(*++p1)) {
+          sokay = USEPREC;
+          prec = atoi(p1);
+          while (isdigit(*++p1))
+            continue;
+        } 
+        else sokay = NOTOKAY;
+      }
+
+      p2 = *p1 ? p1 + 1 : p1; /* Set end pointer. */
+      cs[0] = *p1;            /* Set conversion string. */
+      cs[1] = '\0';
+
+      /*
+       * Figure out the byte count for each conversion;
+       * rewrite the format as necessary, set up blank-
+       * padding for end of data.
+       */
+      switch(cs[0]) {
+        case 'c':
+          pr->flags = F_CHAR;
+          switch(fu->bcnt) {
+            case 0: case 1:
+              pr->bcnt = 1;
+              break;
+            default:
+              p1[1] = '\0';
+              error_exit("\"%s\": bad byte count", p1);
+          }
+          break;
+        case 'd': case 'i':
+          pr->flags = F_INT;
+          goto isint;
+        case 'o': case 'u': case 'x': case 'X':
+          pr->flags = F_UINT;
+isint:
+          /* Regardless of pr->bcnt, all integer
+           * values are cast to [u]int64_t before
+           * being printed by display().  We
+           * therefore need to use PRI?64 as the
+           * format, where '?' could actually
+           * be any of [diouxX].  We make the
+           * assumption (not guaranteed by the
+           * C99 standard) that we can derive
+           * all the other PRI?64 values from
+           * PRId64 simply by changing the last
+           * character.  For example, if PRId64 is
+           * "lld" or "qd", and cs[0] is 'o', then
+           * we end up with "llo" or "qo".
+           */
+          savech = cs[0];
+          strncpy(cs, PRId64, sizeof(PRId64) - 2);
+          cs[sizeof(PRId64) - 2] = savech;
+          cs[sizeof(PRId64) - 1] = '\0';
+          switch(fu->bcnt) {
+            case 0: case 4:
+              pr->bcnt = 4;
+              break;
+            case 1:
+              pr->bcnt = 1;
+              break;
+            case 2:
+              pr->bcnt = 2;
+              break;
+            case 8:
+              pr->bcnt = 8;
+              break;
+            default:
+              p1[1] = '\0';
+              error_exit("\"%s\": bad byte count", p1);
+          }
+          break;
+        case 'e': case 'E': case 'f': case 'g': case 'G':
+          pr->flags = F_DBL;
+          switch(fu->bcnt) {
+            case 0: case 8:
+              pr->bcnt = 8;
+              break;
+            case 4:
+              pr->bcnt = 4;
+              break;
+            default:
+              p1[1] = '\0';
+              error_exit("\"%s\": bad byte count", p1);
+          }
+          break;
+        case 's':
+          pr->flags = F_STR;
+          switch(sokay) {
+            case NOTOKAY:
+              error_exit("%%s: requires a precision or a byte count");
+            case USEBCNT:
+              pr->bcnt = fu->bcnt;
+              break;
+            case USEPREC:
+              pr->bcnt = prec;
+              break;
+          }
+          break;
+        case '_':
+          ++p2;
+          switch(p1[1]) {
+            case 'A':
+              endfu = fu;
+              fu->flags |= F_IGNORE;
+              /* FALLTHROUGH */
+            case 'a':
+              pr->flags = F_ADDRESS;
+              ++p2;
+              switch(p1[2]) {
+                case 'd': case 'o': case'x':
+                  /*
+                   * See comments above for
+                   * the way we use PRId64.
+                   */
+                  strncpy(cs, PRId64,
+                      sizeof(PRId64) - 2);
+                  cs[sizeof(PRId64) - 2] = p1[2];
+                  cs[sizeof(PRId64) - 1] = '\0';
+                  break;
+                default:
+                  p1[3] = '\0';
+                  error_exit("%%%s: bad conversion character", p1);
+              }
+              break;
+            case 'c':
+              pr->flags = F_C;
+              /* cs[0] = 'c'; set in conv_c */
+              goto isint2;
+            case 'p':
+              pr->flags = F_P;
+              cs[0] = 'c';
+              goto isint2;
+            case 'u':
+              pr->flags = F_U;
+              /* cs[0] = 'c'; set in conv_u */
+isint2:
+              switch(fu->bcnt) {
+                case 0: case 1:
+                  pr->bcnt = 1;
+                  break;
+                default:
+                  p1[2] = '\0';
+                  error_exit("%s: bad byte count", p1);
+              }
+              break;
+            default:
+              p1[2] = '\0';
+              error_exit("%%%s: bad conversion character", p1);
+          }
+          break;
+        default:
+          p1[1] = '\0';
+          error_exit("%%%s: bad conversion character", p1);
+      }
+
+      /*
+       * Copy to PR format string, set conversion character
+       * pointer, update original.
+       */
+      savech = *p2;
+      p1[0] = '\0';
+      pr->fmt = xmalloc(strlen(fmtp) + strlen(cs) + 1);
+      strcpy(pr->fmt, fmtp);
+      strcat(pr->fmt, cs);
+      *p2 = savech;
+      pr->cchar = pr->fmt + (p1 - fmtp);
+      fmtp = p2;
+
+      /* Only one conversion character if byte count. */
+      if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++)
+        error_exit("byte count with multiple conversion characters");
+    }
+    /*
+     * If format unit byte count not specified, figure it out
+     * so can adjust rep count later.
+     */
+    if (!fu->bcnt)
+      for (pr = fu->nextpr; pr; pr = pr->nextpr)
+        fu->bcnt += pr->bcnt;
+  }
+  /*
+   * If the format string interprets any data at all, and it's
+   * not the same as the blocksize, and its last format unit
+   * interprets any data at all, and has no iteration count,
+   * repeat it as necessary.
+   *
+   * If, rep count is greater than 1, no trailing whitespace
+   * gets output from the last iteration of the format unit.
+   */
+  for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+    if (!fu->nextfu && fs->bcnt < blocksize && !(fu->flags&F_SETREP) && fu->bcnt)
+      fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
+    if (fu->reps > 1) {
+      if (!fu->nextpr) break;
+      for (pr = fu->nextpr;; pr = pr->nextpr)
+        if (!pr->nextpr) break;
+      for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+        p2 = isspace(*p1) ? p1 : NULL;
+      if (p2) pr->nospace = p2;
+    }
+  }
+}
+
+static void bpad(PR *pr)
+{       
+  char *p1, *p2;
+
+  /*
+   * Remove all conversion flags; '-' is the only one valid
+   * with %s, and it's not useful here.
+   */
+  pr->flags = F_BPAD;
+  pr->cchar[0] = 's';
+  pr->cchar[1] = '\0';
+  for (p1 = pr->fmt; *p1 != '%'; ++p1);
+  for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) {
+    if (pr->nospace) pr->nospace--;
+  }
+  while ((*p2++ = *p1++) != '\0');
+}
+
+static void conv_c(PR *pr, unsigned char *p)
+{
+       const char *str = conv_str;
+       char buf[10];
+
+       do {
+               if (*p == *str) {
+                       ++str;
+                       goto strpr;
+               }
+               str += 4;
+       }while(*str);
+
+       if (isprint(*p)) {
+               *pr->cchar = 'c';
+               printf(pr->fmt, *p);
+       } else {
+               sprintf(buf, "%03o", (int) *p);
+               str = buf;
+strpr:
+    *pr->cchar = 's';
+               printf(pr->fmt, str);
+       }
+}
+
+static void conv_u(PR *pr, u_char *p)
+{
+  static const char *list[] = {
+    "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+    "bs",  "ht",  "lf",  "vt",  "ff",  "cr",  "so",  "si",
+    "dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+    "can",  "em", "sub", "esc",  "fs",  "gs",  "rs",  "us",
+  };
+
+  if (*p <= 0x1f) {
+    *pr->cchar = 's';
+    printf(pr->fmt, list[*p]);
+  } else if (*p == 0x7f) {
+    *pr->cchar = 's';
+    printf(pr->fmt, "del");
+  } else if (isprint(*p)) {
+    *pr->cchar = 'c';
+    printf(pr->fmt, *p);
+  } else {
+    *pr->cchar = 'x';
+    printf(pr->fmt, (int)*p);
+  }
+}
+
+static void doskip(const char *fname, int statok)
+{
+  int cnt;
+  struct stat sbuf;
+
+  memset(&sbuf, 0, sizeof( struct stat));
+  if (statok) {
+    if (fstat(fileno(stdin), &sbuf))
+      perror_exit("fstat %s", fname);
+    if (S_ISREG(sbuf.st_mode) && skip > sbuf.st_size) {
+      skip -= sbuf.st_size;
+      address += sbuf.st_size;
+      return;
+    }
+  }
+  if (S_ISREG(sbuf.st_mode)) {
+    if (fseek(stdin, skip, SEEK_SET))
+      perror_exit("fseek %s", fname);
+    address += skip;
+    skip = 0;
+  } else {
+    for (cnt = 0; cnt < skip; ++cnt)
+      if (getchar() == EOF)
+        break;
+    address += cnt;
+    skip -= cnt;
+  }
+}
+
+static int next(char **argv)
+{
+  static int done;
+  int statok;
+
+  if (argv) {
+    _argv = argv;
+    return(1);
+  }
+  for (;;) {
+    if (*_argv) {
+      if (!(freopen(*_argv, "r", stdin))) {
+        perror_msg("%s", *_argv);
+        ++_argv;
+        continue;
+      }
+      statok = done = 1;
+    } else {
+      if (done++)
+        return(0); /* no next file */
+      statok = 0;
+    }
+    if (skip)
+      doskip(statok ? *_argv : "stdin", statok);
+    if (*_argv)
+      ++_argv;
+    if (!skip)
+      return(1);
+  }
+  /* NOTREACHED */
+}
+
+static unsigned char *get(void)
+{
+  static int ateof = 1;
+  static unsigned char *curp, *savp;
+  int n;
+  int need, nread;
+  unsigned char *tmp;
+
+  if (!curp) {
+    curp = xmalloc(blocksize);
+    savp = xzalloc(blocksize);
+  } else {
+    tmp = curp;
+    curp = savp;
+    savp = tmp;
+    address += blocksize;
+  }
+  need = blocksize;
+  nread = 0;
+  for (;;) {
+    /*
+     * if read the right number of bytes, or at EOF for one file,
+     * and no other files are available, zero-pad the rest of the
+     * block and set the end flag.
+     */
+    if (!length || (ateof && !next(NULL))) {
+      if (need == blocksize) return NULL;
+      if (!need && v_flag != ALL && !memcmp(curp, savp, nread)) {
+        if (v_flag != DUP)
+          printf("*\n");
+        return NULL;
+      }
+      memset((char *)curp + nread, 0, need);
+      eaddress = address + nread;
+      return curp;
+    }
+    n = fread((char *)curp + nread, sizeof(unsigned char),length == -1 ? need : MIN(length, need), stdin);
+    if (!n) {
+      if (ferror(stdin)) perror_msg("%s", _argv[-1]);
+      ateof = 1;
+      continue;
+    }
+    ateof = 0;
+    if (length != -1)  length -= n;
+    need -= n;
+    if (!need) {
+      if (v_flag == ALL || v_flag == FIRST || memcmp(curp, savp, blocksize)) {
+        if (v_flag == DUP || v_flag == FIRST)
+          v_flag = WAIT;
+        return curp;
+      }
+      if (v_flag == WAIT)      printf("*\n");
+      v_flag = DUP;
+      address += blocksize;
+      need = blocksize;
+      nread = 0;
+    }
+    else nread += n;
+  }
+}
+
+static void display(void)
+{
+  FS *fs;
+  FU *fu;
+  PR *pr;
+  int cnt;
+  unsigned char *bp, *savebp;
+  off_t saveaddress;
+  unsigned char savech = '\0';
+
+  while ((bp = get())) {
+    for (fs = fshead, savebp = bp, saveaddress = address; fs; fs = fs->nextfs, bp = savebp, address = saveaddress) {
+      for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+        if (fu->flags & F_IGNORE)      break;
+
+        for (cnt = fu->reps; cnt; --cnt) {
+          for (pr = fu->nextpr; pr; address += pr->bcnt, bp += pr->bcnt, pr = pr->nextpr) {
+            if (eaddress && address >= eaddress && !(pr->flags & (F_TEXT|F_BPAD)))
+              bpad(pr);
+            if (cnt == 1 && pr->nospace) {
+              savech = *pr->nospace;
+              *pr->nospace = '\0';
+            }
+            /* PRINT */
+            switch(pr->flags) {
+              case F_ADDRESS:
+                printf(pr->fmt, (int64_t)address);
+                break;
+              case F_BPAD:
+                printf(pr->fmt, "");
+                break;
+              case F_C:
+                conv_c(pr, bp);
+                break;
+              case F_CHAR:
+                printf(pr->fmt, *bp);
+                break;
+              case F_DBL:
+                {
+                  double f8;
+                  float f4;
+                  switch(pr->bcnt) {
+                    case 4:
+                      memcpy(&f4, bp, sizeof(f4));
+                      printf(pr->fmt, f4);
+                      break;
+                    case 8:
+                      memcpy(&f8, bp, sizeof(f8));
+                      printf(pr->fmt, f8);
+                      break;
+                  }
+                  break;
+                }
+              case F_INT:
+                {
+                  int16_t s2;
+                  int32_t s4;
+                  int64_t s8;
+                  switch(pr->bcnt) {
+                    case 1:
+                      printf(pr->fmt, (int64_t)*bp);
+                      break;
+                    case 2:
+                      memcpy(&s2, bp, sizeof(s2));
+                      printf(pr->fmt, (int64_t)s2);
+                      break;
+                    case 4:
+                      memcpy(&s4, bp, sizeof(s4));
+                      printf(pr->fmt, (int64_t)s4);
+                      break;
+                    case 8:
+                      memcpy(&s8, bp, sizeof(s8));
+                      printf(pr->fmt, (int64_t)s8);
+                      break;
+                  }
+                  break;
+                }
+              case F_P:
+                printf(pr->fmt, isprint(*bp) ? *bp : '.');
+                break;
+              case F_STR:
+                printf(pr->fmt, (char *)bp);
+                break;
+              case F_TEXT:
+                printf("%s", pr->fmt);
+                break;
+              case F_U:
+                conv_u(pr, bp);
+                break;
+              case F_UINT: 
+                {
+                  uint16_t u2;
+                  uint32_t u4;
+                  uint64_t u8;
+                  switch(pr->bcnt) {
+                    case 1:
+                      printf(pr->fmt, (uint64_t)*bp);
+                      break;
+                    case 2:
+                      memcpy(&u2, bp, sizeof(u2));
+                      printf(pr->fmt, (uint64_t)u2);
+                      break;
+                    case 4:
+                      memcpy(&u4, bp, sizeof(u4));
+                      printf(pr->fmt, (uint64_t)u4);
+                      break;
+                    case 8:
+                      memcpy(&u8, bp, sizeof(u8));
+                      printf(pr->fmt, (uint64_t)u8);
+                      break;
+                  }
+                  break;
+                }
+            }
+
+            if (cnt == 1 && pr->nospace)
+              *pr->nospace = savech;
+          }
+        }
+      }
+    }
+  }
+  if (endfu) {
+    /*
+     * If eaddress not set, error or file size was multiple of
+     * blocksize, and no partial block ever found.
+     */
+    if (!eaddress) {
+      if (!address) return;
+      eaddress = address;
+    }
+    for (pr = endfu->nextpr; pr; pr = pr->nextpr) {
+      switch(pr->flags) {
+        case F_ADDRESS:
+          printf(pr->fmt, (int64_t)eaddress);
+          break;
+        case F_TEXT:
+          printf("%s", pr->fmt);
+          break;
+      }
+    }
+  }
+}
+
+static int strtol_range(char *str, long min, long max)
+{
+  char *endptr = NULL;
+  errno = 0;
+  long ret_value = strtol(str, &endptr, 10);
+
+  if(errno) perror_exit("Invalid num %s", str);
+  else if(endptr && (*endptr != '\0' || endptr == str))
+    perror_exit("Invalid num %s", str);
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else perror_exit("Number %s is not in valid [%d-%d] Range\n", str, min, max);
+}
+
+static const char *const add_formats[] = {                            
+       "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"",   /* b */             
+       "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"",   /* c */             
+       "\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\"",  /* d */             
+       "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"",   /* o */             
+       "\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\"", /* x */             
+};
+
+/*  
+ * free all the list nodes on error OR on exit
+ */
+static void free_list(FS *head)                                                                                                                                 
+{
+  FS *tfs = NULL;
+  if (!head) return;
+  tfs = head;
+  while(tfs) {
+    FU *tmp = tfs->nextfu, *tmp1 = tfs->nextfu;
+    if (!tmp) continue;
+    while (tmp1) {
+      tmp = tmp->nextfu;
+      free(tmp1);
+      tmp1 = tmp;
+    }
+    head = head->nextfs;
+    free(tfs);
+    tfs = head;
+  }
+}
+
+static char* get_myline(fd)
+{
+  char c, *buf = NULL;
+  long len = 0;
+
+  for (;;) {
+    if (1>read(fd, &c, 1)) break;
+    if (!(len & 63)) buf=xrealloc(buf, len+65);
+    if (((buf[len++]=c) == '\n') || (c == '\0')) break;
+  }    
+  if (buf) buf[len]=0;
+  if (buf && buf[--len]=='\n') buf[len]=0;
+
+  return buf; 
+}
+
+static const char option_str[]="bcdoxCe:f:n:s:vR";    
+
+void hexdump_main(void)
+{
+  FS *tfs;
+  int ch, r_flag = 0;
+  char *p, **pt;
+  int argc = 0;
+
+  pt = toys.argv;
+  while(*pt++) argc++;
+
+  while ((ch = getopt(argc, toys.argv, option_str)) != -1) {
+    switch (ch) {
+      case 'b': case 'c': case 'd': case 'o': case 'x':
+        p = strchr(option_str, ch);
+        add("\"%07.7_Ax\n\"");
+        add(add_formats[(int)(p-option_str)]);
+        break;
+      case 'C':
+        add("\"%08.8_Ax\n\"");
+        add("\"%08.8_ax  \" 8/1 \"%02x \" \"  \" 8/1 \"%02x \" ");
+        add("\"  |\" 16/1 \"%_p\" \"|\\n\"");
+        break;
+      case 'e':
+        add(optarg);
+        break;
+      case 'f':
+        addfile(optarg);
+        break;
+      case 'n':
+        length = strtol_range(optarg, 0, INT_MAX);
+        break;
+      case 's':
+        errno = 0;
+        if (((skip = strtol(optarg, &p, 0)) < 0) 
+            || errno)
+          error_exit("invalid number '%s'", optarg);
+        if(*p && !*(p+1))
+          switch(*p) {
+            case 'b':
+              skip *= 512;
+              break;
+            case 'k':
+              skip *= 1024;
+              break;
+            case 'm':
+              skip *= 1048576;
+              break;
+            default:
+              error_exit("invalid number '%s'", optarg);
+          }
+        else if(*p && *(p+1))
+          error_exit("invalid number '%s'", optarg);
+        if(skip > INT_MAX || skip < 0)
+          error_exit("number %s is not in 0..%ld range", optarg, INT_MAX);
+        break;
+      case 'v':
+        v_flag = ALL;
+        break;
+      case 'R':
+        r_flag = 1;
+        break;
+
+      default:
+        fprintf(stdout, "usage: %s ", toys.which->name);
+        fprintf(stdout, "[-bCcdovx] [-e format_string] [-f format_file] [-n length] [-s skip] [file ...]\n");
+        exit(EXIT_FAILURE);
+    }
+  }
+
+  if (!fshead) {
+    add("\"%07.7_Ax\n\"");
+    add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+  }
+
+  if (r_flag) {
+    /* -R: reverse of 'hexdump -Cv' */
+    int fd = -1;
+    fd = STDIN_FILENO;
+    if (!*toys.optargs) {
+      toys.optargs--;
+      goto start_print;
+    }
+
+    do {
+      char *buf;
+      fd = xopen(*toys.optargs, O_RDONLY);
+start_print:
+      while ((buf = get_myline(fd)) != NULL) {
+        p = buf;
+        while (1) {
+          /* skip address or previous byte */
+          while (isxdigit(*p)) p++;
+          while (*p == ' ') p++;
+          /* '|' char will break the line */
+          if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1)
+            break;
+          xputc(ch);
+        }
+        free(buf);
+      }
+      close(fd);
+    } while (*++toys.optargs);
+  } else {
+    /* figure out the data block size */
+    for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) {
+      tfs->bcnt = size(tfs);
+      if (blocksize < tfs->bcnt) blocksize = tfs->bcnt;
+    }
+    /* rewrite the rules, do syntax checking */
+    for (tfs = fshead; tfs; tfs = tfs->nextfs)
+      rewrite(tfs);
+
+    next(toys.optargs);
+    display();
+  }
+  if (CFG_TOYBOX_FREE) free_list(fshead);
+}
diff --git a/toys/other/hostid.c b/toys/other/hostid.c
new file mode 100644 (file)
index 0000000..3b73fc0
--- /dev/null
@@ -0,0 +1,26 @@
+/* hostid.c - Print the numeric identifier for the current host.
+ *
+ * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_HOSTID(NEWTOY(hostid, ">0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HOSTID
+  bool "hostid"
+  default y
+  help
+    usage: hostid
+
+    Print the numeric identifier for the current host.
+*/
+
+#include "toys.h"
+
+/*
+ * host id main function.
+ */
+void hostid_main(void)
+{
+  xprintf("%08lx\n", gethostid());
+}
diff --git a/toys/other/ifconfig.c b/toys/other/ifconfig.c
new file mode 100644 (file)
index 0000000..190d801
--- /dev/null
@@ -0,0 +1,1196 @@
+/* ifconfig.c - Configure network interface.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+USE_IFCONFIG(NEWTOY(ifconfig, "?a", TOYFLAG_BIN))
+config IFCONFIG
+  bool "ifconfig"
+  default y
+  help
+    usage: ifconfig [-a] interface [address]
+
+    Configure network interface.
+
+    [add ADDRESS[/PREFIXLEN]]
+    [del ADDRESS[/PREFIXLEN]]
+    [[-]broadcast [ADDRESS]] [[-]pointopoint [ADDRESS]]
+    [netmask ADDRESS] [dstaddr ADDRESS]
+    [outfill NN] [keepalive NN]
+    [hw ether|infiniband ADDRESS] [metric NN] [mtu NN]
+    [[-]trailers] [[-]arp] [[-]allmulti]
+    [multicast] [[-]promisc] [txqueuelen NN] [[-]dynamic]
+    [mem_start NN] [io_addr NN] [irq NN]
+    [up|down] ...    
+*/
+
+#define FOR_ifconfig
+#include "toys.h"
+#include "ifconfig.h"
+
+
+IFACE_LIST *iface_list_head;
+
+#ifndef MAX_PORT_VALUE
+#define MAX_PORT_VALUE 65535
+#endif
+
+typedef struct sockaddr_with_len {
+  union {
+    struct sockaddr sock;
+    struct sockaddr_in sock_in;
+    struct sockaddr_in6 sock_in6;
+  }sock_u;
+  socklen_t socklen;
+} sockaddr_with_len;
+
+sockaddr_with_len *swl;
+
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, const char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+/*
+ * Remove white spaces from the given string.
+ */
+static char *omit_whitespace(char *s)
+{
+  while(*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) s++;
+  return (char *) s;
+}
+/*
+ * used to converts string into int and validate the input str for invalid int value or out-of-range.
+ */
+static unsigned get_strtou(const char *str, char **endp, int base)
+{
+  unsigned long uli;
+  char *endptr;
+
+  if(!isalnum(str[0])) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+  errno = 0;
+  uli = strtoul(str, &endptr, base);
+  if(uli > UINT_MAX) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+
+  if(endp) *endp = endptr;
+  if(endptr[0]) {
+    if(isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
+      errno = ERANGE;
+      return UINT_MAX;
+    }
+    errno = EINVAL;
+  }
+  return uli;
+}
+
+/*
+ * verify the host is local unix path.
+ * if so, set the swl input param accordingly.
+ */
+static int is_host_unix(const char *host, sockaddr_with_len **swl)
+{
+  if(strncmp(host, "local:", 6) == 0) {
+    struct sockaddr_un *sockun;
+    *swl = xzalloc(sizeof(struct sockaddr_with_len));
+    (*swl)->socklen = sizeof(struct sockaddr_un);
+    (*swl)->sock_u.sock.sa_family = AF_UNIX;
+    sockun = (struct sockaddr_un *)&(*swl)->sock_u.sock;
+    safe_strncpy(sockun->sun_path, host + 6, sizeof(sockun->sun_path));
+    return 1;
+  }
+  return 0;
+}
+
+/*
+ * validate the input param (host) for valid ipv6 ip and extract port number (if there).
+ */
+static void get_host_and_port(char **host, int *port)
+{
+  char *ch_ptr;
+  const char *org_host = *host;
+  if(*host[0] == '[') {
+    (*host)++;
+    ch_ptr = strchr(*host, ']');
+    if(!ch_ptr || (ch_ptr[1] != ':' && ch_ptr[1] != '\0'))
+      error_exit("bad address '%s'", org_host);
+  }
+  else {
+    ch_ptr = strrchr(*host, ':');
+    //There is more than one ':' like "::1"
+    if(ch_ptr && strchr(*host, ':') != ch_ptr)
+      ch_ptr = NULL;
+  }
+  if(ch_ptr) { //pointer to ":" or "]:"
+    int size = ch_ptr - (*host) + 1;
+    safe_strncpy(*host, *host, size);
+    if(*ch_ptr != ':') {
+      ch_ptr++; //skip ']'
+      //[nn] without port
+      if(*ch_ptr == '\0')
+        return;
+    }
+    ch_ptr++; //skip ':' to get the port number.
+    *port = get_strtou(ch_ptr, NULL, 10);
+    if(errno || (unsigned)*port > MAX_PORT_VALUE)
+      error_exit("bad port spec '%s'", org_host);
+   }
+  return;
+}
+
+/*
+ * used to extract the address info from the given host ip
+ * and update the swl param accordingly.
+ */
+static int get_socket_stream(const char *host, sa_family_t af, sockaddr_with_len **swl)
+{
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
+  int status = 0;
+
+  memset(&hints, 0 , sizeof(struct addrinfo));
+  hints.ai_family = af;
+  hints.ai_socktype = SOCK_STREAM;
+
+  if((status = getaddrinfo(host, NULL, &hints, &result)) != 0) {
+    perror_exit("bad address '%s' : %s", host, gai_strerror(status));
+    return status;
+  }
+
+  for(rp = result; rp != NULL; rp = rp->ai_next) {
+    if( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) {
+      *swl = xmalloc(sizeof(struct sockaddr_with_len));
+      (*swl)->socklen = rp->ai_addrlen;
+      memcpy(&((*swl)->sock_u.sock), rp->ai_addr, rp->ai_addrlen);
+      break;
+    }
+  }
+  freeaddrinfo(result);
+  return ((!rp)? -1: status);
+}
+
+/*
+ * used to set the port number for ipv4 / ipv6 addresses.
+ */
+static void setport(struct sockaddr *sock, unsigned port_num)
+{
+  //for ipv4
+  if(sock->sa_family == AF_INET) {
+    struct sockaddr_in *sock_in = (void*)sock;
+    sock_in->sin_port = port_num;
+  }
+  //for ipv6
+  else if(sock->sa_family == AF_INET6) {
+    struct sockaddr_in6 *sock_in6 = (void*)sock;
+    sock_in6->sin6_port = port_num;
+  }
+  return;
+}
+
+/*
+ * use to get the socket address with the given host ip.
+ */
+static sockaddr_with_len *get_sockaddr(const char *host, int port, sa_family_t af)
+{
+  sockaddr_with_len *swl = NULL;
+  int status = 0;
+
+  //for unix
+  int is_unix = is_host_unix(host, &swl);
+  if(is_unix && swl) return swl;
+
+  //[IPV6_ip]:port_num
+  if(host[0] == '[' || strrchr(host, ':')) get_host_and_port((char **)&host, &port);
+
+  //for the socket streams.
+  status = get_socket_stream(host, af, &swl);
+  if(status) return NULL;
+
+  setport(&swl->sock_u.sock, htons(port));
+  return swl;
+}
+
+/*
+ * display help info and exit from application.
+ */
+static void show_ifconfig_help(void)
+{
+  toys.exithelp++;
+  error_exit("Invalid argument");
+}
+
+void ifconfig_main(void)
+{
+  char **argv = toys.optargs;
+
+  if(argv[0] && (strcmp(argv[0], "--help") == 0)) {
+    show_help();
+    exit(1);
+  }
+  
+  //"ifconfig" / "ifconfig eth0"
+  if(!argv[0] || !argv[1]) { //one or no argument
+    toys.exitval = show_iface(argv[0]);
+    //free allocated memory.
+    clear_list();
+    return;
+  }
+
+  //set ifconfig params.
+  {
+    struct ifreq ifre;
+    int sockfd = 0;
+    //get interface name
+    memset(&ifre, 0, sizeof(struct ifreq));
+    strncpy(ifre.ifr_name, *argv, IFNAMSIZ);
+    ifre.ifr_name[IFNAMSIZ-1] = 0;
+    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+      perror_exit("cannot open control socket\n", 1);
+
+    while(*++argv != NULL) {
+      /* flags settings */
+      if(strcmp(argv[0], "up") == 0)
+        set_flags(sockfd, &ifre, IFF_UP | IFF_RUNNING, 0);
+      else if(strcmp(argv[0], "down") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_UP);
+
+      else if(strcmp(argv[0], "arp") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_NOARP);
+      else if(strcmp(argv[0], "-arp") == 0)
+        set_flags(sockfd, &ifre, IFF_NOARP, 0);
+      else if(strcmp(argv[0], "trailers") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_NOTRAILERS);
+      else if(strcmp(argv[0], "-trailers") == 0)
+        set_flags(sockfd, &ifre, IFF_NOTRAILERS, 0);
+
+      else if(strcmp(argv[0], "promisc") == 0)
+        set_flags(sockfd, &ifre, IFF_PROMISC, 0);
+      else if(strcmp(argv[0], "-promisc") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_PROMISC);
+      else if(strcmp(argv[0], "allmulti") == 0)
+        set_flags(sockfd, &ifre, IFF_ALLMULTI, 0);
+      else if(strcmp(argv[0], "-allmulti") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_ALLMULTI);
+      else if(strcmp(argv[0], "multicast") == 0)
+        set_flags(sockfd, &ifre, IFF_MULTICAST, 0);
+      else if(strcmp(argv[0], "-multicast") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_MULTICAST);
+      else if(strcmp(argv[0], "dynamic") == 0)
+        set_flags(sockfd, &ifre, IFF_DYNAMIC, 0);
+      else if(strcmp(argv[0], "-dynamic") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_DYNAMIC);
+      else if(strcmp(argv[0], "-pointopoint") == 0)
+        set_flags(sockfd, &ifre, 0, IFF_POINTOPOINT);
+      /*value setup */
+      else if(strcmp(argv[0], "pointopoint") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_address(sockfd, *argv, &ifre, SIOCSIFDSTADDR, "SIOCSIFDSTADDR");
+        set_flags(sockfd, &ifre, IFF_POINTOPOINT, 0);
+      }
+      else if(strcmp(argv[0], "netmask") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_address(sockfd, *argv, &ifre, SIOCSIFNETMASK, "SIOCSIFNETMASK");
+      }
+      else if(strcmp(argv[0], "-broadcast") == 0) {
+        set_flags(sockfd, &ifre, 0, IFF_BROADCAST);
+      }
+      else if(strcmp(argv[0], "broadcast") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_address(sockfd, *argv, &ifre, SIOCSIFBRDADDR, "SIOCSIFBRDADDR");
+        set_flags(sockfd, &ifre, IFF_BROADCAST, 0);
+      }
+      else if(strcmp(argv[0], "dstaddr") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_address(sockfd, *argv, &ifre, SIOCSIFDSTADDR, "SIOCSIFDSTADDR");
+      }
+      else if(strcmp(argv[0], "hw") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_hw_address(sockfd, &argv, &ifre, SIOCSIFHWADDR, "SIOCSIFHWADDR");
+      }
+      else if(strcmp(argv[0], "mtu") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_mtu(sockfd, &ifre, argv[0]);
+      }//end of mtu
+      else if(strcmp(argv[0], "metric") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_metric(sockfd, &ifre, argv[0]);
+      }//end of metric
+      else if(strcmp(argv[0], "txqueuelen") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_qlen(sockfd, &ifre, argv[0]);
+      }//end of txqueuelen
+      else if(strcmp(argv[0], "keepalive") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_data(sockfd, &ifre, argv[0], SIOCSKEEPALIVE, "SIOCSKEEPALIVE");
+      }//end of keepalive
+      else if(strcmp(argv[0], "outfill") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_data(sockfd, &ifre, argv[0], SIOCSOUTFILL, "SIOCSOUTFILL");
+      }//end of outfill
+      else if(strcmp(argv[0], "add") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_ipv6_addr(sockfd, &ifre, argv[0], SIOCSIFADDR, "SIOCSIFADDR");
+      }//end of add ipv6 addr
+      else if(strcmp(argv[0], "del") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_ipv6_addr(sockfd, &ifre, argv[0], SIOCDIFADDR, "SIOCDIFADDR");
+      }//end of del ipv6 addr
+      else if(strcmp(argv[0], "mem_start") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_memstart(sockfd, &ifre, argv[0], SIOCSIFMAP, "SIOCSIFMAP");
+      }//end of mem_start
+      else if(strcmp(argv[0], "io_addr") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_ioaddr(sockfd, &ifre, argv[0], SIOCSIFMAP, "SIOCSIFMAP");
+      }//end of io_addr
+      else if(strcmp(argv[0], "irq") == 0) {
+        if(*++argv == NULL) {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+        set_irq(sockfd, &ifre, argv[0], SIOCSIFMAP, "SIOCSIFMAP");
+      }//end of irq
+      else {
+        if(isdigit(argv[0][0]) || (strcmp(argv[0], "default") == 0)) {
+          char *iface_name = ifre.ifr_name;
+          short int is_colon = 0;
+          set_address(sockfd, *argv, &ifre, SIOCSIFADDR, "SIOCSIFADDR");
+          while(*iface_name) {
+            if(*iface_name == ':') {
+              is_colon = 1;
+              break;
+            }
+            iface_name++;
+          }
+          //if the interface name is not an alias; set the flag and continue.
+          if(!is_colon)
+            set_flags(sockfd, &ifre, IFF_UP | IFF_RUNNING, 0);
+        }
+        else if((strcmp(argv[0], "inet") == 0) || (strcmp(argv[0], "inet6") == 0))
+          continue;
+        else {
+          errno = EINVAL;
+          show_ifconfig_help();
+        }
+      }//set ip for an interface.
+
+    }//end of while.
+    if(sockfd > 0)
+      close(sockfd);
+  }
+  return;
+}//End of main function.
+
+
+static void set_flags(int sockfd, struct ifreq *ifre, int set_flag, int reset_flag)
+{
+  if(ioctl(sockfd, SIOCGIFFLAGS, ifre) < 0)
+    perror_exit("SIOCGIFFLAGS");
+  ifre->ifr_flags = (ifre->ifr_flags & (~reset_flag)) | set_flag;
+  if(ioctl(sockfd, SIOCSIFFLAGS, ifre) < 0)
+    perror_exit("SIOCSIFFLAGS");
+  return;
+}
+
+static void set_data(int sockfd, struct ifreq *ifre, char *kval, int request, const char *req_name)
+{
+  unsigned long val = strtoul(kval, NULL, 0);
+  char *ptr;
+  ptr = ((char *) ifre) + offsetof(struct ifreq, ifr_data);
+  (*(__caddr_t *)ptr) = (__caddr_t)val;
+
+  if(ioctl(sockfd, request, ifre) < 0) {
+    perror_exit((char *)req_name);
+  }
+  return;
+}
+static void set_mtu(int sockfd, struct ifreq *ifre, const char *mtu)
+{
+  ifre->ifr_mtu = strtoul(mtu, NULL, 0);
+  if(ioctl(sockfd, SIOCSIFMTU, ifre) < 0)
+    perror_exit("SIOCSIFMTU");
+  return;
+}
+
+static void set_metric(int sockfd, struct ifreq *ifre, const char *metric)
+{
+  ifre->ifr_metric = strtoul(metric, NULL, 0);
+  if(ioctl(sockfd, SIOCSIFMETRIC, ifre) < 0)
+    perror_exit("SIOCSIFMETRIC");
+  return;
+}
+
+static void set_qlen(int sockfd, struct ifreq *ifre, const char *qlen)
+{
+  ifre->ifr_qlen = strtoul(qlen, NULL, 0);
+  if(ioctl(sockfd, SIOCSIFTXQLEN, ifre) < 0)
+    perror_exit("SIOCSIFTXQLEN");
+  return;
+}
+
+static void set_ipv6_addr(int sockfd, struct ifreq *ifre, const char *ipv6_addr, int request, const char *req_name)
+{
+  char *prefix;
+  int plen = 0;
+  sockaddr_with_len *swl = NULL;
+
+  prefix = strchr(ipv6_addr, '/');
+  if(prefix) {
+    plen = get_int_value(prefix + 1, 0, 128);
+    *prefix = '\0';
+  }
+  swl = get_sockaddr(ipv6_addr, 0, AF_INET6);
+  if(!swl) error_exit("error in resolving host name");
+    int sockfd6;
+    struct ifreq_inet6 ifre6;
+    memcpy((char *) &ifre6.ifrinte6_addr,
+        (char *) &(swl->sock_u.sock_in6.sin6_addr),
+        sizeof(struct in6_addr));
+    //Create a channel to the NET kernel.
+    if( (sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+      perror_exit("AF_INET6 SOCK_DGRAM", 0);
+    if(ioctl(sockfd6, SIOGIFINDEX, ifre) < 0)
+      perror_exit("SIOGIFINDEX");
+    ifre6.ifrinet6_ifindex = ifre->ifr_ifindex;
+    ifre6.ifrinet6_prefixlen = plen;
+
+    if(ioctl(sockfd6, request, &ifre6) < 0)
+      perror_exit((char *)req_name);
+    if(swl != NULL) {
+      free(swl);
+      swl = NULL;
+    }
+  return;
+}
+
+static void set_address(int sockfd, const char *host_name, struct ifreq *ifre, int request, const char *req_name)
+{
+  struct sockaddr_in sock_in;
+  sockaddr_with_len *swl = NULL;
+  sock_in.sin_family = AF_INET;
+  sock_in.sin_port = 0;
+
+  //Default 0.0.0.0
+  if(strcmp(host_name, "default") == 0)
+    sock_in.sin_addr.s_addr = INADDR_ANY;
+  else {
+    swl = get_sockaddr(host_name, 0, AF_INET);
+    if(!swl) error_exit("error in resolving host name");
+
+    sock_in.sin_addr = swl->sock_u.sock_in.sin_addr;
+  }
+  memcpy((char *)&ifre->ifr_addr, (char *) &sock_in, sizeof(struct sockaddr));
+  if(ioctl(sockfd, request, ifre) < 0)
+    perror_exit((char *)req_name);
+
+  if(swl != NULL) {
+    free(swl);
+    swl = NULL;
+  }
+  return;
+}
+
+static int hex_to_binary(const char *hw_addr, struct sockaddr *sock, int count)
+{
+  int i = 0, j = 0;
+  unsigned char nib_val;
+  unsigned char ch;
+
+  char *ptr = (char *) sock->sa_data;
+  if(count == ETH_ALEN)
+    sock->sa_family = ARPHRD_ETHER;
+  else if(count == INFINIBAND_ALEN)
+    sock->sa_family = ARPHRD_INFINIBAND;
+  else
+    return -1;
+  //e.g. hw_addr "62:2D:A6:9E:2D:BE"
+  for(; *hw_addr && (i < count); i++) {
+    if(*hw_addr == ':')
+      hw_addr++;
+    j = nib_val = 0;
+    for(;j < 2; j++) {
+      ch = *hw_addr;
+      //0-9 = 10 chars.
+      if(((unsigned char)(ch - '0')) < 10)
+        ch = (ch - '0');
+      //a-f = 6 chars.
+      else if(((unsigned char)((ch) - 'a')) < 6)
+        ch = (ch - ('a'-10));
+      //A-F = 6 chars.
+      else if(((unsigned char)((ch) - 'A')) < 6)
+        ch = (ch - ('A'-10));
+      else if(j && (ch == ':' || ch == 0))
+        break;
+      else
+        return -1;
+      hw_addr++;
+      nib_val <<= 4;
+      nib_val += ch;
+    }
+    *ptr++ = nib_val;
+  }
+  if(*hw_addr)
+    return -1;
+  return 0;
+}
+
+static void set_hw_address(int sockfd, char ***argv, struct ifreq *ifre, int request, const char *req_name)
+{
+  int hw_class = 0;
+  char *hw_addr;
+  struct sockaddr sock;
+  char *ptr;
+  char *hw_class_strings[] = {
+      "ether",
+      "infiniband",
+      NULL
+  };
+
+  if(strcmp(hw_class_strings[0], **argv) == 0)
+    hw_class = 1;
+  else if(strcmp(hw_class_strings[1], **argv) == 0)
+    hw_class = 2;
+  if(!hw_class || !(*argv += 1))
+    show_ifconfig_help();
+
+  memset(&sock, 0, sizeof(struct sockaddr));
+  hw_addr = **argv;
+  if(hw_class == 1) {
+    if(hex_to_binary(hw_addr, &sock, ETH_ALEN))
+      error_exit("invalid hw-addr %s", hw_addr);
+  }
+  else {
+    if(hex_to_binary(hw_addr, &sock, INFINIBAND_ALEN))
+      error_exit("invalid hw-addr %s", hw_addr);
+  }
+  ptr = (char *)&sock;
+  memcpy( ((char *) ifre) + offsetof(struct ifreq, ifr_hwaddr), ptr, sizeof(struct sockaddr));
+  if(ioctl(sockfd, request, ifre) < 0)
+    perror_exit((char *)req_name);
+  return;
+}
+
+static void set_memstart(int sockfd, struct ifreq *ifre, const char *start_addr, int request, const char *req_name)
+{
+  unsigned long mem_start = strtoul(start_addr, NULL, 0);
+
+  if(ioctl(sockfd, SIOCGIFMAP, ifre) < 0)
+    perror_exit("SIOCGIFMAP");
+  ifre->ifr_map.mem_start = mem_start;
+  if(ioctl(sockfd, request, ifre) < 0)
+    perror_exit((char *)req_name);
+  return;
+}
+
+static void set_ioaddr(int sockfd, struct ifreq *ifre, const char *baddr, int request, const char *req_name)
+{
+  unsigned short int base_addr = strtoul(baddr, NULL, 0);
+  if(ioctl(sockfd, SIOCGIFMAP, ifre) < 0)
+    perror_exit("SIOCGIFMAP");
+  ifre->ifr_map.base_addr = base_addr;
+  if(ioctl(sockfd, request, ifre) < 0)
+    perror_exit((char *)req_name);
+  return;
+}
+
+static void set_irq(int sockfd, struct ifreq *ifre, const char *irq_val, int request, const char *req_name)
+{
+  unsigned short int irq = strtoul(irq_val, NULL, 0);
+  char *ptr;
+  struct ifmap *map;
+
+  if(ioctl(sockfd, SIOCGIFMAP, ifre) < 0)
+    perror_exit("SIOCGIFMAP");
+
+  ptr = ((char *) ifre) + offsetof(struct ifreq, ifr_map);
+  map = (struct ifmap *)ptr;
+  map->irq = irq;
+  if(ioctl(sockfd, request, ifre) < 0)
+    perror_exit((char *)req_name);
+  return;
+}
+
+/* Display ifconfig info. */
+static void get_proc_info(char *buff, IFACE_LIST *l_ptr, int version)
+{
+  char *name;
+  memset(&l_ptr->dev_info, 0, sizeof(PROC_NET_DEV_INFO));
+
+  buff = omit_whitespace(buff);
+  name = strsep(&buff, ":");
+  if(!buff)
+    error_exit("error in getting the device name:");
+
+  if(strlen(name) < (IFNAMSIZ)) {
+    strncpy(l_ptr->dev_info.ifrname, name, IFNAMSIZ-1);
+    l_ptr->dev_info.ifrname[IFNAMSIZ-1] = '\0';
+  }
+  else {
+    l_ptr->dev_info.ifrname[0] = '\0';
+  }
+
+  sscanf(buff, field_format[version],
+      &l_ptr->dev_info.receive_bytes,
+      &l_ptr->dev_info.receive_packets,
+      &l_ptr->dev_info.receive_errors,
+      &l_ptr->dev_info.receive_drop,
+      &l_ptr->dev_info.receive_fifo,
+      &l_ptr->dev_info.receive_frame,
+      &l_ptr->dev_info.receive_compressed,
+      &l_ptr->dev_info.receive_multicast,
+      &l_ptr->dev_info.transmit_bytes,
+      &l_ptr->dev_info.transmit_packets,
+      &l_ptr->dev_info.transmit_errors,
+      &l_ptr->dev_info.transmit_drop,
+      &l_ptr->dev_info.transmit_fifo,
+      &l_ptr->dev_info.transmit_colls,
+      &l_ptr->dev_info.transmit_carrier,
+      &l_ptr->dev_info.transmit_compressed
+    );
+
+  if(version == 0)
+    l_ptr->dev_info.receive_bytes = l_ptr->dev_info.transmit_bytes = 0;
+  if(version == 1)
+    l_ptr->dev_info.receive_multicast = l_ptr->dev_info.receive_compressed = l_ptr->dev_info.transmit_compressed = 0;
+  return;
+}
+
+static void add_iface_to_list(IFACE_LIST *newnode)
+{
+  IFACE_LIST *head_ref = iface_list_head;
+
+  if((head_ref == NULL) || strcmp(newnode->dev_info.ifrname, head_ref->dev_info.ifrname) < 0) {
+    newnode->next = head_ref;
+    head_ref = newnode;
+  }
+  else {
+    IFACE_LIST *current = head_ref;
+    while(current->next != NULL && (strcmp(current->next->dev_info.ifrname, newnode->dev_info.ifrname)) < 0)
+      current = current->next;
+    newnode->next = current->next;
+    current->next = newnode;
+  }
+  iface_list_head = head_ref;
+  return;
+}
+
+static int get_device_info(IFACE_LIST *l_ptr)
+{
+  struct ifreq ifre;
+  char *ifrname = l_ptr->dev_info.ifrname;
+  int sokfd;
+
+  sokfd = socket(AF_INET, SOCK_DGRAM, 0);
+  if(sokfd < 0)
+    return sokfd;
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  if(ioctl(sokfd, SIOCGIFFLAGS, &ifre) < 0) {
+    close(sokfd);
+    return NO_RANGE;
+  }
+  l_ptr->ifrflags = ifre.ifr_flags;
+
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  if(ioctl(sokfd, SIOCGIFHWADDR, &ifre) >= 0)
+    memcpy(l_ptr->ifrhwaddr.sa_data, ifre.ifr_hwaddr.sa_data, sizeof(l_ptr->ifrhwaddr.sa_data));
+
+  l_ptr->hw_type = ifre.ifr_hwaddr.sa_family;
+
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  if(ioctl(sokfd, SIOCGIFMETRIC, &ifre) >= 0)
+    l_ptr->ifrmetric = ifre.ifr_metric;
+
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  if(ioctl(sokfd, SIOCGIFMTU, &ifre) >= 0)
+    l_ptr->ifrmtu = ifre.ifr_mtu;
+
+#ifdef SIOCGIFMAP
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  if(ioctl(sokfd, SIOCGIFMAP, &ifre) == 0)
+    l_ptr->ifrmap = ifre.ifr_map;
+#endif
+
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  l_ptr->txqueuelen = NO_RANGE;
+  if(ioctl(sokfd, SIOCGIFTXQLEN, &ifre) >= 0)
+    l_ptr->txqueuelen = ifre.ifr_qlen;
+
+  strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+  ifre.ifr_addr.sa_family = AF_INET;
+
+  if(ioctl(sokfd, SIOCGIFADDR, &ifre) == 0) {
+    l_ptr->ifaddr = 1;
+    l_ptr->ifraddr = ifre.ifr_addr;
+    strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+    if(ioctl(sokfd, SIOCGIFDSTADDR, &ifre) >= 0)
+      l_ptr->ifrdstaddr = ifre.ifr_dstaddr;
+
+    strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+    if(ioctl(sokfd, SIOCGIFBRDADDR, &ifre) >= 0)
+      l_ptr->ifrbroadaddr = ifre.ifr_broadaddr;
+
+    strncpy(ifre.ifr_name, ifrname, IFNAMSIZ);
+    if(ioctl(sokfd, SIOCGIFNETMASK, &ifre) >= 0)
+      l_ptr->ifrnetmask = ifre.ifr_netmask;
+  }
+  close(sokfd);
+  return 0;
+}
+
+static void get_ifconfig_info(void)
+{
+  IFACE_LIST *l_ptr;
+  char buff[BUFSIZ] = {0,};
+  int version_num = 0;
+
+  FILE *fp = fopen(PROC_NET_DEV, "r");
+  if(fp == NULL)
+         return;
+
+  fgets(buff, sizeof(buff), fp); //skip 1st header line.
+  fgets(buff, sizeof(buff), fp); //skip 2nd header line.
+
+  if(strstr(buff, "compressed"))
+    version_num = 2;
+  else if(strstr(buff, "bytes"))
+    version_num = 1;
+  else
+    version_num = 0;
+
+  while(fgets(buff, BUFSIZ, fp)) {
+    l_ptr = xzalloc(sizeof(IFACE_LIST));
+    get_proc_info(buff, l_ptr, version_num);
+    add_iface_to_list(l_ptr);
+    l_ptr->non_virtual_iface = 1;
+    errno = 0;
+    if(get_device_info(l_ptr) < 0) {
+      const char *errstr = strerror(errno);
+      fclose(fp);
+      fp = NULL;
+      clear_list();
+      perror_exit("%s: error getting interface info: %s", l_ptr->dev_info.ifrname, errstr);
+    }
+  }//end of while.
+  fclose(fp);
+  fp = NULL;
+  return;
+}
+
+static void get_hw_info(int hw_type, HW_INFO *hw_info)
+{
+#define HW_UNSPEC -1
+  switch(hw_type) {
+    case ARPHRD_LOOPBACK: //Loopback device.
+      strncpy(hw_info->hw_name, "loop", HW_NAME_LEN);
+      strncpy(hw_info->hw_title, "Local Loopback", HW_TITLE_LEN);
+      hw_info->hw_addrlen = 0;
+      break;
+    case ARPHRD_ETHER: //Ethernet
+      strncpy(hw_info->hw_name, "ether", HW_NAME_LEN);
+      strncpy(hw_info->hw_title, "Ethernet", HW_TITLE_LEN);
+      hw_info->hw_addrlen = ETH_ALEN;
+      break;
+    case ARPHRD_PPP: //ARPHRD_PPP
+      strncpy(hw_info->hw_name, "ppp", HW_NAME_LEN);
+      strncpy(hw_info->hw_title, "Point-to-Point Protocol", HW_TITLE_LEN);
+      hw_info->hw_addrlen = 0;
+      break;
+    case ARPHRD_INFINIBAND: //InfiniBand
+      strncpy(hw_info->hw_name, "infiniband", HW_NAME_LEN);
+      strncpy(hw_info->hw_title, "InfiniBand", HW_TITLE_LEN);
+      hw_info->hw_addrlen = 20;
+      break;
+    case ARPHRD_SIT: //sit0 device - IPv6-in-IPv4
+      strncpy(hw_info->hw_name, "sit", HW_NAME_LEN);
+      strncpy(hw_info->hw_title, "IPv6-in-IPv4", HW_TITLE_LEN);
+      hw_info->hw_addrlen = 0;
+      break;
+    case HW_UNSPEC: //UNSPEC
+      strncpy(hw_info->hw_name, "unspec", HW_NAME_LEN);
+      strncpy(hw_info->hw_title, "UNSPEC", HW_TITLE_LEN);
+      hw_info->hw_addrlen = 0;
+      break;
+    default:
+      break;
+  }
+#undef HW_UNSPEC
+  return;
+}
+
+static void print_hw_addr(int hw_type, HW_INFO hw_info, IFACE_LIST *l_ptr)
+{
+  unsigned char *address = (unsigned char *) l_ptr->ifrhwaddr.sa_data;
+  if(!address || (hw_info.hw_addrlen == 0))
+    return;
+  xprintf("HWaddr ");
+  if(hw_type == ARPHRD_ETHER)
+    xprintf("%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], address[2],
+                         address[3], address[4], address[5]);
+  return;
+}
+
+static const char *get_ip_addr(struct sockaddr *skaddr)
+{
+  static char *ip_str = NULL;
+  struct sockaddr_in *sin;
+
+  if(skaddr->sa_family == 0xFFFF || skaddr->sa_family == 0)
+    return "[NOT SET]";
+  sin = (struct sockaddr_in *)skaddr;
+  if(sin->sin_family != AF_INET) {
+    errno = EAFNOSUPPORT;
+    return NULL;
+  }
+  if( (ip_str = inet_ntoa(sin->sin_addr)) != NULL)
+    return ip_str;
+
+  return NULL;
+}
+
+static void print_ip_addr(IFACE_LIST *l_ptr)
+{
+  const char *af_name = NULL;
+  int af = l_ptr->ifraddr.sa_family;
+  if(af == AF_INET)
+    af_name = "inet";
+  else if(af == AF_INET6)
+    af_name = "inet6";
+  else if(af == AF_UNSPEC)
+    af_name = "unspec";
+
+  xprintf("%10s%s addr:%s ", " ", af_name, get_ip_addr(&l_ptr->ifraddr));
+  if(l_ptr->ifrflags & IFF_POINTOPOINT)
+    xprintf(" P-t-P:%s ", get_ip_addr(&l_ptr->ifrdstaddr));
+  if(l_ptr->ifrflags & IFF_BROADCAST)
+    xprintf(" Bcast:%s ", get_ip_addr(&l_ptr->ifrbroadaddr));
+  xprintf(" Mask:%s\n", get_ip_addr(&l_ptr->ifrnetmask));
+  return;
+}
+
+static void print_iface_flags(IFACE_LIST *l_ptr)
+{
+  if(l_ptr->ifrflags != 0) {
+    unsigned short mask = 1;
+    char **str = iface_flags_str;
+    for(; *str != NULL; str++) {
+      if(l_ptr->ifrflags & mask)
+        xprintf("%s ", *str);
+      mask = mask << 1;
+    }
+  }
+  else
+    xprintf("[NO FLAGS] ");
+  return;
+}
+
+static void print_media(IFACE_LIST *l_ptr)
+{
+#ifdef IFF_PORTSEL
+  if(l_ptr->ifrflags & IFF_PORTSEL) {
+    xprintf("Media:");
+    if(l_ptr->ifrmap.port == IF_PORT_UNKNOWN)
+      xprintf("%s", "unknown");
+    else if(l_ptr->ifrmap.port == IF_PORT_10BASE2)
+      xprintf("%s", "10base2");
+    else if(l_ptr->ifrmap.port == IF_PORT_10BASET)
+      xprintf("%s", "10baseT");
+    else if(l_ptr->ifrmap.port == IF_PORT_AUI)
+      xprintf("%s", "AUI");
+    else if(l_ptr->ifrmap.port == IF_PORT_100BASET)
+      xprintf("%s", "100baseT");
+    else if(l_ptr->ifrmap.port == IF_PORT_100BASETX)
+      xprintf("%s", "100baseTX");
+    else if(l_ptr->ifrmap.port == IF_PORT_100BASEFX)
+      xprintf("%s", "100baseFX");
+    if(l_ptr->ifrflags & IFF_AUTOMEDIA)
+      xprintf("(auto)");
+  }
+#endif
+  return;
+}
+
+static void print_ip6_addr(IFACE_LIST *l_ptr)
+{
+  char iface_name[IFNAMSIZ] = {0,};
+  char buf[BUFSIZ] = {0,};
+  int plen, scope;
+
+  FILE *fp = fopen(PROC_NET_IFINET6, "r");
+  if(fp == NULL)
+         return;
+
+  while(fgets(buf, BUFSIZ, fp)) {
+    int nitems = 0;
+    char ipv6_addr[40] = {0,};
+    nitems = sscanf(buf, "%32s %*08x %02x %02x %*02x %15s\n",
+        ipv6_addr+7, &plen, &scope, iface_name);
+    if(nitems != 4) {
+      if((nitems < 0) && feof(fp))
+        break;
+      perror_exit("sscanf");
+    }
+    if(strcmp(l_ptr->dev_info.ifrname,iface_name) == 0) {
+      int i = 0;
+      struct sockaddr_in6 sock_in6;
+      int len = sizeof(ipv6_addr) / (sizeof ipv6_addr[0]);
+      char *ptr = ipv6_addr+7;
+      while((i < len-2) && (*ptr)) {
+        ipv6_addr[i++] = *ptr++;
+        //put ':' after 4th bit
+        if(!((i+1) % 5))
+          ipv6_addr[i++] = ':';
+      }
+      ipv6_addr[i+1] = '\0';
+      if(inet_pton(AF_INET6, ipv6_addr, (struct sockaddr *) &sock_in6.sin6_addr) > 0) {
+        sock_in6.sin6_family = AF_INET6;
+        memset(buf, 0, (sizeof(buf) /sizeof(buf[0])));
+        if(inet_ntop(AF_INET6, &sock_in6.sin6_addr, buf, BUFSIZ) > 0) {
+          xprintf("%10sinet6 addr: %s/%d", " ", buf, plen);
+          xprintf(" Scope:");
+          if(scope == IPV6_ADDR_ANY) xprintf(" Global");
+          else if(scope == IPV6_ADDR_LOOPBACK) xprintf(" Host");
+          else if(scope == IPV6_ADDR_LINKLOCAL) xprintf(" Link");
+          else if(scope == IPV6_ADDR_SITELOCAL) xprintf(" Site");
+          else if(scope == IPV6_ADDR_COMPATv4) xprintf(" Compat");
+          else xprintf("Unknown");
+          xprintf("\n");
+        }
+      }
+    }
+  }//end of  while.
+  fclose(fp);
+  fp = NULL;
+  return;
+}
+
+static void display_ifconfig(IFACE_LIST *l_ptr)
+{
+  HW_INFO hw_info;
+  int hw_type = l_ptr->hw_type;
+
+  memset(&hw_info, 0, sizeof(HW_INFO));
+
+  get_hw_info(hw_type, &hw_info);
+  xprintf("%-9s Link encap:%s  ", l_ptr->dev_info.ifrname, hw_info.hw_title);
+  print_hw_addr(hw_type, hw_info, l_ptr);
+
+  print_media(l_ptr);
+
+  xprintf("\n");
+  if(l_ptr->ifaddr)
+    print_ip_addr(l_ptr); //print addr, p-p addr, broadcast addr and mask addr.
+
+  //for ipv6 to do.
+  print_ip6_addr(l_ptr);
+  xprintf("%10s", " ");
+  //print flags
+  print_iface_flags(l_ptr);
+  if(!l_ptr->ifrmetric)
+    l_ptr->ifrmetric = 1;
+  xprintf(" MTU:%d  Metric:%d", l_ptr->ifrmtu, l_ptr->ifrmetric);
+  xprintf("\n");
+  if(l_ptr->non_virtual_iface) {
+    xprintf("%10s", " ");
+    xprintf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
+        l_ptr->dev_info.receive_packets, l_ptr->dev_info.receive_errors,
+        l_ptr->dev_info.receive_drop, l_ptr->dev_info.receive_fifo,
+        l_ptr->dev_info.receive_frame);
+    //Dummy types for non ARP hardware.
+    if((hw_type == ARPHRD_CSLIP) || (hw_type == ARPHRD_CSLIP6))
+      xprintf("%10scompressed:%lu\n", " ", l_ptr->dev_info.receive_compressed);
+    xprintf("%10sTX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n", " ",
+        l_ptr->dev_info.transmit_packets, l_ptr->dev_info.transmit_errors,
+        l_ptr->dev_info.transmit_drop, l_ptr->dev_info.transmit_fifo,
+        l_ptr->dev_info.transmit_carrier);
+    xprintf("%10scollisions:%lu ", " ", l_ptr->dev_info.transmit_colls);
+    //Dummy types for non ARP hardware.
+    if((hw_type == ARPHRD_CSLIP) || (hw_type == ARPHRD_CSLIP6))
+      xprintf("compressed:%lu ", l_ptr->dev_info.transmit_compressed);
+    if(l_ptr->txqueuelen != NO_RANGE)
+      xprintf("txqueuelen:%d ", l_ptr->txqueuelen);
+
+    xprintf("\n%10s", " ");
+    xprintf("RX bytes:%llu ", l_ptr->dev_info.receive_bytes);
+    xprintf("TX bytes:%llu\n", l_ptr->dev_info.transmit_bytes);
+  }
+  if(l_ptr->ifrmap.irq || l_ptr->ifrmap.mem_start || l_ptr->ifrmap.dma || l_ptr->ifrmap.base_addr) {
+    xprintf("%10s", " ");
+    if(l_ptr->ifrmap.irq)
+      xprintf("Interrupt:%d ", l_ptr->ifrmap.irq);
+    if(l_ptr->ifrmap.base_addr >= IO_MAP_INDEX)
+      xprintf("Base address:0x%lx ", l_ptr->ifrmap.base_addr);
+    if(l_ptr->ifrmap.mem_start)
+      xprintf("Memory:%lx-%lx ", l_ptr->ifrmap.mem_start, l_ptr->ifrmap.mem_end);
+    if(l_ptr->ifrmap.dma)
+      xprintf("DMA chan:%x ", l_ptr->ifrmap.dma);
+    xprintf("\n");
+  }
+  xprintf("\n");
+  return;
+}
+
+static int readconf(void)
+{
+#define NUM_OF_REQUESTS 30
+       int num_of_req = NUM_OF_REQUESTS;
+       struct ifconf ifcon;
+       struct ifreq *ifre;
+       int num, status = -1, sokfd;
+
+       ifcon.ifc_buf = NULL;
+       sokfd = socket(AF_INET, SOCK_DGRAM, 0);
+       if(sokfd < 0) {
+               perror_msg("error: no inet socket available");
+               return -1;
+       }
+       for (;;) {
+               ifcon.ifc_len = sizeof(struct ifreq) * num_of_req; //Size of buffer.
+               ifcon.ifc_buf = xrealloc(ifcon.ifc_buf, ifcon.ifc_len);
+
+               if((status = ioctl(sokfd, SIOCGIFCONF, &ifcon)) == -1) {
+                       perror_msg("ioctl %#x failed", SIOCGIFCONF);
+                       goto LOOP_BREAK;
+               }
+               //in case of overflow, increase number of requests and retry.
+               if (ifcon.ifc_len == (int)(sizeof(struct ifreq) * num_of_req)) {
+                       num_of_req += 10;
+                       continue;
+               }
+               break;
+       }//End of while loop
+
+       ifre = ifcon.ifc_req;
+       for(num = 0; num < ifcon.ifc_len && ifre; num += sizeof(struct ifreq), ifre++) {
+               //Escape duplicate values from the list.
+               IFACE_LIST *list_ptr;
+               int match_found = 0;
+               for(list_ptr = iface_list_head; list_ptr != NULL; list_ptr = list_ptr->next) {
+                       //if interface already in the list then donot add it in the list.
+                       if(!strcmp(ifre->ifr_name, list_ptr->dev_info.ifrname)) {
+                               match_found = 1;
+                               break;
+                       }
+               }
+               if(!match_found) {
+                       IFACE_LIST *l_ptr = xzalloc(sizeof(IFACE_LIST));
+                       safe_strncpy(l_ptr->dev_info.ifrname, ifre->ifr_name, IFNAMSIZ);
+                       add_iface_to_list(l_ptr);
+                       errno = 0;
+                       if(get_device_info(l_ptr) < 0) {
+                         clear_list();
+                         perror_exit("%s: error getting interface info: %s", l_ptr->dev_info.ifrname, strerror(errno));
+                       }
+               }
+       }//End of for loop.
+
+LOOP_BREAK:
+       close(sokfd);
+       free(ifcon.ifc_buf);
+#undef NUM_OF_REQUESTS
+       return status;
+}
+
+static int show_iface(char *iface_name)
+{
+  get_ifconfig_info();
+
+  if(iface_name) {
+    IFACE_LIST *l_ptr;
+    int is_dev_found = 0;
+    for(l_ptr = iface_list_head; l_ptr; l_ptr = l_ptr->next) {
+      if(strcmp(l_ptr->dev_info.ifrname, iface_name) == 0) {
+        is_dev_found = 1;
+        display_ifconfig(l_ptr);
+        break;
+      }
+    }
+    //if the given interface is not in the list.
+    if(!is_dev_found) {
+      IFACE_LIST *l_ptr = xzalloc(sizeof(IFACE_LIST));
+      safe_strncpy(l_ptr->dev_info.ifrname, iface_name, IFNAMSIZ);
+      errno = 0;
+      if(get_device_info(l_ptr) < 0) {
+        const char *errmsg;
+        if(errno == ENODEV) errmsg = "Device not found";
+        else errmsg = strerror(errno);
+        error_msg("%s: error getting interface info: %s", iface_name, errmsg);
+        free(l_ptr);
+        return 1;
+      }
+      else display_ifconfig(l_ptr);
+      free(l_ptr);
+    }
+  }
+  else {
+    IFACE_LIST *l_ptr;
+    if(readconf() < 0) return 1;
+    for(l_ptr = iface_list_head; l_ptr; l_ptr = l_ptr->next) {
+      if((l_ptr->ifrflags & IFF_UP) || (toys.optflags & FLAG_a))
+        display_ifconfig(l_ptr);
+    }
+  }
+  return 0;
+}
+
+static void clear_list(void)
+{
+  IFACE_LIST *temp_ptr;
+  while(iface_list_head != NULL) {
+    temp_ptr = iface_list_head->next;
+    free(iface_list_head);
+    iface_list_head = temp_ptr;
+  }
+  return;
+}
diff --git a/toys/other/ifconfig.h b/toys/other/ifconfig.h
new file mode 100644 (file)
index 0000000..7a6b266
--- /dev/null
@@ -0,0 +1,160 @@
+/* ifconfig.h - Header file for ifconfig command.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+*/
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <alloca.h>
+
+//Structure Declaration
+typedef struct _proc_net_dev_info {
+  char        ifrname[IFNAMSIZ]; //interface name.
+  unsigned long long   receive_bytes; //total bytes received
+  unsigned long long   receive_packets; //total packets received
+  unsigned long     receive_errors; //bad packets received
+  unsigned long     receive_drop; //no space in linux buffers
+  unsigned long     receive_fifo; //receiver fifo overrun
+  unsigned long     receive_frame; //received frame alignment error
+  unsigned long     receive_compressed;
+  unsigned long     receive_multicast; //multicast packets received
+
+  unsigned long long   transmit_bytes; //total bytes transmitted
+  unsigned long long   transmit_packets; //total packets transmitted
+  unsigned long     transmit_errors; //packet transmit problems
+  unsigned long     transmit_drop; //no space available in linux
+  unsigned long     transmit_fifo;
+  unsigned long     transmit_colls;
+  unsigned long     transmit_carrier;
+  unsigned long     transmit_compressed; //num_tr_compressed;
+}PROC_NET_DEV_INFO;
+
+//man netdevice
+typedef struct _iface_list {
+  int    hw_type;
+  short   ifrflags; //used for addr, broadcast, and mask.
+  short   ifaddr; //if set print ifraddr, irrdstaddr, ifrbroadaddr and ifrnetmask.
+  struct sockaddr ifraddr;
+  struct sockaddr ifrdstaddr;
+  struct sockaddr ifrbroadaddr;
+  struct sockaddr ifrnetmask;
+  struct sockaddr ifrhwaddr;
+  int    ifrmtu;
+  int   ifrmetric;
+  PROC_NET_DEV_INFO dev_info;
+  int   txqueuelen;
+  struct ifmap ifrmap;
+  int non_virtual_iface;
+  struct  _iface_list *next; //, *prev;
+}IFACE_LIST;
+
+
+#define HW_NAME_LEN 20
+#define HW_TITLE_LEN 30
+
+typedef struct _hw_info {
+  char hw_name[HW_NAME_LEN];
+  char hw_title[HW_TITLE_LEN];
+  int     hw_addrlen;
+}HW_INFO;
+
+static const char *const field_format[] = {
+  "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
+  "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
+  "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
+};
+
+#define PROC_NET_DEV "/proc/net/dev"
+#define PROC_NET_IFINET6 "/proc/net/if_inet6"
+#define NO_RANGE -1
+#define IO_MAP_INDEX 0x100
+
+static int show_iface(char *iface_name);
+static void print_ip6_addr(IFACE_LIST *l_ptr);
+static void clear_list(void);
+
+//from /net/if.h
+static char *iface_flags_str[] = {
+      "UP",
+      "BROADCAST",
+      "DEBUG",
+      "LOOPBACK",
+      "POINTOPOINT",
+      "NOTRAILERS",
+      "RUNNING",
+      "NOARP",
+      "PROMISC",
+      "ALLMULTI",
+      "MASTER",
+      "SLAVE",
+      "MULTICAST",
+      "PORTSEL",
+      "AUTOMEDIA",
+      "DYNAMIC",
+      NULL
+};
+//from /usr/include/linux/netdevice.h
+#ifdef IFF_PORTSEL
+//Media selection options.
+# ifndef IF_PORT_UNKNOWN
+enum {
+    IF_PORT_UNKNOWN = 0,
+    IF_PORT_10BASE2,
+    IF_PORT_10BASET,
+    IF_PORT_AUI,
+    IF_PORT_100BASET,
+    IF_PORT_100BASETX,
+    IF_PORT_100BASEFX
+};
+# endif
+#endif
+
+//from kernel header ipv6.h
+#define IPV6_ADDR_ANY 0x0000U
+#define IPV6_ADDR_LOOPBACK  0x0010U
+#define IPV6_ADDR_LINKLOCAL  0x0020U
+#define IPV6_ADDR_SITELOCAL  0x0040U
+#define IPV6_ADDR_COMPATv4  0x0080U
+
+//==================================================================================
+//for the param settings.
+
+//for ipv6 add/del
+struct ifreq_inet6 {
+  struct in6_addr ifrinte6_addr;
+  uint32_t ifrinet6_prefixlen;
+  int ifrinet6_ifindex;
+};
+
+#ifndef SIOCSKEEPALIVE
+# define SIOCSKEEPALIVE  (SIOCDEVPRIVATE)        /* Set keepalive timeout in sec */
+# define SIOCGKEEPALIVE  (SIOCDEVPRIVATE+1)        /* Get keepalive timeout */
+#endif
+
+#ifndef SIOCSOUTFILL
+# define SIOCSOUTFILL  (SIOCDEVPRIVATE+2)        /* Set outfill timeout */
+# define SIOCGOUTFILL  (SIOCDEVPRIVATE+3)        /* Get outfill timeout */
+#endif
+
+#ifndef INFINIBAND_ALEN
+# define INFINIBAND_ALEN 20
+#endif
+
+static void set_data(int sockfd, struct ifreq *ifre, char *kval, int request, const char *req_name);
+static void set_flags(int sockfd, struct ifreq *ifre, int arg_flag, int flag); //verify
+static void set_mtu(int sockfd, struct ifreq *ifre, const char *mtu); //verify
+static void set_metric(int sockfd, struct ifreq *ifre, const char *metric); //verify
+static void set_qlen(int sockfd, struct ifreq *ifre, const char *qlen); //verify
+static void set_address(int sockfd, const char *host_name, struct ifreq *ifre, int request, const char *req_name);
+static void set_hw_address(int sockfd, char ***argv, struct ifreq *ifre, int request, const char *req_name);
+static void set_ipv6_addr(int sockfd, struct ifreq *ifre, const char *ipv6_addr, int request, const char *req_name);
+static void set_memstart(int sockfd, struct ifreq *ifre, const char *start_addr, int request, const char *req_name);
+static void set_ioaddr(int sockfd, struct ifreq *ifre, const char *baddr, int request, const char *req_name);
+static void set_irq(int sockfd, struct ifreq *ifre, const char *irq_val, int request, const char *req_name);
+
+
+//==================================================================================
diff --git a/toys/other/ifup.c b/toys/other/ifup.c
new file mode 100644 (file)
index 0000000..43ab31b
--- /dev/null
@@ -0,0 +1,1815 @@
+/* ifup.c - Bring a network interface up/down.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+USE_IFUP(NEWTOY(ifup, "mfvnai:", TOYFLAG_SBIN))
+USE_IFUP(OLDTOY(ifdown,ifup, "mfvnai:", TOYFLAG_SBIN))
+
+config IFUP
+  bool "ifup/ifdown"
+  default y
+  help
+    usage: ifup [[anmvf] [i FILE]] [IFACE...]
+    usage: ifdown [[anmvf] [i FILE]] [IFACE...]
+
+    ifup - Bring a network interface up.
+    ifdown - Take a network interface down.
+
+    -a                 De/configure all interfaces marked "auto".
+    -i FILE    Use file for interface definitions.
+    -n                 Print out what would happen, but don't do it. (Note: It doesn't disable mapping).
+    -m                 Don't run any mapping.
+    -v                 Print out what would happen before doing it.
+    -f         Force De/configuration.
+*/
+
+#define FOR_ifup
+#include "toys.h"
+#include <net/if.h>
+#include <fnmatch.h>
+#include <unistd.h>
+
+GLOBALS(
+  char *interface_filename;
+  int isup;
+  char **g_env_var;
+  const char *g_path_var;
+  const char *g_shell;
+)
+
+#define AUTO 0
+#define IFACE 1
+#define MAPPING 2
+#define NETWORK_INTERFACE_STATE_FILE "/var/run/ifstate"
+
+//forward declaration.
+struct _iface_fields;
+
+typedef struct _mapping_fields {
+  char **map_ifaces_list;
+  int num_map_ifaces;
+  char *script_name;
+  char **mapping_list;
+  int num_mapping_items;
+}MAPPING_FIELDS;
+
+typedef struct _method_list {
+  char *method_name;
+  int (*funptr)(struct _iface_fields *);
+}METHOD_LIST;
+
+typedef struct _variable {
+  char *var_name;
+  char *var_val;
+}VARIABLE;
+
+typedef struct _iface_fields {
+  char *iface_name;
+  char *iface_af;
+  METHOD_LIST *iface_method;
+  char *iface_method_name;
+  VARIABLE *var;
+  int num_vars;
+}IFACE_FIELDS;
+
+typedef struct _config_type {
+  struct double_list *auto_interfaces;
+  struct double_list *iface;
+  struct double_list *mapping;
+}CONFIG_TYPE;
+
+static const char *multiple_specified_keywords[] = {
+  "up",
+  "down",
+  "pre-up",
+  "post-down",
+  NULL
+};
+
+static char *get_word(char **buff);
+
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, const char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+/*
+ * Remove white spaces from the given string.
+ */
+static char *omit_whitespace(char *s)
+{
+  while(*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) s++;
+  return (char *) s;
+}
+/*
+ * Remove non white spaces from the given string.
+ */
+static char *omitnon_whitespace(char *s)
+{
+  while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9)) s++;
+  return (char *) s;
+}
+// Find out if the last character of a string matches with the given one.
+// Don't underrun the buffer if the string length is 0.
+static char *find_last_char(char *str, int c)
+{
+  if (str && *str) {
+    size_t sz = strlen(str) - 1;
+    str += sz;
+    if ( (unsigned char)*str == c) return (char*)str;
+  }
+  return NULL;
+}
+
+static char *xxstrdup(char *s)
+{
+  if(s == NULL) return NULL;
+  return xstrdup(s);
+}
+
+/*
+ * Format the name string.
+ * a. small letters in capital letters
+ * b. '-' in '_'
+ * c. numeric values remain same.
+ */
+static char *format_name(const char *name)
+{
+  char *str = (char *)name;
+  int index = 0;
+  while(str[index] != '\0') {
+    if(str[index] == '-') str[index] = '_';
+    if(str[index] >= 'a' && str[index] <= 'z')
+      str[index] -= ('a' - 'A');
+    if(isalnum(str[index]) || str[index] == '_'); //do nothing.
+    index++;
+  }
+  return str;
+}
+
+/*
+ * Prepare environment variables for a command.
+ */
+static void set_env_var(IFACE_FIELDS *ifd, const char *mode)
+{
+  int index = 0, i = 0;
+
+  //free environment variables(if any) and reallocate the variables.
+  if(TT.g_env_var != NULL) {
+    while(TT.g_env_var[index]) {
+      free(TT.g_env_var[index]);
+      TT.g_env_var[index] = NULL;
+      index++;
+    }
+    free(TT.g_env_var);
+    TT.g_env_var = NULL;
+  }
+  //alloc memory to hold the environment variables.
+  TT.g_env_var = xzalloc((ifd->num_vars + 6) * sizeof(char *)); // 6 -> 5 vars(IFACE + ADDRFAM + METHOD + MODE + PATH) and a NULL pointer.
+  index = 0;
+  while(i < ifd->num_vars) {
+    char *str;
+    if( (!strcmp(ifd->var[i].var_name, "up"))
+        ||(!strcmp(ifd->var[i].var_name, "down"))
+        ||(!strcmp(ifd->var[i].var_name, "pre-up"))
+        ||(!strcmp(ifd->var[i].var_name, "post-down")) ) {
+      i++;
+      continue;
+    }
+    str = xstrdup(ifd->var[i].var_name);
+    TT.g_env_var[index++] = xmsprintf("IF_%s=%s", format_name(str), ifd->var[i].var_val);
+    free(str);
+    str = NULL;
+    i++;
+  }
+  TT.g_env_var[index++] = xmsprintf("%s=%s", "IFACE", ifd->iface_name);
+  TT.g_env_var[index++] = xmsprintf("%s=%s", "ADDRFAM", ifd->iface_af);
+  TT.g_env_var[index++] = xmsprintf("%s=%s", "METHOD", ifd->iface_method_name);
+  TT.g_env_var[index++] = xmsprintf("%s=%s", "MODE", mode);
+
+  if(TT.g_path_var)
+    TT.g_env_var[index++] = xmsprintf("%s=%s", "PATH", TT.g_path_var);
+
+  return;
+}
+
+/*
+ * Execute the given command and return the status.
+ */
+static int execute_action(const char *cmd)
+{ 
+  pid_t pid, w_pid;
+  int status;
+  if((toys.optflags & FLAG_v) || (toys.optflags & FLAG_n))
+    xprintf("%s\n", cmd);
+  if(toys.optflags & FLAG_n) return 1;
+
+  fflush(NULL);
+  switch(pid = vfork()) {
+    case -1:// error
+      return 0;
+    case 0: { //child
+              int exit_val;
+              execle(TT.g_shell, TT.g_shell, "-c", cmd, (char *) NULL, TT.g_env_var);
+              exit_val = (errno == ENOENT) ? 127 : 126;
+              _exit(exit_val);
+            }
+            break;
+    default:
+            break;
+  }
+  //wait for child process termination and get the status of the child process.
+  do {
+    w_pid = waitpid(pid, &status, 0);
+  }while((w_pid == -1) && (errno == EINTR));
+
+  if((!WIFEXITED(status)) || (WEXITSTATUS(status) != 0))
+    return 0;
+  return 1;
+}
+
+/*
+ * if "up/down/pre-up/post-down" option is in the iface list then execute it.
+ * otherwise run the script "/etc/network/if-up.d" (an e.g.).
+ */
+static int execute_option(IFACE_FIELDS *ifd, const char *option)
+{
+  int i = 0;
+  char *cmd;
+  int cmd_status = 0;
+
+  while(i < ifd->num_vars) {
+    if(!strcmp(ifd->var[i].var_name, option)) {
+      cmd_status = execute_action(ifd->var[i].var_val);
+      if(!cmd_status) return 0;
+    }
+    i++;
+  }
+  cmd = xmsprintf("run-parts /etc/network/if-%s.d", option);
+  cmd_status = execute_action(cmd);
+  free(cmd);
+  return cmd_status;
+}
+
+/*
+ * Add the upcoming string in the command string.
+ */
+static void add_str_to_cmd(char **cmd, const char *str, size_t size)
+{
+  char *ptr = *cmd;
+  int len = (ptr ? strlen(ptr) : 0);
+  size++;
+  ptr = xrealloc(ptr, len + size);
+  //copy the string to cmd.
+  safe_strncpy(ptr + len, str, size);
+  *cmd = ptr;
+}
+
+/*
+ * Get the value of the variable from the list (if there).
+ * otherwise return NULL.
+ */
+static char *get_varval_from_list(IFACE_FIELDS *ifd, const char *var_name, int var_len)
+{
+  int i = 0;
+  if( (!strncmp(var_name, "iface", var_len))
+      || (!strncmp(var_name, "label", var_len)) )
+    return ifd->iface_name;
+
+  while(i < ifd->num_vars) {
+    if(!strncmp(var_name, ifd->var[i].var_name, var_len))
+      return ifd->var[i].var_val;
+    i++;
+  }
+  return NULL;
+}
+
+/*
+ * Count the number of set bits in the netmask.
+ */
+static int count_bits(const char *bnmask)
+{
+  unsigned int num_of_set_bits = 0;
+  struct in_addr addr;
+
+  if(!inet_aton(bnmask, &addr)) return -1; //IP format is not correct.
+  while(addr.s_addr) {
+    if((addr.s_addr & 1) == 1) num_of_set_bits++;
+    addr.s_addr >>= 1;
+  }
+  return num_of_set_bits;
+}
+
+/*
+ * Prepare command with the help of row_cmd.
+ */
+static char *prepare_command(const char *row_cmd, IFACE_FIELDS *ifd)
+{
+#define ERROR_PERCENT 10001
+#define ERROR_BRACES  10002
+#define ERROR_VARVAL  10003
+  char *cmd = NULL;
+  char *wordptr = NULL, *word = NULL, *line;
+  int num_of_open_braces = 0;
+  int cur_cmd_len = 0;
+
+  line = wordptr = xstrdup((char *)row_cmd);
+  word = get_word(&wordptr);
+
+  while(word) {
+    if(word[0] == '%') {
+      char *var_val, *next_percent;
+      int word_len = 0;
+
+      word++;
+      word_len = strlen(word);
+
+      //e.g. "%broadcast%]"
+      if(word[word_len - 1] == ']') {
+        num_of_open_braces--;
+        next_percent = strchr(word, '%');
+        if(!next_percent) {
+          errno = ERROR_PERCENT;
+          goto ERROR_CONDITION;
+        }
+        var_val = get_varval_from_list(ifd, word, (next_percent - word));
+        if(var_val) {
+          //add it to the cmd
+          if(strncmp(word, "hwaddress", 9) == 0)
+            var_val = omit_whitespace(omitnon_whitespace(var_val));
+          add_str_to_cmd(&cmd, var_val, strlen(var_val)+1);
+          cmd[strlen(cmd)] = ' ';
+          cur_cmd_len = 0;
+          goto NEXT_WORD;
+        }
+        else {
+          cmd[cur_cmd_len] = '\0';
+          cur_cmd_len = 0;
+          goto NEXT_WORD;
+        }
+      }
+      //e.g. %address%/%bnmask%
+      else {
+        next_percent = strchr(word, '%');
+        if(!next_percent) {
+          errno = ERROR_PERCENT;
+          goto ERROR_CONDITION;
+        }
+        var_val = get_varval_from_list(ifd, word, (next_percent - word));
+        if(var_val) {
+          add_str_to_cmd(&cmd, var_val, strlen(var_val)+1);
+          cmd[strlen(cmd)] = ' ';
+          word = next_percent+1;
+          //if NULL go to the next word or check for other conditions.
+          if(word[0] == '\0') goto NEXT_WORD;
+          else {
+            //check for next option.
+            cmd[strlen(cmd)-1] = *word;
+            word++;
+            continue;
+          }
+        }
+        else if(!strncmp(word, "bnmask", 6)) {
+          var_val = get_varval_from_list(ifd, "netmask", 7);
+          if(var_val) {
+            //number of set bits in netmask.
+            unsigned int num_of_set_bits = 0;
+            num_of_set_bits = count_bits(var_val);
+            if(num_of_set_bits > 0) {
+              const char *bnmask = utoa(num_of_set_bits);
+              add_str_to_cmd(&cmd, bnmask, strlen(bnmask)+1);
+              cmd[strlen(cmd)] = ' ';
+              goto NEXT_WORD;
+            }
+          }
+          else {
+            errno = ERROR_VARVAL;
+            goto ERROR_CONDITION;
+          }
+        }
+        else {
+          errno = ERROR_VARVAL;
+          goto ERROR_CONDITION;
+        }
+      }//end of "e.g. %address%/%bnmask%"
+    }//end of "if(word[0] == '%')"
+    else if(word[0] == '[') {
+      word++;
+      num_of_open_braces++;
+      cur_cmd_len = cmd ? strlen(cmd) : 0;
+    }
+    //special cases "e.g. /var/run/wvdial.%iface%"
+    else {
+      char *ptr = strchr(word, '%');
+      if(ptr) {
+        *ptr = '\0';
+        char *var_val;
+        char *next_percent = strchr(++ptr, '%');
+        if(!next_percent) {
+          errno = ERROR_PERCENT;
+          goto ERROR_CONDITION;
+        }
+        var_val = get_varval_from_list(ifd, ptr, (next_percent - ptr));
+        if(var_val) {
+          char *new_word = xmsprintf("%s%s%s", word, var_val, ++next_percent);
+          add_str_to_cmd(&cmd, new_word, strlen(new_word)+1);
+          cmd[strlen(cmd)] = ' ';
+          free(new_word);
+          goto NEXT_WORD;
+        }
+        else {
+          errno = ERROR_VARVAL;
+          goto ERROR_CONDITION;
+        }
+      }
+    }
+    if(word[0] != '\0') {
+      add_str_to_cmd(&cmd, word, strlen(word)+1);
+      cmd[strlen(cmd)] = ' ';
+    }
+NEXT_WORD:
+    word = get_word(&wordptr);
+  }
+  free(line);
+  if(num_of_open_braces != 0) {
+    free(cmd);
+    errno = ERROR_BRACES;
+    return NULL;
+  }
+  cmd[strlen(cmd) ? (strlen(cmd)-1) : 0] = '\0';
+  return cmd;
+ERROR_CONDITION:
+  free(line);
+  line = NULL;
+  free(cmd);
+  return NULL;
+#undef ERROR_PERCENT
+#undef ERROR_BRACES
+#undef ERROR_VARVAL
+}
+
+/*
+ * De/configure interface manually.
+ */
+static int if_manual(IFACE_FIELDS *ifd)
+{
+  //for ifup
+  if(TT.isup) {
+    set_env_var(ifd, "start");
+    if(!execute_option(ifd, "pre-up")) return 0;
+    if(!execute_option(ifd, "up")) return 0;
+  }
+  //for ifdown
+  else {
+    set_env_var(ifd, "stop");
+    if(!execute_option(ifd, "down")) return 0;
+    if(!execute_option(ifd, "post-down")) return 0;
+  }
+  return 1;
+}
+
+/*
+ * Bring up the interface with wvdial method.
+ */
+static int if_wvdial_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("start-stop-daemon --start -x wvdial -p /var/run/wvdial.%iface% -b -m -- [ %provider%]", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    free(cmd);
+    return 0;
+  }
+  cmd_status = execute_action(cmd);
+  free(cmd);
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with wvdial method.
+ */
+static int if_wvdial_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("start-stop-daemon --stop -x wvdial -p /var/run/wvdial.%iface% -s 2", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "stop");
+  cmd_status = execute_action(cmd);
+  free(cmd);
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with wvdial method.
+ */
+static int if_wvdial(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if_wvdial_up(ifd));
+  else return(if_wvdial_down(ifd));
+}
+
+/*
+ * Bring up the interface with ppp method.
+ */
+static int if_ppp_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("pon [ %provider%]", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    free(cmd);
+    return 0;
+  }
+  cmd_status = execute_action(cmd);
+  free(cmd);
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with ppp method.
+ */
+static int if_ppp_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("poff [ %provider%]", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "stop");
+  cmd_status = execute_action(cmd);
+  free(cmd);
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with ppp method.
+ */
+static int if_ppp(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if_ppp_up(ifd));
+  else return(if_ppp_down(ifd));
+}
+
+/*
+ * Bring up the interface with static method.
+ */
+static int if_static_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[3] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip addr add %address%/%bnmask% [broadcast %broadcast%] "
+      "dev %iface% [peer %pointopoint%] [label %label%]", ifd);
+  cmd[index++] = prepare_command("ip link set [mtu %mtu%] [addr %hwaddress%] %iface% up", ifd);
+  cmd[index++] = prepare_command("[ip route add default via %gateway% dev %iface%]", ifd);
+
+  for(index = 0; index < 3; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    for(index = 0; index < 3; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 3; index++) {
+    int status = 0;
+    status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 3) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with static method.
+ */
+static int if_static_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[2] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip addr flush dev %iface%", ifd);
+  cmd[index++] = prepare_command("ip link set %iface% down", ifd);
+
+  for(index = 0; index < 2; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "stop");
+  if(!execute_option(ifd, "down")) {
+    for(index = 0; index < 2; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 2; index++) {
+    int status = 0;
+    status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 2) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with static method.
+ */
+static int if_static(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if_static_up(ifd));
+  else return(if_static_down(ifd));
+}
+
+/*
+ * Bring up the interface with bootp method.
+ */
+static int if_bootp_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("bootpc [--bootfile %bootfile%] --dev %iface%"
+      " [--server %server%] [--hwaddr %hwaddr%]"
+      " --returniffail --serverbcast", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    free(cmd);
+    return 0;
+  }
+
+  cmd_status = execute_action(cmd);
+  free(cmd);
+
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with bootp method.
+ */
+static int if_bootp(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if_bootp_up(ifd));
+  else return(if_static_down(ifd));
+}
+
+/*
+ * Bring up the interface with dhcp method.
+ */
+static int if_dhcp_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[2] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip link set [addr %hwaddress%] %iface% up", ifd);
+  cmd[index++] = prepare_command("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
+      "-i %iface% [-H %hostname%] [-c %client%] [-s %script%] [ %udhcpc_opts%]", ifd);
+
+  for(index = 0; index < 2; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    for(index = 0; index < 2; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 2; index++) {
+    cmd_status = execute_action(cmd[index]);
+    if(cmd_status != 1) {
+      free(cmd[index]);
+      return 0;
+    }
+    free(cmd[index]);
+  }
+
+  if(!execute_option(ifd, "up")) return 0;
+  return 1;
+}
+
+/*
+ * Down the interface with dhcp method.
+ */
+static int if_dhcp_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[3] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("test -f /var/run/udhcpc.%iface%.pid && "
+      "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd);
+  cmd[index++] = prepare_command("ip addr flush dev %iface%", ifd);
+  cmd[index++] = prepare_command("ip link set %iface% down", ifd);
+
+  for(index = 0; index < 3; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "stop");
+  if(!execute_option(ifd, "down")) {
+    for(index = 0; index < 3; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 3; index++) {
+    int status = 0;
+    status = execute_action(cmd[index]);
+    usleep(100000);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 3) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with dhcp method.
+ */
+static int if_dhcp(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if_dhcp_up(ifd));
+  else return(if_dhcp_down(ifd));
+}
+
+/*
+ * Bring up the interface with loopback method.
+ */
+static int if_loopback_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[2] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip addr add 127.0.0.1/8 dev %iface%", ifd);
+  cmd[index++] = prepare_command("ip link set %iface% up", ifd);
+
+  for(index = 0; index < 2; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    for(index = 0; index < 2; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 2; index++) {
+    int status = 0;
+    status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 2) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with loopback method.
+ */
+static int if_loopback_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[2] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip addr flush dev %iface%", ifd);
+  cmd[index++] = prepare_command("ip link set %iface% down", ifd);
+
+  for(index = 0; index < 2; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "stop");
+  if(!execute_option(ifd, "down")) {
+    for(index = 0; index < 2; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 2; index++) {
+    int status = 0;
+    status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 2) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with loopback method.
+ */
+static int if_loopback(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if_loopback_up(ifd));
+  else return(if_loopback_down(ifd));
+}
+
+static const METHOD_LIST methods[] = {
+  { "manual",   if_manual, },
+  { "wvdial",   if_wvdial, },
+  { "ppp",      if_ppp, },
+  { "static",   if_static, },
+  { "bootp",    if_bootp, },
+  { "dhcp",     if_dhcp, },
+  { "loopback", if_loopback, },
+  { NULL, NULL, },
+};
+
+/*
+ * Bring up the interface with v4tunnel method.
+ */
+static int if6_v4tunnel_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[4] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip tunnel add %iface% mode sit remote "
+      "%endpoint% [local %local%] [ttl %ttl%]", ifd);
+  cmd[index++] = prepare_command("ip link set %iface% up", ifd);
+  cmd[index++] = prepare_command("ip addr add %address%/%netmask% dev %iface%", ifd);
+  cmd[index++] = prepare_command("[ip route add ::/0 via %gateway%]", ifd);
+
+  for(index = 0; index < 4; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    for(index = 0; index < 4; index++)
+      free(cmd[index]);
+    return 0;
+  }
+  for(index = 0; index < 4; index++) {
+    int status = 1;
+    if(cmd[index][0])
+      status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 4) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with v4tunnel method.
+ */
+static int if6_v4tunnel_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("ip tunnel del %iface%", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "stop");
+  cmd_status = execute_action(cmd);
+  free(cmd);
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with v4tunnel method.
+ */
+static int if6_v4tunnel(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if6_v4tunnel_up(ifd));
+  else return(if6_v4tunnel_down(ifd));
+}
+
+/*
+ * Bring up the interface with static method.
+ */
+static int if6_static_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[3] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip addr add %address%/%netmask% dev %iface% [label %label%]", ifd);
+  cmd[index++] = prepare_command("ip link set [mtu %mtu%] [addr %hwaddress%] %iface% up", ifd);
+  cmd[index++] = prepare_command("[ip route add ::/0 via %gateway%]", ifd);
+
+  for(index = 0; index < 3; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    for(index = 0; index < 3; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 3; index++) {
+    int status = 1;
+    if(cmd[index][0])
+      status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 3) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with static method.
+ */
+static int if6_static_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("ip link set %iface% down", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "stop");
+  cmd_status = execute_action(cmd);
+  free(cmd);
+
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with static method.
+ */
+static int if6_static(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if6_static_up(ifd));
+  else return(if6_static_down(ifd));
+}
+
+/*
+ * De/configure interface manually.
+ */
+static int if6_manual(IFACE_FIELDS *ifd)
+{
+  //for ifup
+  if(TT.isup) {
+    set_env_var(ifd, "start");
+    if(!execute_option(ifd, "pre-up")) return 0;
+    if(!execute_option(ifd, "up")) return 0;
+  }
+  //for ifdown
+  else {
+    set_env_var(ifd, "stop");
+    if(!execute_option(ifd, "down")) return 0;
+    if(!execute_option(ifd, "post-down")) return 0;
+  }
+  return 1;
+}
+
+/*
+ * Bring up the interface with loopback method.
+ */
+static int if6_loopback_up(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd[2] = {NULL, };
+  int index = 0;
+
+  cmd[index++] = prepare_command("ip addr add ::1 dev %iface%", ifd);
+  cmd[index++] = prepare_command("ip link set %iface% up", ifd);
+
+  for(index = 0; index < 2; index++)
+    if(!cmd[index]) return -1;
+
+  set_env_var(ifd, "start");
+  if(!execute_option(ifd, "pre-up")) {
+    for(index = 0; index < 2; index++)
+      free(cmd[index]);
+    return 0;
+  }
+
+  for(index = 0; index < 2; index++) {
+    int status = 0;
+    status = execute_action(cmd[index]);
+    if(status != 1) status = 0;
+    cmd_status += status;
+    free(cmd[index]);
+  }
+
+  if(cmd_status != 2) return 0;
+  if(!execute_option(ifd, "up")) return 0;
+
+  return 1;
+}
+
+/*
+ * Down the interface with loopback method.
+ */
+static int if6_loopback_down(IFACE_FIELDS *ifd)
+{
+  int cmd_status = 0;
+  char *cmd = prepare_command("ip link set %iface% down", ifd);
+  if(!cmd) return -1;
+
+  set_env_var(ifd, "stop");
+  cmd_status = execute_action(cmd);
+  free(cmd);
+
+  if(cmd_status != 1) return 0;
+  if(!execute_option(ifd, "post-down")) return 0;
+
+  return 1;
+}
+
+/*
+ * De/configure interface with loopback method.
+ */
+static int if6_loopback(IFACE_FIELDS *ifd)
+{
+  if(TT.isup) return(if6_loopback_up(ifd));
+  else return(if6_loopback_down(ifd));
+}
+
+static const METHOD_LIST methods6[] = {
+  { "v4tunnel", if6_v4tunnel, },
+  { "static",   if6_static, },
+  { "manual",   if6_manual, },
+  { "loopback", if6_loopback, },
+  { NULL, NULL, },
+};
+
+/*
+ * display help info and exit from application.
+ */
+static void show_ifup_help(void)
+{
+  toys.exithelp++;
+  error_exit("Invalid Argument");
+}
+
+/*
+ * Find duplicate string from the list.
+ * if found, return the node address
+ * else return NULL.
+ */
+struct double_list *llist_is_duplicate_str(struct double_list *list, const char *str)
+{
+  struct double_list *old = list;
+  while(list) {
+    if(strcmp(list->data, str) == 0) return list;
+
+    list = list->next;
+    //End of list.
+    if(old == list) break;
+  }
+  return NULL;
+}
+
+/*
+ * Validate the list to find out any duplication of iface name and address family.
+ * if found, Prompt error message and exit the application.
+ * if not, continue adding items in the list.
+ */
+void validate_list(CONFIG_TYPE *conf, IFACE_FIELDS *cur_iface)
+{
+  struct double_list *list = conf->iface;
+  struct double_list *old = list;
+  while(list) {
+    IFACE_FIELDS *list_iface = (IFACE_FIELDS *)list->data;
+    if( (!strcmp(list_iface->iface_name, cur_iface->iface_name))
+        && (!strcmp(list_iface->iface_af, cur_iface->iface_af)) )
+      perror_exit("Duplicate interface '%s'", list_iface->iface_name);
+
+    list = list->next;
+    //End of list.
+    if(old == list) break;
+  }
+  return;
+}
+
+/*
+ * Validate the list to find out any duplication of keyword like address/netmask/gateway etc.
+ * (except up/down/pre-up/post-down keywords).
+ */
+static void validate_dup_var(IFACE_FIELDS *cur_iface, const char *cur_word)
+{
+  int index = 0;
+  int is_mul_spec_str = 0;
+  if(!cur_iface->var) return;
+  while(multiple_specified_keywords[index]) {
+    if(!strcmp(cur_word, multiple_specified_keywords[index])) {
+      is_mul_spec_str = 1;
+      break;
+    }
+    index++;
+  }
+
+  if(!is_mul_spec_str) {
+    index = 0;
+    while(index < cur_iface->num_vars) {
+      if(!strcmp(cur_iface->var[index].var_name, cur_word))
+        error_exit("Duplicate option is: '%s'", cur_word);
+      index++;
+    }
+  }
+  return;
+}
+
+/*
+ * If the last char of any line is '\',
+ * concatinate next line (if present) with the current line.
+ */
+static char *concat_next_line(int fd, char *line)
+{
+  char *ptr = NULL;
+  char *next_line = NULL;
+  while((next_line = get_line(fd)) != NULL) {
+    if((ptr = find_last_char(next_line, '\\')) != NULL) {
+      *ptr = '\0';
+      ptr = xmsprintf("%s%s", line, next_line);
+      free(next_line);
+      free(line);
+      line = ptr;
+    }
+    else {
+      ptr = xmsprintf("%s%s", line, next_line);
+      free(next_line);
+      free(line);
+      break;
+    }
+  }
+  return ptr;
+}
+
+/*
+ * Get next word from the given line and move buffer pointer to the next word.
+ */
+static char *get_word(char **buff)
+{
+  int wordlen = 0;
+  char *bufptr = omit_whitespace(*buff);
+  //if the 1st char of the line is NULL then return NULL.
+  //as the line can be an empty line.
+  if(*bufptr == '\0') return NULL;
+  wordlen = strcspn(bufptr, " \t\n"); //the reject pinter is either space/tab/newline.
+  if(bufptr[wordlen] != '\0')
+    bufptr[wordlen++] = '\0'; //NULL terminated word.
+
+  //remove any space is there before next word.
+  *buff = bufptr + wordlen;
+  *buff = omit_whitespace(*buff);
+  return bufptr;
+}
+
+/*
+ * Find out the function name and its pointer from METHOD_LIST.
+ * if found, assign it to IFACE_FIELDS.
+ * if not, report the error message and exit the application.
+ */
+static void get_method_info(IFACE_FIELDS *iface_fields, const METHOD_LIST *mlist)
+{
+  int match_found = 0;
+
+  if(!iface_fields->iface_method_name)
+    perror_exit("Unknown method = '%s'", iface_fields->iface_method_name);
+
+  while(mlist->method_name) {
+    if(strcmp(mlist->method_name, iface_fields->iface_method_name) == 0) {
+      iface_fields->iface_method = (METHOD_LIST *)mlist;
+      match_found = 1;
+      break;
+    }
+    mlist++;
+  }
+  if(!match_found) perror_exit("Unknown method = '%s'", iface_fields->iface_method_name);
+  return;
+}
+
+/*
+ * Parse the given config file and prepare list of config types (auto/iface/mapping).
+ */
+static CONFIG_TYPE *read_interface_file(const char *interface_filename)
+{
+  CONFIG_TYPE *conf = NULL;
+  IFACE_FIELDS *iface_fields = NULL;
+  MAPPING_FIELDS *mapping_fields = NULL;
+  int fd;
+  int flag_current_interface = AUTO;
+  char *line = NULL;
+  char *wordptr = NULL;
+  char *word = NULL;
+
+  conf = xzalloc(sizeof(*conf));
+  fd = xopen((char *)interface_filename, O_RDONLY);
+
+  while((line = get_line(fd)) != NULL) {
+    //if last char is '\' and if it is '\' then concatinate the next line.
+    {
+      char *ptr = find_last_char(line, '\\');
+      if(ptr) {
+        *ptr = '\0';
+        ptr = concat_next_line(fd, line);
+        if(ptr) line = ptr;
+      }
+    }//end of concatinate the next line.
+    wordptr = line;
+    word = get_word(&wordptr);
+    if(word == NULL || word[0] == '#') {
+      free(line);
+      line = NULL;
+      continue;
+    }
+    if(!strcmp(word, "auto")) {
+      //add the list of auto interfaces in a list and through error on duplicate interfaces.
+      while((word = get_word(&wordptr)) != NULL) {
+        //check here for duplicate interface.
+        if(llist_is_duplicate_str(conf->auto_interfaces, word))
+          perror_exit("multiple declaration if interface: '%s'", line);
+        dlist_add(&(conf->auto_interfaces), xstrdup(word));
+      }
+      flag_current_interface = AUTO;
+    }//End of Auto.
+    else if(!strcmp(word, "iface")) {
+      char *iface_name;
+      char *iface_af;
+      char *iface_method_name;
+
+      iface_fields = xzalloc(sizeof(*iface_fields));
+      iface_name = get_word(&wordptr);
+      iface_af = get_word(&wordptr);
+      iface_method_name = get_word(&wordptr);
+
+      //check for method name presence.
+      if(iface_method_name == NULL) perror_exit("Method name is missing: '%s'", line);
+
+      //if some more word is there in the line, it is an error.
+      word = get_word(&wordptr);
+      if(word != NULL) perror_exit("Too many parameters: '%s'", line);
+
+      iface_fields->iface_name = xstrdup(iface_name);
+      iface_fields->iface_af = xstrdup(iface_af);
+      iface_fields->iface_method_name = xstrdup(iface_method_name);
+
+      if(!strcmp(iface_fields->iface_af, "inet"))
+        get_method_info(iface_fields, methods);
+      else if(!strcmp(iface_fields->iface_af, "inet6"))
+        get_method_info(iface_fields, methods6);
+      else perror_exit("Unknown address family: '%s'", iface_af);
+
+      //validate for duplicate interface name and address family.
+      validate_list(conf, iface_fields);
+      dlist_add(&(conf->iface), (char *)iface_fields);
+      flag_current_interface = IFACE;
+    }//End of Iface.
+    else if(!strcmp(word, "mapping")) {
+      mapping_fields = xzalloc(sizeof(*mapping_fields));
+
+      //prepare map_ifaces_list
+      mapping_fields->map_ifaces_list = (char **)xzalloc(sizeof(*mapping_fields->map_ifaces_list));
+      while((word = get_word(&wordptr)) != NULL) {
+        mapping_fields->map_ifaces_list[mapping_fields->num_map_ifaces++] = xstrdup(word);
+      }
+      dlist_add(&(conf->mapping), (char *)mapping_fields);
+      flag_current_interface = MAPPING;
+    }//End of Mapping.
+
+    else {
+      //use it for variables.
+      if(flag_current_interface == IFACE) {
+        if(!word || wordptr[0] == '\0')
+          error_exit("No more option is available: '%s'", line);
+        //check for multiple options except up/down/pre-up/post-down
+        validate_dup_var(iface_fields, word);
+        //allocate the memory for the current variable.
+        iface_fields->var = xrealloc(iface_fields->var, sizeof(*iface_fields->var) * (iface_fields->num_vars + 1));
+        iface_fields->var[iface_fields->num_vars].var_name = xstrdup(word);
+        iface_fields->var[iface_fields->num_vars].var_val = xstrdup(wordptr);
+        iface_fields->num_vars++;
+      }//End of Iface fields.
+      else if(flag_current_interface == MAPPING) {
+        if(!strcmp(word, "script")) {
+          //There should be only one script in a mapping.
+          if(mapping_fields->script_name != NULL)
+            error_exit("multiple script in mapping '%s'", line);
+
+          mapping_fields->script_name = xxstrdup(get_word(&wordptr));
+        }
+        else if(!strcmp(word, "map")) {
+          //allocate double pointer memory only one time for a node.
+          if(mapping_fields->mapping_list == NULL)
+            mapping_fields->mapping_list = (char **)xzalloc(sizeof(*mapping_fields->mapping_list));
+          word = get_word(&wordptr);
+          mapping_fields->mapping_list[mapping_fields->num_mapping_items++] = xxstrdup(word);
+        }
+        //if neither script nor map then it will be an error.
+        else error_exit("misplaced option '%s'", line);
+      }//End of mapping fields.
+      else error_exit("Options are misplaced '%s'", line);
+    }//End of else
+    free(line);
+    line = NULL;
+  }
+  xclose(fd);
+  return conf;
+}
+
+/*
+ * Get interface's current state (using the file "/var/run/ifstate")
+ * and prepare the state list based on it.
+ * From the state list, find out the state of the given iface.
+ */
+static void get_iface_state(struct double_list **state_list, struct double_list **cur_iface_state, const char *iface_name)
+{
+  struct double_list *start = NULL, *end = NULL;
+  char *line = NULL, *word = NULL, *wordptr = NULL;
+  int iface_name_len = strlen(iface_name);
+  int fd = open(NETWORK_INTERFACE_STATE_FILE, O_RDONLY);
+  if(fd < 0) return;
+
+  while((line = get_line(fd)) != NULL) {
+    wordptr = line;
+    word = get_word(&wordptr);
+    if(word) dlist_add(state_list, xstrdup(word));
+    free(line);
+    line = NULL;
+  }
+  xclose(fd);
+
+  start = end = *state_list;
+  while(start) {
+    if(!strncmp(start->data, iface_name, iface_name_len)) {
+      char *ptr = &(start->data[iface_name_len]);
+      if(*ptr == '=') {
+        *cur_iface_state = start; //the pointer to the current node.
+        break;
+      }
+    }
+    start = start->next;
+    if(start == end) break;
+  }
+  return;
+}
+
+/*
+ * Free the state list.
+ */
+static void free_state_list(struct double_list *state_list)
+{
+  struct double_list *cur_node = state_list;
+  if(!cur_node) return;
+  cur_node->prev->next = NULL; //break the circular list.
+  while(cur_node) {
+    struct double_list *tmp_node = cur_node;
+    cur_node = cur_node->next;
+    free(tmp_node->data);
+    free(tmp_node);
+  }
+  return;
+}
+
+/*
+ * Deallocate all memories, used for CONFIG_TYPE list.
+ */
+static void clean_config(CONFIG_TYPE **r_conf)
+{
+  CONFIG_TYPE *conf = *r_conf;
+  if(!conf) return;
+
+  //for auto
+  struct double_list *cur_node = conf->auto_interfaces;
+  if(cur_node) {
+    cur_node->prev->next = NULL; //break the circular list.
+    while(cur_node) {
+      struct double_list *tmp = cur_node;
+      cur_node = cur_node->next;
+      free(tmp->data);
+      tmp->data = NULL;
+      free(tmp);
+      tmp = NULL;
+    }
+  }
+
+  //for iface
+  cur_node = conf->iface;
+  if(cur_node) {
+    cur_node->prev->next = NULL; //break the circular list.
+    while(cur_node) {
+      struct double_list *tmp = cur_node;
+      int index = 0;
+      IFACE_FIELDS *iface = (IFACE_FIELDS *) tmp->data;
+
+      free(iface->iface_af);
+      free(iface->iface_method_name);
+      free(iface->iface_name);
+      iface->iface_af = iface->iface_method_name = iface->iface_name = NULL;
+
+      while(index < iface->num_vars) {
+        free(iface->var[index].var_name);
+        free(iface->var[index].var_val);
+        iface->var[index].var_name = iface->var[index].var_val = NULL;
+        index++;
+      }
+      free(iface->var);
+      iface->var = NULL;
+
+      cur_node = cur_node->next;
+      free(tmp);
+    }
+  }
+  //for mapping.
+  cur_node = conf->mapping;
+  if(cur_node) {
+    cur_node->prev->next = NULL; //break the circular list.
+    while(cur_node) {
+      struct double_list *tmp = cur_node;
+      int index = 0;
+      MAPPING_FIELDS *mapping = (MAPPING_FIELDS *) tmp->data;
+      if(mapping->script_name) {
+        free(mapping->script_name);
+        mapping->script_name = NULL;
+      }
+      while(index < mapping->num_map_ifaces) {
+        free(mapping->map_ifaces_list[index]);
+        mapping->map_ifaces_list[index] = NULL;
+        index++;
+      }
+      free(mapping->map_ifaces_list);
+      mapping->map_ifaces_list = NULL;
+      index = 0;
+      while(index < mapping->num_mapping_items) {
+        if(mapping->mapping_list[index]) {
+         free(mapping->mapping_list[index]);
+          mapping->mapping_list[index] = NULL;
+        }
+        index++;
+      }
+      free(mapping->mapping_list);
+      mapping->mapping_list = NULL;
+
+      cur_node = cur_node->next;
+      free(tmp);
+    }
+  }
+  free(conf);
+  conf = NULL;
+  return;
+}
+
+/*
+ * Duplicate the given fd.
+ */
+void xdup2(int oldfd, int newfd)
+{
+  if(oldfd == newfd) return;
+  if(dup2(oldfd, newfd) != newfd)
+    perror_exit("can't duplicate file descriptor");
+  close(oldfd);
+  return;
+}
+
+/*
+ * Execute the current mapping.
+ */
+static char *execute_mapping(MAPPING_FIELDS *mapping_fields, char *data)
+{
+  FILE *wr_fp;
+  pid_t pid, w_pid;
+  char *argv[] = { mapping_fields->script_name, data, NULL };
+  int rd_pfields[2] = {-1, -1};
+  int wr_pfields[2] = {-1, -1};
+  int index = 0, status = 0;
+
+  char *liface = xstrdup(data);
+
+  if(pipe(rd_pfields)) perror_exit("can't create pipe");
+  if(pipe(wr_pfields)) perror_exit("can't create pipe");
+  fflush(NULL);
+
+  switch(pid = vfork()) {
+    case -1:// error
+      perror_exit("error in creating child process");
+      break;
+    case 0: { //child
+              xclose(rd_pfields[1]); //close write fd of child.
+              xclose(wr_pfields[0]); //close read fd of child.
+              xdup2(rd_pfields[0], 0);
+              xdup2(wr_pfields[1], 1);
+              execvp(argv[0], argv);
+              toys.exitval = (errno == ENOENT) ? 127 : 126;
+              perror_exit("can't execute '%s'", argv[0]);
+            }
+            break;
+  }
+  //parent
+  xclose(rd_pfields[0]); //close read fd of parent.
+  xclose(wr_pfields[1]); //close write fd of parent.
+
+  //fd -> file pointer.
+  wr_fp = fdopen(rd_pfields[1], (((rd_pfields[1] << 1) + 1) & 1) ? "w" : "r");
+  if(!wr_fp) error_exit("out of memory");
+
+  //Write mappings to stdin of mapping script.
+  while(index < mapping_fields->num_map_ifaces)
+    fprintf(wr_fp, "%s\n", mapping_fields->mapping_list[index++]);
+
+  fclose(wr_fp);
+
+  //wait for child process termination and get the status of the child process.
+  do
+    w_pid = waitpid(pid, &status, 0);
+  while((w_pid == -1) && (errno == EINTR));
+
+  if (WIFEXITED(status) && !WEXITSTATUS(status)) {
+    //script executed successfully. Get the new interface name.
+    char *new_iface = get_line(wr_pfields[0]);
+    if(new_iface) {
+      char *ptr = &new_iface[strlen(new_iface) - 1];
+      while(ptr >= new_iface && isspace(*ptr))
+        *(ptr--) = '\0';
+      free(liface);
+      liface = new_iface;
+    }
+  }
+  xclose(wr_pfields[0]);
+
+  return liface;
+}
+
+/*
+ * Update the current state of the interface.
+ * write the current state of interface in the file "/var/run/ifstate" -> for ifup.
+ * Remove the interface from the file "/var/run/ifstate" -> for ifdown.
+ */
+static void update_cur_state(const char *data, const char *iface_name)
+{
+  FILE *fp;
+  struct double_list *state_list = NULL;
+  struct double_list *cur_iface_state = NULL;
+  struct double_list *cur_node = NULL;
+  get_iface_state(&state_list, &cur_iface_state, data);
+
+  //for ifup.
+  if(TT.isup) {
+    char *iface = xmsprintf("%s=%s", data, iface_name);
+    if(!cur_iface_state) dlist_add(&state_list, iface);
+    else {
+      free(cur_iface_state->data);
+      cur_iface_state->data = NULL;
+      cur_iface_state->data = iface;
+    }
+  }
+  //for ifdown.
+  else {
+    struct double_list *start_node = state_list;
+    while(state_list) {
+      //if only one node is in the list.
+      if((state_list == cur_iface_state) && (state_list->next == state_list)) {
+        free(state_list->data);
+        state_list->data = NULL;
+        free(state_list);
+        state_list = NULL;
+        break;
+      }
+      //if more than one node is in the list.
+      if(state_list == cur_iface_state) {
+        state_list->prev->next = state_list->next;
+        state_list->next->prev = state_list->prev;
+        state_list = state_list->next;
+        cur_iface_state->next = cur_iface_state->prev = NULL;
+        free(cur_iface_state->data);
+        free(cur_iface_state);
+        break;
+      }
+      state_list = state_list->next;
+      if(state_list == start_node) break;
+    }
+  }
+  fp = xfopen(NETWORK_INTERFACE_STATE_FILE, "w");
+  cur_node = state_list;
+  while(cur_node) {
+    if(cur_node->data) fprintf(fp, "%s\n", cur_node->data);
+    cur_node = cur_node->next;
+    if(cur_node == state_list) break;
+  }
+  fclose(fp);
+  free_state_list(state_list);
+  return;
+}
+
+/*
+ * configure the interface.
+ */
+static int config_interface(CONFIG_TYPE *conf, const char **interface)
+{
+  struct double_list *curlist = NULL;
+  struct double_list *start = NULL;
+  int is_cmd_failed = 0;
+
+  if(interface && *interface) {
+    while(*interface) {
+      dlist_add(&curlist, (char *)*interface);
+      interface++;
+    }
+  }
+  else curlist = conf->auto_interfaces;
+
+  start = curlist;
+  //go through one by one interfaces.
+  while(curlist) {
+    char *data, *iface_name, *ptr;
+    int cmd_status = 0;
+    int visited_iface = 0;
+
+    data = iface_name = ptr = NULL;
+    data = xstrdup(curlist->data);
+    ptr = strchr(data, '=');
+    if(ptr) {
+      *ptr = '\0';
+      ptr++;
+    }
+    else ptr = data;
+    iface_name = xstrdup(ptr);
+
+    //with "-f" option; get the current state of the interface.
+    //otherwise ignore the current state of the interface.
+    if(!(toys.optflags & FLAG_f)) {
+      struct double_list *state_list = NULL;
+      struct double_list *cur_iface_state = NULL;
+      get_iface_state(&state_list, &cur_iface_state, data);
+      //"ifup" and current interface is already configured - error message.
+      if(TT.isup && cur_iface_state) {
+        error_msg("interface '%s' already configured", data);
+        free_state_list(state_list);
+        goto NEXT_INTERFACE;
+      }
+      //"ifdown" and current interface is not configured - error message.
+      else if(!TT.isup && !cur_iface_state) {
+        error_msg("interface '%s' not configured", data);
+        free_state_list(state_list);
+        goto NEXT_INTERFACE;
+      }
+      free_state_list(state_list);
+    }
+    //For mapping.
+    if(TT.isup && !(toys.optflags & FLAG_m)) {
+      struct double_list *m_list, *list_ptr;
+      list_ptr = m_list = conf->mapping;
+      while(m_list) {
+        int index = 0;
+        MAPPING_FIELDS *mapping_fields = (MAPPING_FIELDS *) m_list->data;
+        while(index < mapping_fields->num_map_ifaces) {
+
+          if(fnmatch(mapping_fields->map_ifaces_list[index++], iface_name, 0))
+            continue;
+          if(toys.optflags & FLAG_v)
+            xprintf("Running mapping script '%s' on '%s'\n", mapping_fields->script_name, iface_name);
+          if(iface_name) {
+                 free(iface_name);
+                 iface_name = NULL;
+          }
+          iface_name = execute_mapping(mapping_fields, data);
+          break;
+        }
+        m_list = m_list->next;
+        if(m_list == list_ptr) break;
+      }
+    }//End of mapping.
+
+    //For iface.
+    {
+      struct double_list *iface_list, *list_ptr;
+      iface_list = list_ptr = conf->iface;
+
+      while(iface_list) {
+        IFACE_FIELDS *cur_iface = (IFACE_FIELDS *) iface_list->data;
+        if(!strcmp(iface_name, cur_iface->iface_name)) {
+          char *old_iface = NULL;
+          int is_new_iface = 0;
+
+          visited_iface = 1;
+
+          //validate the new iface name, which is coming from "abc=def" format, and the list's iface name.
+          if(strcmp(cur_iface->iface_name, data)) {
+            old_iface = cur_iface->iface_name;
+            cur_iface->iface_name = data;
+            is_new_iface = 1;
+          }
+          cmd_status = cur_iface->iface_method->funptr(cur_iface);
+
+          //-1 and 0 are the failure status.
+          if(cmd_status == -1) {
+            error_msg("Missing variable for %s/%s", iface_name, cur_iface->iface_af);
+            is_cmd_failed = 1;
+          }
+          else if(cmd_status == 0) is_cmd_failed = 1;
+          if(is_new_iface) cur_iface->iface_name = old_iface;
+        }
+        iface_list = iface_list->next;
+        if(iface_list == list_ptr) break;
+      }
+    }
+
+    if(!visited_iface && !(toys.optflags & FLAG_f)) {
+      error_msg("ignoring unknown interface %s", iface_name);
+      is_cmd_failed = 1;
+    }
+    //with "-n" option; donot update the current state of the interface.
+    else if(!(toys.optflags & FLAG_n))
+      update_cur_state(data, iface_name);
+
+NEXT_INTERFACE:
+    free(data);
+    free(iface_name);
+    curlist = curlist->next;
+    //already visited all the nodes.
+    if(start == curlist) break;
+  }
+  return is_cmd_failed;
+}
+
+/*
+ * ifup/ifdown main function.
+ */
+void ifup_main(void)
+{
+#define DEFAULT_INTERFACE_FILE_PATH "/etc/network/interfaces"
+  CONFIG_TYPE *conf;
+  char **argv = toys.optargs;
+  TT.isup = (toys.which->name[2] == 'u');
+
+  //"-a" and interface name are mutual-exclusive.
+  if((toys.optflags & FLAG_a) && *argv)
+    show_ifup_help();
+  else if(!(toys.optflags & FLAG_a) && !*argv)
+    show_ifup_help();
+
+  //parse the file and prepare the list of interfaces.
+  if(toys.optflags & FLAG_i) conf = read_interface_file(TT.interface_filename);
+  else conf = read_interface_file(DEFAULT_INTERFACE_FILE_PATH);
+
+  TT.g_path_var = xxstrdup(getenv("PATH"));
+  TT.g_shell = get_shell();
+
+  //With "-a" option there should not be any interface.
+  if(toys.optflags & FLAG_a) toys.exitval = config_interface(conf, NULL);
+  //Without "-a" option there should be an interface.
+  else toys.exitval = config_interface(conf, (const char **)argv);
+
+  if(TT.g_path_var) free((char *)TT.g_path_var);
+  free((char *)TT.g_shell);
+  clean_config(&conf);
+  return;
+#undef DEFAULT_INTERFACE_FILE_PATH
+}//End of main function.
diff --git a/toys/other/init.c b/toys/other/init.c
new file mode 100644 (file)
index 0000000..7df7197
--- /dev/null
@@ -0,0 +1,522 @@
+/* init.c - init program.
+ *
+ * Copyright 2012 Harvind Singh <harvindsingh1981@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN))
+
+config INIT
+  bool "init"
+  default y
+  help
+    usage: init
+
+    init the system.
+*/
+
+#include "toys.h"
+#include<linux/vt.h>
+#include<sys/types.h>
+#include<sys/reboot.h>
+struct action_list_seed {
+  struct action_list_seed *next;
+  pid_t pid;
+  uint8_t action;
+  char terminal_name[40];
+  char command[256];
+};
+struct action_list_seed *action_list_pointer = NULL;
+int caught_signal;
+
+//INITTAB action defination
+#define SYSINIT 0x01
+#define WAIT  0x02
+#define ONCE  0x04
+#define RESPAWN  0x08
+#define ASKFIRST  0x10
+#define CTRLALTDEL  0x20
+#define  SHUTDOWN  0x40
+#define  RESTART  0x80
+
+static void initialize_console(void)
+{
+  int file_dis;
+  char *p;
+  p = getenv("CONSOLE");
+  if (p) xprintf("CONSOLE found----%s\n",p);
+  else {
+    p = getenv("console");
+    if (p) xprintf("console found----%s\n",p);
+  }
+  if (!p) {
+    file_dis = open("/dev/null", O_RDWR);
+    if (file_dis >= 0) {
+      while(file_dis < 2) file_dis = dup(file_dis);
+      while(file_dis > 2) close(file_dis--);
+    }
+  } else {
+    file_dis = open(p, O_RDWR | O_NONBLOCK | O_NOCTTY);
+    if (file_dis < 0) xprintf("Unable to open console %s\n",p);
+    else {
+      dup2(file_dis,0);
+      dup2(file_dis,1);
+      dup2(file_dis,2);
+    }
+  }
+  p = getenv("TERM");
+#ifdef VT_OPENQRY
+  int terminal_no;
+  if (ioctl(0,VT_OPENQRY,&terminal_no)) {
+    if (!p || strcmp(p,"linux") == 0) putenv((char*)"TERM=vt102");
+  } else
+#endif  
+  { 
+    if (!p) putenv((char*)"TERM=linux");
+  }
+}
+static void set_sane_term(void)
+{
+  struct termios terminal;
+  tcgetattr(0, &terminal);
+  terminal.c_cc[VINTR] = 3;//ctrl-c
+  terminal.c_cc[VQUIT] = 28;/*ctrl-\*/
+  terminal.c_cc[VERASE] = 127;//ctrl-?
+  terminal.c_cc[VKILL] = 21;//ctrl-u
+  terminal.c_cc[VEOF] = 4;//ctrl-d
+  terminal.c_cc[VSTART] = 17;//ctrl-q
+  terminal.c_cc[VSTOP] = 19;//ctrl-s
+  terminal.c_cc[VSUSP] = 26;//ctrl-z
+
+  terminal.c_line = 0;
+  terminal.c_cflag = terminal.c_cflag&(CRTSCTS|PARODD|PARENB|CSTOPB|CSIZE|CBAUDEX|CBAUD);
+  terminal.c_cflag = terminal.c_cflag|(CLOCAL|HUPCL|CREAD);
+  terminal.c_iflag = IXON|IXOFF|ICRNL;//enable start/stop input and output control + map CR to NL on input
+  terminal.c_oflag = ONLCR|OPOST;//Map NL to CR-NL on output
+  terminal.c_lflag = IEXTEN|ECHOKE|ECHOCTL|ECHOK|ECHOE|ECHO|ICANON|ISIG;//extended input character processing+ + +echo kill+echo erased character+
+  tcsetattr(0, TCSANOW, &terminal);
+}
+
+static void set_enviornment(void)
+{
+  putenv((char*)"HOME=/");
+  putenv((char*)"PATH=/sbin:/usr/sbin:/bin:/usr/bin");
+  putenv((char*)"SHELL=/bin/sh");
+  putenv((char*)"USER=root");
+}
+static void add_new_action(uint8_t action,char *command,char *term)
+{
+  struct action_list_seed *x,**y;
+  y = &action_list_pointer;
+  x = *y;
+  while(x != NULL) {
+    if ((strcmp(x->command,command) == 0) && (strcmp(x->terminal_name,term) == 0)) {
+      *y = x->next;//remove from the list
+      while(*y != NULL) y = &(*y)->next;//traverse through list till end
+      x->next = NULL;
+      break;
+    }
+    y = &(x)->next;
+    x = *y;
+  }
+  //create a new node
+  if (x == 0) x = xzalloc(sizeof(*x));
+  *y = x;
+  x->action = action;
+  strncpy(x->command, command, sizeof(x->command));
+  x->command[sizeof(x->command) - 1] = '\0';
+  strncpy(x->terminal_name, term, sizeof(x->terminal_name));
+  x->terminal_name[sizeof(x->terminal_name) - 1] = '\0';
+}
+static void console_message(const char *format, ...)
+{
+  char message[128];
+  va_list arguments;
+  unsigned int length;
+  ssize_t x;
+
+  va_start(arguments, format);
+  length = vsnprintf(message,sizeof(message)-2/*reserve space for \n and \0*/,format,arguments)+1;
+  if (length > (sizeof(message) - 1)) length = sizeof(message) - 1;
+  va_end(arguments);
+  message[length++] = '\n';
+  message[length] = '\0';
+  do {
+    x = write(2, message, length);//ensure write is not effected by any signal.
+  } while(x < 0 && errno == EINTR);
+}
+static void inittab_parsing(void)
+{
+#define ONE_TOKEN 1
+#define TWO_TOKENS 2
+#define THREE_TOKENS 3
+#define FOUR_TOKENS 4
+  FILE *fp;
+  char file_line_buffer[256];
+  char terminal_name[40];
+  char command[256];
+  uint8_t action = 0;
+  char *p;
+  char *extracted_token;
+  int line_number = 0;
+  int token_count = 0;
+
+  fp = fopen("/etc/inittab","r");
+  if (fp == NULL) {
+    console_message("Unable to open /etc/inittab. Using Default inittab");
+    add_new_action(SYSINIT, "/etc/init.d/rcS", "");
+    add_new_action(RESPAWN, "/sbin/getty -n -l /bin/sh -L 115200 tty1 vt100", "");
+  } else {
+    while((p = fgets(file_line_buffer,sizeof(file_line_buffer),fp)) != NULL) { //read single line from /etc/inittab
+      line_number++;
+      token_count = 0;
+      action = 0;
+      while((extracted_token = strsep(&p,":")) != NULL) {
+        token_count++;
+        switch (token_count) {
+          case ONE_TOKEN:
+            strcpy(terminal_name,"/dev/");
+            if (*extracted_token != '\0') {
+              strncat(terminal_name, extracted_token, 40);//prepend /dev/ to terminal name
+              terminal_name[39] = '\0';
+            }
+            else strcpy(terminal_name, "");
+            break;
+          case TWO_TOKENS:
+            break;
+          case THREE_TOKENS:
+            if (!strcmp(extracted_token, "sysinit")) action = 0x01;
+            else if (!strcmp(extracted_token, "askfirst")) action = 0x10;  
+            else if (!strcmp(extracted_token, "ctrlaltdel")) action = 0x20;  
+            else if (!strcmp(extracted_token,"respawn"))action = 0x08;  
+            else if (!strcmp(extracted_token, "shutdown"))action = 0x40;  
+            else if (!strcmp(extracted_token, "restart")) action = 0x80;  
+            else if (!strcmp(extracted_token, "wait")) action = 0x02;  
+            else if (!strcmp(extracted_token, "once")) action = 0x04;
+            else console_message("Invalid action at line number %d ---- ignoring",line_number);
+            break;
+          case FOUR_TOKENS:
+            strncpy(command, strsep(&extracted_token, "\n"), sizeof(command));//eliminate trailing \n character using strsep;
+            command[sizeof(command) - 1] = '\0';
+            break;
+          default:
+            break;
+        }
+      }  //while token
+      if (action != 0) add_new_action(action, command, terminal_name);  
+    }//while line
+    fclose(fp);
+    fp = NULL;
+  }
+#undef ONE_TOKEN
+#undef TWO_TOKENS
+#undef THREE_TOKENS
+#undef FOUR_TOKENS
+}
+static void run_command(char *command)
+{
+  char *final_command[128];
+  char temp_buffer[256+6];
+  int hyphen;
+  hyphen = (command[0]=='-');
+  command = command + hyphen;
+  if (strpbrk(command, "?<>'\";[]{}\\|=()*&^$!`~") == NULL) {
+    char *next_command;
+    char *extracted_command;
+    int x = 0;
+    next_command = strcpy(temp_buffer, command - hyphen);
+    command = next_command + hyphen;
+    while((extracted_command = strsep(&next_command," \t")) != NULL) {
+      if (*extracted_command != '\0') {
+        final_command[x] = extracted_command;
+        x++;
+      }
+    }
+    final_command[x] = NULL;
+  } else {
+    sprintf(temp_buffer, "exec %s", command);
+    command = "-/bin/sh"+1;
+    final_command[0] = (char*)("-/bin/sh"+!hyphen);
+    final_command[1] = (char*)"-c";
+    final_command[2] = temp_buffer;
+    final_command[3] = NULL;
+  }
+  if (hyphen) ioctl(0,TIOCSCTTY,0);
+  execvp(command, final_command);
+  console_message("unable to run %s",command);
+}
+//runs all same type of actions
+static pid_t final_run(struct action_list_seed *x)
+{
+  pid_t pid;
+  int fd;
+  sigset_t signal_set;
+  sigfillset(&signal_set);
+  sigprocmask(SIG_BLOCK, &signal_set, NULL);
+  if (x->action & ASKFIRST) pid = fork();
+  else pid = vfork();
+  if (pid != 0) {
+    //parent process or error
+    //unblock the signals
+    sigfillset(&signal_set);
+    sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
+    return pid;      
+  }
+  //new born child process
+  //reset signals to default behavior and remove signal mask
+  signal(SIGUSR1,SIG_DFL);
+  signal(SIGUSR2,SIG_DFL);
+  signal(SIGTERM,SIG_DFL);
+  signal(SIGQUIT,SIG_DFL);
+  signal(SIGINT,SIG_DFL);
+  signal(SIGHUP,SIG_DFL);
+  signal(SIGTSTP,SIG_DFL);
+  signal(SIGSTOP,SIG_DFL);
+  sigset_t signal_set_c;
+  sigfillset(&signal_set_c);
+  sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL);
+  setsid();//new session
+  if (x->terminal_name[0]) {
+    close(0);
+    fd = open(x->terminal_name,(O_RDWR|O_NONBLOCK),0600);
+    if (fd != 0) {
+      console_message("Unable to open %s,%s\n",x->terminal_name,strerror(errno));
+      _exit(EXIT_FAILURE);
+    } else {
+      dup2(0,1);
+      dup2(0,2);
+    }
+  }
+  set_sane_term();
+  /*******code for ASKFIRST handling to be written******************/
+  //to be clarified because this feature is specific to busy box; 
+  /*****************************************************************/
+  run_command(x->command);
+  _exit(-1);
+}
+static struct action_list_seed* mark_as_terminated_process(pid_t pid)
+{
+  struct action_list_seed *x;
+  if (pid > 0) {
+    for (x = action_list_pointer; x; x = x->next) {
+      if (x->pid == pid) {
+        x->pid = 0;
+        return x;
+      }
+    }
+  }
+  return NULL;
+}
+static void waitforpid(pid_t pid)
+{
+  if (pid <= 0) return;
+  for(;;) {
+    pid_t y = wait(NULL);
+    mark_as_terminated_process(y);
+    if (kill(y,0)) break;
+  }
+}
+static void run_action_from_list(int action)
+{
+  struct action_list_seed *x;
+  pid_t pid;
+  x = action_list_pointer;
+  for (x = action_list_pointer; x; x = x->next) {
+    if ((x->action & action) == 0) continue;
+    if (x->action & (SHUTDOWN|ONCE|SYSINIT|CTRLALTDEL|WAIT)) {
+      pid = final_run(x);
+      if (x->action&(SHUTDOWN|SYSINIT|CTRLALTDEL|WAIT)) waitforpid(pid);
+    }
+    if (x->action & (ASKFIRST|RESPAWN))
+      if (!(x->pid)) x->pid = final_run(x);
+  }
+}
+static void halt_poweroff_reboot_handler(int sig_no)
+{
+  unsigned int reboot_magic_no = 0;
+  pid_t pid;
+  signal(SIGUSR1,SIG_DFL);
+  signal(SIGUSR2,SIG_DFL);
+  signal(SIGTERM,SIG_DFL);
+  signal(SIGQUIT,SIG_DFL);
+  signal(SIGINT,SIG_DFL);
+  signal(SIGHUP,SIG_DFL);
+  signal(SIGTSTP,SIG_DFL);
+  signal(SIGSTOP,SIG_DFL);
+  sigset_t signal_set_c;
+  sigfillset(&signal_set_c);
+  sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL);
+  run_action_from_list(SHUTDOWN);
+  console_message("The system is going down NOW!");
+  kill(-1, SIGTERM);
+  console_message("Sent SIGTERM to all processes");
+  sync();
+  sleep(1);
+  kill(-1,SIGKILL);
+  sync();
+  switch (sig_no) {
+    case SIGUSR1:
+      console_message("Requesting system halt");
+      reboot_magic_no=RB_HALT_SYSTEM;
+      break;
+    case SIGUSR2:
+      console_message("Requesting system poweroff");
+      reboot_magic_no=RB_POWER_OFF;
+      break;
+    case SIGTERM:  
+      console_message("Requesting system reboot");
+      reboot_magic_no=RB_AUTOBOOT;
+      break;
+    default:
+      break;
+  }
+  sleep(1);
+  pid = vfork();
+  if (pid == 0) {
+    reboot(reboot_magic_no);
+    _exit(EXIT_SUCCESS);
+  }
+  while(1) sleep(1);
+}
+static void restart_init_handler(int sig_no)
+{
+  struct action_list_seed *x;
+  pid_t pid;
+  int fd;
+  for (x = action_list_pointer; x; x = x->next) {
+    if (!(x->action&RESTART)) continue;
+    signal(SIGUSR1,SIG_DFL);
+    signal(SIGUSR2,SIG_DFL);
+    signal(SIGTERM,SIG_DFL);
+    signal(SIGQUIT,SIG_DFL);
+    signal(SIGINT,SIG_DFL);
+    signal(SIGHUP,SIG_DFL);
+    signal(SIGTSTP,SIG_DFL);
+    signal(SIGSTOP,SIG_DFL);
+    sigset_t signal_set_c;
+    sigfillset(&signal_set_c);
+    sigprocmask(SIG_UNBLOCK,&signal_set_c,NULL);
+    run_action_from_list(SHUTDOWN);
+    console_message("The system is going down NOW!");
+    kill(-1,SIGTERM);
+    console_message("Sent SIGTERM to all processes");
+    sync();
+    sleep(1);
+    kill(-1,SIGKILL);
+    sync();
+
+    if (x->terminal_name[0]) {
+      close(0);
+      fd = open(x->terminal_name,(O_RDWR|O_NONBLOCK),0600);
+      if (fd != 0) {
+        console_message("Unable to open %s,%s\n",x->terminal_name,strerror(errno));
+        sleep(1);
+        pid = vfork();
+        if (pid == 0) {
+          reboot(RB_HALT_SYSTEM);
+          _exit(EXIT_SUCCESS);
+        }
+        while(1) sleep(1);
+      } else {
+        dup2(0,1);
+        dup2(0,2);
+        set_sane_term();
+        run_command(x->command);
+      }
+    }
+  }
+}
+static void catch_signal(int sig_no)
+{
+  caught_signal = sig_no;
+  console_message("signal seen");
+}
+static void pause_handler(int sig_no)
+{
+  int signal_backup,errno_backup;
+  pid_t pid;
+  errno_backup = errno;
+  signal_backup = caught_signal;
+  signal(SIGCONT, catch_signal);
+  while(1) {
+    if (caught_signal == SIGCONT) break;
+    do
+      pid = waitpid(-1,NULL,WNOHANG);
+    while((pid==-1) && (errno=EINTR));
+    mark_as_terminated_process(pid);
+    sleep(1);
+  }
+  signal(SIGCONT,SIG_DFL);
+  errno = errno_backup;
+  caught_signal = signal_backup;
+}
+static void assign_signal_handler(void)
+{
+  struct sigaction sig_act;
+  signal(SIGUSR1, halt_poweroff_reboot_handler);//halt
+  signal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
+  signal(SIGTERM, halt_poweroff_reboot_handler);//reboot
+  signal(SIGQUIT, restart_init_handler);//restart init
+  memset(&sig_act, 0, sizeof(sig_act));
+  sigfillset(&sig_act.sa_mask);
+  sigdelset(&sig_act.sa_mask, SIGCONT);
+  sig_act.sa_handler = pause_handler;
+  sigaction(SIGTSTP, &sig_act, NULL);
+  sigaction(SIGSTOP, &sig_act, NULL);
+  memset(&sig_act, 0, sizeof(sig_act));
+  sig_act.sa_handler = catch_signal;
+  sigaction(SIGINT, &sig_act, NULL);
+  memset(&sig_act, 0, sizeof(sig_act));
+  sig_act.sa_handler = catch_signal;
+  sigaction(SIGHUP, &sig_act, NULL);  
+}
+
+static int check_if_pending_signals(void)
+{
+  int signal_caught = 0;
+  while(1) {
+    int sig = caught_signal;
+    if (!sig) return signal_caught;
+    caught_signal = 0;
+    signal_caught = 1;
+    if (sig == SIGINT) run_action_from_list(CTRLALTDEL);
+  }
+}
+void init_main(void)
+{
+  if (getpid() != 1) {
+    xprintf("Already running\n");
+    return;
+  }
+  xprintf("Starting init......\n");
+  initialize_console();
+  set_sane_term();
+
+  if (chdir("/") != 0) xprintf("cannot change directory to '/'\n");
+  if (!setsid()) xprintf("Initial setsid() failed\n");
+
+  set_enviornment();
+  inittab_parsing();  
+  assign_signal_handler();
+  run_action_from_list(SYSINIT);
+  check_if_pending_signals();
+  run_action_from_list(WAIT);
+  check_if_pending_signals();
+  run_action_from_list(ONCE);
+  while(1) {
+    int suspected_WNOHANG;
+    suspected_WNOHANG = check_if_pending_signals();
+    run_action_from_list(RESPAWN | ASKFIRST);
+    suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
+    sleep(1);//let cpu breath
+    suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
+    if (suspected_WNOHANG) suspected_WNOHANG=WNOHANG;
+    while(1) {
+      pid_t pid;
+      pid = waitpid(-1, NULL, suspected_WNOHANG);
+      if (pid <= 0) break;
+      mark_as_terminated_process(pid);
+      suspected_WNOHANG = WNOHANG;
+    }
+  }
+}
diff --git a/toys/other/inotifyd.c b/toys/other/inotifyd.c
new file mode 100644 (file)
index 0000000..48c89d7
--- /dev/null
@@ -0,0 +1,187 @@
+/* inotifyd.c -  inotify daemon. 
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv
+
+USE_INOTIFYD(NEWTOY(inotifyd, "<2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config INOTIFYD
+  bool "inotifyd"
+  default y
+  help
+  usage: inotifyd PROG FILE[:mask] ...
+
+    Run PROG on filesystem changes.
+    When a filesystem event matching MASK occurs on FILEn,
+    PROG ACTUAL_EVENTS FILEn [SUBFILE] is run.
+    If PROG is -, events are sent to stdout.
+
+    Events:
+      a   File is accessed
+      c   File is modified
+      e   Metadata changed
+      w   Writable file is closed
+      0   Unwritable file is closed
+      r   File is opened
+      D   File is deleted
+      M   File is moved
+      u   Backing fs is unmounted
+      o   Event queue overflowed
+      x   File can't be watched anymore
+    If watching a directory:
+      m   Subfile is moved into dir
+      y   Subfile is moved out of dir
+      n   Subfile is created
+      d   Subfile is deleted
+
+    inotifyd waits for PROG to exit.
+    When x event happens for all FILEs, inotifyd exits.
+*/
+
+#define FOR_inotifyd
+#include "toys.h"
+#include <sys/inotify.h>
+
+GLOBALS(
+  int gotsignal;
+)
+
+struct mask_nameval {
+  char name;
+  unsigned long val;
+};
+
+static void sig_handler(int sig)
+{
+  TT.gotsignal = sig;
+}
+
+static int exec_wait(char **args)
+{
+  int status = 0;
+  pid_t pid = fork();
+
+  if(pid == 0) xexec(args);
+  else waitpid(pid, &status, 0);
+  return WEXITSTATUS(status);
+}
+
+void inotifyd_main(void)
+{
+  int mask;
+  struct pollfd fds;
+  char **files = NULL, **restore = NULL;
+  char *prog_args[5];
+  struct mask_nameval mask_nv[] = {
+    { 'a', IN_ACCESS }, /* File was accessed */
+    { 'c', IN_MODIFY }, /* File was modified */
+    { 'e', IN_ATTRIB }, /* Metadata changed */
+    { 'w', IN_CLOSE_WRITE }, /* Writtable file was closed */
+    { '0', IN_CLOSE_NOWRITE }, /* Unwrittable file closed */
+    { 'r', IN_OPEN }, /* File was opened */
+    { 'm', IN_MOVED_FROM }, /* File was moved from X */
+    { 'y', IN_MOVED_TO }, /* File was moved to Y */
+    { 'n', IN_CREATE }, /* Subfile was created */
+    { 'd', IN_DELETE }, /* Subfile was deleted */
+    { 'D', IN_DELETE_SELF }, /* Self was deleted */
+    { 'M', IN_MOVE_SELF }, /* Self was moved */
+    { 'u', IN_UNMOUNT }, /* Backing fs was unmounted */
+    { 'o', IN_Q_OVERFLOW }, /* Event queued overflowed */
+    { 'x', IN_IGNORED }, /* File was ignored */
+  };
+  int masks_len = ARRAY_LEN(mask_nv);
+
+  prog_args[0] = toys.optargs[0];
+  prog_args[4] = NULL;
+
+  toys.optc--; //1st one is program, rest are files to be watched
+  restore = files = toys.optargs; //wd ZERO is not used, hence toys.optargs is assigned to files.
+
+  fds.fd = inotify_init();
+  if(fds.fd == -1) perror_exit("inotify initialztion failed");
+
+  while(*++toys.optargs) {
+    char *path = *toys.optargs;
+    char *masks = strchr(path, ':');
+    mask = 0x0fff; //assuming all non-kernel events to be notified.
+
+    if(masks) {
+      *masks++ = '\0';
+      mask = 0; 
+      while(*masks) {
+        int i = 0;
+        for(i = 0; i < masks_len; i++) {
+          if(*masks == mask_nv[i].name) {
+            mask |= mask_nv[i].val;
+            break;
+          }
+        }
+        if(i == masks_len) error_exit("wrong mask '%c'",*masks);
+        masks++;
+      }
+    }
+
+    if(inotify_add_watch(fds.fd, path, mask) < 0) perror_exit("add watch '%s' failed", path);
+  }
+
+  toys.optargs = restore;
+  sigatexit(sig_handler);
+  fds.events = POLLIN;
+
+  while(1) {
+    int ret = 0, queue_len;
+    void *buf = NULL;
+    struct inotify_event *event;
+retry:
+    if(TT.gotsignal) break;
+    ret = poll(&fds, 1, -1);
+    if(ret < 0 && errno == EINTR) goto retry;
+    if(ret <= 0) break;
+
+    ret = ioctl(fds.fd, FIONREAD, &queue_len);
+    if(ret < 0) perror_exit("ioctl FIONREAD failed");
+
+    event = buf = xmalloc(queue_len);
+    queue_len = readall(fds.fd, buf, queue_len);
+    while(queue_len > 0) {
+      uint32_t m = event->mask;
+      if(m) {
+        char evts[masks_len+1];
+        char *s = evts;
+        int i = 0;
+        for(i = 0; i < masks_len; i++) {
+          if(m & mask_nv[i].val) *s++ = mask_nv[i].name;
+        }
+        *s = '\0';
+
+        if(prog_args[0][0] == '-' && prog_args[0][1] == '\0') { //stdout
+          printf(event->len ? "%s\t%s\t%s\n" : "%s\t%s\n", evts,
+              files[event->wd],
+              event->name);
+          fflush(stdout);
+        } else {
+          prog_args[1] = evts;
+          prog_args[2] = files[event->wd];
+          prog_args[3] = event->len?event->name : NULL;
+
+          //exec and wait...
+          exec_wait(prog_args);
+        }
+        if(event->mask & IN_IGNORED) {
+          if(--toys.optc <= 0) {
+            free(buf);
+            goto done;
+          }
+          inotify_rm_watch(fds.fd, event->wd);
+        }
+      }
+
+      queue_len -= sizeof(struct inotify_event) + event->len;
+      event = (void*)((char*)event + sizeof(struct inotify_event) + event->len); //next event
+    }
+    free(buf);
+  }
+done:
+  toys.exitval = TT.gotsignal;
+}
diff --git a/toys/other/insmod.c b/toys/other/insmod.c
new file mode 100644 (file)
index 0000000..8aa959a
--- /dev/null
@@ -0,0 +1,42 @@
+/* insmod.c - Load a module into the Linux kernel.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_INSMOD(NEWTOY(insmod, "<1", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config INSMOD
+  bool "insmod"
+  default y
+  help
+    usage: insmod MODULE [MODULE_OPTIONS]
+
+    Load the module named MODULE passing options if given.
+*/
+
+#include "toys.h"
+
+#include <sys/syscall.h>
+#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+
+void insmod_main(void)
+{
+  char * buf = NULL;
+  int len, res, i;
+  int fd = xopen(toys.optargs[0], O_RDONLY);
+
+  len = fdlength(fd);
+  buf = xmalloc(len);
+  xreadall(fd, buf, len);
+
+  i = 1;
+  while(toys.optargs[i] &&
+    strlen(toybuf) + strlen(toys.optargs[i]) + 2 < sizeof(toybuf)) {
+    strcat(toybuf, toys.optargs[i++]);
+    strcat(toybuf, " ");
+  }
+
+  res = init_module(buf, len, toybuf);
+  if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
+
+  if (res) perror_exit("failed to load %s", toys.optargs[0]);
+}
diff --git a/toys/other/iostat.c b/toys/other/iostat.c
new file mode 100644 (file)
index 0000000..b35c03e
--- /dev/null
@@ -0,0 +1,418 @@
+/* iostat.c - Output CPU and I/O Statistics.
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ *
+
+USE_IOSTAT(NEWTOY(iostat, "cdtzkm[!km]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config IOSTAT
+  bool "iostat"
+  default y
+  help
+  Usage: iostat [-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]
+    
+  Report CPU and I/O statistics
+    -c  Show CPU utilization
+    -d  Show device utilization
+    -t  Print current time
+    -z  Omit devices with no activity
+    -k  Use kb/s
+    -m  Use Mb/s
+*/
+
+#define FOR_iostat
+#include "toys.h"
+
+
+GLOBALS(
+    int all_flag;
+    struct double_list *dev_list;
+    struct double_list *disk_data_list;
+    int interval;
+    int count;
+
+    int nr_cpu;
+    char *str;
+    unsigned div;
+    struct tm *time;
+    int curr;
+)
+typedef struct disk_dev_data {
+  unsigned long  read_ops;  //1 -# of reads completed 
+  unsigned long  read_merged; //2 -# of reads merged
+  unsigned long long read_sec; // 3 -# of sectors read
+                              //#4 - of milliseconds spent reading (we will surpass it)
+  unsigned long  write_ops;  //5 - # of writes completed
+  unsigned long write_merged; //6- # of writes merged
+  unsigned long long write_sec; //7- # of sectors written
+} dev_data_t;
+
+typedef struct disk_dev {
+  char *dev_name;    //Enough space for device name, naive
+  dev_data_t curr_data;
+  dev_data_t prev_data;
+}dev_tt;
+
+typedef unsigned long cputime_t; 
+struct stat_list {
+  cputime_t cputime[10];       //user,nice,system,idle,iowait,irq,softirq,steal,guest.
+  cputime_t total_time;       //Total UP time for CPU.
+  cputime_t uptime;       //Total UP time for CPU.
+};
+struct stat_list *stat_array[2]; //current statistics, previous staistics
+
+/*
+ * omit non-white spaces.
+ */
+static char *omitnon_whitespace(const char *s)
+{                                                                                                                                                                                                    
+  while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9)) s++;
+  return (char *) s;
+}
+static int strtol_range(char *str, int min, int max)
+{                     
+  char *endptr = NULL;
+  errno = 0;          
+  long ret_value = strtol(str, &endptr, 10);
+
+  if(errno) perror_exit("Invalid num %s", str);
+  else if(endptr && (*endptr != '\0' || endptr == str))
+    perror_exit("Not a valid num %s", str);
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else perror_exit("Number %s is not in valid [%d-%d] Range\n", str, min, max);
+} 
+/*
+ * Omit white spaces.
+ */
+static char *omit_whitespace(const char *s)
+{   
+  while(*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) s++;
+  return (char *) s;
+}
+/*
+ * get the path with no strip
+ */
+static char *get_last_path_component_withnostrip(const char *path) {                                                                 
+  char *slash = strrchr(path, '/');
+
+  if (!slash || (slash == path && !slash[1]))
+    return (char*)path;
+
+  return slash + 1;
+}
+/*
+ * calculate the CPU SMP uptime.
+ */
+static cputime_t uptime(void)
+{
+  int fd;
+  cputime_t uptime1,uptime2;
+  char *buf = NULL;
+  fd = xopen("/proc/uptime", O_RDONLY);
+  buf = get_line(fd);
+  if(!buf) perror_exit("/proc/uptime has no data"); // is it ?
+  if (sscanf(buf, "%lu.%lu", &uptime1, &uptime2) != 2) perror_exit("can't read /proc/uptime");
+  xclose(fd);
+  if (buf) free(buf);
+  return uptime1 * sysconf(_SC_CLK_TCK) + uptime2 * sysconf(_SC_CLK_TCK) / 100;
+}
+/*
+ * Embrace cpu stats.
+ */
+static void get_cpu_stat(struct stat_list **curr_stat) 
+{
+  int fd, i;
+  char *buf = NULL, *p;
+  fd = xopen("/proc/stat", O_RDONLY);
+  
+  while((buf = get_line(fd)) != NULL) {
+    if (!(buf[0] == 'c' && buf[1] == 'p' && buf[2] == 'u' && buf[3] == ' ')) {
+      free(buf);
+      continue;
+    }
+    p = buf + 4;
+    for(i = 0; i <= 9; i++) {
+      p = omit_whitespace(p);
+      sscanf(p, "%lu", &(*curr_stat)->cputime[i]);
+      p = omitnon_whitespace(p);
+    }
+    free(buf);
+    break;
+  }
+  (*curr_stat)->total_time = 0;
+  for(i = 0; i < 8; i++) (*curr_stat)->total_time += (*curr_stat)->cputime[i];
+  if (TT.nr_cpu > 0) (*curr_stat)->uptime = uptime();
+  xclose(fd);
+  return;
+}
+/*
+ * Return true if exists else false
+ * from device list.
+ */
+static int if_not_exists(char *dev_name)
+{
+  struct double_list *ptr, *temp;
+  temp = ptr = TT.dev_list;
+  if (temp) temp->prev->next = NULL;
+  while(ptr) {
+    if (!strcmp(ptr->data, dev_name)) {
+      temp->prev->next = temp;
+      return 1;
+    }
+    ptr = ptr->next;
+  }
+  if (temp) temp->prev->next = temp;
+  return 0;
+}
+
+#define IS_OVERFLOW(pval, dval) \
+  ( (pval <= 0xffffffff) && ((long long) dval < 0) ? 1 : 0)
+/*
+ *   Compute percent value from current and previous cpu stat records.
+ *   values can overflow as its 32bit entry.
+ */
+static double calulate_precent(cputime_t pval, cputime_t cval , cputime_t del)
+{
+  uint64_t dval = cval - pval;
+  if(IS_OVERFLOW(pval, dval)) dval += ((uint64_t)1 << 16) << 16;
+  return ((double)dval / del * 100);
+}
+/*
+ * print disk stats.
+ */
+static void print_disk_stat(char *device_name, cputime_t del)
+{
+  #define CUR_PTR  ((dev_tt*)(temp_list->data))->curr_data  //Avoid pain to type these
+  #define PREV_PTR ((dev_tt*)(temp_list->data))->prev_data
+  struct double_list *temp_list;
+  temp_list = TT.disk_data_list;
+  
+  while(strcmp(device_name, ((dev_tt*)(temp_list->data))->dev_name) != 0 ) temp_list = temp_list->next;
+
+    if (toys.optflags & FLAG_z) //Dont't print if no activity.
+      if (PREV_PTR.read_ops== CUR_PTR.read_ops && PREV_PTR.write_ops == CUR_PTR.write_ops) return;
+
+  printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu\n",
+      ((dev_tt*)(temp_list->data))->dev_name,
+      calulate_precent(PREV_PTR.read_ops + PREV_PTR.write_ops, CUR_PTR.read_ops + CUR_PTR.write_ops, del),
+      calulate_precent(PREV_PTR.read_sec, CUR_PTR.read_sec, del) / TT.div,
+      calulate_precent(PREV_PTR.write_sec, CUR_PTR.write_sec, del) / TT.div,
+      (CUR_PTR.read_sec -  PREV_PTR.read_sec) / TT.div,
+      (CUR_PTR.write_sec - PREV_PTR.write_sec) / TT.div);   
+}
+/*
+ * Return NODE address if already exists in list.
+ */
+static dev_tt* get_ifexists(char *device_name)
+{
+  struct double_list *list_ptr, *head;
+  head = list_ptr = TT.disk_data_list;
+  if (head) head->prev->next = NULL;
+  while(list_ptr) {
+    if ((strcmp(((dev_tt*)(list_ptr->data))->dev_name, device_name)) == 0) {
+         head->prev->next = head;
+         return (dev_tt*)(list_ptr->data);
+    }
+    list_ptr = list_ptr->next;
+  }
+  if (head) head->prev->next = head;
+  return NULL;
+}
+/*
+ * Embrace disk stats, depending upon the
+ * list (if present) else defaults of 
+ * disks.
+ */
+static void get_disk_stat(cputime_t delta)
+{
+  int fd;
+  char *buf = NULL;
+  dev_tt *cur_dev;
+  dev_data_t *cur_dev_data;
+  fd = xopen("/proc/diskstats", O_RDONLY);
+  char device_name[15] = {0,}; //enough as we sscanf 12(MAX_DEV_NAME) only
+
+  while((buf = get_line(fd)) != NULL) {
+    sscanf(buf, "%*s %*s %12s", device_name);
+    if (TT.dev_list) {
+      if (!if_not_exists(device_name)) {
+        free(buf);
+        continue;
+      }
+    }
+
+    cur_dev = get_ifexists(device_name);
+    if (!cur_dev) {
+      cur_dev = xzalloc(sizeof(dev_tt));
+      cur_dev->dev_name = xmsprintf("%s", device_name);
+      dlist_add(&TT.disk_data_list, (char*)cur_dev); //Double list with data part as dev_tt structure pointers(why we bother about implementing new list).
+    }
+    cur_dev_data = &cur_dev->curr_data;
+
+    sscanf(buf, "%*s %*s %*s %lu %lu %llu %*s %lu %lu %llu", &cur_dev_data->read_ops, &cur_dev_data->read_merged,
+        &cur_dev_data->read_sec,&cur_dev_data->write_ops,
+        &cur_dev_data->write_merged,&cur_dev_data->write_sec);
+    if (!TT.dev_list && !TT.all_flag && cur_dev_data->read_ops == 0 && cur_dev_data->write_ops == 0) {
+      free(buf);
+      continue;
+    }
+    print_disk_stat(cur_dev->dev_name, delta);
+    cur_dev->prev_data = *cur_dev_data;
+    free(buf);
+  }
+  xclose(fd);
+}
+/*
+ * Print the Stats for CPU
+ */
+static void print_cpu_stat(void)
+{
+  struct stat_list *p, *c;
+  c = stat_array[TT.curr];
+  p = stat_array[TT.curr ^ 1];
+  cputime_t diff = c->total_time - p->total_time;
+  if (diff == 0) diff = 1;
+  xputs("avg-cpu:  %user   %nice %system %iowait  %steal   %idle");
+    xprintf("        %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+        calulate_precent(p->cputime[0]  , c->cputime[0], diff),
+        calulate_precent(p->cputime[1]  , c->cputime[1]  , diff),
+        calulate_precent(p->cputime[2] + p->cputime[5] + p->cputime[6],
+                  c->cputime[2] + c->cputime[5] + c->cputime[6], diff),
+        calulate_precent(p->cputime[4], c->cputime[4], diff),
+        calulate_precent(p->cputime[7] , c->cputime[7] , diff),
+        calulate_precent(p->cputime[3]  , c->cputime[3]  , diff) );       
+
+}
+/*
+ * Get the number of cpu counts.
+ */
+static int get_cpu_count(void)
+{
+  int fd, count = 0;
+  char *buf = NULL;
+  fd = xopen("/proc/stat", O_RDONLY);
+  while((buf = get_line(fd)))
+  {
+    if (!strncmp("cpu", buf, 3)) {
+      if (*(buf + 3) != ' ') {
+        sscanf(buf + 3, "%d", &count);
+      } else {
+        if (buf) free(buf); 
+        continue;
+      }
+    }
+  if (buf) free(buf); 
+  }
+  xclose(fd);
+  return (count + 1);
+}
+/*
+ * printf current time stamp.
+ */
+static void curr_time_satamp(void)
+{
+  char tm_date[100] = {0,};
+  time_t t;
+  t = time(NULL);
+  TT.time = localtime(&t);
+  strftime(tm_date, sizeof(tm_date), "%x %X", TT.time);
+  xprintf("%s\n", tm_date);
+}
+/*
+ * Print the header line
+ */
+static void print_header(void)
+{
+  struct utsname u;
+  time_t t;
+  char date[100] = {0,}; //Fair enough to Hold in "%x" of strftime.
+  uname(&u); //can it fails?
+  t = time(NULL);
+  TT.time = localtime(&t);
+  strftime(date, sizeof(date), "%x", TT.time);
+  printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n", u.sysname, u.release, u.nodename,
+      date, u.machine, TT.nr_cpu);
+
+}
+/*
+ * iostat main routine.
+ */
+void iostat_main(void)
+{
+  struct stat_list stat1, stat2;
+  TT.curr = 0;
+  cputime_t delta = 0;
+  TT.all_flag = 0;
+
+  if (!(toys.optflags & FLAG_c) && !(toys.optflags & FLAG_d))
+    toys.optflags |= (FLAG_c + FLAG_d);
+
+  while(*toys.optargs && !isdigit(**toys.optargs)) {   //Either "ALL" or list of devices.
+    if (!strcmp(*toys.optargs, "ALL")) TT.all_flag = 1;
+    else {
+      char *strip_path;
+      strip_path = get_last_path_component_withnostrip(*toys.optargs);
+      if (!if_not_exists(strip_path)) dlist_add(&TT.dev_list, strip_path);
+    }
+    toys.optargs++;
+  }
+  if (*toys.optargs) TT.interval = strtol_range(*toys.optargs, 0, INT_MAX);
+  else TT.interval = 0;
+
+  toys.optargs++;
+
+  if (TT.interval && !*toys.optargs) TT.count  = -1; //infinite
+  else if (*toys.optargs) TT.count = strtol_range(*toys.optargs, 0, INT_MAX);
+  else TT.count = 1;
+
+  TT.nr_cpu = get_cpu_count();
+  if (toys.optflags & FLAG_m) {
+    TT.str = "MB";
+    TT.div = 2048;
+  } else if (toys.optflags & FLAG_k) {
+    TT.str = "kB";
+    TT.div = 2;
+  } else {
+    TT.str = "Blk";
+    TT.div = 1;
+  }
+  memset(&stat2, 0, sizeof(struct stat_list)); //Intially prev stat is zero.
+  memset(&stat1, 0, sizeof(struct stat_list)); //Intially prev stat is zero.
+  stat_array[TT.curr] = &stat1;
+  stat_array[TT.curr ^ 1] = &stat2;
+
+  print_header();
+  while(1) {
+
+    get_cpu_stat(&stat_array[TT.curr]);  //curr is updated.
+
+    if (toys.optflags & FLAG_t) curr_time_satamp();
+
+    if (toys.optflags & FLAG_c) {
+      print_cpu_stat();
+      if (toys.optflags & FLAG_d) xputc('\n');
+    }
+    if (toys.optflags & FLAG_d) {
+      if (TT.nr_cpu > 1) delta = (stat_array[TT.curr]->uptime - stat_array[TT.curr^1]->uptime);
+      else delta = (stat_array[TT.curr]->total_time - stat_array[TT.curr^1]->total_time);
+      xprintf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n","tps",TT.str,"_read", TT.str,"_wrtn",TT. str,"_read", TT.str,"_wrtn" );
+      get_disk_stat(delta == 0 ? 1 : delta);
+    }
+    xputc('\n');
+    if ((TT.count > 0 ) && (--TT.count == 0)) break;
+    sleep(TT.interval);
+    TT.curr ^= 1; 
+  }
+  if (CFG_TOYBOX_FREE) {
+    struct double_list *temp1;
+    temp1 = TT.disk_data_list;
+    if(temp1) temp1->prev->next = NULL;
+      while(temp1) {
+        struct double_list *temp2 = temp1->next;
+        free(((dev_tt*)(temp1->data))->dev_name);
+        free(temp1->data);
+        free(temp1);
+        temp1 = temp2;
+      }
+  }
+}
diff --git a/toys/other/klogd.c b/toys/other/klogd.c
new file mode 100644 (file)
index 0000000..5674539
--- /dev/null
@@ -0,0 +1,146 @@
+/* klogd.c - Klogd, The kernel log Dameon.
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ *
+ * No standard
+
+USE_KLOGD(NEWTOY(klogd, "c#<1>8n", TOYFLAG_SBIN))
+
+config KLOGD
+    bool "klogd"
+    default y
+    help
+    usage: klogd [-n] [-c N]
+
+    -c  N   Print to console messages more urgent than prio N (1-8)"
+    -n    Run in foreground.
+
+config KLOGD_SOURCE_RING_BUFFER
+    bool "enable kernel ring buffer as log source."
+    default n
+    depends on KLOGD
+*/
+
+#define FOR_klogd
+#include "toys.h"
+#include <signal.h>
+GLOBALS(
+  long level;
+  int fd;
+)
+
+#if CFG_KLOGD_SOURCE_RING_BUFFER
+#include <sys/klog.h>
+static void open_klogd(void)
+{
+  syslog(LOG_NOTICE, "KLOGD: started with Kernel ring buffer as log source\n");
+  klogctl(1, NULL, 0);
+}
+
+static int read_klogd(char *bufptr, int len)
+{
+  return klogctl(2, bufptr, len);
+}
+
+static void set_log_level(int level)
+{
+  klogctl(8, NULL, level);
+}
+
+static void close_klogd(void)
+{
+  klogctl(7, NULL, 0);
+  klogctl(0, NULL, 0);
+}
+#else
+static void open_klogd(void)
+{
+  TT.fd = xopen("/proc/kmsg", O_RDONLY); //_PATH_KLOG in paths.h
+  syslog(LOG_NOTICE, "KLOGD: started with /proc/kmsg as log source\n");
+}
+
+static int read_klogd(char *bufptr, int len)
+{
+  return xread(TT.fd, bufptr, len);
+}
+
+static void set_log_level(int level)
+{
+    FILE *fptr = xfopen("/proc/sys/kernel/printk", "w");
+    fprintf(fptr, "%u\n", level);
+    fclose(fptr);
+    fptr = NULL;
+}
+
+static void close_klogd(void)
+{
+  set_log_level(7);
+  xclose(TT.fd);
+}
+#endif
+
+static void handle_signal(int sig)
+{
+  close_klogd();
+  syslog(LOG_NOTICE,"KLOGD: Daemon exiting......");
+  exit(1);
+}
+
+static int daemonize(void)
+{
+  pid_t pid;
+  int fd = open("/dev/null", O_RDWR);
+  if (fd < 0) fd = open("/", O_RDONLY, 0666);
+  if((pid = fork()) < 0) {
+    perror_msg("DAEMON: fail to fork");
+    return -1;
+  }
+  if (pid) exit(EXIT_SUCCESS);
+
+  setsid();
+  dup2(fd, 0);
+  dup2(fd, 1);
+  dup2(fd, 2);
+  if (fd > 2) close(fd);
+  return 0;
+}
+
+/*
+ * Read kernel ring buffer in local buff and keep track of
+ * "used" amount to track next read to start.
+ */
+void klogd_main(void)
+{
+  int prio, size, used = 0;
+  char *start, *line_start, msg_buffer[16348]; //LOG_LINE_LENGTH - Ring buffer size
+
+  sigatexit(handle_signal);
+  if (toys.optflags & FLAG_c) set_log_level(TT.level);    //set log level
+  if (!(toys.optflags & FLAG_n)) daemonize();        //Make it daemon
+  open_klogd();
+  openlog("Kernel", 0, LOG_KERN);    //open connection to system logger..
+
+  while(1) {
+    start = msg_buffer + used; //start updated for re-read.
+    size = read_klogd(start, sizeof(msg_buffer) - used - 1);
+    if (size < 0) perror_exit("error reading file:");
+    start[size] = '\0';  //Ensure last line to be NUL terminated.
+    if (used) start = msg_buffer;
+    while(start) {
+      if ((line_start = strsep(&start, "\n")) != NULL && start != NULL) used = 0;
+      else {                            //Incomplete line, copy it to start of buff.
+        used = strlen(line_start);
+        strcpy(msg_buffer, line_start);
+        if (used < (sizeof(msg_buffer) - 1)) break;
+        used = 0; //we have buffer full, log it as it is.
+      }
+      prio = LOG_INFO;  //we dont know priority, mark it INFO
+      if (*line_start == '<') {  //we have new line to syslog
+        line_start++;
+        if (line_start) prio = (int)strtoul(line_start, &line_start, 10);
+        if (*line_start == '>') line_start++;
+      }
+      if (*line_start) syslog(prio, "%s", line_start);
+    }
+  }
+}
diff --git a/toys/other/login.c b/toys/other/login.c
new file mode 100644 (file)
index 0000000..54190d0
--- /dev/null
@@ -0,0 +1,227 @@
+/* login.c - Start a session on the system.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * No support for PAM/securetty/selinux/login script/issue/utmp
+ * Relies on libcrypt for hash calculation.
+
+USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
+
+config LOGIN
+  bool "login"
+  default y
+  help
+    usage: login [-p] [-h host] [[-f] username]
+
+    Establish a new session with the system.
+    -p Preserve environment
+    -h The name of the remote host for this login
+    -f Do not perform authentication
+*/
+
+#define FOR_login
+#include "toys.h"
+
+#define LOGIN_TIMEOUT 60
+#define LOGIN_FAIL_TIMEOUT 3
+#define USER_NAME_MAX_SIZE 32
+#define HOSTNAME_SIZE 32
+
+GLOBALS(
+  char *hostname;
+)
+
+static void login_timeout_handler(int sig __attribute__((unused)))
+{
+  printf("\nLogin timed out after %d seconds.\n", LOGIN_TIMEOUT);
+  exit(0);
+}
+
+static char *forbid[] = {
+  "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
+  "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
+  "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL", NULL
+};
+
+int verify_password(char * pwd)
+{
+  char *pass;
+
+  if (read_password(toybuf, sizeof(toybuf), "Password: ")) return 1;
+  if (!pwd) return 1;
+  if (pwd[0] == '!' || pwd[0] == '*') return 1;
+
+  pass = crypt(toybuf, pwd);
+  if (pass && !strcmp(pass, pwd)) return 0;
+
+  return 1;
+}
+
+void read_user(char * buff, int size)
+{
+  char hostname[HOSTNAME_SIZE+1];
+  int i = 0;
+  hostname[HOSTNAME_SIZE] = 0;
+  if(!gethostname(hostname, HOSTNAME_SIZE)) fputs(hostname, stdout);
+
+  fputs(" login: ", stdout);
+  fflush(stdout);
+
+  do {
+    int c = getchar();
+    if (c == EOF) exit(EXIT_FAILURE);
+    buff[0] = c;
+  } while (isblank(buff[0]));
+
+  if (buff[0] != '\n') if(!fgets(&buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
+
+  while(i<HOSTNAME_SIZE-1 && isgraph(buff[i])) i++;
+  buff[i] = 0;
+}
+
+void handle_nologin(void)
+{
+  int fd = open("/etc/nologin", O_RDONLY);
+  int size;
+  if (fd == -1) return;
+
+  size = readall(fd, toybuf,sizeof(toybuf)-1);
+  toybuf[size] = 0;
+  if (!size) puts("System closed for routine maintenance\n");
+  else puts(toybuf);
+
+  close(fd);
+  fflush(stdout);
+  exit(EXIT_FAILURE);
+}
+
+void handle_motd(void)
+{
+  int fd = open("/etc/motd", O_RDONLY);
+  int size;
+  if (fd == -1) return;
+
+  size = readall(fd, toybuf,sizeof(toybuf)-1);
+  toybuf[size] = 0;
+  puts(toybuf);
+
+  close(fd);
+  fflush(stdout);
+}
+
+int change_identity(const struct passwd *pwd)
+{
+  if (initgroups(pwd->pw_name,pwd->pw_gid)) return 1;
+  if (setgid(pwd->pw_uid)) return 1;
+  if (setuid(pwd->pw_uid)) return 1;
+
+  return 0;
+}
+
+void spawn_shell(const char *shell)
+{
+  const char * exec_name = strrchr(shell,'/');
+  if (exec_name) exec_name++;
+  else exec_name = shell;
+
+  snprintf(toybuf,sizeof(toybuf)-1, "-%s", shell);
+  execl(shell, toybuf, NULL);
+  error_exit("Failed to spawn shell");
+}
+
+void setup_environment(const struct passwd *pwd, int clear_env)
+{
+  if (chdir(pwd->pw_dir)) printf("bad home dir: %s\n", pwd->pw_dir);
+
+  if (clear_env) {
+    const char * term = getenv("TERM");
+    clearenv();
+    if (term) setenv("TERM", term, 1);
+  }
+
+  setenv("USER", pwd->pw_name, 1);
+  setenv("LOGNAME", pwd->pw_name, 1);
+  setenv("HOME", pwd->pw_dir, 1);
+  setenv("SHELL", pwd->pw_shell, 1);
+}
+
+void login_main(void)
+{
+  int f_flag = toys.optflags & FLAG_f;
+  int h_flag = toys.optflags & FLAG_h;
+  char username[USER_NAME_MAX_SIZE+1], *pass = NULL, **ss;
+  struct passwd * pwd = NULL;
+  struct spwd * spwd = NULL;
+  int auth_fail_cnt = 0;
+
+  if (f_flag && toys.optc != 1) error_exit("-f requires username");
+
+  if (geteuid()) error_exit("not root");
+
+  if (!isatty(0) || !isatty(1) || !isatty(2)) error_exit("no tty");
+
+  openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
+  signal(SIGALRM, login_timeout_handler);
+  alarm(LOGIN_TIMEOUT);
+
+  for (ss = forbid; *ss; ss++) unsetenv(*ss);
+
+  while (1) {
+    tcflush(0, TCIFLUSH);
+
+    username[USER_NAME_MAX_SIZE] = 0;
+    if (toys.optargs[0]) strncpy(username, toys.optargs[0], USER_NAME_MAX_SIZE);
+    else {
+      read_user(username, USER_NAME_MAX_SIZE+1);
+      if (username[0] == 0) continue;
+    }
+
+    pwd = getpwnam(username);
+    if (!pwd) goto query_pass; // Non-existing user
+
+    if (pwd->pw_passwd[0] == '!' || pwd->pw_passwd[0] == '*')
+      goto query_pass;  // Locked account
+
+    if (f_flag) break; // Pre-authenticated
+
+    if (!pwd->pw_passwd[0]) break; // Password-less account
+
+    pass = pwd->pw_passwd;
+    if (pwd->pw_passwd[0] == 'x') {
+      spwd = getspnam (username);
+      if (spwd) pass = spwd->sp_pwdp;
+    }
+
+query_pass:
+    if (!verify_password(pass)) break;
+
+    f_flag = 0;
+    syslog(LOG_WARNING, "invalid password for '%s' on %s %s %s", username,
+      ttyname(0), h_flag?"from":"", h_flag?TT.hostname:"");
+
+    sleep(LOGIN_FAIL_TIMEOUT);
+    puts("Login incorrect");
+
+    if (++auth_fail_cnt == 3)
+      error_exit("Maximum number of tries exceeded (%d)\n", auth_fail_cnt);
+
+    username[0] = 0;
+    pwd = NULL;
+    spwd = NULL;
+  }
+
+  alarm(0);
+
+  if (pwd->pw_uid) handle_nologin();
+
+  if (change_identity(pwd)) error_exit("Failed to change identity");
+
+  setup_environment(pwd, !(toys.optflags & FLAG_p));
+
+  handle_motd();
+
+  syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
+    ttyname(0), h_flag?"from":"", h_flag?TT.hostname:"");
+
+  spawn_shell(pwd->pw_shell);
+}
diff --git a/toys/other/losetup.c b/toys/other/losetup.c
new file mode 100644 (file)
index 0000000..618016e
--- /dev/null
@@ -0,0 +1,188 @@
+/* losetup.c - Loopback setup
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * No standard. (Sigh.)
+
+USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdca[!afj]", TOYFLAG_SBIN))
+
+config LOSETUP
+  bool "losetup"
+  default y
+  help
+    usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}}
+
+    Associate a loopback device with a file, or show current file (if any)
+    associated with a loop device.
+
+    Instead of a device:
+    -a Iterate through all loopback devices
+    -f Find first unused loop device (may create one)
+    -j Iterate through all loopback devices associated with FILE
+
+    existing:
+    -c Check capacity (file size changed)
+    -d Detach loopback device
+
+    new:
+    -s Show device name (alias --show)
+    -o Start assocation at OFFSET into FILE
+    -r Read only
+    -S Limit SIZE of loopback association (alias --sizelimit)
+*/
+
+#define FOR_losetup
+#include "toys.h"
+#include <linux/loop.h>
+
+GLOBALS(
+  char *jfile;
+  long offset;
+  long size;
+
+  int openflags;
+  dev_t jdev;
+  ino_t jino;
+)
+
+/*
+todo: basic /dev file association
+  associate DEV FILE
+  #-a
+  cdfjosS
+  allocate new loop device:
+    /dev/loop-control
+    https://lkml.org/lkml/2011/7/26/148
+*/
+
+// -f: *device is NULL
+
+// Perform requested operation on one device. Returns 1 if handled, 0 if error
+static void loopback_setup(char *device, char *file)
+{
+  struct loop_info64 *loop = (void *)(toybuf+32);
+  int lfd = -1, ffd = ffd;
+  unsigned flags = toys.optflags;
+
+  // Open file (ffd) and loop device (lfd)
+
+  if (file) ffd = xopen(file, TT.openflags);
+  if (!device) {
+    int i, cfd = open("/dev/loop-control", O_RDWR);
+
+    // We assume /dev is devtmpfs so device creation has no lag. Otherwise
+    // just preallocate loop devices and stay within them.
+
+    // mount -o loop depends on found device being at the start of toybuf.
+    if (cfd != -1) {
+      if (0 <= (i = ioctl(cfd, 0x4C82))) // LOOP_CTL_GET_FREE
+        sprintf(device = toybuf, "/dev/loop%d", i);
+      close(cfd);
+    }
+  }
+
+  if (device) lfd = open(device, TT.openflags);
+
+  // Stat the loop device to see if there's a current association.
+  memset(loop, 0, sizeof(struct loop_info64));
+  if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) {
+    if (errno == ENXIO && (flags & (FLAG_a|FLAG_j))) return;
+    if (errno != ENXIO || !file) {
+      perror_msg("%s", device ? device : "-f");
+      goto done;
+    }
+  }
+
+  // Skip -j filtered devices
+  if (TT.jfile && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino))
+    goto done;
+
+  // Check size of file or delete existing association
+  if (flags & (FLAG_c|FLAG_d)) {
+    // The constant is LOOP_SET_CAPACITY
+    if (ioctl(lfd, (flags & FLAG_c) ? 0x4C07 : LOOP_CLR_FD, 0)) {
+      perror_msg("%s", device);
+      goto done;
+    }
+  // Associate file with this device?
+  } else if (file) {
+    char *s = xrealpath(file);
+
+    if (ioctl(lfd, LOOP_SET_FD, ffd)) perror_exit("%s=%s", device, file);
+    loop->lo_offset = TT.offset;
+    loop->lo_sizelimit = TT.size;
+    strncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE);
+    s[LO_NAME_SIZE-1] = 0;
+    if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
+    if (flags & FLAG_s) printf("%s", device);
+    free(s);
+  } else if (flags & FLAG_f) printf("%s", device);
+  else {
+    xprintf("%s: [%04llx]:%llu (%s)", device, loop->lo_device, loop->lo_inode,
+      loop->lo_file_name);
+    if (loop->lo_offset) xprintf(", offset %llu", loop->lo_offset);
+    if (loop->lo_sizelimit) xprintf(", sizelimit %llu", loop->lo_sizelimit);
+    xputc('\n');
+  }
+
+done:
+  if (file) close(ffd);
+  if (lfd != -1) close(lfd);
+}
+
+// Perform an action on all currently existing loop devices
+static int dash_a(struct dirtree *node)
+{
+  char *s = node->name;
+
+  // Initial /dev node needs to recurse down one level, then only loop[0-9]*
+  if (*s == '/') return DIRTREE_RECURSE;
+  if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0;
+
+  s = dirtree_path(node, 0);
+  loopback_setup(s, 0);
+  free(s);
+
+  return 0;
+}
+
+void losetup_main(void)
+{
+  char **s;
+
+  TT.openflags = (toys.optflags & FLAG_r) ? O_RDONLY : O_RDWR;
+
+  if (TT.jfile) {
+    struct stat st;
+
+    xstat(TT.jfile, &st);
+    TT.jdev = st.st_dev;
+    TT.jino = st.st_ino;
+  }
+
+  // With just device, display current association
+  // -a, -f substitute for device
+  // -j substitute for device
+
+  // new association: S size o offset rs - need a file
+  // existing association: cd
+
+  // -f(dc FILE)
+
+  if (toys.optflags & FLAG_f) {
+    if (toys.optc > 1) perror_exit("max 1 arg");
+    loopback_setup(NULL, *toys.optargs);
+  } else if (toys.optflags & (FLAG_a|FLAG_j)) {
+    if (toys.optc) error_exit("bad args");
+    dirtree_read("/dev", dash_a);
+  // Do we need one DEVICE argument?
+  } else {
+    char *file = (toys.optflags & (FLAG_d|FLAG_c)) ? NULL : toys.optargs[1];
+
+    if (!toys.optc || (file && toys.optc>1)) {
+      toys.exithelp++;
+      perror_exit("needs 1 arg");
+    }
+    for (s = toys.optargs; *s; s++) loopback_setup(*s, file);
+  }
+}
diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c
new file mode 100644 (file)
index 0000000..8c7611f
--- /dev/null
@@ -0,0 +1,188 @@
+/* lsattr.c - List file attributes on a Linux second extended file system.
+ *
+ * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ * 
+USE_LSATTR(NEWTOY(lsattr, "vldaR", TOYFLAG_BIN))
+
+config LSATTR
+  bool "lsattr"
+  default y
+  help
+    usage: lsattr [-Radlv] [Files...]
+
+    List file attributes on a Linux second extended file system.
+
+    -R Recursively list attributes of directories and their contents.
+    -a List all files in directories, including files that start with `.'.
+    -d List directories like other files, rather than listing their contents.
+    -l List long flag names.
+    -v List the file's version/generation number.
+*/
+#define FOR_lsattr
+
+#include "toys.h"
+
+static const EXT2_ATTRS toys_ext2_attrs[] = {
+       {"Secure_Deletion",               EXT2_SECRM_FL,        's'},
+       {"Undelete",                      EXT2_UNRM_FL,         'u'},
+       {"Compression_Requested",         EXT2_COMPR_FL,        'c'},
+       {"Synchronous_Updates",           EXT2_SYNC_FL,         'S'},
+       {"Immutable",                     EXT2_IMMUTABLE_FL,    'i'},
+       {"Append_Only",                   EXT2_APPEND_FL,       'a'},
+       {"No_Dump",                       EXT2_NODUMP_FL,       'd'},
+       {"No_Atime",                      EXT2_NOATIME_FL,      'A'},
+       {"Indexed_directory",             EXT2_INDEX_FL,        'I'},
+       {"Journaled_Data",                EXT3_JOURNAL_DATA_FL, 'j'},
+       {"No_Tailmerging",                EXT2_NOTAIL_FL,       't'},
+       {"Synchronous_Directory_Updates", EXT2_DIRSYNC_FL,      'D'},
+       {"Top_of_Directory_Hierarchies",  EXT2_TOPDIR_FL,       'T'},
+       {NULL,                            -1,                    0},
+};
+
+/*
+ * Print the flag's name based on the flag values.
+ */
+static void print_flag_name(unsigned long fileflag)
+{
+  int name_found = 0;
+  const EXT2_ATTRS *ptr_to_ext2_attr = toys_ext2_attrs;
+
+  while(ptr_to_ext2_attr->name) {
+    if(fileflag & ptr_to_ext2_attr->inode_flag) {
+      if(name_found) xprintf(", ");
+      xprintf("%s", ptr_to_ext2_attr->name);
+      name_found = 1;
+    }
+    ptr_to_ext2_attr++;
+  }
+  if(!name_found) xprintf("---");
+  xprintf("\n");
+  return;
+}
+
+/*
+ * Accumulate flag characters and return it's as string.
+ */
+static char *get_flag_str(unsigned long fileflag)
+{
+  char *flag_str = NULL;
+  int index = 0;
+  const EXT2_ATTRS *ptr_to_ext2_attr = toys_ext2_attrs;
+  unsigned int flag_str_len = sizeof(toys_ext2_attrs)/sizeof(toys_ext2_attrs[0]);
+
+  flag_str = xzalloc(flag_str_len + 1);
+  while(ptr_to_ext2_attr->name) {
+    if(fileflag & ptr_to_ext2_attr->inode_flag) flag_str[index++] = ptr_to_ext2_attr->optchar;
+    else flag_str[index++] = '-';
+    ptr_to_ext2_attr++;
+  }
+  return flag_str;
+}
+
+/*
+ * Print file attributes.
+ */
+static void print_file_attr(char *path)
+{
+  unsigned long fileflag = 0;
+  unsigned long version = 0;
+
+  int fd = -1;
+  struct stat sb;
+
+  if(!stat(path, &sb) && !S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) {
+    errno = EOPNOTSUPP;
+    perror_msg("reading '%s'", path);
+    return;
+  }
+
+  if(-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) {
+    perror_msg("reading '%s'", path);
+    return;
+  }
+
+  if(toys.optflags & FLAG_v) {
+    if(get_e2fs_version(fd, &version) == -1) {
+      perror_msg("reading %s", path);
+      goto ERROR;
+    }
+    xprintf("%5lu ", version);
+  }
+
+  if(get_e2fs_flag(fd, &sb, &fileflag) == -1) {
+    perror_msg("while reading flags on '%s'", path);
+    goto ERROR;
+  }
+  else {
+    if(toys.optflags & FLAG_l) {
+      xprintf("%-50s ", path);
+      print_flag_name(fileflag);
+    }
+    else {
+      char *flag_str = get_flag_str(fileflag);
+      xprintf("%s %s\n", flag_str, path);
+      free(flag_str);
+    }
+  }
+ERROR:
+  xclose(fd);
+  return;
+}
+
+/*
+ * Get directory information.
+ */
+static int retell_dir(struct dirtree *root)
+{
+  char *fpath = NULL;
+
+  if(toys.optflags & FLAG_d) {
+    fpath = dirtree_path(root, NULL);
+    print_file_attr(fpath);
+    free(fpath);
+    return 0;
+  }
+
+  if(root->data == -1) {
+    xputc('\n');
+    return 0;
+  }
+
+  if(S_ISDIR(root->st.st_mode) && (root->parent == NULL)) return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN);
+
+  fpath = dirtree_path(root, NULL);
+  //Special case: with '-a' option and '.'/'..' also included in the printing list.
+  if( (root->name[0] != '.') || (toys.optflags & FLAG_a) ) {
+    print_file_attr(fpath);
+
+    if(S_ISDIR(root->st.st_mode) && (toys.optflags & FLAG_R) && dirtree_notdotdot(root) ) {
+      xprintf("\n%s:\n", fpath);
+      free(fpath);
+      return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN);
+    }
+  }
+  free(fpath);
+  return 0;
+}
+
+/*
+ * lsattr main function.
+ */
+void lsattr_main(void)
+{
+  char **argv = toys.optargs;
+  char *tmp = NULL;
+  int allocflag = 0;
+  if(!*argv) {
+    tmp = *argv = xstrdup(".");
+    allocflag = 1;
+  }
+  while (*argv) {
+    dirtree_read(*argv, retell_dir);
+    argv++;
+  }
+  if(allocflag) free(tmp);
+  return;
+}
diff --git a/toys/other/lsmod.c b/toys/other/lsmod.c
new file mode 100644 (file)
index 0000000..b8f5d82
--- /dev/null
@@ -0,0 +1,36 @@
+/* lsmod.c - Show the status of modules in the kernel
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_BIN))
+
+config LSMOD
+  bool "lsmod"
+  default y
+  help
+    usage: lsmod
+
+    Display the currently loaded modules, their sizes and their dependencies.
+*/
+
+#include "toys.h"
+
+void lsmod_main(void)
+{
+  char *modfile = "/proc/modules";
+  FILE * file = xfopen(modfile, "r");
+
+  xprintf("%-23s Size  Used by\n", "Module");
+
+  while (fgets(toybuf, sizeof(toybuf), file)) {
+    char *name = strtok(toybuf, " "), *size = strtok(NULL, " "),
+         *refcnt = strtok(NULL, " "), *users = strtok(NULL, " ");
+
+    if(users) {
+      int len = strlen(users)-1;
+      if (users[len] == ',' || users[len] == '-') users[len] = 0;
+      xprintf("%-19s %8s  %s %s\n", name, size, refcnt, users);
+    } else perror_exit("bad %s", modfile);
+  }
+  fclose(file);
+}
diff --git a/toys/other/lsusb.c b/toys/other/lsusb.c
new file mode 100644 (file)
index 0000000..07886e8
--- /dev/null
@@ -0,0 +1,49 @@
+/* lsusb.c - list available USB devices
+ *
+ * Copyright 2013 Andre Renaud <andre@bluewatersys.com>
+
+USE_LSUSB(NEWTOY(lsusb, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config LSUSB
+  bool "lsusb"
+  default y
+  help
+    usage: lsusb
+
+    List USB hosts/devices.
+*/
+
+#include "toys.h"
+
+static int list_device(struct dirtree *new)
+{
+  FILE *file;
+  char *name;
+  int busnum = 0, devnum = 0, pid = 0, vid = 0;
+
+  if (!new->parent) return DIRTREE_RECURSE;
+  if (new->name[0] == '.') return 0;
+  name = dirtree_path(new, 0);
+  snprintf(toybuf, sizeof(toybuf), "%s/%s", name, "/uevent");
+  file = fopen(toybuf, "r");
+  if (file) {
+    int count = 0;
+
+    while (fgets(toybuf, sizeof(toybuf), file))
+      if (sscanf(toybuf, "BUSNUM=%u\n", &busnum)
+          || sscanf(toybuf, "DEVNUM=%u\n", &devnum)
+          || sscanf(toybuf, "PRODUCT=%x/%x/", &pid, &vid)) count++;
+
+    if (count == 3)
+      printf("Bus %03d Device %03d: ID %04x:%04x\n", busnum, devnum, pid, vid);
+    fclose(file);
+  }
+  free(name);
+
+  return 0;
+}
+
+void lsusb_main(void)
+{
+  dirtree_read("/sys/bus/usb/devices/", list_device);
+}
diff --git a/toys/other/mdev.c b/toys/other/mdev.c
new file mode 100644 (file)
index 0000000..b89ac2c
--- /dev/null
@@ -0,0 +1,209 @@
+/* mdev.c - Populate /dev directory and handle hotplug events
+ *
+ * Copyright 2005, 2008 Rob Landley <rob@landley.net>
+ * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
+
+USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
+
+config MDEV
+  bool "mdev"
+  default n
+  help
+    usage: mdev [-s]
+
+    Create devices in /dev using information from /sys.
+
+    -s Scan all entries in /sys to populate /dev.
+
+config MDEV_CONF
+  bool "Configuration file for mdev"
+  default y
+  depends on MDEV
+  help
+    The mdev config file (/etc/mdev.conf) contains lines that look like:
+    hd[a-z][0-9]* 0:3 660
+
+    Each line must contain three whitespace separated fields. The first
+    field is a regular expression matching one or more device names, and
+    the second and third fields are uid:gid and file permissions for
+    matching devies.
+*/
+
+#include "toys.h"
+#include "lib/xregcomp.h"
+
+// todo, open() block devices to trigger partition scanning.
+
+// mknod in /dev based on a path like "/sys/block/hda/hda1"
+static void make_device(char *path)
+{
+  char *device_name, *s, *temp;
+  int major, minor, type, len, fd;
+  int mode = 0660;
+  uid_t uid = 0;
+  gid_t gid = 0;
+
+  // Try to read major/minor string
+
+  temp = strrchr(path, '/');
+  fd = open(path, O_RDONLY);
+  *temp=0;
+  temp = toybuf;
+  len = read(fd, temp, 64);
+  close(fd);
+  if (len<1) return;
+  temp[len] = 0;
+
+  // Determine device name, type, major and minor
+
+  device_name = strrchr(path, '/') + 1;
+  type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+  major = minor = 0;
+  sscanf(temp, "%u:%u", &major, &minor);
+
+  // If we have a config file, look up permissions for this device
+
+  if (CFG_MDEV_CONF) {
+    char *conf, *pos, *end;
+
+    // mmap the config file
+    if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) {
+      len = fdlength(fd);
+      conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+      if (conf) {
+        int line = 0;
+
+        // Loop through lines in mmaped file
+        for (pos = conf; pos-conf<len;) {
+          int field;
+          char *end2;
+
+          line++;
+          // find end of this line
+          for(end = pos; end-conf<len && *end!='\n'; end++);
+
+          // Three fields: regex, uid:gid, mode
+          for (field = 3; field; field--) {
+            // Skip whitespace
+            while (pos<end && isspace(*pos)) pos++;
+            if (pos==end || *pos=='#') break;
+            for (end2 = pos;
+              end2<end && !isspace(*end2) && *end2!='#'; end2++);
+            switch(field) {
+              // Regex to match this device
+              case 3:
+              {
+                char *regex = strndup(pos, end2-pos);
+                regex_t match;
+                regmatch_t off;
+                int result;
+
+                // Is this it?
+                xregcomp(&match, regex, REG_EXTENDED);
+                result=regexec(&match, device_name, 1, &off, 0);
+                regfree(&match);
+                free(regex);
+
+                // If not this device, skip rest of line
+                if (result || off.rm_so
+                  || off.rm_eo!=strlen(device_name))
+                    goto end_line;
+
+                break;
+              }
+              // uid:gid
+              case 2:
+              {
+                char *s2;
+
+                // Find :
+                for(s = pos; s<end2 && *s!=':'; s++);
+                if (s==end2) goto end_line;
+
+                // Parse UID
+                uid = strtoul(pos,&s2,10);
+                if (s!=s2) {
+                  struct passwd *pass;
+                  char *str = strndup(pos, s-pos);
+                  pass = getpwnam(str);
+                  free(str);
+                  if (!pass) goto end_line;
+                  uid = pass->pw_uid;
+                }
+                s++;
+                // parse GID
+                gid = strtoul(s,&s2,10);
+                if (end2!=s2) {
+                  struct group *grp;
+                  char *str = strndup(s, end2-s);
+                  grp = getgrnam(str);
+                  free(str);
+                  if (!grp) goto end_line;
+                  gid = grp->gr_gid;
+                }
+                break;
+              }
+              // mode
+              case 1:
+              {
+                mode = strtoul(pos, &pos, 8);
+                if (pos!=end2) goto end_line;
+                goto found_device;
+              }
+            }
+            pos=end2;
+          }
+end_line:
+          // Did everything parse happily?
+          if (field && field!=3) error_exit("Bad line %d", line);
+
+          // Next line
+          pos = ++end;
+        }
+found_device:
+        munmap(conf, len);
+      }
+      close(fd);
+    }
+  }
+
+  sprintf(temp, "/dev/%s", device_name);
+  if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
+    perror_exit("mknod %s failed", temp);
+
+  if (CFG_MDEV_CONF) mode=chown(temp, uid, gid);
+}
+
+static int callback(struct dirtree *node)
+{
+  // Entries in /sys/class/block aren't char devices, so skip 'em.  (We'll
+  // get block devices out of /sys/block.)
+  if(!strcmp(node->name, "block")) return 0;
+
+  // Does this directory have a "dev" entry in it?
+  // This is path based because the hotplug callbacks are
+  if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) {
+    int len=4;
+    char *dev = dirtree_path(node, &len);
+    strcpy(dev+len, "/dev");
+    if (!access(dev, R_OK)) make_device(dev);
+    free(dev);
+  }
+
+  // Circa 2.6.25 the entries more than 2 deep are all either redundant
+  // (mouse#, event#) or unnamed (every usb_* entry is called "device").
+
+  return (node->parent && node->parent->parent) ? 0 : DIRTREE_RECURSE;
+}
+
+void mdev_main(void)
+{
+  // Handle -s
+
+  if (toys.optflags) {
+    dirtree_read("/sys/class", callback);
+    dirtree_read("/sys/block", callback);
+  }
+
+  // hotplug support goes here
+}
diff --git a/toys/other/mesg.c b/toys/other/mesg.c
new file mode 100644 (file)
index 0000000..dc68c82
--- /dev/null
@@ -0,0 +1,53 @@
+/* mesg.c - mesg implementation.
+ *
+ * Copyright 2013 Madhur Verma <mad.flexi@gmail.com>
+ *
+
+USE_MESG(NEWTOY(mesg, ">1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config MESG
+  bool "mesg"
+  default y
+  help
+    Usage: mesg [y|n]
+
+    Control write access to your terminal
+    y Allow write access to your terminal
+    n Disallow write access to your terminal
+
+config MESG_ONLY_GROUP
+  bool "Enable writing to tty only by group, not by everybody"
+  default y
+  depends on MESG
+*/
+
+#define FOR_mesg
+#include "toys.h"
+
+#if CFG_MESG_ONLY_GROUP==1
+#define S_GRP S_IWGRP
+#else
+#define S_GRP (S_IWGRP | S_IWOTH)
+#endif
+
+void mesg_main(void)
+{
+  struct stat std;
+  mode_t mode;
+
+  if (!isatty(STDIN_FILENO)) error_exit("not a tty");
+  if(fstat(STDIN_FILENO, &std)) error_exit("can't stat %s", STDIN_FILENO);
+
+  if(toys.optc == 1){
+    if(strlen(toys.optargs[0]) > 1 || !strchr("yn", **toys.optargs)){
+    toys.exithelp++;
+    error_exit("Only y/n");
+    }else{
+      mode = (**toys.optargs == 'y') ? std.st_mode | S_GRP : std.st_mode & ~(S_IWGRP|S_IWOTH);
+      if (fchmod(STDIN_FILENO, mode) != 0) perror_exit("chmod failed");
+    }
+  }else{
+    puts((std.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n");
+    return;
+  }
+}
diff --git a/toys/other/mkpasswd.c b/toys/other/mkpasswd.c
new file mode 100644 (file)
index 0000000..729fc54
--- /dev/null
@@ -0,0 +1,108 @@
+/* mkpasswd.c - encrypt the given passwd using salt
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SuSv4 or LSB
+
+USE_MKPASSWD(NEWTOY(mkpasswd, ">2S:m:P#=0<0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config MKPASSWD
+  bool "mkpasswd"
+  default y
+  help
+    usage: mkpasswd [OPTIONS] [PASSWORD] [SALT]
+
+    Crypt PASSWORD using crypt(3)
+
+    -P N    Read password from fd N
+    -m TYPE Encryption method, when TYPE='help', then show the methods available
+    -S SALT
+*/
+
+#define FOR_mkpasswd
+#include "toys.h"
+#include <regex.h>
+
+GLOBALS(
+  long pfd;
+  char *method;
+  char *salt;
+)
+
+#define DEFAULT_ENCRYPTION_METHOD "des"
+
+/*
+ * validate the salt provided by user.
+ * the allowed character set for salt is [./A-Za-z0-9]
+ */
+static void is_salt_valid(char *salt)
+{
+  char *regex = "[./A-Za-z0-9]*"; //salt REGEX
+  regex_t rp;
+  regmatch_t rm[1];
+  int eval;
+
+  /* compile regular expression */
+  if((eval = regcomp(&rp, regex, REG_NEWLINE)) != 0)
+    error_exit("Regex compile fail");
+
+  /* compare string against pattern --  remember that patterns 
+   *    *        are anchored to the beginning of the line */
+  if (regexec(&rp, salt, 1, rm, 0) == 0 && rm[0].rm_so == 0 
+      && rm[0].rm_eo == strlen(salt))
+      return;
+
+  error_exit("salt should be in character set [./A-Za-z0-9]");
+}
+
+void mkpasswd_main(void)
+{
+  int offset = 0;
+  char salt[MAX_SALT_LEN] = {0,};
+  if(!(toys.optflags & FLAG_m)) TT.method = DEFAULT_ENCRYPTION_METHOD;
+  else if(!strcmp(TT.method, "help")) {
+    xprintf("Available encryption methods are:\n"
+        " des\n md5\n sha256\n sha512\n");
+    return;
+  }
+  /* If arguments are there, then the second argument is Salt, can be NULL also */
+  if(toys.optc && !(toys.optflags & FLAG_S)) TT.salt = toys.optargs[1];
+
+  offset= get_salt(salt, TT.method);
+  if(offset == -1) error_exit("unknown encryption method");
+  if(TT.salt) {
+    is_salt_valid(TT.salt);
+    strncpy(salt + offset, TT.salt, MAX_SALT_LEN - (offset + 1)); //1 for setting '\0'
+    salt[MAX_SALT_LEN - 1] = '\0';
+  }
+
+  if(toys.optflags & FLAG_P) {
+    if(dup2(TT.pfd, STDIN_FILENO) == -1) perror_exit("fd");
+    close(TT.pfd);
+  }
+
+  if(!toys.optc) {
+    if(isatty(STDIN_FILENO)) {
+      if(read_password(toybuf, sizeof(toybuf), "Password: ")) perror_exit("password read failed");
+    } else {
+      /* read from the given FD */
+      int i = 0;
+      while (1) {
+        int ret = read(0, &toybuf[i], 1);
+        if ( ret < 0 ) perror_exit("password read failed");
+        else if (ret == 0 || toybuf[i] == '\n' || toybuf[i] == '\r' || sizeof(toybuf) == i+1)
+        {
+          toybuf[i] = '\0';
+          break;
+        }
+        i++;
+      }
+    }
+  } else {
+    strncpy(toybuf, toys.optargs[0], sizeof(toybuf) -1);
+    toybuf[sizeof(toybuf) -1] = '\0';
+  }
+
+  /* encrypt the password */
+  xprintf("%s\n",crypt(toybuf, salt));
+}
diff --git a/toys/other/mkswap.c b/toys/other/mkswap.c
new file mode 100644 (file)
index 0000000..ff86424
--- /dev/null
@@ -0,0 +1,38 @@
+/* mkswap.c - Format swap device.
+ *
+ * Copyright 2009 Rob Landley <rob@landley.net>
+
+USE_MKSWAP(NEWTOY(mkswap, "<1>1", TOYFLAG_SBIN))
+
+config MKSWAP
+  bool "mkswap"
+  default y
+  help
+    usage: mkswap DEVICE
+
+    Sets up a Linux swap area on a device or file.
+*/
+
+#include "toys.h"
+
+void mkswap_main(void)
+{
+  int fd = xopen(*toys.optargs, O_RDWR), pagesize = sysconf(_SC_PAGE_SIZE);
+  off_t len = fdlength(fd);
+  unsigned int pages = (len/pagesize)-1, *swap = (unsigned int *)toybuf;
+
+  // Write header. Note that older kernel versions checked signature
+  // on disk (not in cache) during swapon, so sync after writing.
+
+  swap[0] = 1;
+  swap[1] = pages;
+  xlseek(fd, 1024, SEEK_SET);
+  xwrite(fd, swap, 129*sizeof(unsigned int));
+  xlseek(fd, pagesize-10, SEEK_SET);
+  xwrite(fd, "SWAPSPACE2", 10);
+  fsync(fd);
+
+  if (CFG_TOYBOX_FREE) close(fd);
+
+  printf("Swapspace size: %luk\n", pages*(unsigned long)(pagesize/1024));
+}
diff --git a/toys/other/modinfo.c b/toys/other/modinfo.c
new file mode 100644 (file)
index 0000000..501ba27
--- /dev/null
@@ -0,0 +1,104 @@
+/* modinfo.c - Display module info
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+
+USE_MODINFO(NEWTOY(modinfo, "<1F:0", TOYFLAG_BIN))
+
+config MODINFO
+  bool "modinfo"
+  default y
+  help
+    usage: modinfo [-0] [-F field] [modulename...]
+*/
+
+#define FOR_modinfo
+#include "toys.h"
+
+GLOBALS(
+  char *field;
+
+  long mod;
+)
+
+static char *modinfo_tags[] = {
+  "alias", "license", "description", "author", "firmware",
+  "vermagic", "srcversion", "intree", "parm", "depends",
+};
+
+static void output_field(char *field, char *value)
+{
+  int len;
+
+  if (TT.field && strcmp(TT.field, field)) return;
+
+  len = strlen(field);
+
+  if (TT.field) xprintf("%s", value);
+  else xprintf("%s:%*s%s", field, 15 - len, "", value);
+  xputc((toys.optflags & FLAG_0) ? 0 : '\n');
+}
+
+static void modinfo_file(struct dirtree *dir)
+{
+  int fd, len, i;
+  char *buf, *pos, *full_name;
+
+  full_name = dirtree_path(dir, NULL);
+  output_field("filename", full_name);
+  fd = xopen(full_name, O_RDONLY);
+  free(full_name);
+
+  len = fdlength(fd);
+  if (!(buf = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0)))
+    perror_exit("mmap %s", full_name);
+
+  for (pos = buf; pos < buf+len; pos++) {
+    if (*pos) continue;
+
+    for (i = 0; i < sizeof(modinfo_tags) / sizeof(*modinfo_tags); i++) {
+      char *str = modinfo_tags[i];
+      int len = strlen(str);
+
+      if (!strncmp(pos+1, str, len) && pos[len+1] == '=') 
+        output_field(str, pos+len+2);
+    }
+  }
+
+  munmap(buf, len);
+  close(fd);
+}
+
+static int check_module(struct dirtree *new)
+{
+  if (S_ISREG(new->st.st_mode)) {
+    char *s;
+
+    for (s = toys.optargs[TT.mod]; *s; s++) {
+      int len = 0;
+
+      // The kernel treats - and _ the same, so we should too.
+      for (len = 0; s[len]; len++) {
+        if (s[len] == '-' && new->name[len] == '_') continue;
+        if (s[len] == '_' && new->name[len] == '-') continue;
+        if (s[len] != new->name[len]) break;
+      }
+      if (s[len] || strcmp(new->name+len, ".ko")) break;
+
+      modinfo_file(new);
+    }
+  }
+
+  return dirtree_notdotdot(new);
+}
+
+void modinfo_main(void)
+{
+  struct utsname uts;
+
+  if (uname(&uts) < 0) perror_exit("bad uname");
+  sprintf(toybuf, "/lib/modules/%s", uts.release);
+
+  for(TT.mod = 0; TT.mod<toys.optc; TT.mod++) {
+    dirtree_read(toybuf, check_module);
+  }
+}
diff --git a/toys/other/modprobe.c b/toys/other/modprobe.c
new file mode 100644 (file)
index 0000000..3461519
--- /dev/null
@@ -0,0 +1,647 @@
+/* modprobe.c - modprobe utility.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_MODPROBE(NEWTOY(modprobe, "alrqvsDb", TOYFLAG_SBIN))
+
+config MODPROBE
+  bool "modprobe"
+  default y
+  help
+    usage: modprobe [-alrqvsDb] MODULE [symbol=value][...]
+
+    modprobe utility - inserts modules and dependencies.
+
+       -a    Load multiple MODULEs
+       -l    List (MODULE is a pattern)
+       -r    Remove MODULE (stacks) or do autoclean
+       -q    Quiet
+       -v    Verbose
+       -s    Log to syslog
+       -D    Show dependencies
+       -b    Apply blacklist to module names too
+*/
+#define FOR_modprobe
+#include "toys.h"
+#include <sys/utsname.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <fnmatch.h>
+
+#define DBASE_SIZE    256
+
+GLOBALS(
+  struct arg_list *probes;
+  struct arg_list *dbase[256]; //#define DBASE_SIZE 256 if modified please update
+  char *cmdopts;
+  int   nudeps;
+  uint8_t  symreq;
+)
+
+#define flagGet(f,v,d)  (toys.optflags & f) ? v : d
+#define flagChk(f)    (toys.optflags & f) ? 1 : 0
+
+/*
+ * Standard configuration defination
+ * can be moved to kconfig in future releases
+ */
+
+#define CONFIG_MODUTILS_SYMBOLS 1
+#define CONFIG_MODUTILS_ALIAS  1
+
+#define MODULES_DIR    "/lib/modules"
+#define DEPMODE_FILE  "modules.dep"
+
+static void (*dbg)(char *format, ...);
+static void dummy(char *format, ...){
+       return;
+}
+
+/*
+ * Modules flag definations
+ */
+#define MOD_ALOADED    0x0001
+#define MOD_BLACKLIST  0x0002
+#define MOD_FNDDEPMOD  0x0004
+#define MOD_NDDEPS    0x0008
+
+/*
+ *  Current probing modules info
+ */
+typedef struct module_s
+{
+  char *cmdname;        // name from argv
+  char *name;          // stripped of name.
+  struct arg_list *rnames;  // real names if name is aliased
+  char *depent;        // entry line from modules.dep containing dependency
+  char *opts;          // options to pass for init module
+  uint32_t flags;        // flags for this module
+  struct arg_list *dep;    // dependency list for this module
+} module_t;
+
+/* Get last path component with no strip.
+ * e.g.
+ * "/"    -> "/"
+ * "abc"    -> "abc"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> ""
+ */
+static char *get_last_path_component_withnostrip(char *path)
+{
+  char *slash = strrchr(path, '/');
+  if (!slash || (slash == path && !slash[1])) return (char*)path;
+  return slash + 1;
+}
+/*
+ * maximum module name length
+ */
+#define MODNAME_LEN        256
+/*
+ * Converts path name FILE to module name also allocates memory
+ * for holding the string if MOD is NULL,
+ * Returns the pointer to the string.
+ *
+ */
+static char *path2mod(char *file, char *mod)
+{
+       int i;
+       char *from;
+
+       if (!file) return NULL;
+       if (!mod) mod = xmalloc(MODNAME_LEN);
+       from = get_last_path_component_withnostrip(file);
+       for (i = 0; i < (MODNAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
+               mod[i] = (from[i] == '-') ? '_' : from[i];
+       mod[i] = '\0';
+       return mod;
+}
+
+#ifndef _GNU_SOURCE
+/*
+ * locate character in string.
+ */
+static char *strchrnul(char *s, int c)
+{
+  while(*s != '\0' && *s != c) s++;
+  return (char*)s;
+}
+#endif
+
+/*
+ * Adds options in opts from toadd by reallocating opts to
+ * store all data;
+ */
+static char *add_opts(char *opts, const char *toadd)
+{
+  if (toadd){
+    int optlen = 0;
+    if (opts != NULL) optlen = strlen(opts);
+    opts = xrealloc(opts, optlen + strlen(toadd) + 2);
+    sprintf(opts + optlen, " %s", toadd);
+  }
+  return opts;
+}
+
+/* Remove first element from the list and return it */
+static void *llist_popme(struct arg_list **head)
+{
+  char *data = NULL;
+  struct arg_list *temp = *head;
+
+  if (temp) {
+    data = temp->arg;
+    *head = temp->next;
+    free(temp);
+  }
+  return data;
+}
+
+/*
+ * Creates new node with given DATA and Adds before the node OLD of link list.
+ * TODO: its higly recommended that this function is added to the llist.c
+ *     and also structures should be typedefed for easy use. i.e
+ *     typedef struct arg_list arglist_t;
+ */
+static void llist_add(struct arg_list **old, void *data)
+{
+  struct arg_list *new = xmalloc(sizeof(struct arg_list));
+  new->arg = (char*)data;
+  new->next = *old;
+  *old = new;
+}
+
+/*
+ * Creates new node with given DATA and Adds at tail of link list.
+ * TODO: its higly recommended that this function is added to the llist.c
+ */
+static void llist_add_tail(struct arg_list **head, void *data)
+{
+  while (*head) head = &(*head)->next;
+  *head = xzalloc(sizeof(struct arg_list));
+  (*head)->arg = (char*)data;
+}
+
+/* Reverse list order. */
+static struct arg_list *llist_rev(struct arg_list *list)
+{
+  struct arg_list *rev = NULL;
+  while (list) {
+    struct arg_list *next = list->next;
+    list->next = rev;
+    rev = list;
+    list = next;
+  }
+  return rev;
+}
+
+/*
+ *  Returns module_t from the data base if found NULL otherwise
+ *
+ *  if ps == 1 then creates module entry and adds it do data base
+ *   and also returns it.
+ */
+static module_t *get_mod(char *mod, uint8_t ps)
+{
+  char name[MODNAME_LEN];
+  module_t *modentry;
+  struct arg_list *temp;
+  unsigned i, hash = 0;
+
+  path2mod(mod, name);
+  for (i = 0; name[i]; i++) hash = ((hash*31) + hash) + name[i];
+  hash %= DBASE_SIZE;
+  for (temp = TT.dbase[hash]; temp; temp = temp->next) {
+    modentry = (module_t*) temp->arg;
+    if (strcmp(modentry->name, name) == 0) return modentry;
+  }
+  if (!ps) return NULL;
+  modentry = xzalloc(sizeof(*modentry));
+  modentry->name = xstrdup(name);
+  llist_add(&TT.dbase[hash], modentry);
+  return modentry;
+}
+
+/*
+ * Reads one line from fl with \ continuation
+ * returns allocated string pointer in *li
+ * Don't forget to free it :)
+ */
+static int read_line(FILE *fl, char **li)
+{
+  char *nxtline = NULL, *line;
+  int len, nxtlen, linelen, nxtlinelen;
+
+DROP_LINE:
+  line = NULL;
+  linelen = nxtlinelen = 0;
+  len = getline(&line, (size_t*)&linelen, fl);
+  if (len <= 0) return len;
+  /* checking for commented lines */
+  if(line[0] == '#'){
+    free(line);
+    goto DROP_LINE;
+  }
+  for (;;){
+    if (line[len - 1] == '\n') len--;
+    /* checking line continuation */
+    if (len == 0 || line[len - 1] != '\\') break;
+    len--;
+    nxtlen = getline(&nxtline, (size_t*)&nxtlinelen, fl);
+    if (nxtlen <= 0) break;
+    if (linelen < len + nxtlen + 1){
+      linelen = len + nxtlen + 1;
+      line = xrealloc(line, linelen);
+    }
+    memcpy(&line[len], nxtline, nxtlen);
+    len += nxtlen;
+  }
+  line[len] = '\0';
+  *li = xstrdup(line);
+  if (line) free(line);
+  if (nxtline) free(nxtline);
+  return len;
+}
+
+/*
+ * Action to be taken on all config files in default directories
+ * checks for aliases, options, install, remove and blacklist
+ */
+static int config_action(struct dirtree *node)
+{
+  FILE *fc;
+  char *filename, *tokens[3], *line, *linecp = NULL;
+  module_t *modent;
+  int tcount = 0;
+
+  if (!dirtree_notdotdot(node)) return 0;
+  if (S_ISDIR(node->st.st_mode)) return DIRTREE_RECURSE;
+
+  //process only regular file
+  if (S_ISREG(node->st.st_mode)) {
+    filename = dirtree_path(node, NULL);
+    fc = fopen(filename, "r");
+    if (fc == NULL) return 0;
+    while (read_line(fc, &line) > 0) {
+      char *tk = NULL;
+      if (strlen(line) == 0) goto DO_AGAIN;
+      linecp = xstrdup(line);
+      for (tk = strtok(linecp, "# \t"), tcount = 0; tk; tk = strtok(NULL, "# \t"), tcount++) {
+        tokens[tcount] = tk;
+        if (tcount == 2) {
+          tokens[2] = line + strlen(tokens[0]) + strlen(tokens[1]) + 2;
+          break;
+        }
+      }
+      if (tk == NULL) goto DO_AGAIN;
+      /* process the tokens[0] contains first word of config line */
+      if (strcmp(tokens[0], "alias") == 0) {
+        struct arg_list *temp;
+        char aliase[MODNAME_LEN], *realname;
+        if (tokens[2] == NULL) goto DO_AGAIN;
+        path2mod(tokens[1], aliase);
+        for (temp = TT.probes; temp; temp = temp->next) {
+          modent = (module_t*) temp->arg;
+          if (fnmatch(aliase, modent->name, 0) != 0) goto DO_AGAIN;
+          realname = path2mod(tokens[2], NULL);
+          llist_add(&modent->rnames, realname);
+          if (modent->flags & MOD_NDDEPS) {
+            modent->flags &= ~MOD_NDDEPS;
+            TT.nudeps--;
+          }
+          modent = get_mod(realname, 1);
+          if (!(modent->flags & MOD_NDDEPS)) {
+            modent->flags |= MOD_NDDEPS;
+            TT.nudeps++;
+          }
+        }
+      } else if (strcmp(tokens[0], "options") == 0) {
+        if (tokens[2] == NULL) goto DO_AGAIN;
+        modent = get_mod(tokens[1], 1);
+        modent->opts = add_opts(modent->opts, tokens[2]);
+      } else if (strcmp(tokens[0], "include") == 0) dirtree_read(tokens[1], config_action);
+      else if (strcmp(tokens[0], "blacklist") == 0) get_mod(tokens[1], 1)->flags |= MOD_BLACKLIST;
+      else if (strcmp(tokens[0], "install") == 0) goto DO_AGAIN;
+      else if (strcmp(tokens[0], "remove") == 0) goto DO_AGAIN;
+      else error_msg("Invalid option %s found in file %s", tokens[0], filename);
+
+DO_AGAIN:  free(line);
+      line = NULL;
+      free(linecp);
+      linecp = NULL;
+    }
+    fclose(fc);
+    fc = NULL;
+  }
+  return 0;
+}
+
+/*
+ *  Reads lines from modules.dep files.
+ *  and displays all matching modules and returns 0
+ *  only used for -l option.
+ */
+static int depmode_read_entry(char *cmdname)
+{
+  char *line, *modname, *name;
+  FILE *fe;
+  int ret = -1;
+
+  fe = xfopen(DEPMODE_FILE, "r");
+  while (read_line(fe, &line) > 0){
+    modname = xstrdup(line);
+    char *tmp = strchr(modname, ':');
+    if (tmp != NULL){
+      *tmp = '\0';
+      tmp = NULL;
+      name = basename(modname);
+      tmp = strchr(name, '.');
+      if (tmp != NULL) *tmp = '\0';
+      if(cmdname == NULL){
+         if (tmp != NULL) *tmp = '.';
+         printf("%s\n", modname);
+         ret = 0;
+      }else if (fnmatch(cmdname, name, 0) == 0){
+          if (tmp != NULL) *tmp = '.';
+          printf("%s\n", modname);
+          ret = 0;
+      }
+    }
+    free(modname);
+    free(line);
+  }
+  return ret;
+}
+
+/*
+ * Finds dependencies for modules ffrom the modules.dep file.
+ */
+static void find_dep(void)
+{
+  char *line, *modname;
+  FILE *fe;
+  module_t *mod;
+
+  fe = xfopen(DEPMODE_FILE, "r");
+  while (read_line(fe, &line) > 0){
+    modname = xstrdup(line);
+    char *tmp = strchr(modname, ':');
+    if (tmp != NULL){
+      *tmp = '\0';
+      mod = get_mod(modname, 0);
+      if (mod == NULL){
+        free(modname);
+        continue;
+      }
+      if ((mod->flags & MOD_ALOADED) && !(flagChk((FLAG_r | FLAG_D)))){
+        free(modname);
+        free(line);
+        continue;
+      }
+      mod->flags |= MOD_FNDDEPMOD;
+      if ((mod->flags & MOD_NDDEPS) && (mod->dep == NULL)){
+        TT.nudeps--;
+        llist_add(&mod->dep, xstrdup(modname));
+        tmp++;
+        if (*tmp){
+          char *tok;
+          while ((tok = strsep(&tmp, " \t")) != NULL){
+            if (tok[0] == '\0') continue;
+            llist_add_tail(&mod->dep, xstrdup(tok));
+          }
+        }
+      }
+    }
+    free(modname);
+    free(line);
+  }
+  fclose(fe);
+  fe = NULL;
+}
+
+/* Unloads given modules from system
+ * if modules == NULL does auto remove.
+ */
+static int rm_mod(char *modules, uint32_t flags)
+{
+  errno = 0;
+  if(modules){
+    int len = strlen(modules);
+    if (len > 3 && !strcmp(&modules[len-3], ".ko" )) modules[len-3] = 0;
+  }
+  if(!flags) flags = O_NONBLOCK|O_EXCL;
+  syscall(__NR_delete_module, modules, flags);
+  return errno;
+}
+
+/*
+ * Insert module same as insmod implementation.
+ */
+static int ins_mod(char *modules, char *flags)
+{
+  char *buf = NULL;
+  int len, res;
+  int fd = xopen(modules, O_RDONLY);
+
+  len = fdlength(fd);
+  buf = xmalloc(len);
+  xreadall(fd, buf, len);
+  close(fd);
+
+  while (flags && strlen(toybuf) + strlen(flags) + 2 < sizeof(toybuf)){
+    strcat(toybuf, flags);
+    strcat(toybuf, " ");
+  }
+  res = syscall(__NR_init_module, buf, len, toybuf);
+  if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
+  if (res) perror_exit("failed to load %s ", toys.optargs[0]);
+  return res;
+}
+
+/*
+ * Adds module_t entry by name in probes list
+ * and sets other variables as needed.
+ */
+static void add_mod(char *name)
+{
+  module_t *mod;
+  mod = get_mod(name, 1);
+  if (!(flagChk((FLAG_r | FLAG_D))) && (mod->flags & MOD_ALOADED)){
+    dbg("skipping %s, it is already loaded\n", name);
+    return;
+  }
+  dbg("queuing %s\n", name);
+  mod->cmdname = name;
+  mod->flags |= MOD_NDDEPS;
+  llist_add_tail(&TT.probes, mod);
+  TT.nudeps++;
+  if (CONFIG_MODUTILS_SYMBOLS&& strncmp(mod->name, "symbol:", 7) == 0) TT.symreq = 1;
+}
+
+/*
+ * Parse cmdline options suplied for module
+ */
+static char *add_cmdopt(char **argv)
+{
+  char *opt = xzalloc(1);;
+  int lopt = 0;
+
+  while (*++argv){
+    const char *fmt, *var, *val;
+    var = *argv;
+    opt = xrealloc(opt, lopt + 2 + strlen(var) + 2);
+    /* chking for key=val or key = val */
+    fmt = "%.*s%s ";
+    val = strchrnul((char *)var, '=');
+    if (*val){
+      val++;
+      if (strchr(val, ' ')) fmt = "%.*s\"%s\" ";
+    }
+    lopt += sprintf(opt + lopt, fmt, (int) (val - var), var, val);
+  }
+  return opt;
+}
+
+/*
+ * Probes a single module and loads all its dependencies
+ */
+static int go_probe(module_t *m)
+{
+  int rc = 0, first = 1;
+
+  if (!(m->flags & MOD_FNDDEPMOD)) {
+    if (!flagChk(FLAG_s)) error_msg("module %s not found in modules.dep", m->name);
+    return -ENOENT;
+  }
+  dbg("go_prob'ing %s\n", m->name);
+  if (!flagChk(FLAG_r)) m->dep = llist_rev(m->dep);
+  while (m->dep) {
+    module_t *m2;
+    char *fn, *options;
+    rc = 0;
+    fn = llist_popme(&m->dep);
+    m2 = get_mod(fn, 1);
+    /* are we removing ? */
+    if (flagChk(FLAG_r)) {
+      if (m2->flags & MOD_ALOADED) {
+        rc = rm_mod(m2->name, O_EXCL);
+        if (rc) {
+          if (first) {
+            perror_msg("can't unload module %s", m2->name);
+            break;
+          }
+        } else m2->flags &= ~MOD_ALOADED;
+      }
+      first = 0;
+      continue;
+    }
+    options = m2->opts;
+    m2->opts = NULL;
+    /* are we only checking dependencies ? */
+    if (flagChk(FLAG_D)) {
+      dbg(options ? "insmod %s %s\n" : "insmod %s\n", fn, options);
+      if(options) free(options);
+      continue;
+    }
+    if (m2->flags & MOD_ALOADED) {
+      dbg("%s is already loaded, skipping\n", fn);
+      if(options) free(options);
+      continue;
+    }
+    /* none of above is true insert the module. */
+    rc = ins_mod(fn, options);
+    dbg("loaded %s '%s', rc:%d\n", fn, options, rc);
+    if (rc == EEXIST) rc = 0;
+    if(options) free(options);
+    if (rc) {
+      perror_msg("can't load module %s (%s)", m2->name, fn);
+      break;
+    }
+    m2->flags |= MOD_ALOADED;
+  }
+  return rc;
+}
+
+/*
+ * Main function for modeprobe.
+ */
+void modprobe_main(void)
+{
+  struct utsname uts;
+  char **argv = toys.optargs;
+  FILE *fs;
+  module_t *module;
+
+  dbg = dummy;
+  if(flagChk(FLAG_v)) dbg = xprintf;
+
+  if ((toys.optc < 1) && (((flagChk(FLAG_r))&&(flagChk(FLAG_l)))||(!((flagChk(FLAG_r))||(flagChk(FLAG_l)))))) {
+         toys.exithelp++;
+         error_exit(" Syntex Error.");
+  }
+  /* Check for -r flag without arg if yes then do auto remove */
+  if ((flagChk(FLAG_r)) && (toys.optc == 0)){
+    if (rm_mod(NULL, O_NONBLOCK | O_EXCL) != 0)        perror_exit("rmmod");
+    return;
+  }
+
+  /* change directory to /lib/modules/<release>/ */
+  xchdir(MODULES_DIR);
+  uname(&uts);
+  xchdir(uts.release);
+
+  /* modules.dep processing for dependency check.*/
+  if (flagChk(FLAG_l)){
+    if (depmode_read_entry(toys.optargs[0]) == -1) error_exit("no module found.");
+    return;
+  }
+  /* Read /proc/modules to get loadded modules. */
+  fs = xfopen("/proc/modules", "r");
+  char *procline = NULL;
+  while (read_line(fs, &procline) > 0){
+    *(strchr(procline, ' ')) = '\0';
+    get_mod(procline, 1)->flags = MOD_ALOADED;
+    free(procline);
+    procline = NULL;
+  }
+  fclose(fs);
+  fs = NULL;
+  if (flagChk(FLAG_a) || flagChk(FLAG_r)) {
+    do{
+      add_mod(*argv++);
+    } while (*argv);
+  } else {
+    add_mod(argv[0]);
+    TT.cmdopts = add_cmdopt(argv);
+  }
+  if(TT.probes == NULL){
+    error_msg("All modules loaded successfully. ");
+    return;
+  }
+  dirtree_read("/etc/modprobe.conf", config_action);
+  dirtree_read("/etc/modprobe.d", config_action);
+  if (CONFIG_MODUTILS_SYMBOLS && TT.symreq) dirtree_read("modules.symbols", config_action);
+  if (CONFIG_MODUTILS_ALIAS && TT.nudeps) dirtree_read("modules.alias", config_action);
+  find_dep();
+  while ((module = llist_popme(&TT.probes)) != NULL) {
+    if (module->rnames == NULL) {
+      dbg("probing by module name\n");
+      /* This is not an alias. Literal names are blacklisted
+       * only if '-b' is given.
+       */
+      if (!(flagChk(FLAG_b)) || !(module->flags & MOD_BLACKLIST)) go_probe(module);
+      continue;
+    }
+    do { /* Probe all real names for the alias */
+      char *real = llist_pop(&module->rnames);
+      module_t *m2;
+      dbg("probing alias %s by realname %s\n", module->name, real);
+      m2 = get_mod(real, 0);
+      if(m2 == NULL) continue;
+      if (!(m2->flags & MOD_BLACKLIST) && (!(m2->flags & MOD_ALOADED) || (flagChk((FLAG_r | FLAG_D)))))
+        go_probe(m2);
+      free(real);
+    } while (module->rnames != NULL);
+  }
+}
diff --git a/toys/other/mountpoint.c b/toys/other/mountpoint.c
new file mode 100644 (file)
index 0000000..29b8ae6
--- /dev/null
@@ -0,0 +1,54 @@
+/* mountpoint.c - Check if a directory is a mountpoint.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_MOUNTPOINT(NEWTOY(mountpoint, "<1qdx", TOYFLAG_BIN))
+
+config MOUNTPOINT
+  bool "mountpoint"
+  default y
+  help
+    usage: mountpoint [-q] [-d] directory
+           mountpoint [-q] [-x] device
+
+    -q Be quiet, return zero if directory is a mountpoint
+    -d Print major/minor device number of the directory
+    -x Print major/minor device number of the block device
+*/
+
+#define FOR_mountpoint
+#include "toys.h"
+
+void mountpoint_main(void)
+{
+  struct stat st1, st2;
+  int res = 0;
+  int quiet = toys.optflags & FLAG_q;
+  toys.exitval = 1; // be pessimistic
+  strncpy(toybuf, toys.optargs[0], sizeof(toybuf));
+  if (((toys.optflags & FLAG_x) && lstat(toybuf, &st1)) || stat(toybuf, &st1))
+    perror_exit("%s", toybuf);
+
+  if (toys.optflags & FLAG_x){
+    if (S_ISBLK(st1.st_mode)) {
+      if (!quiet) printf("%u:%u\n", major(st1.st_rdev), minor(st1.st_rdev));
+      toys.exitval = 0;
+      return;
+    }
+    if (!quiet) printf("%s: not a block device\n", toybuf);
+    return;
+  }
+
+  if(!S_ISDIR(st1.st_mode)){
+    if (!quiet) printf("%s: not a directory\n", toybuf);
+    return;
+  }
+  strncat(toybuf, "/..", sizeof(toybuf));
+  stat(toybuf, &st2);
+  res = (st1.st_dev != st2.st_dev) ||
+    (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
+  if (!quiet) printf("%s is %sa mountpoint\n", toys.optargs[0], res ? "" : "not ");
+  if (toys.optflags & FLAG_d)
+    printf("%u:%u\n", major(st1.st_dev), minor(st1.st_dev));
+  toys.exitval = res ? 0 : 1;
+}
diff --git a/toys/other/netcat.c b/toys/other/netcat.c
new file mode 100644 (file)
index 0000000..0173e6d
--- /dev/null
@@ -0,0 +1,222 @@
+/* netcat.c - Forward stdin/stdout to a file or network connection.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * TODO: udp, ipv6, genericize for telnet/microcom/tail-f
+
+USE_NETCAT(OLDTOY(nc, netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
+USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
+
+config NETCAT
+  bool "netcat"
+  default y
+  help
+    usage: netcat [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|-let} [-e COMMAND]
+
+    -w SECONDS timeout for connection
+    -p local port number
+    -s local ipv4 address
+    -q SECONDS quit this many seconds after EOF on stdin.
+    -f use FILENAME (ala /dev/ttyS0) instead of network
+
+    Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
+    netcat -f to connect to a serial port.
+
+config NETCAT_LISTEN
+  bool "netcat server options (-let)"
+  default y
+  depends on NETCAT
+  help
+    -t allocate tty (must come before -l or -L)
+    -l listen for one incoming connection.
+    -L listen for multiple incoming connections (server mode).
+
+    Any additional command line arguments after -l or -L are executed
+    to handle each incoming connection. If none, the connection is
+    forwarded to stdin/stdout.
+
+    For a quick-and-dirty server, try something like:
+    netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l
+*/
+
+#define FOR_netcat
+#include "toys.h"
+#include "toynet.h"
+
+GLOBALS(
+  char *filename;        // -f read from filename instead of network
+  long quit_delay;       // -q Exit after EOF from stdin after # seconds.
+  char *source_address;  // -s Bind to a specific source address.
+  long port;             // -p Bind to a specific source port.
+  long wait;             // -w Wait # seconds for a connection.
+)
+
+static void timeout(int signum)
+{
+  if (TT.wait) error_exit("Timeout");
+  exit(0);
+}
+
+static void set_alarm(int seconds)
+{
+  signal(SIGALRM, seconds ? timeout : SIG_DFL);
+  alarm(seconds);
+}
+
+// Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name.
+static void lookup_name(char *name, uint32_t *result)
+{
+  struct hostent *hostbyname;
+
+  hostbyname = gethostbyname(name);
+  if (!hostbyname) error_exit("no host '%s'", name);
+  *result = *(uint32_t *)*hostbyname->h_addr_list;
+}
+
+// Worry about a fancy lookup later.
+static void lookup_port(char *str, uint16_t *port)
+{
+  *port = SWAP_BE16(atoi(str));
+}
+
+void netcat_main(void)
+{
+  int sockfd=-1, pollcount=2;
+  struct pollfd pollfds[2];
+
+  memset(pollfds, 0, 2*sizeof(struct pollfd));
+  pollfds[0].events = pollfds[1].events = POLLIN;
+  set_alarm(TT.wait);
+
+  // The argument parsing logic can't make "<2" conditional on other
+  // arguments like -f and -l, so we do it by hand here.
+  if (toys.optflags&FLAG_f) {
+    if (toys.optc) toys.exithelp++;
+  } else if (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2) toys.exithelp++;
+
+  if (toys.exithelp) error_exit("Argument count wrong");
+
+  if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR);
+  else {
+    int temp;
+    struct sockaddr_in address;
+
+    // Setup socket
+    sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if (-1 == sockfd) perror_exit("socket");
+    fcntl(sockfd, F_SETFD, FD_CLOEXEC);
+    temp = 1;
+    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp));
+    memset(&address, 0, sizeof(address));
+    address.sin_family = AF_INET;
+    if (TT.source_address || TT.port) {
+      address.sin_port = SWAP_BE16(TT.port);
+      if (TT.source_address)
+        lookup_name(TT.source_address, (uint32_t *)&address.sin_addr);
+      if (bind(sockfd, (struct sockaddr *)&address, sizeof(address)))
+        perror_exit("bind");
+    }
+
+    // Dial out
+
+    if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) {
+      // Figure out where to dial out to.
+      lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr);
+      lookup_port(toys.optargs[1], &address.sin_port);
+      temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
+      if (temp<0) perror_exit("connect");
+      pollfds[0].fd = sockfd;
+
+    // Listen for incoming connections
+
+    } else {
+      socklen_t len = sizeof(address);
+
+      if (listen(sockfd, 5)) error_exit("listen");
+      if (!TT.port) {
+        getsockname(sockfd, (struct sockaddr *)&address, &len);
+        printf("%d\n", SWAP_BE16(address.sin_port));
+        fflush(stdout);
+      }
+      // Do we need to return immediately because -l has arguments?
+
+      if ((toys.optflags&FLAG_l) && toys.optc) {
+        if (fork()) goto cleanup;
+        close(0);
+        close(1);
+        close(2);
+      }
+
+      for (;;) {
+        pid_t child = 0;
+
+        // For -l, call accept from the _new_ thread.
+
+        pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address, &len);
+        if (pollfds[0].fd<0) perror_exit("accept");
+
+        // Do we need a tty?
+
+        if (toys.optflags&FLAG_t)
+          child = forkpty(&(pollfds[1].fd), NULL, NULL, NULL);
+
+        // Do we need to fork and/or redirect for exec?
+
+        else {
+          if (toys.optflags&FLAG_L) child = fork();
+          if (!child && toys.optc) {
+            int fd = pollfds[0].fd;
+
+            if (!temp) close(sockfd);
+            dup2(fd, 0);
+            dup2(fd, 1);
+            dup2(fd, 2);
+            if (fd>2) close(fd);
+          }
+        }
+
+        if (child<0) error_msg("Fork failed\n");
+        if (child<1) break;
+        close(pollfds[0].fd);
+      }
+    }
+  }
+
+  // We have a connection.  Disarm timeout.
+  // (Does not play well with -L, but what _should_ that do?)
+  set_alarm(0);
+
+  if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc)) {
+    execvp(*toys.optargs, toys.optargs);
+    error_exit("Exec failed");
+  }
+
+  // Poll loop copying stdin->socket and socket->stdout.
+  for (;;) {
+    int i;
+
+    if (0>poll(pollfds, pollcount, -1)) perror_exit("poll");
+
+    for (i=0; i<pollcount; i++) {
+      if (pollfds[i].revents & POLLIN) {
+        int len = read(pollfds[i].fd, toybuf, sizeof(toybuf));
+        if (len<1) goto dohupnow;
+        xwrite(i ? pollfds[0].fd : 1, toybuf, len);
+      } else if (pollfds[i].revents & POLLHUP) {
+dohupnow:
+        // Close half-connection.  This is needed for things like
+        // "echo GET / | netcat landley.net 80"
+        if (i) {
+          shutdown(pollfds[0].fd, SHUT_WR);
+          pollcount--;
+          set_alarm(TT.quit_delay);
+        } else goto cleanup;
+      }
+    }
+  }
+cleanup:
+  if (CFG_TOYBOX_FREE) {
+    close(pollfds[0].fd);
+    close(sockfd);
+  }
+}
diff --git a/toys/other/netstat.c b/toys/other/netstat.c
new file mode 100644 (file)
index 0000000..3348c60
--- /dev/null
@@ -0,0 +1,658 @@
+/* netstat.c - Display Linux networking subsystem.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
+config NETSTAT
+  bool "netstat"
+  default y
+  help
+    usage: netstat [-pWrxwutneal]
+
+    Display networking information.
+
+    -r  Display routing table.
+    -a  Display all sockets (Default: Connected).
+    -l  Display listening server sockets.
+    -t  Display TCP sockets.
+    -u  Display UDP sockets.
+    -w  Display Raw sockets.
+    -x  Display Unix sockets.
+    -e  Display other/more information.
+    -n  Don't resolve names.
+    -W  Wide Display.
+    -p  Display PID/Program name for sockets.
+*/
+
+#define FOR_netstat
+#include "toys.h"
+#include <net/route.h>
+
+typedef union _iaddr {
+  unsigned u;
+  unsigned char b[4];
+} iaddr;
+
+typedef union _iaddr6 {
+  struct {
+    unsigned a;
+    unsigned b;
+    unsigned c;
+    unsigned d;
+  } u;
+  unsigned char b[16];
+} iaddr6;
+
+#define ADDR_LEN (INET6_ADDRSTRLEN + 1 + 5 + 1) //IPv6 addr len + : + port + '\0'
+
+//For unix states
+enum {
+       SOCK_ACCEPTCON = (1 << 16),  //performed a listen.
+       SOCK_WAIT_DATA = (1 << 17),  //wait data to read.
+       SOCK_NO_SPACE = (1 << 18),  //no space to write.
+};
+
+#define SOCK_NOT_CONNECTED 1
+//For PID/Progrma Name
+#define PROGRAM_NAME "PID/Program Name"
+#define PROGNAME_LEN 21
+
+typedef struct _pidlist {
+  struct _pidlist *next;
+  long inode;
+  char name[PROGNAME_LEN];
+} PID_LIST;
+PID_LIST *pid_list = NULL;
+
+/*
+ * Get base name from the input name.
+ */
+static const char *get_basename(char *name)
+{
+  const char *c = strrchr(name, '/');
+  if (c) return c + 1;
+  return name;
+}
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+#ifndef _GNU_SOURCE
+/*
+ * locate character in string.
+ */
+static char *strchrnul(char *s, int c)
+{
+  while(*s != '\0' && *s != c) s++;
+  return (char*)s;
+}
+#endif
+
+// Find out if the last character of a string matches with the given one.
+// Don't underrun the buffer if the string length is 0.
+static char *find_last_char(char *str, int c)
+{
+  if (str && *str) {
+    size_t sz = strlen(str) - 1;
+    str += sz;
+    if ( (unsigned char)*str == c) return (char*)str;
+  }
+  return NULL;
+}
+/*
+ * Concat path and the file name.
+ */
+static char *append_pathandfile(char *path, char *fname)
+{
+  char *c;
+  if (!path) path = "";
+  c = find_last_char(path, '/');
+  while (*fname == '/') fname++;
+  return xmsprintf("%s%s%s", path, (c==NULL ? "/" : ""), fname);
+}
+/*
+ * Concat sub-path and the file name.
+ */
+static char *append_subpathandfile(char *path, char *fname)
+{
+#define ISDOTORDOTDOT(s) ((s)[0] == '.' && (!(s)[1] || ((s)[1] == '.' && !(s)[2])))
+  if(!fname) return NULL;
+  if(ISDOTORDOTDOT(fname)) return NULL;
+  return append_pathandfile(path, fname);
+#undef ISDOTORDOTDOT
+}
+/*
+ * used to converts string into int and validate the input str for invalid int value or out-of-range.
+ */
+static unsigned get_strtou(char *str, char **endp, int base)
+{
+  unsigned long uli;
+  char *endptr;
+
+  if (!isalnum(str[0])) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+  errno = 0;
+  uli = strtoul(str, &endptr, base);
+  if (uli > UINT_MAX) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+
+  if (endp) *endp = endptr;
+  if (endptr[0]) {
+    if (isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
+      errno = ERANGE;
+      return UINT_MAX;
+    }
+    errno = EINVAL;
+  }
+  return uli;
+}
+/*
+ * used to retrive pid name from pid list.
+ */
+static const char *get_pid_name(unsigned long inode)
+{
+  PID_LIST *tmp;
+  for (tmp = pid_list; tmp; tmp = tmp->next)
+    if (tmp->inode == inode) return tmp->name;
+  return "-";
+}
+/*
+ * For TCP/UDP/RAW display data.
+ */
+static void display_data(unsigned rport, char *label, unsigned rxq, unsigned txq, char *lip, char *rip, unsigned state, unsigned long inode)
+{
+  char *ss_state = "UNKNOWN";
+  char *state_label[] = {"", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
+                                "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING", "UNKNOWN"};
+  if (!strcmp(label, "tcp")) {
+    int sz = ARRAY_LEN(state_label);
+    if (!state || state >= sz) state = sz-1;
+    ss_state = state_label[state];
+  }
+  else if (!strcmp(label, "udp")) {
+    if (state == 1) ss_state = state_label[state];
+    else if (state == 7) ss_state = "";
+  }
+  else if (!strcmp(label, "raw")) ss_state = itoa(state);
+
+  if ( (toys.optflags & FLAG_W) && (toys.optflags & FLAG_p))
+    xprintf("%3s   %6d %6d %-51s %-51s %-12s%s\n", label, rxq, txq, lip, rip, ss_state, get_pid_name(inode));
+  else if (toys.optflags & FLAG_W)
+    xprintf("%3s   %6d %6d %-51s %-51s %-12s\n", label, rxq, txq, lip, rip, ss_state);
+  else if (toys.optflags & FLAG_p)
+    xprintf("%3s   %6d %6d %-23s %-23s %-12s%s\n", label, rxq, txq, lip, rip, ss_state, get_pid_name(inode));
+  else xprintf("%3s   %6d %6d %-23s %-23s %-12s\n", label, rxq, txq, lip, rip, ss_state);
+}
+/*
+ * For TCP/UDP/RAW show data.
+ */
+static void show_data(unsigned rport, char *label, unsigned rxq, unsigned txq, char *lip, char *rip, unsigned state, unsigned long inode)
+{
+  if (toys.optflags & FLAG_l) {
+    if (!rport && (state && 0xA)) display_data(rport, label, rxq, txq, lip, rip, state, inode);
+  } else if (toys.optflags & FLAG_a) display_data(rport, label, rxq, txq, lip, rip, state, inode);
+  //rport && (TCP | UDP | RAW)
+  else if (rport && (0x10 | 0x20 | 0x40)) display_data(rport, label, rxq, txq, lip, rip, state, inode);
+}
+/*
+ * used to get service name.
+ */
+static char *get_servname(int port, char *label)
+{
+  int lport = htons(port);
+  if (!lport) return xmsprintf("%s", "*");
+  struct servent *ser = getservbyport(lport, label);
+  if (ser) return xmsprintf("%s", ser->s_name);
+  return xmsprintf("%s", itoa(ntohs(lport)));
+}
+/*
+ * used to convert address into text format.
+ */
+static void addr2str(int af, void *addr, unsigned port, char *buf, char *label)
+{
+  char ip[ADDR_LEN] = {0,};
+  if (!inet_ntop(af, addr, ip, ADDR_LEN)) {
+    *buf = '\0';
+    return;
+  }
+  size_t iplen = strlen(ip);
+  if (!port) {
+    strncat(ip+iplen, ":*", ADDR_LEN-iplen-1);
+    memcpy(buf, ip, ADDR_LEN);
+    return;
+  }
+
+  if (!(toys.optflags & FLAG_n)) {
+    struct addrinfo hints, *result, *rp;
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = af;
+
+    if (!getaddrinfo(ip, NULL, &hints, &result)) {
+      char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
+      socklen_t sock_len;
+      char *sname = NULL;
+      int plen = 0;
+
+      if (af == AF_INET) sock_len = sizeof(struct sockaddr_in);
+      else sock_len = sizeof(struct sockaddr_in6);
+
+      for (rp = result; rp; rp = rp->ai_next)
+        if (!getnameinfo(rp->ai_addr, sock_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICSERV))
+          break;
+
+      freeaddrinfo(result);
+      sname = get_servname(port, label);
+      plen = strlen(sname);
+      if (*hbuf) {
+        memset(ip, 0, ADDR_LEN);
+        memcpy(ip, hbuf, (ADDR_LEN - plen - 2));
+        iplen = strlen(ip);
+      }
+      snprintf(ip + iplen, ADDR_LEN-iplen, ":%s", sname);
+      free(sname);
+    }
+  }
+  else snprintf(ip+iplen, ADDR_LEN-iplen, ":%d", port);
+  memcpy(buf, ip, ADDR_LEN);
+}
+/*
+ * display ipv4 info for TCP/UDP/RAW.
+ */
+static void show_ipv4(char *fname, char *label)
+{
+  FILE *fp = fopen((char *)fname, "r");
+  if (!fp) {
+     perror_msg("'%s'", fname);
+     return;
+  }
+  fgets(toybuf, sizeof(toybuf), fp); //skip header.
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+    char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
+    iaddr laddr, raddr;
+    unsigned lport, rport, state, txq, rxq, num, uid;
+    unsigned long inode;
+
+    int nitems = sscanf(toybuf, " %d: %x:%x %x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
+        &num, &laddr.u, &lport, &raddr.u, &rport, &state, &txq, &rxq, &uid, &inode);
+    if (nitems == 10) {
+      addr2str(AF_INET, &laddr, lport, lip, label);
+      addr2str(AF_INET, &raddr, rport, rip, label);
+      show_data(rport, label, rxq, txq, lip, rip, state, inode);
+    }
+  }//End of While
+  fclose(fp);
+}
+/*
+ * display ipv6 info for TCP/UDP/RAW.
+ */
+static void show_ipv6(char *fname, char *label)
+{
+  FILE *fp = fopen((char *)fname, "r");
+  if (!fp) {
+    perror_msg("'%s'", fname);
+    return;
+  }
+  fgets(toybuf, sizeof(toybuf), fp); //skip header.
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+    char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
+    iaddr6 laddr6, raddr6;
+    unsigned lport, rport, state, txq, rxq, num, uid;
+    unsigned long inode;
+    int nitems = sscanf(toybuf, " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
+        &num, &laddr6.u.a, &laddr6.u.b, &laddr6.u.c, &laddr6.u.d, &lport, &raddr6.u.a, &raddr6.u.b,
+        &raddr6.u.c, &raddr6.u.d, &rport, &state, &txq, &rxq, &uid, &inode);
+    if (nitems == 16) {
+      addr2str(AF_INET6, &laddr6, lport, lip, label);
+      addr2str(AF_INET6, &raddr6, rport, rip, label);
+      show_data(rport, label, rxq, txq, lip, rip, state, inode);
+    }
+  }//End of While
+  fclose(fp);
+}
+/*
+ * display unix socket info.
+ */
+static void show_unix_sockets(char *fname, char *label)
+{
+  FILE *fp = fopen((char *)fname, "r");
+  if (!fp) {
+    perror_msg("'%s'", fname);
+    return;
+  }
+  fgets(toybuf, sizeof(toybuf), fp); //skip header.
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+    unsigned long int refcount, label, flags, inode;
+    int nitems = 0, path_offset = 0, type, state;
+    char sock_flags[32] = {0,}, *sock_type, *sock_state, *bptr = toybuf;
+
+    if (!toybuf[0]) continue;
+    nitems = sscanf(toybuf, "%*p: %lX %lX %lX %X %X %lu %n",
+        &refcount, &label, &flags, &type, &state, &inode, &path_offset);
+    //for state one less
+    if (nitems < 6) break;
+    if (toys.optflags & FLAG_l) {
+      if ( !((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) ) continue;
+    } else if (!(toys.optflags & FLAG_a)) {
+      if ((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) continue;
+    }
+
+    //prepare socket type, state and flags.
+    {
+      char *ss_type[] = { "", "STREAM", "DGRAM", "RAW", "RDM", "SEQPACKET", "UNKNOWN"};
+      char *ss_state[] = { "FREE", "LISTENING", "CONNECTING", "CONNECTED", "DISCONNECTING", "UNKNOWN"};
+
+      int sz = ARRAY_LEN(ss_type);//sizeof(ss_type)/sizeof(ss_type[0]);
+      if ( (type < SOCK_STREAM) || (type > SOCK_SEQPACKET) ) sock_type = ss_type[sz-1];
+      else sock_type = ss_type[type];
+
+      sz = ARRAY_LEN(ss_state);//sizeof(ss_state)/sizeof(ss_state[0]);
+      if ((state < 0) || (state > sz-2)) sock_state = ss_state[sz-1];
+      else if (state == SOCK_NOT_CONNECTED) {
+        if (flags & SOCK_ACCEPTCON) sock_state = ss_state[state];
+        else sock_state = " ";
+      } else sock_state = ss_state[state];
+
+      strcpy(sock_flags, "[ ");
+      if (flags & SOCK_ACCEPTCON) strcat(sock_flags, "ACC ");
+      if (flags & SOCK_WAIT_DATA) strcat(sock_flags, "W ");
+      if (flags & SOCK_NO_SPACE) strcat(sock_flags, "N ");
+      strcat(sock_flags, "]");
+    }
+    xprintf("%-5s %-6ld %-11s %-10s %-13s %6lu ", (!label ? "unix" : "??"), refcount, sock_flags, sock_type, sock_state, inode);
+    if (toys.optflags & FLAG_p) xprintf("%-20s", get_pid_name(inode));
+
+    bptr += path_offset;
+    *strchrnul(bptr, '\n') = '\0';
+    xprintf("%s\n", bptr);
+  }//End of while
+  fclose(fp);
+}
+/*
+ * extract inode value from the link.
+ */
+static long ss_inode(char *link)
+{
+  long inode = -1;
+  //"link = socket:[12345]", get "12345" as inode.
+  if (!strncmp(link, "socket:[", sizeof("socket:[")-1)) {
+    inode = get_strtou(link + sizeof("socket:[")-1, (char**)&link, 0);
+    if (*link != ']') inode = -1;
+  }
+  //"link = [0000]:12345", get "12345" as inode.
+  else if (!strncmp(link, "[0000]:", sizeof("[0000]:")-1)) {
+    inode = get_strtou(link + sizeof("[0000]:")-1, NULL, 0);
+    //if not NULL terminated.
+    if (errno) inode = -1;
+  }
+  return inode;
+}
+/*
+ * add inode and progname in the pid list.
+ */
+static void add2list(long inode, char *progname)
+{
+  PID_LIST *node = pid_list;
+  for(; node; node = node->next) {
+    if(node->inode == inode)
+      return;
+  }
+  PID_LIST *new = (PID_LIST *)xzalloc(sizeof(PID_LIST));
+  new->inode = inode;
+  safe_strncpy(new->name, progname, PROGNAME_LEN-1);
+  new->next = pid_list;
+  pid_list = new;
+}
+/*
+ * add pid info in the list.
+ */
+static void extract_inode(char *path, char *progname)
+{
+  DIR *dp;
+  struct dirent *entry;
+
+  if (!(dp = opendir(path))) {
+    if (errno == EACCES) return;
+    else perror_exit("%s", path);
+  }
+  while ((entry = readdir(dp))) {
+    char *link = NULL, *fname = append_subpathandfile(path, entry->d_name);
+    if (!fname) continue;
+    link = xreadlink(fname);
+    if (link) {
+      long inode = ss_inode(link);
+      free(link);
+      if (inode != -1) add2list(inode, progname);
+    }
+    free(fname);
+  }//end of while.
+  closedir(dp);
+}
+/*
+ * prepare the list for all pids in /proc directory.
+ */
+static void get_pid_list(void)
+{
+  DIR *dp;
+  struct dirent *entry;
+  char path[64] = {0,};
+  uid_t uid = geteuid();
+
+  if (!(dp = opendir("/proc"))) perror_exit("opendir");
+
+  while ((entry = readdir(dp))) {
+    int fd, nitems = 0, length = 0;
+    char *pid, *progname;
+
+    if (!isdigit(*entry->d_name)) continue;
+    pid = entry->d_name;
+    length = snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name);
+    if (sizeof(path) <= length) continue;
+
+    fd = xopen(path, O_RDONLY);
+    nitems = readall(fd, toybuf, sizeof(toybuf) - 1);
+    xclose(fd);
+    if (nitems < 1) continue;
+    toybuf[nitems] = '\0';
+    strcpy(path + length - (sizeof("cmdline")-1), "fd");
+    progname = append_pathandfile(pid, (char *)get_basename(toybuf)); //e.g. progname = 2054/gnome-keyring-daemon
+    extract_inode(path, progname);
+  }//end of while.
+  closedir(dp);
+
+  if (uid) fprintf(stderr, "(Not all processes could be identified, non-owned process info "
+      "will not be shown, you would have to be root to see it all.)\n");
+}
+/*
+ * Dealloc pid list.
+ */
+static void clean_pid_list(void)
+{
+  PID_LIST *tmp;
+  while (pid_list) {
+    tmp = pid_list->next;
+    free(pid_list);
+    pid_list = tmp;
+  }
+}
+/*
+ * For TCP/UDP/RAW show the header.
+ */
+static void show_header(void)
+{
+  if ((toys.optflags & FLAG_W) && (toys.optflags & FLAG_p))
+    xprintf("\nProto Recv-Q Send-Q %-51s %-51s %-12s%s\n", "Local Address", "Foreign Address", "State", PROGRAM_NAME);
+  else if (toys.optflags & FLAG_p)
+    xprintf("\nProto Recv-Q Send-Q %-23s %-23s %-12s%s\n", "Local Address", "Foreign Address", "State", PROGRAM_NAME);
+  else if (toys.optflags & FLAG_W)
+         xprintf("\nProto Recv-Q Send-Q %-51s %-51s State     \n", "Local Address", "Foreign Address");
+  else xprintf("\nProto Recv-Q Send-Q %-23s %-23s State     \n", "Local Address", "Foreign Address");
+}
+/*
+ * used to get the flag values for route command.
+ */
+static void get_flag_value(char **flagstr, int flags)
+{
+  int i = 0;
+  char *str = *flagstr;
+  static const char flagchars[] = "GHRDMDAC";
+  static const unsigned flagarray[] = {
+    RTF_GATEWAY,
+    RTF_HOST,
+    RTF_REINSTATE,
+    RTF_DYNAMIC,
+    RTF_MODIFIED,
+    RTF_DEFAULT,
+    RTF_ADDRCONF,
+    RTF_CACHE
+  };
+  *str++ = 'U';
+  while ( (*str = flagchars[i]) ) {
+    if (flags & flagarray[i++]) ++str;
+  }
+}
+/*
+ * extract inet4 route info from /proc/net/route file and display it.
+ */
+static void display_routes(int is_more_info, int notresolve)
+{
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+  unsigned long dest, gate, mask;
+  int flags, ref, use, metric, mss, win, irtt;
+  char iface[64]={0,};
+  char *flag_val = xzalloc(10); //there are 9 flags "UGHRDMDAC" for route.
+
+  FILE *fp = xfopen("/proc/net/route", "r");
+  xprintf("Kernel IP routing table\n"
+                   "Destination     Gateway         Genmask         Flags %s Iface\n",
+                               is_more_info ? "  MSS Window  irtt" : "Metric Ref    Use");
+  fgets(toybuf, sizeof(toybuf), fp); //skip 1st line.
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+     int nitems = 0;
+     char *destip = NULL, *gateip = NULL, *maskip = NULL;
+     memset(flag_val, 0, 10);
+
+     nitems = sscanf(toybuf, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
+                 iface, &dest, &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt);
+     if (nitems != 11) {//EOF with no (nonspace) chars read.
+       if ((nitems < 0) && feof(fp)) break;
+      perror_exit("sscanf");
+    }
+    //skip down interfaces.
+    if (!(flags & RTF_UP)) continue;
+
+    if (dest) {//For Destination
+      if (inet_ntop(AF_INET, &dest, toybuf, sizeof(toybuf)) ) destip = xstrdup(toybuf);
+    } else {
+      if (!notresolve) destip = xstrdup("default");
+      else destip = xstrdup("0.0.0.0");
+    }
+    if (gate) {//For Gateway
+      if (inet_ntop(AF_INET, &gate, toybuf, sizeof(toybuf)) ) gateip = xstrdup(toybuf);
+    } else {
+      if (!notresolve) gateip = xstrdup("*");
+      else gateip = xstrdup("0.0.0.0");
+    }
+    //For Mask
+    if (inet_ntop(AF_INET, &mask, toybuf, sizeof(toybuf)) ) maskip = xstrdup(toybuf);
+
+    //Get flag Values
+    get_flag_value(&flag_val, (flags & IPV4_MASK));
+    if (flags & RTF_REJECT) flag_val[0] = '!';
+    xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
+    if (destip) free(destip);
+    if (gateip) free(gateip);
+    if (maskip) free(maskip);
+    if (is_more_info) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
+    else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
+  }//end of while.
+  fclose(fp);
+  if (flag_val) free(flag_val);
+#undef IPV4_MASK
+  return;
+}
+/*
+ * netstat utily main function.
+ */
+void netstat_main(void)
+{
+#define IS_NETSTAT_PROTO_FLAGS_UP (toys.optflags & (FLAG_t | FLAG_u | FLAG_w | FLAG_x))
+  int flag_listen_and_all = 0;
+  if (!toys.optflags) toys.optflags = FLAG_t | FLAG_u | FLAG_w | FLAG_x;
+
+  //When a is set
+  if (toys.optflags & FLAG_a) flag_listen_and_all = 1;
+  //when a and l both are set
+  if ( (toys.optflags & FLAG_a) && (toys.optflags & FLAG_l) )
+    toys.optflags &= ~FLAG_l;
+  //when only a is set
+  if ( (toys.optflags & FLAG_a) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
+    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
+  //when only l is set
+  if ( (toys.optflags & FLAG_l) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
+    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
+  //when only e/n is set
+  if( ((toys.optflags & FLAG_e) || (toys.optflags & FLAG_n)) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
+         toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
+  //when W is set
+  if ( (toys.optflags & FLAG_W) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
+    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
+   //when p is set
+  if ( (toys.optflags & FLAG_p) && (!IS_NETSTAT_PROTO_FLAGS_UP) )
+    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
+
+  //Display routing table.
+  if (toys.optflags & FLAG_r) {
+    display_routes(!(toys.optflags & FLAG_e), (toys.optflags & FLAG_n));
+    return;
+  }
+
+  if (toys.optflags & FLAG_p) get_pid_list();
+
+  //For TCP/UDP/RAW.
+  if ( (toys.optflags & FLAG_t) || (toys.optflags & FLAG_u) || (toys.optflags & FLAG_w) ) {
+    xprintf("Active Internet connections ");
+
+    if (flag_listen_and_all) xprintf("(servers and established)");
+    else if (toys.optflags & FLAG_l) xprintf("(only servers)");
+    else xprintf("(w/o servers)");
+
+    show_header();
+    if (toys.optflags & FLAG_t) {//For TCP
+      show_ipv4("/proc/net/tcp",  "tcp");
+      show_ipv6("/proc/net/tcp6", "tcp");
+    }
+    if (toys.optflags & FLAG_u) {//For UDP
+      show_ipv4("/proc/net/udp",  "udp");
+      show_ipv6("/proc/net/udp6", "udp");
+    }
+    if (toys.optflags & FLAG_w) {//For raw
+      show_ipv4("/proc/net/raw",  "raw");
+      show_ipv6("/proc/net/raw6", "raw");
+    }
+  }
+  if (toys.optflags & FLAG_x) {//For UNIX.
+    xprintf("Active UNIX domain sockets ");
+    if (flag_listen_and_all) xprintf("(servers and established)");
+    else if (toys.optflags & FLAG_l) xprintf("(only servers)");
+    else xprintf("(w/o servers)");
+
+    if (toys.optflags & FLAG_p) xprintf("\nProto RefCnt Flags       Type       State         I-Node %s    Path\n", PROGRAM_NAME);
+    else xprintf("\nProto RefCnt Flags       Type       State         I-Node Path\n");
+    show_unix_sockets("/proc/net/unix", "unix");
+  }
+  if (toys.optflags & FLAG_p) clean_pid_list();
+  if (toys.exitval) toys.exitval = 0;
+#undef IS_NETSTAT_PROTO_FLAGS_UP
+}
diff --git a/toys/other/nslookup.c b/toys/other/nslookup.c
new file mode 100644 (file)
index 0000000..d3d9a12
--- /dev/null
@@ -0,0 +1,321 @@
+/* nslookup.c - query Internet name servers
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+
+USE_NSLOOKUP(NEWTOY(nslookup, "<1>2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NSLOOKUP
+  bool "nslookup"
+  default y
+  help
+    usage: nslookup [HOST] [SERVER]
+
+    Query the nameserver for the IP address of the given HOST
+    optionally using a specified DNS server.
+
+    Note:- Only non-interactive mode is supported.
+*/
+
+#define FOR_nslookup
+#include "toys.h"
+#include <resolv.h>
+
+#define DNS_PORT  53
+
+#ifndef MAX_PORT_VALUE
+#define MAX_PORT_VALUE 65535
+#endif
+
+typedef struct sockaddr_with_len {
+  union {
+    struct sockaddr sock;
+    struct sockaddr_in sock_in;
+    struct sockaddr_in6 sock_in6;
+  }sock_u;
+  socklen_t socklen;
+} sockaddr_with_len;
+
+sockaddr_with_len *swl;
+
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, const char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+/*
+ * used to converts string into int and validate the input str for invalid int value or out-of-range.
+ */
+static unsigned get_strtou(const char *str, char **endp, int base)
+{
+  unsigned long uli;
+  char *endptr;
+
+  if(!isalnum(str[0])) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+  errno = 0;
+  uli = strtoul(str, &endptr, base);
+  if(uli > UINT_MAX) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+
+  if(endp) *endp = endptr;
+  if(endptr[0]) {
+    if(isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
+      errno = ERANGE;
+      return UINT_MAX;
+    }
+    errno = EINVAL;
+  }
+  return uli;
+}
+
+/*
+ * verify the host is local unix path.
+ * if so, set the swl input param accordingly.
+ */
+static int is_host_unix(const char *host, sockaddr_with_len **swl)
+{
+  if(strncmp(host, "local:", 6) == 0) {
+    struct sockaddr_un *sockun;
+    *swl = xzalloc(sizeof(struct sockaddr_with_len));
+    (*swl)->socklen = sizeof(struct sockaddr_un);
+    (*swl)->sock_u.sock.sa_family = AF_UNIX;
+    sockun = (struct sockaddr_un *)&(*swl)->sock_u.sock;
+    safe_strncpy(sockun->sun_path, host + 6, sizeof(sockun->sun_path));
+    return 1;
+  }
+  return 0;
+}
+
+/*
+ * validate the input param (host) for valid ipv6 ip and extract port number (if there).
+ */
+static void get_host_and_port(char **host, int *port)
+{
+  char *ch_ptr;
+  const char *org_host = *host;
+  if(*host[0] == '[') {
+    (*host)++;
+    ch_ptr = strchr(*host, ']');
+    if(!ch_ptr || (ch_ptr[1] != ':' && ch_ptr[1] != '\0'))
+      error_exit("bad address '%s'", org_host);
+  }
+  else {
+    ch_ptr = strrchr(*host, ':');
+    //There is more than one ':' like "::1"
+    if(ch_ptr && strchr(*host, ':') != ch_ptr)
+      ch_ptr = NULL;
+  }
+  if(ch_ptr) { //pointer to ":" or "]:"
+    int size = ch_ptr - (*host) + 1;
+    safe_strncpy(*host, *host, size);
+    if(*ch_ptr != ':') {
+      ch_ptr++; //skip ']'
+      //[nn] without port
+      if(*ch_ptr == '\0')
+        return;
+    }
+    ch_ptr++; //skip ':' to get the port number.
+    *port = get_strtou(ch_ptr, NULL, 10);
+    if(errno || (unsigned)*port > MAX_PORT_VALUE)
+      error_exit("bad port spec '%s'", org_host);
+   }
+  return;
+}
+
+/*
+ * used to extract the address info from the given host ip
+ * and update the swl param accordingly.
+ */
+static int get_socket_stream(const char *host, sa_family_t af, sockaddr_with_len **swl)
+{
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
+  int status = 0;
+
+  memset(&hints, 0 , sizeof(struct addrinfo));
+  hints.ai_family = af;
+  hints.ai_socktype = SOCK_STREAM;
+
+  if((status = getaddrinfo(host, NULL, &hints, &result)) != 0) {
+    perror_exit("bad address '%s' : %s", host, gai_strerror(status));
+    return status;
+  }
+
+  for(rp = result; rp != NULL; rp = rp->ai_next) {
+    if( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) {
+      *swl = xmalloc(sizeof(struct sockaddr_with_len));
+      (*swl)->socklen = rp->ai_addrlen;
+      memcpy(&((*swl)->sock_u.sock), rp->ai_addr, rp->ai_addrlen);
+      break;
+    }
+  }
+  freeaddrinfo(result);
+  return ((!rp)? -1: status);
+}
+
+/*
+ * used to set the port number for ipv4 / ipv6 addresses.
+ */
+static void setport(struct sockaddr *sock, unsigned port_num)
+{
+  //for ipv4
+  if(sock->sa_family == AF_INET) {
+    struct sockaddr_in *sock_in = (void*)sock;
+    sock_in->sin_port = port_num;
+  }
+  //for ipv6
+  else if(sock->sa_family == AF_INET6) {
+    struct sockaddr_in6 *sock_in6 = (void*)sock;
+    sock_in6->sin6_port = port_num;
+  }
+  return;
+}
+
+/*
+ * use to get the socket address with the given host ip.
+ */
+static sockaddr_with_len *get_sockaddr(const char *host, int port, sa_family_t af)
+{
+  sockaddr_with_len *swl = NULL;
+  int status = 0;
+
+  //for unix
+  int is_unix = is_host_unix(host, &swl);
+  if(is_unix && swl) return swl;
+
+  //[IPV6_ip]:port_num
+  if(host[0] == '[' || strrchr(host, ':')) get_host_and_port((char **)&host, &port);
+
+  //for the socket streams.
+  status = get_socket_stream(host, af, &swl);
+  if(status) return NULL;
+
+  setport(&swl->sock_u.sock, htons(port));
+  return swl;
+}
+
+/*
+ * get the numeric hostname and service name, for a given socket address.
+ */
+static char *address_to_name(const struct sockaddr *sock)
+{
+  //man page of getnameinfo.
+  char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
+  int status = 0;
+  if(sock->sa_family == AF_INET) {
+    socklen_t len = sizeof(struct sockaddr_in);
+    if((status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) == 0)
+      return xmsprintf("%s:%s", hbuf, sbuf);
+    else {
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(status));
+      return NULL;
+    }
+  }
+  else if(sock->sa_family == AF_INET6) {
+    socklen_t len = sizeof(struct sockaddr_in6);
+    if((status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) == 0) {
+      //verification for resolved hostname.
+      if(strchr(hbuf, ':')) return xmsprintf("[%s]:%s", hbuf, sbuf);
+      else return xmsprintf("%s:%s", hbuf, sbuf);
+    }
+    else {
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(status));
+      return NULL;
+    }
+  }
+  else if(sock->sa_family == AF_UNIX) {
+    struct sockaddr_un *sockun = (void*)sock;
+    return xmsprintf("local:%.*s", (int) sizeof(sockun->sun_path), sockun->sun_path);
+  }
+  return NULL;
+}
+
+void print_addrs(char *hostname, char *msg)
+{
+  struct addrinfo hints, *res = NULL, *cur = NULL;
+  int ret_ga;
+  char *n = xstrdup(hostname), *p, *tmp;
+  tmp = n;
+  if((*n == '[') && (p = strrchr(n, ']')) != NULL ) {
+    n++;
+    *p = '\0';
+  }
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_socktype = SOCK_STREAM;
+
+  ret_ga = getaddrinfo(n, NULL, &hints, &res);                                                                                                                    
+  if (ret_ga) error_exit("Hostname %s", gai_strerror(ret_ga));
+
+  cur = res;
+  while(cur) {
+    char *colon = NULL;
+    char *name = address_to_name(cur->ai_addr);
+    if(name) {
+      colon = strrchr(name, ':');
+      if(colon) *colon = '\0';
+      xprintf("%-8s %s\n",msg, hostname);
+      xprintf("Address: %s\n", name);
+      free(name);
+    }
+    cur = cur->ai_next;
+  }
+  if (!res->ai_addr) error_exit("getaddrinfo failed");
+
+  freeaddrinfo(res);
+  free(tmp);
+}
+
+/* set the default DSN if it ,toys.optargs[1], provided */
+void set_print_dns()
+{
+  struct sockaddr *sock = NULL;
+  char *colon = NULL, *name = NULL;
+
+  res_init(); //initialize the _res struct, for DNS name.
+
+  if(toys.optargs[1]) { //set the default DNS
+    sockaddr_with_len *swl = get_sockaddr(toys.optargs[1], DNS_PORT, AF_UNSPEC);
+    if(!swl) perror_exit("bad DNS name '%s'", toys.optargs[1]);
+
+    if(swl->sock_u.sock.sa_family == AF_INET) {
+      _res.nscount = 1;
+      _res.nsaddr_list[0] = swl->sock_u.sock_in;
+    }
+    if(swl->sock_u.sock.sa_family == AF_INET6) {
+      _res._u._ext.nscount = 1;
+      _res._u._ext.nsaddrs[0] = &swl->sock_u.sock_in6;
+    }
+  }
+
+  sock = (struct sockaddr*)_res._u._ext.nsaddrs[0];
+  if(!sock) sock = (struct sockaddr*)&_res.nsaddr_list[0];
+
+  name = address_to_name(sock);
+  if(name) {
+    colon = strrchr(name, ':');
+    if(colon) *colon = '\0';
+    print_addrs(name, "Server:");
+    free(name);
+  }
+  puts("");
+}
+
+void nslookup_main(void)
+{
+  if(toys.optargs[0][0] == '-') {
+    toys.exithelp = 1;
+    error_exit("");
+  }
+  set_print_dns();
+  print_addrs(toys.optargs[0], "Name:");
+}
diff --git a/toys/other/oneit.c b/toys/other/oneit.c
new file mode 100644 (file)
index 0000000..5bf4e94
--- /dev/null
@@ -0,0 +1,75 @@
+/* oneit.c - tiny init replacement to launch a single child process.
+ *
+ * Copyright 2005, 2007 by Rob Landley <rob@landley.net>.
+
+USE_ONEIT(NEWTOY(oneit, "^<1c:p", TOYFLAG_SBIN))
+
+config ONEIT
+  bool "oneit"
+  default y
+  help
+    usage: oneit [-p] [-c /dev/tty0] command [...]
+
+    A simple init program that runs a single supplied command line with a
+    controlling tty (so CTRL-C can kill it).
+
+    -p Power off instead of rebooting when command exits.
+    -c Which console device to use.
+
+    The oneit command runs the supplied command line as a child process
+    (because PID 1 has signals blocked), attached to /dev/tty0, in its
+    own session. Then oneit reaps zombies until the child exits, at
+    which point it reboots (or with -p, powers off) the system.
+*/
+
+#define FOR_oneit
+#include "toys.h"
+#include <sys/reboot.h>
+
+GLOBALS(
+  char *console;
+)
+
+// The minimum amount of work necessary to get ctrl-c and such to work is:
+//
+// - Fork a child (PID 1 is special: can't exit, has various signals blocked).
+// - Do a setsid() (so we have our own session).
+// - In the child, attach stdio to /dev/tty0 (/dev/console is special)
+// - Exec the rest of the command line.
+//
+// PID 1 then reaps zombies until the child process it spawned exits, at which
+// point it calls sync() and reboot().  I could stick a kill -1 in there.
+
+
+void oneit_main(void)
+{
+  int i;
+  pid_t pid;
+
+  // Create a new child process.
+  pid = vfork();
+  if (pid) {
+
+    // pid 1 just reaps zombies until it gets its child, then halts the system.
+    while (pid != wait(&i));
+    sync();
+
+    // PID 1 can't call reboot() because it kills the task that calls it,
+    // which causes the kernel to panic before the actual reboot happens.
+    if (!vfork()) reboot((toys.optflags & FLAG_p) ? RB_POWER_OFF : RB_AUTOBOOT);
+    sleep(5);
+    _exit(1);
+  }
+
+  // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works.
+  setsid();
+  for (i=0; i<3; i++) {
+    close(i);
+    xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR);
+  }
+
+  // Can't xexec() here, because we vforked so we don't want to error_exit().
+  toy_exec(toys.optargs);
+  execvp(*toys.optargs, toys.optargs);
+  _exit(127);
+}
diff --git a/toys/other/pgrep.c b/toys/other/pgrep.c
new file mode 100644 (file)
index 0000000..61ca5cb
--- /dev/null
@@ -0,0 +1,164 @@
+/* pgrep.c - pgrep and pkill implementation
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+
+USE_PGREP(NEWTOY(pgrep, "?P# s# xvonlf[!sP]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PGREP(OLDTOY(pkill, pgrep, "?P# s# xvonlf[!sP]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PGREP
+  bool "pgrep"
+  default y
+  help
+    usage: pgrep [-flnovx] [-s SID|-P PPID|PATTERN]
+           pkill [-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]
+
+    -l  Show command name too / List all signals
+    -f  Match against entire command line
+    -n  Show/Signal the newest process only
+    -o  Show/Signal the oldest process only
+    -v  Negate the match
+    -x  Match whole name (not substring)
+    -s  Match session ID (0 for current)
+    -P  Match parent process ID
+*/
+
+#define FOR_pgrep
+#include "toys.h"
+#include <regex.h>
+
+#define flag_get(f,v,d)   ((toys.optflags & f) ? v : d)
+#define flag_chk(f)       ((toys.optflags & f) ? 1 : 0)
+
+GLOBALS(
+  long sid;       //-s
+  long ppid;      //-P
+  char *signame;
+)
+
+static int exec_action(unsigned pid, char *name, int signal)
+{
+  if (toys.which->name[1] == 'g') {
+    printf("%d", pid);
+    if (flag_chk(FLAG_l)) printf(" %s", name);
+    printf("\n");
+  } else {
+    kill(pid, signal);
+  }
+  return 0;
+}
+
+static int regex_match(regex_t *rp, char *tar, char *patt)
+{
+  regmatch_t rm[1];
+  int len = strlen(tar);
+  if (regexec(rp, tar, 1, rm, 0) == 0) {
+    if (flag_chk(FLAG_x)) {
+      if ((rm[0].rm_so == 0) && ((rm[0].rm_eo - rm[0].rm_so) == len)) return 1;
+    } else return 1;
+  }
+  return 0;
+}
+
+void pgrep_main(void)
+{
+  int signum=0, eval=0, ret=1;
+  DIR *dp=NULL;
+  struct dirent *entry=NULL;
+  regex_t rp;
+  unsigned  pid=0, ppid=0, sid=0, latest_pid=0;
+  char *cmdline=NULL, *latest_cmdline = NULL;
+  pid_t self = getpid();
+
+  if (!(dp = opendir("/proc"))) perror_exit("OPENDIR: failed to open /proc");
+  setlinebuf(stdout);
+
+  if (toys.which->name[1] == 'k') {
+    if (flag_chk(FLAG_l)) {
+      sig_to_num(NULL);
+      return;
+    }
+    if (!TT.signame && *toys.optargs && **toys.optargs == '-') {
+      TT.signame = *(toys.optargs++) + 1;
+    }
+    if (TT.signame) {
+      char *arg;
+      int i = strtol(TT.signame, &arg, 10);
+      if (!*arg) arg = num_to_sig(i);
+      else arg = TT.signame;
+      if (!arg || (signum = sig_to_num(arg)) == -1)
+        error_exit("Unknown signal '%s'", arg);
+    } else signum = SIGTERM;
+  }
+  if (!(flag_chk(FLAG_s) || flag_chk(FLAG_P)) && !*toys.optargs) {
+    toys.exithelp++;
+    error_exit("missing argument");
+  }
+  if (*(toys.optargs+1) && !(flag_chk(FLAG_s) || flag_chk(FLAG_P))) {
+    toys.exithelp++;
+    error_exit("max argument > 1");
+  }
+  if (*toys.optargs) { /* compile regular expression(PATTERN) */
+    if ((eval = regcomp(&rp, *toys.optargs, REG_EXTENDED | REG_NOSUB)) != 0) {
+      char errbuf[256];
+      (void) regerror(eval, &rp, errbuf, sizeof(errbuf));
+      error_exit("%s", errbuf);
+    }
+  }
+  if (flag_chk(FLAG_s)&&(TT.sid==0)) TT.sid = getsid(0);
+  while ((entry = readdir(dp))) {
+    int fd = -1, n = 0;
+    if (!isdigit(*entry->d_name)) continue;
+
+    pid = strtol(entry->d_name, NULL, 10);
+    if (pid == self) continue;
+
+    snprintf(toybuf, sizeof(toybuf), "/proc/%s/cmdline", entry->d_name);
+    if ((fd = open(toybuf, O_RDONLY)) == -1) goto cmdline_fail;
+    n = read(fd, toybuf, sizeof(toybuf));
+    close(fd);
+    toybuf[n--] = '\0';
+    if (n < 0) {
+cmdline_fail:
+      snprintf(toybuf, sizeof(toybuf), "/proc/%s/comm", entry->d_name);
+      if ((fd = open(toybuf, O_RDONLY)) == -1) continue;
+      n = read(fd, toybuf, sizeof(toybuf));
+      close(fd);
+      toybuf[--n] = '\0';
+      if (n < 1) continue;
+    }
+    if (flag_chk(FLAG_f)) {
+      while (--n)
+        if (toybuf[n] < ' ') toybuf[n] = ' ';
+    }
+    if (cmdline) free(cmdline);
+    cmdline = xstrdup(toybuf);
+    if (flag_chk(FLAG_s) || flag_chk(FLAG_P)) {
+      snprintf(toybuf, sizeof(toybuf), "/proc/%s/stat", entry->d_name);
+      if ((fd = open(toybuf, O_RDONLY)) == -1) continue;
+      n = read(fd, toybuf, sizeof(toybuf));
+      close(fd);
+      if (n<1) continue;
+      n = sscanf(toybuf, "%*u %*s %*c %u %*u %u", &ppid, &sid);
+      if (flag_chk(FLAG_s)) if (sid != TT.sid) continue;
+      if (flag_chk(FLAG_P)) if (ppid != TT.ppid) continue;
+    }
+    if (!*toys.optargs || (regex_match(&rp, cmdline, *toys.optargs)^flag_chk(FLAG_v))) {
+      if (flag_chk(FLAG_n)) {
+        if (latest_cmdline) free(latest_cmdline);
+        latest_cmdline = xstrdup(cmdline);
+        latest_pid = pid;
+      } else exec_action(pid, cmdline, signum);
+      ret = 0;
+      if (flag_chk(FLAG_o)) break;
+    }
+  }
+  if (cmdline) free(cmdline);
+  if (latest_cmdline) {
+    exec_action(latest_pid, latest_cmdline, signum);
+    free(latest_cmdline);
+  }
+  if (*toys.optargs) regfree(&rp);
+  closedir(dp);
+  toys.exitval = ret;
+}
diff --git a/toys/other/ping.c b/toys/other/ping.c
new file mode 100644 (file)
index 0000000..82706db
--- /dev/null
@@ -0,0 +1,875 @@
+/* ping.c - ping program.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+USE_PING(NEWTOY(ping, "<1>1t#<0>255c#<0s#<0>65535I:W#<0w#<0q46", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
+config PING
+  bool "ping"
+  default y
+  help
+    usage: ping [OPTIONS] HOST
+
+    Send ICMP ECHO_REQUEST packets to network hosts
+
+    Options:
+    -4, -6      Force IP or IPv6 name resolution
+    -c CNT      Send only CNT pings
+    -s SIZE     Send SIZE data bytes in packets (default:56)
+    -t TTL      Set TTL
+    -I IFACE/IP Use interface or IP address as source
+    -W SEC      Seconds to wait for the first response (default:10)
+                (after all -c CNT packets are sent)
+    -w SEC      Seconds until ping exits (default:infinite)
+                (can exit earlier with -c CNT)
+    -q          Quiet, only displays output at start
+                and when finished
+*/
+#define FOR_ping 
+#include "toys.h"
+
+#include <netinet/in_systm.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+
+GLOBALS(
+  long wait_exit;
+  long wait_resp;
+  char *iface;
+  long size;
+  long count;
+  long ttl;
+)
+
+
+#ifndef MAX
+#define MAX(x,y) (x > y ? x : y)
+#endif
+
+#define  MAXPACKET  (IP_MAXPACKET-60-8)  /* max packet size */
+
+#define F_QUIET    0x0001    /* minimize all output */
+#define F_TIMING  0x0002    /* room for a timestamp */
+#define F_SOURCE_ADDR  0x0004    /* set source IP address/interface */
+#define F_MCAST    0x0008    /* multicast target */
+
+/* MAX_DUP_CHK is the number of bits in received table, the
+ *  maximum number of received sequence numbers we can track to check
+ *  for duplicates.
+ */
+#define MAX_DUP_CHK   (8 * 2048)
+u_char  rcvd_tbl[MAX_DUP_CHK/8];
+int   nrepeats = 0;
+#define A(seq)  rcvd_tbl[(seq/8)%sizeof(rcvd_tbl)]  /* byte in array */
+#define B(seq)  (1 << (seq & 0x07))  /* bit in byte */
+#define SET(seq) (A(seq) |= B(seq))
+#define CLR(seq) (A(seq) &= (~B(seq)))
+#define TST(seq) (A(seq) & B(seq))
+
+struct tv32 {
+  int32_t tv32_sec;
+  int32_t tv32_usec;
+};
+
+
+u_char  *packet;
+int  packlen;
+int  pingflags = 0, ping_options;
+char  *fill_pat;
+
+int s;          /* Socket file descriptor */
+int sloop;        /* Socket file descriptor/loopback */
+
+#define PHDR_LEN sizeof(struct tv32)  /* size of timestamp header */
+struct sockaddr_in whereto, send_addr;  /* Who to ping */
+struct sockaddr_in src_addr;    /* from where */
+struct sockaddr_in loc_addr;    /* 127.1 */
+int datalen = 64 - PHDR_LEN;    /* How much data */
+
+#define MAXHOSTNAMELEN  64
+char hostname[MAXHOSTNAMELEN];
+
+static struct {
+  struct ip  o_ip;
+  char    o_opt[MAX_IPOPTLEN];
+  union {
+    u_char    u_buf[MAXPACKET+offsetof(struct icmp, icmp_data)];
+    struct icmp u_icmp;
+  } o_u;
+} out_pack;
+#define  opack_icmp  out_pack.o_u.u_icmp
+
+char optspace[MAX_IPOPTLEN];    /* record route space */
+int optlen;
+
+
+int npackets = 0;        /* total packets to send */
+int ntransmitted;      /* output sequence # = #sent */
+int ident;        /* our ID, in network byte order */
+int nreceived;        /* # of packets we got back */
+double interval = 1.0;      /* interval between packets */
+struct timeval interval_tv;
+double tmin = 999999999.0;
+double tmax = 0.0;
+double tsum = 0.0;      /* sum of all times */
+double tsumsq = 0.0;
+double maxwait = 0.0;
+double wait_resp = 10.0;
+int bufspace = IP_MAXPACKET;
+struct timeval now, clear_cache, last_tx, next_tx, first_tx;
+struct timeval last_rx, first_rx;
+int lastrcvd = 1;      /* last ping sent has been received */
+
+static void pr_pack_sub(int cc, char *addr, int seqno,
+    int dupflag, int ttl, double triptime)
+{
+  (void)printf("%d bytes from %s: seq=%u", cc, addr, seqno);
+  if (dupflag) (void)printf(" DUP!");
+  (void)printf(" ttl=%d", ttl);
+  if (pingflags & F_TIMING)
+    (void)printf(" time=%.3f ms", triptime*1000.0);
+}
+
+/* Compute the IP checksum
+ *  This assumes the packet is less than 32K long.
+ */
+static u_int16_t in_cksum(u_int16_t *p, u_int len)
+{
+  u_int32_t sum = 0;
+  int nwords = len >> 1;
+
+  while (nwords-- != 0)
+    sum += *p++;
+
+  if (len & 1) {
+    union {
+      u_int16_t w;
+      u_int8_t c[2];
+    } u;
+    u.c[0] = *(u_char *)p;
+    u.c[1] = 0;
+    sum += u.w;
+  }
+
+  /* end-around-carry */
+  sum = (sum >> 16) + (sum & 0xffff);
+  sum += (sum >> 16);
+  return (~sum);
+}
+
+// compute the difference of two timevals in seconds
+static double diffsec(struct timeval *timenow, struct timeval *then)
+{
+  return ((timenow->tv_sec - then->tv_sec)*1.0
+      + (timenow->tv_usec - then->tv_usec)/1000000.0);
+}
+
+static void timevaladd(struct timeval *t1, struct timeval *t2)
+{
+  t1->tv_sec += t2->tv_sec;
+  if ((t1->tv_usec += t2->tv_usec) >= 1000000) {
+    t1->tv_sec++;
+    t1->tv_usec -= 1000000;
+  }
+}
+
+static void sec_to_timeval(const double sec, struct timeval *tp)
+{
+  tp->tv_sec = sec;
+  tp->tv_usec = (sec - tp->tv_sec) * 1000000.0;
+}
+
+static double timeval_to_sec(const struct timeval *tp)
+{
+  return tp->tv_sec + tp->tv_usec / 1000000.0;
+}
+
+/*
+ * Print statistics.
+ * Heavily buffered STDIO is used here, so that all the statistics
+ * will be written with 1 sys-write call.  This is nice when more
+ * than one copy of the program is running on a terminal;  it prevents
+ * the statistics output from becomming intermingled.
+ */
+static void summary(int header)
+{
+  if (header)
+    (void)printf("\n--- %s PING Statistics ---\n", hostname);
+  (void)printf("%d packets transmitted, ", ntransmitted);
+  (void)printf("%d packets received, ", nreceived);
+  if (nrepeats)
+    (void)printf("+%d duplicates, ", nrepeats);
+  if (ntransmitted) {
+    if (nreceived > ntransmitted)
+      (void)printf("-- somebody's duplicating packets!");
+    else
+      (void)printf("%.1f%% packet loss",
+          (((ntransmitted-nreceived)*100.0) /
+           ntransmitted));
+  }
+  xputc('\n');
+  if (nreceived && (pingflags & F_TIMING)) {
+    double n = nreceived + nrepeats;
+    double avg = (tsum / n);
+
+    printf("round-trip min/avg/max = "
+        "%.3f/%.3f/%.3f ms\n",
+        tmin * 1000.0, avg * 1000.0, tmax * 1000.0);
+  }
+}
+
+// Print statistics when SIGINFO is received.
+static void prtsig(int dummy)
+{
+  summary(0);
+}
+
+// Print statistics and give up.
+static void finish(int dummy)
+{
+  (void)signal(SIGQUIT, SIG_DFL);
+  summary(1);
+  exit(nreceived > 0 ? 0 : 2);
+}
+
+// On the first SIGINT, allow any outstanding packets to dribble in
+static void prefinish(int dummy)
+{
+  if (lastrcvd      /* quit now if caught up */
+      || nreceived == 0)    /* or if remote is dead */
+    finish(0);
+
+  (void)signal(dummy, finish);  /* do this only the 1st time */
+
+  if (npackets > ntransmitted)  /* let the normal limit work */
+    npackets = ntransmitted;
+}
+
+/*
+ *  Return an ASCII host address
+ *  as a dotted quad and optionally with a hostname
+ */
+static char *pr_addr(struct in_addr *addr)    /* in network order */
+{
+  struct  hostent  *hp;
+  static  char buf[MAXHOSTNAMELEN+4+16+1];
+
+  if (!(hp = gethostbyaddr((char *)addr, sizeof(*addr), AF_INET))) {
+    (void)snprintf(buf, sizeof(buf), "%s", inet_ntoa(*addr));
+  } else {
+    (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
+        inet_ntoa(*addr));
+  }
+
+  return buf;
+}
+
+// Print an ASCII host address starting from a string of bytes.
+static void pr_saddr(u_char *cp)
+{
+  n_long l;
+  struct in_addr addr;
+
+  l = (u_char)*++cp;
+  l = (l<<8) + (u_char)*++cp;
+  l = (l<<8) + (u_char)*++cp;
+  l = (l<<8) + (u_char)*++cp;
+  addr.s_addr = htonl(l);
+  (void)printf("\t%s", (l == 0) ? "0.0.0.0" : pr_addr(&addr));
+}
+
+// Print a descriptive string about an ICMP header other than an echo reply.
+static int pr_icmph(struct icmp *icp, struct sockaddr_in *from, int cc) /* 0=printed nothing */
+{
+  switch (icp->icmp_type ) {
+    case ICMP_UNREACH:
+      (void)printf("Destination Unreachable");
+      break;
+    case ICMP_SOURCEQUENCH:
+      (void)printf("Source Quench");
+      break;
+    case ICMP_REDIRECT:
+      (void)printf("Redirect (change route)");
+      break;
+    case ICMP_ECHO:
+      (void)printf("Echo Request");
+      break;
+    case ICMP_ECHOREPLY:
+      /* displaying other's pings is too noisey */
+      return 0;
+    case ICMP_TIME_EXCEEDED:
+      (void)printf("Time Exceeded");
+      break;
+    case ICMP_PARAMETERPROB: 
+      (void)printf("Parameter Problem");
+      break;
+    case ICMP_TIMESTAMP:
+      printf("Timestamp Request");
+      break;
+    case ICMP_TIMESTAMPREPLY: 
+      printf("Timestamp Reply");
+      break;
+    case ICMP_INFO_REQUEST:
+      printf("Information Request");
+      break;
+    case ICMP_INFO_REPLY:  
+      printf("Information Reply");
+      break;
+    case ICMP_ADDRESS:   
+      printf("Address Mask Request");
+      break;
+    case ICMP_ADDRESSREPLY: 
+      printf("Address Mask Reply");
+      break;
+    default:
+      (void)printf("Bad ICMP type: %d", icp->icmp_type);
+      break;
+  }
+  return 1;
+}
+
+static void get_ifaddr(char *addrname, const char* name)
+{
+  struct ifaddrs *ifaddr_list, *ifa_item;
+  int family, s;  
+  char host[NI_MAXHOST];
+
+  if (getifaddrs(&ifaddr_list) == -1) {
+    perror("getifaddrs");
+    exit(EXIT_FAILURE);
+  }
+
+  /* Walk through linked list, maintaining head pointer so we
+   *    *         can free list later */
+
+  for (ifa_item = ifaddr_list; ifa_item != NULL; ifa_item = ifa_item->ifa_next) {
+    if (ifa_item->ifa_addr == NULL)
+      continue;
+
+    family = ifa_item->ifa_addr->sa_family;
+    /* For an AF_INET* interface address, display the address */
+
+    if ((family == AF_INET) && (strcmp(ifa_item->ifa_name,name) ==0)) {
+      s = getnameinfo(ifa_item->ifa_addr,
+          (family == AF_INET) ? sizeof(struct sockaddr_in) :
+          sizeof(struct sockaddr_in6),
+          host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+      if (s != 0) {
+        printf("getnameinfo() failed: %s\n", gai_strerror(s));
+        exit(EXIT_FAILURE);
+      }     
+      strcpy(addrname, host);
+    }
+  }
+}
+
+static void gethost(const char *arg, const char *name, struct sockaddr_in *sa,
+    char *realname, int realname_len)
+{
+  struct hostent *hp;
+  unsigned int if_idx = 0;
+  char addrname[MAXHOSTNAMELEN+1];
+  int len = strlen(arg);
+
+  (void)memset(sa, 0, sizeof(*sa));
+  sa->sin_family = AF_INET;
+
+  /* If it is an IP address, try to convert it to a name to
+   * have something nice to display.
+   */
+  if (inet_aton(name, &sa->sin_addr) != 0) {
+    if (realname) {
+      (void)strncpy(realname, name, realname_len);
+    }
+    if (len) {
+      pingflags |= F_SOURCE_ADDR;
+      TT.iface = NULL;
+    }
+    return;
+  }
+
+  if_idx = if_nametoindex(name);
+  if (if_idx != 0) get_ifaddr(addrname, name);
+  else {
+    strncpy(addrname,name, MAXHOSTNAMELEN);
+    addrname[MAXHOSTNAMELEN] = '\0';
+    if (len) perror_exit("unknown interface '%s'",name);
+  }
+
+  hp = gethostbyname(addrname);
+  if (!hp)
+    error_exit("Cannot resolve \"%s\" (%s)",name,hstrerror(h_errno));
+
+  if (hp->h_addrtype != AF_INET)
+    error_exit("%s only supported with IP", arg);
+
+  (void)memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
+
+  if (realname) (void)strncpy(realname, hp->h_name, realname_len);
+}
+
+/*
+ * Print out the packet, if it came from us.  This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair).  This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+static void pr_pack(u_char *buf, int tot_len, struct sockaddr_in *from)
+{
+  struct ip *ip;
+  struct icmp *icp;
+  int i, j, net_len;
+  u_char *cp;
+  static int old_rrlen;
+  static char old_rr[MAX_IPOPTLEN];
+  int hlen, dupflag = 0, dumped;
+  double triptime = 0.0;
+#define PR_PACK_SUB() {if (!dumped) {      \
+  dumped = 1;          \
+  pr_pack_sub(net_len, inet_ntoa(from->sin_addr),  \
+      ntohs((u_int16_t)icp->icmp_seq),  \
+      dupflag, ip->ip_ttl, triptime);}}
+
+  /* Check the IP header */
+  ip = (struct ip *) buf;
+  hlen = ip->ip_hl << 2;
+  if (tot_len < datalen + ICMP_MINLEN) return;
+
+  /* Now the ICMP part */
+  dumped = 0;
+  net_len = tot_len - hlen;
+  icp = (struct icmp *)(buf + hlen);
+  if (icp->icmp_id != ident) return;
+  if (icp->icmp_type == ICMP_ECHOREPLY
+      && icp->icmp_id == ident) {
+    if (icp->icmp_seq == htons((u_int16_t)(ntransmitted-1)))
+      lastrcvd = 1;
+    last_rx = now;
+    if (first_rx.tv_sec == 0)
+      first_rx = last_rx;
+    nreceived++;
+    if (pingflags & F_TIMING) {
+      struct timeval tv;
+      struct tv32 tv32;
+
+      (void) memcpy(&tv32, icp->icmp_data, sizeof(tv32));
+      tv.tv_sec = ntohl(tv32.tv32_sec);
+      tv.tv_usec = ntohl(tv32.tv32_usec);
+      triptime = diffsec(&last_rx, &tv);
+      tsum += triptime;
+      tsumsq += triptime * triptime;
+      if (triptime < tmin) tmin = triptime;
+      if (triptime > tmax) tmax = triptime;
+    }
+
+    if (TST(ntohs((u_int16_t)icp->icmp_seq))) {
+      nrepeats++, nreceived--;
+      dupflag=1;
+    } else {
+      SET(ntohs((u_int16_t)icp->icmp_seq));
+    }
+
+    if (!dupflag) {
+      static u_int16_t last_seqno = 0xffff;
+      u_int16_t seqno = ntohs((u_int16_t)icp->icmp_seq);
+      u_int16_t gap = seqno - (last_seqno + 1);
+
+      if (gap < 0x8000) last_seqno = seqno;
+    }
+
+    if (pingflags & F_QUIET) return;
+
+    PR_PACK_SUB();
+  } else if (icp->icmp_type != ICMP_ECHO){
+    if (!pr_icmph(icp, from, net_len))
+      return;
+    dumped = 2;
+  }
+
+  /* Display any IP options */
+  cp = buf + sizeof(struct ip);
+  while (hlen > (int)sizeof(struct ip)) {
+    switch (*cp) {
+      case IPOPT_EOL:
+        hlen = 0;
+        break;
+      case IPOPT_LSRR:
+        hlen -= 2;
+        j = *++cp;
+        ++cp;
+        j -= IPOPT_MINOFF;
+        if (j <= 0) continue;
+        if (dumped <= 1) {
+          j = ((j+3)/4)*4;
+          hlen -= j;
+          cp += j;
+          break;
+        }
+        PR_PACK_SUB();
+        (void)printf("\nLSRR: ");
+        for (;;) {
+          pr_saddr(cp);
+          cp += 4;
+          hlen -= 4;
+          j -= 4;
+          if (j <= 0) break;
+          xputc('\n');
+        }
+        break;
+      case IPOPT_RR:
+        j = *++cp;  /* get length */
+        i = *++cp;  /* and pointer */
+        hlen -= 2;
+        if (i > j) i = j;
+        i -= IPOPT_MINOFF;
+        if (i <= 0) continue;
+        if (dumped <= 1) {
+          if (i == old_rrlen
+              && !memcmp(cp, old_rr, i)) {
+            if (dumped)
+              (void)printf("\t(same route)");
+            j = ((i+3)/4)*4;
+            hlen -= j;
+            cp += j;
+            break;
+          }
+          old_rrlen = i;
+          (void) memcpy(old_rr, cp, i);
+        }
+        if (!dumped) {
+          (void)printf("RR: ");
+          dumped = 1;
+        } else (void)printf("\nRR: ");
+        for (;;) {
+          pr_saddr(cp);
+          cp += 4;
+          hlen -= 4;
+          i -= 4;
+          if (i <= 0) break;
+          xputc('\n');
+        }
+        break;
+      case IPOPT_NOP:
+        if (dumped <= 1)
+          break;
+        PR_PACK_SUB();
+        (void)printf("\nNOP");
+        break;
+      default:
+        PR_PACK_SUB();
+        (void)printf("\nunknown option 0x%x", *cp);
+        break;
+    }
+    hlen--;
+    cp++;
+  }
+
+  if (dumped) {
+    xputc('\n');
+    (void)fflush(stdout);
+  } 
+}
+
+/*
+ * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
+ * will be added on by the kernel.  The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer.  The first PHDR_LEN bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+static void pinger(void)
+{
+  struct tv32 tv32;
+  int i, cc, sw;
+
+  opack_icmp.icmp_code = 0;
+  opack_icmp.icmp_seq = htons((u_int16_t)(ntransmitted));
+
+  /* clear the cached route in the kernel after an ICMP
+   * response such as a Redirect is seen to stop causing
+   * more such packets.  Also clear the cached route
+   * periodically in case of routing changes that make
+   * black holes come and go.
+   */
+  if (clear_cache.tv_sec != now.tv_sec) {
+    opack_icmp.icmp_type = ICMP_ECHOREPLY;
+    opack_icmp.icmp_id = ~ident;
+    opack_icmp.icmp_cksum = 0;
+    opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp,
+        PHDR_LEN);
+    sw = 0;
+    if (setsockopt(sloop,IPPROTO_IP,IP_HDRINCL,
+          (char *)&sw,sizeof(sw)) < 0)
+      perror_exit("Can't turn off special IP header");
+    if (sendto(sloop, (char *) &opack_icmp, PHDR_LEN, MSG_DONTROUTE,
+          (struct sockaddr *)&loc_addr,
+          sizeof(struct sockaddr_in)) < 0) {
+    }
+    sw = 1;
+    if (setsockopt(sloop,IPPROTO_IP,IP_HDRINCL,
+          (char *)&sw, sizeof(sw)) < 0)
+      perror_exit("Can't set special IP header");
+
+    (void)gettimeofday(&clear_cache,0);
+  }
+
+  opack_icmp.icmp_type = ICMP_ECHO;
+  opack_icmp.icmp_id = ident;
+  tv32.tv32_sec = htonl(now.tv_sec);
+  tv32.tv32_usec = htonl(now.tv_usec);
+  if (pingflags & F_TIMING)
+    (void) memcpy(&opack_icmp.icmp_data[0], &tv32, sizeof(tv32));
+  cc = datalen + PHDR_LEN;
+  opack_icmp.icmp_cksum = 0;
+  opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp, cc);
+
+  i = sendto(s, (char *) &opack_icmp, cc, 0,
+      (struct sockaddr *)&send_addr, sizeof(struct sockaddr_in));
+  if (i != cc) {
+    if (i < 0) perror_exit("sendto");
+    else error_msg("wrote %s %d chars, ret=%d", hostname, cc, i);
+    (void)fflush(stderr);
+  }
+  lastrcvd = 0;
+
+  CLR(ntransmitted);
+  ntransmitted++;
+
+  last_tx = now;
+  if (next_tx.tv_sec == 0) {
+    first_tx = now;
+    next_tx = now;
+  }
+
+  /* Transmit regularly, at always the same microsecond in the
+   * second when going at one packet per second.
+   * If we are at most 100 ms behind, send extras to get caught up.
+   * Otherwise, skip packets we were too slow to send.
+   */
+  if (diffsec(&next_tx, &now) <= interval) {
+    do {
+      timevaladd(&next_tx, &interval_tv);
+    } while (diffsec(&next_tx, &now) < -0.1);
+  }
+}
+
+static void doit(void)
+{
+  int cc, once = 1;
+  struct sockaddr_in from;
+  socklen_t fromlen;
+  double sec, last, d_last, resp_last, d_resp;
+  struct pollfd fdmaskp[1];
+
+  (void)gettimeofday(&clear_cache,0);
+  if (maxwait != 0) {
+    last = timeval_to_sec(&clear_cache) + maxwait;
+    d_last = 0;
+  } else {
+    last = 0;
+    d_last = 365*24*60*60;
+  }
+
+  resp_last = 0;
+  d_resp = 0;
+
+  do {
+    (void)gettimeofday(&now,0);
+
+    if (last != 0)
+      d_last = last - timeval_to_sec(&now);
+
+    if (ntransmitted < npackets && d_last > 0) {
+      /* send if within 100 usec or late for next packet */
+      sec = diffsec(&next_tx,&now);
+      if (sec <= 0.0001 ) {
+        pinger();
+        sec = diffsec(&next_tx,&now);
+      }
+      if (sec < 0.0) sec = 0.0;
+      if (d_last < sec) sec = d_last;
+
+    } else {
+      /* For the last response, wait twice as long as the
+       * worst case seen, or 10 times as long as the
+       * maximum interpacket interval, whichever is longer.
+       */
+      if ((nreceived == 0) && wait_resp > 0 && once) {
+        (void)gettimeofday(&clear_cache,0);
+        resp_last = timeval_to_sec(&clear_cache);
+
+        if (toys.optflags & FLAG_w) resp_last += d_last;
+        else resp_last += wait_resp;
+        d_resp = 0;
+        once = 0;
+      }
+      if (resp_last != 0) d_resp = resp_last - timeval_to_sec(&now);
+
+      if ((nreceived == 0) && d_resp > 0) sec = d_resp;
+      else {
+        sec = 2 * tmax;
+        if (d_last < sec) sec = d_last;
+        if (!last) last = timeval_to_sec(&now) + sec;
+      }
+      if (sec <= 0) break;
+    }
+
+    fdmaskp[0].fd = s;
+    fdmaskp[0].events = POLLIN;
+    cc = poll(fdmaskp, 1, (int)(sec * 1000));
+    if (cc <= 0) {
+      if (cc < 0) {
+        if (errno == EINTR) continue;
+        perror_exit("poll");
+      }
+      continue;
+    }
+    fromlen  = sizeof(from);
+    cc = recvfrom(s, (char *) packet, packlen,
+        0, (struct sockaddr *)&from,
+        &fromlen);
+    if (cc < 0) {
+      if (errno != EINTR) {
+        perror_msg("recvfrom");
+        (void)fflush(stderr);
+      }
+      continue;
+    }
+    (void)gettimeofday(&now, 0);
+    pr_pack(packet, cc, &from);
+
+  } while (nreceived < npackets);
+
+  finish(0);
+}
+
+void ping_main(void)
+{
+  const int const_int_1 = 1;
+  int  i;//, on = 1;
+  unsigned ttl = 0;
+  struct in6_addr in6;
+
+  if(!(toys.optflags & FLAG_4) && (inet_pton(AF_INET6, toys.optargs[0], (void*)&in6)))
+    toys.optflags |= FLAG_6;
+  if (toys.optflags & FLAG_6) {
+    //ping6 has 4 options, so 4+3 option placeholders
+    //1 for cmdname 1 for NULL and toys.optc
+    int cnt = 0, opt = 0;
+    char **argv6 = xzalloc((9 + toys.optc) * sizeof(char*)); 
+    argv6[cnt++] = "ping6";
+    if (toys.optflags & FLAG_c) {
+      argv6[cnt++] = "-c";
+      argv6[cnt++] = xmsprintf("%d",TT.count);
+    }
+    if (toys.optflags & FLAG_s) {
+      argv6[cnt++] = "-s";
+      argv6[cnt++] = xmsprintf("%d",TT.size);
+    }
+    if (toys.optflags & FLAG_I) {
+      argv6[cnt++] = "-I";
+      argv6[cnt++] = xmsprintf("%s",TT.iface);
+    }
+    if (toys.optflags & FLAG_q) argv6[cnt++] = "-q";
+
+    while(opt < toys.optc) argv6[cnt++] = toys.optargs[opt++];
+    argv6[cnt] = NULL;
+    xexec(argv6);
+    /*Not Reached */
+    return;
+  }
+
+  s = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  sloop = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+
+  if (toys.optflags & FLAG_c) npackets = TT.count;
+  if (toys.optflags & FLAG_s) datalen = TT.size;
+  if (toys.optflags & FLAG_t) ttl = TT.ttl;
+  if (toys.optflags & FLAG_I) {
+    gethost("-I", TT.iface, &src_addr, 0, 0);
+  }
+  if (toys.optflags & FLAG_W) wait_resp = (double)TT.wait_resp;
+  if (toys.optflags & FLAG_w) maxwait = (double)TT.wait_exit;
+  if (toys.optflags & FLAG_q) pingflags |= F_QUIET;
+
+  sec_to_timeval(interval, &interval_tv);
+
+  if (!npackets) npackets = INT_MAX;
+
+  gethost("", toys.optargs[0], &whereto, hostname, sizeof(hostname));
+  if (IN_MULTICAST(ntohl(whereto.sin_addr.s_addr)))
+    pingflags |= F_MCAST;
+  (void) memcpy(&send_addr, &whereto, sizeof(send_addr));
+
+  loc_addr.sin_family = AF_INET;
+  loc_addr.sin_addr.s_addr = htonl((127<<24)+1);
+
+  if (datalen >= PHDR_LEN) pingflags |= F_TIMING;
+
+  packlen = datalen + 60 + 76;  /* MAXIP + MAXICMP */
+  packet = xmalloc(packlen);
+
+  for (i = PHDR_LEN; i < datalen; i++)
+    opack_icmp.icmp_data[i] = i;
+
+  ident = rand() & 0xFFFF;
+
+  if (ttl) {
+    if (setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
+      perror_exit("Can't set time-to-live");
+    if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,  &ttl, sizeof(ttl)) < 0)
+      perror_exit("Can't set multicast time-to-live");
+  }
+  if (pingflags & F_MCAST) {
+    if ((pingflags & F_SOURCE_ADDR)
+        && setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+          (char *) &src_addr.sin_addr,
+          sizeof(src_addr.sin_addr)) < 0)
+      perror_exit("Can't set multicast source interface");
+  } else if (pingflags & F_SOURCE_ADDR) {
+    if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+          (char *) &src_addr.sin_addr,
+          sizeof(src_addr.sin_addr)) < 0)
+      perror_exit("Can't set source interface/address");
+    if (bind(s,  (struct sockaddr*)&src_addr, sizeof(src_addr)))
+      perror_exit("bind");
+  }
+  if (TT.iface && memcmp(&src_addr, &send_addr, sizeof(send_addr))) {
+    struct ifreq ifr;
+    strncpy(ifr.ifr_name, TT.iface, IFNAMSIZ);
+    if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
+      perror_exit("can't bind to interface %s", TT.iface);
+  }
+
+  setsockopt(s, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+  (void)printf("PING %s (%s):", hostname, inet_ntoa(whereto.sin_addr));
+  if (toys.optflags & FLAG_I)
+    printf(" from %s:", inet_ntoa(src_addr.sin_addr));
+  printf(" %d data bytes.\n", datalen);
+
+  /* When pinging the broadcast address, you can get a lot
+   * of answers.  Doing something so evil is useful if you
+   * are trying to stress the ethernet, or just want to
+   * fill the arp cache to get some stuff for /etc/ethers.
+   */
+  while (0 > setsockopt(s, SOL_SOCKET, SO_RCVBUF,
+        (char*)&bufspace, sizeof(bufspace))) {
+    if ((bufspace -= 4096) <= 0)
+      perror_exit("Cannot set the receive buffer size");
+  }
+
+  /* make it possible to send giant probes, but do not worry now
+   * if it fails, since we probably won't send giant probes.
+   */
+  (void)setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+      (char*)&bufspace, sizeof(bufspace));
+
+  (void)signal(SIGINT, prefinish);
+  (void)signal(SIGQUIT, prtsig);
+  (void)signal(SIGCONT, prtsig);
+
+  doit();
+}
diff --git a/toys/other/ping6.c b/toys/other/ping6.c
new file mode 100644 (file)
index 0000000..6f2cfa4
--- /dev/null
@@ -0,0 +1,554 @@
+/* ping6.c - ping program for IPv6.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_PING6(NEWTOY(ping6, "<1>1c#<0s#<0>65535I:q", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
+
+config PING6
+  bool "ping6"
+  default y
+  help
+    usage: ping6 [OPTIONS] HOST
+
+    Send ICMP ECHO_REQUEST packets to network hosts
+
+    Options:
+    -c CNT      Send only CNT pings
+    -s SIZE     Send SIZE data bytes in packets (default:56)
+    -I IFACE/IP Use interface or IP address as source
+    -q          Quiet, only displays output at start
+                and when finished
+*/
+
+/*
+ * Copyright (c) 1989, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define FOR_ping6
+#include "toys.h"
+
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <net/route.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <arpa/nameser.h>
+#include <signal.h>
+#include <ifaddrs.h>
+
+#define MAXHOSTNAMELEN  64
+
+GLOBALS(
+  char *iface;
+  long size;
+  long count;
+  int sock;
+  long nreceived;     /* # of packets we got back */ 
+  long nrepeats;      /* number of duplicates */
+  long ntransmitted;    /* sequence # for outbound packets = #sent */
+  int timing;     /* flag to do timing */
+  double tmin;  /* minimum round trip time */
+  double tmax;    /* maximum round trip time */
+  double tsum;    /* sum of all times, for doing average */
+  struct sockaddr_in6 src;
+  struct sockaddr_in6 dst;
+  struct timeval interval; //default 1 second delay between pings
+  char *outpack;
+  u_char *packet;
+  int packlen;
+  uint8_t myid; //id for the packets.
+  char hostname[64/*MAXHOSTNAMELEN*/];
+)
+
+struct tv32 {
+  u_int32_t tv32_sec;
+  u_int32_t tv32_usec;
+};
+
+#define ICMP6ECHOLEN  8   /* icmp echo header len excluding time */
+#define A(bit)    rcvd_tbl[(bit)>>3]  /* identify byte in array */
+#define B(bit)    (1 << ((bit) & 0x07))   /* identify bit in byte */
+#define SET(bit)  (A(bit) |= B(bit))
+#define CLR(bit)  (A(bit) &= (~B(bit)))
+#define TST(bit)  (A(bit) & B(bit))
+
+/*
+ * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
+ * number of received sequence numbers we can keep track of.  Change 128
+ * to 8192 for complete accuracy...
+ */
+#define MAX_DUP_CHK (8 * 8192)
+int mx_dup_ck = MAX_DUP_CHK;
+char rcvd_tbl[MAX_DUP_CHK / 8];
+
+/*
+ * Print the ping summary and exit 
+ */
+static void summary(void)
+{
+  xprintf("\n--- %s ping6 statistics ---\n", TT.hostname);
+  xprintf("%ld packets transmitted, ", TT.ntransmitted);
+  xprintf("%ld packets received, ", TT.nreceived);
+  if (TT.nrepeats) xprintf("+%ld duplicates, ", TT.nrepeats);
+  if (TT.ntransmitted) {
+    if (TT.nreceived > TT.ntransmitted)
+      xprintf("-- somebody's duplicating packets!");
+    else xprintf("%.1f%% packet loss",
+        ((((double)TT.ntransmitted - TT.nreceived) * 100.0) /
+         TT.ntransmitted));
+  }     
+  xputc('\n');   
+  if (TT.nreceived && TT.timing) {
+    /* Only display average to microseconds */
+    double num = TT.nreceived + TT.nrepeats;
+    double avg = TT.tsum / num;
+    xprintf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
+        TT.tmin, avg, TT.tmax);
+    (void)fflush(stdout);
+  }     
+  (void)fflush(stdout);  
+}
+
+/* Got an INTERRUPT like CTL^C, Print summary and exit
+ */
+static void onint(int notused)
+{   
+  summary();
+
+  (void)signal(SIGINT, SIG_DFL);
+  (void)kill(getpid(), SIGINT);
+}
+
+/* Compose an ICMP6_ECHO_REQUEST packet and send it to destination.
+ */
+static int pinger()
+{
+  struct icmp6_hdr *icp;
+  int i, cc;
+  int seq;
+
+  if (TT.count && TT.ntransmitted >= TT.count)
+    return(-1); /* no more transmission */
+
+  icp = (struct icmp6_hdr *)TT.outpack;
+  memset(icp, 0, sizeof(*icp));
+  icp->icmp6_cksum = 0;
+  seq = TT.ntransmitted++; //seq is incremental, at every request
+  CLR(seq % mx_dup_ck);
+
+  icp->icmp6_type = ICMP6_ECHO_REQUEST;
+  icp->icmp6_code = 0;
+  icp->icmp6_id = htons(TT.myid);
+  icp->icmp6_seq = ntohs(seq);
+  if (TT.timing) { //add timing information to packet only if packet size is long enough to hold.
+    struct timeval tv;
+    struct tv32 *tv32;
+    (void)gettimeofday(&tv, NULL);
+    tv32 = (struct tv32 *)&TT.outpack[ICMP6ECHOLEN];
+    tv32->tv32_sec = htonl(tv.tv_sec); //convert the value to network order
+    tv32->tv32_usec = htonl(tv.tv_usec);
+  }
+  cc = ICMP6ECHOLEN + TT.size; //total request size.
+
+
+  i = sendto(TT.sock, (char *) TT.outpack, cc, 0,
+      (struct sockaddr *)&TT.dst, sizeof(struct sockaddr_in6)); //send it to destination
+
+  if (i < 0 || i != cc)  {
+    if (i < 0) perror_exit("sendto");
+    xprintf("ping6: wrote %s %d chars, ret=%d\n",
+        TT.hostname, cc, i);
+  }
+
+  return(0);
+}
+
+/*
+ * Transmit an ICMP ECHO packet to destination host, if no more ping is required
+ * wait for the response and exit after timeout.
+ */
+static void retransmit(void)
+{
+  struct itimerval itimer;
+
+  if (pinger() == 0) return;
+
+  /*
+   * If we're not transmitting any more packets, change the timer
+   * to wait two round-trip times if we've received any packets or
+   * ten seconds if we haven't.
+   */
+  if (TT.nreceived) {
+    itimer.it_value.tv_sec =  2 * TT.tmax / 1000;
+    if (itimer.it_value.tv_sec == 0)
+      itimer.it_value.tv_sec = 1;
+  } else itimer.it_value.tv_sec = 10; //default MAX wait for 1st response
+  itimer.it_interval.tv_sec = 0;
+  itimer.it_interval.tv_usec = 0;
+  itimer.it_value.tv_usec = 0;
+
+  (void)signal(SIGALRM, onint);
+  (void)setitimer(ITIMER_REAL, &itimer, NULL);
+} 
+
+/*
+ * SIGALRM handler, for ping interval timeout.
+ */
+static void onsignal(int sig)
+{
+  retransmit();
+}
+
+/* 
+ * tvsub --
+ *  Subtract 2 timeval structs:  out = out - in.  Out is assumed to
+ * be >= in.
+ */ 
+static void tvsub(struct timeval *out, struct timeval *in)
+{  
+  if ((out->tv_usec -= in->tv_usec) < 0) {
+    --out->tv_sec;
+    out->tv_usec += 1000000;
+  }
+  out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * Get HOPLIMIT value from the message
+ */
+static int get_hoplim(struct msghdr *mhdr)
+{
+  struct cmsghdr *cm;
+  int *val;
+
+  for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+      cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+    if (!cm->cmsg_len) return(-1);
+
+    if (cm->cmsg_level == IPPROTO_IPV6 &&
+        cm->cmsg_type == IPV6_HOPLIMIT &&
+        cm->cmsg_len == CMSG_LEN(sizeof(int))) {
+      val = (int *)CMSG_DATA(cm);
+      return *val;
+    }
+  }
+  return(-1);
+}
+
+/*
+ * Unpack the message recieved from destination host
+ */
+static void unpack(u_char *packet, int cc, struct msghdr *mhdr)
+{
+  struct icmp6_hdr *icp;
+  int hoplim;
+  struct sockaddr_in6 *from;
+  struct timeval tv, tp;
+  struct tv32 *tpp;
+  double triptime = 0;
+  int dupflag;
+  u_int16_t seq;
+  char buf[INET6_ADDRSTRLEN];
+
+  (void)gettimeofday(&tv, NULL);
+
+  if (!mhdr || !mhdr->msg_name ||
+      mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
+      ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) {
+    error_msg("invalid peername");
+    return;
+  }
+  from = (struct sockaddr_in6 *)mhdr->msg_name;
+  if (cc < sizeof(struct icmp6_hdr)) {
+    error_msg("packet too short (%d bytes) from %s", cc,
+        inet_ntop(AF_INET6, (void*)&from->sin6_addr, buf, sizeof(buf))); //short packet received
+    return;
+  }
+  icp = (struct icmp6_hdr *)packet;
+
+  if ((hoplim = get_hoplim(mhdr)) == -1) {
+    error_msg("failed to get receiving hop limit");
+    return;
+  }
+
+  if (ntohs(icp->icmp6_id) != TT.myid) return;
+  if (icp->icmp6_type == ICMP6_ECHO_REPLY ) { 
+    /* this is the REPLY packet destined to us. */
+    seq = ntohs(icp->icmp6_seq);
+    ++TT.nreceived;
+    if (TT.timing) {
+      tpp = (struct tv32 *)(icp + 1);
+      tp.tv_sec = ntohl(tpp->tv32_sec);
+      tp.tv_usec = ntohl(tpp->tv32_usec);
+      tvsub(&tv, &tp);
+      triptime = ((double)tv.tv_sec) * 1000.0 +
+        ((double)tv.tv_usec) / 1000.0;
+      TT.tsum += triptime;
+      if (triptime < TT.tmin) TT.tmin = triptime;
+      if (triptime > TT.tmax) TT.tmax = triptime;
+    }
+
+    if (TST(seq % mx_dup_ck)) { //duplicate packet received
+      ++TT.nrepeats;
+      --TT.nreceived;
+      dupflag = 1;
+    } else {
+      SET(seq % mx_dup_ck);
+      dupflag = 0;
+    }
+
+    if (toys.optflags & FLAG_q) return;
+    /* print the REPLY message */
+    xprintf("%d bytes from %s: seq=%u", cc,
+        inet_ntop(AF_INET6, (void*)&from->sin6_addr, buf, sizeof(buf)), seq);
+    xprintf(" ttl=%d", hoplim);
+
+    if (TT.timing) xprintf(" time=%.3f ms", triptime);
+    if (dupflag) xprintf("(DUP!)");
+    xputs("");
+  }
+  else { 
+    /* An ERROR packet received from destination, decode it for proper reason */
+    switch(icp->icmp6_type) {
+      case ICMP6_DST_UNREACH:
+        xprintf("Destination unreachable\n");
+        break;
+      case ICMP6_PACKET_TOO_BIG:
+        xprintf("Packet too big\n");
+        break;
+      case ICMP6_TIME_EXCEEDED:
+        xprintf("Time to live exceeded\n");
+        break;
+      case ICMP6_PARAM_PROB:
+        xprintf("Parameter problem\n");
+        break;
+      case ICMP6_ECHO_REQUEST:
+        xprintf("Echo Request\n");
+        break;
+      case ICMP6_ECHO_REPLY:
+        xprintf("Echo Reply\n");
+        break;
+      case MLD_LISTENER_QUERY:
+        xprintf("Listener Query\n");
+        break;
+      case MLD_LISTENER_REPORT:
+        xprintf("Listener Report\n");
+        break;
+      case MLD_LISTENER_REDUCTION:
+        xprintf("Listener Done\n");
+        break;
+      default:
+        xprintf("Bad ICMP type: %d", icp->icmp6_type);
+        break;
+    }
+  }
+}
+
+/*
+ * do_ping() recieve messages from destination host,
+ * unpack them to decode for success of ping or failure reason.
+ */
+static void do_ping()
+{
+  for (;;) {  //infinite loop
+    struct msghdr m;
+    u_char buf[1024];
+    struct iovec iov[2];
+    int cc;
+    struct sockaddr_in6 from;
+    m.msg_name = (caddr_t)&from; //this will be populated by recvmsg
+    m.msg_namelen = sizeof(from);
+    memset(&iov, 0, sizeof(iov));
+    iov[0].iov_base = (caddr_t)TT.packet;
+    iov[0].iov_len = TT.packlen;
+    m.msg_iov = iov;
+    m.msg_iovlen = 1;
+    m.msg_control = (caddr_t)buf; 
+    m.msg_controllen = sizeof(buf);
+
+    cc = recvmsg(TT.sock, &m, 0);
+    if (cc < 0) {
+      if (errno != EINTR) {
+        perror_msg("recvmsg");
+        sleep(1);
+      }  
+      continue;
+    } else if (!cc) continue;
+    else {
+      /*
+       * an ICMPv6 message (probably an echoreply) arrived.
+       */
+      unpack(TT.packet, cc, &m);
+    }  
+    if (TT.count && TT.nreceived >= TT.count) break; //break if number of packets (cmdline input) is received
+  }
+}
+
+/*
+ * Resolve the hostname to address info, based on IPv6 resolution.
+ */
+static void resolve_name(int flags, char *name, char *hostname, struct sockaddr_in6 *sock)
+{
+  struct addrinfo hints, *res;
+  int ret_ga;
+
+  memset(&hints, 0, sizeof(struct addrinfo)); 
+  hints.ai_flags = flags;
+  hints.ai_family = AF_INET6;
+  hints.ai_socktype = SOCK_RAW;
+  hints.ai_protocol = IPPROTO_ICMPV6;
+
+  ret_ga = getaddrinfo(toys.optargs[0], NULL, &hints, &res);
+  if (ret_ga) error_exit("Hostname: %s", gai_strerror(ret_ga));
+  if (hostname) {
+    if (res->ai_canonname) strncpy(hostname, res->ai_canonname, MAXHOSTNAMELEN);
+    else strncpy(hostname, toys.optargs[0], MAXHOSTNAMELEN);
+  }
+  if (!res->ai_addr) error_exit("getaddrinfo failed"); 
+
+  memcpy(sock, res->ai_addr, res->ai_addrlen);
+  freeaddrinfo(res);
+}
+
+/*
+ * get the interface address for the given interface device.
+ * Resolution is done for IPv6 address family only
+ */
+static void get_src(void *src, char *name)
+{  
+  struct ifaddrs *ifaddr_list, *ifa_item;
+  int family;  
+
+  if (getifaddrs(&ifaddr_list) == -1) perror_exit("getifaddrs");
+
+  /* Walk through linked list, maintaining head pointer so we
+     can free list later */
+
+  for (ifa_item = ifaddr_list; ifa_item; ifa_item = ifa_item->ifa_next) {
+    if (!ifa_item->ifa_addr) continue;
+
+    family = ifa_item->ifa_addr->sa_family;
+
+    if ((family == AF_INET6) && !(strcmp(ifa_item->ifa_name,name))) {
+      memcpy(src, ifa_item->ifa_addr, sizeof(struct sockaddr_in6)); //copy the address details to src
+      break;
+    }
+  }
+  freeifaddrs(ifaddr_list); // free the memory allocated.
+}
+
+void ping6_main(void)
+{
+  int optval, if_idx;
+  const int const_int_1 = 1;
+  struct itimerval itimer;
+  char buf[INET6_ADDRSTRLEN];
+  TT.tmin = 999999999.0; //min time to start with
+  TT.tmax = 0.0;
+  TT.tsum = 0.0;
+  TT.interval.tv_sec = 1;
+  TT.interval.tv_usec = 0;
+  TT.ntransmitted = 0;
+  TT.nreceived = 0;
+  if (!(toys.optflags & FLAG_s)) TT.size = 56;//defualt size;
+  TT.sock = xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+
+  if (toys.optflags & FLAG_I) { //-I interface/address 
+    TT.src.sin6_family = AF_INET6; 
+    if (!inet_pton(AF_INET6, TT.iface, (void*)&TT.src.sin6_addr)) { //check if the input iface address is numeric and proper
+      if_idx = if_nametoindex(TT.iface); //get the interface index of given interface
+      if (if_idx) get_src((void*)&TT.src, TT.iface); //if the interface is proper, get the socket addr for the same
+      else error_exit("Unknown source interface");
+    }
+  }
+
+  resolve_name(AI_CANONNAME, toys.optargs[0], TT.hostname, &TT.dst);
+
+  if (toys.optflags & FLAG_I) {
+    if (bind(TT.sock, (struct sockaddr *)&TT.src, sizeof(TT.src)))
+      perror_exit("bind failed: ");
+  }
+
+  if (TT.size >= sizeof(struct tv32)) TT.timing = 1;
+  else TT.timing = 0;
+
+  TT.packlen = TT.size + 40 + ICMP6ECHOLEN + 256; //IPLEN = 40, EXTRA=256
+  TT.packet = xmalloc(TT.packlen);
+  TT.outpack = xmalloc(TT.size + sizeof(struct icmp6_hdr));
+  TT.myid = getpid();
+
+  optval = 64;// default IPV6_DEFHLIM;
+  if (IN6_IS_ADDR_MULTICAST(&TT.dst.sin6_addr))
+    if (setsockopt(TT.sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+          &optval, sizeof(optval)) == -1)
+      error_exit("IPV6_MULTICAST_HOPS ");
+
+#ifdef ICMP6_FILTER
+  {/* ICMP6 filter settings */ 
+    struct icmp6_filter filt;
+    if (!(toys.optflags & (FLAG_c << 1))) {
+      ICMP6_FILTER_SETBLOCKALL(&filt);
+      ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+    } else ICMP6_FILTER_SETPASSALL(&filt);
+
+    if (setsockopt(TT.sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+          sizeof(filt)) < 0) perror_exit("setsockopt(ICMP6_FILTER): ");
+  }
+#endif /*ICMP6_FILTER*/
+
+  optval = 1;
+#ifdef IPV6_RECVHOPLIMIT                
+  if (setsockopt(TT.sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval,
+        sizeof(optval)) < 0)              
+    error_msg("setsockopt(IPV6_RECVHOPLIMIT)"); 
+#else  /* old adv. API */                 
+  if (setsockopt(TT.sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval,
+        sizeof(optval)) < 0)              
+    error_msg("setsockopt(IPV6_HOPLIMIT)"); 
+#endif
+  setsockopt(TT.sock, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+  xprintf("PING %s (%s):", TT.hostname,                                                                   
+      inet_ntop(AF_INET6, (void*)&TT.dst.sin6_addr, buf, sizeof(buf)));
+  if (toys.optflags & FLAG_I)
+    xprintf(" from %s:", inet_ntop(AF_INET6, (void*)&TT.src.sin6_addr, buf, sizeof(buf)));
+  xprintf(" %ld data bytes.\n", TT.size);
+  signal(SIGINT, onint);
+  signal(SIGALRM, onsignal);
+  itimer.it_interval = TT.interval;
+  itimer.it_value = TT.interval;
+  (void)setitimer(ITIMER_REAL, &itimer, NULL); //interval timer between pings
+  retransmit();
+  do_ping();
+  summary();
+}
diff --git a/toys/other/printenv.c b/toys/other/printenv.c
new file mode 100644 (file)
index 0000000..e8bcf29
--- /dev/null
@@ -0,0 +1,43 @@
+/* printenv.c - Print environment variables.
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+
+USE_PRINTENV(NEWTOY(printenv, "0(null)", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PRINTENV
+  bool "printenv"
+  default y
+  help
+    usage: printenv [-0] [env_var...]
+
+    Print environment variables.
+
+    -0 Use \0 as delimiter instead of \n
+*/
+
+#include "toys.h"
+
+extern char **environ;
+
+void printenv_main(void)
+{
+  char **env, **var = toys.optargs;
+  char delim = '\n';
+
+  if (toys.optflags) delim = 0;
+
+  do {
+    int catch = 0, len = *var ? strlen(*var) : 0;
+
+    for (env = environ; *env; env++) {
+      char *out = *env;
+      if (*var) {
+        if (!strncmp(out, *var, len) && out[len] == '=') out += len +1;
+        else continue;
+      }
+      xprintf("%s%c", out, delim);
+      catch++;
+    }
+    if (*var && !catch) toys.exitval = 1;
+  } while (*var && *(++var));
+}
diff --git a/toys/other/readahead.c b/toys/other/readahead.c
new file mode 100644 (file)
index 0000000..4edd651
--- /dev/null
@@ -0,0 +1,37 @@
+/* readahead.c - preload files into disk cache.
+ *
+ * Copyright 2013 Rob Landley <rob@landley.net>
+ *
+ * No standard.
+
+USE_READAHEAD(NEWTOY(readahead, NULL, TOYFLAG_BIN))
+
+config READAHEAD
+  bool "readahead"
+  default y
+  help
+    usage: readahead FILE...
+
+    Preload files into disk cache.
+*/
+
+#include "toys.h"
+
+#include <sys/syscall.h>
+
+static void do_readahead(int fd, char *name)
+{
+  int rc;
+
+  // Since including fcntl.h doesn't give us the wrapper, use the syscall.
+  // 32 bits takes LO/HI offset (we don't care about endianness of 0).
+  if (sizeof(long) == 4) rc = syscall(__NR_readahead, fd, 0, 0, INT_MAX);
+  else rc = syscall(__NR_readahead, fd, 0, INT_MAX);
+
+  if (rc) perror_msg("readahead: %s", name);
+}
+
+void readahead_main(void)
+{
+  loopfiles(toys.optargs, do_readahead);
+}
diff --git a/toys/other/readlink.c b/toys/other/readlink.c
new file mode 100644 (file)
index 0000000..1c33362
--- /dev/null
@@ -0,0 +1,41 @@
+/* readlink.c - Return string representation of a symbolic link.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+
+USE_READLINK(NEWTOY(readlink, "<1>1fenq[-fe]", TOYFLAG_BIN))
+
+config READLINK
+  bool "readlink"
+  default y
+  help
+    usage: readlink FILE
+
+    With no options, show what symlink points to, return error if not symlink.
+
+    Options for producing cannonical paths (all symlinks/./.. resolved):
+
+    -e cannonical path to existing entry (fail if missing)
+    -f full path (fail if directory missing)
+    -n no trailing newline
+    -q quiet (no output, just error code)
+*/
+
+#define FOR_readlink
+#include "toys.h"
+
+void readlink_main(void)
+{
+  char *s;
+
+  // Calculating full cannonical path?
+
+  if (toys.optflags & (FLAG_f|FLAG_e))
+    s = xabspath(*toys.optargs, toys.optflags & FLAG_e);
+  else s = xreadlink(*toys.optargs);
+
+  if (s) {
+    if (!(toys.optflags & FLAG_q))
+      xprintf((toys.optflags & FLAG_n) ? "%s" : "%s\n", s);
+    if (CFG_TOYBOX_FREE) free(s);
+  } else toys.exitval = 1;
+}
diff --git a/toys/other/realpath.c b/toys/other/realpath.c
new file mode 100644 (file)
index 0000000..ec98108
--- /dev/null
@@ -0,0 +1,26 @@
+/* realpath.c - Return the canonical version of a pathname
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+
+USE_REALPATH(NEWTOY(realpath, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config REALPATH
+  bool "realpath"
+  default y
+  help
+    usage: realpath FILE...
+
+    Display the canonical absolute pathname
+*/
+
+#include "toys.h"
+
+void realpath_main(void)
+{
+  char **s = toys.optargs;
+
+  for (s = toys.optargs; *s; s++) {
+    if (!realpath(*s, toybuf)) perror_msg("%s", *s);
+    else xputs(toybuf);
+  }
+}
diff --git a/toys/other/reboot.c b/toys/other/reboot.c
new file mode 100644 (file)
index 0000000..4f31d3a
--- /dev/null
@@ -0,0 +1,43 @@
+/* reboot.c - Reboot program.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_REBOOT(NEWTOY(reboot, "d#<0nf", TOYFLAG_USR|TOYFLAG_BIN))
+
+config REBOOT
+  bool "reboot"
+  default y
+  help
+    usage: reboot [-d delay] [-n] [-f]
+
+    Reboot utility to reboot the sytem.
+    -d DELAY  waits DELAY seconds before rebooting.
+    -n    overrides sync() call before reboot.
+    -f    Forces system REBOOT and bypasses init process.
+*/
+
+#define FOR_reboot
+#include "toys.h"
+
+#include <sys/reboot.h>
+#include <err.h>
+#include <signal.h>
+#include <syslog.h>
+
+GLOBALS(
+  long delay_number;
+)
+
+void reboot_main(void)
+{
+  if (toys.optflags & FLAG_d) sleep((unsigned int)TT.delay_number);
+  if ((toys.optflags & FLAG_n)?0:1) sync();
+  if (toys.optflags & FLAG_f) reboot(RB_AUTOBOOT); //howto=RB_AUTOBOOT;
+  else {
+    kill(1,SIGTERM);
+    sleep(3);
+    reboot(RB_DISABLE_CAD);
+  }
+}
diff --git a/toys/other/rev.c b/toys/other/rev.c
new file mode 100644 (file)
index 0000000..b5720a3
--- /dev/null
@@ -0,0 +1,41 @@
+/* rev.c - reverse lines of a set of given input files
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_REV(NEWTOY(rev, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config REV
+  bool "rev"
+  default y
+  help
+    usage: rev [FILE...]
+
+    Output each line reversed, when no files are given stdin is used.
+*/
+
+#include "toys.h"
+
+void do_rev(int fd, char *name)
+{
+  char *c;
+
+  for (;;) {
+    int len, i;
+
+    if (!(c = get_line(fd))) break;
+    len = strlen(c) - 1;
+    for (i = 0; i <= len/2; i++) {
+      char tmp = c[i];
+
+      c[i] = c[len-i];
+      c[len-i] = tmp;
+    }
+    xputs(c);
+    free(c);
+  }
+}
+
+void rev_main(void)
+{
+  loopfiles(toys.optargs, do_rev);
+}
diff --git a/toys/other/rmmod.c b/toys/other/rmmod.c
new file mode 100644 (file)
index 0000000..b789acc
--- /dev/null
@@ -0,0 +1,43 @@
+/* rmmod.c - Remove a module from the Linux kernel.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_RMMOD(NEWTOY(rmmod, "<1wf", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config RMMOD
+  bool "rmmod"
+  default y
+  help
+    usage: rmmod [-wf] [MODULE]
+
+    Unload the module named MODULE from the Linux kernel.
+    -f Force unload of a module
+    -w Wait until the module is no longer used.
+
+*/
+
+#define FOR_rmmod
+#include "toys.h"
+
+#include <sys/syscall.h>
+#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+
+void rmmod_main(void)
+{
+  unsigned int flags = O_NONBLOCK|O_EXCL;
+  char * mod_name;
+  int len;
+
+  // Basename
+  mod_name = basename(*toys.optargs);
+
+  // Remove .ko if present
+  len = strlen(mod_name);
+  if (len > 3 && !strcmp(&mod_name[len-3], ".ko" )) mod_name[len-3] = 0;
+
+  if (toys.optflags & FLAG_f) flags |= O_TRUNC;
+  if (toys.optflags & FLAG_w) flags &= ~O_NONBLOCK;
+
+  if (delete_module(mod_name, flags))
+    perror_exit("failed to unload %s", mod_name);
+}
diff --git a/toys/other/route.c b/toys/other/route.c
new file mode 100644 (file)
index 0000000..db68a24
--- /dev/null
@@ -0,0 +1,743 @@
+/* route.c - Display routing table.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
+config ROUTE
+  bool "route"
+  default y
+  help
+    usage: route -neA inet{6} / [{add|del}]
+
+    Display/Edit kernel routing tables.
+
+    -n  Don't resolve names
+    -e  Display other/more information
+    -A  inet{6} Select Address Family
+*/
+
+#define FOR_route
+#include "toys.h"
+#include <net/route.h>
+#include <sys/param.h>
+#include <net/if.h>
+
+GLOBALS(
+  char *family;
+)
+
+
+#ifndef RTF_IRTT
+#define RTF_IRTT 0x0100 //Initial round trip time.
+#endif
+
+#define INET4 "inet"
+#define INET6 "inet6"
+#define DEFAULT_PREFIXLEN 128
+#define ACTION_ADD  1
+#define INVALID_ADDR 0xffffffffUL
+#define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0')
+
+#define TEST_ARGV(argv) if(!*argv) show_help()
+
+typedef struct _arglist {
+  char *arg;
+  int action;
+}ARGLIST;
+
+//add - add a new route.
+//del - delete a route.
+static ARGLIST arglist1[] = {
+  { "add", 1 },
+  { "del", 2 },
+  { "delete", 2 },
+  { NULL, 0 }
+};
+
+//-net - the target is a network.
+//-host - the target is a host.
+static ARGLIST arglist2[] = {
+  { "-net", 1 },
+  { "-host", 2 },
+  { NULL, 0 }
+};
+
+//Function Declaration
+void display_routes6(void);
+static void show_route_help(void);
+static void setroute(char **);
+static void setroute_inet6(char **);
+
+/*
+ * used to get the host name from the given ip.
+ */
+int get_hostname(const char *ipstr, struct sockaddr_in *sockin)
+{
+  struct hostent *host;
+  sockin->sin_family = AF_INET;
+  sockin->sin_port = 0;
+
+  if(strcmp(ipstr, "default") == 0) {
+    sockin->sin_addr.s_addr = INADDR_ANY;
+    return 1;
+  }
+
+  if(inet_aton(ipstr, &sockin->sin_addr)) return 0;
+
+  host = gethostbyname(ipstr);
+  if(host == NULL) return -1;
+  memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr));
+  return 0;
+}
+
+/*
+ * used to extract the address info from the given ip.
+ */
+int get_addrinfo(const char *ip, struct sockaddr_in6 *sock_in6)
+{
+  struct addrinfo hints, *result;
+  int status = 0;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_INET6;
+  if((status = getaddrinfo(ip, NULL, &hints, &result)) != 0) {
+    perror_msg("getaddrinfo: %s", gai_strerror(status));
+    return -1;
+  }
+  if(result) {
+    memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6));
+    freeaddrinfo(result);
+  }
+  return 0;
+}
+
+/*
+ * used to get the flag values for route command.
+ */
+static void get_flag_value(char **flagstr, int flags)
+{
+  int i = 0;
+  char *str = *flagstr;
+  static const char flagchars[] = "GHRDMDAC";
+  static const unsigned flagarray[] = {
+    RTF_GATEWAY,
+    RTF_HOST,
+    RTF_REINSTATE,
+    RTF_DYNAMIC,
+    RTF_MODIFIED,
+    RTF_DEFAULT,
+    RTF_ADDRCONF,
+    RTF_CACHE
+  };
+  *str++ = 'U';
+  while( (*str = flagchars[i]) != 0) {
+    if(flags & flagarray[i++]) ++str;
+  }
+}
+
+/*
+ * extract inet4 route info from /proc/net/route file and display it.
+ */
+static void display_routes(int is_more_info, int notresolve)
+{
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+  unsigned long dest, gate, mask;
+  int flags, ref, use, metric, mss, win, irtt;
+  char iface[64]={0,};
+  char buf[BUFSIZ] = {0,};
+  char *flag_val = xzalloc(10); //there are 9 flags "UGHRDMDAC" for route.
+
+  FILE *fp = xfopen("/proc/net/route", "r");
+
+  xprintf("Kernel IP routing table\n"
+                   "Destination     Gateway         Genmask         Flags %s Iface\n",
+                               is_more_info ? "  MSS Window  irtt" : "Metric Ref    Use");
+  fgets(buf, BUFSIZ, fp); //skip 1st line.
+  while(fgets(buf, BUFSIZ, fp)) {
+     int nitems = 0;
+     char *destip = NULL, *gateip = NULL, *maskip = NULL;
+     memset(flag_val, 0, 10);
+
+     nitems = sscanf(buf, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
+                 iface, &dest, &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt);
+     if(nitems != 11) {//EOF with no (nonspace) chars read...
+       if((nitems < 0) && feof(fp)) break;
+      perror_exit("fscanf");
+    }
+    //skip down interfaces...
+    if(!(flags & RTF_UP)) continue;
+
+    //For Destination
+    if(dest){
+      if(inet_ntop(AF_INET, &dest, buf, BUFSIZ) > 0) destip = xstrdup(buf);
+    }
+    else {
+      if(!notresolve) destip = xstrdup("default");
+      else destip = xstrdup("0.0.0.0");
+    }
+    //For Gateway
+    if(gate){
+      if(inet_ntop(AF_INET, &gate, buf, BUFSIZ) > 0) gateip = xstrdup(buf);
+    }
+    else {
+      if(!notresolve) gateip = xstrdup("*");
+      else gateip = xstrdup("0.0.0.0");
+    }
+    //For Mask
+    if(inet_ntop(AF_INET, &mask, buf, BUFSIZ) > 0) maskip = xstrdup(buf);
+
+    //Get flag Values
+    get_flag_value(&flag_val, (flags & IPV4_MASK));
+    if(flags & RTF_REJECT) flag_val[0] = '!';
+    xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
+    if(destip) free(destip);
+    if(gateip) free(gateip);
+    if(maskip) free(maskip);
+    if(is_more_info) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
+    else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
+  }//end of while...
+  fclose(fp);
+  if(flag_val) free(flag_val);
+#undef IPV4_MASK
+  return;
+}
+
+/*
+ * used to converts string into int and validate the input str for invalid int value or out-of-range.
+ */
+static unsigned get_strtou(const char *str, char **endp, int base)
+{
+  unsigned long uli;
+  char *endptr;
+
+  if(!isalnum(str[0])) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+  errno = 0;
+  uli = strtoul(str, &endptr, base);
+  if(uli > UINT_MAX) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+
+  if(endp) *endp = endptr;
+  if(endptr[0]) {
+    if(isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
+      errno = ERANGE;
+      return UINT_MAX;
+    }
+    errno = EINVAL;
+  }
+  return uli;
+}
+
+/*
+ * find the given parameter in list like add/del/net/host.
+ * and if match found return the appropriate action.
+ */
+static int get_action(char ***argv, ARGLIST *list)
+{
+  if(!**argv) return 0;
+
+  const ARGLIST *alist;
+  for(alist = list; alist->arg; alist++) { //find the given parameter in list and return the action.
+    if(strcmp(**argv, alist->arg) == 0) {
+      *argv += 1;
+      return alist->action;
+    }
+  }
+  return 0;
+}
+
+/*
+ * route utility main function.
+ */
+void route_main(void)
+{
+  char **argv = toys.optargs;
+  if(!*argv) {
+    if((toys.optflags & FLAG_A) && (strcmp(TT.family, INET6) == 0))
+      display_routes6();
+    else if( (!(toys.optflags & FLAG_A)) ||
+        ((toys.optflags & FLAG_A) && (strcmp(TT.family, INET4) == 0)))
+      display_routes((toys.optflags & FLAG_e), (toys.optflags & FLAG_n));
+    else show_route_help();
+    return;
+  }//End of if statement.
+  
+  if((toys.optflags & FLAG_A) && (strcmp(TT.family, INET6) == 0))
+    setroute_inet6(argv);
+  else setroute(argv);
+  return;
+}
+
+/*
+ * get prefix len (if any) and remove the prefix from target ip.
+ * if no prefix then set netmask as default.
+ */
+static void is_prefix(char **tip, char **netmask, struct rtentry *rt)
+{
+  char *prefix = strchr(*tip, '/');
+  if(prefix) {
+    unsigned long plen;
+    plen = get_int_value(prefix + 1, 0, 32);
+    //used to verify the netmask and route conflict.
+    (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr) = htonl( ~(INVALID_ADDR >> plen));
+    *prefix = '\0';
+    rt->rt_genmask.sa_family = AF_INET;
+  }
+  else *netmask = "default"; //default netmask.
+  return;
+}
+
+/*
+ * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values.
+ * additionally set the flag values for reject, mod, dyn and reinstate.
+ */
+static void get_next_params(char **argv, struct rtentry *rt, char **netmask)
+{
+
+  while(*argv) {
+    //set the metric field in the routing table.
+    if(strcmp(*argv, "metric") == 0) {
+      //+1 for binary compatibility!
+      argv += 1;
+      TEST_ARGV(argv);
+      rt->rt_metric = get_strtou(*argv, NULL, 10) + 1;
+      argv += 1;
+    }
+    //when adding a network route, the netmask to be used.
+    else if(strcmp(*argv, "netmask") == 0) {
+      struct sockaddr sock;
+      unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
+      if(addr_mask) show_route_help();
+      argv += 1;
+      TEST_ARGV(argv);
+      *netmask = *argv;
+      if(get_hostname(*netmask, (struct sockaddr_in *) &sock) < 0)
+        perror_exit("resolving '%s'", *netmask);
+      rt->rt_genmask = sock;
+      argv += 1;
+    }
+    //route packets via a gateway.
+    else if(strcmp(*argv, "gw") == 0) {
+      if(!(rt->rt_flags & RTF_GATEWAY)) {
+        int ishost;
+        argv += 1;
+      TEST_ARGV(argv);
+        if((ishost = get_hostname(*argv, (struct sockaddr_in *) &rt->rt_gateway)) == 0) {
+          rt->rt_flags |= RTF_GATEWAY;
+          argv += 1;
+        }
+        else if(ishost < 0) perror_exit("resolving '%s'", *argv);
+        else perror_exit("gateway '%s' is a NETWORK", *argv);
+      }
+      else show_route_help();
+    }
+    //set the TCP Maximum Segment Size (MSS) for connections over this route to M bytes.
+    else if(strcmp(*argv, "mss") == 0) {
+#define MSS_LOW 64
+#define MSS_MAX 32768
+      argv += 1;
+      TEST_ARGV(argv);
+      rt->rt_mss = get_int_value(*argv, MSS_LOW, MSS_MAX);
+      rt->rt_flags |= RTF_MSS;
+#undef MSS_LOW
+#undef MSS_MAX
+      argv += 1;
+    }
+    //set the TCP window size for connections over this route to W bytes.
+    else if(strcmp(*argv, "window") == 0) {
+#define WIN_LOW 128
+      argv += 1;
+      TEST_ARGV(argv);
+      rt->rt_window = get_int_value(*argv, WIN_LOW, INT_MAX);
+      rt->rt_flags |= RTF_WINDOW;
+#undef WIN_LOW
+      argv += 1;
+    }
+    //set the initial round trip time (irtt) for TCP connections over this route to I milliseconds (1-12000).
+    else if(strcmp(*argv, "irtt") == 0) {
+      long nclock_ticks = sysconf(_SC_CLK_TCK); //number of clock ticks per second.
+      argv += 1;
+      TEST_ARGV(argv);
+      nclock_ticks /= 100;
+      rt->rt_irtt = strtoul(*argv, NULL, 10);
+      if(nclock_ticks > 0) rt->rt_irtt *= nclock_ticks;
+      rt->rt_flags |= RTF_IRTT;
+      argv += 1;
+    }
+    //force the route to be associated with the specified device, as the kernel will otherwise try to determine the device on its own.
+    else if(strcmp(*argv, "dev") == 0) {
+      argv += 1;
+      TEST_ARGV(argv);
+      if((!rt->rt_dev)) rt->rt_dev = *argv;
+      argv += 1;
+    }
+    //install a blocking route, which will force a route lookup to fail. This is NOT for firewalling.
+    else if(strcmp(*argv, "reject") == 0) {
+      rt->rt_flags |= RTF_REJECT;
+      argv += 1;
+    }
+    //install a dynamic or modified route. These flags are for diagnostic purposes, and are generally only set by routing daemons.
+    else if(strcmp(*argv, "mod") == 0) {
+      rt->rt_flags |= RTF_MODIFIED;
+      argv += 1;
+    }
+    else if(strcmp(*argv, "dyn") == 0) {
+      rt->rt_flags |= RTF_DYNAMIC;
+      argv += 1;
+    }
+    else if(strcmp(*argv, "reinstate") == 0) {
+      rt->rt_flags |= RTF_REINSTATE;
+      argv += 1;
+    }
+    //No match found; exit form the application.
+    else show_route_help();
+  }//end of while loop.
+  if(!rt->rt_dev) {
+    if(rt->rt_flags & RTF_REJECT) rt->rt_dev = (char *)"lo";
+  }
+  return;
+}
+
+/*
+ * used to verify the netmask and conflict in netmask and route address.
+ */
+static void verify_netmask(struct rtentry *rt, char *netmask)
+{
+  unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
+  unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr);
+  if(addr_mask) {
+       addr_mask = ~ntohl(addr_mask);
+    if((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR)
+      perror_exit("conflicting netmask and host route");
+    if(addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask);
+    addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
+    if(addr_mask & router_addr) perror_exit("conflicting netmask and route address");
+  }
+  return;
+}
+
+/*
+ * used to update the kermel info.
+ */
+static void do_ioctl(int sokfd, int action, void *rt)
+{
+  if(action == ACTION_ADD) {
+    if(ioctl(sokfd, SIOCADDRT, rt) < 0) perror_exit("SIOCADDRT");
+  }
+  else {
+    if(ioctl(sokfd, SIOCDELRT, rt) < 0) perror_exit("SIOCDELRT");
+  }
+  return;
+}
+
+/*
+ * add/del a route.
+ */
+static void setroute(char **argv)
+{
+  struct rtentry rt;
+  const char *netmask = NULL;
+  const char *targetip;
+  int is_net_or_host = 0;
+  int sokfd;
+  
+  //verify the arg for add/del.
+  int action = get_action(&argv, arglist1);
+  if(!action || !*argv) show_route_help();
+
+  //verify the arg for -net or -host.
+  int arg2_action = get_action(&argv, arglist2);
+  //next arg can be other than -net or -host.
+  if(*argv == NULL) show_route_help();
+
+  memset(&rt, 0, sizeof(struct rtentry));
+  targetip = *argv++;
+  
+  is_prefix((char **)&targetip, (char **)&netmask, &rt);
+
+  if((is_net_or_host = get_hostname(targetip, (struct sockaddr_in *) &rt.rt_dst)) < 0)
+    perror_exit("resolving '%s'", targetip);
+
+  if(arg2_action) is_net_or_host = arg2_action & 1;
+
+  rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST));
+
+  get_next_params(argv, &rt, (char **)&netmask);
+  verify_netmask(&rt, (char *)netmask);
+
+  if((action == ACTION_ADD) && (rt.rt_flags & RTF_HOST))
+    (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR;
+
+  sokfd = socket(AF_INET, SOCK_DGRAM, 0);
+  if(sokfd < 0) perror_exit("socket");
+  do_ioctl(sokfd, action, &rt);
+  xclose(sokfd);
+  return;
+}
+
+/*
+ * get prefix len (if any) and remove the prefix from target ip.
+ * if no prefix then set default prefix len.
+ */
+static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt)
+{
+  unsigned long plen;
+  char *prefix = strchr(*tip, '/');
+  if(prefix) {
+    *prefix = '\0';
+    plen = get_int_value(prefix + 1, 0, DEFAULT_PREFIXLEN);
+  }
+  else plen = DEFAULT_PREFIXLEN;
+  rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP;
+  rt->rtmsg_dst_len = plen;
+  return;
+}
+
+/*
+ * used to get the params like: metric, gw, dev and their values.
+ * additionally set the flag values for mod and dyn.
+ */
+static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name)
+{
+  while(*argv) {
+    //set the metric field in the routing table.
+    if(strcmp(*argv, "metric") == 0) {
+      argv += 1;
+      TEST_ARGV(argv);
+      rt->rtmsg_metric = get_strtou(*argv, NULL, 10);
+      argv += 1;
+    }
+    //route packets via a gateway.
+    else if(strcmp(*argv, "gw") == 0) {
+      if(!(rt->rtmsg_flags & RTF_GATEWAY)) {
+        argv += 1;
+        TEST_ARGV(argv);
+        if(get_addrinfo(*argv, (struct sockaddr_in6 *) &sock_in6) == 0) {
+          memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr));
+          rt->rtmsg_flags |= RTF_GATEWAY;
+          argv += 1;
+        }
+        else perror_exit("resolving '%s'", *argv);
+      }
+      else show_route_help();
+    }
+    //force the route to be associated with the specified device, as the kernel will otherwise try to determine the device on its own.
+    else if(strcmp(*argv, "dev") == 0) {
+      argv += 1;
+      TEST_ARGV(argv);
+      if(!*dev_name) *dev_name = *argv;
+      argv += 1;
+    }
+    //install a dynamic or modified route. These flags are for diagnostic purposes, and are generally only set by routing daemons.
+    else if(strcmp(*argv, "mod") == 0) {
+      rt->rtmsg_flags |= RTF_MODIFIED;
+      argv += 1;
+    }
+    else if(strcmp(*argv, "dyn") == 0) {
+      rt->rtmsg_flags |= RTF_DYNAMIC;
+      argv += 1;
+    }
+    //Nothing matched.
+    else show_route_help();
+  }//end of while loop.
+  return;
+}
+
+/*
+ * used to verify the interface name, if interface name is specified.
+ * otherwise kernel will try to determine the device on its own.
+ */
+static void verify_iface(char *dev_name, int action, struct in6_rtmsg *rt)
+{
+#ifndef IFNAMSIZ
+  #define IFNAMSIZ 16
+#endif
+  //Create a socket.
+  int sokfd = socket(AF_INET6, SOCK_DGRAM, 0);
+  if(sokfd < 0) perror_exit("socket");
+  rt->rtmsg_ifindex = 0;
+  if(dev_name) {
+    char ifre_buf[sizeof(struct ifreq)] = {0,};
+    struct ifreq *ifre = (void *)ifre_buf;
+    strncpy(ifre->ifr_name, dev_name, IFNAMSIZ-1);
+    if(ioctl(sokfd, SIOGIFINDEX, ifre) < 0) {
+      xclose(sokfd);
+      perror_exit("ioctl %#x failed", SIOGIFINDEX);
+    }
+    rt->rtmsg_ifindex = ifre->ifr_ifindex;
+  }
+  do_ioctl(sokfd, action, rt);
+  xclose(sokfd);
+  return;
+}
+
+/*
+ * add/del a route.
+ */
+static void setroute_inet6(char **argv)
+{
+  struct sockaddr_in6 sock_in6;
+  struct in6_rtmsg rt;
+  memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
+  memset(&rt, 0, sizeof(struct in6_rtmsg));
+
+  //verify the arg for add/del.
+  int action = get_action(&argv, arglist1);
+  if(!action || !*argv) show_route_help();
+
+  const char *targetip = *argv++;
+  if(*argv) {
+    unsigned long plen = 0;
+    const char *dev_name = NULL;
+    if(!strcmp(targetip, "default")) {
+       rt.rtmsg_flags = RTF_UP;
+       rt.rtmsg_dst_len = plen;
+    }
+    else {
+      is_prefix_inet6((char **)&targetip, &rt);
+
+      if(get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6) != 0)
+        perror_exit("resolving '%s'", targetip);
+    }
+    rt.rtmsg_metric = 1; //default metric.
+    memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+    get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name);
+    verify_iface((char *)dev_name, action, &rt);
+  }
+  //no more arguments.
+  else show_route_help();
+  return;
+}
+
+/*
+ * format the dest and src address in ipv6 format.
+ * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62
+ */
+static void ipv6_addr_formating(char *ptr, char *addr)
+{
+  int i = 0;
+  while(i <= IPV6_ADDR_LEN) {
+    if(!*ptr) {
+      if(i == IPV6_ADDR_LEN) {
+        addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' seperated address.
+        break;
+      }
+      error_exit("IPv6 ip format error");
+    }
+    addr[i++] = *ptr++;
+    //put ':' after 4th bit
+    if(!((i+1) % 5)) addr[i++] = ':';
+  }
+  return;
+}
+
+/*
+ * display the ipv6 route info
+ * [    Dest addr /     plen ]
+* fe80000000000000025056fffec00008 80 \
+* [ (?subtree) : src addr/plen : 0/0]
+* 00000000000000000000000000000000 00 \
+* [    next hop        ][ metric ][ref ctn][ use   ]
+* 00000000000000000000000000000000 00000000 00000000 00000000 \
+* [ flags ][dev name]
+* 80200001     lo
+*/
+void display_routes6(void)
+{
+#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
+  char ipv6_addr[80] = {0,};
+  char ipv6_dest_addr[41] = {0,}; //32 bytes for address, 7 for ':' and 2 for '\0'
+  char ipv6_src_addr[41] = {0,}; //32 bytes for address, 7 for ':' and 2 for '\0'
+  char ipv6_addr6[128] = {0,};
+  char *flag_val = xzalloc(10);
+  int prefixlen, len, metric, use, refcount, flag;
+
+  FILE *fp = xfopen("/proc/net/ipv6_route", "r");
+  char buf[BUFSIZ] = {0,};
+
+  xprintf("Kernel IPv6 routing table\n");
+  xprintf("%-44s%-40s" "Flags Metric Ref    Use Iface\n", "Destination", "Next Hop");
+  while(fgets(buf, BUFSIZ, fp)) {
+    int nitems = 0;
+    char iface[16] = {0,};
+    memset(flag_val, 0, 10);
+
+    //7 ':' and one '\0' = 8
+    nitems = sscanf(buf, "%32s%x%*s%x%32s%x%x%x%x%10s\n",
+      ipv6_dest_addr+8, &prefixlen, &len, ipv6_src_addr+8, &metric, &use, &refcount, &flag, iface);
+
+    if(nitems != 9) {//EOF with no (nonspace) chars read.
+      if((nitems < 0) && feof(fp)) break;
+      perror_exit("sscanf");
+    }
+
+    //skipped down interfaces.
+    if(!(flag & RTF_UP)) continue;
+
+    //ipv6_dest_addr+8: as the values are filled from the 8th location of the array.
+    ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr);
+    ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr);
+
+    //merge dest and src array, as we need to get ip for dest as well as source.
+    //it will reduce the duplication of code.
+    {
+      int i = 0, j = 0;
+      while(i < IPV6_ADDR_LEN) {
+        ipv6_addr[i] = ipv6_dest_addr[i];
+        i++;
+      }
+      while(j < IPV6_ADDR_LEN) {
+        ipv6_addr[i+j] = ipv6_src_addr[j];
+        j++;
+      }
+    }
+    get_flag_value(&flag_val, (flag & IPV6_MASK));
+    nitems = 0;
+    do {
+      struct sockaddr_in6 skaddr6;
+      char *destip = NULL;
+      if(inet_pton(AF_INET6, ipv6_addr + nitems,(struct sockaddr *) &skaddr6.sin6_addr) > 0) {
+        skaddr6.sin6_family = AF_INET6;
+        if(inet_ntop(AF_INET6, &skaddr6.sin6_addr, buf, BUFSIZ) > 0) {
+          destip = xstrdup(buf);
+          if(!nitems) {
+            sprintf(ipv6_addr6, "%s/%d", destip, prefixlen);
+            nitems += IPV6_ADDR_LEN;
+            free(destip);
+            destip = NULL;
+          }
+          else {
+            xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n",
+                ipv6_addr6, destip, flag_val, metric, refcount, use, iface);
+            free(destip);
+            destip = NULL;
+            break;
+          }
+        }
+      }
+    }while(nitems <= IPV6_ADDR_LEN);
+  }
+  fclose(fp);
+  fp = NULL;
+  if(flag_val) {
+    free(flag_val);
+    flag_val = NULL;
+  }
+#undef IPV6_MASK
+  return;
+}
+
+/*
+ * display help info and exit from application.
+ */
+static void show_route_help(void)
+{
+  toys.exithelp = 1;
+  error_exit("Invalid Argument");
+}
diff --git a/toys/other/setsid.c b/toys/other/setsid.c
new file mode 100644 (file)
index 0000000..59a1d78
--- /dev/null
@@ -0,0 +1,28 @@
+/* setsid.c - Run program in a new session ID.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+
+USE_SETSID(NEWTOY(setsid, "^<1t", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SETSID
+  bool "setsid"
+  default y
+  help
+    usage: setsid [-t] command [args...]
+
+    Run process in a new session.
+
+    -t Grab tty (become foreground process, receiving keyboard signals)
+*/
+
+#include "toys.h"
+
+void setsid_main(void)
+{
+  while (setsid()<0) if (vfork()) _exit(0);
+  if (toys.optflags) {
+    setpgid(0,0);
+    tcsetpgrp(0, getpid());
+  }
+  xexec(toys.optargs);
+}
diff --git a/toys/other/stat.c b/toys/other/stat.c
new file mode 100644 (file)
index 0000000..c5c96fc
--- /dev/null
@@ -0,0 +1,163 @@
+/* stat.c : display file or file system status
+ * Copyright 2012 <warior.linux@gmail.com>
+ * Copyright 2013 <anand.sinha85@gmail.com>
+
+USE_STAT(NEWTOY(stat, "c:f", TOYFLAG_BIN)) 
+
+config STAT
+  bool stat
+  default y
+  help
+    usage: stat [-f] [-c FORMAT] FILE...
+
+    Display status of files or filesystems.
+
+    -f display filesystem status instead of file status
+    -c Output specified FORMAT string instead of default
+
+    The valid format escape sequences for files:
+    %a  Access bits (octal) |%A  Access bits (flags)|%b  Blocks allocated
+    %B  Bytes per block     |%d  Device ID (dec)    |%D  Device ID (hex)
+    %f  All mode bits (hex) |%F  File type          |%g  Group ID
+    %G  Group name          |%h  Hard links         |%i  Inode
+    %n  Filename            |%N  Long filename      |%o  I/O block size
+    %s  Size (bytes)        |%u  User ID            |%U  User name
+    %x  Access time         |%X  Access unix time   |%y  File write time
+    %Y  File write unix time|%z  Dir change time    |%Z  Dir change unix time
+
+    The valid format escape sequences for filesystems:
+    %a  Available blocks    |%b  Total blocks       |%c  Total inodes
+    %d  Free inodes         |%f  Free blocks        |%i  File system ID
+    %l  Max filename length |%n  File name          |%s  Fragment size
+    %S  Best transfer size  |%t  File system type
+*/
+
+#define FOR_stat
+#include "toys.h"
+
+GLOBALS(
+  char *fmt;
+
+  union {
+    struct stat st;
+    struct statfs sf;
+  } stat;
+  struct passwd *user_name;
+  struct group *group_name;
+)
+
+
+// Note: the atime, mtime, and ctime fields in struct stat are the start
+// of embedded struct timespec, but posix won't let them use that
+// struct definition for legacy/namespace reasons.
+
+static void date_stat_format(struct timespec *ts)
+{
+  strftime(toybuf, sizeof(toybuf), "%Y-%m-%d %H:%M:%S",
+    localtime(&(ts->tv_sec)));
+  xprintf("%s.%09d", toybuf, ts->tv_nsec);
+}
+
+static void print_stat(char type)
+{
+  struct stat *stat = (struct stat *)&TT.stat;
+
+  if (type == 'a') xprintf("%04lo", stat->st_mode & ~S_IFMT);
+  else if (type == 'A') {
+    char str[11];
+
+    mode_to_string(stat->st_mode, str);
+    xprintf("%s", str);
+  } else if (type == 'b') xprintf("%llu", stat->st_blocks);
+  else if (type == 'B') xprintf("%lu", stat->st_blksize);
+  else if (type == 'd') xprintf("%ldd", stat->st_dev);
+  else if (type == 'D') xprintf("%llxh", stat->st_dev);
+  else if (type == 'f') xprintf("%lx", stat->st_mode);
+  else if (type == 'F') {
+    char *t = "character device\0directory\0block device\0" \
+              "regular file\0symbolic link\0socket\0FIFO (named pipe)";
+    int i, filetype = stat->st_mode & S_IFMT;
+
+    for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1;
+    if (!stat->st_size && filetype == S_IFREG) t = "regular empty file";
+    xprintf("%s", t);
+  } else if (type == 'g') xprintf("%lu", stat->st_gid);
+  else if (type == 'G') xprintf("%8s", TT.user_name->pw_name);
+  else if (type == 'h') xprintf("%lu", stat->st_nlink);
+  else if (type == 'i') xprintf("%llu", stat->st_ino);
+  else if (type == 'N') {
+    xprintf("`%s'", *toys.optargs);
+    if (S_ISLNK(stat->st_mode))
+      if (0<readlink(*toys.optargs, toybuf, sizeof(toybuf)))
+        xprintf(" -> `%s'", toybuf);
+  } else if (type == 'o') xprintf("%lu", stat->st_blksize);
+  else if (type == 's') xprintf("%llu", stat->st_size);
+  else if (type == 'u') xprintf("%lu", stat->st_uid);
+  else if (type == 'U') xprintf("%8s", TT.user_name->pw_name);
+  else if (type == 'x') date_stat_format((void *)&stat->st_atime);
+  else if (type == 'X') xprintf("%llu", (long long)stat->st_atime);
+  else if (type == 'y') date_stat_format((void *)&stat->st_mtime);
+  else if (type == 'Y') xprintf("%llu", (long long)stat->st_mtime);
+  else if (type == 'z') date_stat_format((void *)&stat->st_ctime);
+  else if (type == 'Z') xprintf("%llu", (long long)stat->st_ctime);
+  else xprintf("?");
+}
+
+static void print_statfs(char type) {
+  struct statfs *statfs = (struct statfs *)&TT.stat;
+
+  if (type == 'a') xprintf("%lu", statfs->f_bavail);
+  else if (type == 'b') xprintf("%lu", statfs->f_blocks);
+  else if (type == 'c') xprintf("%lu", statfs->f_files);
+  else if (type == 'd') xprintf("%lu", statfs->f_ffree);
+  else if (type == 'f') xprintf("%lu", statfs->f_bfree);
+  else if (type == 'l') xprintf("%ld", statfs->f_namelen);
+  else if (type == 't') xprintf("%lx", statfs->f_type);
+  else if (type == 'i')
+    xprintf("%08x%08x", statfs->f_fsid.__val[0], statfs->f_fsid.__val[1]);
+  else if (type == 's') xprintf("%d", statfs->f_frsize);
+  else if (type == 'S') xprintf("%d", statfs->f_bsize);
+  else xprintf("?");
+}
+
+void stat_main(void)
+{
+  int flagf = toys.optflags & FLAG_f;
+  char *format = flagf
+    ? "  File: \"%n\"\n    ID: %i Namelen: %l    Type: %t\n"
+      "Block Size: %s    Fundamental block size: %S\n"
+      "Blocks: Total: %b\tFree: %f\tAvailable: %a\n"
+      "Inodes: Total: %c\tFree: %d"
+    : "  File: %N\n  Size: %s\t Blocks: %b\t IO Blocks: %B\t%F\n"
+      "Device: %D\t Inode: %i\t Links: %h\n"
+      "Access: (%a/%A)\tUid: (%u/%U)\tGid: (%g/%G)\n"
+      "Access: %x\nModify: %y\nChange: %z";
+
+  if (toys.optflags & FLAG_c) format = TT.fmt;
+
+  for (; *toys.optargs; toys.optargs++) {
+    char *f;
+
+    if (flagf && !statfs(*toys.optargs, (void *)&TT.stat));
+    else if (!flagf && !lstat(*toys.optargs, (void *)&TT.stat)) {
+      struct stat *stat = (struct stat*)&TT.stat;
+
+      // check user and group name
+      TT.user_name = getpwuid(stat->st_uid);
+      TT.group_name = getgrgid(stat->st_gid);
+    } else {
+      perror_msg("'%s'", *toys.optargs);
+      continue;
+    }
+
+    for (f = format; *f; f++) {
+      if (*f != '%') putchar(*f);
+      else {
+        if (*++f == 'n') xprintf("%s", *toys.optargs);
+        else if (flagf) print_statfs(*f);
+        else print_stat(*f);
+      }
+    }
+    xputc('\n');
+  }
+}
diff --git a/toys/other/swapoff.c b/toys/other/swapoff.c
new file mode 100644 (file)
index 0000000..b89e915
--- /dev/null
@@ -0,0 +1,21 @@
+/* swapoff.c - Disable region for swapping
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config SWAPOFF
+  bool "swapoff"
+  default y
+  help
+    usage: swapoff swapregion
+
+    Disable swapping on a given swapregion.
+*/
+
+#include "toys.h"
+
+void swapoff_main(void)
+{
+  if (swapoff(toys.optargs[0])) perror_exit("failed to remove swaparea");
+}
diff --git a/toys/other/swapon.c b/toys/other/swapon.c
new file mode 100644 (file)
index 0000000..49f1249
--- /dev/null
@@ -0,0 +1,32 @@
+/* swapon.c - Enable region for swapping
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_SWAPON(NEWTOY(swapon, "<1>1p#<0>32767", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+
+config SWAPON
+  bool "swapon"
+  default y
+  help
+    usage: swapon [-p priority] filename
+
+    Enable swapping on a given device/file.
+*/
+
+#define FOR_swapon
+#include "toys.h"
+
+GLOBALS(
+  long priority;
+)
+
+void swapon_main(void)
+{
+  int flags = 0;
+
+  if (toys.optflags)
+    flags = SWAP_FLAG_PREFER | (TT.priority << SWAP_FLAG_PRIO_SHIFT);
+
+  if (swapon(*toys.optargs, flags))
+    perror_exit("Couldn't swapon '%s'", *toys.optargs);
+}
diff --git a/toys/other/switch_root.c b/toys/other/switch_root.c
new file mode 100644 (file)
index 0000000..1dfa20d
--- /dev/null
@@ -0,0 +1,93 @@
+/* switch_root.c - Switch from rootfs/initramfs to another filesystem
+ *
+ * Copyright 2005 Rob Landley <rob@landley.net>
+
+USE_SWITCH_ROOT(NEWTOY(switch_root, "<2c:h", TOYFLAG_SBIN))
+
+config SWITCH_ROOT
+  bool "switch_root"
+  default y
+  help
+    usage: switch_root [-c /dev/console] NEW_ROOT NEW_INIT...
+
+    Use from PID 1 under initramfs to free initramfs, chroot to NEW_ROOT,
+    and exec NEW_INIT.
+
+    -c Redirect console to device in NEW_ROOT
+    -h Hang instead of exiting on failure (avoids kernel panic)
+*/
+
+#define FOR_switch_root
+#include "toys.h"
+#include <sys/vfs.h>
+
+GLOBALS(
+  char *console;
+
+  dev_t rootdev;
+)
+
+static int del_node(struct dirtree *node)
+{
+  if (node->st.st_dev == TT.rootdev && dirtree_notdotdot(node)) {
+    int flag = 0;
+    if (S_ISDIR(node->st.st_mode)) {
+      if (node->data != -1) return DIRTREE_COMEAGAIN;
+      flag = AT_REMOVEDIR;
+    }
+    unlinkat(dirtree_parentfd(node), node->name, flag);
+  }
+
+  return 0;
+}
+
+void switch_root_main(void)
+{
+  char *newroot = *toys.optargs, **cmdline = toys.optargs+1;
+  struct stat st1, st2;
+  struct statfs stfs;
+  int console = console; // gcc's "may be used" warnings are broken.
+
+  if (getpid() != 1) error_exit("not pid 1");
+
+  // Root filesystem we're leaving must be ramfs or tmpfs
+  if (statfs("/", &stfs) ||
+    (stfs.f_type != 0x858458f6 && stfs.f_type != 0x01021994))
+  {
+    error_msg("not ramfs");
+    goto panic;
+  }
+
+  // New directory must be different filesystem instance
+  if (chdir(newroot) || stat(".", &st1) || stat("/", &st2) ||
+    st1.st_dev == st2.st_dev)
+  {
+    error_msg("bad newroot '%s'", newroot);
+    goto panic;
+  }
+  TT.rootdev=st2.st_dev;
+
+  // init program must exist and be an executable file
+  if (stat("init", &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
+    error_msg("bad init");
+    goto panic;
+  }
+
+  if (TT.console && -1 == (console = open(TT.console, O_RDWR))) {
+    perror_msg("bad console '%s'", TT.console);
+    goto panic;
+  }
+  // Ok, enough safety checks: wipe root partition.
+  dirtree_read("/", del_node);
+
+  if (TT.console) {
+    int i;
+    for (i=0; i<3; i++) if (console != i) dup2(console, i);
+    if (console>2) close(console);
+  }
+  execv(*cmdline, cmdline);
+  perror_msg("Failed to exec '%s'", *cmdline);
+panic:
+  if (toys.optflags & FLAG_h) for (;;) wait(NULL);
+}
diff --git a/toys/other/syslogd.c b/toys/other/syslogd.c
new file mode 100644 (file)
index 0000000..f49cbad
--- /dev/null
@@ -0,0 +1,902 @@
+/* syslogd.c - a system logging utility.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_SYSLOGD(NEWTOY(syslogd,">0l#<1>8=8R:b#<0>99=1B#<0>99=0s#<0=2000m#<0>71582787=20O:p:f:a:nSKLD", TOYFLAG_SBIN|TOYFLAG_STAYROOT))
+
+config SYSLOGD
+  bool "syslogd"
+  default y
+  help
+  Usage: syslogd  [-a socket][-p socket][-O logfile][-f config file][-m interval]
+                  [-n][-p socket][-S][-s SIZE][-b N][-B N][-R HOST][-L][-K][-l N][-D]
+
+  System logging utility
+
+  -a        Extra unix socket for listen
+  -O FILE   Default log file <DEFAULT: /var/log/messages>
+  -f FILE   Config file <DEFAULT: /etc/syslog.conf>
+  -p        Alternative unix domain socket <DEFAULT : /dev/log>
+  -n        Avoid auto-backgrounding.
+  -S        Smaller output
+  -m MARK   interval <DEFAULT: 20 minutes> (RANGE: 0 to 71582787)
+  -R HOST   Log to IP or hostname on PORT (default PORT=514/UDP)"
+  -L        Log locally and via network (default is network only if -R)"
+  -s SIZE   Max size (KB) before rotation (default:2000KB, 0=off)
+  -b N      rotated logs to keep (default:1, max=99, 0=purge)
+  -B N      Set number of logs to keep logs in memory buffer before write (default:0, max=99, 0=purge)
+  -K        Log to kernel printk buffer (use dmesg to read it)
+  -l N      Log only messages more urgent than prio(default:8 max:8 min:1)
+  -D        Drop duplicates
+*/
+
+#define FOR_syslogd
+#include "toys.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+
+GLOBALS(
+  char *socket;
+  char *config_file;
+  char *unix_socket;
+  char *logfile;
+  long interval;
+  long rot_size;
+  long buf_count;
+  long rot_count;
+  char *remote_log;
+  long log_prio;
+
+  struct arg_list *lsocks;  // list of listen sockets
+  struct arg_list *lfiles;  // list of write logfiles
+  fd_set rfds;        // fds for reading
+  int sd;            // socket for logging remote messeges.
+)
+
+#define flag_get(f,v,d)  ((toys.optflags & f) ? v : d)
+#define flag_chk(f)    ((toys.optflags & f) ? 1 : 0)
+
+#ifndef SYSLOG_NAMES
+#define  INTERNAL_NOPRI  0x10
+#define  INTERNAL_MARK  LOG_MAKEPRI(LOG_NFACILITIES, 0)
+
+typedef struct _code {
+  char *c_name;
+  int c_val;
+} CODE;
+
+static CODE prioritynames[] =
+{
+  { "alert", LOG_ALERT },
+  { "crit", LOG_CRIT },
+  { "debug", LOG_DEBUG },
+  { "emerg", LOG_EMERG },
+  { "err", LOG_ERR },
+  { "error", LOG_ERR },    /* DEPRECATED */
+  { "info", LOG_INFO },
+  { "none", INTERNAL_NOPRI },    /* INTERNAL */
+  { "notice", LOG_NOTICE },
+  { "panic", LOG_EMERG },    /* DEPRECATED */
+  { "warn", LOG_WARNING },    /* DEPRECATED */
+  { "warning", LOG_WARNING },
+  { NULL, -1 }
+};
+
+static CODE facilitynames[] =
+{
+  { "auth", LOG_AUTH },
+  { "authpriv", LOG_AUTHPRIV },
+  { "cron", LOG_CRON },
+  { "daemon", LOG_DAEMON },
+  { "ftp", LOG_FTP },
+  { "kern", LOG_KERN },
+  { "lpr", LOG_LPR },
+  { "mail", LOG_MAIL },
+  { "mark", INTERNAL_MARK },    /* INTERNAL */
+  { "news", LOG_NEWS },
+  { "security", LOG_AUTH },    /* DEPRECATED */
+  { "syslog", LOG_SYSLOG },
+  { "user", LOG_USER },
+  { "uucp", LOG_UUCP },
+  { "local0", LOG_LOCAL0 },
+  { "local1", LOG_LOCAL1 },
+  { "local2", LOG_LOCAL2 },
+  { "local3", LOG_LOCAL3 },
+  { "local4", LOG_LOCAL4 },
+  { "local5", LOG_LOCAL5 },
+  { "local6", LOG_LOCAL6 },
+  { "local7", LOG_LOCAL7 },
+  { NULL, -1 }
+};
+#endif
+
+/*
+ * -- CONFIG ---
+ * Misc. configuration.
+ * TODO: move to kconfig if possible.
+ */
+#define DEFLOGFILE    "/var/log/messages"
+#define DEFLOGSOCK    "/dev/log"
+#define DEFCONFFILE   "/etc/syslog.conf"
+#define DEFPORT       514
+
+/* Signal handling */
+struct fd_pair { int rd; int wr; };
+static struct fd_pair sigfd;
+
+/*
+ * UNIX Sockets for listening
+ */
+typedef struct unsocks_s {
+  char *path;
+  struct sockaddr_un sdu;
+  int sd;
+} unsocks_t;
+
+/*
+ * Log file entry to log into.
+ */
+typedef struct logfile_s {
+  char *filename;
+  char *config;
+  uint8_t isNetwork;
+  uint32_t facility[8];
+  uint8_t level[LOG_NFACILITIES];
+  int logfd;
+  struct sockaddr_in saddr;
+} logfile_t;
+
+/*
+ * Log buffer.
+ */
+typedef struct logbuffer_s {
+  int len;
+  char buf[1024];
+} logbuffer_t;
+
+/*
+ * Adds opened socks to rfds for select()
+ */
+static int addrfds(void)
+{
+  struct arg_list *node;
+  unsocks_t *sock;
+  int ret = 0;
+  FD_ZERO(&TT.rfds);
+  node = TT.lsocks;
+
+  while (node) {
+    sock = (unsocks_t*) node->arg;
+    if (sock->sd > 2) {
+      FD_SET(sock->sd, &TT.rfds);
+      ret = sock->sd;
+    }
+    node = node->next;
+  }
+  FD_SET(sigfd.rd, &TT.rfds);
+//  if (flag_chk(FLAG_r)) FD_SET(TT.sd, &TT.rfds);  TODO: fix for remote log
+  return (sigfd.rd > ret)?sigfd.rd:ret;
+}
+
+/*
+ * initializes unsock_t structure
+ * and opens socket for reading
+ * and adds to global lsock list.
+ */
+static int open_unix_socks(void)
+{
+  struct arg_list *node;
+  unsocks_t *sock;
+  int ret = 0;
+
+  for(node = TT.lsocks; node; node = node->next) {
+    sock = (unsocks_t*) node->arg;
+    sock->sdu.sun_family = AF_UNIX;
+    strcpy(sock->sdu.sun_path, sock->path);
+    sock->sd = socket(AF_UNIX, SOCK_DGRAM, 0);
+    if (sock->sd <= 0) {
+      perror_msg("OPEN SOCKS : failed");
+      continue;
+    }
+    unlink(sock->sdu.sun_path);
+    if (bind(sock->sd, (struct sockaddr *) &sock->sdu, sizeof(sock->sdu))) {
+      perror_msg("BIND SOCKS : failed sock : %s", sock->sdu.sun_path);
+      close(sock->sd);
+      continue;
+    }
+    chmod(sock->path, 0777);
+    ret++;
+  }
+  return ret;
+}
+
+/*
+ * creates a socket of family INET and protocol UDP
+ * if successful then returns SOCK othrwise error
+ */
+static int open_udp_socks(char *host, int port, struct sockaddr_in *sadd)
+{
+  int ret;
+  struct addrinfo *info, *rp;
+
+  rp = xzalloc(sizeof(struct addrinfo));
+  rp->ai_family = AF_INET;
+  rp->ai_socktype = SOCK_DGRAM;
+  rp->ai_protocol = IPPROTO_UDP;
+
+  ret = getaddrinfo(host, NULL, rp, &info);
+  if (ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", host);
+  free(rp);
+
+  ret = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (ret <= 0) perror_exit("Can't create socket. ");
+
+  for(rp = info; rp != NULL; rp = rp->ai_next){
+    ((struct sockaddr_in*)rp->ai_addr)->sin_port = htons(port);
+    memcpy(sadd, rp->ai_addr, rp->ai_addrlen);
+    break;
+  }
+  if (!rp) perror_exit("Connect failed ");
+  freeaddrinfo(info);
+  return ret;
+}
+
+/*
+ * Returns node having filename
+ */
+static struct arg_list *get_file_node(char *filename, struct arg_list *list)
+{
+  while (list) {
+    if (strcmp(((logfile_t*) list->arg)->filename, filename) == 0)
+      return list;
+    list = list->next;
+  }
+  return list;
+}
+
+/*
+ * recurses the logfile list and resolves config
+ * for evry file and updates facilty and log level bits.
+ */
+static int resolve_config(logfile_t *file)
+{
+  char *tk, *fac, *lvl, *tmp, *nfac;
+  int count = 0;
+  unsigned facval = 0;
+  uint8_t set, levval, neg;
+  CODE *val = NULL;
+
+  tmp = xstrdup(file->config);
+  for (tk = strtok(tmp, "; \0"); tk; tk = strtok(NULL, "; \0")) {
+    fac = tk;
+    tk = strchr(fac, '.');
+    if (tk == NULL) return -1;
+    *tk = '\0';
+    lvl = tk + 1;
+
+    while(1) {
+      count = 0;
+      if (*fac == '*') {
+        facval = 0xFFFFFFFF;
+        fac++;
+      }
+      nfac = strchr(fac, ',');
+      if (nfac) *nfac = '\0';
+      while (*fac && ((CODE*) &facilitynames[count])->c_name) {
+        val = (CODE*) &facilitynames[count];
+        if (strcmp(fac, val->c_name) == 0) {
+          facval |= (1<<LOG_FAC(val->c_val));
+          break;
+        }
+        count++;
+      }
+      if (((CODE*) &facilitynames[count])->c_val == -1)
+        return -1;
+
+      if (nfac) fac = nfac+1;
+      else break;
+    }
+
+    count = 0;
+    set = 0;
+    levval = 0;
+    neg = 0;
+    if (*lvl == '!') {
+      neg = 1;
+      lvl++;
+    }
+    if (*lvl == '=') {
+      set = 1;
+      lvl++;
+    }
+    if (*lvl == '*') {
+      levval = 0xFF;
+      lvl++;
+    }
+    while (*lvl && ((CODE*) &prioritynames[count])->c_name) {
+      val = (CODE*) &prioritynames[count];
+      if (strcmp(lvl, val->c_name) == 0) {
+        levval |= set ? LOG_MASK(val->c_val):LOG_UPTO(val->c_val);
+        if (neg) levval = ~levval;
+        break;
+      }
+      count++;
+    }
+    if (((CODE*) &prioritynames[count])->c_val == -1) return -1;
+
+    count = 0;
+    set = levval;
+    while(set) {
+      if (set & 0x1) file->facility[count] |= facval;
+      set>>=1;
+      count++;
+    }
+    for (count =0; count < LOG_NFACILITIES; count++) {
+      if (facval & 0x1) file->level[count] |= levval;
+      facval >>= 1;
+    }
+  }
+  free(tmp);
+
+  return 0;
+}
+
+/*
+ * Parse config file and update the log file list.
+ */
+static int parse_config_file(void)
+{
+  logfile_t *file;
+  FILE *fs = NULL;
+  char *confline=NULL, *tk=NULL, *tokens[2]={NULL, NULL};
+  int len, linelen, tcount, lineno = 0;
+  struct arg_list *node;
+  /*
+   * if -K then open only /dev/kmsg
+   * all other log files are neglected
+   * thus no need to open config either.
+   */
+  if (flag_chk(FLAG_K)) {
+    node = xzalloc(sizeof(struct arg_list));
+    file = xzalloc(sizeof(logfile_t));
+    file->filename = "/dev/kmsg";
+    file->config = "*.*";
+    memset(file->level, 0xFF, sizeof(file->level));
+    memset(file->facility, 0xFFFFFFFF, sizeof(file->facility));
+    node->arg = (char*) file;
+    TT.lfiles = node;
+    return 0;
+  }
+  /*
+   * if -R then add remote host to log list
+   * if -L is not provided all other log
+   * files are neglected thus no need to
+   * open config either so just return.
+   */
+   if (flag_chk(FLAG_R)) {
+     node = xzalloc(sizeof(struct arg_list));
+     file = xzalloc(sizeof(logfile_t));
+     file->filename = xmsprintf("@%s",TT.remote_log);
+     file->isNetwork = 1;
+     file->config = "*.*";
+     memset(file->level, 0xFF, sizeof(file->level));
+     memset(file->facility, 0xFFFFFFFF, sizeof(file->facility));
+     node->arg = (char*) file;
+     TT.lfiles = node;
+     if (!flag_chk(FLAG_L))return 0;
+   }
+  /*
+   * Read config file and add logfiles to the list
+   * with their configuration.
+   */
+  fs = fopen(flag_get(FLAG_f, TT.config_file, DEFCONFFILE), "r");
+  if (fs==NULL && flag_chk(FLAG_f))
+    perror_exit("can't open '%s'", TT.config_file);
+
+  for (len = 0, linelen = 0; fs;) {
+    len = getline(&confline, (size_t*) &linelen, fs);
+    if (len <= 0) break;
+    lineno++;
+    for (; *confline == ' '; confline++, len--) ;
+    if ((confline[0] == '#') || (confline[0] == '\n')) continue;
+    for (tcount = 0, tk = strtok(confline, " \t"); tk && (tcount < 2); tk =
+        strtok(NULL, " \t"), tcount++) {
+      if (tcount == 2) {
+        error_msg("error in '%s' at line %d", flag_get(FLAG_f, TT.config_file, DEFCONFFILE), lineno);
+        return -1;
+      }
+      tokens[tcount] = xstrdup(tk);
+    }
+    if (tcount <= 1 || tcount > 2) {
+      if (tokens[0]) free(tokens[0]);
+      error_msg("bad line %d: 1 tokens found, 2 needed", lineno);
+      return -1;
+    }
+    tk = (tokens[1] + (strlen(tokens[1]) - 1));
+    if (*tk == '\n') *tk = '\0';
+    if (*tokens[1] == '\0') {
+      error_msg("bad line %d: 1 tokens found, 2 needed", lineno);
+      return -1;
+    }
+    if (*tokens[1] == '*') goto loop_again;
+
+    node = get_file_node(tokens[1], TT.lfiles);
+    if (node == NULL) {
+      node = xzalloc(sizeof(struct arg_list));
+      file = xzalloc(sizeof(logfile_t));
+      file->config = xstrdup(tokens[0]);
+      if (resolve_config(file)==-1) {
+        error_msg("error in '%s' at line %d", flag_get(FLAG_f, TT.config_file, DEFCONFFILE), lineno);
+        return -1;
+      }
+      file->filename = xstrdup(tokens[1]);
+      if (*file->filename == '@') file->isNetwork = 1;
+      node->arg = (char*) file;
+      node->next = TT.lfiles;
+      TT.lfiles = node;
+    } else {
+      file = (logfile_t*) node->arg;
+      int rel = strlen(file->config) + strlen(tokens[0]) + 2;
+      file->config = xrealloc(file->config, rel);
+      sprintf(file->config, "%s;%s", file->config, tokens[0]);
+    }
+loop_again:
+    if (tokens[0]) free(tokens[0]);
+    if (tokens[1]) free(tokens[1]);
+    free(confline);
+    confline = NULL;
+  }
+  /*
+   * Can't open config file or support is not enabled
+   * adding default logfile to the head of list.
+   */
+  if (fs==NULL){
+    node = xzalloc(sizeof(struct arg_list));
+    file = xzalloc(sizeof(logfile_t));
+    file->filename = flag_get(FLAG_O, TT.logfile, DEFLOGFILE);
+    file->isNetwork = 0;
+    file->config = "*.*";
+    memset(file->level, 0xFF, sizeof(file->level));
+    memset(file->facility, 0xFFFFFFFF, sizeof(file->facility));
+    node->arg = (char*) file;
+    node->next = TT.lfiles;
+    TT.lfiles = node;
+  }
+  if (fs) {
+    fclose(fs);
+    fs = NULL;
+  }
+  return 0;
+}
+
+/*
+ * String STR to UINT32 conversion strored in VAR
+ */
+static long strtou32(const char *str)
+{
+  char *endptr = NULL;
+  int base = 10;
+  errno=0;
+  if (str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
+    base = 16;
+    str+=2;
+  }
+  long ret_val = strtol(str, &endptr, base);
+  if (errno) return -1;
+  else if (endptr && (*endptr!='\0'||endptr == str)) return -1;
+  return ret_val;
+}
+
+/*
+ * open every log file in list.
+ */
+static int open_logfiles(void)
+{
+  struct arg_list *node;
+  logfile_t *tfd;
+  int port = -1;
+  char *p, *tmpfile;
+  node = TT.lfiles;
+
+  while (node) {
+    tfd = (logfile_t*) node->arg;
+    if (tfd->isNetwork) {
+      tmpfile = xstrdup(tfd->filename +1);
+      if ((p = strchr(tmpfile, ':'))!=NULL) {
+        *p = '\0';
+        port = strtou32(p + 1);
+        if (port<0 || port>65535) error_exit("wrong port no in %s", tfd->filename);
+      }
+      tfd->logfd = open_udp_socks(tmpfile, (port>=0)?port:DEFPORT, &tfd->saddr);
+      free(tmpfile);
+    } else tfd->logfd = open(tfd->filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
+    if (tfd->logfd <= 0) {
+      tfd->filename = "/dev/console";
+      tfd->logfd = open(tfd->filename, O_APPEND);
+    }
+    node = node->next;
+  }
+  return 0;
+}
+
+/*
+ * write to file with rotation
+ */
+static int write_rotate( logfile_t *tf, int len)
+{
+  static int buf_idx = 0;
+  static logbuffer_t buffer[100];
+  int size, isreg, idx;
+  struct stat statf;
+  isreg = (fstat(tf->logfd, &statf) == 0 && S_ISREG(statf.st_mode));
+  size = statf.st_size;
+
+  if (flag_chk(FLAG_s)||flag_chk(FLAG_b)) {
+    if (TT.rot_size && isreg && (size+len)>(TT.rot_size*1024)) {
+      if (TT.rot_count) { /* always 0..99 */
+        int i = strlen(tf->filename) + 3 + 1;
+        char old_file[i];
+        char new_file[i];
+        i = TT.rot_count - 1;
+        while (1) {
+          sprintf(new_file, "%s.%d", tf->filename, i);
+          if (i == 0) break;
+          sprintf(old_file, "%s.%d", tf->filename, --i);
+          rename(old_file, new_file);
+        }
+        rename(tf->filename, new_file);
+        unlink(tf->filename);
+        close(tf->logfd);
+        tf->logfd = open(tf->filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
+        if (tf->logfd < 0) {
+          perror_msg("can't open %s", tf->filename);
+          return -1;
+        }
+      }
+      ftruncate(tf->logfd, 0);
+    }
+  }
+  if (TT.buf_count && flag_chk(FLAG_B)) {
+    if (buf_idx < TT.buf_count) {
+        memcpy(buffer[buf_idx].buf, toybuf, len);
+        buffer[buf_idx].buf[len + 1] = '\0';
+        buffer[buf_idx].len = len;
+        buf_idx++;
+        return len;
+    } else {
+      for (idx = 0; idx < TT.buf_count; idx++) {
+         write(tf->logfd, buffer[idx].buf, buffer[idx].len);
+      }
+      buf_idx = 0;
+    }
+  }
+  return write(tf->logfd, toybuf, len);
+}
+
+/*
+ * search the given name and return its value
+ */
+static char* dec(int val, CODE *clist)
+{
+  const CODE *c;
+
+  for (c = clist; c->c_name; c++) { //find the given parameter in list and return the value.
+    if (val == c->c_val) return c->c_name;
+  }
+  return itoa(val);
+}
+/*
+ * Compute priority from "facility.level" pair
+ */
+static void priority_to_string(int pri, char **facstr, char **lvlstr)
+{
+  int fac,lev;
+
+  fac = LOG_FAC(pri);
+  lev = LOG_PRI(pri);
+  *facstr = dec(fac<<3, facilitynames);
+  *lvlstr = dec(lev, prioritynames);
+  return;
+}
+
+/*
+ * Parse messege and write to file.
+ */
+static int logmsg(char *msg, int len)
+{
+  time_t now;
+  char *ts, *lvlstr, *facstr;
+  char *p;
+  int pri = 0;
+  struct utsname *uts;
+  struct arg_list *lnode = TT.lfiles;
+
+  char *omsg = msg;
+  int olen = len, fac, lvl;
+  /*
+   * Extract the priority no.
+   */
+  if (*msg == '<') {
+    pri = (int) strtoul(msg + 1, &p, 10);
+    if (*p == '>') msg = p + 1;
+  }
+  /* Jan 18 00:11:22 msg...
+   * 01234567890123456
+   */
+  if (len < 16 || msg[3] != ' ' || msg[6] != ' ' || msg[9] != ':'
+      || msg[12] != ':' || msg[15] != ' ') {
+    time(&now);
+    ts = ctime(&now) + 4; /* skip day of week */
+  } else {
+    now = 0;
+    ts = msg;
+    msg += 16;
+  }
+  ts[15] = '\0';
+  fac = LOG_FAC(pri);
+  lvl = LOG_PRI(pri);
+
+  if (flag_chk(FLAG_K)) {
+    len = sprintf(toybuf, "<%d> %s\n", pri, msg);
+    goto do_log;
+  }
+  priority_to_string(pri, &facstr, &lvlstr);
+
+  p = "local";
+  uts = xzalloc(sizeof(struct utsname));
+  if (!uname(uts)) p = uts->nodename;
+  if (flag_chk(FLAG_S)) len = sprintf(toybuf, "%s %s\n", ts, msg);
+  else len = sprintf(toybuf, "%s %s %s.%s %s\n", ts, p, facstr, lvlstr, msg);
+  free(uts);
+
+do_log:
+  if (lvl >= TT.log_prio) return 0;
+
+  while (lnode) {
+    logfile_t *tf;
+    tf = (logfile_t*) lnode->arg;
+    if (tf->logfd > 0) {
+      if ((tf->facility[lvl] & (1 << fac))&&(tf->level[fac] & (1<<lvl))) {
+        int wlen;
+        if (tf->isNetwork)
+          wlen = sendto(tf->logfd, omsg, olen, 0, (struct sockaddr*)&tf->saddr, sizeof(tf->saddr));
+        else wlen = write_rotate(tf, len);
+        if (wlen < 0) perror_msg("write failed file : %s ", (tf->isNetwork)?(tf->filename+1):tf->filename);
+      }
+    }
+    lnode = lnode->next;
+  }
+  return 0;
+}
+
+/*
+ * closes all read and write fds
+ * and frees all nodes and lists
+ */
+static void cleanup(void)
+{
+  struct arg_list *fnode;
+  logmsg("<46>syslogd exiting", 19);
+  while (TT.lsocks) {
+    fnode = TT.lsocks;
+    if (((unsocks_t*) fnode->arg)->sd >= 0)
+      close(((unsocks_t*) fnode->arg)->sd);
+    free(fnode->arg);
+    TT.lsocks = fnode->next;
+    free(fnode);
+  }
+  unlink("/dev/log");
+
+  while (TT.lfiles) {
+    fnode = TT.lfiles;
+    if (((logfile_t*) fnode->arg)->logfd >= 0)
+      close(((logfile_t*) fnode->arg)->logfd);
+    free(fnode->arg);
+    TT.lfiles = fnode->next;
+    free(fnode);
+  }
+  return;
+}
+
+#ifdef REMOTE_LOG_READ
+/*
+ * TODO: used in remote logging
+ * open UDP port 514 for remote logging
+ */
+static void remote_log(void)
+{
+  struct sockaddr_in me;
+  const int set = 1;
+
+  TT.sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (TT.sd < 0) perror_exit("Can't open sock for remote logging ");
+  setsockopt(TT.sd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
+
+  memset(&me, 0, sizeof(me));
+  me.sin_family = AF_INET;
+  me.sin_port = htons(DEFPORT);
+  me.sin_addr.s_addr = htonl(INADDR_ANY);
+
+  if (bind(TT.sd, (struct sockaddr *)&me, sizeof(me)) < 0)
+    perror_exit("bind failed on remote log sock");
+}
+#endif
+
+/********* SIGNAL HANDLING ****************/
+static void signal_handler(int sig)
+{
+  unsigned char ch = sig;
+  if (write(sigfd.wr, &ch, 1) != 1) error_msg("can't send signal");
+}
+
+static int setup_signal()
+{
+  if (pipe((int *)&sigfd) < 0){
+    error_msg("pipe failed\n");
+    return -1;
+  }
+
+  fcntl(sigfd.wr , F_SETFD, FD_CLOEXEC);
+  fcntl(sigfd.rd , F_SETFD, FD_CLOEXEC);
+  int flags = fcntl(sigfd.wr, F_GETFL);
+  fcntl(sigfd.wr, F_SETFL, flags | O_NONBLOCK);
+  signal(SIGHUP, signal_handler);
+  signal(SIGTERM, signal_handler);
+  signal(SIGINT, signal_handler);
+  signal(SIGQUIT, signal_handler);
+
+  return 0;
+}
+
+static int syslog_daemon(void)
+{
+  int fd;
+
+  fd = open("/dev/null", O_RDWR);
+  if (fd < 0) fd = open("/", O_RDONLY, 0666);
+  pid_t pid = fork();
+
+  if (pid < 0) {
+    perror_msg("DAEMON: failed to fork");
+    return -1;
+  }
+  if (pid) exit(EXIT_SUCCESS);
+
+  setsid();
+  dup2(fd, 0);
+  dup2(fd, 1);
+  dup2(fd, 2);
+  close(fd);
+
+  //don't daemonize again if SIGHUP received.
+  toys.optflags |= FLAG_n;
+
+  return 0;
+}
+/********************************************/
+
+/*
+ * Main syslogd routine.
+ */
+void syslogd_main(void)
+{
+  char *temp;
+  unsocks_t *tsd;
+  int maxfd, retval, last_len=0;
+  struct timeval tv;
+  struct arg_list *node;
+  char buffer[1024], last_buf[1024];
+
+  if (flag_chk(FLAG_p) && strlen(TT.unix_socket) > 108) {
+    error_msg("Socket path should not be more than %d", 108);
+    return;
+  }
+
+init_jumpin:
+  TT.lsocks = xzalloc(sizeof(struct arg_list));
+  tsd = xzalloc(sizeof(unsocks_t));
+
+  tsd->path = flag_get(FLAG_p, TT.unix_socket , DEFLOGSOCK);
+  TT.lsocks->arg = (char*) tsd;
+
+  if (flag_chk(FLAG_a)) {
+    for (temp = strtok(TT.socket, ":"); temp; temp = strtok(NULL, ":")) {
+      struct arg_list *ltemp = xzalloc(sizeof(struct arg_list));
+      if (strlen(temp) > 107) temp[108] = '\0';
+      tsd = xzalloc(sizeof(unsocks_t));
+      tsd->path = temp;
+      ltemp->arg = (char*) tsd;
+      ltemp->next = TT.lsocks;
+      TT.lsocks = ltemp;
+    }
+  }
+//  if (flag_chk(FLAG_r)) remote_log();  TODO: fix for remote log
+  if (open_unix_socks() == 0) {
+    error_msg("Can't open single socket for listenning.");
+    goto clean_and_exit;
+  }
+  setup_signal();
+  if (parse_config_file() == -1) goto clean_and_exit;
+  open_logfiles();
+  if (!flag_chk(FLAG_n)) syslog_daemon();
+  {
+    int pidfile = open("/var/run/syslogd.pid", O_CREAT | O_WRONLY | O_TRUNC, 0666);
+    if (pidfile > 0) {
+      unsigned pid = getpid();
+      char pidbuf[32];
+      int len = sprintf(pidbuf, "%u\n", pid);
+      write(pidfile, pidbuf, len);
+      close(pidfile);
+    }
+  }
+  logmsg("<46>syslogd started: ToyBox v1.26.0", 35);
+
+  for (;;) {
+    maxfd = addrfds();
+    tv.tv_usec = 0;
+    tv.tv_sec = TT.interval*60;
+
+    retval = select(maxfd + 1, &TT.rfds, NULL, NULL, (TT.interval)?&tv:NULL);
+    if (retval < 0) { /* Some error. */
+      if (errno == EINTR) continue;
+      perror_msg("Error in select ");
+      continue;
+    }
+    if (retval == 0) { /* Timed out */
+      logmsg("<46>-- MARK --", 14);
+      continue;
+    }
+    if (FD_ISSET(sigfd.rd, &TT.rfds)) { /* May be a signal */
+      unsigned char sig;
+
+      if (read(sigfd.rd, &sig, 1) != 1) {
+        error_msg("signal read failed.\n");
+        continue;
+      }
+      switch(sig) {
+      case SIGTERM:    /* FALLTHROUGH */
+      case SIGINT:     /* FALLTHROUGH */
+      case SIGQUIT:
+         cleanup();
+         signal(sig, SIG_DFL);
+         sigset_t ss;
+         sigemptyset(&ss);
+         sigaddset(&ss, sig);
+         sigprocmask(SIG_UNBLOCK, &ss, NULL);
+         raise(sig);
+         _exit(1);  /* Should not reach it */
+         break;
+      case SIGHUP:
+        cleanup();
+        goto init_jumpin;
+      default: break;
+      }
+    }
+    if (retval > 0) { /* Some activity on listen sockets. */
+      node = TT.lsocks;
+      while (node) {
+        int sd = ((unsocks_t*) node->arg)->sd;
+        if (FD_ISSET(sd, &TT.rfds)) {
+          int len = read(sd, buffer, 1023);
+          if (len > 0) {
+            buffer[len] = '\0';
+            if(flag_chk(FLAG_D) && (len == last_len))
+              if (memcmp(last_buf, buffer, len) == 0)
+                break;
+
+            memcpy(last_buf, buffer, len);
+            last_len = len;
+            logmsg(buffer, len);
+          }
+          break;
+        }
+        node = node->next;
+      }
+    }
+  }
+clean_and_exit:
+  cleanup();
+  return;
+}
diff --git a/toys/other/tac.c b/toys/other/tac.c
new file mode 100644 (file)
index 0000000..538d1b0
--- /dev/null
@@ -0,0 +1,49 @@
+/* tac.c - output lines in reverse order
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+
+USE_TAC(NEWTOY(tac, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config TAC
+  bool "tac"
+  default y
+  help
+    usage: tac [FILE...]
+
+    Output lines in reverse order.
+*/
+
+#include "toys.h"
+
+void do_tac(int fd, char *name)
+{
+  struct arg_list *list = NULL;
+  char *c;
+
+  // Read in lines
+  for (;;) {
+    struct arg_list *temp;
+    long len;
+
+    if (!(c = get_rawline(fd, &len, '\n'))) break;
+
+    temp = xmalloc(sizeof(struct arg_list));
+    temp->next = list;
+    temp->arg = c;
+    list = temp;
+  }
+
+  // Play them back.
+  while (list) {
+    struct arg_list *temp = list->next;
+    xprintf("%s", list->arg);
+    free(list->arg);
+    free(list);
+    list = temp;
+  }
+}
+
+void tac_main(void)
+{
+  loopfiles(toys.optargs, do_tac);
+}
diff --git a/toys/other/taskset.c b/toys/other/taskset.c
new file mode 100644 (file)
index 0000000..2b067d4
--- /dev/null
@@ -0,0 +1,106 @@
+/* taskset.c - Retrieve or set the CPU affinity of a process.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_TASKSET(NEWTOY(taskset, "<1^pa", TOYFLAG_BIN|TOYFLAG_STAYROOT))
+
+config TASKSET
+  bool "taskset"
+  default y
+  help
+    usage: taskset [-ap] [mask] [PID | cmd [args...]]
+
+    Launch a new task which may only run on certain processors, or change
+    the processor affinity of an exisitng PID.
+
+    Mask is a hex string where each bit represents a processor the process
+    is allowed to run on. PID without a mask displays existing affinity.
+
+    -p Set/get the affinity of given PID instead of a new command.
+    -a Set/get the affinity of all threads of the PID.
+*/
+
+#define FOR_taskset
+#include "toys.h"
+
+#include <sys/syscall.h>
+#define sched_setaffinity(pid, size, cpuset) \
+  syscall(__NR_sched_setaffinity, (pid_t)pid, (size_t)size, (void *)cpuset)
+#define sched_getaffinity(pid, size, cpuset) \
+  syscall(__NR_sched_getaffinity, (pid_t)pid, (size_t)size, (void *)cpuset)
+
+// mask is an array of long, which makes the layout a bit weird on big
+// endian systems but as long as it's consistent...
+
+static void do_taskset(pid_t pid, int quiet)
+{
+  unsigned long *mask = (unsigned long *)toybuf;
+  char *s = *toys.optargs, *failed = "failed to %s %d's affinity";
+  int i, j, k;
+
+  for (i=0; ; i++) {
+    if (!quiet) {
+      int j = sizeof(toybuf), flag = 0;
+
+      if (-1 == sched_getaffinity(pid, sizeof(toybuf), (void *)mask))
+        perror_exit(failed, "get", pid);
+
+      printf("pid %d's %s affinity mask: ", pid, i ? "new" : "current");
+
+      while (j--) {
+        int x = 255 & (mask[j/sizeof(long)] >> (8*(j&(sizeof(long)-1))));
+
+        if (flag) printf("%02x", x);
+        else if (x) {
+          flag++;
+          printf("%x", x);
+        }
+      }
+      putchar('\n');
+    }
+
+    if (i || toys.optc < 2) return;
+
+    memset(toybuf, 0, sizeof(toybuf));
+    k = strlen(s = *toys.optargs);
+    s += k;
+    for (j = 0; j<k; j++) {
+      unsigned long digit = *(--s) - '0';
+
+      if (digit > 9) digit = 10 + tolower(*s)-'a';
+      if (digit > 15) error_exit("bad mask '%s'", *toys.optargs);
+      mask[j/(2*sizeof(long))] |= digit << 4*(j&((2*sizeof(long))-1));
+    }
+
+    if (-1 == sched_setaffinity(pid, sizeof(toybuf), (void *)mask))
+      perror_exit(failed, "set", pid);
+  }
+}
+
+static int task_callback(struct dirtree *new)
+{
+  if (!new->parent) return DIRTREE_RECURSE;
+  if (isdigit(*new->name)) do_taskset(atoi(new->name), 0);
+
+  return 0;
+}
+
+void taskset_main(void)
+{
+  if (!(toys.optflags & FLAG_p)) {
+    if (toys.optc < 2) error_exit("Needs 2 args");
+    do_taskset(getpid(), 1);
+    xexec(toys.optargs+1);
+  } else {
+    char *c;
+    pid_t pid = strtol(toys.optargs[toys.optc-1], &c, 10);
+
+    if (*c) error_exit("Not int %s", toys.optargs[1]);
+
+    if (toys.optflags & FLAG_a) {
+      char buf[33];
+      sprintf(buf, "/proc/%ld/task/", (long)pid);
+      dirtree_read(buf, task_callback);
+    } else do_taskset(pid, 0);
+  }
+}
diff --git a/toys/other/telnet.c b/toys/other/telnet.c
new file mode 100644 (file)
index 0000000..66fb14b
--- /dev/null
@@ -0,0 +1,397 @@
+/* telnet.c - Telnet client.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN))
+
+config TELNET
+  bool "telnet"
+  default y
+  help
+    usage: telnet HOST [PORT]
+    Connect to telnet server
+*/
+#define FOR_telnet
+#include "toys.h"
+#include <arpa/telnet.h>
+#include <netinet/in.h>
+#include  <sys/poll.h>
+
+GLOBALS(
+  char *hostname;
+  int port;
+  int sfd;
+  char buff[128];
+  int pbuff;
+  char iac[256];
+  int piac;
+  char *ttype;
+  struct termios def_term;
+  struct termios raw_term;
+  uint8_t term_ok;
+  uint8_t term_mode;
+  uint8_t flags;
+  int win_width;
+  int win_height;
+  unsigned signalno;
+)
+
+#define TELNET_PORT    23
+#define DATABUFSIZE    128
+#define IACBUFSIZE    256
+#define  CM_TRY      0
+#define CM_ON      1
+#define CM_OFF      2
+#define UF_ECHO      0x01
+#define UF_SGA      0x02
+
+/*
+ * creates a socket of family INET and protocol TCP and connects
+ * it to HOST at PORT.
+ * if successful then returns SOCK othrwise error
+ */
+static int xconnect_inet_tcp(char *host, int port)
+{
+  int ret;
+  struct addrinfo *info, *rp;
+
+  rp = xzalloc(sizeof(struct addrinfo));
+  rp->ai_family = AF_INET;
+  rp->ai_socktype = SOCK_STREAM;
+  rp->ai_protocol = IPPROTO_TCP;
+
+  ret = getaddrinfo(host, NULL, rp, &info);
+  if(ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", host);
+  free(rp);
+
+  ret = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if(ret <= 0) perror_exit("Can't create socket. ");
+
+  for(rp = info; rp != NULL; rp = rp->ai_next){
+    ((struct sockaddr_in*)rp->ai_addr)->sin_port = htons(port);
+    if(connect(ret, rp->ai_addr, rp->ai_addrlen) != -1) break;
+  }
+  if(!rp) perror_exit("Connect failed ");
+  freeaddrinfo(info);
+  return ret;
+}
+
+/*
+ * sets terminal mode: LINE or CHARACTER based om internal stat.
+ */
+static char const es[] = "\r\nEscape character is ";
+static void set_mode(void)
+{
+  if (TT.flags & UF_ECHO) {
+    if (TT.term_mode == CM_TRY) {
+      TT.term_mode = CM_ON;
+      printf("\r\nEntering character mode%s'^]'.\r\n", es);
+      if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
+    }
+  } else {
+    if (TT.term_mode != CM_OFF) {
+      TT.term_mode = CM_OFF;
+      printf("\r\nEntering line mode%s'^C'.\r\n", es);
+      if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
+    }
+  }
+}
+
+/*
+ * flushes all data in IAC buff to server.
+ */
+static void flush_iac(void)
+{
+  int wlen = write(TT.sfd, TT.iac, TT.piac);
+  if(wlen <= 0) error_msg("IAC : send failed.");
+  TT.piac = 0;
+}
+
+/*
+ * puts DATA in iac buff of length LEN and updates iac buff pointer.
+ */
+static void put_iac(int len, ...)
+{
+  if(TT.piac + len >= IACBUFSIZE) flush_iac();
+  va_list va; va_start(va, len);
+  for(;len > 0; TT.iac[TT.piac++] = (uint8_t)va_arg(va, int), len--);
+  va_end(va);
+}
+
+/*
+ * puts string STR in iac buff and updates iac buff pointer.
+ */
+static void str_iac(char *str)
+{
+  int len = strlen(str);
+  if(TT.piac + len + 1 >= IACBUFSIZE) flush_iac();
+  strcpy(&TT.iac[TT.piac], str);
+  TT.piac += len+1;
+}
+
+/*
+ * Handles escape sequence.
+ */
+static void handle_esc(void)
+{
+  char input;
+  if(TT.signalno && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
+  write(1,"\r\nConsole escape. Commands are:\r\n\n"
+      " l  go to line mode\r\n"
+      " c  go to character mode\r\n"
+      " z  suspend telnet\r\n"
+      " e  exit telnet\r\n", 114);
+
+  if (read(STDIN_FILENO, &input, 1) <= 0){
+    if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
+    exit(0);
+  }
+
+  switch (input) {
+  case 'l':
+    if (!TT.signalno) {
+      TT.term_mode = CM_TRY;
+      TT.flags &= ~(UF_ECHO | UF_SGA);
+      set_mode();
+      put_iac(6, IAC,DONT,TELOPT_ECHO,IAC,DONT, TELOPT_SGA);
+      flush_iac();
+      goto ret;
+    }
+    break;
+  case 'c':
+    if (TT.signalno) {
+      TT.term_mode = CM_TRY;
+      TT.flags |= (UF_ECHO | UF_SGA);
+      set_mode();
+      put_iac(6, IAC,DO,TELOPT_ECHO,IAC,DO,TELOPT_SGA);
+      flush_iac();
+      goto ret;
+    }
+    break;
+  case 'z':
+    if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
+    kill(0, SIGTSTP);
+    if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
+    break;
+  case 'e':
+    if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
+    exit(0);
+  default: break;
+  }
+
+  write(1, "continuing...\r\n", 15);
+  if (TT.signalno && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
+
+ret:
+  TT.signalno = 0;
+}
+
+/*
+ * calculates current window size and updates in
+ * global variables win_height win_width
+ * first tries ioctl if fails then tries env variable and
+ * if still fails provides default values.
+ */
+static void get_win_size(void)
+{
+  struct winsize win;
+  char *temp;
+
+  win.ws_row = 0;
+  win.ws_col = 0;
+  ioctl(0, TIOCGWINSZ, &win);
+  TT.win_height = (!win.ws_row)? ((temp = getenv("ROWS"))!=NULL)? atoi(temp):24:win.ws_row;
+  TT.win_width = (!win.ws_col)?((temp = getenv("COLUMN"))!=NULL)? atoi(temp):80:win.ws_col;
+}
+
+/*
+ * handles telnet SUB NEGOTIATIONS
+ * only terminal type is supported.
+ */
+static void handle_negotiations(void)
+{
+  char opt = TT.buff[TT.pbuff++];
+  switch(opt){
+  case TELOPT_TTYPE:
+    opt =  TT.buff[TT.pbuff++];
+    if(opt == TELQUAL_SEND){
+      put_iac(4, IAC,SB,TELOPT_TTYPE,TELQUAL_IS);
+      str_iac(TT.ttype);
+      put_iac(2, IAC,SE);
+    }
+    break;
+  default: break;
+  }
+}
+
+/*
+ * handles server's DO DONT WILL WONT requests.
+ * supports ECHO, SGA, TTYPE, NAWS
+ */
+static void handle_ddww(char ddww)
+{
+  char opt = TT.buff[TT.pbuff++];
+  switch (opt) {
+  case TELOPT_ECHO: /* ECHO */
+    if (ddww == DO) put_iac(3, IAC,WONT,TELOPT_ECHO);
+    if(ddww == DONT) break;
+    if (TT.flags & UF_ECHO) {
+        if (ddww == WILL) return;
+      } else if (ddww == WONT) return;
+    if (TT.term_mode != CM_OFF) TT.flags ^= UF_ECHO;
+    (TT.flags & UF_ECHO)?put_iac(3, IAC,DO,TELOPT_ECHO):put_iac(3, IAC,DONT,TELOPT_ECHO);
+    set_mode();
+    printf("\r\n");
+    break;
+
+  case TELOPT_SGA: /* Supress GO Ahead */
+    if (TT.flags & UF_SGA){ if (ddww == WILL) return;
+    } else if (ddww == WONT) return;
+
+    TT.flags ^= UF_SGA;
+    (TT.flags & UF_SGA)?put_iac(3, IAC,DO,TELOPT_SGA): put_iac(3, IAC,DONT,TELOPT_SGA);
+    break;
+
+  case TELOPT_TTYPE: /* Terminal Type */
+    (TT.ttype)?put_iac(3, IAC,WILL,TELOPT_TTYPE):put_iac(3, IAC,WONT,TELOPT_TTYPE);
+    break;
+
+  case TELOPT_NAWS: /* Window Size */
+    put_iac(3, IAC,WILL,TELOPT_NAWS);
+    put_iac(9, IAC,SB,TELOPT_NAWS,(TT.win_width >> 8) & 0xff,TT.win_width & 0xff,(TT.win_height >> 8) & 0xff,TT.win_height & 0xff,IAC,SE);
+    break;
+
+  default: /* Default behaviour is to say NO */
+    if(ddww == WILL) put_iac(3, IAC,DONT,opt);
+    if(ddww == DO) put_iac(3, IAC,WONT,opt);
+    break;
+  }
+}
+
+/*
+ * parses data which is read from server of length LEN.
+ * and passes it to console.
+ */
+static int read_server(int len)
+{
+  int i = 0;
+  char curr;
+  TT.pbuff = 0;
+
+  do {
+    curr = TT.buff[TT.pbuff++];
+    if (curr == IAC) {
+      curr = TT.buff[TT.pbuff++];
+      switch (curr) {
+      case DO:    /* FALLTHROUGH */
+      case DONT:    /* FALLTHROUGH */
+      case WILL:    /* FALLTHROUGH */
+      case WONT:
+        handle_ddww(curr);
+        break;
+      case SB:
+        handle_negotiations();
+        break;
+      case SE:
+        break;
+      default: break;
+      }
+    } else {
+      toybuf[i++] = curr;
+      if (curr == '\r') { curr = TT.buff[TT.pbuff++];
+        if (curr != '\0') TT.pbuff--;
+      }
+    }
+  } while (TT.pbuff < len);
+
+  if (i) write(STDIN_FILENO, toybuf, i);
+  return 0;
+}
+
+/*
+ * parses data which is read from console og length LEN
+ * and passes it to server.
+ */
+static void write_server(int len)
+{
+  char *c = (char*)TT.buff;
+  int i = 0;
+
+  for (; len > 0; len--, c++) {
+    if (*c == 0x1d) {
+      handle_esc();
+      return;
+    }
+    toybuf[i++] = *c;
+    if (*c == IAC) toybuf[i++] = *c; /* IAC -> IAC IAC */
+    else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */
+  }
+  if(i) write(TT.sfd, toybuf, i);
+}
+
+/*
+ * SIGINT signal handling.
+ * only sets signalno which get handle in main loop.
+ */
+static void handle_sigint(int signo)
+{
+  TT.signalno = signo;
+}
+
+void telnet_main(void)
+{
+  int set = 1, len;
+  struct pollfd pfds[2];
+
+  TT.hostname = toys.optargs[0];
+  TT.port = TELNET_PORT;
+
+  if(toys.optc == 2) TT.port = atoi(toys.optargs[1]);
+  if(TT.port <= 0 || TT.port > 65535) error_exit("PORT can only contain non zero positive numerical value upto 65535.");
+
+  TT.ttype = getenv("TERM");
+  if(!TT.ttype) TT.ttype = "";
+  if(strlen(TT.ttype) > IACBUFSIZE-1) TT.ttype[IACBUFSIZE - 1] = '\0';
+
+  if (tcgetattr(0, &TT.def_term) >= 0) {
+    TT.term_ok = 1;
+    TT.raw_term = TT.def_term;
+    cfmakeraw(&TT.raw_term);
+  }
+  get_win_size();
+
+  TT.sfd = xconnect_inet_tcp(TT.hostname, TT.port);
+  setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
+  setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set));
+
+  pfds[0].fd = STDIN_FILENO;
+  pfds[0].events = POLLIN;
+  pfds[1].fd = TT.sfd;
+  pfds[1].events = POLLIN;
+
+  TT.piac = TT.pbuff = 0;
+  TT.signalno = 0;
+  signal(SIGINT, handle_sigint);
+  while(1){
+    if(TT.piac) flush_iac();
+    if(poll(pfds, 2, -1) < 0){
+      (TT.signalno)?handle_esc():sleep(1);
+      continue;
+    }
+    if(pfds[0].revents){
+      len = read(STDIN_FILENO, TT.buff, DATABUFSIZE);
+      if(len > 0) write_server(len);
+    }
+    if(pfds[1].revents){
+      len = read(TT.sfd, TT.buff, DATABUFSIZE);
+      if(len > 0) read_server(len);
+      else{
+        printf("Connection closed by foreign host\r\n");
+        if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
+        exit(0);
+      }
+    }
+  }
+}
diff --git a/toys/other/tftp.c b/toys/other/tftp.c
new file mode 100644 (file)
index 0000000..1b6292f
--- /dev/null
@@ -0,0 +1,469 @@
+/* tftp.c - TFTP client.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:gp", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TFTP
+  bool "tftp"
+  default y
+  help
+    usage: tftp [OPTIONS] HOST [PORT]
+
+    Transfer file from/to tftp server.
+
+    -l FILE Local FILE
+    -r FILE Remote FILE
+    -g    Get file
+    -p    Put file
+    -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
+*/
+
+#define FOR_tftp
+#include "toys.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+GLOBALS(
+  char *local_file;
+  char *remote_file;
+  long block_size;
+)
+
+#define flagGet(f,v,d)    ((toys.optflags & f) ? v : d)
+#define flagChk(f)      ((toys.optflags & f) ? 1 : 0)
+
+#define TFTP_PORT      69
+#define TFTP_TIMEOUT    10
+#define TFTP_BLKSIZE    512
+#define TFTP_RETRIES    3
+
+#define TFTP_ACKHEADERSIZE  4
+#define TFTP_ERRHEADERSIZE  4
+#define TFTP_DATAHEADERSIZE 4
+
+#define TFTP_MAXPACKETSIZE  (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
+#define TFTP_PACKETSIZE    TFTP_MAXPACKETSIZE
+
+#define TFTP_DATASIZE    (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
+#define TFTP_IOBUFSIZE    (TFTP_PACKETSIZE+8)
+
+#define TFTP_OP_RRQ      1  /* Read Request      RFC 1350, RFC 2090 */
+#define TFTP_OP_WRQ      2  /* Write Request     RFC 1350 */
+#define TFTP_OP_DATA    3  /* Data chunk      RFC 1350 */
+#define TFTP_OP_ACK      4  /* Acknowledgement     RFC 1350 */
+#define TFTP_OP_ERR      5  /* Error Message     RFC 1350 */
+#define TFTP_OP_OACK    6  /* Option acknowledgment RFC 2347 */
+
+#define TFTP_ER_NONE    0  /* No error */
+#define TFTP_ER_NOSUCHFILE  1  /* File not found */
+#define TFTP_ER_ACCESS    2  /* Access violation */
+#define TFTP_ER_FULL    3  /* Disk full or allocation exceeded */
+#define TFTP_ER_ILLEGALOP  4  /* Illegal TFTP operation */
+#define TFTP_ER_UNKID    5  /* Unknown transfer ID */
+#define TFTP_ER_EXISTS    6  /* File already exists */
+#define TFTP_ER_UNKUSER    7  /* No such user */
+#define TFTP_ER_NEGOTIATE  8  /* Terminate transfer due to option negotiation */
+
+#define TFTP_ES_NOSUCHFILE  "File not found"
+#define TFTP_ES_ACCESS    "Access violation"
+#define TFTP_ES_FULL    "Disk full or allocation exceeded"
+#define TFTP_ES_ILLEGALOP  "Illegal TFTP operation"
+#define TFTP_ES_UNKID    "Unknown transfer ID"
+#define TFTP_ES_EXISTS    "File already exists"
+#define TFTP_ES_UNKUSER    "No such user"
+#define TFTP_ES_NEGOTIATE  "Terminate transfer due to option negotiation"
+
+/*
+ * convert str to long within given range
+ */
+static int strtol_range(char *str, int min, int max)
+{
+  char *endptr = NULL;
+  errno = 0;
+  long ret_value = strtol(str, &endptr, 10);
+  if(errno) perror_exit("Invalid num %s", str);
+  else {
+    if(endptr && (*endptr != '\0' || endptr == str))
+      perror_exit("Not a valid num %s", str);
+  }
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else  perror_exit("Number %s is not in valid [%d-%d] Range\n", str, min, max);
+}
+
+/*
+ * Initializes SERVER with ADDR and returns socket.
+ */
+static int init_tftp(struct sockaddr_in *server, in_addr_t addr)
+{
+  struct timeval to;
+  int sd, ret;
+  const int set = 1;
+
+  int port = TFTP_PORT;
+  sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (sd < 0) perror_exit("socket create failed");
+  to.tv_sec = TFTP_TIMEOUT;
+  to.tv_usec = 0;
+  ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(struct timeval));
+  ret = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
+  if (ret < 0) perror_exit("setsockopt failed");
+  if(toys.optc == 2) port = strtol_range(toys.optargs[1], 1, 65535);
+  memset(server, 0, sizeof(struct sockaddr_in));
+  server->sin_family = AF_INET;
+  server->sin_addr.s_addr = addr;
+  server->sin_port = htons(port);
+  return sd;
+}
+
+/*
+ * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
+ * and returns length of packet.
+ */
+static int mkpkt_request(uint8_t *buffer, int opcode, const char *path, int mode)
+{
+  buffer[0] = opcode >> 8;
+  buffer[1] = opcode & 0xff;
+  if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
+  return sprintf((char*) &buffer[2], "%s%c%s", path, 0, (mode ? "octet" : "netascii")) + 3;
+}
+
+/*
+ * Makes an acknowledgement packet in BUFFER of BLOCNO
+ * and returns packet length.
+ */
+static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
+{
+  buffer[0] = TFTP_OP_ACK >> 8;
+  buffer[1] = TFTP_OP_ACK & 0xff;
+  buffer[2] = blockno >> 8;
+  buffer[3] = blockno & 0xff;
+  return 4;
+}
+
+/*
+ * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
+ * and returns packet length.
+ */
+static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, const char *errormsg)
+{
+  buffer[0] = TFTP_OP_ERR >> 8;
+  buffer[1] = TFTP_OP_ERR & 0xff;
+  buffer[2] = errorcode >> 8;
+  buffer[3] = errorcode & 0xff;
+  strcpy((char*) &buffer[4], errormsg);
+  return strlen(errormsg) + 5;
+}
+
+/*
+ * Recieves data from server in BUFF with socket SD and updates FROM
+ * and returns read length.
+ */
+static ssize_t read_server(int sd, void *buf, size_t len, struct sockaddr_in *from)
+{
+  int alen;
+  ssize_t nb;
+
+  for (;;) {
+    memset(buf, 0, len);
+    alen = sizeof(struct sockaddr_in);
+    nb = recvfrom(sd, buf, len, 0, (struct sockaddr*) from, (socklen_t*) &alen);
+    if (nb < 0) {
+      if (errno == EAGAIN) {
+        perror_msg("server read timed out");
+        return nb;
+      }else if (errno != EINTR) {
+        perror_msg("server read failed");
+        return nb;
+      }
+    }else return nb;
+  }
+  return nb;
+}
+
+/*
+ * sends data to server TO from BUFF of length LEN through socket SD
+ * and returns successfully send bytes number.
+ */
+static ssize_t write_server(int sd, const void *buf, size_t len, struct sockaddr_in *to)
+{
+  ssize_t nb;
+  for (;;) {
+    nb = sendto(sd, buf, len, 0, (struct sockaddr*) to, sizeof(struct sockaddr_in));
+    if (nb < 0) {
+      if (errno != EINTR) {
+        perror_msg("server write failed");
+        return nb;
+      }
+    } else return nb;
+  }
+  return nb;
+}
+
+/*
+ * checks packet for data and updates block no
+ */
+static inline int check_data(const uint8_t *packet, uint16_t *opcode, uint16_t *blockno)
+{
+  *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+  if (*opcode == TFTP_OP_DATA) {
+    *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+    return 0;
+  }
+  return -1;
+}
+
+/*
+ * Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
+ */
+static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
+{
+  off_t tmp;
+  int nbytesread;
+
+  packet[0] = TFTP_OP_DATA >> 8;
+  packet[1] = TFTP_OP_DATA & 0xff;
+  packet[2] = blockno >> 8;
+  packet[3] = blockno & 0xff;
+  tmp = lseek(fd, offset, SEEK_SET);
+  if (tmp == (off_t) -1) {
+    perror_msg("lseek failed");
+    return -1;
+  }
+  nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
+  if (nbytesread < 0) return -1;
+  return nbytesread + TFTP_DATAHEADERSIZE;
+}
+
+/*
+ * Receives ACK responses from server and updates blockno
+ */
+static int read_ack(int sd, uint8_t *packet, struct sockaddr_in *server, uint16_t *port, uint16_t *blockno)
+{
+  struct sockaddr_in from;
+  ssize_t nbytes;
+  uint16_t opcode, rblockno;
+  int packetlen, retry;
+
+  for (retry = 0; retry < TFTP_RETRIES; retry++) {
+    for (;;) {
+      nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+      if (nbytes < TFTP_ACKHEADERSIZE) {
+        if (nbytes == 0) error_msg("Connection lost.");
+        else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
+        else error_msg("Server read ACK failure.");
+        break;
+      } else {
+        if (!*port) {
+          *port = from.sin_port;
+          server->sin_port = from.sin_port;
+        }
+        if (server->sin_addr.s_addr != from.sin_addr.s_addr) {
+          error_msg("Invalid address in DATA.");
+          continue;
+        }
+        if (*port != server->sin_port) {
+          error_msg("Invalid port in DATA.");
+          packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+          (void) write_server(sd, packet, packetlen, server);
+          continue;
+        }
+        opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+        rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+
+        if (opcode != TFTP_OP_ACK) {
+          error_msg("Bad opcode.");
+          if (opcode > 5) {
+            packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+            (void) write_server(sd, packet, packetlen, server);
+          }
+          break;
+        }
+        if(blockno != NULL) *blockno = rblockno;
+        return 0;
+      }
+    }
+  }
+  error_msg("Timeout, Waiting for ACK.");
+  return -1;
+}
+
+/*
+ * receives file from server.
+ */
+static int file_get(void)
+{
+  struct in_addr inaddr;
+  struct sockaddr_in server, from;
+  uint8_t *packet;
+  uint16_t blockno = 0, opcode, rblockno;
+  int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
+
+  packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
+  fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+  inet_aton(toys.optargs[0], &inaddr);
+  sd = init_tftp(&server, inaddr.s_addr);
+  if (sd < 0) {
+    unlink(TT.local_file);
+    goto errout_with_fd;
+  }
+
+  do {
+    blockno++;
+    for (retry = 0; retry < TFTP_RETRIES; retry++) {
+      if (blockno == 1) {
+        len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
+        ret = write_server(sd, packet, len, &server);
+        if (ret != len){
+          unlink(TT.local_file);
+          goto errout_with_sd;
+        }
+        server.sin_port = 0;
+      }
+      nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+      if (nbytesrecvd > 0) {
+        if (server.sin_addr.s_addr != from.sin_addr.s_addr) {
+          error_msg("Invalid address in DATA.");
+          retry--;
+          continue;
+        }
+        if (server.sin_port && server.sin_port != from.sin_port) {
+          error_msg("Invalid port in DATA.");
+          len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+          ret = write_server(sd, packet, len, &from);
+          retry--;
+          continue;
+        }
+        if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
+          error_msg("Tiny data packet ignored.");
+          continue;
+        }
+        if (check_data(packet, &opcode, &rblockno) != 0
+            || blockno != rblockno) {
+
+        if(opcode == TFTP_OP_ERR){
+               switch(opcode){
+                 case TFTP_ER_NOSUCHFILE: error_msg(TFTP_ES_NOSUCHFILE); break;
+                 case TFTP_ER_ACCESS: error_msg(TFTP_ES_ACCESS); break;
+                 case TFTP_ER_FULL: error_msg(TFTP_ES_FULL); break;
+                 case TFTP_ER_ILLEGALOP: error_msg(TFTP_ES_ILLEGALOP); break;
+                 case TFTP_ER_UNKID: error_msg(TFTP_ES_UNKID); break;
+                 case TFTP_ER_EXISTS: error_msg(TFTP_ES_EXISTS); break;
+                 case TFTP_ER_UNKUSER: error_msg(TFTP_ES_UNKUSER); break;
+                 case TFTP_ER_NEGOTIATE: error_msg(TFTP_ES_NEGOTIATE); break;
+                 default: error_msg("DATA Check failure."); break;
+               }
+        }
+        if (opcode > 5) {
+          len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+          ret = write_server(sd, packet, len, &from);
+        }
+        continue;
+        }
+        if (!server.sin_port) server.sin_port = from.sin_port;
+        break;
+      }
+    }
+    if (retry == TFTP_RETRIES) {
+      error_msg("Retry limit exceeded.");
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+    ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
+    if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+    len = mkpkt_ack(packet, blockno);
+    ret = write_server(sd, packet, len, &server);
+    if (ret != len){
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+  } while (ndatabytes >= TFTP_DATASIZE);
+
+  result = 0;
+
+errout_with_sd: close(sd);
+errout_with_fd: close(fd);
+  free(packet);
+  return result;
+}
+
+/*
+ * Sends file to server.
+ */
+int file_put(void)
+{
+  struct in_addr inaddr;
+  struct sockaddr_in server;
+  uint8_t *packet;
+  off_t offset = 0;
+  uint16_t blockno = 1, rblockno, port = 0;
+  int packetlen, sd, fd, retry = 0, ret, result = -1;
+
+  packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
+  fd = xopen(TT.local_file, O_RDONLY);
+  inet_aton(toys.optargs[0], &inaddr);
+  sd = init_tftp(&server, inaddr.s_addr);
+  if (sd < 0) goto errout_with_fd;
+
+  for (;;) {  //first loop for request send and confirmation from server.
+    packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
+    ret = write_server(sd, packet, packetlen, &server);
+    if (ret != packetlen) goto errout_with_sd;
+    if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
+    if (++retry > TFTP_RETRIES) {
+      error_msg("Retry count exceeded.");
+      goto errout_with_sd;
+    }
+  }
+  for (;;) {  // loop for data sending and receving ack from server.
+    packetlen = mkpkt_data(fd, offset, packet, blockno);
+    if (packetlen < 0) goto errout_with_sd;
+
+    ret = write_server(sd, packet, packetlen, &server);
+    if (ret != packetlen) goto errout_with_sd;
+
+    if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
+      if (rblockno == blockno) {
+        if (packetlen < TFTP_PACKETSIZE) break;
+        blockno++;
+        offset += TFTP_DATASIZE;
+        retry = 0;
+        continue;
+      }
+    }
+    if (++retry > TFTP_RETRIES) {
+      error_msg("Retry count exceeded.");
+      goto errout_with_sd;
+    }
+  }
+  result = 0;
+
+errout_with_sd: close(sd);
+errout_with_fd: close(fd);
+  free(packet);
+  return result;
+}
+
+void tftp_main(void)
+{
+  // TODO: remove these two chks when base arg parsing is fixed.
+  if((flagChk(FLAG_p)) && (flagChk(FLAG_g))) error_exit("Can't do GET and PUT at the same time.");
+  if((!flagChk(FLAG_p)) && (!flagChk(FLAG_g))) error_exit("provide an action GET or PUT.");
+
+  if(flagChk(FLAG_r)){
+         if(!(flagChk(FLAG_l))){
+                 char *slash = strrchr(TT.remote_file, '/');
+                 TT.local_file = (slash) ? slash + 1 : TT.remote_file;
+         }
+  }else if (flagChk(FLAG_l)) TT.remote_file = TT.local_file;
+  else error_exit("Please provide some files.");
+
+  if(flagChk(FLAG_g)) file_get();
+  if(flagChk(FLAG_p)) file_put();
+}
diff --git a/toys/other/traceroute.c b/toys/other/traceroute.c
new file mode 100644 (file)
index 0000000..6b8a8ed
--- /dev/null
@@ -0,0 +1,497 @@
+/* traceroute - trace the route to "host".
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_TRACEROUTE(NEWTOY(traceroute, "<1>2f#<0>255z#<0>86400g*w#<0>86400t#<0>255s:q#<1>255p#<1>65535m#<1>255rvndlIUF64", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TRACEROUTE
+  bool "traceroute"
+  default y
+  help
+    usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]
+    [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]
+
+    Trace the route to HOST
+
+    -4,-6   Force IP or IPv6 name resolution
+    -F    Set the don't fragment bit
+    -U    Use UDP datagrams instead of ICMP ECHO
+    -I    Use ICMP ECHO instead of UDP datagrams
+    -l    Display the TTL value of the returned packet
+    -f    Start from the 1ST_TTL hop (instead from 1)(RANGE 0 to 255)
+    -d    Set SO_DEBUG options to socket
+    -n    Print numeric addresses
+    -v    verbose
+    -r    Bypass routing tables, send directly to HOST
+    -m    Max time-to-live (max number of hops)(RANGE 1 to 255)
+    -p    Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)
+    -q    Number of probes per TTL (default 3)(RANGE 1 to 255)
+    -s    IP address to use as the source address
+    -t    Type-of-service in probe packets (default 0)(RANGE 0 to 255)
+    -w    Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)
+    -g    Loose source route gateway (8 max)
+    -z    Pause Time in sec (default 0)(RANGE 0 to 86400)
+*/
+#define FOR_traceroute
+#include "toys.h"
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+#include <linux/if_ether.h>
+#include <arpa/inet.h>
+#include  <sys/poll.h>
+#include <time.h>
+
+GLOBALS(
+  long max_ttl;
+  long port;
+  long ttl_probes;
+  char *src_ip;
+  long tos;
+  long wait_time;
+  struct arg_list *loose_source;
+  long pause_time;
+  long first_ttl;
+
+  char* hostname;
+  int recv_sock;
+  int snd_sock;
+  unsigned msg_len;
+  struct ip *packet;
+  struct sockaddr_in dest;
+  struct sockaddr_in from;
+  uint32_t gw_list[9];
+)
+
+
+#define flag_get(f,v,d)    ((toys.optflags & f) ? v : d)
+#define flag_chk(f)      ((toys.optflags & f) ? 1 : 0)
+
+#define RANGE_PACKET_SIZE  32768
+#define DEF_UDP_PORT    33434
+#define DEF_MAX_TTL      30
+#define HOST_NAME_SIZE    1025
+#define DEF_PROBE_VAL    3
+#define DEF_WAIT_TIME    3
+#define NGATEWAYS      8
+#define ICMP_HD_SIZE    8
+#define send_icmp      ((struct icmp *)(TT.packet +   1))
+#define send_udp      ((struct udphdr *)(TT.packet + 1))
+
+typedef struct payload_s {
+  unsigned char seq;
+  unsigned char ttl;
+  struct timeval tv __attribute__((__packed__));
+} payload_t;
+
+payload_t *send_data;
+
+/*
+ * Computes and returns checksum SUM of buffer P of length LEN
+ */
+static u_int16_t in_cksum(u_int16_t *p, u_int len)
+{
+  u_int32_t sum = 0;
+  int nwords = len >> 1;
+
+  while (nwords-- != 0) sum += *p++;
+  if (len & 1) {
+    union {
+      u_int16_t w;
+      u_int8_t c[2];
+    } u;
+    u.c[0] = *(u_char *) p;
+    u.c[1] = 0;
+    sum += u.w;
+  }
+  /* end-around-carry */
+  sum = (sum >> 16) + (sum & 0xffff);
+  sum += (sum >> 16);
+  return (~sum);
+}
+
+/*
+ * sends a single probe packet with sequence SEQ and
+ * time-to-live TTL
+ */
+void send_probe(int seq, int ttl)
+{
+  int res, len;
+  void *out;
+
+  if (flag_chk(FLAG_U)) {
+    send_data->seq = seq;
+    send_data->ttl = ttl;
+  } else {
+    send_icmp->icmp_seq = htons(seq);
+    send_icmp->icmp_cksum = 0;
+    send_icmp->icmp_cksum = in_cksum((uint16_t *) send_icmp, TT.msg_len - sizeof(struct ip));
+    if (send_icmp->icmp_cksum == 0) send_icmp->icmp_cksum = 0xffff;
+  }
+  TT.dest.sin_port = TT.port + seq;
+
+#ifdef IP_TTL
+  res = setsockopt(TT.snd_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
+  if (res < 0) perror_exit("setsockopt ttl %d", ttl);
+#endif
+
+  if (flag_chk(FLAG_U)) {
+    out = send_data;
+    len = sizeof(payload_t);
+  } else {
+    out = send_icmp;
+    len = TT.msg_len - sizeof(struct ip);
+  }
+  res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &TT.dest, sizeof(TT.dest));
+  if (res != len) perror_exit(" sendto");
+}
+
+/*
+ * converst str to long and checks the range of STR as MIN < STR < MAX
+ */
+static int strtol_range(char *str, int min, int max)
+{
+  char *endptr = NULL;
+  errno=0;
+  long ret_value = strtol(str,&endptr,10);
+  if(errno) perror_exit("Invalid num %s",str);
+  else {
+    if(endptr && (*endptr!='\0'||endptr == str))
+      perror_exit("Not a valid num %s",str);
+  }
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else  perror_exit("Number %s is not in valid [%d-%d] Range",str,min,max);
+}
+
+/*
+ * Traceroute main routine.
+ */
+void traceroute_main(void)
+{
+  const int set = 1;
+  uint32_t ident;
+  unsigned opt_len = 0;
+  unsigned pack_size, tyser = 0;
+  int lsrr = 0, ret;
+  struct addrinfo *info, *rp;
+
+  if (flag_chk(FLAG_g)) {
+    struct arg_list *node = TT.loose_source;
+
+    while (node) {
+      struct sockaddr_in *sin = xzalloc(sizeof(struct sockaddr_in));
+      if (lsrr >= NGATEWAYS) error_exit("no more than %d gateways", NGATEWAYS);
+
+      rp = xzalloc(sizeof(struct addrinfo));
+      rp->ai_family = AF_INET;
+      rp->ai_socktype = SOCK_STREAM;
+
+      ret = getaddrinfo(node->arg, NULL, rp, &info);
+      if (ret || !info) perror_exit("LSRR BAD ADDRESS: can't find : %s ", TT.hostname);
+      free(rp);
+
+      for (rp = info; rp != NULL; rp = rp->ai_next) {
+        if (rp->ai_addrlen == sizeof(struct sockaddr_in)) {
+          memcpy(sin, rp->ai_addr, rp->ai_addrlen);
+          break;
+        }
+      }
+      if (!rp) perror_exit("Resolve failed ");
+      freeaddrinfo(info);
+
+      TT.gw_list[lsrr] = sin->sin_addr.s_addr;
+      free(sin);
+      ++lsrr;
+      node = node->next;
+    }
+    opt_len = (lsrr + 1) * sizeof(TT.gw_list[0]);
+  }
+
+  pack_size = sizeof(struct ip) + opt_len;
+  pack_size += (flag_chk(FLAG_U)) ? sizeof(struct udphdr) + sizeof(payload_t) : ICMP_HD_SIZE;
+
+  if (toys.optc == 2) TT.msg_len = strtol_range(toys.optargs[1], pack_size, RANGE_PACKET_SIZE);
+
+  TT.msg_len = (TT.msg_len < pack_size) ? pack_size : TT.msg_len;
+  TT.recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  if (TT.recv_sock <= 0) perror_exit("Failed to create recv sock.");
+  if (flag_chk(FLAG_d)
+      && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0))
+    perror_exit("SO_DEBUG failed ");
+  if (flag_chk(FLAG_r)
+      && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0))
+    perror_exit("SO_DONTROUTE failed ");
+
+  TT.hostname = toys.optargs[0];
+  TT.port = flag_get(FLAG_p, TT.port, DEF_UDP_PORT);
+  TT.snd_sock =(flag_chk(FLAG_U)) ? socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP):socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  if (TT.snd_sock <= 0) error_exit("Failed to create send sock.");
+
+#ifdef IP_OPTIONS
+  if (lsrr > 0) {
+    unsigned char optlist[MAX_IPOPTLEN];
+    unsigned size;
+
+    TT.gw_list[lsrr] = TT.dest.sin_addr.s_addr;
+    ++lsrr;
+
+    optlist[0] = IPOPT_NOP;
+    /* loose source route option */
+    optlist[1] = IPOPT_LSRR;
+    size = lsrr * sizeof(TT.gw_list[0]);
+    optlist[2] = size + 3;
+    optlist[3] = IPOPT_MINOFF;
+    memcpy(optlist + 4, TT.gw_list, size);
+
+    if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_OPTIONS,
+        (char *)optlist, size + sizeof(TT.gw_list[0])) < 0)
+      perror_exit("LSRR IP_OPTIONS");
+  }
+#endif
+#ifdef SO_SNDBUF
+  if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, sizeof(TT.msg_len)) < 0)
+    perror_exit("SO_SNDBUF failed ");
+#endif
+#ifdef IP_TOS
+  if (flag_chk(FLAG_t) && setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0)
+    perror_exit("IP_TOS %d failed ", TT.tos);
+#endif
+#ifdef IP_DONTFRAG
+  if (flag_chk(FLAG_F) && (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, sizeof(set)) < 0))
+    perror_exit("IP_DONTFRAG failed ");
+#endif
+
+  if (flag_chk(FLAG_d)
+      && (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0))
+    perror_exit("SO_DEBUG failed ");
+  if (flag_chk(FLAG_r)
+      && (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0))
+    perror_exit("SO_DONTROUTE failed ");
+
+  rp = xzalloc(sizeof(struct addrinfo));
+  rp->ai_family = AF_INET;
+  rp->ai_socktype = flag_chk(FLAG_U) ? SOCK_DGRAM : SOCK_RAW;
+  rp->ai_protocol = flag_chk(FLAG_U) ? IPPROTO_UDP : IPPROTO_ICMP;
+
+  ret = getaddrinfo(TT.hostname, NULL, rp, &info);
+  if (ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", TT.hostname);
+  free(rp);
+
+  for (rp = info; rp != NULL; rp = rp->ai_next) {
+    if (rp->ai_addrlen == sizeof(struct sockaddr_in)) {
+      memcpy(&TT.dest, rp->ai_addr, rp->ai_addrlen);
+      break;
+    }
+  }
+  if (!rp) perror_exit("Resolve failed ");
+  freeaddrinfo(info);
+
+  TT.packet = xmalloc(TT.msg_len);
+  ident = getpid();
+
+  if (flag_chk(FLAG_U)) {
+    send_data = (payload_t *) (send_udp + 1);
+  } else {
+    ident |= 0x8000;
+    send_icmp->icmp_type = ICMP_ECHO;
+    send_icmp->icmp_id = htons(ident);
+  }
+  if (flag_chk(FLAG_s)) {
+      struct sockaddr_in *source =  xzalloc(sizeof(struct sockaddr_in));
+      inet_aton(TT.src_ip, &source->sin_addr);
+        if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_MULTICAST_IF, (struct sockaddr*)&source, sizeof(*source)))
+          perror_exit("can't set multicast source interface");
+      bind(TT.snd_sock,(struct sockaddr*)&source, sizeof(*source));
+      free(source);
+  }
+  struct pollfd pfd[1];
+  pfd[0].fd = TT.recv_sock;
+  pfd[0].events = POLLIN;
+  int tv = flag_get(FLAG_w ,TT.wait_time, DEF_WAIT_TIME) * 1000;
+
+  int seq, fexit = 0, ttl = flag_get(FLAG_f, TT.first_ttl, 1);
+  TT.max_ttl = flag_get(FLAG_m, TT.max_ttl, DEF_MAX_TTL);
+  TT.ttl_probes = flag_get(FLAG_q, TT.ttl_probes, DEF_PROBE_VAL);
+
+  printf("traceroute to %s(%s)", TT.hostname, inet_ntoa(TT.dest.sin_addr));
+  if (flag_chk(FLAG_s)) printf(" from s");
+  printf(", %ld hops max, %u byte packets\n", TT.max_ttl, TT.msg_len);
+
+  if(ttl > TT.max_ttl) perror_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl);
+  for (; ttl <= TT.max_ttl; ++ttl) {
+    int probe;
+    struct timeval t1, t2;
+    struct sockaddr_in last;
+    memset(&last, 0, sizeof(last));
+
+    printf("%2d", ttl);
+    fflush(NULL);
+
+    for (probe = 0, seq = 0; probe < TT.ttl_probes; ++probe) {
+      int res = 0, tleft = tv;
+      if (probe != 0 && flag_chk(FLAG_z)) sleep(TT.pause_time);
+      fexit = 0;
+
+      send_probe(++seq, ttl);
+      gettimeofday(&t1, NULL);
+
+POLL_IN:   res = poll(pfd, 1, tleft);
+      gettimeofday(&t2, NULL);
+
+      if (res == 0) {
+        printf("  *");
+        continue;
+      }
+      if (res < 0) {
+        tleft = tv - (t2.tv_sec * 1000000ULL + t2.tv_usec)
+            + (t1.tv_sec * 1000000ULL + t1.tv_usec);
+        goto POLL_IN;
+      }
+      if (pfd[0].revents) {
+        unsigned addrlen = sizeof(TT.from);
+        struct ip *rcv_pkt = (struct ip*) toybuf;
+        int pmtu = 0;
+        int rcv_len = recvfrom(TT.recv_sock, rcv_pkt, sizeof(toybuf),
+            MSG_DONTWAIT, (struct sockaddr *) &TT.from, &addrlen);
+        if (rcv_len > 0) {
+          struct icmp *ricmp;
+          int icmp_res = -1;
+          ricmp = (struct icmp *) ((void*)rcv_pkt + (rcv_pkt->ip_hl << 2));
+
+          if (ricmp->icmp_code == ICMP_UNREACH_NEEDFRAG)
+            pmtu = ntohs(ricmp->icmp_nextmtu);
+
+          if ((ricmp->icmp_type == ICMP_TIMXCEED
+              && ricmp->icmp_code == ICMP_TIMXCEED_INTRANS)
+              || ricmp->icmp_type == ICMP_UNREACH
+              || ricmp->icmp_type == ICMP_ECHOREPLY) {
+
+            const struct ip *hip;
+            const struct udphdr *hudp;
+            struct icmp *hicmp;
+
+            hip = &ricmp->icmp_ip;
+
+            if (flag_chk(FLAG_U)) {
+              hudp = (struct udphdr*) (hip + (hip->ip_hl << 2));
+              if ((hip->ip_hl << 2) + 12 <= rcv_len && hip->ip_p == IPPROTO_UDP
+                  && hudp->dest == htons(TT.port + seq))
+                icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ?-1 : ricmp->icmp_code);
+            } else {
+              hicmp = (struct icmp *) ((void*)hip + (hip->ip_hl << 2));
+              if (ricmp->icmp_type == ICMP_ECHOREPLY && ricmp->icmp_id == htons(ident)
+                  && ricmp->icmp_seq == htons(seq))
+                icmp_res = ICMP_UNREACH_PORT;
+
+              if ((hip->ip_hl << 2) + ICMP_HD_SIZE <= rcv_len
+                  && hip->ip_p == IPPROTO_ICMP
+                  && hicmp->icmp_id == htons(ident)
+                  && hicmp->icmp_seq == htons(seq))
+                icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : ricmp->icmp_code);
+            }
+          }
+
+          if (addrlen > 0) {
+            unsigned delta = (t2.tv_sec * 1000000ULL + t2.tv_usec)
+                - (t1.tv_sec * 1000000ULL + t1.tv_usec);
+
+            if (memcmp(&last.sin_addr, &TT.from.sin_addr,
+                sizeof(struct in_addr)) != 0) {
+
+              if (!flag_chk(FLAG_n)) {
+                char host[HOST_NAME_SIZE];
+                if (getnameinfo((struct sockaddr *) &TT.from,
+                    sizeof(TT.from), host, HOST_NAME_SIZE, NULL, 0, 0)
+                    == 0)
+                  printf("  %s (", host);
+              }
+              printf(" %s", inet_ntoa(TT.from.sin_addr));
+              if (!flag_chk(FLAG_n) )
+                printf(")");
+              last = TT.from;
+            }
+            printf("  %u.%03u ms", delta / 1000, delta % 1000);
+            if (flag_chk(FLAG_l)) printf(" (%d)", rcv_pkt->ip_ttl);
+                       if (flag_chk(FLAG_v)) {
+                               printf(" %d bytes from %s : icmp type %d code %d\t",
+                                               rcv_len, inet_ntoa(TT.from.sin_addr),
+                                               ricmp->icmp_type, ricmp->icmp_code);
+                       }
+          } else if (addrlen <= 0)
+            printf("\t!H");
+
+                       if (ricmp->icmp_type == ICMP_DEST_UNREACH) {
+                               switch (icmp_res) {
+                               case ICMP_UNREACH_PORT:
+                                 if (rcv_pkt->ip_ttl <= 1)
+                                       printf(" !");
+                                 break;
+                               case ICMP_UNREACH_NET:
+                                 printf(" !N");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_HOST:
+                                 printf(" !H");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_PROTOCOL:
+                                 printf(" !P");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_NEEDFRAG:
+                                 printf(" !F-%d", pmtu);
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_SRCFAIL:
+                                 printf(" !S");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_FILTER_PROHIB:  /* FALLTHROUGH */
+                               case ICMP_UNREACH_NET_PROHIB: /* misuse */
+                                 printf(" !A");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_HOST_PROHIB:
+                                 printf(" !C");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_HOST_PRECEDENCE:
+                                 printf(" !V");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+                                 printf(" !C");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_NET_UNKNOWN:  /* FALLTHROUGH */
+                               case ICMP_UNREACH_HOST_UNKNOWN:
+                                 printf(" !U");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_ISOLATED:
+                                 printf(" !I");
+                                 ++fexit;
+                                 break;
+                               case ICMP_UNREACH_TOSNET:  /* FALLTHROUGH */
+                               case ICMP_UNREACH_TOSHOST:
+                                 printf(" !T");
+                                 ++fexit;
+                                 break;
+                               }
+                       }
+        } else if (rcv_len <= 0) {
+          tleft = tv - (t2.tv_sec * 1000000ULL + t2.tv_usec)
+              + (t1.tv_sec * 1000000ULL + t1.tv_usec);
+          goto POLL_IN;
+        }
+      }
+    }
+    printf("\n");
+    if (( memcmp(&TT.from.sin_addr, &TT.dest.sin_addr, sizeof(struct in_addr)) == 0) || (fexit >= TT.ttl_probes -1))
+      break;
+  }
+}
diff --git a/toys/other/truncate.c b/toys/other/truncate.c
new file mode 100644 (file)
index 0000000..123ae55
--- /dev/null
@@ -0,0 +1,40 @@
+/* truncate.c - set file length, extending sparsely if necessary
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+
+USE_TRUNCATE(NEWTOY(truncate, "<1s#|c", TOYFLAG_BIN))
+
+config TRUNCATE
+  bool "truncate"
+  default y
+  help
+    usage: truncate [-c] -s file...
+
+    Set length of file(s), extending sparsely if necessary.
+
+    -c Don't create file if it doesn't exist.
+    -s New size
+*/
+
+#define FOR_truncate
+#include "toys.h"
+
+GLOBALS(
+  long size;
+)
+
+static void do_truncate(int fd, char *name)
+{
+  if (fd<0) return;
+  if (ftruncate(fd, TT.size)) perror_msg("'%s' to '%ld'", name, TT.size);
+}
+
+void truncate_main(void)
+{
+  int cr = !(toys.optflags&1);
+
+  // Create files with mask rwrwrw.
+  // Nonexistent files are only an error if we're supposed to create them.
+  loopfiles_rw(toys.optargs, O_WRONLY|(cr ? O_CREAT : 0), 0666, cr,
+    do_truncate);
+}
diff --git a/toys/other/udhcpc.c b/toys/other/udhcpc.c
new file mode 100644 (file)
index 0000000..ef49c6b
--- /dev/null
@@ -0,0 +1,1645 @@
+/* udhcpc.c - DHCP client for dynamic network configuration.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+USE_UDHCPC(NEWTOY(udhcpc, "V:H:F:x*r:O*A#<0T#<0t#<0s:p:i:SBRCaovqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
+
+config UDHCPC
+  bool "udhcpc"
+  default y
+  help
+   usage: udhcpc [-fbnqvoCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]
+               [-H HOSTNAME] [-V VENDOR] [-x OPT:VAL] [-O OPT]
+
+        Configure network dynamicaly using DHCP.
+
+      -i Interface to use (default eth0)
+      -p Create pidfile
+      -s Run PROG at DHCP events (default /usr/share/udhcpc/default.script)
+      -B Request broadcast replies
+      -t Send up to N discover packets
+      -T Pause between packets (default 3 seconds)
+      -A Wait N seconds after failure (default 20)
+      -f Run in foreground
+      -b Background if lease is not obtained
+      -n Exit if lease is not obtained
+      -q Exit after obtaining lease
+      -R Release IP on exit
+      -S Log to syslog too
+      -a Use arping to validate offered address
+      -O Request option OPT from server (cumulative)
+      -o Don't request any options (unless -O is given)
+      -r Request this IP address
+      -x OPT:VAL  Include option OPT in sent packets (cumulative)
+      -F Ask server to update DNS mapping for NAME
+      -H Send NAME as client hostname (default none)
+      -V VENDOR Vendor identifier (default 'udhcp VERSION')
+      -C Don't send MAC as client identifier
+      -v Verbose
+
+      Signals:
+      USR1  Renew current lease
+      USR2  Release current lease
+
+*/
+
+#define FOR_udhcpc
+#include "toys.h"
+#include <linux/filter.h> //FIXME: linux specific. fix for other OS ports
+#include <linux/if_ether.h>
+
+GLOBALS(
+    char *iface;         // -i
+    char *pidfile;        // -p
+    char *script;        // -s
+    long retries;        // -t
+    long timeout;        // -T
+    long tryagain;        // -A
+    struct arg_list *req_opt;  // -O
+    char *req_ip;        // -r
+    struct arg_list *pkt_opt;  // -x
+    char *fdn_name;        // -F
+    char *hostname;        // -H
+    char *vendor_cls;      // -V
+)
+
+#define flag_get(f,v,d) ((toys.optflags & f) ? v : d)
+#define flag_chk(f)     ((toys.optflags & f) ? 1 : 0)
+
+#define STATE_INIT            0
+#define STATE_REQUESTING      1
+#define STATE_BOUND           2
+#define STATE_RENEWING        3
+#define STATE_REBINDING       4
+#define STATE_RENEW_REQUESTED 5
+#define STATE_RELEASED        6
+
+#define BOOTP_BROADCAST   0x8000
+#define DHCP_MAGIC        0x63825363
+
+#define DHCP_REQUEST          1
+#define DHCP_REPLY            2
+#define DHCP_HTYPE_ETHERNET   1
+#define DHCP_HLEN_ETHERNET    6
+#define DHCP_MSG_LEN          236
+
+#define DHCPC_SERVER_PORT     67
+#define DHCPC_CLIENT_PORT     68
+
+#define DHCPDISCOVER      1
+#define DHCPOFFER         2
+#define DHCPREQUEST       3
+#define DHCPDECLINE       4
+#define DHCPACK           5
+#define DHCPNAK           6
+#define DHCPRELEASE       7
+#define DHCPINFORM        8
+
+#define DHCP_OPTION_PADDING     0x00
+#define DHCP_OPTION_SUBNET_MASK 0x01
+#define DHCP_OPTION_ROUTER      0x03
+#define DHCP_OPTION_DNS_SERVER  0x06
+#define DHCP_OPTION_HOST_NAME   0x0c
+#define DHCP_OPTION_DOMAIN_NAME 0x0f
+#define DHCP_OPTION_BROADCAST   0x1c
+#define DHCP_OPTION_NTP_SERVER  0x2a
+#define DHCP_OPTION_REQ_IPADDR  0x32
+#define DHCP_OPTION_LEASE_TIME  0x33
+#define DHCP_OPTION_OVERLOAD    0x34
+#define DHCP_OPTION_MSG_TYPE    0x35
+#define DHCP_OPTION_SERVER_ID   0x36
+#define DHCP_OPTION_REQ_LIST    0x37
+#define DHCP_OPTION_MAX_SIZE    0x39
+#define DHCP_OPTION_CLIENTID    0x3D
+#define DHCP_OPTION_VENDOR      0x3C
+#define DHCP_OPTION_FQDN        0x51
+#define DHCP_OPTION_END         0xFF
+
+#define DHCP_NUM8           (1<<8)
+#define DHCP_NUM16          (1<<9)
+#define DHCP_NUM32          DHCP_NUM16 | DHCP_NUM8
+#define DHCP_STRING         (1<<10)
+#define DHCP_STRLST         (1<<11)
+#define DHCP_IP             (1<<12)
+#define DHCP_IPLIST         (1<<13)
+#define DHCP_IPPLST         (1<<14)
+#define DHCP_STCRTS         (1<<15)
+
+#define LOG_SILENT          0x0
+#define LOG_CONSOLE         0x1
+#define LOG_SYSTEM          0x2
+
+#define BUFFER_SIZE       256
+
+#define MODE_OFF        0
+#define MODE_RAW        1
+#define MODE_APP        2
+
+static void (*dbg)(char *format, ...);
+static void dummy(char *format, ...){
+       return;
+}
+
+typedef struct dhcpc_result_s {
+  struct in_addr serverid;
+  struct in_addr ipaddr;
+  struct in_addr netmask;
+  struct in_addr dnsaddr;
+  struct in_addr default_router;
+  uint32_t lease_time;
+} dhcpc_result_t;
+
+typedef struct __attribute__((packed)) dhcp_msg_s {
+  uint8_t op;
+  uint8_t htype;
+  uint8_t hlen;
+  uint8_t hops;
+  uint32_t xid;
+  uint16_t secs;
+  uint16_t flags;
+  uint32_t ciaddr;
+  uint32_t yiaddr;
+  uint32_t nsiaddr;
+  uint32_t ngiaddr;
+  uint8_t chaddr[16];
+  uint8_t sname[64];
+  uint8_t file[128];
+  uint32_t cookie;
+  uint8_t options[308];
+} dhcp_msg_t;
+
+typedef struct __attribute__((packed)) dhcp_raw_s {
+  struct iphdr iph;
+  struct udphdr udph;
+  dhcp_msg_t dhcp;
+} dhcp_raw_t;
+
+typedef struct dhcpc_state_s {
+  uint8_t macaddr[6];
+  const char *iface;
+  int ifindex;
+  int sockfd;
+  int status;
+  int mode;
+  uint32_t mask;
+  struct in_addr ipaddr;
+  struct in_addr serverid;
+  dhcp_msg_t pdhcp;
+} dhcpc_state_t;
+
+typedef struct option_val_s {
+  char *key;
+  uint16_t code;
+  void *val;
+  size_t len;
+} option_val_t;
+
+struct fd_pair { int rd; int wr; };
+static uint32_t xid;
+static dhcpc_state_t *state;
+static struct fd_pair sigfd;
+uint8_t bmacaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+const int set = 1;
+uint8_t infomode = LOG_CONSOLE;
+uint8_t raw_opt[29];
+int raw_optcount = 0;
+struct arg_list *x_opt;
+in_addr_t server = 0;
+
+static option_val_t *msgopt_list = NULL;
+static option_val_t options_list[] = {
+    {"lease"          , DHCP_NUM32  | 0x33, NULL, 0},
+    {"subnet"         , DHCP_IP     | 0x01, NULL, 0},
+    {"broadcast"      , DHCP_IP     | 0x1c, NULL, 0},
+    {"router"         , DHCP_IP     | 0x03, NULL, 0},
+    {"ipttl"          , DHCP_NUM8   | 0x17, NULL, 0},
+    {"mtu"            , DHCP_NUM16  | 0x1a, NULL, 0},
+    {"hostname"       , DHCP_STRING | 0x0c, NULL, 0},
+    {"domain"         , DHCP_STRING | 0x0f, NULL, 0},
+    {"search"         , DHCP_STRLST | 0x77, NULL, 0},
+    {"nisdomain"      , DHCP_STRING | 0x28, NULL, 0},
+    {"timezone"       , DHCP_NUM32  | 0x02, NULL, 0},
+    {"tftp"           , DHCP_STRING | 0x42, NULL, 0},
+    {"bootfile"       , DHCP_STRING | 0x43, NULL, 0},
+    {"bootsize"       , DHCP_NUM16  | 0x0d, NULL, 0},
+    {"rootpath"       , DHCP_STRING | 0x11, NULL, 0},
+    {"wpad"           , DHCP_STRING | 0xfc, NULL, 0},
+    {"serverid"       , DHCP_IP     | 0x36, NULL, 0},
+    {"message"        , DHCP_STRING | 0x38, NULL, 0},
+    {"vlanid"         , DHCP_NUM32  | 0x84, NULL, 0},
+    {"vlanpriority"   , DHCP_NUM32  | 0x85, NULL, 0},
+    {"dns"            , DHCP_IPLIST | 0x06, NULL, 0},
+    {"wins"           , DHCP_IPLIST | 0x2c, NULL, 0},
+    {"nissrv"         , DHCP_IPLIST | 0x29, NULL, 0},
+    {"ntpsrv"         , DHCP_IPLIST | 0x2a, NULL, 0},
+    {"lprsrv"         , DHCP_IPLIST | 0x09, NULL, 0},
+    {"swapsrv"        , DHCP_IP     | 0x10, NULL, 0},
+    {"routes"         , DHCP_STCRTS | 0x21, NULL, 0},
+    {"staticroutes"   , DHCP_STCRTS | 0x79, NULL, 0},
+    {"msstaticroutes" , DHCP_STCRTS | 0xf9, NULL, 0},
+};
+
+static const struct sock_filter filter_instr[] = {
+    BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
+    BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
+    BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
+    BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
+    BPF_STMT(BPF_RET|BPF_K, 0xffffffff), BPF_STMT(BPF_RET|BPF_K, 0),
+};
+
+static const struct sock_fprog filter_prog = {
+    .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
+    .filter = (struct sock_filter *) filter_instr,
+};
+
+/*
+ * calculate options size.
+ */
+static int dhcp_opt_size(uint8_t *optionptr)
+{
+  int i = 0;
+  for(;optionptr[i] != 0xff; i++) if(optionptr[i] != 0x00) i += optionptr[i + 1] + 2 -1;
+  return i;
+}
+
+/*
+ * calculates checksum for udhcp messeges.
+ */
+static uint16_t udhcp_checksum(void *addr, int count)
+{
+  int32_t sum = 0;
+  uint16_t *source = (uint16_t *)addr;
+  uint16_t tmp = 0;
+
+  while (count > 1)  {
+    sum += *source++;
+    count -= 2;
+  }
+  if (count > 0) {
+    *(uint8_t*)&tmp = *(uint8_t*)source;
+    sum += tmp;
+  }
+  while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
+  return ~sum;
+}
+
+/*
+ * gets information of INTERFACE and updates IFINDEX, MAC and IP
+ */
+static int get_interface(const char *interface, int *ifindex, uint32_t *oip, uint8_t *mac)
+{
+  struct ifreq req;
+  int fd;
+  struct sockaddr_in *ip;
+
+  fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+  if (fd < 0) {
+    error_msg("IFACE : fail to get interface. ERROR : %d\n", fd);
+    return fd;
+  }
+  req.ifr_addr.sa_family = AF_INET;
+  strncpy(req.ifr_name, interface, IFNAMSIZ);
+  req.ifr_name[IFNAMSIZ-1] = '\0';
+
+  if (ioctl(fd, SIOCGIFFLAGS, &req) != 0) {
+    error_msg("Is interface %s configured ?\n", interface);
+    close(fd);
+    return -1;
+  }
+  if (!(req.ifr_flags & IFF_UP)) return -1;
+
+  if (oip) {
+    if (ioctl(fd, SIOCGIFADDR, &req) != 0){
+      error_msg("Is interface %s configured ?\n", interface);
+      close(fd);
+      return -1;
+    }
+    ip = (struct sockaddr_in*) &req.ifr_addr;
+    dbg("IP %s\n", inet_ntoa(ip->sin_addr));
+    *oip = ntohl(ip->sin_addr.s_addr);
+  }
+  if (ifindex) {
+    if (ioctl(fd, SIOCGIFINDEX, &req) != 0) {
+      error_msg("IFACE : NO INDEX interface %s\n", interface);
+      close(fd);
+      return -1;
+    }
+    dbg("Adapter index %d\n", req.ifr_ifindex);
+    *ifindex = req.ifr_ifindex;
+  }
+  if (mac) {
+    if (ioctl(fd, SIOCGIFHWADDR, &req) != 0) {
+      error_msg("IFACE : NO MAC interface %s\n", interface);
+      close(fd);
+      return -1;
+    }
+    memcpy(mac, req.ifr_hwaddr.sa_data, 6);
+    dbg("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+  }
+  close(fd);
+  return 0;
+}
+
+static int dhcp_daemon(void)
+{
+  int fd;
+
+  fd = open("/dev/null", O_RDWR);
+  if (fd < 0) fd = open("/", O_RDONLY, 0666);
+  pid_t pid = fork();
+
+  if (pid < 0) {
+    perror_msg("DAEMON: failed to fork");
+    return -1;
+  }
+  if (pid) exit(EXIT_SUCCESS);
+
+  setsid();
+  dup2(fd, 0);
+  dup2(fd, 1);
+  dup2(fd, 2);
+  close(fd);
+
+  return 0;
+}
+
+/*
+ *logs messeges to syslog or console
+ *opening the log is still left with applet.
+ *FIXME: move to more relevent lib. probably libc.c
+ */
+static void infomsg(uint8_t infomode, const char *s, ...)
+{
+  int used;
+  char *msg;
+  va_list p, t;
+
+  if (infomode == LOG_SILENT) return;
+  va_start(p, s);
+  va_copy(t, p);
+  used = vsnprintf(NULL, 0, s, t);
+  used++;
+  va_end(t);
+
+  msg = xmalloc(used);
+  vsnprintf(msg, used, s, p);
+  va_end(p);
+
+  if (infomode & LOG_SYSTEM) syslog(LOG_INFO, "%s", msg);
+  if (infomode & LOG_CONSOLE) printf("%s\n", msg);
+  free(msg);
+}
+
+/*
+ * Writes self PID in file PATH
+ * FIXME: libc implementation only writes in /var/run
+ * this is more generic as some implemenation may provide
+ * arguments to write in specific file. as udhcpd does.
+ */
+static void write_pid(char *path)
+{
+  int pidfile = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+  if (pidfile > 0) {
+    unsigned pid = getpid();
+    char pidbuf[32];
+    int len = sprintf(pidbuf, "%lu\n", (long)pid);
+    write(pidfile, pidbuf, len);
+    close(pidfile);
+  }
+}
+
+/*
+ * String STR to UINT32 conversion strored in VAR
+ */
+static long strtou32(const char *str)
+{
+  char *endptr = NULL;
+  int base = 10;
+  errno=0;
+  if (str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
+    base = 16;
+    str+=2;
+  }
+  long ret_val = strtol(str, &endptr, base);
+  if (errno) return -1;
+  else if (endptr && (*endptr!='\0'||endptr == str)) return -1;
+  return ret_val;
+}
+
+/*
+ * IP String STR to binary data.
+ */
+static int striptovar(const char *str, void *var)
+{
+  in_addr_t addr;
+  if(!str) error_exit("NULL address string.");
+  addr = inet_addr(str);
+  if(addr == -1) error_exit("Wrong address %s.",str );
+  *((uint32_t*)(var)) = (uint32_t)addr;
+  return 0;
+}
+
+/*
+ * String to dhcp option conversion
+ */
+static int strtoopt(const char *str, uint8_t optonly)
+{
+  int size, count;
+  char *option, *valstr, *grp, *tp;
+  long optcode = 0, convtmp;
+  uint16_t flag = 0;
+  uint32_t mask, nip, router;
+
+  if (*str == '\0') return 0;
+  size = sizeof(options_list) / sizeof(option_val_t);
+
+  option = strtok((char*)str, ":");
+  if (!option) return -1;
+
+  dbg("-x option : %s ", option);
+  optcode = strtou32(option);
+
+  if (optcode > 0 && optcode < 256) {         // raw option
+    for (count = 0; count < size; count++) {
+      if ((options_list[count].code & 0X00FF) == optcode) {
+        flag = (options_list[count].code & 0XFF00);
+        break;
+      }
+    }
+    if (count == size) {
+      error_exit("Obsolete OR Unknown Option : %s", option);
+      return -1;
+    }
+  } else {                                    // string option
+    for (count = 0; count < size; count++) {
+      if (strcmp(options_list[count].key, option)==0) {
+        flag = (options_list[count].code & 0XFF00);
+        optcode = (options_list[count].code & 0X00FF);
+        break;
+      }
+    }
+    if (count == size) {
+      error_exit("Obsolete OR Unknown Option : %s", option);
+      return -1;
+    }
+  }
+  if (!flag || !optcode) return -1;
+  if (optonly) return optcode;
+
+  valstr = strtok(NULL, "\n");
+  if (!valstr) {
+    error_exit("option %s has no value defined.\n", option);
+    return -1;
+  }
+  dbg(" value : %-20s \n ", valstr);
+  switch (flag) {
+  case DHCP_NUM32:
+    options_list[count].len = sizeof(uint32_t);
+    options_list[count].val = xmalloc(sizeof(uint32_t));
+    convtmp = strtou32(valstr);
+    if (convtmp < 0) error_exit("Invalid/wrong formated number %s", valstr);
+    convtmp = htonl(convtmp);
+    memcpy(options_list[count].val, &convtmp, sizeof(uint32_t));
+    break;
+  case DHCP_NUM16:
+    options_list[count].len = sizeof(uint16_t);
+    options_list[count].val = xmalloc(sizeof(uint16_t));
+    convtmp = strtou32(valstr);
+    if (convtmp < 0) error_exit("Invalid/malformed number %s", valstr);
+    convtmp = htons(convtmp);
+    memcpy(options_list[count].val, &convtmp, sizeof(uint16_t));
+    break;
+  case DHCP_NUM8:
+    options_list[count].len = sizeof(uint8_t);
+    options_list[count].val = xmalloc(sizeof(uint8_t));
+    convtmp = strtou32(valstr);
+    if (convtmp < 0) error_exit("Invalid/malformed number %s", valstr);
+    memcpy(options_list[count].val, &convtmp, sizeof(uint8_t));
+    break;
+  case DHCP_IP:
+    options_list[count].len = sizeof(uint32_t);
+    options_list[count].val = xmalloc(sizeof(uint32_t));
+    striptovar(valstr, options_list[count].val);
+    break;
+  case DHCP_STRING:
+    options_list[count].len = strlen(valstr);
+    options_list[count].val = strdup(valstr);
+    break;
+  case DHCP_IPLIST:
+    while(valstr){
+      options_list[count].val = xrealloc(options_list[count].val, options_list[count].len + sizeof(uint32_t));
+      striptovar(valstr, ((uint8_t*)options_list[count].val)+options_list[count].len);
+      options_list[count].len += sizeof(uint32_t);
+      valstr = strtok(NULL," \t");
+    }
+    break;
+  case DHCP_STRLST: //FIXME: do smthing.
+  case DHCP_IPPLST:
+    break;
+  case DHCP_STCRTS:
+    /* Option binary format:
+     * mask [one byte, 0..32]
+     * ip [0..4 bytes depending on mask]
+     * router [4 bytes]
+     * may be repeated
+     * staticroutes 10.0.0.0/8 10.127.0.1, 10.11.12.0/24 10.11.12.1
+     */
+    grp = strtok(valstr, ",");;
+    while(grp){
+      while(*grp == ' ' || *grp == '\t') grp++;
+      tp = strchr(grp, '/');
+      if (!tp) error_exit("malformed static route option");
+      *tp = '\0';
+      mask = strtol(++tp, &tp, 10);
+      if (striptovar(grp, (uint8_t*)&nip)<0) error_exit("malformed static route option");
+      while(*tp == ' ' || *tp == '\t' || *tp == '-') tp++;
+      if (striptovar(tp, (uint8_t*)&router)<0) error_exit("malformed static route option");
+      options_list[count].val = xrealloc(options_list[count].val, options_list[count].len + 1 + mask/8 + 4);
+      memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &mask, 1);
+      options_list[count].len += 1;
+      memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &nip, mask/8);
+      options_list[count].len += mask/8;
+      memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &router, 4);
+      options_list[count].len += 4;
+      tp = NULL;
+      grp = strtok(NULL, ",");
+    }
+    break;
+  }
+  return 0;
+}
+
+/*
+ * Cretes environment pointers from RES to use in script
+ */
+static int fill_envp(dhcpc_result_t *res)
+{
+  int size, count, ret = -1;
+  struct in_addr temp;
+
+  ret = setenv("interface", state->iface, 1);
+  if (!res) return ret;
+  if (res->ipaddr.s_addr) {
+      temp.s_addr = htonl(res->ipaddr.s_addr);
+      ret = setenv("ip", inet_ntoa(temp), 1);
+      if (ret) return ret;
+  }
+  size = sizeof(options_list) / sizeof(option_val_t);
+  if (msgopt_list) {
+    for (count = 0; count < size; count++) {
+        if ((msgopt_list[count].len == 0) || (msgopt_list[count].val == NULL)) continue;
+        ret = setenv(msgopt_list[count].key, (char*)msgopt_list[count].val, 1);
+        if (ret) return ret;
+      }
+  }
+  return ret;
+}
+
+/*
+ * Executes Script NAME.
+ */
+static void run_script(dhcpc_result_t *res, const char *name)
+{
+  char *argv[3];
+  char *script;
+  struct stat sts;
+
+  script = flag_get(FLAG_s, TT.script, "/usr/share/udhcpc/default.script");
+  if (stat(script, &sts) == -1 && errno == ENOENT) return;
+  if (fill_envp(res)) {
+    dbg("Failed to create environment variables.");
+    return;
+  }
+  dbg("Executing %s %s\n", script, name);
+  argv[0] = (char*) script;
+  argv[1] = (char*) name;
+  argv[2] = NULL;
+  volatile int error = 0;
+  pid_t pid;
+  fflush(NULL);
+  pid = vfork();
+  if (pid < 0) {
+    dbg("Fork failed.\n");
+    return;
+  }
+  if (!pid) {
+    execvp(argv[0], argv);
+    error = errno;
+    _exit(111);
+  }
+  if (error) {
+    waitpid(pid, NULL,0);
+    errno = error;
+    perror_msg("script exec failed");
+  }
+  dbg("script complete.\n");
+}
+
+/*
+ * returns a randome ID
+ */
+static uint32_t getxid(void)
+{
+  struct timeval t1;
+  gettimeofday(&t1, NULL);
+  srand(t1.tv_usec * t1.tv_sec);
+  return rand();
+}
+
+/*
+ * opens socket in raw mode.
+ */
+static int mode_raw(void)
+{
+  state->mode = MODE_OFF;
+  struct sockaddr_ll sock;
+
+  if (state->sockfd > 0) close(state->sockfd);
+  dbg("Opening raw socket on ifindex %d\n", state->ifindex);
+
+  state->sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+  if (state->sockfd < 0) {
+    dbg("MODE RAW : socket fail ERROR : %d\n", state->sockfd);
+    return -1;
+  }
+  dbg("Got raw socket fd %d\n", state->sockfd);
+  memset(&sock, 0, sizeof(sock));
+  sock.sll_family = AF_PACKET;
+  sock.sll_protocol = htons(ETH_P_IP);
+  sock.sll_ifindex = state->ifindex;
+
+  if (bind(state->sockfd, (struct sockaddr *) &sock, sizeof(sock)) != 0 ) {
+    dbg("MODE RAW : bind fail.\n");
+    close(state->sockfd);
+    return -1;
+  }
+  state->mode = MODE_RAW;
+  if (setsockopt(state->sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0)
+    dbg("MODE RAW : filter attach fail.\n");
+
+  dbg("MODE RAW : success\n");
+  return 0;
+}
+
+/*
+ * opens UDP socket
+ */
+static int mode_app(void)
+{
+  struct sockaddr_in addr;
+  struct ifreq ifr;
+
+  state->mode = MODE_OFF;
+  if (state->sockfd > 0) close(state->sockfd);
+
+  dbg("Opening listen socket on *:%d %s\n", DHCPC_CLIENT_PORT, state->iface);
+  state->sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (state->sockfd < 0) {
+    dbg("MODE APP : socket fail ERROR: %d\n", state->sockfd);
+    return -1;
+  }
+  setsockopt(state->sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
+  if (setsockopt(state->sockfd, SOL_SOCKET, SO_BROADCAST, &set, sizeof(set)) == -1) {
+    dbg("MODE APP : brodcast failed.\n");
+    close(state->sockfd);
+    return -1;
+  }
+  strncpy(ifr.ifr_name, state->iface, IFNAMSIZ);
+  setsockopt(state->sockfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(DHCPC_CLIENT_PORT);
+  addr.sin_addr.s_addr = INADDR_ANY ;
+
+  if (bind(state->sockfd, (struct sockaddr *) &addr, sizeof(addr))) {
+    close(state->sockfd);
+    dbg("MODE APP : bind failed.\n");
+    return -1;
+  }
+  state->mode = MODE_APP;
+  dbg("MODE APP : success\n");
+  return 0;
+}
+
+/*
+ * Reads from raw socket.
+ */
+static int read_raw(void)
+{
+  int bytes = 0;
+  dhcp_raw_t packet;
+  uint16_t check;
+
+  memset(&packet, 0, sizeof(packet));
+  bytes = read(state->sockfd, &packet, sizeof(packet));
+  if (bytes < 0) {
+    dbg("\tPacket read error, ignoring\n");
+    return bytes;
+  }
+  if (bytes < (int) (sizeof(packet.iph) + sizeof(packet.udph))) {
+    dbg("\tPacket is too short, ignoring\n");
+    return -2;
+  }
+  if (bytes < ntohs(packet.iph.tot_len)) {
+    dbg("\tOversized packet, ignoring\n");
+    return -2;
+  }
+  /* ignore any extra garbage bytes */
+  bytes = ntohs(packet.iph.tot_len);
+  /* make sure its the right packet for us, and that it passes sanity checks */
+  if (packet.iph.protocol != IPPROTO_UDP || packet.iph.version != IPVERSION
+   || packet.iph.ihl != (sizeof(packet.iph) >> 2)
+   || packet.udph.dest != htons(DHCPC_CLIENT_PORT)
+   || ntohs(packet.udph.len) != (uint16_t)(bytes - sizeof(packet.iph))) {
+    dbg("\tUnrelated/bogus packet, ignoring\n");
+    return -2;
+  }
+  /* verify IP checksum */
+  check = packet.iph.check;
+  packet.iph.check = 0;
+  if (check != udhcp_checksum(&packet.iph, sizeof(packet.iph))) {
+    dbg("\tBad IP header checksum, ignoring\n");
+    return -2;
+  }
+  memset(&packet.iph, 0, ((size_t) &((struct iphdr *)0)->protocol));
+  packet.iph.tot_len = packet.udph.len;
+  check = packet.udph.check;
+  packet.udph.check = 0;
+  if (check && check != udhcp_checksum(&packet, bytes)) {
+    dbg("\tPacket with bad UDP checksum received, ignoring\n");
+    return -2;
+  }
+  memcpy(&state->pdhcp, &packet.dhcp, bytes - (sizeof(packet.iph) + sizeof(packet.udph)));
+  if (state->pdhcp.cookie != htonl(DHCP_MAGIC)) {
+    dbg("\tPacket with bad magic, ignoring\n");
+    return -2;
+  }
+  return bytes - sizeof(packet.iph) - sizeof(packet.udph);
+}
+
+/*
+ * Reads from UDP socket
+ */
+static int read_app(void)
+{
+  int ret;
+
+  memset(&state->pdhcp, 0, sizeof(dhcp_msg_t));
+  ret = read(state->sockfd, &state->pdhcp, sizeof(dhcp_msg_t));
+  if (ret < 0) {
+    dbg("Packet read error, ignoring\n");
+    return ret; /* returns -1 */
+  }
+  if (state->pdhcp.cookie != htonl(DHCP_MAGIC)) {
+    dbg("Packet with bad magic, ignoring\n");
+    return -2;
+  }
+  return ret;
+}
+
+/*
+ * Sends data through raw socket.
+ */
+static int send_raw(void)
+{
+  struct sockaddr_ll dest_sll;
+  dhcp_raw_t packet;
+  unsigned padding;
+  int fd, result = -1;
+
+  memset(&packet, 0, sizeof(dhcp_raw_t));
+  memcpy(&packet.dhcp, &state->pdhcp, sizeof(dhcp_msg_t));
+
+  fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+  if (fd < 0) {
+    dbg("SEND RAW: socket failed\n");
+    return -1;
+  }
+  memset(&dest_sll, 0, sizeof(dest_sll));
+  dest_sll.sll_family = AF_PACKET;
+  dest_sll.sll_protocol = htons(ETH_P_IP);
+  dest_sll.sll_ifindex = state->ifindex;
+  dest_sll.sll_halen = 6;
+  memcpy(dest_sll.sll_addr, bmacaddr , 6);
+
+  if (bind(fd, (struct sockaddr *) &dest_sll, sizeof(dest_sll)) < 0) {
+    dbg("SEND RAW: bind failed\n");
+    close(fd);
+    return -1;
+  }
+  padding = 308 - 1 - dhcp_opt_size(state->pdhcp.options);
+  packet.iph.protocol = IPPROTO_UDP;
+  packet.iph.saddr = INADDR_ANY;
+  packet.iph.daddr = INADDR_BROADCAST;
+  packet.udph.source = htons(DHCPC_CLIENT_PORT);
+  packet.udph.dest = htons(DHCPC_SERVER_PORT);
+  packet.udph.len = htons(sizeof(dhcp_raw_t) - sizeof(struct iphdr) - padding);
+  packet.iph.tot_len = packet.udph.len;
+  packet.udph.check = udhcp_checksum(&packet, sizeof(dhcp_raw_t) - padding);
+  packet.iph.tot_len = htons(sizeof(dhcp_raw_t) - padding);
+  packet.iph.ihl = sizeof(packet.iph) >> 2;
+  packet.iph.version = IPVERSION;
+  packet.iph.ttl = IPDEFTTL;
+  packet.iph.check = udhcp_checksum(&packet.iph, sizeof(packet.iph));
+
+  result = sendto(fd, &packet, sizeof(dhcp_raw_t) - padding, 0,
+      (struct sockaddr *) &dest_sll, sizeof(dest_sll));
+
+  close(fd);
+  if (result < 0) dbg("SEND RAW: PACKET send error\n");
+  return result;
+}
+
+/*
+ * Sends data through UDP socket.
+ */
+static int send_app(void)
+{
+  struct sockaddr_in cli;
+  int fd, ret = -1;
+
+  fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (fd < 0) {
+    dbg("SEND APP: sock failed.\n");
+    return ret;
+  }
+  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
+
+  memset(&cli, 0, sizeof(cli));
+  cli.sin_family = AF_INET;
+  cli.sin_port = htons(DHCPC_CLIENT_PORT);
+  cli.sin_addr.s_addr = state->pdhcp.ciaddr;
+  if (bind(fd, (struct sockaddr *)&cli, sizeof(cli)) == -1) {
+    dbg("SEND APP: bind failed.\n");
+    goto error_fd;
+  }
+  memset(&cli, 0, sizeof(cli));
+  cli.sin_family = AF_INET;
+  cli.sin_port = htons(DHCPC_SERVER_PORT);
+  cli.sin_addr.s_addr = state->serverid.s_addr;
+  if (connect(fd, (struct sockaddr *)&cli, sizeof(cli)) == -1) {
+    dbg("SEND APP: connect failed.\n");
+    goto error_fd;
+  }
+  int padding = 308 - 1 - dhcp_opt_size(state->pdhcp.options);
+  ret = write(fd, &state->pdhcp, sizeof(dhcp_msg_t) - padding);
+  if (ret < 0) {
+    dbg("SEND APP: write failed error %d\n", ret);
+    goto error_fd;
+  }
+  dbg("SEND APP: write success wrote %d\n", ret);
+error_fd:
+  close(fd);
+  return ret;
+}
+
+/*
+ * Generic signal handler real handling is done in main funcrion.
+ */
+static void signal_handler(int sig)
+{
+  unsigned char ch = sig;
+  if (write(sigfd.wr, &ch, 1) != 1) dbg("can't send signal\n");
+}
+
+/*
+ * signal setup for SIGUSR1 SIGUSR2 SIGTERM
+ */
+static int setup_signal()
+{
+  if (pipe((int *)&sigfd) < 0) {
+    dbg("signal pipe failed\n");
+    return -1;
+  }
+  fcntl(sigfd.wr , F_SETFD, FD_CLOEXEC);
+  fcntl(sigfd.rd , F_SETFD, FD_CLOEXEC);
+  int flags = fcntl(sigfd.wr, F_GETFL);
+  fcntl(sigfd.wr, F_SETFL, flags | O_NONBLOCK);
+  signal(SIGUSR1, signal_handler);
+  signal(SIGUSR2, signal_handler);
+  signal(SIGTERM, signal_handler);
+
+  return 0;
+}
+
+/*
+ * adds client id to dhcp packet
+ */
+static uint8_t *dhcpc_addclientid(uint8_t *optptr)
+{
+  *optptr++ = DHCP_OPTION_CLIENTID;
+  *optptr++ = 7;
+  *optptr++ = 1;
+  memcpy(optptr, &state->macaddr, 6);
+  return optptr + 6;
+  return optptr;
+}
+
+/*
+ * adds messege type to dhcp packet
+ */
+static uint8_t *dhcpc_addmsgtype(uint8_t *optptr, uint8_t type)
+{
+  *optptr++ = DHCP_OPTION_MSG_TYPE;
+  *optptr++ = 1;
+  *optptr++ = type;
+  return optptr;
+}
+
+/*
+ * adds max size to dhcp packet
+ */
+static uint8_t *dhcpc_addmaxsize(uint8_t *optptr, uint16_t size)
+{
+  *optptr++ = DHCP_OPTION_MAX_SIZE;
+  *optptr++ = 2;
+  memcpy(optptr, &size, 2);
+  return optptr + 2;
+}
+
+static uint8_t *dhcpc_addstropt(uint8_t *optptr, uint8_t opcode, char* str, int len)
+{
+  *optptr++ = opcode;
+  *optptr++ = len;
+  memcpy(optptr, str, len);
+  return optptr + len;
+}
+
+/*
+ * adds server id to dhcp packet.
+ */
+static uint8_t *dhcpc_addserverid(struct in_addr *serverid, uint8_t *optptr)
+{
+  *optptr++ = DHCP_OPTION_SERVER_ID;
+  *optptr++ = 4;
+  memcpy(optptr, &serverid->s_addr, 4);
+  return optptr + 4;
+}
+
+/*
+ * adds requested ip address to dhcp packet.
+ */
+static uint8_t *dhcpc_addreqipaddr(struct in_addr *ipaddr, uint8_t *optptr)
+{
+  *optptr++ = DHCP_OPTION_REQ_IPADDR;
+  *optptr++ = 4;
+  memcpy(optptr, &ipaddr->s_addr, 4);
+  return optptr + 4;
+}
+
+/*
+ * adds hostname to dhcp packet.
+ */
+static uint8_t *dhcpc_addfdnname(uint8_t *optptr,  char *hname)
+{
+  int size = strlen(hname);
+  *optptr++ = DHCP_OPTION_FQDN;
+  *optptr++ = size + 3;
+  *optptr++ = 0x1;  //flags
+  optptr += 2;      // two blank bytes
+  strncpy((char*)optptr, hname, size); // name
+  return optptr + size;
+}
+
+/*
+ * adds request options using -o,-O flag to dhcp packet
+ */
+static uint8_t *dhcpc_addreqoptions(uint8_t *optptr)
+{
+  uint8_t *len;
+
+  *optptr++ = DHCP_OPTION_REQ_LIST;
+  len = optptr;
+  *len = 0;
+  optptr++;
+
+  if (!flag_chk(FLAG_o)) {
+    *len = 4;
+    *optptr++ = DHCP_OPTION_SUBNET_MASK;
+    *optptr++ = DHCP_OPTION_ROUTER;
+    *optptr++ = DHCP_OPTION_DNS_SERVER;
+    *optptr++ = DHCP_OPTION_BROADCAST;
+  }
+  if (flag_chk(FLAG_O)) {
+    memcpy(optptr++, raw_opt, raw_optcount);
+    *len += raw_optcount;
+  }
+  return optptr;
+}
+
+static uint8_t *dhcpc_addend(uint8_t *optptr)
+{
+  *optptr++ = DHCP_OPTION_END;
+  return optptr;
+}
+
+/*
+ * Sets values of -x options in dhcp discover and request packet.
+ */
+static uint8_t* set_xopt(uint8_t *optptr)
+{
+  int count;
+  int size = sizeof(options_list) / sizeof(option_val_t);
+  for (count = 0; count < size; count++) {
+    if ((options_list[count].len == 0) || (options_list[count].val == NULL)) continue;
+    *optptr++ = (uint8_t) (options_list[count].code & 0x00FF);
+    *optptr++ = (uint8_t) options_list[count].len;
+    memcpy(optptr, options_list[count].val, options_list[count].len);
+    optptr += options_list[count].len;
+  }
+  return optptr;
+}
+
+static uint32_t get_option_serverid (uint8_t *opt, dhcpc_result_t *presult)
+{
+  uint32_t var = 0;
+  while (*opt != DHCP_OPTION_SERVER_ID) {
+    if (*opt == DHCP_OPTION_END) return var;
+    opt += opt[1] + 2;
+  }
+  memcpy(&var, opt+2, sizeof(uint32_t));
+  state->serverid.s_addr = var;
+  presult->serverid.s_addr = state->serverid.s_addr;
+  presult->serverid.s_addr = ntohl(presult->serverid.s_addr);
+  return var;
+}
+
+static uint8_t get_option_msgtype(uint8_t *opt)
+{
+  uint32_t var = 0;
+  while (*opt != DHCP_OPTION_MSG_TYPE) {
+    if (*opt == DHCP_OPTION_END) return var;
+    opt += opt[1] + 2;
+  }
+  memcpy(&var, opt+2, sizeof(uint8_t));
+  return var;
+}
+
+static uint8_t get_option_lease(uint8_t *opt, dhcpc_result_t *presult)
+{
+  uint32_t var = 0;
+  while (*opt != DHCP_OPTION_LEASE_TIME) {
+    if (*opt == DHCP_OPTION_END) return var;
+    opt += opt[1] + 2;
+  }
+  memcpy(&var, opt+2, sizeof(uint32_t));
+  var = htonl(var);
+  presult->lease_time = var;
+  return var;
+}
+
+
+/*
+ * sends dhcp msg of MSGTYPE
+ */
+static int dhcpc_sendmsg(int msgtype)
+{
+  uint8_t *pend;
+  struct in_addr rqsd;
+  char *vendor;
+
+  /* Create the common message header settings */
+  memset(&state->pdhcp, 0, sizeof(dhcp_msg_t));
+  state->pdhcp.op = DHCP_REQUEST;
+  state->pdhcp.htype = DHCP_HTYPE_ETHERNET;
+  state->pdhcp.hlen = 6;
+  state->pdhcp.xid = xid;
+  memcpy(state->pdhcp.chaddr, state->macaddr, 6);
+  memset(&state->pdhcp.chaddr[6], 0, 10);
+  state->pdhcp.cookie = htonl(DHCP_MAGIC);;
+
+  /* Add the common header options */
+  pend = state->pdhcp.options;
+  pend = dhcpc_addmsgtype(pend, msgtype);
+
+  if (!flag_chk(FLAG_C)) pend = dhcpc_addclientid(pend);
+  /* Handle the message specific settings */
+  switch (msgtype) {
+  case DHCPDISCOVER: /* Broadcast DISCOVER message to all servers */
+    state->pdhcp.flags = htons(BOOTP_BROADCAST); /*  Broadcast bit. */
+    if (flag_chk(FLAG_r)) {
+      inet_aton(TT.req_ip, &rqsd);
+      pend = dhcpc_addreqipaddr(&rqsd, pend);
+    }
+    pend = dhcpc_addmaxsize(pend, htons(sizeof(dhcp_raw_t)));
+    vendor = flag_get(FLAG_V, TT.vendor_cls, "udhcp 0.1.20\0");
+    pend = dhcpc_addstropt(pend, DHCP_OPTION_VENDOR, vendor, strlen(vendor));
+    if (flag_chk(FLAG_H)) pend = dhcpc_addstropt(pend, DHCP_OPTION_HOST_NAME, TT.hostname, strlen(TT.hostname));
+    if (flag_chk(FLAG_F)) pend = dhcpc_addfdnname(pend, TT.fdn_name);
+    if ((!flag_chk(FLAG_o)) || flag_chk(FLAG_O)) pend = dhcpc_addreqoptions(pend);
+    if (flag_chk(FLAG_x)) pend = set_xopt(pend);
+    break;
+  case DHCPREQUEST: /* Send REQUEST message to the server that sent the *first* OFFER */
+    state->pdhcp.flags = htons(BOOTP_BROADCAST); /*  Broadcast bit. */
+    if (state->status == STATE_RENEWING) memcpy(&state->pdhcp.ciaddr, &state->ipaddr.s_addr, 4);
+    pend = dhcpc_addmaxsize(pend, htons(sizeof(dhcp_raw_t)));
+    rqsd.s_addr = htonl(server);
+    pend = dhcpc_addserverid(&rqsd, pend);
+    pend = dhcpc_addreqipaddr(&state->ipaddr, pend);
+    vendor = flag_get(FLAG_V, TT.vendor_cls, "udhcp 0.1.20\0");
+    pend = dhcpc_addstropt(pend, DHCP_OPTION_VENDOR, vendor, strlen(vendor));
+    if (flag_chk(FLAG_H)) pend = dhcpc_addstropt(pend, DHCP_OPTION_HOST_NAME, TT.hostname, strlen(TT.hostname));
+    if (flag_chk(FLAG_F)) pend = dhcpc_addfdnname(pend, TT.fdn_name);
+    if ((!flag_chk(FLAG_o)) || flag_chk(FLAG_O)) pend = dhcpc_addreqoptions(pend);
+    if (flag_chk(FLAG_x)) pend = set_xopt(pend);
+    break;
+  case DHCPRELEASE: /* Send RELEASE message to the server. */
+    memcpy(&state->pdhcp.ciaddr, &state->ipaddr.s_addr, 4);
+    rqsd.s_addr = htonl(server);
+    pend = dhcpc_addserverid(&rqsd, pend);
+    break;
+  default:
+    return -1;
+  }
+  pend = dhcpc_addend(pend);
+
+  if (state->mode == MODE_APP) return send_app();
+  return send_raw();
+}
+
+/*
+ * parses options from received dhcp packet at OPTPTR and
+ * stores result in PRESULT or MSGOPT_LIST
+ */
+static uint8_t dhcpc_parseoptions(dhcpc_result_t *presult, uint8_t *optptr)
+{
+  uint8_t type = 0, *options, overloaded = 0;;
+  uint16_t flag = 0;
+  uint32_t convtmp = 0;
+  int size, count, optlen;
+  char *dest;
+  char *pfx;
+  struct in_addr addr;
+
+  size = sizeof(options_list) / sizeof(option_val_t);
+
+  if (flag_chk(FLAG_x)) {
+    if(msgopt_list){
+      for (count = 0; count < size; count++){
+        if(msgopt_list[count].val) free(msgopt_list[count].val);
+        msgopt_list[count].val = NULL;
+        msgopt_list[count].len = 0;
+      }
+    } else {
+     msgopt_list = xmalloc(sizeof(options_list));
+     memcpy(msgopt_list, options_list, sizeof(options_list));
+     for (count = 0; count < size; count++) {
+         msgopt_list[count].len = 0;
+         msgopt_list[count].val = NULL;
+     }
+    }
+  } else {
+    msgopt_list = options_list;
+    for (count = 0; count < size; count++) {
+      msgopt_list[count].len = 0;
+      if(msgopt_list[count].val) free(msgopt_list[count].val);
+      msgopt_list[count].val = NULL;
+    }
+  }
+
+  while (*optptr != DHCP_OPTION_END) {
+    while (*optptr == DHCP_OPTION_PADDING) optptr++;
+    if (*optptr == DHCP_OPTION_OVERLOAD) {
+      overloaded = optptr[2];
+      optptr += optptr[1] + 2;
+      continue;
+    }
+    for (count = 0, flag = 0; count < size; count++) {
+      if ((msgopt_list[count].code & 0X00FF) == *optptr) {
+        flag = (msgopt_list[count].code & 0XFF00);
+        break;
+      }
+    }
+    switch (flag) {
+    case DHCP_NUM32:
+      memcpy(&convtmp, &optptr[2], sizeof(uint32_t));
+      convtmp = htonl(convtmp);
+      sprintf(toybuf, "%u", convtmp);
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    case DHCP_NUM16:
+      memcpy(&convtmp, &optptr[2], sizeof(uint16_t));
+      convtmp = htons(convtmp);
+      sprintf(toybuf, "%u", convtmp);
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    case DHCP_NUM8:
+      memcpy(&convtmp, &optptr[2], sizeof(uint8_t));
+      sprintf(toybuf, "%u", convtmp);
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    case DHCP_IP:
+      memcpy(&convtmp, &optptr[2], sizeof(uint32_t));
+      addr.s_addr = convtmp;
+      sprintf(toybuf, "%s", inet_ntoa(addr));
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    case DHCP_STRING:
+      sprintf(toybuf, "%.*s", optptr[1], &optptr[2]);
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    case DHCP_IPLIST:
+      optlen = optptr[1];
+      dest = toybuf;
+      while (optlen) {
+        memcpy(&convtmp, &optptr[2], sizeof(uint32_t));
+        addr.s_addr = convtmp;
+        dest += sprintf(dest, "%s ", inet_ntoa(addr));
+        optlen -= 4;
+      }
+      *(dest - 1) = '\0';
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    case DHCP_STRLST: //FIXME: do smthing.
+    case DHCP_IPPLST:
+      break;
+    case DHCP_STCRTS:
+      pfx = "";
+      dest = toybuf;
+      options = &optptr[2];
+      optlen = optptr[1];
+
+      while (optlen >= 1 + 4) {
+        uint32_t nip = 0;
+        unsigned mask;
+        int bytes;
+        uint8_t *p_tmp;
+
+        mask = *options;
+        if (mask > 32) break;
+        optlen--;
+        p_tmp = (void*) &nip;
+        bytes = (mask + 7) / 8;
+        while (--bytes >= 0) {
+          *p_tmp++ = *options++;
+          optlen--;
+        }
+        if (optlen < 4) break;
+        dest += sprintf(dest, "%s%u.%u.%u.%u", pfx, ((uint8_t*) &nip)[0],
+            ((uint8_t*) &nip)[1], ((uint8_t*) &nip)[2], ((uint8_t*) &nip)[3]);
+        pfx = " ";
+        dest += sprintf(dest, "/%u ", mask);
+        dest += sprintf(dest, "%u.%u.%u.%u", options[0], options[1], options[2], options[3]);
+        options += 4;
+        optlen -= 4;
+      }
+      msgopt_list[count].val = strdup(toybuf);
+      msgopt_list[count].len = strlen(toybuf);
+      break;
+    default: break;
+    }
+    optptr += optptr[1] + 2;
+  }
+  if ((overloaded == 1) || (overloaded == 3)) dhcpc_parseoptions(presult, optptr);
+  if ((overloaded == 2) || (overloaded == 3)) dhcpc_parseoptions(presult, optptr);
+  return type;
+}
+
+/*
+ * parses recvd messege to check that it was for us.
+ */
+static uint8_t dhcpc_parsemsg(dhcpc_result_t *presult)
+{
+  if (state->pdhcp.op == DHCP_REPLY
+      && memcmp(state->pdhcp.chaddr, state->macaddr, 6) == 0
+      && memcmp(&state->pdhcp.xid, &xid, sizeof(xid)) == 0) {
+    memcpy(&presult->ipaddr.s_addr, &state->pdhcp.yiaddr, 4);
+    presult->ipaddr.s_addr = ntohl(presult->ipaddr.s_addr);
+    return get_option_msgtype(state->pdhcp.options);
+  }
+  return 0;
+}
+
+/*
+ * Sends a IP renew request.
+ */
+static void renew(void)
+{
+  infomsg(infomode, "Performing a DHCP renew");
+  switch (state->status) {
+  case STATE_INIT:
+    break;
+  case STATE_BOUND:
+    mode_raw();
+  case STATE_RENEWING:    /* FALLTHROUGH */
+  case STATE_REBINDING:   /* FALLTHROUGH */
+    state->status = STATE_RENEW_REQUESTED;
+    break;
+  case STATE_RENEW_REQUESTED:
+    run_script(NULL, "deconfig");
+  case STATE_REQUESTING:           /* FALLTHROUGH */
+  case STATE_RELEASED:             /* FALLTHROUGH */
+    mode_raw();
+    state->status = STATE_INIT;
+    break;
+  default: break;
+  }
+}
+
+/*
+ * Sends a IP release request.
+ */
+static void release(void)
+{
+  char buffer[sizeof("255.255.255.255\0")];
+  struct in_addr temp_addr;
+
+  mode_app();
+  /* send release packet */
+  if (state->status == STATE_BOUND || state->status == STATE_RENEWING || state->status == STATE_REBINDING) {
+    temp_addr.s_addr = htonl(server);
+    strncpy(buffer, inet_ntoa(temp_addr), sizeof(buffer));
+    temp_addr.s_addr = state->ipaddr.s_addr;
+    infomsg( infomode, "Unicasting a release of %s to %s", inet_ntoa(temp_addr), buffer);
+    dhcpc_sendmsg(DHCPRELEASE);
+    run_script(NULL, "deconfig");
+  }
+  infomsg(infomode, "Entering released state");
+  close(state->sockfd);
+  state->sockfd = -1;
+  state->mode = MODE_OFF;
+  state->status = STATE_RELEASED;
+}
+
+static void free_option_stores(void)
+{
+  int count, size = sizeof(options_list) / sizeof(option_val_t);
+  for (count = 0; count < size; count++)
+    if (options_list[count].val) free(options_list[count].val);
+  if(flag_chk(FLAG_x)){
+    for (count = 0; count < size; count++)
+        if (msgopt_list[count].val) free(msgopt_list[count].val);
+    free(msgopt_list);
+  }
+}
+
+void udhcpc_main(void)
+{
+  struct timeval tv;
+  int retval, bufflen = 0;
+  dhcpc_result_t result;
+  uint8_t packets = 0, retries = 0;
+  uint32_t timeout = 0, waited = 0;
+  fd_set rfds;
+
+  xid = 0;
+
+  setlinebuf(stdout);
+  dbg = dummy;
+  if (flag_chk(FLAG_v)) dbg = xprintf;
+  if (flag_chk(FLAG_p)) write_pid(TT.pidfile);
+  retries = flag_get(FLAG_t, TT.retries, 3);
+  if (flag_chk(FLAG_S)) {
+      openlog("UDHCPC :", LOG_PID, LOG_DAEMON);
+      infomode |= LOG_SYSTEM;
+  }
+  infomsg(infomode, "udhcpc (v0.1.20) started");
+  if (flag_chk(FLAG_O)) {
+    while (TT.req_opt) {
+      raw_opt[raw_optcount] = (uint8_t) strtoopt(TT.req_opt->arg, 1);
+      raw_optcount++;
+      TT.req_opt = TT.req_opt->next;
+    }
+  }
+  if (flag_chk(FLAG_x)) {
+    while (TT.pkt_opt) {
+      (void) strtoopt(TT.pkt_opt->arg, 0);
+      TT.pkt_opt = TT.pkt_opt->next;
+    }
+  }
+  memset(&result, 0, sizeof(dhcpc_result_t));
+  state = (dhcpc_state_t*) xmalloc(sizeof(dhcpc_state_t));
+  memset(state, 0, sizeof(dhcpc_state_t));
+  state->iface = flag_get(FLAG_i, TT.iface, "eth0");
+
+  if (get_interface(state->iface, &state->ifindex, NULL, state->macaddr) != 0)
+    perror_exit("Failed to get interface %s", state->iface);
+
+  run_script(NULL, "deconfig");
+  setup_signal();
+  state->status = STATE_INIT;
+  mode_raw();
+  fcntl(state->sockfd, F_SETFD, FD_CLOEXEC);
+
+  for (;;) {
+    FD_ZERO(&rfds);
+    if (state->sockfd >= 0) FD_SET(state->sockfd, &rfds);
+    FD_SET(sigfd.rd, &rfds);
+    tv.tv_sec = timeout - waited;
+    tv.tv_usec = 0;
+    retval = 0;
+
+    int maxfd = (sigfd.rd > state->sockfd)? sigfd.rd : state->sockfd;
+    dbg("select wait ....\n");
+    uint32_t timestmp = time(NULL);
+    retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
+    if (retval < 0) {
+      if (errno == EINTR) {
+        waited += (unsigned) time(NULL) - timestmp;
+        continue;
+      }
+      perror_exit("Error in select");
+      continue;
+    }
+    if (retval == 0) { /* Timed out */
+      if (get_interface(state->iface, &state->ifindex, NULL, state->macaddr) != 0)
+        error_exit("Interface lost %s\n", state->iface);
+
+      switch (state->status) {
+      case STATE_INIT:
+        if (packets < retries) {
+          if (packets == 0) xid = getxid();
+          run_script(NULL, "deconfig");
+          infomsg(infomode, "Sending discover...");
+          dhcpc_sendmsg(DHCPDISCOVER);
+          server = 0;
+          timeout = flag_get(FLAG_T, TT.timeout, 3);
+          waited = 0;
+          packets++;
+          continue;
+        }
+lease_fail:
+        run_script(NULL,"leasefail");
+        if (flag_chk(FLAG_n)) {
+          infomsg(infomode, "Lease failed. Exiting");
+          goto ret_with_sockfd;
+        }
+        if (flag_chk(FLAG_b)) {
+          infomsg(infomode, "Lease failed. Going Daemon mode");
+          dhcp_daemon();
+          if (flag_chk(FLAG_p)) write_pid(TT.pidfile);
+          toys.optflags &= ~FLAG_b;
+          toys.optflags |= FLAG_f;
+        }
+        timeout = flag_get(FLAG_A, TT.tryagain, 20);
+        waited = 0;
+        packets = 0;
+        continue;
+      case STATE_REQUESTING:
+        if (packets < retries) {
+          memcpy(&state->ipaddr.s_addr,&state->pdhcp.yiaddr, 4);
+          dhcpc_sendmsg(DHCPREQUEST);
+          infomsg(infomode, "Sending select for %d.%d.%d.%d...",
+              (result.ipaddr.s_addr >> 24) & 0xff, (result.ipaddr.s_addr >> 16) & 0xff, (result.ipaddr.s_addr >> 8) & 0xff, (result.ipaddr.s_addr) & 0xff);
+          timeout = flag_get(FLAG_T, TT.timeout, 3);
+          waited = 0;
+          packets++;
+          continue;
+        }
+        mode_raw();
+        state->status = STATE_INIT;
+        goto lease_fail;
+      case STATE_BOUND:
+        state->status = STATE_RENEWING;
+        dbg("Entering renew state\n");
+        /* FALLTHROUGH */
+      case STATE_RENEW_REQUESTED:   /* FALLTHROUGH */
+      case STATE_RENEWING:
+renew_requested:
+        if (timeout > 60) {
+          dhcpc_sendmsg(DHCPREQUEST);
+          timeout >>= 1;
+          waited = 0;
+          continue;
+        }
+        dbg("Entering rebinding state\n");
+        state->status = STATE_REBINDING;
+        /* FALLTHROUGH */
+      case STATE_REBINDING:
+        mode_raw();
+        if (timeout > 0) {
+          dhcpc_sendmsg(DHCPREQUEST);
+          timeout >>= 1;
+          waited = 0;
+          continue;
+        }
+        infomsg(infomode, "Lease lost, entering INIT state");
+        run_script(NULL, "deconfig");
+        state->status = STATE_INIT;
+        timeout = 0;
+        waited = 0;
+        packets = 0;
+        continue;
+      default: break;
+      }
+      timeout = INT_MAX;
+      waited = 0;
+      continue;
+    }
+    if (FD_ISSET(sigfd.rd, &rfds)) { /* Some Activity on RDFDs : is signal */
+      unsigned char sig;
+      if (read(sigfd.rd, &sig, 1) != 1) {
+        dbg("signal read failed.\n");
+        continue;
+      }
+      switch (sig) {
+      case SIGUSR1:
+        infomsg(infomode, "Received SIGUSR1");
+        renew();
+        packets = 0;
+        waited = 0;
+        if (state->status == STATE_RENEW_REQUESTED) goto renew_requested;
+        if (state->status == STATE_INIT) timeout = 0;
+        continue;
+      case SIGUSR2:
+        infomsg(infomode, "Received SIGUSR2");
+        release();
+        timeout = INT_MAX;
+        waited = 0;
+        packets = 0;
+        continue;
+      case SIGTERM:
+        infomsg(infomode, "Received SIGTERM");
+        if (flag_chk(FLAG_R)) release();
+        goto ret_with_sockfd;
+      default: break;
+      }
+    }
+    if (FD_ISSET(state->sockfd, &rfds)) { /* Some Activity on RDFDs : is socket */
+      dbg("main sock read\n");
+      uint8_t msgType;
+      if (state->mode == MODE_RAW) bufflen = read_raw();
+      if (state->mode == MODE_APP) bufflen = read_app();
+      if (bufflen < 0) {
+        if (state->mode == MODE_RAW) mode_raw();
+        if (state->mode == MODE_APP) mode_app();
+        continue;
+      }
+      waited += time(NULL) - timestmp;
+      memset(&result, 0, sizeof(dhcpc_result_t));
+      msgType = dhcpc_parsemsg(&result);
+      if (msgType != DHCPNAK && result.ipaddr.s_addr == 0 ) continue;       // no ip for me ignore
+      if (!msgType || !get_option_serverid(state->pdhcp.options, &result)) continue; //no server id ignore
+      if (msgType == DHCPOFFER && server == 0) server = result.serverid.s_addr; // select the server
+      if (result.serverid.s_addr != server) continue; // not from the server we requested ignore
+      dhcpc_parseoptions(&result, state->pdhcp.options);
+      get_option_lease(state->pdhcp.options, &result);
+
+      switch (state->status) {
+      case STATE_INIT:
+        if (msgType == DHCPOFFER) {
+          state->status = STATE_REQUESTING;
+          mode_raw();
+          timeout = 0;
+          waited = 0;
+          packets = 0;
+        }
+        continue;
+      case STATE_REQUESTING:         /* FALLTHROUGH */
+      case STATE_RENEWING:           /* FALLTHROUGH */
+      case STATE_RENEW_REQUESTED:    /* FALLTHROUGH */
+      case STATE_REBINDING:
+        if (msgType == DHCPACK) {
+          timeout = result.lease_time / 2;
+          run_script(&result, state->status == STATE_REQUESTING ? "bound" : "renew");
+          state->status = STATE_BOUND;
+          infomsg(infomode, "Lease of %d.%d.%d.%d obtained, lease time %d from server %d.%d.%d.%d",
+              (result.ipaddr.s_addr >> 24) & 0xff, (result.ipaddr.s_addr >> 16) & 0xff, (result.ipaddr.s_addr >> 8) & 0xff, (result.ipaddr.s_addr) & 0xff,
+              result.lease_time,
+              (result.serverid.s_addr >> 24) & 0xff, (result.serverid.s_addr >> 16) & 0xff, (result.serverid.s_addr >> 8) & 0xff, (result.serverid.s_addr) & 0xff);
+          if (flag_chk(FLAG_q)) {
+            if (flag_chk(FLAG_R)) release();
+            goto ret_with_sockfd;
+          }
+          toys.optflags &= ~FLAG_n;
+          if (!flag_chk(FLAG_f)) {
+            dhcp_daemon();
+            toys.optflags |= FLAG_f;
+            if (flag_chk(FLAG_p)) write_pid(TT.pidfile);
+          }
+          waited = 0;
+          continue;
+        } else if (msgType == DHCPNAK) {
+          dbg("NACK received.\n");
+          run_script(&result, "nak");
+          if (state->status != STATE_REQUESTING) run_script(NULL, "deconfig");
+          mode_raw();
+          sleep(3);
+          state->status = STATE_INIT;
+          state->ipaddr.s_addr = 0;
+          server = 0;
+          timeout = 0;
+          packets = 0;
+          waited = 0;
+        }
+        continue;
+      default: break;
+      }
+    }
+  }
+ret_with_sockfd:
+  free_option_stores();
+  if (state->sockfd > 0) close(state->sockfd);
+  free(state);
+  return;
+}
diff --git a/toys/other/udhcpd.c b/toys/other/udhcpd.c
new file mode 100644 (file)
index 0000000..daad2b4
--- /dev/null
@@ -0,0 +1,1431 @@
+/* udhcpd.c - DHCP server for dynamic network configuration.
+ *
+ * Copyright 2013 Madhur Verma <mad.flexi@gmail.com>
+ *
+ * Not in SUSv4.
+USE_UDHCPD(NEWTOY(udhcpd, ">1P#<0>65535=67fS", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
+
+config UDHCPD
+  bool "udhcpd"
+  default y
+  help
+   Usage: udhcpd [-fS] [-P N] [CONFFILE]
+
+    -f    Run in foreground
+    -S    Log to syslog too
+    -P N  Use port N (default 67)
+
+config DEBUG_DHCP
+  bool "debugging messeges ON/OFF"
+  default n
+  depends on UDHCPD
+*/
+
+#define FOR_udhcpd
+
+#include "toys.h"
+#include <linux/sockios.h> //FIXME: linux specific. fix for other OS ports
+#include <linux/if_ether.h>
+
+#if CFG_DEBUG_DHCP==1
+# define dbg(fmt, arg...)   printf(fmt, ##arg)
+#else
+# define dbg(fmt, arg...)
+#endif
+
+#define flag_get(f,v,d)     ((toys.optflags & f) ? v : d)
+#define flag_chk(f)         ((toys.optflags & f) ? 1 : 0)
+
+#define LOG_SILENT          0x0
+#define LOG_CONSOLE         0x1
+#define LOG_SYSTEM          0x2
+
+#define DEFAULT_LEASE_TIME  (60*60*24*10)
+#define LEASES_FILE         "/var/lib/misc/udhcpd.leases"
+#define DHCPD_CONF_FILE     "/etc/udhcpd.conf"
+#define DPID_FILE           "/var/run/udhcpd.pid"
+#define SERVER_PORT         67
+#define CLIENT_PORT         68
+
+#define BOOTPREQUEST       1
+#define BOOTPREPLY         2
+
+#define BOOTP_BROADCAST     0x8000
+#define DHCP_MAGIC          0x63825363
+
+#define DHCPDISCOVER        1
+#define DHCPOFFER           2
+#define DHCPREQUEST         3
+#define DHCPDECLINE         4
+#define DHCPACK             5
+#define DHCPNAK             6
+#define DHCPRELEASE         7
+#define DHCPINFORM          8
+
+#define DHCPMIN             DHCPDISCOVER
+#define DHCPMAX             DHCPINFORM
+
+#define DHCP_NUM8           (1<<8)
+#define DHCP_NUM16          (1<<9)
+#define DHCP_NUM32          DHCP_NUM16 | DHCP_NUM8
+#define DHCP_STRING         (1<<10)
+#define DHCP_STRLST         (1<<11)
+#define DHCP_IP             (1<<12)
+#define DHCP_IPLIST         (1<<13)
+#define DHCP_IPPLST         (1<<14)
+#define DHCP_STCRTS         (1<<15)
+
+/*
+ * DHCP option codes (partial list). See RFC 2132 and
+ */
+#define DHCP_OPT_PADDING                          0x00
+#define DHCP_OPT_SUBNET             DHCP_IP     | 0x01
+#define DHCP_OPT_TIME_OFFSET        DHCP_NUM32  | 0x02 /* (localtime - UTC_time) in seconds. signed */
+#define DHCP_OPT_ROUTER             DHCP_IPLIST | 0x03
+#define DHCP_OPT_DNS_SERVER         DHCP_IPLIST | 0x06
+#define DHCP_OPT_LPR_SERVER         DHCP_IPLIST | 0x09
+#define DHCP_OPT_HOST_NAME          DHCP_STRING | 0x0c /* either client informs server or server gives name to client */
+#define DHCP_OPT_BOOT_SIZE          DHCP_NUM16  | 0x0d
+#define DHCP_OPT_DOMAIN_NAME        DHCP_STRING | 0x0f /* server gives domain suffix */
+#define DHCP_OPT_SWAP_SERVER        DHCP_IP     | 0x10
+#define DHCP_OPT_ROOT_PATH          DHCP_STRING | 0x11
+#define DHCP_OPT_IP_TTL             DHCP_NUM8   | 0x17
+#define DHCP_OPT_MTU                DHCP_NUM16  | 0x1a
+#define DHCP_OPT_BROADCAST          DHCP_IP     | 0x1c
+#define DHCP_OPT_ROUTES             DHCP_STCRTS | 0x21
+#define DHCP_OPT_NIS_DOMAIN         DHCP_STRING | 0x28
+#define DHCP_OPT_NIS_SERVER         DHCP_IPLIST | 0x29
+#define DHCP_OPT_NTP_SERVER         DHCP_IPLIST | 0x2a
+#define DHCP_OPT_WINS_SERVER        DHCP_IPLIST | 0x2c
+#define DHCP_OPT_REQUESTED_IP       DHCP_IP     | 0x32 /* sent by client if specific IP is wanted */
+#define DHCP_OPT_LEASE_TIME         DHCP_NUM32  | 0x33
+#define DHCP_OPT_OPTION_OVERLOAD                  0x34
+#define DHCP_OPT_MESSAGE_TYPE       DHCP_NUM8   | 0x35
+#define DHCP_OPT_SERVER_ID          DHCP_IP     | 0x36 /* by default server's IP */
+#define DHCP_OPT_PARAM_REQ          DHCP_STRING | 0x37 /* list of options client wants */
+#define DHCP_OPT_ERR_MESSAGE        DHCP_STRING | 0x38 /* error message when sending NAK etc */
+#define DHCP_OPT_MAX_SIZE           DHCP_NUM16  | 0x39
+#define DHCP_OPT_VENDOR             DHCP_STRING | 0x3c /* client's vendor (a string) */
+#define DHCP_OPT_CLIENT_ID          DHCP_STRING | 0x3d /* by default client's MAC addr, but may be arbitrarily long */
+#define DHCP_OPT_TFTP_SERVER_NAME   DHCP_STRING | 0x42 /* same as 'sname' field */
+#define DHCP_OPT_BOOT_FILE          DHCP_STRING | 0x43 /* same as 'file' field */
+#define DHCP_OPT_USER_CLASS         DHCP_STRING | 0x4d /* RFC 3004. set of LASCII strings. "I am a printer" etc */
+#define DHCP_OPT_FQDN                             0x51 /* client asks to update DNS to map its FQDN to its new IP */
+#define DHCP_OPT_DOMAIN_SEARCH      DHCP_STRLST | 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */
+#define DHCP_OPT_SIP_SERVERS        DHCP_STRLST | 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */
+#define DHCP_OPT_STATIC_ROUTES      DHCP_STCRTS | 0x79 /* RFC 3442. (mask,ip,router) tuples */
+#define DHCP_OPT_VLAN_ID            DHCP_NUM    | 0x84 /* 802.1P VLAN ID */
+#define DHCP_OPT_VLAN_PRIORITY      DHCP_NUM    | 0x85 /* 802.1Q VLAN priority */
+#define DHCP_OPT_MS_STATIC_ROUTES   DHCP_STCRTS | 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */
+#define DHCP_OPT_WPAD               DHCP_STRING | 0xfc
+#define DHCP_OPT_END                              0xff
+
+GLOBALS(
+    long port;
+);
+
+typedef struct __attribute__((packed)) dhcp_msg_s {
+  uint8_t op;
+  uint8_t htype;
+  uint8_t hlen;
+  uint8_t hops;
+  uint32_t xid;
+  uint16_t secs;
+  uint16_t flags;
+  uint32_t ciaddr;
+  uint32_t yiaddr;
+  uint32_t nsiaddr;
+  uint32_t ngiaddr;
+  uint8_t chaddr[16];
+  uint8_t sname[64];
+  uint8_t file[128];
+  uint32_t cookie;
+  uint8_t options[308];
+} dhcp_msg_t;
+
+typedef struct __attribute__((packed)) dhcp_raw_s {
+  struct iphdr iph;
+  struct udphdr udph;
+  dhcp_msg_t dhcp;
+} dhcp_raw_t;
+
+typedef struct static_lease_s {
+  struct static_lease_s *next;
+  uint32_t nip;
+  int mac[6];
+} static_lease;
+
+typedef struct {
+  uint32_t expires;
+  uint32_t lease_nip;
+  uint8_t lease_mac[6];
+  char hostname[20];
+  uint8_t pad[2];
+} dyn_lease;
+
+typedef struct option_val_s {
+  char *key;
+  uint16_t code;
+  void *val;
+  size_t len;
+} option_val_t;
+
+typedef struct __attribute__((__may_alias__)) server_config_s {
+  char *interface;                /* interface to use */
+  int ifindex;
+  uint32_t server_nip;
+  uint32_t port;
+  uint8_t server_mac[6];          /* our MAC address (used only for ARP probing) */
+  void *options[256];             /* list of DHCP options loaded from the config file */
+  /* start,end are in host order: we need to compare start <= ip <= end */
+  uint32_t start_ip;              /* start address of leases, in host order */
+  uint32_t end_ip;                /* end of leases, in host order */
+  uint32_t max_lease_sec;         /* maximum lease time (host order) */
+  uint32_t min_lease_sec;         /* minimum lease time a client can request */
+  uint32_t max_leases;            /* maximum number of leases (including reserved addresses) */
+  uint32_t auto_time;             /* how long should udhcpd wait before writing a config file.
+                                   * if this is zero, it will only write one on SIGUSR1 */
+  uint32_t decline_time;          /* how long an address is reserved if a client returns a
+                                   * decline message */
+  uint32_t conflict_time;         /* how long an arp conflict offender is leased for */
+  uint32_t offer_time;            /* how long an offered address is reserved */
+  uint32_t siaddr_nip;            /* "next server" bootp option */
+  char *lease_file;
+  char *pidfile;
+  char *notify_file;              /* what to run whenever leases are written */
+  char *sname;                    /* bootp server name */
+  char *boot_file;                /* bootp boot file option */
+  struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
+} server_config_t;
+
+typedef struct __attribute__((__may_alias__)) server_state_s {
+  uint8_t rqcode;
+  int listensock;
+  dhcp_msg_t rcvd_pkt;
+  uint8_t* rqopt;
+  dhcp_msg_t send_pkt;
+  static_lease *sleases;
+  struct arg_list *dleases;
+} server_state_t;
+
+typedef struct config_keyword {
+  const char *keyword;
+  int (*handler)(const char *str, void *var);
+  void *var;
+  const char *def;
+} config_keyword_t;
+
+static option_val_t options_list[] = {
+    {"lease"          , DHCP_NUM32  | 0x33, NULL, 0},
+    {"subnet"         , DHCP_IP     | 0x01, NULL, 0},
+    {"broadcast"      , DHCP_IP     | 0x1c, NULL, 0},
+    {"router"         , DHCP_IP     | 0x03, NULL, 0},
+    {"ipttl"          , DHCP_NUM8   | 0x17, NULL, 0},
+    {"mtu"            , DHCP_NUM16  | 0x1a, NULL, 0},
+    {"hostname"       , DHCP_STRING | 0x0c, NULL, 0},
+    {"domain"         , DHCP_STRING | 0x0f, NULL, 0},
+    {"search"         , DHCP_STRLST | 0x77, NULL, 0},
+    {"nisdomain"      , DHCP_STRING | 0x28, NULL, 0},
+    {"timezone"       , DHCP_NUM32  | 0x02, NULL, 0},
+    {"tftp"           , DHCP_STRING | 0x42, NULL, 0},
+    {"bootfile"       , DHCP_STRING | 0x43, NULL, 0},
+    {"bootsize"       , DHCP_NUM16  | 0x0d, NULL, 0},
+    {"rootpath"       , DHCP_STRING | 0x11, NULL, 0},
+    {"wpad"           , DHCP_STRING | 0xfc, NULL, 0},
+    {"serverid"       , DHCP_IP     | 0x36, NULL, 0},
+    {"message"        , DHCP_STRING | 0x38, NULL, 0},
+    {"vlanid"         , DHCP_NUM32  | 0x84, NULL, 0},
+    {"vlanpriority"   , DHCP_NUM32  | 0x85, NULL, 0},
+    {"dns"            , DHCP_IPLIST | 0x06, NULL, 0},
+    {"wins"           , DHCP_IPLIST | 0x2c, NULL, 0},
+    {"nissrv"         , DHCP_IPLIST | 0x29, NULL, 0},
+    {"ntpsrv"         , DHCP_IPLIST | 0x2a, NULL, 0},
+    {"lprsrv"         , DHCP_IPLIST | 0x09, NULL, 0},
+    {"swapsrv"        , DHCP_IP     | 0x10, NULL, 0},
+    {"routes"         , DHCP_STCRTS | 0x21, NULL, 0},
+    {"staticroutes"   , DHCP_STCRTS | 0x79, NULL, 0},
+    {"msstaticroutes" , DHCP_STCRTS | 0xf9, NULL, 0},
+};
+
+struct fd_pair { int rd; int wr; };
+static server_config_t gconfig;
+static server_state_t gstate;
+static uint8_t infomode;
+static struct fd_pair sigfd;
+static const int constone = 1;
+
+static int dhcp_daemon(void)
+{
+  int fd;
+
+  fd = open("/dev/null", O_RDWR);
+  if (fd < 0) fd = open("/", O_RDONLY, 0666);
+  pid_t pid = fork();
+
+  if (pid < 0) {
+    error_msg("DAEMON: fail to fork");
+    return -1;
+  }
+  if (pid) exit(EXIT_SUCCESS);
+
+  setsid();
+  dup2(fd, 0);
+  dup2(fd, 1);
+  dup2(fd, 2);
+  close(fd--);
+  return 0;
+}
+
+/*
+ * calculate options size.
+ */
+static int dhcp_opt_size(uint8_t *optionptr)
+{
+  int i = 0;
+  for(;optionptr[i] != 0xff; i++) if(optionptr[i] != 0x00) i += optionptr[i + 1] + 2 -1;
+  return i;
+}
+
+/*
+ * calculates checksum for udhcp messeges.
+ */
+static uint16_t udhcp_checksum(void *addr, int count)
+{
+  int32_t sum = 0;
+  uint16_t *source = (uint16_t *)addr;
+  uint16_t tmp = 0;
+
+  while (count > 1)  {
+    sum += *source++;
+    count -= 2;
+  }
+  if (count > 0) {
+    *(uint8_t*)&tmp = *(uint8_t*)source;
+    sum += tmp;
+  }
+  while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
+  return ~sum;
+}
+
+/*
+ * gets information of INTERFACE and updates IFINDEX, MAC and IP
+ */
+static int get_interface(const char *interface, int *ifindex, uint32_t *oip, uint8_t *mac)
+{
+  struct ifreq req;
+  int fd;
+  struct sockaddr_in *ip;
+
+  fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+  if (fd < 0) {
+    error_msg("IFACE : fail to get interface. ERROR : %d\n", fd);
+    return fd;
+  }
+  req.ifr_addr.sa_family = AF_INET;
+  strncpy(req.ifr_name, interface, IFNAMSIZ);
+  req.ifr_name[IFNAMSIZ-1] = '\0';
+
+  if (ioctl(fd, SIOCGIFFLAGS, &req) != 0) {
+    error_msg("Is interface %s configured ?\n", interface);
+    close(fd);
+    return -1;
+  }
+  if (!(req.ifr_flags & IFF_UP)) return -1;
+
+  if (oip) {
+    if (ioctl(fd, SIOCGIFADDR, &req) != 0){
+      error_msg("Is interface %s configured ?\n", interface);
+      close(fd);
+      return -1;
+    }
+    ip = (struct sockaddr_in*) &req.ifr_addr;
+    dbg("IP %s\n", inet_ntoa(ip->sin_addr));
+    *oip = ntohl(ip->sin_addr.s_addr);
+  }
+  if (ifindex) {
+    if (ioctl(fd, SIOCGIFINDEX, &req) != 0) {
+      error_msg("IFACE : NO INDEX interface %s\n", interface);
+      close(fd);
+      return -1;
+    }
+    dbg("Adapter index %d\n", req.ifr_ifindex);
+    *ifindex = req.ifr_ifindex;
+  }
+  if (mac) {
+    if (ioctl(fd, SIOCGIFHWADDR, &req) != 0) {
+      error_msg("IFACE : NO MAC interface %s\n", interface);
+      close(fd);
+      return -1;
+    }
+    memcpy(mac, req.ifr_hwaddr.sa_data, 6);
+    dbg("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+  }
+  close(fd);
+  return 0;
+}
+
+/*
+ *logs messeges to syslog or console
+ *opening the log is still left with applet.
+ *FIXME: move to more relevent lib. probably libc.c
+ */
+static void infomsg(uint8_t infomode, const char *s, ...)
+{
+  int used;
+  char *msg;
+  va_list p, t;
+
+  if (infomode == LOG_SILENT) return;
+  va_start(p, s);
+  va_copy(t, p);
+  used = vsnprintf(NULL, 0, s, t);
+  used++;
+  va_end(t);
+
+  msg = xmalloc(used);
+  vsnprintf(msg, used, s, p);
+  va_end(p);
+
+  if (infomode & LOG_SYSTEM) syslog(LOG_INFO, "%s", msg);
+  if (infomode & LOG_CONSOLE) printf("%s\n", msg);
+  free(msg);
+}
+
+/*
+ * Writes self PID in file PATH
+ * FIXME: libc implementation only writes in /var/run
+ * this is more generic as some implemenation may provide
+ * arguments to write in specific file. as udhcpd does.
+ */
+static void write_pid(char *path)
+{
+  int pidfile = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+  if (pidfile > 0) {
+    unsigned pid = getpid();
+    char pidbuf[32];
+    int len = sprintf(pidbuf, "%lu\n", (long)pid);
+    write(pidfile, pidbuf, len);
+    close(pidfile);
+  }
+}
+
+/*
+ * Generic signal handler real handling is done in main funcrion.
+ */
+static void signal_handler(int sig)
+{
+  unsigned char ch = sig;
+  if (write(sigfd.wr, &ch, 1) != 1) dbg("can't send signal\n");
+}
+
+/*
+ * signal setup for SIGUSR1 SIGTERM
+ */
+static int setup_signal()
+{
+  if (pipe((int *)&sigfd) < 0) {
+    dbg("signal pipe failed\n");
+    return -1;
+  }
+  fcntl(sigfd.wr , F_SETFD, FD_CLOEXEC);
+  fcntl(sigfd.rd , F_SETFD, FD_CLOEXEC);
+  int flags = fcntl(sigfd.wr, F_GETFL);
+  fcntl(sigfd.wr, F_SETFL, flags | O_NONBLOCK);
+  signal(SIGUSR1, signal_handler);
+  signal(SIGTERM, signal_handler);
+  return 0;
+}
+
+/*
+ * String STR to UINT32 conversion strored in VAR
+ */
+static int strtou32(const char *str, void *var)
+{
+  char *endptr = NULL;
+  int base = 10;
+  errno=0;
+  *((uint32_t*)(var)) = 0;
+  if (str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
+    base = 16;
+    str+=2;
+  }
+  long ret_val = strtol(str, &endptr, base);
+  if (errno) infomsg(infomode, "config : Invalid num %s",str);
+  else if (endptr && (*endptr!='\0'||endptr == str))
+      infomsg(infomode, "config : Not a valid num %s",str);
+  else *((uint32_t*)(var)) = (uint32_t)ret_val;
+  return 0;
+}
+
+/*
+ * copy string STR in variable VAR
+ */
+static int strinvar(const char *str, void *var)
+{
+  char **dest = var;
+  free(*dest);
+  *dest = strdup(str);
+  return 0;
+}
+
+/*
+ * IP String STR to binary data.
+ */
+static int striptovar(const char *str, void *var)
+{
+  in_addr_t addr;
+  *((uint32_t*)(var)) = 0;
+  if(!str) {
+    error_msg("config : NULL address string \n");
+    return -1;
+  }
+  addr = inet_addr(str);
+  if(addr == -1) {
+    error_msg("config : wrong address %s \n",str );
+    return -1;
+  }
+  *((uint32_t*)(var)) = (uint32_t)addr;
+  return 0;
+}
+
+/*
+ * String to dhcp option conversion
+ */
+static int strtoopt(const char *str, void *var)
+{
+  int size, count;
+  char *option, *valstr, *grp, *tp;
+  uint32_t optcode = 0, inf = infomode, convtmp;
+  uint16_t flag = 0;
+  uint32_t mask, nip, router;
+
+  if (*str == '\0') return 0;
+  size = sizeof(options_list) / sizeof(option_val_t);
+
+  option = strtok((char*)str, " \t=");
+  if (!option) return -1;
+
+  infomode = LOG_SILENT;
+  strtou32(option, (uint32_t*)&optcode);
+  infomode = inf;
+
+  if (optcode > 0 && optcode < 256) { // raw option
+    for (count = 0; count < size; count++) {
+      if ((options_list[count].code & 0X00FF) == optcode) {
+        flag = (options_list[count].code & 0XFF00);
+        break;
+      }
+    }
+    if (count == size) {
+      infomsg(inf, "config : Obsolete OR Unknown Option : %s", option);
+      return -1;
+    }
+  } else { //string option
+    for (count = 0; count < size; count++) {
+      if (strncmp(options_list[count].key, option, strlen(options_list[count].key))==0) {
+        flag = (options_list[count].code & 0XFF00);
+        optcode = (options_list[count].code & 0X00FF);
+        break;
+      }
+    }
+    if (count == size) {
+      infomsg(inf, "config : Obsolete OR Unknown Option : %s", option);
+      return -1;
+    }
+  }
+  if (!flag || !optcode) {
+    return -1;
+  }
+
+  valstr = strtok(NULL, " \t");
+  if (!valstr) {
+    dbg("config : option %s has no value defined.\n", option);
+    return -1;
+  }
+  dbg(" value : %-20s : ", valstr);
+  switch (flag) {
+  case DHCP_NUM32:
+    options_list[count].len = sizeof(uint32_t);
+    options_list[count].val = xmalloc(sizeof(uint32_t));
+    strtou32(valstr, &convtmp);
+    memcpy(options_list[count].val, &convtmp, sizeof(uint32_t));
+    break;
+  case DHCP_NUM16:
+    options_list[count].len = sizeof(uint16_t);
+    options_list[count].val = xmalloc(sizeof(uint16_t));
+    strtou32(valstr, &convtmp);
+    memcpy(options_list[count].val, &convtmp, sizeof(uint16_t));
+    break;
+  case DHCP_NUM8:
+    options_list[count].len = sizeof(uint8_t);
+    options_list[count].val = xmalloc(sizeof(uint8_t));
+    strtou32(valstr, &convtmp);
+    memcpy(options_list[count].val, &convtmp, sizeof(uint8_t));
+    break;
+  case DHCP_IP:
+    options_list[count].len = sizeof(uint32_t);
+    options_list[count].val = xmalloc(sizeof(uint32_t));
+    striptovar(valstr, options_list[count].val);
+    break;
+  case DHCP_STRING:
+    options_list[count].len = strlen(valstr);
+    options_list[count].val = strdup(valstr);
+    break;
+  case DHCP_IPLIST:
+    while(valstr){
+      options_list[count].val = xrealloc(options_list[count].val, options_list[count].len + sizeof(uint32_t));
+      striptovar(valstr, ((uint8_t*)options_list[count].val)+options_list[count].len);
+      options_list[count].len += sizeof(uint32_t);
+      valstr = strtok(NULL," \t");
+    }
+    break;
+  case DHCP_STRLST: //FIXME: do smthing.
+  case DHCP_IPPLST:
+    break;
+  case DHCP_STCRTS:
+    /* Option binary format:
+     * mask [one byte, 0..32]
+     * ip [0..4 bytes depending on mask]
+     * router [4 bytes]
+     * may be repeated
+     * staticroutes 10.0.0.0/8 10.127.0.1, 10.11.12.0/24 10.11.12.1
+     */
+    grp = strtok(valstr, ",");;
+    while(grp){
+      while(*grp == ' ' || *grp == '\t') grp++;
+      tp = strchr(grp, '/');
+      if (!tp) error_exit("wrong formated static route option");
+      *tp = '\0';
+      mask = strtol(++tp, &tp, 10);
+      if (striptovar(grp, (uint8_t*)&nip)<0) error_exit("wrong formated static route option");
+      while(*tp == ' ' || *tp == '\t' || *tp == '-') tp++;
+      if (striptovar(tp, (uint8_t*)&router)<0) error_exit("wrong formated static route option");
+      options_list[count].val = xrealloc(options_list[count].val, options_list[count].len + 1 + mask/8 + 4);
+      memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &mask, 1);
+      options_list[count].len += 1;
+      memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &nip, mask/8);
+      options_list[count].len += mask/8;
+      memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &router, 4);
+      options_list[count].len += 4;
+      tp = NULL;
+      grp = strtok(NULL, ",");
+    }
+    break;
+  }
+  return 0;
+}
+
+/*
+ * Reads Static leases from STR and updates inner structures.
+ */
+static int get_staticlease(const char *str, void *var)
+{
+  struct static_lease_s *sltmp;
+  char *tkmac, *tkip;
+  int count;
+
+  if (*str == '\0') return 0;
+
+  tkmac = strtok((char*)str, " \t");
+  if (!tkmac) {
+    infomsg(infomode, "config : static lease : mac not found");
+    return 0;
+  }
+  tkip = strtok(NULL, " \t");
+  if (!tkip) {
+    infomsg(infomode, "config : static lease : no ip bind to mac %s", tkmac);
+    return 0;
+  }
+  sltmp = xzalloc(sizeof(struct static_lease_s));
+  for (count = 0; count < 6; count++, tkmac++) {
+    errno = 0;
+    sltmp->mac[count] = strtol(tkmac, &tkmac, 16);
+    if (sltmp->mac[count]>255 || sltmp->mac[count]<0 || (*tkmac && *tkmac!=':') || errno) {
+      infomsg(infomode, "config : static lease : mac address wrong format");
+      free(sltmp);
+      return 0;
+    }
+  }
+  striptovar(tkip, &sltmp->nip);
+  sltmp->next = gstate.sleases;
+  gstate.sleases = sltmp;
+
+  return 0;
+}
+
+static const struct config_keyword keywords[] = {
+/* keyword          handler           variable address                default */
+  {"start"        , striptovar      , (void*)&gconfig.start_ip     , "192.168.0.20"},
+  {"end"          , striptovar      , (void*)&gconfig.end_ip       , "192.168.0.254"},
+  {"interface"    , strinvar        , (void*)&gconfig.interface    , "eth0"},
+  {"port"         , strtou32        , (void*)&gconfig.port         , "67"},
+  {"min_lease"    , strtou32        , (void*)&gconfig.min_lease_sec, "60"},
+  {"max_leases"   , strtou32        , (void*)&gconfig.max_leases   , "235"},
+  {"auto_time"    , strtou32        , (void*)&gconfig.auto_time    , "7200"},
+  {"decline_time" , strtou32        , (void*)&gconfig.decline_time , "3600"},
+  {"conflict_time", strtou32        , (void*)&gconfig.conflict_time, "3600"},
+  {"offer_time"   , strtou32        , (void*)&gconfig.offer_time   , "60"},
+  {"lease_file"   , strinvar        , (void*)&gconfig.lease_file   , LEASES_FILE},
+  {"pidfile"      , strinvar        , (void*)&gconfig.pidfile      , DPID_FILE},
+  {"siaddr"       , striptovar      , (void*)&gconfig.siaddr_nip   , "0.0.0.0"},
+  {"option"       , strtoopt        , (void*)&gconfig.options      , ""},
+  {"opt"          , strtoopt        , (void*)&gconfig.options      , ""},
+  {"notify_file"  , strinvar        , (void*)&gconfig.notify_file  , ""},
+  {"sname"        , strinvar        , (void*)&gconfig.sname        , ""},
+  {"boot_file"    , strinvar        , (void*)&gconfig.boot_file    , ""},
+  {"static_lease" , get_staticlease , (void*)&gconfig.static_leases, ""},
+};
+
+/*
+ * Parses the server config file and updates the global server config accordingly.
+ */
+static int parse_server_config(char *config_file, const struct config_keyword *confkey)
+{
+  int size, count;
+  FILE *fs = NULL;
+  char *confline_temp = NULL,*confline = NULL, *tk = NULL, *tokens[2] = {NULL, NULL};
+  int len, linelen, tcount;
+
+  size = sizeof(keywords) / sizeof(struct config_keyword);
+  for (count = 0; count < size; count++)
+    if (confkey[count].handler) confkey[count].handler(confkey[count].def, confkey[count].var);
+
+  fs = fopen(config_file, "r");
+  if (fs == NULL) perror_msg("%s", config_file);
+  for (len = 0, linelen = 0; fs;) {
+    len = getline(&confline_temp, (size_t*) &linelen, fs);
+    confline = confline_temp;
+    if (len <= 0) break;
+    for (; *confline == ' '; confline++, len--);
+    if ((confline[0] == '#') || (confline[0] == '\n')) goto free_conf_continue;
+    tk = strchr(confline, '#');
+    if (tk) {
+      for (; *(tk-1)==' ' || *(tk-1)=='\t'; tk--);
+      *tk = '\0';
+    }
+    tk = strchr(confline, '\n');
+    if (tk) {
+      for (; *(tk-1)==' ' || *(tk-1)=='\t'; tk--);
+      *tk = '\0';
+    }
+    for (tcount=0, tk=strtok(confline, " \t"); tk && (tcount < 2); tcount++, tk=strtok(NULL,(tcount==1)?"":" \t")) {
+      while ((*tk == '\t') || (*tk == ' ')) tk++;
+      tokens[tcount] = xstrdup(tk);
+    }
+    if (tcount<=1) goto free_tk0_continue;
+    for (count = 0; count < size; count++) {
+      if (strcmp(confkey[count].keyword,tokens[0])==0) {
+        dbg("got config : %15s : ", confkey[count].keyword);
+        if (confkey[count].handler(tokens[1], confkey[count].var) == 0)
+          dbg("%s \n", tokens[1]);
+        break;
+      }
+    }
+    if (tokens[1]) { free(tokens[1]); tokens[1] = NULL; }
+free_tk0_continue:
+    if (tokens[0]) { free(tokens[0]); tokens[0] = NULL; }
+free_conf_continue:
+    free(confline_temp);
+    confline_temp = NULL;
+  }
+  if (fs) {
+    fclose(fs);
+    fs = NULL;
+  }
+  return 0;
+}
+
+/*
+ * opens UDP socket for listen
+ */
+static int open_listensock(void)
+{
+  struct sockaddr_in addr;
+  struct ifreq ifr;
+
+  if (gstate.listensock > 0) close(gstate.listensock);
+
+  dbg("Opening listen socket on *:%d %s\n", gconfig.port, gconfig.interface);
+  gstate.listensock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (gstate.listensock < 0) {
+    perror_exit("listensocket fail ERROR: %d ", gstate.listensock);
+    return -1;
+  }
+  setsockopt(gstate.listensock, SOL_SOCKET, SO_REUSEADDR, &constone, sizeof(constone));
+  if (setsockopt(gstate.listensock, SOL_SOCKET, SO_BROADCAST, &constone, sizeof(constone)) == -1) {
+      dbg("OPEN : brodcast ioctl failed.\n");
+      close(gstate.listensock);
+      return -1;
+  }
+  memset(&ifr, 0, sizeof(ifr));
+  strncpy(ifr.ifr_name, gconfig.interface, IFNAMSIZ);
+  setsockopt(gstate.listensock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = (flag_chk(FLAG_P))?htons(TT.port):htons(SERVER_PORT);
+  addr.sin_addr.s_addr = INADDR_ANY ;
+
+  if (bind(gstate.listensock, (struct sockaddr *) &addr, sizeof(addr))) {
+    close(gstate.listensock);
+    perror_exit("bind failed");
+    return -1;
+  }
+  dbg("OPEN : success\n");
+  return 0;
+}
+
+/*
+ * Sends data through raw socket.
+ */
+static int send_packet(uint8_t broadcast)
+{
+  struct sockaddr_ll dest_sll;
+  dhcp_raw_t packet;
+  unsigned padding;
+  int fd;
+  int result = -1;
+  uint8_t bmacaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+  memset(&packet, 0, sizeof(dhcp_raw_t));
+  memcpy(&packet.dhcp, &gstate.send_pkt, sizeof(dhcp_msg_t));
+
+  fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+  if (fd < 0) {
+    dbg("SEND : socket failed\n");
+    return -1;
+  }
+  memset(&dest_sll, 0, sizeof(dest_sll));
+  dest_sll.sll_family = AF_PACKET;
+  dest_sll.sll_protocol = htons(ETH_P_IP);
+  dest_sll.sll_ifindex = gconfig.ifindex;
+  dest_sll.sll_halen = 6;
+  memcpy(dest_sll.sll_addr, (broadcast)?bmacaddr:gstate.rcvd_pkt.chaddr , 6);
+
+  if (bind(fd, (struct sockaddr *) &dest_sll, sizeof(dest_sll)) < 0) {
+    dbg("SEND : bind failed\n");
+    close(fd);
+    return -1;
+  }
+  padding = 308 - 1 - dhcp_opt_size(gstate.send_pkt.options);
+  packet.iph.protocol = IPPROTO_UDP;
+  packet.iph.saddr = gconfig.server_nip;
+  packet.iph.daddr = (broadcast || (gstate.rcvd_pkt.ciaddr == 0))?INADDR_BROADCAST:gstate.rcvd_pkt.ciaddr;
+  packet.udph.source = htons(SERVER_PORT);
+  packet.udph.dest = htons(CLIENT_PORT);
+  packet.udph.len = htons(sizeof(dhcp_raw_t) - sizeof(struct iphdr) - padding);
+  packet.iph.tot_len = packet.udph.len;
+  packet.udph.check = udhcp_checksum(&packet, sizeof(dhcp_raw_t) - padding);
+  packet.iph.tot_len = htons(sizeof(dhcp_raw_t) - padding);
+  packet.iph.ihl = sizeof(packet.iph) >> 2;
+  packet.iph.version = IPVERSION;
+  packet.iph.ttl = IPDEFTTL;
+  packet.iph.check = udhcp_checksum(&packet.iph, sizeof(packet.iph));
+
+  result = sendto(fd, &packet, sizeof(dhcp_raw_t) - padding, 0,
+      (struct sockaddr *) &dest_sll, sizeof(dest_sll));
+
+  dbg("sendto %d\n", result);
+  close(fd);
+  if (result < 0) dbg("PACKET send error\n");
+  return result;
+}
+
+/*
+ * Reads from UDP socket
+ */
+static int read_packet(void)
+{
+  int ret;
+
+  memset(&gstate.rcvd_pkt, 0, sizeof(dhcp_msg_t));
+  ret = read(gstate.listensock, &gstate.rcvd_pkt, sizeof(dhcp_msg_t));
+  if (ret < 0) {
+    dbg("Packet read error, ignoring. \n");
+    return ret; /* returns -1 */
+  }
+  if (gstate.rcvd_pkt.cookie != htonl(DHCP_MAGIC)) {
+    dbg("Packet with bad magic, ignoring. \n");
+    return -2;
+  }
+  if (gstate.rcvd_pkt.op != BOOTPREQUEST) {
+    dbg("Not a BOOT REQUEST ignoring. \n");
+    return -2;
+  }
+  if (gstate.rcvd_pkt.hlen != 6) {
+    dbg("hlen != 6 ignoring. \n");
+    return -2;
+  }
+  dbg("Received a packet. Size : %d \n", ret);
+  return ret;
+}
+
+/*
+ * Preapres a dhcp packet with defaults and configs
+ */
+static uint8_t* prepare_send_pkt(void)
+{
+  memset((void*)&gstate.send_pkt, 0, sizeof(gstate.send_pkt));
+  gstate.send_pkt.op = BOOTPREPLY;
+  gstate.send_pkt.htype = 1;
+  gstate.send_pkt.hlen = 6;
+  gstate.send_pkt.xid = gstate.rcvd_pkt.xid;
+  gstate.send_pkt.cookie = htonl(DHCP_MAGIC);
+  gstate.send_pkt.nsiaddr = gconfig.server_nip;
+  memcpy(gstate.send_pkt.chaddr, gstate.rcvd_pkt.chaddr, 16);
+  gstate.send_pkt.options[0] = DHCP_OPT_END;
+  return gstate.send_pkt.options;
+}
+
+/*
+ * Sets a option value in dhcp packet's option field
+ */
+static uint8_t* set_optval(uint8_t *optptr, uint16_t opt, void *var, size_t len)
+{
+  while (*optptr != DHCP_OPT_END) optptr++;
+  *optptr++ = (uint8_t)(opt & 0x00FF);
+  *optptr++ = (uint8_t) len;
+  memcpy(optptr, var, len);
+  optptr += len;
+  *optptr = DHCP_OPT_END;
+  return optptr;
+}
+
+/*
+ * Gets a option value from dhcp packet's option field
+ */
+static uint8_t* get_optval(uint8_t *optptr, uint16_t opt, void *var)
+{
+  size_t len;
+  uint8_t overloaded = 0;
+
+  while (1) {
+    while (*optptr == DHCP_OPT_PADDING) optptr++;
+    if ((*optptr & 0x00FF) == DHCP_OPT_END) break;
+    if ((*optptr & 0x00FF) == DHCP_OPT_OPTION_OVERLOAD) {
+      overloaded = optptr[2];
+      optptr += optptr[1] + 2;
+    }
+    len = optptr[1];
+    if (*optptr == (opt & 0x00FF))
+      switch (opt & 0xFF00) {
+      case DHCP_NUM32: /* FALLTHROUGH */
+      case DHCP_IP:
+        memcpy(var, optptr+2, sizeof(uint32_t));
+        optptr += len + 2;
+        return optptr;
+        break;
+      case DHCP_NUM16:
+        memcpy(var, optptr+2, sizeof(uint16_t));
+        optptr += len + 2;
+        return optptr;
+        break;
+      case DHCP_NUM8:
+        memcpy(var, optptr+2, sizeof(uint8_t));
+        optptr += len + 2;
+        return optptr;
+        break;
+      case DHCP_STRING:
+        var = xstrndup((char*) optptr, len);
+        optptr += len + 2;
+        return optptr;
+        break;
+      case DHCP_STRLST: /* FALLTHROUGH */ //FIXME: RFC3397 : support of domain search with encoding currently neglecting
+      case DHCP_IPLIST: /* FALLTHROUGH */ //FIXME: do smthing.
+      case DHCP_IPPLST: /* FALLTHROUGH */ //FIXME: do smthing.
+      case DHCP_STCRTS:
+        break;
+      }
+    optptr += len + 2;
+  }
+  if ((overloaded == 1) | (overloaded == 3)) get_optval((uint8_t*)&gstate.rcvd_pkt.file, opt, var);
+  if ((overloaded == 2) | (overloaded == 3)) get_optval((uint8_t*)&gstate.rcvd_pkt.sname, opt, var);
+  return optptr;
+}
+
+/*
+ * Retrives Requested Parameter list from dhcp req packet.
+ */
+static uint8_t get_reqparam(uint8_t **list)
+{
+  uint8_t len, *optptr;
+  if(*list) free(*list);
+  for (optptr = gstate.rcvd_pkt.options; *optptr && *optptr!=((DHCP_OPT_PARAM_REQ) & 0x00FF); optptr+=optptr[1]+2);
+  len = *++optptr;
+  *list = xzalloc(len+1);
+  memcpy(*list, ++optptr, len);
+  return len;
+}
+
+/*
+ * Sets values of req param in dhcp offer packet.
+ */
+static uint8_t* set_reqparam(uint8_t *optptr, uint8_t *list)
+{
+  uint8_t reqcode;
+  int count;
+  int size = sizeof(options_list) / sizeof(option_val_t);
+  while (*list) {
+    reqcode = *list++;
+    for (count = 0; count < size; count++) {
+      if ((options_list[count].code & 0X00FF)==reqcode) {
+        if ((options_list[count].len==0)||(options_list[count].val==NULL)) break;
+        for (; *optptr && *optptr!=DHCP_OPT_END; optptr+=optptr[1]+2);
+        *optptr++ = (uint8_t) (options_list[count].code & 0x00FF);
+        *optptr++ = (uint8_t) options_list[count].len;
+        memcpy(optptr, options_list[count].val, options_list[count].len);
+        optptr += options_list[count].len;
+        *optptr = DHCP_OPT_END;
+        break;
+      }
+    }
+  }
+  return optptr;
+}
+
+/*
+ * Executes Script.
+ */
+static void run_notify(char **argv)
+{
+  struct stat sts;
+  volatile int error = 0;
+  pid_t pid;
+
+  if (stat(argv[0], &sts) == -1 && errno == ENOENT) {
+    infomsg(infomode, "notify file: %s : not exist.", argv[0]);
+    return;
+  }
+  fflush(NULL);
+
+  pid = vfork();
+  if (pid < 0) {
+    dbg("Fork failed.\n");
+    return;
+  }
+  if (!pid) {
+    execvp(argv[0], argv);
+    error = errno;
+    _exit(111);
+  }
+  if (error) {
+    waitpid(pid, NULL, 0);
+    errno = error;
+  }
+  dbg("script complete.\n");
+}
+
+/*
+ * Writes lease file.
+ */
+static int write_leasefile(void)
+{
+  int fd;
+  uint32_t curr, tmp_time;
+  int64_t timestamp;
+  struct arg_list *listdls = gstate.dleases;
+  dyn_lease *dls;
+
+  fd = open(gconfig.lease_file, O_WRONLY | O_CREAT | O_TRUNC);
+  if (fd < 0){
+    perror_msg("can't open %s ", gconfig.lease_file);
+    return fd;
+  }
+
+  curr = timestamp = time(NULL);
+  timestamp = SWAP_BE64(timestamp);
+  writeall(fd, &timestamp, sizeof(timestamp));
+
+  while (listdls) {
+    dls = (dyn_lease*)listdls->arg;
+    tmp_time = dls->expires;
+    dls->expires -= curr;
+    if ((int32_t) dls->expires < 0) goto skip;
+    dls->expires = htonl(dls->expires);
+    writeall(fd, dls, sizeof(dyn_lease));
+skip:
+    dls->expires = tmp_time;
+    listdls = listdls->next;
+  }
+  close(fd);
+  if (gconfig.notify_file) {
+    char *argv[3];
+    argv[0] = gconfig.notify_file;
+    argv[1] = gconfig.lease_file;
+    argv[2] = NULL;
+    run_notify(argv);
+  }
+  return 0;
+}
+
+/*
+ * Update max lease time from options.
+ */
+static void set_maxlease(void)
+{
+  int count;
+  int size = sizeof(options_list) / sizeof(option_val_t);
+  for (count = 0; count < size; count++)
+    if (options_list[count].val && options_list[count].code == (DHCP_OPT_LEASE_TIME)) {
+      gconfig.max_lease_sec = *((uint32_t*)options_list[count].val);
+      break;
+    }
+  if (gconfig.max_lease_sec == 0) gconfig.max_lease_sec = DEFAULT_LEASE_TIME;
+}
+
+/*
+ * Returns lease time for client.
+ */
+static uint32_t get_lease(uint32_t req_exp)
+{
+  uint32_t now = time(NULL);
+  req_exp = req_exp - now;
+  if ((req_exp <= 0) || (req_exp > gconfig.max_lease_sec))
+    return gconfig.max_lease_sec;
+
+  if (req_exp < gconfig.min_lease_sec)
+    return gconfig.min_lease_sec;
+
+  return req_exp;
+}
+
+/*
+ * Verify ip NIP in current leases ( assigned or not)
+ */
+static int verifyip_in_lease(uint32_t nip, uint8_t mac[6])
+{
+  static_lease *sls;
+  struct arg_list *listdls;
+
+  for (listdls = gstate.dleases; listdls; listdls = listdls->next) {
+    if (((dyn_lease*) listdls->arg)->lease_nip == nip) {
+      if (((int32_t)(((dyn_lease*) listdls->arg)->expires) - time(NULL)) < 0)
+        return 0;
+      return -1;
+    }
+    if (memcmp(((dyn_lease*) listdls->arg)->lease_mac, mac, 6) == 0) return -1;
+  }
+  for (sls = gstate.sleases; sls; sls = sls->next)
+    if (sls->nip == nip) return -2;
+
+  if ((ntohl(nip) < gconfig.start_ip) || (ntohl(nip) > gconfig.end_ip))
+    return -3;
+
+  return 0;
+}
+
+/*
+ * add ip assigned_nip to dynamic lease.
+ */
+static int addip_to_lease(uint32_t assigned_nip, uint8_t mac[6], uint32_t *req_exp, char *hostname, uint8_t update)
+{
+  struct arg_list *listdls = gstate.dleases;
+  dyn_lease *dls;
+  uint32_t now = time(NULL);
+
+  while (listdls) {
+    if (memcmp(((dyn_lease*) listdls->arg)->lease_mac, mac, 6) == 0) {
+      if (update) *req_exp = get_lease(*req_exp + ((dyn_lease*) listdls->arg)->expires);
+      ((dyn_lease*) listdls->arg)->expires = *req_exp + now;
+      return 0;
+    }
+    listdls = listdls->next;
+  }
+
+  dls = xzalloc(sizeof(dyn_lease));
+  memcpy(dls->lease_mac, mac, 6);
+  dls->lease_nip = assigned_nip;
+  if (hostname) memcpy(dls->hostname, hostname, 20);
+
+  if (update) *req_exp = get_lease(*req_exp + now);
+  dls->expires = *req_exp + now;
+
+  listdls = xzalloc(sizeof(struct arg_list));
+  listdls->next = gstate.dleases;
+  listdls->arg = (char*)dls;
+  gstate.dleases = listdls;
+
+  return 0;
+}
+
+/*
+ * delete ip assigned_nip from dynamic lease.
+ */
+static int delip_from_lease(uint32_t assigned_nip, uint8_t mac[6], uint32_t del_time)
+{
+  struct arg_list *listdls = gstate.dleases;
+
+  while (listdls) {
+    if (memcmp(((dyn_lease*) listdls->arg)->lease_mac, mac, 6) == 0) {
+      ((dyn_lease*) listdls->arg)->expires = del_time + time(NULL);
+      return 0;
+    }
+    listdls = listdls->next;
+  }
+  return -1;
+}
+
+/*
+ * returns a IP from static, dynamic leases or free ip pool, 0 otherwise.
+ */
+static uint32_t getip_from_pool(uint32_t req_nip, uint8_t mac[6], uint32_t *req_exp, char *hostname)
+{
+  uint32_t nip = 0;
+  static_lease *sls = gstate.sleases;
+  struct arg_list *listdls = gstate.dleases, *tmp = NULL;
+
+  if (req_nip && (verifyip_in_lease(req_nip, mac) == 0)) nip = req_nip;
+
+  if (!nip) {
+    while (listdls) {
+      if (memcmp(((dyn_lease*)listdls->arg)->lease_mac, mac, 6) == 0) {
+        nip = ((dyn_lease*)listdls->arg)->lease_nip;
+        if (tmp) tmp->next = listdls->next;
+        else gstate.dleases = listdls->next;
+        free(listdls->arg);
+        free(listdls);
+        if (verifyip_in_lease(nip, mac) < 0) nip = 0;
+        break;
+      }
+      tmp = listdls;
+      listdls = listdls->next;
+    }
+  }
+  if (!nip) {
+    while (sls) {
+      if (memcmp(sls->mac, mac, 6) == 0) {
+        nip = sls->nip;
+        break;
+      }
+      sls = sls->next;
+    }
+  }
+  if (!nip) {
+    for (nip = htonl(gconfig.start_ip); ntohl(nip) <= gconfig.end_ip; ) {
+      if (verifyip_in_lease(nip, mac) == 0) break;
+      nip = ntohl(nip);
+      nip = htonl(++nip);
+    }
+    if (ntohl(nip) > gconfig.end_ip) {
+      nip = 0;
+      infomsg(infomode, "can't find free IP in IP Pool.");
+    }
+  }
+  if (nip) addip_to_lease(nip, mac, req_exp, hostname, 1);
+  return nip;
+}
+
+/*
+ * Read lease file.
+ */
+static int read_leasefile(void)
+{
+  int fd, ret = -1;
+  uint32_t passed, ip;
+  int32_t tmp_time;
+  int64_t timestamp;
+  dyn_lease *dls;
+
+  fd = open(gconfig.lease_file, O_RDONLY);
+  if (fd < 0) return fd;
+  dls = xzalloc(sizeof(dyn_lease));
+
+  if (read(fd, &timestamp, sizeof(timestamp)) != sizeof(timestamp)) goto error_exit;
+
+  timestamp = SWAP_BE64(timestamp);
+  passed = time(NULL) - timestamp;
+
+  if ((uint64_t)passed > 12 * 60 * 60) goto error_exit;
+
+  while (read(fd, dls, sizeof(dyn_lease)) == sizeof(dyn_lease)) {
+    ip = ntohl(dls->lease_nip);
+    if (ip >= gconfig.start_ip && ip <= gconfig.end_ip) {
+      tmp_time = ntohl(dls->expires) - passed;
+      if (tmp_time < 0) continue;
+      addip_to_lease(dls->lease_nip, dls->lease_mac, (uint32_t*)&tmp_time, dls->hostname, 0);
+    }
+  }
+  ret = 0;
+error_exit:
+  free(dls);
+  close(fd);
+  return ret;
+}
+
+void udhcpd_main(void)
+{
+  struct timeval tv;
+  int retval, bufflen=0;
+  uint8_t *optptr, msgtype = 0;
+  uint32_t waited = 0, serverid = 0, requested_nip = 0;
+  uint32_t reqested_lease = 0, ip_pool_size = 0;
+  char *hstname = NULL;
+  fd_set rfds;
+
+  infomode = LOG_CONSOLE;
+  if (!(flag_chk(FLAG_f))) {
+    dhcp_daemon();
+    infomode = LOG_SILENT;
+  }
+  if (flag_chk(FLAG_S)) {
+        openlog("UDHCPD :", LOG_PID, LOG_DAEMON);
+        infomode |= LOG_SYSTEM;
+  }
+  setlinebuf(stdout);
+  parse_server_config((toys.optc==1)?toys.optargs[0]:DHCPD_CONF_FILE, keywords);
+  infomsg(infomode, "udhcpd (v0.1.20) started");
+  gconfig.start_ip = ntohl(gconfig.start_ip);
+  gconfig.end_ip = ntohl(gconfig.end_ip);
+  ip_pool_size = gconfig.end_ip - gconfig.start_ip + 1;
+  if (gconfig.max_leases > ip_pool_size) {
+    error_msg("max_leases=%u is too big, setting to %u", (unsigned) gconfig.max_leases, ip_pool_size);
+    gconfig.max_leases = ip_pool_size;
+  }
+  write_pid(gconfig.pidfile);
+  set_maxlease();
+  read_leasefile();
+
+  if (get_interface(gconfig.interface, &gconfig.ifindex, &gconfig.server_nip, gconfig.server_mac)<0)
+    perror_exit("Failed to get interface %s", gconfig.interface);
+  gconfig.server_nip = htonl(gconfig.server_nip);
+
+  setup_signal();
+  open_listensock();
+  fcntl(gstate.listensock, F_SETFD, FD_CLOEXEC);
+
+  for (;;) {
+    FD_ZERO(&rfds);
+    FD_SET(gstate.listensock, &rfds);
+    FD_SET(sigfd.rd, &rfds);
+    tv.tv_sec = gconfig.auto_time - waited;
+    tv.tv_usec = 0;
+    retval = 0;
+    serverid = 0;
+    msgtype = 0;
+
+    int maxfd = (sigfd.rd > gstate.listensock)? sigfd.rd : gstate.listensock;
+    dbg("select waiting ....\n");
+    uint32_t timestmp = time(NULL);
+    retval = select(maxfd + 1, &rfds, NULL, NULL, (gconfig.auto_time?&tv:NULL));
+    if (retval < 0) {
+      if (errno == EINTR) {
+        waited += (unsigned) time(NULL) - timestmp;
+        continue;
+      }
+      dbg("Error in select wait again...\n");
+      continue;
+    }
+    if (retval == 0) { /* Timed out */
+      dbg("select wait Timed Out...\n");
+      waited = 0;
+      write_leasefile();
+      if (get_interface(gconfig.interface, &gconfig.ifindex, &gconfig.server_nip, gconfig.server_mac)<0)
+        perror_exit("Interface lost %s\n", gconfig.interface);
+      gconfig.server_nip = htonl(gconfig.server_nip);
+      continue;
+    }
+    if (FD_ISSET(sigfd.rd, &rfds)) { /* Some Activity on RDFDs : is signal */
+      unsigned char sig;
+      if (read(sigfd.rd, &sig, 1) != 1) {
+        dbg("signal read failed.\n");
+        continue;
+      }
+      switch (sig) {
+      case SIGUSR1:
+        infomsg(infomode, "Received SIGUSR1");
+        write_leasefile();
+        continue;
+      case SIGTERM:
+        infomsg(infomode, "Received SIGTERM");
+        write_leasefile();
+        unlink(gconfig.pidfile);
+        exit(0);
+        break;
+      default: break;
+      }
+    }
+    if (FD_ISSET(gstate.listensock, &rfds)) { /* Some Activity on RDFDs : is socket */
+      dbg("select listen sock read\n");
+      bufflen = read_packet();
+      if (bufflen < 0) {
+        open_listensock();
+        continue;
+      }
+      waited += time(NULL) - timestmp;
+      get_optval((uint8_t*)&gstate.rcvd_pkt.options, DHCP_OPT_MESSAGE_TYPE, &gstate.rqcode);
+      if (gstate.rqcode == 0 || gstate.rqcode < DHCPMIN || gstate.rqcode > DHCPMAX) {
+        dbg("no or bad message type option, ignoring packet.\n");
+        continue;
+      }
+      get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_SERVER_ID, &serverid);
+      if (serverid) {
+        if (serverid != gconfig.server_nip) {
+          dbg("server ID doesn't match, ignoring packet.\n");
+          continue;
+        }
+      }
+      switch (gstate.rqcode) {
+      case DHCPDISCOVER:
+        msgtype = DHCPOFFER;
+        dbg("Message Type : DHCPDISCOVER\n");
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_REQUESTED_IP, &requested_nip);
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_HOST_NAME, &hstname);
+        reqested_lease = gconfig.offer_time;
+        get_reqparam(&gstate.rqopt);
+        optptr = prepare_send_pkt();
+        gstate.send_pkt.yiaddr = getip_from_pool(requested_nip, gstate.rcvd_pkt.chaddr, &reqested_lease, hstname);
+        if(!gstate.send_pkt.yiaddr){
+          msgtype = DHCPNAK;
+          optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+          send_packet(1);
+          break;
+        }
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_LEASE_TIME, &reqested_lease);
+        reqested_lease = htonl(get_lease(reqested_lease + time(NULL)));
+        optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+        optptr = set_optval(optptr, DHCP_OPT_SERVER_ID, &gconfig.server_nip, 4);
+        optptr = set_optval(optptr, DHCP_OPT_LEASE_TIME, &reqested_lease, 4);
+        optptr = set_reqparam(optptr, gstate.rqopt);
+        send_packet(1);
+        break;
+      case DHCPREQUEST:
+        msgtype = DHCPACK;
+        dbg("Message Type : DHCPREQUEST\n");
+        get_reqparam(&gstate.rqopt);
+        optptr = prepare_send_pkt();
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_REQUESTED_IP, &requested_nip);
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_LEASE_TIME, &reqested_lease);
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_HOST_NAME, &hstname);
+        gstate.send_pkt.yiaddr = getip_from_pool(requested_nip, gstate.rcvd_pkt.chaddr, &reqested_lease, hstname);
+        if (!serverid) {
+          reqested_lease = gconfig.max_lease_sec;
+        }
+        if (!gstate.send_pkt.yiaddr) {
+          msgtype = DHCPNAK;
+          optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+          send_packet(1);
+          break;
+        }
+        optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+        optptr = set_optval(optptr, DHCP_OPT_SERVER_ID, &gconfig.server_nip, 4);
+        reqested_lease = htonl(reqested_lease);
+        optptr = set_optval(optptr, DHCP_OPT_LEASE_TIME, &reqested_lease, 4);
+        optptr = set_reqparam(optptr, gstate.rqopt);
+        send_packet(1);
+        write_leasefile();
+        break;
+      case DHCPDECLINE:/* FALL THROUGH */
+      case DHCPRELEASE:
+        dbg("Message Type : DHCPDECLINE or DHCPRELEASE \n");
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_SERVER_ID, &serverid);
+        if (serverid != gconfig.server_nip) break;
+        get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_REQUESTED_IP, &requested_nip);
+        delip_from_lease(requested_nip, gstate.rcvd_pkt.chaddr, (gstate.rqcode==DHCPRELEASE)?0:gconfig.decline_time);
+        break;
+      default:
+        dbg("Message Type : %u\n", gstate.rqcode);
+        break;
+      }
+    }
+  }
+}
diff --git a/toys/other/unshare.c b/toys/other/unshare.c
new file mode 100644 (file)
index 0000000..9a93818
--- /dev/null
@@ -0,0 +1,39 @@
+/* unshare.c - run command in new context
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+
+USE_UNSHARE(NEWTOY(unshare, "<1^nium", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNSHARE
+  bool "unshare"
+  default y
+  depends on TOYBOX_CONTAINER
+  help
+    usage: unshare [-muin] COMMAND...
+
+    Create new namespace(s) for this process and its children, so some
+    attribute is not shared with the parent process.  This is part of
+    Linux Containers.  Each process can have its own:
+
+    -m Mount/unmount tree
+    -u Host and domain names
+    -i SysV IPC (message queues, semaphores, shared memory)
+    -n Network address, sockets, routing, iptables
+*/
+
+#include "toys.h"
+#include <linux/sched.h>
+extern int unshare (int __flags);
+
+void unshare_main(void)
+{
+  unsigned flags[]={CLONE_NEWNS, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWNET, 0};
+  unsigned f=0;
+  int i;
+
+  for (i=0; flags[i]; i++) if (toys.optflags & (1<<i)) f |= flags[i];
+
+  if(unshare(f)) perror_exit("failed");
+
+  xexec(toys.optargs);
+}
diff --git a/toys/other/uptime.c b/toys/other/uptime.c
new file mode 100644 (file)
index 0000000..f4ce5e4
--- /dev/null
@@ -0,0 +1,45 @@
+/* uptime.c - Tell how long the system has been running.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_UPTIME(NEWTOY(uptime, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config UPTIME
+  bool "uptime"
+  default y
+  help
+    usage: uptime
+
+    Tell how long the system has been running and the system load
+    averages for the past 1, 5 and 15 minutes.
+*/
+
+#include "toys.h"
+
+void uptime_main(void)
+{
+  struct sysinfo info;
+  time_t tmptime;
+  struct tm * now;
+  unsigned int days, hours, minutes;
+
+  // Obtain the data we need.
+  sysinfo(&info);
+  time(&tmptime);
+  now = localtime(&tmptime);
+
+  // Time
+  xprintf(" %02d:%02d:%02d up ", now->tm_hour, now->tm_min, now->tm_sec);
+  // Uptime
+  info.uptime /= 60;
+  minutes = info.uptime%60;
+  info.uptime /= 60;
+  hours = info.uptime%24;
+  days = info.uptime/24;
+  if (days) xprintf("%d day%s, ", days, (days!=1)?"s":"");
+  if (hours) xprintf("%2d:%02d, ", hours, minutes);
+  else printf("%d min, ", minutes);
+
+  printf(" load average: %.02f %.02f %.02f\n", info.loads[0]/65536.0,
+    info.loads[1]/65536.0, info.loads[2]/65536.0);
+}
diff --git a/toys/other/usleep.c b/toys/other/usleep.c
new file mode 100644 (file)
index 0000000..6040cc0
--- /dev/null
@@ -0,0 +1,26 @@
+/* usleep.c - Wait for a number of microseconds.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_USLEEP(NEWTOY(usleep, "<1", TOYFLAG_BIN))
+
+config USLEEP
+  bool "usleep"
+  default y
+  help
+    usage: usleep MICROSECONDS
+
+    Pause for MICROSECONDS microseconds.
+*/
+
+#include "toys.h"
+
+void usleep_main(void)
+{
+  struct timespec tv;
+  long delay = atol(*toys.optargs);
+
+  tv.tv_sec = delay/1000000;
+  tv.tv_nsec = (delay%1000000) * 1000;
+  toys.exitval = !!nanosleep(&tv, NULL);
+}
diff --git a/toys/other/vconfig.c b/toys/other/vconfig.c
new file mode 100644 (file)
index 0000000..f7505d2
--- /dev/null
@@ -0,0 +1,129 @@
+/* vconfig.c - Creates virtual ethernet devices.
+ *
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gmail.com>, Kyungwan Han <asura321@gmail.com>
+ *
+ * Not in SUSv4.
+
+USE_VCONFIG(NEWTOY(vconfig, "<2>4", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+
+config VCONFIG
+  bool "vconfig"
+  default y
+  help
+    usage: vconfig COMMAND [OPTIONS]
+
+       add             [interface-name] [vlan_id]
+       rem             [vlan-name]
+       set_flag        [interface-name] [flag-num]       [0 | 1]
+       set_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]
+       set_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]
+       set_name_type   [name-type]
+
+    Create and remove virtual ethernet devices
+*/
+
+#include "toys.h"
+#include <linux/if_vlan.h>
+#include <linux/sockios.h>
+/*
+ * convert str to long within given range
+ */
+static int strtol_range(char *str, int min, int max)
+{
+  char *endptr = NULL;
+  errno = 0;
+  long ret_value = strtol(str, &endptr, 10);
+
+  if(errno) perror_exit("Invalid num %s", str);
+  else if(endptr && (*endptr != '\0' || endptr == str))
+    perror_exit("Not a valid num %s", str);
+  if(ret_value >= min && ret_value <= max) return ret_value;
+  else perror_exit("Number %s is not in valid [%d-%d] Range\n", str, min, max);
+}
+/*
+ * vconfig main function.
+ */
+void vconfig_main(void)
+{
+#define MAX_VLAN_ID 4094
+  struct vlan_ioctl_args request;
+  char *interface_name = NULL;
+  unsigned int name_type = VLAN_NAME_TYPE_PLUS_VID;
+  char *cmd;
+  int fd = 0;
+  int vlan_id = 0;
+
+  if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror_exit("Can't open socket"); //Use socket instead of open
+  memset(&request, 0, sizeof(struct vlan_ioctl_args));         // Null set all the VLAN info's.
+  cmd = toys.optargs[0];                                       // Fetch cmd and proceed.
+  if(strcmp(cmd, "set_name_type") == 0) {
+    if(strcmp(toys.optargs[1], "VLAN_PLUS_VID") == 0) {
+      name_type = VLAN_NAME_TYPE_PLUS_VID;
+    }
+    else if(strcmp(toys.optargs[1], "VLAN_PLUS_VID_NO_PAD") == 0) {
+      name_type = VLAN_NAME_TYPE_PLUS_VID_NO_PAD;
+    }
+    else if(strcmp(toys.optargs[1], "DEV_PLUS_VID") == 0) {
+      name_type = VLAN_NAME_TYPE_RAW_PLUS_VID;
+    }
+    else if(strcmp(toys.optargs[1], "DEV_PLUS_VID_NO_PAD") == 0) {
+      name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD;
+    }
+    else perror_exit("ERROR: Invalid name type");
+
+    request.u.name_type = name_type;
+    request.cmd = SET_VLAN_NAME_TYPE_CMD;
+    if(ioctl(fd, SIOCSIFVLAN, &request) == 0) {
+      xprintf("Successful set_name_type for VLAN subsystem\n");
+      exit(EXIT_SUCCESS);
+    }
+    else perror_exit("Failed to set set_name_type:");
+  }
+  else {
+    interface_name = toys.optargs[1]; // Store interface name.
+    if(strlen(interface_name) > 15) perror_exit("ERROR:if_name length can not be greater than 15");
+    strcpy(request.device1, interface_name); //we had exited if interface_name length greater than 15, so here it never overflows.
+  }
+
+  if(strcmp(cmd, "add") == 0) {
+    request.cmd = ADD_VLAN_CMD;
+    if(toys.optargs[2]) vlan_id = strtol_range(toys.optargs[2], 0, MAX_VLAN_ID);
+    else vlan_id = 0;
+    request.u.VID = vlan_id;
+  }
+  else if(strcmp(cmd, "rem") == 0) {
+    request.cmd = DEL_VLAN_CMD;
+  }
+  else if(strcmp(cmd, "set_flag") == 0) {
+    request.cmd = SET_VLAN_FLAG_CMD;
+    if(toys.optargs[2]) request.u.flag = strtol_range(toys.optargs[2], 0, 1);
+    else request.u.flag = 0;
+    if(toys.optargs[3]) request.vlan_qos = strtol_range(toys.optargs[3], 0, 7);
+    else request.vlan_qos = 0;
+  }
+  else if(strcmp(cmd, "set_egress_map") == 0) {
+    request.cmd = SET_VLAN_EGRESS_PRIORITY_CMD;
+    if(toys.optargs[2]) request.u.skb_priority = strtol_range(toys.optargs[2], 0, INT_MAX);
+    else request.u.skb_priority = 0;
+    if(toys.optargs[3]) request.vlan_qos = strtol_range(toys.optargs[3], 0, 7);
+    else request.vlan_qos = 0;
+  }
+  else if(strcmp(cmd, "set_ingress_map") == 0) {
+    request.cmd = SET_VLAN_INGRESS_PRIORITY_CMD;
+    if(toys.optargs[2]) request.u.skb_priority = strtol_range(toys.optargs[2], 0, INT_MAX);
+    else request.u.skb_priority = 0;
+    //To set flag we must have to set vlan_qos
+    if(toys.optargs[3]) request.vlan_qos = strtol_range(toys.optargs[3], 0, 7);
+    else request.vlan_qos = 0;
+  }
+  else {
+    xclose(fd);
+    perror_exit("Unknown command %s", cmd);
+  }
+  if(ioctl(fd, SIOCSIFVLAN, &request) == 0) {
+    if(strcmp(cmd, "add") == 0 && vlan_id == 1)
+      xprintf("WARNING: VLAN 1 does not work with many switches,consider another number if you have problems.\n");
+    xprintf("Successful %s on device %s\n", cmd, interface_name);
+  }
+  else perror_exit("Failed to %s, on vlan subsystem %s.", cmd, interface_name);
+}
diff --git a/toys/other/vmstat.c b/toys/other/vmstat.c
new file mode 100644 (file)
index 0000000..16d45f2
--- /dev/null
@@ -0,0 +1,184 @@
+/* vmstat.c - Report virtual memory statistics.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+
+USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
+
+config VMSTAT
+  bool "vmstat"
+  default y
+  help
+    usage: vmstat [-n] [delay [count]]
+    -n Display the header only once
+    delay      The delay between updates in seconds, when not specified
+       the average since boot is displayed.
+    count      Number of updates to display, the default is inifinite.
+*/
+
+#include "toys.h"
+
+void read_proc_stat(unsigned int *proc_running, unsigned int *proc_blocked,
+  uint64_t *sys_irq, uint64_t *sys_ctxt, uint64_t *cpu_user, uint64_t *cpu_sys,
+  uint64_t *cpu_idle, uint64_t *cpu_wait)
+{
+  char * off;
+  uint64_t c_user, c_nice, c_sys, c_irq, c_sirq;
+  int fd = xopen("/proc/stat", O_RDONLY);
+  size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
+
+  toybuf[s] = 0;
+  if (s == sizeof(toybuf)-1) error_exit("/proc/stat is too large");
+
+  off = strstr(toybuf, "cpu ");
+  // Ignoring steal and guest fields for now.
+  if (off) sscanf(off, "cpu  %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 \
+    " %"PRIu64" %"PRIu64" %"PRIu64, &c_user, &c_nice, &c_sys, cpu_idle,
+    cpu_wait, &c_irq, &c_sirq);
+  *cpu_user = c_user + c_nice;
+  *cpu_sys = c_sys + c_irq + c_sirq;
+  off = strstr(toybuf, "intr");
+  if (off) sscanf(off, "intr %"PRIu64, sys_irq);
+
+  off = strstr(toybuf, "ctxt");
+  if (off) sscanf(off, "ctxt %"PRIu64, sys_ctxt);
+
+  off = strstr(toybuf, "procs_running");
+  if (off) sscanf(off, "procs_running %u", proc_running);
+  (*proc_running)--; // look, i'm invisible.
+
+  off = strstr(toybuf, "procs_blocked");
+  if (off) sscanf(off, "procs_blocked %u", proc_blocked);
+
+  close(fd);
+}
+
+void read_proc_meminfo(unsigned long *mem_swapped, unsigned long *mem_free,
+  unsigned long *mem_buff, unsigned long *mem_cache)
+{
+  char * off;
+  unsigned long swap_total, swap_free;
+  int fd = xopen("/proc/meminfo", O_RDONLY);
+  size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
+
+  toybuf[s] = 0;
+  if (s == sizeof(toybuf)-1) error_exit("/proc/meminfo is too large");
+
+  off = strstr(toybuf, "MemFree");
+  if (off) sscanf(off, "MemFree: %lu kB", mem_free);
+
+  off = strstr(toybuf, "Buffers");
+  if (off) sscanf(off, "Buffers: %lu kB", mem_buff);
+
+  off = strstr(toybuf, "Cached");
+  if (off) sscanf(off, "Cached: %lu kB", mem_cache);
+
+  off = strstr(toybuf, "SwapFree");
+  if (off) sscanf(off, "SwapFree: %lu kB", &swap_free);
+
+  off = strstr(toybuf, "SwapTotal");
+  if (off) sscanf(off, "SwapTotal: %lu kB", &swap_total);
+  *mem_swapped = swap_total - swap_free;
+
+  close(fd);
+}
+
+void read_proc_vmstat(unsigned long *io_pages_in, unsigned long *io_pages_out,
+  unsigned long *swap_bytes_in, unsigned long *swap_bytes_out)
+{
+  char *off;
+  unsigned long s_pages_in, s_pages_out;
+  unsigned long pagesize_kb = sysconf(_SC_PAGESIZE) / 1024L;
+  int fd = xopen("/proc/vmstat", O_RDONLY);
+  size_t s = xread(fd, toybuf, sizeof(toybuf)-1);
+
+  toybuf[s] = 0;
+  if (s == sizeof(toybuf)-1) error_exit("/proc/vmstat is too large");
+
+  off = strstr(toybuf, "pgpgin");
+  if (off) sscanf(off, "pgpgin %lu", io_pages_in);
+
+  off = strstr(toybuf, "pgpgout");
+  if (off) sscanf(off, "pgpgout %lu", io_pages_out);
+
+  off = strstr(toybuf, "pswpin");
+  if (off) sscanf(off, "pswpin %lu", &s_pages_in);
+  *swap_bytes_in = s_pages_in * pagesize_kb;
+
+  off = strstr(toybuf, "pswpout");
+  if (off) sscanf(off, "pswpout %lu", &s_pages_out);
+  *swap_bytes_out = s_pages_out * pagesize_kb;
+
+  close(fd);
+}
+
+void vmstat_main(void)
+{
+  const char fmt[] = "%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n";
+  unsigned int loop_num = 0, loop_max_num = 0, loop_delay = 0;
+  unsigned int running = 0, blocked = 0;
+  unsigned long mem_swap = 0, mem_free = 0, mem_buff = 0, mem_cache = 0;
+  unsigned long io_pages_in[2], io_pages_out[2], swap_bytes_in[2], swap_bytes_out[2];
+  uint64_t sys_irq[2], sys_ctxt[2], cpu_user[2], cpu_sys[2], cpu_idle[2], cpu_wait[2];
+  int first_run = 1;
+  int no_header = toys.optflags;
+  unsigned num_rows = 22;
+
+  if (toys.optc >= 1) loop_delay = atoi(toys.optargs[0]);
+  if (toys.optc >= 2) loop_max_num = atoi(toys.optargs[1]);
+
+  if (loop_max_num < 0 || loop_delay < 0) error_exit("Invalid arguments");
+
+  while(1) {
+    uint64_t total_jif;
+    int idx = loop_num%2;
+
+    if(first_run || (!(loop_num % num_rows) && !no_header)) {
+      unsigned rows = 0, cols = 0;
+      terminal_size(&cols, &rows);
+      num_rows = (rows > 3)? rows - 3 : 22;
+      printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n");
+      printf(" r  b   swpd   free   buff  cache   si   so      bi      bo   in   cs us sy id wa\n");
+    }
+
+    read_proc_stat(&running, &blocked, &sys_irq[idx], &sys_ctxt[idx], &cpu_user[idx],
+      &cpu_sys[idx], &cpu_idle[idx], &cpu_wait[idx]);
+    read_proc_meminfo(&mem_swap, &mem_free, &mem_buff, &mem_cache);
+    read_proc_vmstat(&io_pages_in[idx], &io_pages_out[idx], &swap_bytes_in[idx], &swap_bytes_out[idx]);
+
+    if (first_run) {
+      struct sysinfo inf;
+      sysinfo(&inf);
+      first_run = 0;
+      total_jif = cpu_user[idx] + cpu_idle[idx] + cpu_wait[idx];
+      printf(fmt, running, blocked, mem_swap, mem_free, mem_buff, mem_cache,
+           (unsigned) (swap_bytes_in[idx]/inf.uptime),
+           (unsigned) (swap_bytes_out[idx]/inf.uptime),
+           (unsigned) (io_pages_in[idx]/inf.uptime),
+           (unsigned) (io_pages_out[idx]/inf.uptime),
+           (unsigned) (sys_irq[idx]/inf.uptime),
+           (unsigned) (sys_ctxt[idx]/inf.uptime),
+           (unsigned) (100*cpu_user[idx]/total_jif),
+           (unsigned) (100*cpu_sys[idx]/total_jif),
+           (unsigned) (100*cpu_idle[idx]/total_jif),
+           (unsigned) (100*cpu_wait[idx]/total_jif));
+    }else{
+      total_jif = cpu_user[idx] - cpu_user[!idx] + cpu_idle[idx] - cpu_idle[!idx] + cpu_wait[idx] - cpu_wait[!idx];
+      printf(fmt, running, blocked, mem_swap, mem_free, mem_buff, mem_cache,
+           (unsigned) ((swap_bytes_in[idx] - swap_bytes_in[!idx])/loop_delay),
+           (unsigned) ((swap_bytes_out[idx] - swap_bytes_out[!idx])/loop_delay),
+           (unsigned) ((io_pages_in[idx] - io_pages_in[!idx])/loop_delay),
+           (unsigned) ((io_pages_out[idx] - io_pages_out[!idx])/loop_delay),
+           (unsigned) ((sys_irq[idx] - sys_irq[!idx])/loop_delay),
+           (unsigned) ((sys_ctxt[idx] - sys_ctxt[!idx])/loop_delay),
+           (unsigned) (100*(cpu_user[idx] - cpu_user[!idx])/total_jif),
+           (unsigned) (100*(cpu_sys[idx]  - cpu_sys[!idx]) /total_jif),
+           (unsigned) (100*(cpu_idle[idx] - cpu_idle[!idx])/total_jif),
+           (unsigned) (100*(cpu_wait[idx] - cpu_wait[!idx])/total_jif));
+    }
+
+    loop_num++;
+    if (loop_delay == 0 || (loop_max_num != 0 && loop_num >= loop_max_num))
+      break;
+    sleep(loop_delay);
+  }
+}
diff --git a/toys/other/w.c b/toys/other/w.c
new file mode 100644 (file)
index 0000000..c271e8b
--- /dev/null
@@ -0,0 +1,33 @@
+/* w.c - shows logged in users
+ *
+ * Copyright 2012 Gaurang Shastri <gmshastri@gmail.com>
+
+USE_W(NEWTOY(w, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config W
+  bool "w"
+  default y
+  help
+    usage: w
+
+    Show who is logged on and since how long they logged in.
+*/
+
+#include "toys.h"
+
+void w_main(void)
+{
+  struct utmpx *x;
+
+  xprintf("USER     TTY             LOGIN@              FROM");
+  setutxent();
+  while ((x=getutxent()) != NULL) {
+    if (x->ut_type==7) {
+      time_t tt = x->ut_tv.tv_sec;
+
+      xprintf("\n%-9.8s%-9.8s %-4.24s (%-1.12s)", x->ut_user, x->ut_line,
+        ctime(&tt), x->ut_host);
+    }
+  }
+  xputc('\n');
+}
diff --git a/toys/other/wget.c b/toys/other/wget.c
new file mode 100644 (file)
index 0000000..7fc6cff
--- /dev/null
@@ -0,0 +1,1309 @@
+/* wget.c - Get files from HTTP or FTP.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ * 
+USE_WGET(NEWTOY(wget, "<1csqO:P:Y:U:T#<1", TOYFLAG_BIN))
+
+config WGET
+  bool "wget"
+  default y
+  help
+    usage: wget [-c -s -q -O outputfile -P dirprefix -Y Proxy -U useragent -T timeout] URL.
+
+    wget - Get files from HTTP or FTP.
+
+    -c  resume getting a partially-downloaded file.
+    -s  spider mode. Don’t download anything, only check file existence.
+    -q  quiet (no output).
+    -O FILE  write documents to FILE. ('-' for stdout).
+    -P DIR  save files to DIR (default .)
+    -Y Use   proxy ('on' or 'off').
+    -U AGENT Use AGENT for User-Agent header.
+    -T SEC  set all timeout values to SECONDS.
+*/
+
+#define FOR_wget
+#include "toys.h"
+#include <sys/poll.h>
+
+/*
+ * To Do list: as toybox parsing is not supporting long options.
+ *  "passive-ftp"
+ *  "header"
+ *  "post-data"
+ *  "no-check-certificate"
+ *
+ */
+
+GLOBALS(
+  unsigned timeout;
+  char *useragent;
+  char *proxyflag;
+  char *dirprefix;
+  char *outputfile;
+  int sockfd;
+  int flags;
+  off_t filepos;
+  off_t contentlen;
+  int cleanit;
+  int chunked;
+  int isftp;
+)
+
+typedef struct _hostinfo {
+  char *hostname;
+  const char *username;
+  char *password;
+  const char *path;
+  char *url;
+  int portnum;
+} HOSTINFO;
+
+//HTTP Response Code
+#define HTTP_CONTINUE         100
+#define HTTP_OK               200
+#define HTTP_NOCONTENT        204
+#define HTTP_PARTIALCONTENT   206
+#define HTTP_MULTIPLECHOICES  300
+#define HTTP_MOVEDPERMANENTLY 301
+#define HTTP_FOUND            302
+#define HTTP_SEEOTHER         303
+
+//FTP Response Code
+#define FTPFILE_STATUSOKAY      150
+#define FTPFILE_STATUS          213
+#define FTPSERVER_READY         220
+#define CLOSE_DATACONECTION     226
+#define PASSIVE_MODE            227
+#define USERLOGGED_SUCCESS      230
+#define PASSWORD_REQUEST        331
+#define REQUESTED_PENDINGACTION 350
+
+#define OFLAG_LIST1 O_WRONLY | O_CREAT | O_TRUNC | O_EXCL
+#define OFLAG_LIST2 O_WRONLY | O_CREAT | O_TRUNC
+
+#define IS_DASH(s) ((s)[0] == '-' && !(s)[1])
+#define HTTPSTR  "http://"
+#define FTPSTR "ftp://"
+#define FTPPROXY_VAR "ftp_proxy"
+#define HTTPPROXY_VAR "http_proxy"
+
+#define HTTP_DEFAULT_PORT 80
+#define FTP_DEFAULT_PORT  21
+
+#ifndef MAX_PORT_VALUE
+#define MAX_PORT_VALUE 65535
+#endif
+
+typedef struct sockaddr_with_len {
+  union {
+    struct sockaddr sock;
+    struct sockaddr_in sock_in;
+    struct sockaddr_in6 sock_in6;
+  }sock_u;
+  socklen_t socklen;
+} sockaddr_with_len;
+
+sockaddr_with_len *swl;
+
+static void start_download(const char *);
+static void parse_url(const char *, HOSTINFO *);
+static char *base64_encodeing(char *);
+static void get_ftp_data(FILE *);
+static void get_http_data(FILE *);
+static int sendcommand_to_ftp(const char *, const char *, FILE *);
+static void close_connection(const char *);
+
+/*
+ * copy string from src to dest -> only number of bytes.
+ */
+static char *safe_strncpy(char *dst, const char *src, size_t size)
+{
+  if(!size) return dst;
+  dst[--size] = '\0';
+  return strncpy(dst, src, size);
+}
+/*
+ * Remove white spaces from the given string.
+ */
+static char *omit_whitespace(char *s)
+{
+  while(*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) s++;
+  return (char *) s;
+}
+/*
+ * Remove non white spaces from the given string.
+ */
+static char *omitnon_whitespace(char *s)
+{
+  while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9)) s++;
+  return (char *) s;
+}
+#ifndef _GNU_SOURCE
+/*
+ * locate character in string.
+ */
+static char *strchrnul(char *s, int c)
+{
+  while(*s != '\0' && *s != c) s++;
+  return (char*)s;
+}
+#endif
+/* Get last path component with no strip.
+ * e.g.
+ * "/"    -> "/"
+ * "abc"    -> "abc"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> ""
+ */
+static char *get_last_path_component_withnostrip(char *path)
+{
+  char *slash = strrchr(path, '/');
+  if (!slash || (slash == path && !slash[1])) return (char*)path;
+  return slash + 1;
+}
+// Find out if the last character of a string matches with the given one.
+// Don't underrun the buffer if the string length is 0.
+static char *find_last_char(char *str, int c)
+{
+  if (str && *str) {
+    size_t sz = strlen(str) - 1;
+    str += sz;
+    if ( (unsigned char)*str == c) return (char*)str;
+  }
+  return NULL;
+}
+/*
+ * Concat path and the file name.
+ */
+static char *append_pathandfile(char *path, char *fname)
+{
+  char *c;
+  if (!path) path = "";
+  c = find_last_char(path, '/');
+  while (*fname == '/') fname++;
+  return xmsprintf("%s%s%s", path, (c==NULL ? "/" : ""), fname);
+}
+
+/*
+ * used to converts string into int and validate the input str for invalid int value or out-of-range.
+ */
+static unsigned get_strtou(const char *str, char **endp, int base)
+{
+  unsigned long uli;
+  char *endptr;
+
+  if(!isalnum(str[0])) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+  errno = 0;
+  uli = strtoul(str, &endptr, base);
+  if(uli > UINT_MAX) {
+    errno = ERANGE;
+    return UINT_MAX;
+  }
+
+  if(endp) *endp = endptr;
+  if(endptr[0]) {
+    if(isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
+      errno = ERANGE;
+      return UINT_MAX;
+    }
+    errno = EINVAL;
+  }
+  return uli;
+}
+
+/*
+ * verify the host is local unix path.
+ * if so, set the swl input param accordingly.
+ */
+static int is_host_unix(const char *host, sockaddr_with_len **swl)
+{
+  if(strncmp(host, "local:", 6) == 0) {
+    struct sockaddr_un *sockun;
+    *swl = xzalloc(sizeof(struct sockaddr_with_len));
+    (*swl)->socklen = sizeof(struct sockaddr_un);
+    (*swl)->sock_u.sock.sa_family = AF_UNIX;
+    sockun = (struct sockaddr_un *)&(*swl)->sock_u.sock;
+    safe_strncpy(sockun->sun_path, host + 6, sizeof(sockun->sun_path));
+    return 1;
+  }
+  return 0;
+}
+
+/*
+ * validate the input param (host) for valid ipv6 ip and extract port number (if there).
+ */
+static void get_host_and_port(char **host, int *port)
+{
+  char *ch_ptr;
+  const char *org_host = *host;
+  if(*host[0] == '[') {
+    (*host)++;
+    ch_ptr = strchr(*host, ']');
+    if(!ch_ptr || (ch_ptr[1] != ':' && ch_ptr[1] != '\0'))
+      error_exit("bad address '%s'", org_host);
+  }
+  else {
+    ch_ptr = strrchr(*host, ':');
+    //There is more than one ':' like "::1"
+    if(ch_ptr && strchr(*host, ':') != ch_ptr)
+      ch_ptr = NULL;
+  }
+  if(ch_ptr) { //pointer to ":" or "]:"
+    int size = ch_ptr - (*host) + 1;
+    safe_strncpy(*host, *host, size);
+    if(*ch_ptr != ':') {
+      ch_ptr++; //skip ']'
+      //[nn] without port
+      if(*ch_ptr == '\0')
+        return;
+    }
+    ch_ptr++; //skip ':' to get the port number.
+    *port = get_strtou(ch_ptr, NULL, 10);
+    if(errno || (unsigned)*port > MAX_PORT_VALUE)
+      error_exit("bad port spec '%s'", org_host);
+   }
+  return;
+}
+
+/*
+ * used to extract the address info from the given host ip
+ * and update the swl param accordingly.
+ */
+static int get_socket_stream(const char *host, sa_family_t af, sockaddr_with_len **swl)
+{
+  struct addrinfo hints;
+  struct addrinfo *result, *rp;
+  int status = 0;
+
+  memset(&hints, 0 , sizeof(struct addrinfo));
+  hints.ai_family = af;
+  hints.ai_socktype = SOCK_STREAM;
+
+  if((status = getaddrinfo(host, NULL, &hints, &result)) != 0) {
+    perror_exit("bad address '%s' : %s", host, gai_strerror(status));
+    return status;
+  }
+
+  for(rp = result; rp != NULL; rp = rp->ai_next) {
+    if( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) {
+      *swl = xmalloc(sizeof(struct sockaddr_with_len));
+      (*swl)->socklen = rp->ai_addrlen;
+      memcpy(&((*swl)->sock_u.sock), rp->ai_addr, rp->ai_addrlen);
+      break;
+    }
+  }
+  freeaddrinfo(result);
+  return ((!rp)? -1: status);
+}
+
+/*
+ * used to set the port number for ipv4 / ipv6 addresses.
+ */
+static void setport(struct sockaddr *sock, unsigned port_num)
+{
+  //for ipv4
+  if(sock->sa_family == AF_INET) {
+    struct sockaddr_in *sock_in = (void*)sock;
+    sock_in->sin_port = port_num;
+  }
+  //for ipv6
+  else if(sock->sa_family == AF_INET6) {
+    struct sockaddr_in6 *sock_in6 = (void*)sock;
+    sock_in6->sin6_port = port_num;
+  }
+  return;
+}
+
+/*
+ * use to get the socket address with the given host ip.
+ */
+static sockaddr_with_len *get_sockaddr(const char *host, int port, sa_family_t af)
+{
+  sockaddr_with_len *swl = NULL;
+  int status = 0;
+
+  //for unix
+  int is_unix = is_host_unix(host, &swl);
+  if(is_unix && swl) return swl;
+
+  //[IPV6_ip]:port_num
+  if(host[0] == '[' || strrchr(host, ':')) get_host_and_port((char **)&host, &port);
+
+  //for the socket streams.
+  status = get_socket_stream(host, af, &swl);
+  if(status) return NULL;
+
+  setport(&swl->sock_u.sock, htons(port));
+  return swl;
+}
+
+/*
+ * get the numeric hostname and service name, for a given socket address.
+ */
+static char *address_to_name(const struct sockaddr *sock)
+{
+  //man page of getnameinfo.
+  char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
+  int status = 0;
+  if(sock->sa_family == AF_INET) {
+    socklen_t len = sizeof(struct sockaddr_in);
+    if((status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) == 0)
+      return xmsprintf("%s:%s", hbuf, sbuf);
+    else {
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(status));
+      return NULL;
+    }
+  }
+  else if(sock->sa_family == AF_INET6) {
+    socklen_t len = sizeof(struct sockaddr_in6);
+    if((status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) == 0) {
+      //verification for resolved hostname.
+      if(strchr(hbuf, ':')) return xmsprintf("[%s]:%s", hbuf, sbuf);
+      else return xmsprintf("%s:%s", hbuf, sbuf);
+    }
+    else {
+      fprintf(stderr, "getnameinfo: %s\n", gai_strerror(status));
+      return NULL;
+    }
+  }
+  else if(sock->sa_family == AF_UNIX) {
+    struct sockaddr_un *sockun = (void*)sock;
+    return xmsprintf("local:%.*s", (int) sizeof(sockun->sun_path), sockun->sun_path);
+  }
+  return NULL;
+}
+
+/*
+ * used to get the port number for ftp and http.
+ */
+static unsigned getport(const char *port, const char *protocol, unsigned defport)
+{
+#define RANGE_STR "Port Number should be in [1-65535] range"
+  long v;
+  char *endptr = NULL;
+
+  unsigned portnum = defport;
+  if(port) {
+    errno = 0;
+    v = strtol(port, &endptr, 10);
+    if((v > 0) && (!*endptr)) {//for numeric like 123
+      portnum = v;
+      if(portnum > MAX_PORT_VALUE) error_exit("Invalid Port Number '%s' "RANGE_STR, port);
+    }
+    else if((v == 0) && (endptr != NULL)) {
+      switch(defport) {
+        case FTP_DEFAULT_PORT:
+          if(strcmp(endptr, "ftp") == 0) portnum = defport; //for "ftp" string.
+          else goto ERROR_EXIT;
+          break;
+        case HTTP_DEFAULT_PORT:
+          if(strcmp(endptr, "http") == 0) portnum = defport;//for "HTTP" string.
+          else goto ERROR_EXIT;
+          break;
+        default:
+ERROR_EXIT:
+          error_exit("Invalid Port");
+          break;
+      }
+    }
+    else perror_exit("Invalid Port Number: '%s' "RANGE_STR, port);
+  }
+#undef RANGE_STR
+  return (uint16_t)portnum;
+}
+
+/*
+ * used to connect with the socket.
+ */
+static int connect_to_stream(const sockaddr_with_len *swl)
+{
+  int sockfd;
+  if((sockfd = socket(swl->sock_u.sock.sa_family, SOCK_STREAM, 0)) < 0)
+    perror_exit("cannot open control socket");
+  if(connect(sockfd, &swl->sock_u.sock, swl->socklen) < 0) {
+    close(sockfd);
+    perror_exit("can't connect to remote host");
+  }
+  return sockfd;
+}
+
+/*
+ * used to connect to socket.
+ */
+static FILE *do_login(sockaddr_with_len *swl)
+{
+  FILE *fp;
+  int fd = connect_to_stream(swl);
+  fp = fdopen(fd, "r+");
+  if(!fp) perror_exit("unable to connect:");
+  return fp;
+}
+
+/*
+ * wget main function.
+ */
+void wget_main(void)
+{
+  char **argv = toys.optargs; //url or ftp file name.
+
+  if(!(toys.optflags & FLAG_Y)) TT.proxyflag = "on";
+  else {
+    if(strcmp(TT.proxyflag, "on") && strcmp(TT.proxyflag, "off"))
+     error_exit("Proxy will be either on/off");
+  }
+  if(!(toys.optflags & FLAG_U)) TT.useragent = "Wget";
+  if(!(toys.optflags & FLAG_T)) TT.timeout = 900;
+  if(!(toys.optflags & FLAG_c)) TT.filepos = 0;
+  
+  TT.sockfd = -1;
+
+  TT.flags = OFLAG_LIST1;
+  //If outputfile name is in the command line argument.
+  if(TT.outputfile) {
+    if(IS_DASH(TT.outputfile)) {
+      TT.sockfd = 1; //stdout
+      toys.optflags &= ~FLAG_c;
+    }
+    //Modify the OFLAG_LIST.
+    TT.flags = OFLAG_LIST2;
+  }
+  while(*argv) start_download(*argv++);
+  
+  if(TT.sockfd >= 0) xclose(TT.sockfd);
+  return;
+}
+
+/*
+ * get http response data.
+ */
+static void get_http_responsedata(FILE *fp, char *ch)
+{
+  char *ptr;
+  if(fgets(toybuf, sizeof(toybuf) - 1, fp) == NULL) {
+    fclose(fp);
+    fp = NULL;
+    perror_exit("error in getting response data");
+  }
+  //remove '\n' and '\r' characters (if any).
+  ptr = strchrnul(toybuf, '\n');
+  ch[0] = *ptr;
+  ch[1] = 0;
+  *ptr = '\0';
+  ptr = strchrnul(toybuf, '\r');
+  *ptr = '\0';
+  return;
+}
+
+/*
+ * get header info of http.
+*/
+static char *get_header(FILE *fp)
+{
+  char *str, *headerval;
+  char newline_ch;
+  get_http_responsedata(fp, &newline_ch); //get header line.
+
+  //end of the headers.
+  if(toybuf[0] == '\0') return NULL;
+  //convert the header name to lower case.
+  for(str = toybuf; isalnum(*str) || *str == '-' || *str == '.'; ++str) {*str |= 0x20;}
+  //verify we are at the end of the header name.
+  if(*str != ':') perror_exit("bad header line: ");
+  //locate the start of the header value.
+  *str++ = '\0';
+  headerval = omit_whitespace(str);
+
+  if(newline_ch != '\n') {
+    for(newline_ch = getc(fp); newline_ch != EOF; newline_ch = getc(fp)) {
+      if(newline_ch == '\n') break;
+    }
+  }
+  return headerval;
+}
+
+/*
+ * resolve host name.
+ */
+static sockaddr_with_len *resolve_hostname(HOSTINFO *srchostinfo)
+{
+  sockaddr_with_len *swl = get_sockaddr(srchostinfo->hostname, srchostinfo->portnum, AF_UNSPEC);
+  if(!swl) error_exit("error in resolving host name");
+  if(!(toys.optflags & FLAG_q)) {
+    char *str = address_to_name(&swl->sock_u.sock);
+    if(str != NULL) {
+      fprintf(stderr, "Connecting to %s (%s)\n", srchostinfo->hostname, str);
+      free(str);
+      str = NULL;
+    }
+  }
+  return swl;
+}
+
+/*
+ * prepare http request.
+ */
+static void prepare_http_request(int isproxy, FILE *srcfp, HOSTINFO *srchostinfo, HOSTINFO *dsthostinfo)
+{
+  if(isproxy) fprintf(srcfp, "GET %stp://%s/%s HTTP/1.1\r\n", TT.isftp ? "f" : "ht", dsthostinfo->hostname, dsthostinfo->path);
+  else fprintf(srcfp, "GET /%s HTTP/1.1\r\n", dsthostinfo->path);
+  fprintf(srcfp, "Host: %s\r\nUser-Agent: %s\r\n", dsthostinfo->hostname, TT.useragent);
+  //close the connection as soon as task done.
+  fprintf(srcfp, "Connection: close\r\n");
+  //Use Authenication:
+  //<user>:<passwd>" is base64 encoded.
+  //URL-decode "user:password" string before base64-encoding:
+  //wget http://username:pass%50word@example.com should send
+  //which decodes to "username:pass word"
+  if(dsthostinfo->username) {
+    char *username = base64_encodeing((char *)dsthostinfo->username);
+    fprintf(srcfp, "Proxy-Authorization: Basic %s\r\n", username);
+    free(username);
+    username = NULL;
+  }
+  if(isproxy && srchostinfo->username) {
+    char *username = base64_encodeing((char *)srchostinfo->username);
+    fprintf(srcfp, "Proxy-Authorization: Basic %s\r\n", username);
+    free(username);
+    username = NULL;    
+  }
+
+  if(TT.filepos) fprintf(srcfp, "Range: bytes=%lu-\r\n", (unsigned long) TT.filepos);
+  fprintf(srcfp, "\r\n");
+  fflush(srcfp);
+  return;
+}
+
+/*
+ * get http response.
+ */
+static int get_http_response(FILE *srcfp, int *cmd_status)
+{
+  char *str, ch;
+  get_http_responsedata(srcfp, &ch);
+  str = toybuf;
+  //e.g. "HTTP/1.1 301 Moved Permanently"
+  //we need to get 301 value.
+  str = omitnon_whitespace(str);
+  str = omit_whitespace(str);
+
+  str[3] = '\0';
+  if(*str == '\0') perror_exit("Error in getting http response status");
+
+  *cmd_status = get_int_value(str, 0, INT_MAX);
+
+  switch(*cmd_status) {
+    case HTTP_CONTINUE:
+      while(get_header(srcfp) != NULL); //leave all remaining headers(if any).
+      return 0;
+    case HTTP_OK: /*FALL THROUGH*/
+    case HTTP_NOCONTENT:
+      break;
+    case HTTP_MULTIPLECHOICES: /*FALL THROUGH*/
+    case HTTP_MOVEDPERMANENTLY: /*FALL THROUGH*/
+    case HTTP_FOUND: /*FALL THROUGH*/
+    case HTTP_SEEOTHER:
+      break;
+    case HTTP_PARTIALCONTENT:
+      if(TT.filepos) break;
+    default:
+      close_connection("server error:");
+      break;
+  }
+  return 1;
+}
+
+/*
+ * get http header keyword
+ * content length - server need to know the length of the content before it starts transmitting.
+ * transfer encoding - data transfer mechanism, where server does not need to know the length of the content before it starts transmitting.
+ * location - either to load a different web page or the location of a newly-created resource.
+ */
+static unsigned int get_http_keyword(char **str)
+{
+  int index = 0;
+  static const char HTTP_keywords[] =
+    "content-length\0""transfer-encoding\0""location\0";
+
+  char *key_string = (char *)HTTP_keywords;
+  //remove '\t' from the http header(if any).
+  char *ptr = strchrnul(*str, '\0') - 1;
+  while(ptr >= *str && (*ptr == ' ' || *ptr == '\t')) {
+    *ptr = '\0';
+    ptr--;
+  }
+
+  while(*key_string) {
+    if(strcmp(key_string, toybuf) == 0) return (index + 1);
+    key_string += strlen(key_string) + 1;
+    index++;
+  }
+  return -1;
+}
+
+/*
+ * verify content length for garbage data.
+ */
+static void verify_content_len(char *str)
+{
+  if(TT.contentlen < 0 || errno) {
+    strncpy(toybuf, str, strlen(str));
+    perror_exit("content-length garbage: ");
+  }
+  TT.cleanit = 1;
+  return;
+}
+
+/*
+ * verify location for change location.
+ * if new location change source host and dest host info accordingly.
+ */
+static int verify_location(int isproxy, char *str, HOSTINFO **srchostinfo, HOSTINFO **dsthostinfo)
+{
+  int is_new_location = 0;
+  static int rd_dirlevel = 5; //5 level of directory read limit.
+  HOSTINFO *shostinfo = *srchostinfo;
+  HOSTINFO *dhostinfo = *dsthostinfo;
+
+  //verify the directory read level.
+  if(--rd_dirlevel == 0) perror_exit("max dir level reached");
+  //if location is changed.
+  if(str[0] == '/') dhostinfo->path = xstrdup(str+1);
+  else {
+    parse_url(str, dhostinfo);
+    if(!isproxy) {
+      if(shostinfo->url) {
+        free(shostinfo->url);
+        shostinfo->url = NULL;
+      }
+      shostinfo->hostname = dhostinfo->hostname;
+      shostinfo->portnum = dhostinfo->portnum;
+      is_new_location = 1;
+    }
+  }
+  return is_new_location;
+}
+
+/*
+ * Convert the input string in the lower.
+ */
+static char *upper_to_lower(char *str)
+{
+  char *c;
+  for(c = str; *c; ++c) *c = tolower(*c);
+  return str;
+}
+
+/*
+ * create http session and verify the data.
+ */
+static FILE *create_http_session(int isproxy, HOSTINFO *srchostinfo, HOSTINFO *dsthostinfo)
+{
+#define CONTENT_KEY 1
+#define TRANSFER_KEY 2
+#define LOCATION_KEY 3
+
+  sockaddr_with_len *swl;
+  FILE *srcfp; //socket to web server.
+  char *str;
+  int status = 0;
+  int isreadall = 0;
+
+NEWADDRESS:
+  swl = resolve_hostname(srchostinfo);
+NEWSESSION:
+  TT.chunked = TT.cleanit = 0;
+  srcfp = do_login(swl); //Open socket for http server.
+
+  //Prepare HTTP request.
+  prepare_http_request(isproxy, srcfp, srchostinfo, dsthostinfo);
+
+  //Get HTTP response.
+  do {
+    isreadall = get_http_response(srcfp, &status);
+  } while(!isreadall);
+
+  //getting HTTP headers.
+  while((str = get_header(srcfp)) != NULL) {
+    unsigned key = get_http_keyword(&str);
+
+    if(key == CONTENT_KEY) {
+      TT.contentlen = get_strtou(str, NULL, 10);
+      verify_content_len(str);
+      continue;
+    }
+    if(key == TRANSFER_KEY) {
+      if(strcmp(upper_to_lower(str), "chunked") != 0) {
+        strncpy(toybuf, str, strlen(str));
+        perror_exit("transfer encoding chunked is not supported: ");
+      }
+      TT.chunked = 1;
+    }
+    //HTTP Location header - either to load a different web page or location of a newly-created resource.
+    if(key == LOCATION_KEY && status >= HTTP_MULTIPLECHOICES) {
+      int is_new_location = verify_location(isproxy, str, &srchostinfo, &dsthostinfo);
+      fclose(srcfp);
+      if(is_new_location) {
+        if(swl) {
+          free(swl);
+          swl = NULL;
+        }
+        goto NEWADDRESS;
+      }
+      else goto NEWSESSION;
+    }
+  }//end of while loop
+  if(swl) {
+    free(swl);
+    swl = NULL;
+  }
+#undef CONTENT_KEY
+#undef TRANSFER_KEY
+#undef LOCATION_KEY
+  return srcfp;
+}
+
+/*
+ * send request to ftp  for login.
+ */
+static void send_ftp_request(FILE *srcfp, HOSTINFO *dsthostinfo, sockaddr_with_len *swl)
+{
+  char *pch;
+  unsigned portnum;
+  //FTP connection request.
+  if(sendcommand_to_ftp(NULL, NULL, srcfp) != FTPSERVER_READY) close_connection(NULL);
+
+  //resolve password.
+  dsthostinfo->password = strchr(dsthostinfo->username, ':');
+  if(dsthostinfo->password) *dsthostinfo->password++ = '\0';
+
+  //230 User authenticated, password please; 331 Password request.
+  switch(sendcommand_to_ftp("USER ", dsthostinfo->username, srcfp)) {
+    case USERLOGGED_SUCCESS:
+      break;
+    case PASSWORD_REQUEST: //user logged in... Need Password.
+      if(sendcommand_to_ftp("PASS ", dsthostinfo->password, srcfp) != USERLOGGED_SUCCESS) close_connection("PASS");
+      break;
+    default://login failed.
+      close_connection("USER");
+      break;
+  }
+  sendcommand_to_ftp("TYPE I", NULL, srcfp); //200 Type Binary... Command okay.
+  if(sendcommand_to_ftp("SIZE ", dsthostinfo->path, srcfp) == FTPFILE_STATUS) {
+    //remove '\n' and '\r' char from toybuf buffer to get the size of the file.
+    char *ptr = strchrnul(toybuf, '\n');
+    *ptr = '\0';
+    ptr = strchrnul(toybuf, '\r');
+    *ptr = '\0';
+    TT.contentlen = get_strtou(toybuf + 4, NULL, 10);
+    verify_content_len(toybuf);
+  }
+  if(sendcommand_to_ftp("PASV", NULL, srcfp) != PASSIVE_MODE) close_connection("PASV");
+  //Response is "NNN <some text> (N1,N2,N3,N4,P1,P2) garbage.
+  //Server's IP is N1.N2.N3.N4
+  //Server's port for data connection is P1*256+P2.
+  pch = strrchr(toybuf, ')');
+  if(pch) *pch = '\0';
+
+  pch = strrchr(toybuf, ',');
+  if(pch) *pch = '\0';
+  portnum = get_int_value(pch + 1, 0, 255);
+
+  pch = strrchr(toybuf, ',');
+  if(pch) *pch = '\0';
+  portnum = portnum + (get_int_value(pch + 1, 0, 255) * 256);
+  setport(&swl->sock_u.sock, htons(portnum));
+  return;
+}
+
+/*
+ * create ftp session and verify modes of ftp.
+ */
+static FILE *create_ftp_session(FILE **datafp, HOSTINFO *srchostinfo, HOSTINFO *dsthostinfo)
+{
+  FILE *srcfp; //socket to web/ftp server.
+  int alloc_flag = 0;
+  sockaddr_with_len *swl = resolve_hostname(srchostinfo);
+
+  if(!dsthostinfo->username || (strlen(dsthostinfo->username) == 0)) {
+    alloc_flag = 1;
+    dsthostinfo->username = xstrdup("anonymous:anonymous");
+  }
+
+  srcfp = do_login(swl);
+  send_ftp_request(srcfp, dsthostinfo, swl);
+
+  *datafp = do_login(swl);
+
+  if(TT.filepos) {
+    sprintf(toybuf, "REST %lu", (unsigned long) TT.filepos);
+    if(sendcommand_to_ftp(toybuf, NULL, srcfp) == REQUESTED_PENDINGACTION) TT.contentlen = TT.contentlen - TT.filepos;
+  }
+
+  if(sendcommand_to_ftp("RETR ", dsthostinfo->path, srcfp) > FTPFILE_STATUSOKAY) close_connection("RETR");
+
+  if(swl) {
+    free(swl);
+    swl = NULL;
+  }
+  if(alloc_flag) {
+    free((char *)dsthostinfo->username);
+    dsthostinfo->username = NULL;
+  }
+  return srcfp;
+}
+
+/*
+ * clean http data (remove '\n' and '\r' and get the content length).
+ */
+static int clean_http_data(FILE *datafp)
+{
+  int status = 0;
+  char ch;
+  get_http_responsedata(datafp, &ch);
+  TT.contentlen = get_strtou(toybuf, NULL, 16);
+  if(TT.contentlen == 0) status = 1;
+  TT.cleanit = 1;
+  return status;
+}
+
+/*
+ * transfer the file content from srouce to destination.
+ */
+static void transfer_data(FILE *srcfp, FILE *datafp)
+{
+  if(!(toys.optflags & FLAG_s)) {
+    if(TT.sockfd < 0) TT.sockfd = xcreate(TT.outputfile, TT.flags, 0666);
+    //for ftp data download.
+    if(srcfp != datafp) get_ftp_data(datafp);
+    else {//for http data download.
+      int iscleaned = 0;
+      if(TT.chunked) iscleaned = clean_http_data(datafp);
+      if(!iscleaned) get_http_data(datafp);
+    }
+    if(!(toys.optflags & FLAG_O)) {
+      xclose(TT.sockfd);
+      TT.sockfd = -1;
+    }
+  }
+  //Close ftp connection.
+  if(datafp != srcfp) {
+    fclose(datafp);
+    if(sendcommand_to_ftp(NULL, NULL, srcfp) != CLOSE_DATACONECTION) close_connection("ftp error: ");
+  }
+  return;
+}
+
+/*
+ * get proxy information.
+ */
+static int get_proxy_info(const char *url, HOSTINFO *dsthostinfo, HOSTINFO *srchostinfo)
+{
+  int isproxy;
+  char *proxy = NULL;
+  parse_url(url, dsthostinfo);
+  isproxy = (strncmp(TT.proxyflag, "off", 3) != 0);
+  if(isproxy) {
+    proxy = getenv(TT.isftp ? FTPPROXY_VAR : HTTPPROXY_VAR);
+    isproxy = (proxy && proxy[0]);
+    if(isproxy) parse_url(proxy, srchostinfo);
+  }
+  if(!isproxy) {
+    srchostinfo->portnum = dsthostinfo->portnum;
+    srchostinfo->hostname = srchostinfo->url = xstrdup(dsthostinfo->hostname);
+  }
+  return isproxy;
+}
+
+/*
+ * download http/ftp url.
+ */ 
+static void start_download(const char *url)
+{
+  HOSTINFO srchostinfo, dsthostinfo;
+  int isproxy;
+  char *outputfile = NULL;
+
+  FILE *srcfp; //socket to web/ftp server.
+  FILE *datafp; //socket to ftp server (data).
+
+  srchostinfo.username = dsthostinfo.username = NULL;
+  srchostinfo.url = dsthostinfo.url = NULL;
+
+  isproxy = get_proxy_info(url, &dsthostinfo, &srchostinfo);
+
+  //if -O option is not define then directory prefix can be there.
+  if(!(toys.optflags & FLAG_O)) {
+    TT.outputfile = get_last_path_component_withnostrip((char *)dsthostinfo.path);
+    if(!TT.outputfile[0] || TT.outputfile[0] == '/') TT.outputfile = (char*)"index.html";
+    //Keep a local copy of output file
+    if(TT.dirprefix) TT.outputfile = outputfile = append_pathandfile(TT.dirprefix, TT.outputfile);
+    else TT.outputfile = outputfile = xstrdup(TT.outputfile);
+  }
+
+  //Reset the file postion as begining of the file.
+  TT.filepos = 0;
+  if(toys.optflags & FLAG_c) {
+    TT.sockfd = open(TT.outputfile, O_WRONLY);
+    //if file is present, move the file cursor.
+    if(TT.sockfd >= 0) TT.filepos = xlseek(TT.sockfd, 0, SEEK_END);
+  }
+
+  //Create HTTP session.
+  if(isproxy || !TT.isftp) {
+    srcfp = create_http_session(isproxy, &srchostinfo, &dsthostinfo);
+    datafp = srcfp;
+  }
+  //Create FTP session.
+  else srcfp = create_ftp_session(&datafp, &srchostinfo, &dsthostinfo);
+
+  transfer_data(srcfp, datafp);
+
+  if(srcfp) fclose(srcfp);
+
+  if(outputfile) {
+    free(outputfile);
+    outputfile = NULL;
+  }
+  if(dsthostinfo.url) {
+    free(dsthostinfo.url);
+    dsthostinfo.url = NULL;
+  }
+  if(srchostinfo.url) {
+    free(srchostinfo.url);
+    srchostinfo.url = NULL;
+  }
+  return;
+}
+
+/*
+ * used to verify the timeout.
+ */
+static int is_timeout(int fd, int event, unsigned int timeinms, int *revents)
+{
+  struct pollfd pfds[1];
+  int ret = 0;
+
+  pfds[0].fd = fd;
+  pfds[0].events = event;
+
+  ret = poll(pfds, 1, timeinms);
+  *revents = pfds[0].revents;
+  return ret;
+}
+
+/*
+ * get ftp data.
+ */
+static void get_ftp_data(FILE *datafp)
+{
+  int srcfd;
+  int revents = 0;
+  srcfd = fileno(datafp);
+  unsigned int timeinms = (TT.timeout *1000);
+
+  fprintf(stderr, "Please wait Downloading...\n");
+  for(;;) {
+    int len = 0;
+    int ret = is_timeout(srcfd, POLLIN, timeinms, &revents);
+    if(ret < 0) {
+      if(timeinms != 0 && --timeinms == 0) error_exit("download timed out");
+      continue;
+    }
+    if(ret == 0) error_exit("download timed out");
+
+    if(revents) {
+      len = xread(srcfd, toybuf, sizeof(toybuf) - 1);
+      if(!len) break;
+    }
+    revents = 0;
+    ret = is_timeout(TT.sockfd, POLLOUT, timeinms, &revents);
+    if(ret < 0) {
+      if(timeinms != 0 && --timeinms == 0) error_exit("download timed out");
+      continue;
+    }
+    if(ret == 0) error_exit("download timed out");
+
+    if(revents) xwrite(TT.sockfd, toybuf, len);
+  }
+  return;
+}
+
+/*
+ * get http data.
+ */
+static void get_http_data(FILE *datafp)
+{
+  int srcfd = fileno(datafp);
+  unsigned int timeinms = (TT.timeout *1000);
+  int iscleaned = 0;
+  char ch;
+
+  fprintf(stderr, "Please wait Downloading...\n");
+  for(;;) {
+    for(;;) {
+      int nitems = 0, revents = 0;
+
+      //there is no content to read.
+      if((int)TT.contentlen <= 0) break;
+      clearerr(datafp);
+      errno = 0;
+      int ret = is_timeout(srcfd, POLLIN, timeinms, &revents);
+      if(ret < 0) {
+        if(timeinms != 0 && --timeinms == 0) error_exit("download timed out");
+        continue;
+      }
+      if(ret == 0) error_exit("download timed out");
+
+      if(revents) {
+        unsigned readsize = sizeof(toybuf);
+        //read data upto content length.
+        if(TT.contentlen < readsize) readsize = TT.contentlen;
+        nitems = fread(toybuf, 1, readsize, datafp);
+        if(nitems <= 0) {
+          if(errno == EWOULDBLOCK) continue;
+          if(ferror(datafp)) perror_exit("read error");
+          break;
+        }
+      }
+
+      revents = 0;
+      ret = is_timeout(TT.sockfd, POLLOUT, timeinms, &revents);
+      if(ret < 0) {
+        if(timeinms != 0 && --timeinms == 0) error_exit("download timed out");
+        continue;
+      }
+      if(ret == 0) error_exit("download timed out");
+      
+      if(revents) xwrite(TT.sockfd, toybuf, nitems);
+
+      if((TT.contentlen -= nitems) == 0) break;
+    }//inner loop.
+    clearerr(datafp);
+    if(!TT.chunked) break;
+    get_http_responsedata(datafp, &ch);
+    iscleaned = clean_http_data(datafp);
+    if(iscleaned) break;
+  }
+  return;
+}
+
+/*
+ * send command to ftp and return the status of ftp command.
+ */
+static int sendcommand_to_ftp(const char *str1, const char *str2, FILE *fp)
+{
+  unsigned cmd_status = 0;
+  if(str1){
+    if(!str2) str2 = "";
+    fprintf(fp, "%s%s\r\n", str1, str2);
+    fflush(fp);
+  }
+  do{
+    if(fgets(toybuf, sizeof(toybuf) - 1, fp) == NULL) {
+      fclose(fp);
+      close_connection(NULL);
+    }
+  }while(!isdigit(toybuf[0]) || toybuf[3] != ' ');
+
+  toybuf[3] = '\0';
+  cmd_status = get_int_value(toybuf, 0, INT_MAX);
+  toybuf[3] = ' ';
+  return cmd_status;
+}
+
+/*
+ * used to verify the url for http or ftp.
+ */
+static void isftp_or_http(const char *srcurl, HOSTINFO **hinfo)
+{
+  HOSTINFO *host_info = *hinfo;
+  char *url;
+  if(host_info->url) {
+    free(host_info->url);
+    host_info->url = NULL;
+  }
+  host_info->url = url = xstrdup((char *)srcurl);
+  if(strncmp(url, HTTPSTR, 7) == 0) {
+    host_info->portnum = getport("http", "tcp", 80);
+    host_info->hostname = url + 7;
+    TT.isftp = 0;
+  } else if(strncmp(url, FTPSTR, 6) == 0) {
+    host_info->portnum = getport("ftp", "tcp", 21);
+    host_info->hostname = url + 6;
+    TT.isftp = 1;
+  } else {
+    error_exit("not an http or ftp url: %s", url);
+  }
+  return;
+}
+
+/*
+ * get source path from given url.
+ * e.g. scheme://username:password@domain:port/path?query_string#fragment_id
+ * The fragment-id follows the URL of the whole object from which it is separated
+ * by a hash sign (#). If the fragment-id is void, the hash sign may be omitted:
+ * A void fragment-id with or without the hash sign means that the URL refers to the whole object.
+ */
+
+static void get_srcpath(HOSTINFO **hinfo)
+{
+  HOSTINFO *host_info = *hinfo;
+  char *srcpath, *query, *fragment_id;
+  srcpath = strchr(host_info->hostname, '/'); //for the source path. If not then path is "".
+  query = strchr(host_info->hostname, '?'); //for the query. abc.com?login=Name1@Name2
+  if(!srcpath || (query && srcpath > query)) srcpath = query;
+
+  fragment_id = strchr(host_info->hostname, '#');
+  if(!srcpath || (fragment_id && srcpath > fragment_id)) srcpath = fragment_id;
+  if(!srcpath) host_info->path = "";
+  else if(*srcpath == '/') {
+    *srcpath = '\0';
+    host_info->path = srcpath + 1;
+  } else {// '#' or '?'
+    //hostname = scheme://username:password@domain:port/path?query_string#fragment_id
+    //after memmove "hostname = scheme:/username:password@domain:port/path?query_string#fragment_id"
+    //as http request needs one '/' after GET string. e.g. 'GET /?login=john@doe HTTP/1.0'
+    memmove(host_info->hostname - 1, host_info->hostname, srcpath - host_info->hostname);
+    host_info->hostname--;
+    srcpath[-1] = '\0'; //NULL terminate the request string.
+    host_info->path = srcpath;
+  }
+  return;
+}
+/*
+ * Get user info from given url.
+ */
+static void get_usrinfo(HOSTINFO **hinfo)
+{
+  HOSTINFO *host_info = *hinfo;
+  char *srcpath = strrchr(host_info->hostname, '@');
+  if(srcpath != NULL) {
+    host_info->username = host_info->hostname;
+    *srcpath = '\0';
+    host_info->hostname = srcpath + 1;
+  }
+  srcpath = host_info->hostname;
+  return;
+}
+/*
+ * parse url given by user.
+ */
+static void parse_url(const char *srcurl, HOSTINFO *hinfo)
+{
+  isftp_or_http(srcurl, &hinfo);
+  get_srcpath(&hinfo);
+  get_usrinfo(&hinfo);
+  return;
+}
+
+static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+                'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+                'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+                'w', 'x', 'y', 'z', '0', '1', '2', '3',
+                '4', '5', '6', '7', '8', '9', '+', '/'
+};
+/* 
+ * The rfc (Request for Comments) states that non base64 chars are to be ignored.
+ * str param, a pointer to a base64 encoded string.
+ * Base64 encoding converts 3 octets into 4 encoded characters. (Reference:http://en.wikipedia.org/wiki/Base64)
+ * e.g.Encoded in ASCII, the characters M, a, and n are stored as the bytes 77, 97, and 110,
+ * which are the 8-bit binary values 01001101, 01100001, and 01101110. These three values are
+ * joined together into a 24-bit string, producing 010011010110000101101110.
+ * Groups of 6 bits are converted into individual numbers from left to right.
+ * (in this case, there are four numbers in a 24-bit string),
+ * which are then converted into their corresponding Base64 character values.
+ * The Base64 index table:
+ * char      val
+ * A-Z      0-25
+ * a-z       26-51
+ * 0-1      52-61
+ * '+'      62
+ * '/'      63
+*/
+static char *base64_encodeing(char *str)
+{
+  char *instr, *temp_instr;
+  unsigned ch = 0;
+  int i = 0;
+
+  int len = strlen(str);
+  int padding = len % 3;
+
+  if(padding) {
+    if(padding == 1) {
+      instr = xstrndup(str, strlen(str) + 3);
+      strcat(instr, "00");
+    }
+    else {
+      instr = xstrndup(str, strlen(str) + 2);
+      strcat(instr, "0");
+    }
+    len = ((len / 3) * 4) + 4;
+  }
+  else {
+    instr = str;
+    len = (len / 3) * 4;
+  }
+
+  char *ptr = xzalloc(len + 1);
+  char *temp_ptr = ptr;
+  temp_instr = instr;
+
+  while(*instr) {
+    int val = *instr++;
+
+    ch = (ch << 8) | val;
+    i++;
+    if(i == 3) {
+      int counter = 4;
+      unsigned mask = 0x3F;
+      while(counter) {
+        temp_ptr[counter - 1] = ch & mask;
+        ch = ch >> 6;
+        counter--;
+      }
+      temp_ptr += 4;
+      i = 0;
+    }
+  }
+
+  ptr[len] = '\0';
+  i = 0;
+  while(ptr[i]) {
+    int val = ptr[i];
+    ptr[i++] = (char) encoding_table[val];
+  }
+  if(padding == 1) {
+    ptr[len - 2] = '=';
+    ptr[len - 1] = '=';
+  }
+  else ptr[len - 1] = '=';
+  if(temp_instr) {
+    free(temp_instr);
+    temp_instr = NULL;
+  }
+  return ptr;
+}
+
+/*
+ * close http/ftp connection and print the message.
+ */
+static void close_connection(const char *msgstr)
+{
+  char *str = toybuf; //buf holds peer's response.
+  //Remove garbage characters from remote server response.
+  while (*str >= 0x20 && *str < 0x7f) // from ' ' space to '\x7f' DEL
+    str++;
+  *str = '\0';
+  if(TT.sockfd >= 0) xclose(TT.sockfd);
+  perror_exit("%s %s", (msgstr ? msgstr : ""), toybuf);
+}
diff --git a/toys/other/which.c b/toys/other/which.c
new file mode 100644 (file)
index 0000000..fc65fe8
--- /dev/null
@@ -0,0 +1,68 @@
+/* which.c - Find executable files in $PATH.
+ *
+ * Copyright 2006 Rob landley <rob@landley.net>
+
+USE_WHICH(NEWTOY(which, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
+
+config WHICH
+  bool "which"
+  default y
+  help
+    usage: which [-a] filename ...
+
+    Search $PATH for executable files matching filename(s).
+
+    -a Show all matches
+*/
+#include "toys.h"
+
+// Find an exectuable file either at a path with a slash in it (absolute or
+// relative to current directory), or in $PATH.  Returns absolute path to file,
+// or NULL if not found.
+
+static int which_in_path(char *filename)
+{
+  struct string_list *list;
+
+  // If they gave us a path, don't worry about $PATH or -a
+
+  if (strchr(filename, '/')) {
+    // Confirm it has the executable bit set, and it's not a directory.
+    if (!access(filename, X_OK)) {
+      struct stat st;
+
+      if (!stat(filename, &st) && S_ISREG(st.st_mode)) {
+        puts(filename);
+        return 0;
+      }
+      return 1;
+    }
+  }
+
+  // Search $PATH for matches.
+  list = find_in_path(getenv("PATH"), filename);
+  if (!list) return 1;
+
+  // Print out matches
+  while (list) {
+    if (!access(list->str, X_OK)) {
+      puts(list->str);
+      // If we should stop at one match, do so
+      if (!toys.optflags) {
+        llist_traverse(list, free);
+        break;
+      }
+    }
+    free(llist_pop(&list));
+  }
+
+  return 0;
+}
+
+void which_main(void)
+{
+  int i;
+
+  for (i=0; toys.optargs[i]; i++)
+    toys.exitval |= which_in_path(toys.optargs[i]);
+}
diff --git a/toys/other/whoami.c b/toys/other/whoami.c
new file mode 100644 (file)
index 0000000..a7ed047
--- /dev/null
@@ -0,0 +1,29 @@
+/* whoami.c - Print effective user name
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+
+USE_WHOAMI(NEWTOY(whoami, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config WHOAMI
+  bool "whoami"
+  default y
+  help
+    usage: whoami
+
+    Print effective user name.
+*/
+
+#include "toys.h"
+
+void whoami_main(void)
+{
+  struct passwd *pw = getpwuid(geteuid());
+
+  if (!pw) {
+    perror("getpwuid");
+    toys.exitval = 1;
+    return;
+  }
+
+  xputs(pw->pw_name);
+}
diff --git a/toys/other/yes.c b/toys/other/yes.c
new file mode 100644 (file)
index 0000000..773a5a8
--- /dev/null
@@ -0,0 +1,29 @@
+/* yes.c - Repeatedly output a string.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+
+USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config YES
+  bool "yes"
+  default y
+  help
+    usage: yes [args...]
+
+    Repeatedly output line until killed. If no args, output 'y'.
+*/
+
+#include "toys.h"
+
+void yes_main(void)
+{
+  for (;;) {
+    int i;
+    for (i=0; toys.optargs[i]; i++) {
+      if (i) xputc(' ');
+      xprintf("%s", toys.optargs[i]);
+    }
+    if (!i) xputc('y');
+    xputc('\n');
+  }
+}
diff --git a/toys/pending/README b/toys/pending/README
new file mode 100644 (file)
index 0000000..1dd6250
--- /dev/null
@@ -0,0 +1,5 @@
+pending
+
+These commands await additional review and/or cleanup. Code in this directory
+may or may not work, some of the commands here are unfinished stubs, others
+just need a more thorough inspection than I've had time for yet.
diff --git a/toys/pending/getkey.c_1 b/toys/pending/getkey.c_1
new file mode 100644 (file)
index 0000000..398cf99
--- /dev/null
@@ -0,0 +1,75 @@
+/* getkey.c - A getkey program.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * Not in SUSv4.
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/
+
+USE_GETKEY(NEWTOY(getkey, "m:c#i", TOYFLAG_USR|TOYFLAG_BIN))
+
+config GETKEY
+  bool "getkey"
+  default n
+  help
+    usage: getkey [-c seconds] [-i] [-m string] KEYS
+
+    A getkey program to get the desired KEYS input or any key press if KEYS not specified.
+*/
+
+#define FOR_getkey
+#include "toys.h"
+#include <unistd.h>
+#include <termios.h>
+
+// Hello doesn't use these globals, they're here for example/skeleton purposes.
+
+GLOBALS(
+  long timeout;
+  char *fmt_string;
+  struct termios inf;
+)
+
+void timeout(int x)
+{
+  tcsetattr(0, TCSANOW, &TT.inf);
+  exit(x);
+}
+void getkey_main(void)
+{
+  char ch;
+  struct termios inf, newf;
+  char *list = NULL;
+
+  tcgetattr(0, &inf);
+  memcpy(&newf, &inf, sizeof(struct termios));
+  memcpy(&TT.inf, &inf, sizeof(struct termios));
+
+  //disable echo
+  newf.c_lflag &= ~(ISIG | ICANON );
+  tcsetattr(0, TCSANOW, &newf);
+
+  if(toys.optflags & FLAG_c)
+  {
+      signal(SIGALRM, timeout);
+      alarm(TT.timeout);
+  }
+
+  if(toys.optflags & FLAG_m)
+  {
+      fprintf(stderr, TT.fmt_string, TT.timeout);
+  }
+  list = toys.optargs[0];
+  
+  while(1)
+  {
+      if(read(STDIN_FILENO, &ch, 1) != 1)
+          continue;
+      if(!(toys.optflags & FLAG_i) && (ch == 3 || ch ==4))
+          timeout(1);
+      if(!list || strchr(list, ch) || strchr(list, toupper(ch)) || strchr(list, tolower(ch)))
+      {
+          tcsetattr(0, TCSANOW, &inf);
+          exit(0);
+      }
+  }
+}
diff --git a/toys/pending/last.c_1 b/toys/pending/last.c_1
new file mode 100644 (file)
index 0000000..36447b9
--- /dev/null
@@ -0,0 +1,326 @@
+/* vi: set sw=4 ts=4:
+ *
+ * last.c - A program to show the listings of last logged in users.
+ *
+ * Copyright 2012 VIKASH KUMAR <k.vikash@samsung.com>
+ *
+
+USE_LAST(NEWTOY(last,"f:W", TOYFLAG_USR|TOYFLAG_BIN ))
+
+config LAST
+  bool "last"
+  default n 
+  help
+    usage: last [-W] [-f FILE] [name...] [tty...]
+
+    A program to reset the terminal.
+
+    options:
+  -W : Display the informations without host-column truncation.
+  -f FILE : Read from file FILE instead of /var/log/wtmp.
+  name... : Search for username 'name' and show result only for that.
+  tty... : Search for terminal name 'tty' and show result only for that.
+
+*/
+#define FOR_last
+
+#include "toys.h"
+#include <utmp.h>
+
+#define DAY_SEC 86400  //24*60*60
+
+#ifndef SHUTDOWN_TIME
+#define SHUTDOWN_TIME 254
+#endif
+
+struct tty{
+  char tty_name[32];
+  time_t exit_time;
+  struct tty *next;
+};
+enum{NO,YES};
+
+GLOBALS(
+
+  char *f_file;
+
+  int t_flag;
+  int o_flag;
+  int flag;
+  struct tty *tty_list;
+)
+
+
+struct tty * deltty(struct utmp bt, int check)
+{
+  struct tty *temp, *prev, *ret_tty;
+  time_t tm;
+  temp = TT.tty_list;
+  prev = temp;
+  time(&tm);
+  while(temp != NULL)
+  {
+      if(strcmp(temp->tty_name, bt.ut_line) == 0)
+      {
+          ret_tty=temp;
+
+          if(strcmp(bt.ut_line, "system boot") == 0)
+          {
+              if(!check)
+              {
+                  TT.t_flag = 1;
+                  TT.o_flag = 0;
+                  return ret_tty;
+              }
+          }
+          if(temp == TT.tty_list)
+          {
+              TT.tty_list = temp->next;
+              free(temp);
+              return ret_tty;
+          }
+          else
+          {
+              prev->next = temp->next;
+              free(temp);
+              return ret_tty;
+          }
+      }
+      else
+      {
+          prev = temp;
+          temp = temp->next;
+      }
+  }
+  return NULL;
+}
+
+void addtty(struct utmp bt)
+{
+  struct tty *temp;
+  temp = (struct tty *)malloc(sizeof(struct tty));
+  strcpy(temp->tty_name, bt.ut_line);
+  temp->exit_time = (time_t)bt.ut_tv.tv_sec;
+  if(strcmp(bt.ut_line, "system boot") == 0)
+      deltty(bt, YES);
+  if(TT.tty_list == NULL)
+  {
+      TT.tty_list = temp;
+      TT.tty_list->next = NULL;
+  }
+  else
+  {
+      temp->next = TT.tty_list;
+      TT.tty_list = temp;
+  }
+}
+
+void diff_time(time_t end, time_t start)
+{
+  time_t diff;
+  double dif;
+  dif=difftime(end, start);
+
+  if(dif > 0)
+  {
+      diff = end - start;
+      if(dif > DAY_SEC)
+          {if(TT.flag)printf("(%d+%5.5s)\n", ((int)dif/DAY_SEC), asctime(gmtime(&diff))+11);}
+      else
+          {if(TT.flag)printf(" (%5.5s)\n", asctime(gmtime(&diff))+11);}
+  }
+  else
+  {
+      diff = start - end;
+      dif = -dif;
+      if(dif > DAY_SEC)
+          {if(TT.flag)printf("(-%d-%5.5s)\n", ((int)dif/DAY_SEC), asctime(gmtime(&diff))+11);}
+      else
+          {if(TT.flag)printf(" (-%5.5s)\n", asctime(gmtime(&diff))+11);}
+  }
+}
+
+void check_args(struct utmp ut)
+{
+  int count;
+  char **ptr;
+
+  count = toys.optc;
+  ptr = toys.optargs;
+
+  while(count-- > 0)
+  {
+       if(strcmp(*ptr, ut.ut_user) != 0)
+       {
+               if(strcmp(*ptr, ut.ut_line) != 0)
+               {
+                       TT.flag = 0;
+                       ptr++;
+                       continue;
+               }
+               else
+               {
+                       TT.flag = 1;
+                       return;
+               }
+       }
+       else
+       {
+               TT.flag = 1;
+               return;
+       }
+  }
+  return;
+}
+
+void last_main(void)
+{
+  struct tty *temp;
+  struct utmp ut;
+  int n, fd;
+  off_t loc;
+  time_t tmp, tm, crash_time=0;
+  char t_name[32];
+  char *file;
+
+  if(toys.optflags & FLAG_f)
+       file = TT.f_file;
+  else
+       file = "/var/log/wtmp";
+
+  TT.tty_list = NULL;
+  time(&tm);
+  TT.flag=1;
+
+  if((fd=open(file, O_RDONLY)) == -1)
+  {
+       fprintf(stdout, "%s:%s\n", file, strerror(errno));
+       exit(EXIT_FAILURE);
+  }
+
+  loc = lseek(fd, 0, SEEK_END);
+  loc = lseek(fd, loc - sizeof(ut), SEEK_SET);
+
+  while((n = read(fd, &ut, sizeof(ut))) != 0)
+  {
+       if(n != sizeof(ut))
+       {
+               printf("Incomplete Read: \n");
+               exit(EXIT_FAILURE);
+       }
+       if(toys.optc > 0)
+               check_args(ut);
+
+       if(strcmp(ut.ut_line, "~\0") == 0)
+       {
+               if(strcmp(ut.ut_user, "runlevel") == 0)
+                       ut.ut_type = RUN_LVL;
+               else if(strcmp(ut.ut_user, "reboot") == 0)
+                       ut.ut_type = BOOT_TIME;
+               else if(strcmp(ut.ut_user, "shutdown") == 0)
+               {
+                       TT.o_flag=1;
+                       crash_time=(time_t)ut.ut_tv.tv_sec;
+                       strcpy(ut.ut_line, "system boot");
+                       ut.ut_type = SHUTDOWN_TIME;
+                       addtty(ut);
+               }
+       }
+       else
+       {
+               if(ut.ut_user[0] == '\0' || strcmp(ut.ut_user, "LOGIN") == 0)
+               {
+                       if(ut.ut_user[0] == '\0' && ut.ut_host[0] != '\0')
+                       {
+                               strcpy(t_name, ut.ut_line);
+                               addtty(ut);
+                       }
+                       goto jump;
+               }
+               if(ut.ut_host[0] == '\0' && ut.ut_user[0] != '\0')
+               {
+                       addtty(ut);
+                       goto jump;
+               }
+               if(ut.ut_type != DEAD_PROCESS && ut.ut_user[0] && ut.ut_line[0])
+                       ut.ut_type = USER_PROCESS;
+       }
+
+       if(ut.ut_type != USER_PROCESS)
+       {
+               switch(ut.ut_type)
+               {
+                       case BOOT_TIME:
+                               strcpy(ut.ut_line, "system boot");
+                               crash_time=(time_t)ut.ut_tv.tv_sec;
+                               break;
+                       case RUN_LVL:
+                       case SHUTDOWN_TIME:
+                               goto jump;
+               }
+       }
+
+       tmp = (time_t)ut.ut_tv.tv_sec;
+
+       if(toys.optflags & FLAG_W)
+               {if(TT.flag)printf("%-9.8s%-13.13s%-45.45s%-16.16s - ",ut.ut_user, ut.ut_line, ut.ut_host, ctime(&tmp));}
+       else
+               {if(TT.flag)printf("%-9.8s%-13.13s%-17.16s%-16.16s - ",ut.ut_user, ut.ut_line, ut.ut_host, ctime(&tmp));}
+
+       temp = (struct tty *)malloc(sizeof(struct tty));
+       temp = deltty(ut, NO);
+
+       if(temp == NULL)
+       {
+               if(strcmp(ut.ut_line, "system boot") == 0)
+               {
+                       TT.t_flag=1;
+                       TT.o_flag=0;
+                       if(TT.flag)printf("%-5.5s ", ctime(&tm)+11);
+                       diff_time(tm, tmp);
+               }
+               else if(strcmp(ut.ut_line, t_name) == 0)//(strncmp(ut.ut_line, "tty", 3) == 0)
+               {
+                       if(TT.t_flag)
+                       {
+                  if(TT.flag)printf("%s", (TT.o_flag==0?"crash ":"down  "));
+                               diff_time(crash_time, tmp);
+                       }
+              else
+                               printf("%s\n", ((strcmp(file, "/var/log/wtmp")==0)?"still logged in":"gone,no logout"));
+               }
+               else
+               {
+                       if(TT.flag)printf("%s", (TT.t_flag==0?((strcmp(file, "/var/log/wtmp")==0)?"still logged in\n":"gone,no logout\n"):"crash "));
+                       if(TT.t_flag)
+                               diff_time(crash_time, tmp);
+               }
+       }
+       else
+       {
+               if(strcmp(ut.ut_line, t_name) == 0)//(strncmp(ut.ut_line, "tty", 3) == 0)
+               {
+                       if(TT.t_flag)
+                       {
+                               TT.t_flag = 0;
+                               if(TT.flag)printf("%s ", "down ");
+                       }
+                       else
+                               {if(TT.flag)printf("%-5.5s ", ctime(&temp->exit_time)+11);}
+               }
+               else
+                       {if(TT.flag)printf("%-5.5s ", ctime(&temp->exit_time)+11);}
+               diff_time(temp->exit_time, tmp);
+       }
+
+jump:          loc -= sizeof(ut);
+       if(loc < 0)
+               break;
+       lseek(fd, loc, SEEK_SET);
+  }
+  fflush(stdout);
+  close(fd);
+  printf("\n%s begins %-24.24s\n", basename(file), (ctime(&tmp)));
+
+  return;
+}
diff --git a/toys/pending/mdev.c b/toys/pending/mdev.c
new file mode 100644 (file)
index 0000000..b89ac2c
--- /dev/null
@@ -0,0 +1,209 @@
+/* mdev.c - Populate /dev directory and handle hotplug events
+ *
+ * Copyright 2005, 2008 Rob Landley <rob@landley.net>
+ * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
+
+USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
+
+config MDEV
+  bool "mdev"
+  default n
+  help
+    usage: mdev [-s]
+
+    Create devices in /dev using information from /sys.
+
+    -s Scan all entries in /sys to populate /dev.
+
+config MDEV_CONF
+  bool "Configuration file for mdev"
+  default y
+  depends on MDEV
+  help
+    The mdev config file (/etc/mdev.conf) contains lines that look like:
+    hd[a-z][0-9]* 0:3 660
+
+    Each line must contain three whitespace separated fields. The first
+    field is a regular expression matching one or more device names, and
+    the second and third fields are uid:gid and file permissions for
+    matching devies.
+*/
+
+#include "toys.h"
+#include "lib/xregcomp.h"
+
+// todo, open() block devices to trigger partition scanning.
+
+// mknod in /dev based on a path like "/sys/block/hda/hda1"
+static void make_device(char *path)
+{
+  char *device_name, *s, *temp;
+  int major, minor, type, len, fd;
+  int mode = 0660;
+  uid_t uid = 0;
+  gid_t gid = 0;
+
+  // Try to read major/minor string
+
+  temp = strrchr(path, '/');
+  fd = open(path, O_RDONLY);
+  *temp=0;
+  temp = toybuf;
+  len = read(fd, temp, 64);
+  close(fd);
+  if (len<1) return;
+  temp[len] = 0;
+
+  // Determine device name, type, major and minor
+
+  device_name = strrchr(path, '/') + 1;
+  type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+  major = minor = 0;
+  sscanf(temp, "%u:%u", &major, &minor);
+
+  // If we have a config file, look up permissions for this device
+
+  if (CFG_MDEV_CONF) {
+    char *conf, *pos, *end;
+
+    // mmap the config file
+    if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) {
+      len = fdlength(fd);
+      conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+      if (conf) {
+        int line = 0;
+
+        // Loop through lines in mmaped file
+        for (pos = conf; pos-conf<len;) {
+          int field;
+          char *end2;
+
+          line++;
+          // find end of this line
+          for(end = pos; end-conf<len && *end!='\n'; end++);
+
+          // Three fields: regex, uid:gid, mode
+          for (field = 3; field; field--) {
+            // Skip whitespace
+            while (pos<end && isspace(*pos)) pos++;
+            if (pos==end || *pos=='#') break;
+            for (end2 = pos;
+              end2<end && !isspace(*end2) && *end2!='#'; end2++);
+            switch(field) {
+              // Regex to match this device
+              case 3:
+              {
+                char *regex = strndup(pos, end2-pos);
+                regex_t match;
+                regmatch_t off;
+                int result;
+
+                // Is this it?
+                xregcomp(&match, regex, REG_EXTENDED);
+                result=regexec(&match, device_name, 1, &off, 0);
+                regfree(&match);
+                free(regex);
+
+                // If not this device, skip rest of line
+                if (result || off.rm_so
+                  || off.rm_eo!=strlen(device_name))
+                    goto end_line;
+
+                break;
+              }
+              // uid:gid
+              case 2:
+              {
+                char *s2;
+
+                // Find :
+                for(s = pos; s<end2 && *s!=':'; s++);
+                if (s==end2) goto end_line;
+
+                // Parse UID
+                uid = strtoul(pos,&s2,10);
+                if (s!=s2) {
+                  struct passwd *pass;
+                  char *str = strndup(pos, s-pos);
+                  pass = getpwnam(str);
+                  free(str);
+                  if (!pass) goto end_line;
+                  uid = pass->pw_uid;
+                }
+                s++;
+                // parse GID
+                gid = strtoul(s,&s2,10);
+                if (end2!=s2) {
+                  struct group *grp;
+                  char *str = strndup(s, end2-s);
+                  grp = getgrnam(str);
+                  free(str);
+                  if (!grp) goto end_line;
+                  gid = grp->gr_gid;
+                }
+                break;
+              }
+              // mode
+              case 1:
+              {
+                mode = strtoul(pos, &pos, 8);
+                if (pos!=end2) goto end_line;
+                goto found_device;
+              }
+            }
+            pos=end2;
+          }
+end_line:
+          // Did everything parse happily?
+          if (field && field!=3) error_exit("Bad line %d", line);
+
+          // Next line
+          pos = ++end;
+        }
+found_device:
+        munmap(conf, len);
+      }
+      close(fd);
+    }
+  }
+
+  sprintf(temp, "/dev/%s", device_name);
+  if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
+    perror_exit("mknod %s failed", temp);
+
+  if (CFG_MDEV_CONF) mode=chown(temp, uid, gid);
+}
+
+static int callback(struct dirtree *node)
+{
+  // Entries in /sys/class/block aren't char devices, so skip 'em.  (We'll
+  // get block devices out of /sys/block.)
+  if(!strcmp(node->name, "block")) return 0;
+
+  // Does this directory have a "dev" entry in it?
+  // This is path based because the hotplug callbacks are
+  if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) {
+    int len=4;
+    char *dev = dirtree_path(node, &len);
+    strcpy(dev+len, "/dev");
+    if (!access(dev, R_OK)) make_device(dev);
+    free(dev);
+  }
+
+  // Circa 2.6.25 the entries more than 2 deep are all either redundant
+  // (mouse#, event#) or unnamed (every usb_* entry is called "device").
+
+  return (node->parent && node->parent->parent) ? 0 : DIRTREE_RECURSE;
+}
+
+void mdev_main(void)
+{
+  // Handle -s
+
+  if (toys.optflags) {
+    dirtree_read("/sys/class", callback);
+    dirtree_read("/sys/block", callback);
+  }
+
+  // hotplug support goes here
+}
diff --git a/toys/pending/mke2fs.c b/toys/pending/mke2fs.c
new file mode 100644 (file)
index 0000000..750e442
--- /dev/null
@@ -0,0 +1,656 @@
+/* mke2fs.c - Create an ext2 filesystem image.
+ *
+ * Copyright 2006, 2007 Rob Landley <rob@landley.net>
+
+// Still to go: "E:jJ:L:m:O:"
+USE_MKE2FS(NEWTOY(mke2fs, "<1>2g:Fnqm#N#i#b#", TOYFLAG_SBIN))
+
+config MKE2FS
+  bool "mke2fs"
+  default n
+  help
+    usage: mke2fs [-Fnq] [-b ###] [-N|i ###] [-m ###] device
+
+    Create an ext2 filesystem on a block device or filesystem image.
+
+    -F         Force to run on a mounted device
+    -n         Don't write to device
+    -q         Quiet (no output)
+    -b size    Block size (1024, 2048, or 4096)
+    -N inodes  Allocate this many inodes
+    -i bytes   Allocate one inode for every XXX bytes of device
+    -m percent Reserve this percent of filesystem space for root user
+
+config MKE2FS_JOURNAL
+  bool "Journaling support (ext3)"
+  default n
+  depends on MKE2FS
+  help
+    usage: [-j] [-J size=###,device=XXX]
+
+    -j         Create journal (ext3)
+    -J         Journal options
+               size: Number of blocks (1024-102400)
+               device: Specify an external journal
+
+config MKE2FS_GEN
+  bool "Generate (gene2fs)"
+  default n
+  depends on MKE2FS
+  help
+    usage: gene2fs [options] device filename
+
+    The [options] are the same as mke2fs.
+
+config MKE2FS_LABEL
+  bool "Label support"
+  default n
+  depends on MKE2FS
+  help
+    usage: mke2fs [-L label] [-M path] [-o string]
+
+    -L         Volume label
+    -M         Path to mount point
+    -o         Created by
+
+config MKE2FS_EXTENDED
+  bool "Extended options"
+  default n
+  depends on MKE2FS
+  help
+    usage: mke2fs [-E stride=###] [-O option[,option]]
+
+    -E stride= Set RAID stripe size (in blocks)
+    -O [opts]  Specify fewer ext2 option flags (for old kernels)
+               All of these are on by default (as appropriate)
+       none         Clear default options (all but journaling)
+       dir_index    Use htree indexes for large directories
+       filetype     Store file type info in directory entry
+       has_journal  Set by -j
+       journal_dev  Set by -J device=XXX
+       sparse_super Don't allocate huge numbers of redundant superblocks
+*/
+
+#define FOR_mke2fs
+#include "toys.h"
+
+GLOBALS(
+  // Command line arguments.
+  long blocksize;
+  long bytes_per_inode;
+  long inodes;           // Total inodes in filesystem.
+  long reserved_percent; // Integer precent of space to reserve for root.
+  char *gendir;          // Where to read dirtree from.
+
+  // Internal data.
+  struct dirtree *dt;    // Tree of files to copy into the new filesystem.
+  unsigned treeblocks;   // Blocks used by dt
+  unsigned treeinodes;   // Inodes used by dt
+
+  unsigned blocks;       // Total blocks in the filesystem.
+  unsigned freeblocks;   // Free blocks in the filesystem.
+  unsigned inodespg;     // Inodes per group
+  unsigned groups;       // Total number of block groups.
+  unsigned blockbits;    // Bits per block.  (Also blocks per group.)
+
+  // For gene2fs
+  unsigned nextblock;    // Next data block to allocate
+  unsigned nextgroup;    // Next group we'll be allocating from
+  int fsfd;              // File descriptor of filesystem (to output to).
+
+  struct ext2_superblock sb;
+)
+
+#define INODES_RESERVED 10
+
+static uint32_t div_round_up(uint32_t a, uint32_t b)
+{
+  uint32_t c = a/b;
+
+  if (a%b) c++;
+  return c;
+}
+
+// Calculate data blocks plus index blocks needed to hold a file.
+
+static uint32_t file_blocks_used(uint64_t size, uint32_t *blocklist)
+{
+  uint32_t dblocks = (uint32_t)((size+(TT.blocksize-1))/TT.blocksize);
+  uint32_t idx=TT.blocksize/4, iblocks=0, diblocks=0, tiblocks=0;
+
+  // Fill out index blocks in inode.
+
+  if (blocklist) {
+    int i;
+
+    // Direct index blocks
+    for (i=0; i<13 && i<dblocks; i++) blocklist[i] = i;
+    // Singly indirect index blocks
+    if (dblocks > 13+idx) blocklist[13] = 13+idx;
+    // Doubly indirect index blocks
+    idx = 13 + idx + (idx*idx);
+    if (dblocks > idx) blocklist[14] = idx;
+
+    return 0;
+  }
+
+  // Account for direct, singly, doubly, and triply indirect index blocks
+
+  if (dblocks > 12) {
+    iblocks = ((dblocks-13)/idx)+1;
+    if (iblocks > 1) {
+      diblocks = ((iblocks-2)/idx)+1;
+      if (diblocks > 1)
+        tiblocks = ((diblocks-2)/idx)+1;
+    }
+  }
+
+  return dblocks + iblocks + diblocks + tiblocks;
+}
+
+// Use the parent pointer to iterate through the tree non-recursively.
+static struct dirtree *treenext(struct dirtree *this)
+{
+  while (this && !this->next) this = this->parent;
+  if (this) this = this->next;
+
+  return this;
+}
+
+// Recursively calculate the number of blocks used by each inode in the tree.
+// Returns blocks used by this directory, assigns bytes used to *size.
+// Writes total block count to TT.treeblocks and inode count to TT.treeinodes.
+
+static long check_treesize(struct dirtree *that, off_t *size)
+{
+  long blocks;
+
+  while (that) {
+    *size += sizeof(struct ext2_dentry) + strlen(that->name);
+
+    if (that->child)
+      that->st.st_blocks = check_treesize(that->child, &that->st.st_size);
+    else if (S_ISREG(that->st.st_mode)) {
+       that->st.st_blocks = file_blocks_used(that->st.st_size, 0);
+       TT.treeblocks += that->st.st_blocks;
+    }
+    that = that->next;
+  }
+  TT.treeblocks += blocks = file_blocks_used(*size, 0);
+  TT.treeinodes++;
+
+  return blocks;
+}
+
+// Calculate inode numbers and link counts.
+//
+// To do this right I need to copy the tree and sort it, but here's a really
+// ugly n^2 way of dealing with the problem that doesn't scale well to large
+// numbers of files (> 100,000) but can be done in very little code.
+// This rewrites inode numbers to their final values, allocating depth first.
+
+static void check_treelinks(struct dirtree *tree)
+{
+  struct dirtree *current=tree, *that;
+  long inode = INODES_RESERVED;
+
+  while (current) {
+    ++inode;
+    // Since we can't hardlink to directories, we know their link count.
+    if (S_ISDIR(current->st.st_mode)) current->st.st_nlink = 2;
+    else {
+      dev_t new = current->st.st_dev;
+
+      if (!new) continue;
+
+      // Look for other copies of current node
+      current->st.st_nlink = 0;
+      for (that = tree; that; that = treenext(that)) {
+        if (current->st.st_ino == that->st.st_ino &&
+          current->st.st_dev == that->st.st_dev)
+        {
+          current->st.st_nlink++;
+          current->st.st_ino = inode;
+        }
+      }
+    }
+    current->st.st_ino = inode;
+    current = treenext(current);
+  }
+}
+
+// According to http://www.opengroup.org/onlinepubs/9629399/apdxa.htm
+// we should generate a uuid structure by reading a clock with 100 nanosecond
+// precision, normalizing it to the start of the gregorian calendar in 1582,
+// and looking up our eth0 mac address.
+//
+// On the other hand, we have 128 bits to come up with a unique identifier, of
+// which 6 have a defined value.  /dev/urandom it is.
+
+static void create_uuid(char *uuid)
+{
+  // Read 128 random bits
+  int fd = xopen("/dev/urandom", O_RDONLY);
+  xreadall(fd, uuid, 16);
+  close(fd);
+
+  // Claim to be a DCE format UUID.
+  uuid[6] = (uuid[6] & 0x0F) | 0x40;
+  uuid[8] = (uuid[8] & 0x3F) | 0x80;
+
+  // rfc2518 section 6.4.1 suggests if we're not using a macaddr, we should
+  // set bit 1 of the node ID, which is the mac multicast bit.  This means we
+  // should never collide with anybody actually using a macaddr.
+  uuid[11] = uuid[11] | 128;
+}
+
+// Calculate inodes per group from total inodes.
+static uint32_t get_inodespg(uint32_t inodes)
+{
+  uint32_t temp;
+
+  // Round up to fill complete inode blocks.
+  temp = (inodes + TT.groups - 1) / TT.groups;
+  inodes = TT.blocksize/sizeof(struct ext2_inode);
+  return ((temp + inodes - 1)/inodes)*inodes;
+}
+
+// Fill out superblock and TT structures.
+
+static void init_superblock(struct ext2_superblock *sb)
+{
+  uint32_t temp;
+
+  // Set log_block_size and log_frag_size.
+
+  for (temp = 0; temp < 4; temp++) if (TT.blocksize == 1024<<temp) break;
+  if (temp==4) error_exit("bad blocksize");
+  sb->log_block_size = sb->log_frag_size = SWAP_LE32(temp);
+
+  // Fill out blocks_count, r_blocks_count, first_data_block
+
+  sb->blocks_count = SWAP_LE32(TT.blocks);
+  sb->free_blocks_count = SWAP_LE32(TT.freeblocks);
+  temp = (TT.blocks * (uint64_t)TT.reserved_percent) / 100;
+  sb->r_blocks_count = SWAP_LE32(temp);
+
+  sb->first_data_block = SWAP_LE32(TT.blocksize == 1024 ? 1 : 0);
+
+  // Set blocks_per_group and frags_per_group, which is the size of an
+  // allocation bitmap that fits in one block (I.E. how many bits per block)?
+
+  sb->blocks_per_group = sb->frags_per_group = SWAP_LE32(TT.blockbits);
+
+  // Set inodes_per_group and total inodes_count
+  sb->inodes_per_group = SWAP_LE32(TT.inodespg);
+  sb->inodes_count = SWAP_LE32(TT.inodespg * TT.groups);
+
+  // Determine free inodes.
+  temp = TT.inodespg*TT.groups - INODES_RESERVED;
+  if (temp < TT.treeinodes) error_exit("Not enough inodes.\n");
+  sb->free_inodes_count = SWAP_LE32(temp - TT.treeinodes);
+
+  // Fill out the rest of the superblock.
+  sb->max_mnt_count=0xFFFF;
+  sb->wtime = sb->lastcheck = sb->mkfs_time = SWAP_LE32(time(NULL));
+  sb->magic = SWAP_LE32(0xEF53);
+  sb->state = sb->errors = SWAP_LE16(1);
+
+  sb->rev_level = SWAP_LE32(1);
+  sb->first_ino = SWAP_LE32(INODES_RESERVED+1);
+  sb->inode_size = SWAP_LE16(sizeof(struct ext2_inode));
+  sb->feature_incompat = SWAP_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE);
+  sb->feature_ro_compat = SWAP_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
+
+  create_uuid(sb->uuid);
+
+  // TODO If we're called as mke3fs or mkfs.ext3, do a journal.
+
+  //if (strchr(toys.which->name,'3'))
+  //   sb->feature_compat |= SWAP_LE32(EXT3_FEATURE_COMPAT_HAS_JOURNAL);
+}
+
+// Does this group contain a superblock backup (and group descriptor table)?
+static int is_sb_group(uint32_t group)
+{
+  int i;
+
+  // Superblock backups are on groups 0, 1, and powers of 3, 5, and 7.
+  if(!group || group==1) return 1;
+  for (i=3; i<9; i+=2) {
+    int j = i;
+    while (j<group) j*=i;
+    if (j==group) return 1;
+  }
+  return 0;
+}
+
+
+// Number of blocks used in group by optional superblock/group list backup.
+static int group_superblock_overhead(uint32_t group)
+{
+  int used;
+
+  if (!is_sb_group(group)) return 0;
+
+  // How many blocks does the group descriptor table take up?
+  used = TT.groups * sizeof(struct ext2_group);
+  used += TT.blocksize - 1;
+  used /= TT.blocksize;
+  // Plus the superblock itself.
+  used++;
+  // And a corner case.
+  if (!group && TT.blocksize == 1024) used++;
+
+  return used;
+}
+
+// Number of blocks used in group to store superblock/group/inode list
+static int group_overhead(uint32_t group)
+{
+  // Return superblock backup overhead (if any), plus block/inode
+  // allocation bitmaps, plus inode tables.
+  return group_superblock_overhead(group) + 2 + get_inodespg(TT.inodespg)
+        / (TT.blocksize/sizeof(struct ext2_inode));
+}
+
+// In bitmap "array" set "len" bits starting at position "start" (from 0).
+static void bits_set(char *array, int start, int len)
+{
+  while(len) {
+    if ((start&7) || len<8) {
+      array[start/8]|=(1<<(start&7));
+      start++;
+      len--;
+    } else {
+      array[start/8]=255;
+      start+=8;
+      len-=8;
+    }
+  }
+}
+
+// Seek past len bytes (to maintain sparse file), or write zeroes if output
+// not seekable
+static void put_zeroes(int len)
+{
+  if(-1 == lseek(TT.fsfd, len, SEEK_SET)) {
+    memset(toybuf, 0, sizeof(toybuf));
+    while (len) {
+      int out = len > sizeof(toybuf) ? sizeof(toybuf) : len;
+      xwrite(TT.fsfd, toybuf, out);
+      len -= out;
+    }
+  }
+}
+
+// Fill out an inode structure from struct stat info in dirtree.
+static void fill_inode(struct ext2_inode *in, struct dirtree *that)
+{
+  uint32_t fbu[15];
+  int temp;
+
+  file_blocks_used(that->st.st_size, fbu);
+
+  // If that inode needs data blocks allocated to it.
+  if (that->st.st_size) {
+    int i, group = TT.nextblock/TT.blockbits;
+
+    // TODO: teach this about indirect blocks.
+    for (i=0; i<15; i++) {
+      // If we just jumped into a new group, skip group overhead blocks.
+      while (group >= TT.nextgroup)
+        TT.nextblock += group_overhead(TT.nextgroup++);
+    }
+  }
+  // TODO :  S_ISREG/DIR/CHR/BLK/FIFO/LNK/SOCK(m)
+  in->mode = SWAP_LE32(that->st.st_mode);
+
+  in->uid = SWAP_LE16(that->st.st_uid & 0xFFFF);
+  in->uid_high = SWAP_LE16(that->st.st_uid >> 16);
+  in->gid = SWAP_LE16(that->st.st_gid & 0xFFFF);
+  in->gid_high = SWAP_LE16(that->st.st_gid >> 16);
+  in->size = SWAP_LE32(that->st.st_size & 0xFFFFFFFF);
+
+  // Contortions to make the compiler not generate a warning for x>>32
+  // when x is 32 bits.  The optimizer should clean this up.
+  if (sizeof(that->st.st_size) > 4) temp = 32;
+  else temp = 0;
+  if (temp) in->dir_acl = SWAP_LE32(that->st.st_size >> temp);
+
+  in->atime = SWAP_LE32(that->st.st_atime);
+  in->ctime = SWAP_LE32(that->st.st_ctime);
+  in->mtime = SWAP_LE32(that->st.st_mtime);
+
+  in->links_count = SWAP_LE16(that->st.st_nlink);
+  in->blocks = SWAP_LE32(that->st.st_blocks);
+  // in->faddr
+}
+
+// Works like an archiver.
+// The first argument is the name of the file to create.  If it already
+// exists, that size will be used.
+
+void mke2fs_main(void)
+{
+  int i, temp;
+  off_t length;
+  uint32_t usedblocks, usedinodes, dtiblk, dtbblk;
+  struct dirtree *dti, *dtb;
+
+  // Handle command line arguments.
+
+  if (toys.optargs[1]) {
+    sscanf(toys.optargs[1], "%u", &TT.blocks);
+    temp = O_RDWR|O_CREAT;
+  } else temp = O_RDWR;
+  if (!TT.reserved_percent) TT.reserved_percent = 5;
+
+  // TODO: Check if filesystem is mounted here
+
+  // For mke?fs, open file.  For gene?fs, create file.
+  TT.fsfd = xcreate(*toys.optargs, temp, 0777);
+
+  // Determine appropriate block size and block count from file length.
+  // (If no length, default to 4k.  They can override it on the cmdline.)
+
+  length = fdlength(TT.fsfd);
+  if (!TT.blocksize) TT.blocksize = (length && length < 1<<29) ? 1024 : 4096;
+  TT.blockbits = 8*TT.blocksize;
+  if (!TT.blocks) TT.blocks = length/TT.blocksize;
+
+  // Collect gene2fs list or lost+found, calculate requirements.
+
+  if (TT.gendir) {
+    strncpy(toybuf, TT.gendir, sizeof(toybuf));
+    dti = dirtree_read(toybuf, dirtree_notdotdot);
+  } else {
+    dti = xzalloc(sizeof(struct dirtree)+11);
+    strcpy(dti->name, "lost+found");
+    dti->st.st_mode = S_IFDIR|0755;
+    dti->st.st_ctime = dti->st.st_mtime = time(NULL);
+  }
+
+  // Add root directory inode.  This is iterated through for when finding
+  // blocks, but not when finding inodes.  The tree's parent pointers don't
+  // point back into this.
+
+  dtb = xzalloc(sizeof(struct dirtree)+1);
+  dtb->st.st_mode = S_IFDIR|0755;
+  dtb->st.st_ctime = dtb->st.st_mtime = time(NULL);
+  dtb->child = dti;
+
+  // Figure out how much space is used by preset files
+  length = check_treesize(dtb, &(dtb->st.st_size));
+  check_treelinks(dtb);
+
+  // Figure out how many total inodes we need.
+
+  if (!TT.inodes) {
+    if (!TT.bytes_per_inode) TT.bytes_per_inode = 8192;
+    TT.inodes = (TT.blocks * (uint64_t)TT.blocksize) / TT.bytes_per_inode;
+  }
+
+  // If we're generating a filesystem and have no idea how many blocks it
+  // needs, start with a minimal guess, find the overhead of that many
+  // groups, and loop until this is enough groups to store this many blocks.
+  if (!TT.blocks) TT.groups = (TT.treeblocks/TT.blockbits)+1;
+  else TT.groups = div_round_up(TT.blocks, TT.blockbits);
+
+  for (;;) {
+    temp = TT.treeblocks;
+
+    for (i = 0; i<TT.groups; i++) temp += group_overhead(i);
+
+    if (TT.blocks) {
+      if (TT.blocks < temp) error_exit("Not enough space.\n");
+      break;
+    }
+    if (temp <= TT.groups * TT.blockbits) {
+      TT.blocks = temp;
+      break;
+    }
+    TT.groups++;
+  }
+  TT.freeblocks = TT.blocks - temp;
+
+  // Now we know all the TT data, initialize superblock structure.
+
+  init_superblock(&TT.sb);
+
+  // Start writing.  Skip the first 1k to avoid the boot sector (if any).
+  put_zeroes(1024);
+
+  // Loop through block groups, write out each one.
+  dtiblk = dtbblk = usedblocks = usedinodes = 0;
+  for (i=0; i<TT.groups; i++) {
+    struct ext2_inode *in = (struct ext2_inode *)toybuf;
+    uint32_t start, itable, used, end;
+    int j, slot;
+
+    // Where does this group end?
+    end = TT.blockbits;
+    if ((i+1)*TT.blockbits > TT.blocks) end = TT.blocks & (TT.blockbits-1);
+
+    // Blocks used by inode table
+    itable = (TT.inodespg*sizeof(struct ext2_inode))/TT.blocksize;
+
+    // If a superblock goes here, write it out.
+    start = group_superblock_overhead(i);
+    if (start) {
+      struct ext2_group *bg = (struct ext2_group *)toybuf;
+      int treeblocks = TT.treeblocks, treeinodes = TT.treeinodes;
+
+      TT.sb.block_group_nr = SWAP_LE16(i);
+
+      // Write superblock and pad it up to block size
+      xwrite(TT.fsfd, &TT.sb, sizeof(struct ext2_superblock));
+      temp = TT.blocksize - sizeof(struct ext2_superblock);
+      if (!i && TT.blocksize > 1024) temp -= 1024;
+      memset(toybuf, 0, TT.blocksize);
+      xwrite(TT.fsfd, toybuf, temp);
+
+      // Loop through groups to write group descriptor table.
+      for(j=0; j<TT.groups; j++) {
+
+        // Figure out what sector this group starts in.
+        used = group_superblock_overhead(j);
+
+        // Find next array slot in this block (flush block if full).
+        slot = j % (TT.blocksize/sizeof(struct ext2_group));
+        if (!slot) {
+          if (j) xwrite(TT.fsfd, bg, TT.blocksize);
+          memset(bg, 0, TT.blocksize);
+        }
+
+        // How many free inodes in this group?
+        temp = TT.inodespg;
+        if (!i) temp -= INODES_RESERVED;
+        if (temp > treeinodes) {
+          treeinodes -= temp;
+          temp = 0;
+        } else {
+          temp -= treeinodes;
+          treeinodes = 0;
+        }
+        bg[slot].free_inodes_count = SWAP_LE16(temp);
+
+        // How many free blocks in this group?
+        temp = TT.inodespg/(TT.blocksize/sizeof(struct ext2_inode)) + 2;
+        temp = end-used-temp;
+        if (temp > treeblocks) {
+          treeblocks -= temp;
+          temp = 0;
+        } else {
+          temp -= treeblocks;
+          treeblocks = 0;
+        }
+        bg[slot].free_blocks_count = SWAP_LE32(temp);
+
+        // Fill out rest of group structure
+        used += j*TT.blockbits;
+        bg[slot].block_bitmap = SWAP_LE32(used++);
+        bg[slot].inode_bitmap = SWAP_LE32(used++);
+        bg[slot].inode_table = SWAP_LE32(used);
+        bg[slot].used_dirs_count = 0;  // (TODO)
+      }
+      xwrite(TT.fsfd, bg, TT.blocksize);
+    }
+
+    // Now write out stuff that every block group has.
+
+    // Write block usage bitmap
+
+    start += 2 + itable;
+    memset(toybuf, 0, TT.blocksize);
+    bits_set(toybuf, 0, start);
+    bits_set(toybuf, end, TT.blockbits-end);
+    temp = TT.treeblocks - usedblocks;
+    if (temp) {
+      if (end-start > temp) temp = end-start;
+      bits_set(toybuf, start, temp);
+    }
+    xwrite(TT.fsfd, toybuf, TT.blocksize);
+
+    // Write inode bitmap
+    memset(toybuf, 0, TT.blocksize);
+    j = 0;
+    if (!i) bits_set(toybuf, 0, j = INODES_RESERVED);
+    bits_set(toybuf, TT.inodespg, slot = TT.blockbits-TT.inodespg);
+    temp = TT.treeinodes - usedinodes;
+    if (temp) {
+      if (slot-j > temp) temp = slot-j;
+      bits_set(toybuf, j, temp);
+    }
+    xwrite(TT.fsfd, toybuf, TT.blocksize);
+
+    // Write inode table for this group (TODO)
+    for (j = 0; j<TT.inodespg; j++) {
+      slot = j % (TT.blocksize/sizeof(struct ext2_inode));
+      if (!slot) {
+        if (j) xwrite(TT.fsfd, in, TT.blocksize);
+        memset(in, 0, TT.blocksize);
+      }
+      if (!i && j<INODES_RESERVED) {
+        // Write root inode
+        if (j == 2) fill_inode(in+slot, dtb);
+      } else if (dti) {
+        fill_inode(in+slot, dti);
+        dti = treenext(dti);
+      }
+    }
+    xwrite(TT.fsfd, in, TT.blocksize);
+
+    while (dtb) {
+      // TODO write index data block
+      // TODO write root directory data block
+      // TODO write directory data block
+      // TODO write file data block
+      put_zeroes(TT.blocksize);
+      start++;
+      if (start == end) break;
+    }
+    // Write data blocks (TODO)
+    put_zeroes((end-start) * TT.blocksize);
+  }
+}
diff --git a/toys/pending/more.c_1 b/toys/pending/more.c_1
new file mode 100644 (file)
index 0000000..12b2309
--- /dev/null
@@ -0,0 +1,1023 @@
+/* vi: set sw=4 ts=4:
+ *
+ * more.c - A more command implementation for toybox/busybox_replacement project.
+ *
+ * Copyright 2012 Vikash Kumar <k.vikash@samsung.com>
+ *
+ *
+
+USE_MORE(NEWTOY(more, "p:l#n#sfd", TOYFLAG_USR|TOYFLAG_BIN))
+
+config MORE
+  bool "more"
+  default n
+  help
+
+    usage: more [-dfs] [-n number] [-l num] [-p string] [file...]
+
+    A more command implementation for toybox/busybox_replacement project.
+
+    options:
+
+       -d:Prompt the user with the message "[Press <space> to continue, 'q' to quit/]" and will display "[Press 'h' for help instruction.]" when an illegal key is pressed.
+
+       -f:Do not scroll. Instead, it will clear the whole screen and then display the text.
+
+       -s:squeeze multiple blank lines into single line.
+
+       -n number:will set the screen size(in lines).
+
+       -l num:will start displaying the text at line number 'num'.
+
+       -p string:will search the string and then display the text at line containing that string.
+
+*/
+
+#define FOR_more
+
+#include "toys.h"
+
+#include <termios.h>
+#include <signal.h>
+
+GLOBALS(
+
+  long n_number;
+  long l_number;
+  char *p_string;
+
+  char *filename;
+  int file_count;
+  int file_flag;
+  int loop_exit;
+  int vi_flag;
+  int skip_flag;
+  int tty_fileno;
+  struct termios term;
+  struct termios buff;
+)
+
+
+void signal_handler(int signum)
+{
+  if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+       perror("TCSETATTR error: ");
+  printf("\033[2K\r");
+  exit(signum);
+}
+
+void get_screen_size(int *rw, int *colm)
+{
+  struct winsize win;
+  char *crw, *ccolm;
+  
+  if(ioctl(TT.tty_fileno, TIOCGWINSZ, &win) < 0)
+  {
+       if((crw=getenv("LINES")) != NULL && (ccolm=getenv("COLUMNS")) != NULL)
+       {
+               *rw=atoi(crw);
+               *colm=atoi(ccolm);
+       }
+  }
+  else
+  {
+       *rw=win.ws_row;
+       *colm=win.ws_col;
+  }
+}
+
+int total_lines(FILE *fp)
+{
+  int total=0, ch;
+  do
+  {
+       if((ch = fgetc(fp)) == '\n')
+               total++;
+  }while(ch != EOF);
+  rewind(fp);
+  return total;
+}
+
+void print_help(void)
+{
+  puts("\nMost commands optionally preceded by integer argument k.  Defaults in brackets.");
+  puts("Star (*) indicates argument becomes new default.");
+  puts("---------------------------------------"
+       "----------------------------------------");
+  puts(
+"<space>               Display next k lines of text [current screen size]\n"
+"z                     Display next k lines of text [current screen size]*\n"
+"<return>              Display next k lines of text [1]*\n"
+"d or ctrl-D           Scroll k lines [current scroll size, half screenful]*\n"
+"q or Q or <interrupt>   Exit from more\n"
+"s                     Skip forward k lines of text [1]\n"
+"f                     Skip forward k screenfuls of text [1]\n"
+"b or ctrl-B           Skip backwards k screenfuls of text [1]*\n"
+"k                     Skip backwards k lines of text [1]*\n"
+"u                     Skip backwards half screenful of text [1]*\n"
+"=                     Display current line number\n"
+"/<regular expression>   Search for kth occurrence of regular expression [1]\n"
+"n                     Search for kth occurrence of last r.e [1]\n"
+"!<cmd>                Execute <cmd> in a subshell\n"
+"v                     Start up /usr/bin/vi at current line\n"
+"ctrl-L                Redraw screen\n"
+":n                    Go to kth next file [1]\n"
+":p                    Go to kth previous file [1]\n"
+":f                    Display current file name and line number\n"
+".                     Repeat previous command"
+);
+  puts("---------------------------------------"
+       "----------------------------------------");
+  return;
+}
+
+int line_skeep(FILE *file, char str, int new, int old)
+{
+  int ch, rws, cols, ret; //count=1;
+
+  get_screen_size(&rws, &cols);
+  printf("\033[2K\r");
+  printf("HI\n");
+  if(str == 's')
+  {
+       rws=((new==0)?2:(new+1));
+       if(new == 0)
+               puts("\n...skipping one line");
+       else if(old)                            //old->to distinguish between option '-l' & input 's' 
+               fprintf(stdout, "\n...skipping %d lines\n", (rws-1));
+  }
+  else if(str == 'f')
+  {
+       rws=((old==0)?rws:(old+1));
+       rws=((new==0)?rws:(new*(rws-1)+1));
+       fprintf(stdout, "\n...skipping %d lines\n", (rws-1));
+  }
+  ret=rws-1;
+  while(--rws > 0)
+  {
+       while((ch=fgetc(file)) != '\n' && ch != EOF)
+       {
+               ;
+       }
+       if(ch == EOF)
+       {
+               fclose(file);
+               TT.file_count--;
+               if(TT.file_count <= 0)
+               {
+                       if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                               fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                       exit(0);
+               }
+               else
+               {       
+                       TT.skip_flag=1;
+                       return 0;
+               }
+       }
+       if(ch == '\n' );
+  }
+  return ret;
+}
+
+void file_edit(int rw_count, int rws)
+{
+  pid_t pid;
+  int ret;
+  int arg;
+  char *editor;
+  char buf[10];
+
+  if((pid = fork()) < 0)
+  {
+       fprintf(stderr, "Error Caused, Editor can't be opened: %s\n", strerror(errno));
+       return;
+  }
+  else if(pid == 0)
+  {
+       if((editor=getenv("VISUAL")) == (char *)0)
+               if((editor=getenv("EDITOR")) == (char *)0)
+                       editor="vi";
+       if(rw_count == (rws -1))
+       {
+               arg=1;
+       }
+       else
+       {
+               if(!TT.vi_flag)
+                       arg=1 + (rw_count/2);
+               else if(TT.vi_flag)
+                       arg=(rw_count-(rws/2));
+       }
+       printf("\033[2K\r");
+       if(strcmp(editor, "vi") == 0)
+       {
+               sprintf(buf, "%d", arg);
+               printf("%s -c %d %s------------------------\n", editor, arg, TT.filename);
+               if(execlp(editor, editor, "-c", buf, TT.filename, NULL) == -1)
+                       perror("Editor Error: ");
+       }
+       else if(strcmp(editor, "gedit") == 0)
+       {
+               sprintf(buf, "+%d", arg);
+               printf("%s +%d %s------------------------\n", editor, arg, TT.filename);
+               if(execlp(editor, editor, buf, TT.filename, NULL) == -1)
+                       perror("Editor Error: ");
+       }
+  }
+  if(wait(&ret) != pid)
+       perror("Error handling editor: ");
+  return;
+}
+
+void line_skeep_back(FILE *file, char str, int *rw_cnt,  int new, ...)
+{
+  int rws, cols, ch, count, ret;
+  int old;
+  va_list arg;
+
+  va_start(arg, new);
+  get_screen_size(&rws, &cols);
+  printf("\033[2K\r");
+  if(str == 'b' || str == CTRL('B'))
+  {
+       old=va_arg(arg, int);
+       va_end(arg);
+       rws=((old==0)?(rws):(old+1));
+       rws=((new==0)?(rws*2):rws*(new+1)-(new-1));
+
+       if(new == 0 || new == 1)
+               puts("\n...back 1 page");
+       else
+               fprintf(stdout, "\n...back %d pages\n", new);
+  }
+  else if(str == CTRL('l'))
+  {
+       old=va_arg(arg, int);
+       cols=va_arg(arg, int);                  //cols=previous screen columns
+       va_end(arg);
+
+       if(rws == new)                          //new=previous screen rows
+               rws=((old==0)?rws:(old+1));
+       else
+               rws=((old==0)?new:(old+1));
+  }
+  else if(str == 'k')
+  {
+       old=va_arg(arg, int);
+       va_end(arg);
+       rws=((old==0)?rws:(old+1));
+       rws=((new==0)?(rws+2):(rws+new+1));
+
+       if(new == 0|| new == 1)
+               puts("\n...back one line");
+       else
+               fprintf(stdout, "\n...back %d lines\n", new);
+  }
+  else if(str == 'u')
+  {
+       if(new == 0)
+               fprintf(stdout, "\n...back %d lines\n", (rws-1)/2);
+       else
+               fprintf(stdout, "\n...back %d lines\n", new);
+       rws=((new==0)?(rws+(rws/2)):(rws+new+1));
+  }
+  ret=rws-1;
+  fseek(file, -1, SEEK_CUR);
+  while(rws > 0)
+  {
+       count=1;
+       while(1)
+       {
+               ch=fgetc(file);
+               fseek(file, -2, SEEK_CUR);
+               if(ch == '\t')
+                       count+=8;
+               else
+                       count++;
+               if(ch=='\n' || ftell(file) == 0 || count > cols)
+                       break;
+       }
+       if(ftell(file) == 0 && rws > 0)
+       {
+               *rw_cnt=0;
+               break;
+       }
+       rws--;
+  }
+  if(rws == 0)
+       *rw_cnt-=ret;
+  if(ftell(file) != 0)
+       fseek(file, 2, SEEK_CUR);
+  return;
+}
+
+long pattern_find(FILE *file, char *patt, char str, int *rw_count)
+{
+  long row_loc, temp_loc, act_loc;
+  char *temp;
+  int ch, rws, cols, i, cnt=0;
+
+  get_screen_size(&rws, &cols);
+  act_loc=ftell(file);
+  line_skeep_back(file, CTRL('L'), rw_count, rws, 0, cols);
+  row_loc=ftell(file);
+  temp_loc=row_loc;
+
+  temp=(char *)malloc(sizeof(patt));
+
+  for(;;)
+  {
+       i=0;
+       while((ch=fgetc(file)) == ' ' || ch == '\t');
+       while(ch != ' ' && ch != '\t' && ch != '\n' && (i < strlen(patt)))
+       {
+               temp[i]=ch;
+               ch=fgetc(file);
+               i++;
+       }
+       temp[i]='\0';
+       if(strcmp(patt, temp) == 0)
+       {
+               if(str != 'n')
+               {
+                       row_loc = temp_loc;
+                       break;
+               }
+               else if(row_loc != temp_loc)
+               {
+                       row_loc = temp_loc;
+                       break;
+               }
+       }
+       else if(ch == EOF)
+       {
+               fseek(file, act_loc, SEEK_SET);
+               row_loc=-1;
+               break;
+       }
+       else if(ch == '\n')
+       {
+               cnt++;
+               temp_loc=ftell(file);
+       }
+       memset(temp, '\0', sizeof(temp));
+  }
+  if(ch != EOF)
+       *rw_count+=cnt;
+  else
+       *rw_count+=(rws-1);
+  fflush(stdout);
+
+  return row_loc;
+}
+
+int copy_file(FILE *file, int rws, int *row_count, char cur_str)
+{
+  int columns, rows, col_count, ch; 
+  static int flag = 1;
+
+  get_screen_size(&rows, &columns);
+  rows=rws;
+
+  while(--rows > 0)
+  {
+       col_count=1;
+       while((ch=fgetc(file)) != '\n' && col_count < columns && ch != EOF)
+       {
+               if(ch == '\t')
+                       col_count+=8;
+               else
+                       col_count++;
+               fputc(ch,stdout);
+       }
+       if((ch == '\n') || (col_count >= columns))
+       {
+               if(ch == '\n')
+               {
+                       if(col_count == 1)
+                       {
+                               if(flag)
+                               {
+                                       fputc(ch, stdout);
+                                       if(toys.optflags & FLAG_s)
+                                               flag = 0;
+                               }
+                               else
+                               {
+                                       rows++;
+                                       (*row_count)++;
+                               }
+                       }
+                       else
+                       {
+                               fputc(ch, stdout);
+                               flag = 1;
+                       }
+               }
+               else
+               {
+                       fputc(ch,stdout);
+                       if(cur_str != 'k'&&cur_str != CTRL('L')&&cur_str != 'b'&&cur_str != 'u')
+                               (*row_count)--;
+               }
+               continue;
+       }
+       else if(ch == EOF)
+       {
+               fclose(file);
+               TT.file_count--;
+               if(TT.file_count <= 0)
+               {
+                       if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                               fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                       exit(0);
+               }
+               else
+                       return 1;
+       }
+  }
+  return 0;
+}
+
+void more_main(void)
+{
+  int i; 
+  int screen_rows;                     //hold the screen row size
+  int total_line=0;                    //for the total lines in a file
+  int rows;                                    //row size of the screen / terminal
+  int columns;                         //column size of the screen / terminal
+  int row_count=0;                     //to count the number of lines read of the open file
+  int old_arg=0;                               //to hold the fixed numeral arguments
+  int new_arg=0;                               //to hold the passed numeral arguments
+  int d_arg=0;                         //to hold the numeral argument for 'd' feature
+  int u_arg=0;                         //to hold the numeral argument for 'u' feature
+  int dot_arg=0;                               //to hold the numeral argument for '.' feature
+  int l_arg=0;                         //to hold the numeral argument for 'CTRL('l') feature
+  FILE *file=NULL;                     //for the file to be read
+  long ptr_loc;                                //needed for the pattern_matching, hold the reading head position
+  int in_tty;                                  //for the input terminal
+  int out_tty;                         //for the output terminal
+  struct stat st;
+  char ch;
+  char prev_str='\0';                  //hold the previous input passed
+  char cur_str='\0';                   //hold the current input passed
+  char cmd_call[20]={'\0'};    //for the '!' commmand call
+  char arg_str[30]={'\0'};     //for the '=' feature
+  char patt_str[30]={'\0'};    //for the pattern matching
+
+  signal(SIGINT, signal_handler);
+  
+  out_tty=isatty(STDOUT_FILENO);
+  if((in_tty=isatty(STDIN_FILENO)) && !(toys.optc == 1 && strcmp(*toys.optargs, "-") == 0))
+       TT.tty_fileno=STDIN_FILENO;
+  else
+       TT.tty_fileno=STDOUT_FILENO;
+
+  if(tcgetattr(TT.tty_fileno, &TT.term) < 0)
+  {
+       fprintf(stderr, "Can't get terminal attributes: %s", strerror(errno));
+       exit(EXIT_FAILURE);
+  }
+  memcpy(&TT.buff, &TT.term,sizeof(TT.buff));
+  TT.buff.c_lflag &= ~(ECHO | ICANON);
+  TT.buff.c_cc[VMIN] = 1;
+  TT.buff.c_cc[VTIME] = 0;
+
+  if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.buff) < 0)
+  {
+      tcsetattr(TT.tty_fileno, TCSANOW, &TT.term);
+          exit(EXIT_FAILURE);
+     }
+  get_screen_size(&rows, &columns);
+  screen_rows=rows;
+  TT.file_count=toys.optc;
+
+  if(!in_tty)
+  {
+       file = stdin;
+       TT.file_flag=1;
+       toys.optc++;
+       TT.file_count=toys.optc;
+  }
+  else if(out_tty)
+  {
+       if(toys.optc < 1 || (strcmp(*toys.optargs,"-") == 0 && toys.optc == 1))
+       {
+               printf("usage: %s [-dfs] [-n number] [-l num] [-p string] [file...]\n", toys.which->name);
+               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+               exit(0);
+       }
+
+loop:  if(*toys.optargs)
+       {
+               TT.filename=*toys.optargs;
+  if(strcmp(*toys.optargs, "-") == 0 && ((TT.file_count == toys.optc) || (!in_tty && TT.file_count == toys.optc-1)))
+               {
+                       printf("\033[2K\r");
+                       TT.file_count--;
+                       if(!in_tty && toys.optc == 2)
+                       {
+                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                               exit(0);
+                       }
+                       toys.optargs++;
+                       goto loop;
+               }
+               if((file = fopen(TT.filename, "r")) == NULL)
+               {
+                       printf("\033[2K\r");
+                       fprintf(stderr,"%s: %s\n",TT.filename,strerror(errno));
+                       TT.file_count--;
+                       if(TT.file_count <= 0)
+                       {
+                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                               exit(0);
+                       }
+                       else
+                       {
+                               TT.file_flag=1;
+                               toys.optargs++;
+                               goto loop;
+                       }
+               }
+               else
+               {
+                       TT.loop_exit=1;
+                       memset(arg_str, '\0', sizeof(arg_str));
+                       total_line = total_lines(file);
+                       row_count = 0;
+                       if(TT.file_flag)
+                       {
+                               printf("---MORE---(next file %s)", TT.filename);
+                               fflush(stdout);
+                               goto jump;
+                       }
+               }
+       }
+  }
+  st.st_size = 0;
+  fstat(fileno(file), &st);
+
+  if(file != NULL)
+  {
+       if(toys.optflags & FLAG_n)
+               rows=TT.n_number+1;
+
+       if(toys.optflags & FLAG_l)
+               if(!in_tty || (strcmp(TT.filename, toys.optargs[0]) == 0 && in_tty))
+                       row_count+=line_skeep(file, 's', (TT.l_number-1), 0);
+
+       if((toys.optflags & FLAG_p) && file != stdin)
+       {
+               if(strcmp(TT.filename, toys.optargs[0]) == 0)
+               {
+                       if((ptr_loc=pattern_find(file, TT.p_string, '/', &row_count)) == -1)
+                       {
+                               printf("Pattern Not Found");
+                               fflush(stdout);
+                               goto jump;
+                       }
+                       else
+                       {
+                               fseek(file, ptr_loc, SEEK_SET);
+                               strcpy(patt_str, TT.p_string);
+                       }
+               }
+               else
+                       memset(patt_str, '\0', sizeof(patt_str));
+       }
+
+       while(1)
+       {
+               if(TT.loop_exit)
+               {
+                       if(toys.optc > 1)
+                       {
+                               printf("\033[2K\r");
+                               puts("::::::::::::::");
+                               printf("%s\n", TT.filename);
+                               puts("::::::::::::::");
+                               if((rows+3) > screen_rows)
+                                       rows-=((rows+3)-screen_rows);
+                               TT.file_flag=1;
+                       }
+                       TT.loop_exit=0;
+               }
+
+               printf("\033[2K\r");
+
+               row_count+=(rows-1);
+
+               if(copy_file(file, rows, &row_count, cur_str))
+               {
+                       if(file != stdin)
+                               toys.optargs++;
+                       goto loop;
+               }
+               if(row_count >= total_line && file != stdin)
+               {
+                       fclose(file);
+                       TT.file_count--;
+                              if(TT.file_count<=0)
+                       {
+                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                                  exit(0);
+                       }
+                       else
+                       {
+                               toys.optargs++;
+                               goto loop;
+                       }
+               }
+               if(file != stdin)
+                       printf("---MORE---(%d%%)",(row_count*100)/total_line);
+               else
+                       printf("---MORE---");
+               if(toys.optflags & FLAG_d)
+                       printf("[Press <space> to continue, 'q' to quit.]");
+               fflush(stdout);
+
+jump:          if(read(TT.tty_fileno, &cur_str, 1) < 0)
+               {
+                       perror("READ ERROR: ");
+                       exit(EXIT_FAILURE);
+               }
+
+               if(isdigit(cur_str))
+               {
+                       strcat(arg_str, &cur_str);
+                       arg_str[strlen(arg_str)-2]='\0';
+                       goto jump;
+               }
+               new_arg=atoi(arg_str);
+               memset(arg_str, '\0', sizeof(arg_str));
+
+               if(toys.optflags & FLAG_n)
+                       old_arg=TT.n_number;
+
+swap:          switch(cur_str)
+               {
+                       case '\n':
+                               get_screen_size(&rows, &columns);
+                               screen_rows=rows;
+                               rows=((new_arg==0)?2:(new_arg+1));
+                               old_arg=((new_arg==0)?old_arg:new_arg);
+                               dot_arg=new_arg;
+                               prev_str=cur_str;
+                               break;
+                       case ' ':
+                               get_screen_size(&rows, &columns);
+                               screen_rows=rows;
+                               rows=((new_arg==0)?((old_arg==0)?rows:(old_arg+1)):(new_arg+1));
+                               dot_arg=new_arg;
+                               TT.vi_flag=1;
+                               prev_str=cur_str;
+                               break;
+                       case 'q':
+                       case 'Q':
+                               fclose(file);
+                               printf("\033[2K\r");
+                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                               exit(0);
+                       case 'h':
+                       case '?':
+                               if(toys.optflags & FLAG_f)
+                                       printf("\033[2J\033[1;1H");
+                               print_help();
+                               prev_str=cur_str;
+                               rows=1;
+                               break;
+                       case 's':
+                               row_count+=line_skeep(file, cur_str, new_arg, 1);
+                               get_screen_size(&rows, &columns);
+                               rows=((old_arg==0)?rows:(old_arg+1));
+                               dot_arg=new_arg;
+                               if(TT.skip_flag == 1)
+                               {
+                                       toys.optargs++;
+                                       goto loop;
+                               }
+                               prev_str=cur_str;
+                               break;
+                       case CTRL('f'):
+                       case 'f':
+                               get_screen_size(&rows, &columns);
+                               rows=((old_arg==0)?rows:(old_arg+1));
+                               row_count+=line_skeep(file, cur_str, new_arg, old_arg);
+                               dot_arg=new_arg;
+                               if(TT.skip_flag == 1)
+                               {
+                                       toys.optargs++;
+                                       goto loop;
+                               }
+                               prev_str=cur_str;
+                               break;
+                       case 'v':
+                               if(file != stdin)
+                               {
+                                       get_screen_size(&rows, &columns);
+                                       file_edit(row_count, rows);
+                                       prev_str=cur_str;
+                               }
+                               rows=1;
+                               break;
+                       case 'z':
+                               get_screen_size(&rows, &columns);
+                               screen_rows=rows;
+                               old_arg=((new_arg==0)?old_arg:new_arg);
+                               rows=((old_arg==0)?rows:(old_arg+1));
+                               dot_arg=new_arg;
+                               TT.vi_flag=1;
+                               prev_str=cur_str;
+                               break;
+                       case CTRL('d'):
+                       case 'd':
+                               get_screen_size(&rows, &columns);
+                               screen_rows=rows;
+                               d_arg=((new_arg==0)?d_arg:new_arg);
+                               TT.vi_flag=1;
+                               rows=((d_arg==0)?(rows/2):(d_arg+1));
+                               dot_arg=d_arg;
+                               prev_str=cur_str;
+                               break;
+                       case CTRL('L'):
+                               if(file != stdin)
+                               {
+                                       printf("\033[2K\r");
+                                       printf("\033[2J\033[1;1H");
+                                       l_arg=((old_arg==0)?l_arg:old_arg);
+                                       line_skeep_back(file, cur_str, &row_count, screen_rows, l_arg, columns);
+                                       get_screen_size(&rows, &columns);
+                                       if(rows == screen_rows)
+                                               rows=((l_arg==0)?rows:(l_arg+1));
+                                       else
+                                       {
+                                               screen_rows=rows;
+                                               l_arg=0;
+                                               old_arg=0;
+                                       }
+                                       dot_arg=l_arg;
+                                       prev_str=cur_str;
+                                       break;
+                               }
+                               else
+                                       goto jump;
+                       case 'b':
+                       case CTRL('B'):
+                               if(file != stdin)
+                               {
+                                       get_screen_size(&rows, &columns);
+                                       line_skeep_back(file, cur_str, &row_count, new_arg, old_arg);
+                                       if(ftell(file) != 0)
+                                               rows=((old_arg==0)?(rows+1):(old_arg+2));
+                                       else
+                                               rows=((old_arg==0)?rows:(old_arg+1));
+                                       dot_arg=new_arg;
+                                       prev_str=cur_str;
+                                       break;
+                               }
+                               else
+                                       goto jump;
+                       case 'k':
+                               if(file != stdin)
+                               {
+                                       get_screen_size(&rows, &columns);
+                                       line_skeep_back(file, cur_str, &row_count, new_arg, old_arg);
+                                       if(ftell(file) != 0)
+                                               rows=((old_arg==0)?(rows+1):(old_arg+2));
+                                       else
+                                               rows=((old_arg==0)?rows:(old_arg+1));
+                                       dot_arg=new_arg;
+                                       prev_str=cur_str;
+                                       break;
+                               }
+                               else
+                                       goto jump;
+                       case 'u':
+                               if(file != stdin)
+                               {
+                                       get_screen_size(&rows, &columns);
+                                       u_arg=((new_arg==0)?u_arg:new_arg);
+                                       line_skeep_back(file, cur_str, &row_count, u_arg);
+                                       if(ftell(file) != 0)
+                                               rows=((old_arg==0)?(rows+1):(old_arg+2));
+                                       else
+                                               rows=((old_arg==0)?rows:(old_arg+1));
+                                       dot_arg=new_arg;
+                                       prev_str=cur_str;
+                                       break;
+                               }
+                               else
+                                       goto jump;
+                       case '!':
+                               printf("\033[2K\r");
+                               fflush(stdout);
+                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                               write(TT.tty_fileno, &cur_str, 1);
+                               if(prev_str != '.')
+                               {
+                                       if((ch=read(TT.tty_fileno, cmd_call, 20)) < 0)
+                                       {
+                                               perror("READ ERROR: ");
+                                               exit(EXIT_FAILURE);
+                                       }
+                                       cmd_call[ch-1] = '\0';
+                               }
+                               else
+                                       fprintf(stdout, "%s\n", cmd_call);
+                                               
+                               system(cmd_call);
+                               puts("---------------------------------");
+                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.buff) < 0)
+                                       fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                               prev_str=cur_str;
+                               rows=1;
+                               break;
+                       case '=':
+                               printf("\033[2K\r");
+                               fflush(stdout);
+                               i=sprintf(arg_str,"%d",row_count);
+                               write(TT.tty_fileno, arg_str, i);
+                               prev_str=cur_str;
+                               memset(arg_str, '\0', sizeof(arg_str));
+                               goto jump;
+                       case '.':
+                               cur_str=prev_str;
+                               new_arg=dot_arg;
+                               prev_str='.';
+                               goto swap;
+                       case ':':
+                               if(prev_str != '.')
+                                       if(read(TT.tty_fileno, &ch, 1) < 0)
+                                       {
+                                               perror("Read Error: ");
+                                               exit(EXIT_FAILURE);
+                                       }
+                               switch(ch)
+                               {
+                                       case 'f':
+                                               printf("\033[2K\r");
+                                               fflush(stdout);
+                                               i=sprintf(arg_str, "\"%s\" line %d", TT.filename, row_count);
+                                               write(TT.tty_fileno, arg_str, i);
+                                               memset(arg_str, '\0', sizeof(arg_str));
+                                               break;
+                                       case 'n':
+                                               fclose(file);
+                                               printf("\033[2K\r");
+                                               dot_arg=new_arg;
+                                               if(new_arg==0)
+                                                       new_arg++;
+                                               while(new_arg > 0)
+                                               {
+                                                       TT.file_count--;
+                                                       if(TT.file_count <= 0)
+                                                       {
+                                                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                                                       perror("TCSETATTR error: ");
+                                                               exit(0);
+                                                       }
+                                                       if(file == stdin && new_arg == 1)
+                                                       {
+                                                               if(strcmp(*toys.optargs, "-") == 0)
+                                                               {
+                                                                       if(toys.optc == 2)
+                                                                       {
+                                                                               printf("\033[2K\r");
+                                                                               if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                               perror("TCSETATTR error: ");
+                                       exit(0);
+                                                                       }
+                                                                       else
+                                                                       {
+                                                                               toys.optargs++;
+                                                                               TT.file_count--;
+                                                                       }
+                                                               }
+                                                       }
+                                                       else if(!(file == stdin && new_arg == 1))
+                                                               toys.optargs++;
+                                                       new_arg--;
+                                               }
+                                               puts("\n...Skipping");
+                                               fprintf(stdout, "...Skipping to file %s\n\n", *toys.optargs);
+                                               break;
+                                       case 'p':
+                                               if(file != stdin)
+                                               {
+                                                       if(ftell(file) == 0)
+                                                               new_arg=((new_arg == 0)?2:new_arg);
+                                                       fclose(file);
+                                                       printf("\033[2K\r");
+                                                       dot_arg=new_arg;
+                                                       puts("\n...Skipping");
+                                                       while(new_arg > 1)
+                                                       {
+                                                               if(TT.file_count == toys.optc || (!in_tty && TT.file_count == toys.optc-1))
+                                                                       break;
+                                                               else
+                                                               {
+                                                                       toys.optargs--;
+                                                                       TT.file_count++;
+                                                                       if(strcmp(*toys.optargs, "-") == 0)
+                                                                       {
+                                                                               if(!in_tty && TT.file_count == toys.optc-1)
+                                                                               {
+                                                                                       toys.optargs++;
+                                                                                       TT.file_count--;
+                                                                               }
+                                                                       }
+                                                               }
+                                                               new_arg--;
+                                                       }
+                                                       fprintf(stdout, "...Skipping back to file %s\n\n", *toys.optargs);
+                                                       break;
+                                               }
+                                               else
+                                                       goto jump;
+                                       default:
+                                               if(toys.optflags & FLAG_d)
+                                               {
+                                                       printf("\033[2K\r");
+                                                       printf("[Press 'h' for help instructions.]");
+                                                       fflush(stdout);
+                                               }
+                                               goto jump;
+                               }
+                               prev_str=cur_str;
+                               if(ch == 'f')
+                                       goto jump;
+                               else
+                                       goto loop;
+                       case '/':
+                       case 'n':
+                               if(file != stdin)
+                               {
+                                       printf("\033[2K\r");
+                                       fflush(stdout);
+                                       if(cur_str == 'n')
+                                       {
+                                               if(strlen(patt_str) == 0)
+                                               {
+                                                       printf("Nothing to search");
+                                                       fflush(stdout);
+                                                       goto jump;
+                                               }
+                                       }
+                                       if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+                                               fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                                       write(TT.tty_fileno, "/", 1);
+                                       if(prev_str != '.' && cur_str != 'n')
+                                       {
+                                               if((ch=read(TT.tty_fileno, patt_str, sizeof(patt_str))) < 0)
+                                                       exit(EXIT_FAILURE);
+                                               patt_str[ch-1]='\0';
+                                       }
+                                       else
+                                               fprintf(stdout, "%s\n", patt_str);
+                                       get_screen_size(&rows, &columns);
+                                       screen_rows=rows;
+                                       if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.buff) < 0)
+                                               fprintf(stderr, "Can't set terminal attributes: %s", strerror(errno));
+                                       if(strlen(patt_str)==0||(ptr_loc=pattern_find(file, patt_str, cur_str, &row_count))==-1)
+                                       {
+                                               printf("Pattern Not Found");
+                                               fflush(stdout);
+                                               prev_str=cur_str;
+                                               goto jump;
+                                       }
+                                       else
+                                       {
+                                               fseek(file, ptr_loc, SEEK_SET);
+                                               prev_str=cur_str;
+                                               break;
+                                       }
+                               }
+                               else
+                                       goto jump;
+                       default:
+                               if(toys.optflags & FLAG_d)
+                               {
+                                       printf("\033[2K\r");
+                                       printf("[Press 'h' for help instructions.]");
+                                       fflush(stdout);
+                               }
+                               goto jump;
+               }
+          if(toys.optflags & FLAG_f)
+                       if(cur_str != 'h' && cur_str != '?')
+                               if((cur_str == '\n' && new_arg != 0) || cur_str != '\n')
+                       printf("\033[2J\033[1;1H");
+       }
+  }
+  if(tcsetattr(TT.tty_fileno, TCSANOW, &TT.term) < 0)
+       perror("TCSETATTR Error - final: ");
+  return;
+}
diff --git a/toys/pending/nbd_client.c b/toys/pending/nbd_client.c
new file mode 100644 (file)
index 0000000..8697230
--- /dev/null
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4:
+ *
+ * nbd-client.c - network block device client
+ *
+ * Copyright 2010 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+
+USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NBD_CLIENT
+  bool "nbd-client"
+  default n
+  help
+    usage: nbd-client [-ns] HOST PORT DEVICE
+
+    -n Do not fork into background
+    -s nbd swap support (lock server into memory)
+*/
+
+/*
+    Usage: nbd-client [-sSpn] [-b BLKSZ] [-t SECS] [-N name] HOST PORT DEVICE
+
+    -b block size
+    -t timeout in seconds
+    -S sdp
+    -p persist
+    -n nofork
+    -d DEVICE
+    -c DEVICE
+*/
+
+#define FOR_nbd_client
+#include "toys.h"
+#include "toynet.h"
+
+#define NBD_SET_SOCK          _IO(0xab, 0)
+#define NBD_SET_BLKSIZE       _IO(0xab, 1)
+#define NBD_SET_SIZE          _IO(0xab, 2)
+#define NBD_DO_IT             _IO(0xab, 3)
+#define NBD_CLEAR_SOCK        _IO(0xab, 4)
+#define NBD_CLEAR_QUEUE       _IO(0xab, 5)
+#define NBD_PRINT_DEBUG       _IO(0xab, 6)
+#define NBD_SET_SIZE_BLOCKS   _IO(0xab, 7)
+#define NBD_DISCONNECT        _IO(0xab, 8)
+#define NBD_SET_TIMEOUT       _IO(0xab, 9)
+
+void nbd_client_main(void)
+{
+  int sock = -1, nbd, flags;
+  unsigned long timeout = 0;
+  struct addrinfo *addr, *p;
+  char *host=toys.optargs[0], *port=toys.optargs[1], *device=toys.optargs[2];
+  uint64_t devsize;
+
+  // Repeat until spanked
+
+  nbd = xopen(device, O_RDWR);
+  for (;;) {
+    int temp;
+    struct addrinfo hints;
+
+    // Find and connect to server
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    if (getaddrinfo(host, port, &hints, &addr)) addr = 0;
+    for (p = addr; p; p = p->ai_next) {
+      sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+      if (-1 != connect(sock, p->ai_addr, p->ai_addrlen)) break;
+      close(sock);
+    }
+    freeaddrinfo(addr);
+
+    if (!p) perror_exit("%s:%s", host, port);
+
+    temp = 1;
+    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int));
+
+    // Read login data
+
+    xreadall(sock, toybuf, 152);
+    if (memcmp(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16))
+      error_exit("bad login %s:%s", host, port);
+    devsize = SWAP_BE64(*(uint64_t *)(toybuf+16));
+    flags = SWAP_BE32(*(int *)(toybuf+24));
+
+    // Set 4k block size.  Everything uses that these days.
+    ioctl(nbd, NBD_SET_BLKSIZE, 4096);
+    ioctl(nbd, NBD_SET_SIZE_BLOCKS, devsize/4096);
+    ioctl(nbd, NBD_CLEAR_SOCK);
+
+    // If the sucker was exported read only, respect that locally.
+    temp = (flags & 2) ? 1 : 0;
+    xioctl(nbd, BLKROSET, &temp);
+
+    if (timeout && ioctl(nbd, NBD_SET_TIMEOUT, timeout)<0) break;
+    if (ioctl(nbd, NBD_SET_SOCK, sock) < 0) break;
+
+    if (toys.optflags & FLAG_s) mlockall(MCL_CURRENT|MCL_FUTURE);
+
+    // Open the device to force reread of the partition table.
+    if ((toys.optflags & FLAG_n) || !fork()) {
+      char *s = strrchr(device, '/');
+      int i;
+
+      sprintf(toybuf, "/sys/block/%.32s/pid", s ? s+1 : device);
+      // Is it up yet? (Give it 10 seconds.)
+      for (i=0; i<100; i++) {
+        temp = open(toybuf, O_RDONLY);
+        if (temp == -1) msleep(100);
+        else {
+          close(temp);
+          break;
+        }
+      }
+      close(open(device, O_RDONLY));
+      if (!(toys.optflags & FLAG_n)) exit(0);
+    }
+
+    // Daemonize here.
+
+    daemon(0,0);
+
+    // Process NBD requests until further notice.
+
+    if (ioctl(nbd, NBD_DO_IT)>=0 || errno==EBADR) break;
+    close(sock);
+  }
+  close(nbd);
+
+  // Flush queue and exit.
+
+  ioctl(nbd, NBD_CLEAR_QUEUE);
+  ioctl(nbd, NBD_CLEAR_SOCK);
+}
diff --git a/toys/pending/reset.c b/toys/pending/reset.c
new file mode 100644 (file)
index 0000000..c7a37db
--- /dev/null
@@ -0,0 +1,35 @@
+/* reset.c - A program to reset the terminal.
+ *
+ * Copyright 2012 VIKASH KUMAR <k.vikash@samsung.com>
+ * USE_RESET(NEWTOY(reset, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+USE_RESET(NEWTOY(reset, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+
+config RESET
+  bool "reset"
+  default y
+  help
+    usage: reset
+
+    A program to reset the terminal.
+*/
+#define FOR_reset
+#include "toys.h"
+
+void reset_main(void)
+{
+  char *args[3] = {"stty", "sane", NULL};
+
+  /*   \033c - reset the terminal with default setting
+   *   \033(B - set the G0 character set (B=US)
+   *   \033[2J - clear the whole screen
+   *   \033[0m - Reset all attributes
+   */
+
+  printf("\033c\033(B\033[0m\033[2J");
+  fflush(stdout);
+  /* set the terminal to sane settings */
+  xexec(args);
+  return;
+}
diff --git a/toys/pending/sed.c b/toys/pending/sed.c
new file mode 100644 (file)
index 0000000..0ce25ac
--- /dev/null
@@ -0,0 +1,138 @@
+/* sed.c - Stream editor.
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/sed.c
+
+USE_SED(NEWTOY(sed, "irne*f*", TOYFLAG_BIN))
+
+config SED
+  bool "sed"
+  default n
+  help
+    usage: sed [-irn] {command | [-e command]...} [FILE...]
+
+    Stream EDitor, transforms text by appling script of command to each line
+    of input.
+
+    -e  Add expression to the command script (if no -e, use first argument)
+    -i Modify file in place
+    -n  No default output (p commands only)
+    -r  Use extended regular expression syntex
+*/
+
+#define FOR_sed
+#include "toys.h"
+#include "lib/xregcomp.h"
+
+GLOBALS(
+  struct arg_list *files;
+  struct arg_list *scripts;
+
+  void *commands;
+)
+
+// Digested version of what sed commands can actually tell use to do.
+
+
+struct sed_command {
+  // double_list compatibility (easier to create in-order)
+  struct sed_command *next, *prev;
+
+  // data string for (saicytb)
+  char c, *data;
+  // Regexes for s/match/data/ and /begin/,/end/command
+  regex_t *rmatch, *rbegin, *rend;
+  // For numeric ranges ala 10,20command
+  long lstart, lstop;
+  // Which match to replace, 0 for all. s and w commands can write to a file
+  int which, outfd;
+};
+
+//  Space. Space. Gotta get past space. Spaaaaaaaace! (But not newline.)
+static void spaceorb(char **s)
+{
+  while (**s == ' ' || **s == '\t') ++*s;
+}
+
+// Parse sed commands
+
+static void parse_scripts(void)
+{
+  struct arg_list *script;
+  int which = 0, i;
+
+  // Loop through list of scripts collated from command line and/or files
+
+  for (script = TT.scripts; script; script = script->next) {
+    char *str = script->arg;
+    struct sed_command *cmd;
+
+    // we can get multiple commands from a string (semicolons and such)
+
+    which++;
+    for (i=1;;) {
+      if (!*str) break;
+
+      cmd = xzalloc(sizeof(struct sed_command));
+
+      // Identify prefix
+      for (;;) {
+        spaceorb(&str);
+        if (*str == '^') {
+          if (cmd->lstart) goto parse_fail;
+          cmd->lstart = -1;
+          str++;
+          continue;
+        } else if (*str == '$') {
+          cmd->lstop = LONG_MAX;
+          str++;
+          break;
+        } else if (isdigit(*str)) {
+          long ll = strtol(str, &str, 10);
+
+          if (ll<0) goto parse_fail;
+          if (cmd->lstart) {
+            cmd->lstop = ll;
+            break;
+          } else cmd->lstart = ll;
+        } else if (*str == '/' || *str == '\\') {
+          // set begin/end
+          printf("regex\n");
+          exit(1);
+        } else if (!cmd->lstart && !cmd->rbegin) break;
+        else goto parse_fail;  // , with no range after it
+
+        spaceorb(&str);
+        if (*str != ',') break;
+        str++;
+      }
+      i = stridx("{bcdDgGhHlnNpPstwxyrqia= \t#:}", *str);
+      if (i == -1) goto parse_fail;
+
+      dlist_add_nomalloc((struct double_list **)&TT.commands,
+                         (struct double_list *)cmd);
+      exit(1);
+    }
+  }
+
+  return;
+
+parse_fail:
+  error_exit("bad expression %d@%d: %s", which, i, script->arg+i);
+}
+
+void sed_main(void)
+{
+  char **files=toys.optargs;
+
+  // If no -e, use first argument
+  if (!TT.scripts) {
+    if (!*files) error_exit("Need script");
+    (TT.scripts = xzalloc(sizeof(struct arg_list)))->arg = *(files++);
+  }
+
+  parse_scripts();
+
+  while (*files) dprintf(2,"file=%s\n", *(files++));
+}
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
new file mode 100644 (file)
index 0000000..2f09f63
--- /dev/null
@@ -0,0 +1,392 @@
+/* sh.c - toybox shell
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * The POSIX-2008/SUSv4 spec for this is at:
+ * http://opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
+ * and http://opengroup.org/onlinepubs/9699919799/utilities/sh.html
+ *
+ * The first link describes the following shell builtins:
+ *
+ *   break colon continue dot eval exec exit export readonly return set shift
+ *   times trap unset
+ *
+ * The second link (the utilities directory) also contains specs for the
+ * following shell builtins:
+ *
+ *   alias bg cd command fc fg getopts hash jobs kill read type ulimit
+ *   umask unalias wait
+ *
+ * Things like the bash man page are good to read too.
+ *
+ * TODO: // Handle embedded NUL bytes in the command line.
+
+USE_SH(NEWTOY(cd, NULL, TOYFLAG_NOFORK))
+USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
+
+USE_SH(NEWTOY(sh, "c:i", TOYFLAG_BIN))
+USE_SH(OLDTOY(toysh, sh, "c:i", TOYFLAG_BIN))
+
+config SH
+  bool "sh (toysh)"
+  default n
+  help
+    usage: sh [-c command] [script]
+
+    Command shell.  Runs a shell script, or reads input interactively
+    and responds to it.
+
+    -c command line to execute
+
+config SH_TTY
+  bool "Interactive shell (terminal control)"
+  default n
+  depends on SH
+  help
+    Add terminal control to toysh.  This is necessary for interactive use,
+    so the shell isn't killed by CTRL-C.
+
+config SH_PROFILE
+  bool "Profile support"
+  default n
+  depends on SH_TTY
+  help
+    Read /etc/profile and ~/.profile when running interactively.
+
+    Also enables the built-in command "source".
+
+config SH_JOBCTL
+  bool "Job Control (fg, bg, jobs)"
+  default n
+  depends on SH_TTY
+  help
+    Add job control to toysh.  This lets toysh handle CTRL-Z, and enables
+    the built-in commands "fg", "bg", and "jobs".
+
+    With pipe support, enable use of "&" to run background processes.
+
+config SH_FLOWCTL
+  bool "Flow control (if, while, for, functions)"
+  default n
+  depends on SH
+  help
+    Add flow control to toysh.  This enables the if/then/else/fi,
+    while/do/done, and for/do/done constructs.
+
+    With pipe support, this enables the ability to define functions
+    using the "function name" or "name()" syntax, plus curly brackets
+    "{ }" to group commands.
+
+config SH_QUOTES
+  bool "Smarter argument parsing (quotes)"
+  default n
+  depends on SH
+  help
+    Add support for parsing "" and '' style quotes to the toysh command
+    parser, with lets arguments have spaces in them.
+
+config SH_WILDCARDS
+  bool "Wildcards ( ?*{,} )"
+  default n
+  depends on SH_QUOTES
+  help
+    Expand wildcards in argument names, ala "ls -l *.t?z" and
+    "rm subdir/{one,two,three}.txt".
+
+config SH_PROCARGS
+  bool "Executable arguments ( `` and $() )"
+  default n
+  depends on SH_QUOTES
+  help
+    Add support for executing arguments contianing $() and ``, using
+    the output of the command as the new argument value(s).
+
+    (Bash calls this "command substitution".)
+
+config SH_ENVVARS
+  bool "Environment variable support"
+  default n
+  depends on SH_QUOTES
+  help
+    Substitute environment variable values for $VARNAME or ${VARNAME},
+    and enable the built-in command "export".
+
+config SH_LOCALS
+  bool "Local variables"
+  default n
+  depends on SH_ENVVARS
+  help
+    Support for local variables, fancy prompts ($PS1), the "set" command,
+    and $?.
+
+config SH_ARRAYS
+  bool "Array variables"
+  default n
+  depends on SH_LOCALS
+  help
+    Support for ${blah[blah]} style array variables.
+
+config SH_PIPES
+  bool "Pipes and redirects ( | > >> < << & && | || () ; )"
+  default n
+  depends on SH
+  help
+    Support multiple commands on the same command line.  This includes
+    | pipes, > >> < redirects, << here documents, || && conditional
+    execution, () subshells, ; sequential execution, and (with job
+    control) & background processes.
+
+config SH_BUILTINS
+  bool "Builtin commands"
+  default n
+  depends on SH
+  help
+    Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set,
+    unset, read, alias.
+
+config EXIT
+  bool
+  default n
+  depends on SH
+  help
+    usage: exit [status]
+
+    Exit shell.  If no return value supplied on command line, use value
+    of most recent command, or 0 if none.
+
+config CD
+  bool
+  default n
+  depends on SH
+  help
+    usage: cd [path]
+
+    Change current directory.  With no arguments, go to $HOME.
+
+config CD_P
+  bool # "-P support for cd"
+  default n
+  depends on SH
+  help
+    usage: cd [-PL]
+
+    -P    Physical path: resolve symlinks in path.
+    -L    Cancel previous -P and restore default behavior.
+*/
+
+#define FOR_sh
+#include "toys.h"
+
+GLOBALS(
+  char *command;
+)
+
+// A single executable, its arguments, and other information we know about it.
+#define SH_FLAG_EXIT    1
+#define SH_FLAG_SUSPEND 2
+#define SH_FLAG_PIPE    4
+#define SH_FLAG_AND     8
+#define SH_FLAG_OR      16
+#define SH_FLAG_AMP     32
+#define SH_FLAG_SEMI    64
+#define SH_FLAG_PAREN   128
+
+// What we know about a single process.
+struct command {
+  struct command *next;
+  int flags;              // exit, suspend, && ||
+  int pid;                // pid (or exit code)
+  int argc;
+  char *argv[0];
+};
+
+// A collection of processes piped into/waiting on each other.
+struct pipeline {
+  struct pipeline *next;
+  int job_id;
+  struct command *cmd;
+  char *cmdline;         // Unparsed line for display purposes
+  int cmdlinelen;        // How long is cmdline?
+};
+
+// Parse one word from the command line, appending one or more argv[] entries
+// to struct command.  Handles environment variable substitution and
+// substrings.  Returns pointer to next used byte, or NULL if it
+// hit an ending token.
+static char *parse_word(char *start, struct command **cmd)
+{
+  char *end;
+
+  // Detect end of line (and truncate line at comment)
+  if (CFG_SH_PIPES && strchr("><&|(;", *start)) return 0;
+
+  // Grab next word.  (Add dequote and envvar logic here)
+  end = start;
+  while (*end && !isspace(*end)) end++;
+  (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
+
+  // Allocate more space if there's no room for NULL terminator.
+
+  if (!((*cmd)->argc & 7))
+    *cmd=xrealloc(*cmd,
+        sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
+  (*cmd)->argv[(*cmd)->argc] = 0;
+  return end;
+}
+
+// Parse a line of text into a pipeline.
+// Returns a pointer to the next line.
+
+static char *parse_pipeline(char *cmdline, struct pipeline *line)
+{
+  struct command **cmd = &(line->cmd);
+  char *start = line->cmdline = cmdline;
+
+  if (!cmdline) return 0;
+
+  if (CFG_SH_JOBCTL) line->cmdline = cmdline;
+
+  // Parse command into argv[]
+  for (;;) {
+    char *end;
+
+    // Skip leading whitespace and detect end of line.
+    while (isspace(*start)) start++;
+    if (!*start || *start=='#') {
+      if (CFG_SH_JOBCTL) line->cmdlinelen = start-cmdline;
+      return 0;
+    }
+
+    // Allocate next command structure if necessary
+    if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
+
+    // Parse next argument and add the results to argv[]
+    end = parse_word(start, cmd);
+
+    // If we hit the end of this command, how did it end?
+    if (!end) {
+      if (CFG_SH_PIPES && *start) {
+        if (*start==';') {
+          start++;
+          break;
+        }
+        // handle | & < > >> << || &&
+      }
+      break;
+    }
+    start = end;
+  }
+
+  if (CFG_SH_JOBCTL) line->cmdlinelen = start-cmdline;
+
+  return start;
+}
+
+// Execute the commands in a pipeline
+static void run_pipeline(struct pipeline *line)
+{
+  struct toy_list *tl;
+  struct command *cmd = line->cmd;
+  if (!cmd || !cmd->argc) return;
+
+  tl = toy_find(cmd->argv[0]);
+  // Is this command a builtin that should run in this process?
+  if (tl && (tl->flags & TOYFLAG_NOFORK)) {
+    struct toy_context temp;
+    jmp_buf rebound;
+
+    // This fakes lots of what toybox_main() does.
+    memcpy(&temp, &toys, sizeof(struct toy_context));
+    memset(&toys, 0, sizeof(struct toy_context));
+
+    if (!setjmp(rebound)) {
+      toys.rebound = rebound;
+      toy_init(tl, cmd->argv);
+      tl->toy_main();
+    }
+    cmd->pid = toys.exitval;
+    if (toys.optargs != toys.argv+1) free(toys.optargs);
+    if (toys.old_umask) umask(toys.old_umask);
+    memcpy(&toys, &temp, sizeof(struct toy_context));
+  } else {
+    int status;
+
+    cmd->pid = vfork();
+    if (!cmd->pid) xexec(cmd->argv);
+    else waitpid(cmd->pid, &status, 0);
+
+    if (CFG_SH_FLOWCTL || CFG_SH_PIPES) {
+      if (WIFEXITED(status)) cmd->pid = WEXITSTATUS(status);
+      if (WIFSIGNALED(status)) cmd->pid = WTERMSIG(status);
+    }
+  }
+
+  return;
+}
+
+// Free the contents of a command structure
+static void free_cmd(void *data)
+{
+  struct command *cmd=(struct command *)data;
+
+  while(cmd->argc) free(cmd->argv[--cmd->argc]);
+}
+
+
+// Parse a command line and do what it says to do.
+static void handle(char *command)
+{
+  struct pipeline line;
+  char *start = command;
+
+  // Loop through commands in this line
+
+  for (;;) {
+
+    // Parse a group of connected commands
+
+    memset(&line,0,sizeof(struct pipeline));
+    start = parse_pipeline(start, &line);
+    if (!line.cmd) break;
+
+    // Run those commands
+
+    run_pipeline(&line);
+    llist_traverse(line.cmd, free_cmd);
+  }
+}
+
+void cd_main(void)
+{
+  char *dest = *toys.optargs ? *toys.optargs : getenv("HOME");
+  xchdir(dest);
+}
+
+void exit_main(void)
+{
+  exit(*toys.optargs ? atoi(*toys.optargs) : 0);
+}
+
+void sh_main(void)
+{
+  FILE *f;
+
+  // Set up signal handlers and grab control of this tty.
+  if (CFG_SH_TTY) {
+    if (isatty(0)) toys.optflags |= 1;
+  }
+  f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL;
+  if (TT.command) handle(TT.command);
+  else {
+    size_t cmdlen = 0;
+    for (;;) {
+      char *command = 0;
+      if (!f) xputc('$');
+      if (1 > getline(&command, &cmdlen, f ? f : stdin)) break;
+      handle(command);
+      free(command);
+    }
+  }
+
+  toys.exitval = 1;
+}
diff --git a/toys/pending/top.c_1 b/toys/pending/top.c_1
new file mode 100644 (file)
index 0000000..4ebd7d1
--- /dev/null
@@ -0,0 +1,740 @@
+/* top.c - A top world program.
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * Not in SUSv4.
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/
+
+USE_TOP(NEWTOY(top, "m#n#d#s:th", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TOP
+  bool "top"
+  default n
+  help
+    usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ]
+
+    -m num  Maximum number of processes to display.
+    -n num  Updates to show before exiting.
+    -d num  Seconds to wait between updates.
+    -s col  Column to sort by (cpu,vss,rss,thr).
+    -t      Show threads instead of processes.
+*/
+
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the 
+ *  distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *  may be used to endorse or promote products derived from this
+ *  software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define FOR_top
+#include "toys.h"
+#include <unistd.h>
+#include <termios.h>
+#include <poll.h>
+
+GLOBALS(
+  char *s;
+  long d;
+  long n;
+  long m;
+)
+
+struct cpu_info {
+  long unsigned utime, ntime, stime, itime;
+  long unsigned iowtime, irqtime, sirqtime;
+};
+
+#define PROC_NAME_LEN 64
+#define THREAD_NAME_LEN 32
+
+struct proc_info {
+  struct proc_info *next;
+  pid_t pid;
+  pid_t ppid;
+  pid_t tid;
+  uid_t uid;
+  gid_t gid;
+  char name[PROC_NAME_LEN];
+  char tname[THREAD_NAME_LEN];
+  char state;
+  long unsigned utime;
+  long unsigned stime;
+  long unsigned delta_utime;
+  long unsigned delta_stime;
+  long unsigned delta_time;
+  long vss;
+  long rss;
+  int prs;
+  int num_threads;
+  char policy[32];
+};
+
+struct proc_list {
+  struct proc_info **array;
+  int size;
+};
+
+#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
+
+#define INIT_PROCS 50
+#define THREAD_MULT 8
+static struct proc_info **old_procs, **new_procs;
+static int num_old_procs, num_new_procs;
+static struct proc_info *free_procs;
+static int num_used_procs, num_free_procs;
+
+static int max_procs, delay, iterations, threads;
+
+static struct cpu_info old_cpu, new_cpu;
+
+static struct proc_info *alloc_proc(void);
+static void free_proc(struct proc_info *proc);
+static void read_procs(void);
+static int read_stat(char *filename, struct proc_info *proc);
+static void read_policy(int pid, struct proc_info *proc);
+static void add_proc(int proc_num, struct proc_info *proc);
+static int read_cmdline(char *filename, struct proc_info *proc);
+static int read_status(char *filename, struct proc_info *proc);
+static void print_procs(void);
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
+static void free_old_procs(void);
+static int (*proc_cmp)(const void *a, const void *b);
+static int proc_cpu_cmp(const void *a, const void *b);
+static int proc_vss_cmp(const void *a, const void *b);
+static int proc_rss_cmp(const void *a, const void *b);
+static int proc_thr_cmp(const void *a, const void *b);
+static int numcmp(long long a, long long b);
+static void usage(char *cmd);
+static unsigned int read_input(int delay);
+
+#if 0
+int top_main1(int argc, char *argv[]) 
+{
+  printf("The count = %d, %s ....... %s\n", toys.optc, toys.argv[0], toys.argv[1]);
+  top_main1(toys.optc+2, toys.argv);
+}
+#endif
+
+void* catch_signal1(int signum)
+{
+  printf("\n\n\n got signal \n\n\n");
+  exit(1);
+}
+
+void top_main(void )
+{
+  int i;
+
+  num_used_procs = num_free_procs = 0;
+
+  max_procs = 0;
+  delay = 3;
+  iterations = -1;
+  proc_cmp = &proc_cpu_cmp;
+
+
+  if(toys.optflags & FLAG_m)
+      max_procs = TT.m;
+  if(toys.optflags & FLAG_n)
+      iterations = TT.n;
+  if(toys.optflags & FLAG_d)
+      delay = TT.d;
+  if(toys.optflags & FLAG_t)
+      threads = 1;
+  if(toys.optargs[0])
+  {
+      fprintf(stderr, "Invalid argument \"%s\".\n", toys.optargs[0]);
+      usage(toys.argv[0]);
+      exit(EXIT_FAILURE);
+  }
+
+#if 0
+  for (i = 1; i < argc; i++) {
+      if (!strcmp(argv[i], "-m")) {
+          if (i + 1 >= argc) {
+              fprintf(stderr, "Option -m expects an argument.\n");
+              usage(argv[0]);
+              exit(EXIT_FAILURE);
+          }
+          max_procs = atoi(argv[++i]);
+          continue;
+      }
+      if (!strcmp(argv[i], "-n")) {
+          if (i + 1 >= argc) {
+              fprintf(stderr, "Option -n expects an argument.\n");
+              usage(argv[0]);
+              exit(EXIT_FAILURE);
+          }
+          iterations = atoi(argv[++i]);
+          continue;
+      }
+      if (!strcmp(argv[i], "-d")) {
+          if (i + 1 >= argc) {
+              fprintf(stderr, "Option -d expects an argument.\n");
+              usage(argv[0]);
+              exit(EXIT_FAILURE);
+          }
+          delay = atoi(argv[++i]);
+          continue;
+      }
+      if (!strcmp(argv[i], "-s")) {
+          if (i + 1 >= argc) {
+              fprintf(stderr, "Option -s expects an argument.\n");
+              usage(argv[0]);
+              exit(EXIT_FAILURE);
+          }
+          ++i;
+          if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
+          if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
+          if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
+          if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
+          fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
+          exit(EXIT_FAILURE);
+      }
+      if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+      if (!strcmp(argv[i], "-h")) {
+          usage(argv[0]);
+          exit(EXIT_SUCCESS);
+      }
+      fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+      usage(argv[0]);
+      exit(EXIT_FAILURE);
+  }
+#endif
+
+  if (threads && proc_cmp == &proc_thr_cmp) {
+      fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
+      exit(EXIT_FAILURE);
+  }
+
+  free_procs = NULL;
+
+  num_new_procs = num_old_procs = 0;
+  new_procs = old_procs = NULL;
+
+  sigatexit(catch_signal1);
+  read_procs();
+  while ((iterations == -1) || (iterations-- > 0)) {
+      old_procs = new_procs;
+      num_old_procs = num_new_procs;
+      memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
+//      sleep(delay);
+      read_procs();
+      print_procs();
+      free_old_procs();
+      if(read_input(delay))
+          break;
+  }
+
+//  return 0;
+}
+
+static unsigned int read_input(int delay)
+{
+  struct pollfd pfd[1];
+  int ret, fret = 0;
+  int timeout = delay * 1000; //to convert it to msec.
+  char ch;
+  struct termios inf, newf;
+  pfd[0].fd = 0;
+  pfd[0].events = POLLIN;
+
+  /*prepare terminal for input, without Enter of Carriage return */
+  tcgetattr(0, &inf);
+  memcpy(&newf, &inf, sizeof(struct termios));
+  newf.c_lflag &= ~(ICANON | ECHO | ECHONL);
+  tcsetattr(0, TCSANOW, &newf);
+
+  while(1)
+  {
+      if((ret = poll(pfd, 1, timeout)) >= 0) {
+          xprintf("Poll got an value\n");
+          break;
+      }
+      else
+      {
+          if(timeout > 0)
+              timeout--;
+          if(errno == EINTR) {
+              printf("signal aaya re .... signal aaya\n\n\n\n");
+              continue;
+          }
+          xprintf("Poll got an error\n");
+          fret = 0;
+      }
+  }
+  if(ret <= 0)
+  {
+      fret = 0;
+  }
+  else
+  {
+      if(read(STDIN_FILENO, &ch, 1) != 1)
+      {
+          fret = 0;
+      }
+      else if(ch == 'q')
+          fret = 1;
+
+  }
+  tcsetattr(0, TCSANOW, &inf);
+  return fret;
+}
+
+static struct proc_info *alloc_proc(void) {
+  struct proc_info *proc;
+
+  if (free_procs) {
+      proc = free_procs;
+      free_procs = free_procs->next;
+      num_free_procs--;
+  } else {
+      proc = malloc(sizeof(*proc));
+      if (!proc) die("Could not allocate struct process_info.\n");
+  }
+
+  num_used_procs++;
+
+  return proc;
+}
+
+static void free_proc(struct proc_info *proc) {
+  proc->next = free_procs;
+  free_procs = proc;
+
+  num_used_procs--;
+  num_free_procs++;
+}
+
+#define MAX_LINE 256
+
+static void read_procs(void) {
+  DIR *proc_dir, *task_dir;
+  struct dirent *pid_dir, *tid_dir;
+  char filename[64];
+  FILE *file;
+  int proc_num;
+  struct proc_info *proc;
+  pid_t pid, tid;
+
+  int i;
+
+  proc_dir = opendir("/proc");
+  if (!proc_dir) die("Could not open /proc.\n");
+
+  new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
+  num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
+
+  file = fopen("/proc/stat", "r");
+  if (!file) die("Could not open /proc/stat.\n");
+  fscanf(file, "cpu  %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
+          &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
+  fclose(file);
+
+  proc_num = 0;
+  while ((pid_dir = readdir(proc_dir))) {
+      if (!isdigit(pid_dir->d_name[0]))
+          continue;
+
+      pid = atoi(pid_dir->d_name);
+      
+      struct proc_info cur_proc;
+      
+      if (!threads) {
+          proc = alloc_proc();
+
+          proc->pid = proc->tid = pid;
+
+          sprintf(filename, "/proc/%d/stat", pid);
+          read_stat(filename, proc);
+
+          sprintf(filename, "/proc/%d/cmdline", pid);
+          read_cmdline(filename, proc);
+
+          sprintf(filename, "/proc/%d/status", pid);
+          read_status(filename, proc);
+
+          read_policy(pid, proc);
+
+          proc->num_threads = 0;
+      } else {
+          sprintf(filename, "/proc/%d/cmdline", pid);
+          read_cmdline(filename, &cur_proc);
+
+          sprintf(filename, "/proc/%d/status", pid);
+          read_status(filename, &cur_proc);
+          
+          proc = NULL;
+      }
+
+      sprintf(filename, "/proc/%d/task", pid);
+      task_dir = opendir(filename);
+      if (!task_dir) continue;
+
+      while ((tid_dir = readdir(task_dir))) {
+          if (!isdigit(tid_dir->d_name[0]))
+              continue;
+
+          if (threads) {
+              tid = atoi(tid_dir->d_name);
+
+              proc = alloc_proc();
+
+              proc->pid = pid; proc->tid = tid;
+
+              sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+              read_stat(filename, proc);
+
+              read_policy(tid, proc);
+
+              strcpy(proc->name, cur_proc.name);
+              proc->uid = cur_proc.uid;
+              proc->gid = cur_proc.gid;
+
+              add_proc(proc_num++, proc);
+          } else {
+              proc->num_threads++;
+          }
+      }
+
+      closedir(task_dir);
+      
+      if (!threads)
+          add_proc(proc_num++, proc);
+  }
+
+  for (i = proc_num; i < num_new_procs; i++)
+      new_procs[i] = NULL;
+
+  closedir(proc_dir);
+}
+
+static int read_stat(char *filename, struct proc_info *proc) {
+  FILE *file;
+  char buf[MAX_LINE], *open_paren, *close_paren;
+  int res, idx;
+
+  file = fopen(filename, "r");
+  if (!file) return 1;
+  fgets(buf, MAX_LINE, file);
+  fclose(file);
+
+  /* Split at first '(' and last ')' to get process name. */
+  open_paren = strchr(buf, '(');
+  close_paren = strrchr(buf, ')');
+  if (!open_paren || !close_paren) return 1;
+
+  *open_paren = *close_paren = '\0';
+  strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
+  proc->tname[THREAD_NAME_LEN-1] = 0;
+  
+  /* Scan rest of string. */
+  sscanf(close_paren + 1, " %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+               "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld "
+               "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d",
+               &proc->state, &proc->ppid, &proc->utime, &proc->stime, &proc->vss, &proc->rss, &proc->prs);
+
+  return 0;
+}
+
+static void add_proc(int proc_num, struct proc_info *proc) {
+  int i;
+
+  if (proc_num >= num_new_procs) {
+      new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
+      if (!new_procs) die("Could not expand procs array.\n");
+      for (i = num_new_procs; i < 2 * num_new_procs; i++)
+          new_procs[i] = NULL;
+      num_new_procs = 2 * num_new_procs;
+  }
+  new_procs[proc_num] = proc;
+}
+
+static int read_cmdline(char *filename, struct proc_info *proc) {
+  FILE *file;
+  char line[MAX_LINE];
+
+  line[0] = '\0';
+  file = fopen(filename, "r");
+  if (!file) return 1;
+  fgets(line, MAX_LINE, file);
+  fclose(file);
+  if (strlen(line) > 0) {
+  //    printf("%s\n", line);
+      strncpy(proc->name, line, PROC_NAME_LEN);
+      proc->name[PROC_NAME_LEN-1] = 0;
+  } else
+      proc->name[0] = 0;
+  return 0;
+}
+
+static void read_policy(int pid, struct proc_info *proc) {
+          strcpy(proc->policy, "fg");
+#if 0
+  SchedPolicy p;
+  if (get_sched_policy(pid, &p) < 0)
+      strcpy(proc->policy, "unk");
+  else {
+      if (p == SP_BACKGROUND)
+          strcpy(proc->policy, "bg");
+      else if (p == SP_FOREGROUND)
+          strcpy(proc->policy, "fg");
+      else
+          strcpy(proc->policy, "er");
+  }
+#endif
+}
+
+static int read_status(char *filename, struct proc_info *proc) {
+  FILE *file;
+  char line[MAX_LINE];
+  unsigned int uid, gid;
+
+  file = fopen(filename, "r");
+  if (!file) return 1;
+  while (fgets(line, MAX_LINE, file)) {
+      sscanf(line, "Uid: %u", &uid);
+      sscanf(line, "Gid: %u", &gid);
+  }
+  fclose(file);
+  proc->uid = uid; proc->gid = gid;
+  return 0;
+}
+static unsigned long long convert(unsigned long d, unsigned int iscale,
+                      unsigned int oscale)
+{
+      return ((unsigned long long)d*iscale)>>oscale;
+}
+
+static char* show_percent(int num, int den)
+{
+  div_t result;
+  char *quot, *rem;
+  result = div((num * 100), den);
+  quot = xmsprintf("%s",itoa(result.quot));
+  rem = xmsprintf("%s", itoa(result.rem));
+  return xmsprintf("%s.%c%%",quot, *rem);
+  printf("%d.%d%%", result.quot, '0' + result.rem);
+}
+
+static void print_procs(void) {
+  int i;
+  struct proc_info *old_proc, *proc;
+  long unsigned total_delta_time;
+  struct passwd *user;
+  struct group *group;
+  char *user_str, user_buf[20];
+  char *group_str, group_buf[20];
+  struct sysinfo info;
+  unsigned int oscale = 0, iscale = 1;
+  unsigned int cols=0, rows =0;
+
+  /*determine terminal size */
+  terminal_size(&cols, &rows);
+  if(rows == 0)
+      rows = 40; //40 rows by default.
+  /*Clear and display again*/
+//  printf("\033[H\033[J");
+  rows--;
+
+  for (i = 0; i < num_new_procs; i++) {
+      if (new_procs[i]) {
+          old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
+          if (old_proc) {
+              new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
+              new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
+          } else {
+              new_procs[i]->delta_utime = 0;
+              new_procs[i]->delta_stime = 0;
+          }
+          new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
+      }
+  }
+
+  total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
+                      + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
+                   - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
+                      + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
+
+  qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
+
+  /*Memory details */
+  sysinfo(&info);
+  if (info.mem_unit) iscale = info.mem_unit;
+  oscale = 10;
+  xprintf("Mem: %lluK used, %lluK free, %lluK shared, %lluK buff\n",
+          convert(info.totalram-info.freeram, iscale, oscale),
+          convert(info.freeram, iscale, oscale),
+          convert(info.sharedram, iscale, oscale),
+          convert(info.bufferram, iscale, oscale));
+  rows--;
+//  printf("\n\n\n");
+  printf("CPU: User %s System %s, IOW %s",
+          show_percent((new_cpu.utime - old_cpu.utime), total_delta_time),
+          show_percent(((new_cpu.stime ) - (old_cpu.stime)), total_delta_time),
+          show_percent((new_cpu.iowtime - old_cpu.iowtime), total_delta_time));
+  printf(" Nice %s Idle %s IRQ %s SIRQ %s\n",
+          show_percent(new_cpu.ntime - old_cpu.ntime, total_delta_time),
+          show_percent(new_cpu.itime - old_cpu.itime, total_delta_time),
+          show_percent(new_cpu.irqtime - old_cpu.irqtime, total_delta_time),
+          show_percent(new_cpu.sirqtime - old_cpu.sirqtime, total_delta_time));
+  xprintf("Load average: %0.2f %0.2f %0.2f\n",(float)(info.loads[0]/((float)(1 << SI_LOAD_SHIFT))),
+          (float)(info.loads[1]/((float)(1 << SI_LOAD_SHIFT))),
+          (float)(info.loads[2]/((float)(1 << SI_LOAD_SHIFT))));
+  printf("\n");
+  rows -= 3;
+#if 0 //Ashwini
+  if (!threads) 
+      printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name");
+  else
+      printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc");
+#endif
+  if (!threads) 
+      printf("%5s %5s %-8s %1s %5s %7s %7s %5s %s\n", "PID", "PPID", "UID", "S", "#THR", "VSS", "RSS", "CPU%", "COMMAND");
+  else
+      printf("%5s %5s %2s %-8s %1s %7s %7s %5s %-15s %s\n", "PID", "TID", "PR", "UID", "S", "VSS", "RSS", "CPU%", "Thread", "Proc");
+
+  rows--;
+
+  for (i = 0; i < num_new_procs; i++) {
+      proc = new_procs[i];
+
+      if (!proc || (max_procs && (i >= max_procs)))
+          break;
+      user  = getpwuid(proc->uid);
+      group = getgrgid(proc->gid);
+      if (user && user->pw_name) {
+          user_str = user->pw_name;
+      } else {
+          snprintf(user_buf, 20, "%d", proc->uid);
+          user_str = user_buf;
+      }
+      if (group && group->gr_name) {
+          group_str = group->gr_name;
+      } else {
+          snprintf(group_buf, 20, "%d", proc->gid);
+          group_str = group_buf;
+      }
+      if (!threads)
+      {
+          printf("%5d %5d %-8.8s %c %5d %6ldK %6ldK %5s %s\n", proc->pid, proc->ppid, user_str, proc->state, proc->num_threads, 
+                  proc->vss / 1024, proc->rss * getpagesize() / 1024, show_percent(proc->delta_time,  total_delta_time), proc->name[0] != 0 ? proc->name : proc->tname);
+      }
+      else
+          printf("%5d %5d %2d %-8.8s %c %6ldK %6ldK %5s %-15s %s\n", proc->pid, proc->tid, proc->prs, user_str, proc->state,
+                  proc->vss / 1024, proc->rss * getpagesize() / 1024, show_percent(proc->delta_time, total_delta_time), proc->tname, proc->name);
+      rows--;
+      if(rows == 0)
+          break; //don't print any more process details.
+  }
+}
+
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
+  int i;
+
+  for (i = 0; i < num_old_procs; i++)
+      if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
+          return old_procs[i];
+
+  return NULL;
+}
+
+static void free_old_procs(void) {
+  int i;
+
+  for (i = 0; i < num_old_procs; i++)
+      if (old_procs[i])
+          free_proc(old_procs[i]);
+
+  free(old_procs);
+}
+
+static int proc_cpu_cmp(const void *a, const void *b) {
+  struct proc_info *pa, *pb;
+
+  pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+  if (!pa && !pb) return 0;
+  if (!pa) return 1;
+  if (!pb) return -1;
+
+  return -numcmp(pa->delta_time, pb->delta_time);
+}
+
+static int proc_vss_cmp(const void *a, const void *b) {
+  struct proc_info *pa, *pb;
+
+  pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+  if (!pa && !pb) return 0;
+  if (!pa) return 1;
+  if (!pb) return -1;
+
+  return -numcmp(pa->vss, pb->vss);
+}
+
+static int proc_rss_cmp(const void *a, const void *b) {
+  struct proc_info *pa, *pb;
+
+  pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+  if (!pa && !pb) return 0;
+  if (!pa) return 1;
+  if (!pb) return -1;
+
+  return -numcmp(pa->rss, pb->rss);
+}
+
+static int proc_thr_cmp(const void *a, const void *b) {
+  struct proc_info *pa, *pb;
+
+  pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+  if (!pa && !pb) return 0;
+  if (!pa) return 1;
+  if (!pb) return -1;
+
+  return -numcmp(pa->num_threads, pb->num_threads);
+}
+
+static int numcmp(long long a, long long b) {
+  if (a < b) return -1;
+  if (a > b) return 1;
+  return 0;
+}
+
+static void usage(char *cmd) {
+  fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n"
+                  "    -m num  Maximum number of processes to display.\n"
+                  "    -n num  Updates to show before exiting.\n"
+                  "    -d num  Seconds to wait between updates.\n"
+                  "    -s col  Column to sort by (cpu,vss,rss,thr).\n"
+                  "    -t      Show threads instead of processes.\n"
+                  "    -h      Display this help screen.\n",
+      cmd);
+}
diff --git a/toys/pending/xzcat.c b/toys/pending/xzcat.c
new file mode 100644 (file)
index 0000000..5d69087
--- /dev/null
@@ -0,0 +1,3342 @@
+/*
+ * Simple XZ decoder command line tool
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ * Modified for toybox by Isaac Dunham
+USE_XZCAT(NEWTOY(xzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config XZCAT
+  bool "xzcat"
+  default n
+  help
+    usage: xzcat < file.xz
+    
+    Read xz-compressed file from stdin and write decompressed file to stdout.
+
+*/
+#define FOR_xzcat
+#include "toys.h"
+
+// BEGIN xz.h
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE:              Single-call mode. This uses less RAM than
+ *                          than multi-call modes, because the LZMA2
+ *                          dictionary doesn't need to be allocated as
+ *                          part of the decoder state. All required data
+ *                          structures are allocated at initialization,
+ *                          so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC:            Multi-call mode with preallocated LZMA2
+ *                          dictionary buffer. All data structures are
+ *                          allocated at initialization, so xz_dec_run()
+ *                          cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC:            Multi-call mode. The LZMA2 dictionary is
+ *                          allocated once the required size has been
+ *                          parsed from the stream headers. If the
+ *                          allocation fails, xz_dec_run() will return
+ *                          XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. xzcat uses only XZ_DEC_DYNALLOC, 
+ * so that is the default.
+ */
+enum xz_mode {
+  XZ_SINGLE,
+  XZ_PREALLOC,
+  XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK:                  Everything is OK so far. More input or more
+ *                          output space is required to continue. This
+ *                          return code is possible only in multi-call mode
+ *                          (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END:          Operation finished successfully.
+ * @XZ_UNSUPPORTED_CHECK:   Integrity check type is not supported. Decoding
+ *                          is still possible in multi-call mode by simply
+ *                          calling xz_dec_run() again.
+ *                          Note that this return value is used only if
+ *                          XZ_DEC_ANY_CHECK was defined at build time,
+ *                          which is not used in the kernel. Unsupported
+ *                          check types return XZ_OPTIONS_ERROR if
+ *                          XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR:           Allocating memory failed. This return code is
+ *                          possible only if the decoder was initialized
+ *                          with XZ_DYNALLOC. The amount of memory that was
+ *                          tried to be allocated was no more than the
+ *                          dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR:      A bigger LZMA2 dictionary would be needed than
+ *                          allowed by the dict_max argument given to
+ *                          xz_dec_init(). This return value is possible
+ *                          only in multi-call mode (XZ_PREALLOC or
+ *                          XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ *                          ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR:        File format was not recognized (wrong magic
+ *                          bytes).
+ * @XZ_OPTIONS_ERROR:       This implementation doesn't support the requested
+ *                          compression options. In the decoder this means
+ *                          that the header CRC32 matches, but the header
+ *                          itself specifies something that we don't support.
+ * @XZ_DATA_ERROR:          Compressed data is corrupt.
+ * @XZ_BUF_ERROR:           Cannot make any progress. Details are slightly
+ *                          different between multi-call and single-call
+ *                          mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * stream that is truncated or otherwise corrupt.
+ *
+ * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
+ * is too small or the compressed input is corrupt in a way that makes the
+ * decoder produce more output than the caller expected. When it is
+ * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
+ * is used instead of XZ_BUF_ERROR.
+ */
+enum xz_ret {
+  XZ_OK,
+  XZ_STREAM_END,
+  XZ_UNSUPPORTED_CHECK,
+  XZ_MEM_ERROR,
+  XZ_MEMLIMIT_ERROR,
+  XZ_FORMAT_ERROR,
+  XZ_OPTIONS_ERROR,
+  XZ_DATA_ERROR,
+  XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in:         Beginning of the input buffer. This may be NULL if and only
+ *              if in_pos is equal to in_size.
+ * @in_pos:     Current position in the input buffer. This must not exceed
+ *              in_size.
+ * @in_size:    Size of the input buffer
+ * @out:        Beginning of the output buffer. This may be NULL if and only
+ *              if out_pos is equal to out_size.
+ * @out_pos:    Current position in the output buffer. This must not exceed
+ *              out_size.
+ * @out_size:   Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf {
+  const uint8_t *in;
+  size_t in_pos;
+  size_t in_size;
+
+  uint8_t *out;
+  size_t out_pos;
+  size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode:       Operation mode
+ * @dict_max:   Maximum size of the LZMA2 dictionary (history buffer) for
+ *              multi-call decoding. This is ignored in single-call mode
+ *              (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ *              or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ *              in practice), so other values for dict_max don't make sense.
+ *              In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ *              512 KiB, and 1 MiB are probably the only reasonable values,
+ *              except for kernel and initramfs images where a bigger
+ *              dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s:          Decoder state allocated using xz_dec_init()
+ * @b:          Input and output buffers
+ *
+ * The possible return values depend on build options and operation mode.
+ * See enum xz_ret for details.
+ *
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
+ * contents of the output buffer from b->out[b->out_pos] onward are
+ * undefined. This is true even after XZ_BUF_ERROR, because with some filter
+ * chains, there may be a second pass over the output buffer, and this pass
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s:          Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+void xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s:          Decoder state allocated using xz_dec_init(). If s is NULL,
+ *              this function does nothing.
+ */
+void xz_dec_end(struct xz_dec *s);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+static uint32_t xz_crc32_table[256];
+
+uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+{
+  crc = ~crc;
+
+  while (size != 0) {
+    crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+    --size;
+  }
+
+  return ~crc;
+}
+
+/*
+ * This must be called before any other xz_* function (but after crc_init())
+ * to initialize the CRC64 lookup table.
+ */
+static uint64_t xz_crc64_table[256];
+
+void xz_crc64_init(void)
+{
+  const uint64_t poly = 0xC96C5795D7870F42ULL;
+
+  uint32_t i;
+  uint32_t j;
+  uint64_t r;
+
+  for (i = 0; i < 256; ++i) {
+    r = i;
+    for (j = 0; j < 8; ++j)
+      r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+    xz_crc64_table[i] = r;
+  }
+
+  return;
+}
+
+/*
+ * Update CRC64 value using the polynomial from ECMA-182. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc)
+{
+  crc = ~crc;
+
+  while (size != 0) {
+    crc = xz_crc64_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+    --size;
+  }
+
+  return ~crc;
+}
+
+// END xz.h
+
+static uint8_t in[BUFSIZ];
+static uint8_t out[BUFSIZ];
+
+void xzcat_main(void)
+{
+  struct xz_buf b;
+  struct xz_dec *s;
+  enum xz_ret ret;
+  const char *msg;
+
+  crc_init(xz_crc32_table, 1);
+  xz_crc64_init();
+
+  /*
+   * Support up to 64 MiB dictionary. The actually needed memory
+   * is allocated once the headers have been parsed.
+   */
+  s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+  if (s == NULL) {
+    msg = "Memory allocation failed\n";
+    goto error;
+  }
+
+  b.in = in;
+  b.in_pos = 0;
+  b.in_size = 0;
+  b.out = out;
+  b.out_pos = 0;
+  b.out_size = BUFSIZ;
+
+  for (;;) {
+    if (b.in_pos == b.in_size) {
+      b.in_size = fread(in, 1, sizeof(in), stdin);
+      b.in_pos = 0;
+    }
+
+    ret = xz_dec_run(s, &b);
+
+    if (b.out_pos == sizeof(out)) {
+      if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+        msg = "Write error\n";
+        goto error;
+      }
+
+      b.out_pos = 0;
+    }
+
+    if (ret == XZ_OK)
+      continue;
+
+    if (ret == XZ_UNSUPPORTED_CHECK)
+      continue;
+
+    if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+        || fclose(stdout)) {
+      msg = "Write error\n";
+      goto error;
+    }
+
+    switch (ret) {
+    case XZ_STREAM_END:
+      xz_dec_end(s);
+      return;
+
+    case XZ_MEM_ERROR:
+      msg = "Memory allocation failed\n";
+      goto error;
+
+    case XZ_MEMLIMIT_ERROR:
+      msg = "Memory usage limit reached\n";
+      goto error;
+
+    case XZ_FORMAT_ERROR:
+      msg = "Not a .xz file\n";
+      goto error;
+
+    case XZ_OPTIONS_ERROR:
+      msg = "Unsupported options in the .xz headers\n";
+      goto error;
+
+    case XZ_DATA_ERROR:
+    case XZ_BUF_ERROR:
+      msg = "File is corrupt\n";
+      goto error;
+
+    default:
+      msg = "Bug!\n";
+      goto error;
+    }
+  }
+
+error:
+  xz_dec_end(s);
+  error_exit("%s", msg);
+}
+
+// BEGIN xz_private.h
+
+
+/* Uncomment as needed to enable BCJ filter decoders. 
+ * These cost about 2.5 k when all are enabled; SPARC and IA64 make 0.7 k
+ * */
+
+#define XZ_DEC_X86
+#define XZ_DEC_POWERPC
+#define XZ_DEC_IA64
+#define XZ_DEC_ARM
+#define XZ_DEC_ARMTHUMB
+#define XZ_DEC_SPARC
+
+
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+
+#ifndef min
+#      define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#define min_t(type, x, y) min(x, y)
+
+
+/* Inline functions to access unaligned unsigned 32-bit integers */
+#ifndef get_unaligned_le32
+static inline uint32_t get_unaligned_le32(const uint8_t *buf)
+{
+  return (uint32_t)buf[0]
+      | ((uint32_t)buf[1] << 8)
+      | ((uint32_t)buf[2] << 16)
+      | ((uint32_t)buf[3] << 24);
+}
+#endif
+
+#ifndef get_unaligned_be32
+static inline uint32_t get_unaligned_be32(const uint8_t *buf)
+{
+  return (uint32_t)(buf[0] << 24)
+      | ((uint32_t)buf[1] << 16)
+      | ((uint32_t)buf[2] << 8)
+      | (uint32_t)buf[3];
+}
+#endif
+
+#ifndef put_unaligned_le32
+static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
+{
+  buf[0] = (uint8_t)val;
+  buf[1] = (uint8_t)(val >> 8);
+  buf[2] = (uint8_t)(val >> 16);
+  buf[3] = (uint8_t)(val >> 24);
+}
+#endif
+
+#ifndef put_unaligned_be32
+static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
+{
+  buf[0] = (uint8_t)(val >> 24);
+  buf[1] = (uint8_t)(val >> 16);
+  buf[2] = (uint8_t)(val >> 8);
+  buf[3] = (uint8_t)val;
+}
+#endif
+
+/*
+ * Use get_unaligned_le32() also for aligned access for simplicity. On
+ * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
+ * could save a few bytes in code size.
+ */
+#ifndef get_le32
+#      define get_le32 get_unaligned_le32
+#endif
+
+
+#define XZ_DEC_DYNALLOC
+/* DYNALLOC is what we use, but we may want these later.
+#define XZ_DEC_SINGLE
+#define XZ_DEC_PREALLOC
+*/
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+#      define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+#      define DEC_IS_SINGLE(mode) (0)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+#      define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+#      define DEC_IS_PREALLOC(mode) (0)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+#      define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+#      define DEC_IS_DYNALLOC(mode) (0)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+#      define DEC_IS_MULTI(mode) (1)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+#      define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+#      define DEC_IS_MULTI(mode) (0)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+#      if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
+      || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+      || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+      || defined(XZ_DEC_SPARC)
+#              define XZ_DEC_BCJ
+#      endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+               uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
+           uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+               struct xz_buf *b);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+struct xz_dec_bcj *xz_dec_bcj_create(int single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+             struct xz_dec_lzma2 *lzma2,
+             struct xz_buf *b);
+#endif
+
+// END "xz_private.h"
+
+
+
+
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ * The rest of the code is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj {
+  /* Type of the BCJ filter being used */
+  enum {
+    BCJ_X86 = 4,        /* x86 or x86-64 */
+    BCJ_POWERPC = 5,    /* Big endian only */
+    BCJ_IA64 = 6,       /* Big or little endian */
+    BCJ_ARM = 7,        /* Little endian only */
+    BCJ_ARMTHUMB = 8,   /* Little endian only */
+    BCJ_SPARC = 9       /* Big or little endian */
+  } type;
+
+  /*
+   * Return value of the next filter in the chain. We need to preserve
+   * this information across calls, because we must not call the next
+   * filter anymore once it has returned XZ_STREAM_END.
+   */
+  enum xz_ret ret;
+
+  /* True if we are operating in single-call mode. */
+  int single_call;
+
+  /*
+   * Absolute position relative to the beginning of the uncompressed
+   * data (in a single .xz Block). We care only about the lowest 32
+   * bits so this doesn't need to be uint64_t even with big files.
+   */
+  uint32_t pos;
+
+  /* x86 filter state */
+  uint32_t x86_prev_mask;
+
+  /* Temporary space to hold the variables from struct xz_buf */
+  uint8_t *out;
+  size_t out_pos;
+  size_t out_size;
+
+  struct {
+    /* Amount of already filtered data in the beginning of buf */
+    size_t filtered;
+
+    /* Total amount of data currently stored in buf  */
+    size_t size;
+
+    /*
+     * Buffer to hold a mix of filtered and unfiltered data. This
+     * needs to be big enough to hold Alignment + 2 * Look-ahead:
+     *
+     * Type         Alignment   Look-ahead
+     * x86              1           4
+     * PowerPC          4           0
+     * IA-64           16           0
+     * ARM              4           0
+     * ARM-Thumb        2           2
+     * SPARC            4           0
+     */
+    uint8_t buf[16];
+  } temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is used to test the most significant byte of a memory address
+ * in an x86 instruction.
+ */
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+  return b == 0x00 || b == 0xFF;
+}
+
+static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+  static const int mask_to_allowed_status[8]
+    = { 1,1,1,0,1,0,0,0 };
+
+  static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+  size_t i;
+  size_t prev_pos = (size_t)-1;
+  uint32_t prev_mask = s->x86_prev_mask;
+  uint32_t src;
+  uint32_t dest;
+  uint32_t j;
+  uint8_t b;
+
+  if (size <= 4)
+    return 0;
+
+  size -= 4;
+  for (i = 0; i < size; ++i) {
+    if ((buf[i] & 0xFE) != 0xE8)
+      continue;
+
+    prev_pos = i - prev_pos;
+    if (prev_pos > 3) {
+      prev_mask = 0;
+    } else {
+      prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+      if (prev_mask != 0) {
+        b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+        if (!mask_to_allowed_status[prev_mask]
+            || bcj_x86_test_msbyte(b)) {
+          prev_pos = i;
+          prev_mask = (prev_mask << 1) | 1;
+          continue;
+        }
+      }
+    }
+
+    prev_pos = i;
+
+    if (bcj_x86_test_msbyte(buf[i + 4])) {
+      src = get_unaligned_le32(buf + i + 1);
+      for (;;) {
+        dest = src - (s->pos + (uint32_t)i + 5);
+        if (prev_mask == 0)
+          break;
+
+        j = mask_to_bit_num[prev_mask] * 8;
+        b = (uint8_t)(dest >> (24 - j));
+        if (!bcj_x86_test_msbyte(b))
+          break;
+
+        src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+      }
+
+      dest &= 0x01FFFFFF;
+      dest |= (uint32_t)0 - (dest & 0x01000000);
+      put_unaligned_le32(dest, buf + i + 1);
+      i += 4;
+    } else {
+      prev_mask = (prev_mask << 1) | 1;
+    }
+  }
+
+  prev_pos = i - prev_pos;
+  s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+  return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+  size_t i;
+  uint32_t instr;
+
+  for (i = 0; i + 4 <= size; i += 4) {
+    instr = get_unaligned_be32(buf + i);
+    if ((instr & 0xFC000003) == 0x48000001) {
+      instr &= 0x03FFFFFC;
+      instr -= s->pos + (uint32_t)i;
+      instr &= 0x03FFFFFC;
+      instr |= 0x48000001;
+      put_unaligned_be32(instr, buf + i);
+    }
+  }
+
+  return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+  static const uint8_t branch_table[32] = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    4, 4, 6, 6, 0, 0, 7, 7,
+    4, 4, 0, 0, 4, 4, 0, 0
+  };
+
+  /*
+   * The local variables take a little bit stack space, but it's less
+   * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+   * stack usage here without doing that for the LZMA2 decoder too.
+   */
+
+  /* Loop counters */
+  size_t i;
+  size_t j;
+
+  /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+  uint32_t slot;
+
+  /* Bitwise offset of the instruction indicated by slot */
+  uint32_t bit_pos;
+
+  /* bit_pos split into byte and bit parts */
+  uint32_t byte_pos;
+  uint32_t bit_res;
+
+  /* Address part of an instruction */
+  uint32_t addr;
+
+  /* Mask used to detect which instructions to convert */
+  uint32_t mask;
+
+  /* 41-bit instruction stored somewhere in the lowest 48 bits */
+  uint64_t instr;
+
+  /* Instruction normalized with bit_res for easier manipulation */
+  uint64_t norm;
+
+  for (i = 0; i + 16 <= size; i += 16) {
+    mask = branch_table[buf[i] & 0x1F];
+    for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
+      if (((mask >> slot) & 1) == 0)
+        continue;
+
+      byte_pos = bit_pos >> 3;
+      bit_res = bit_pos & 7;
+      instr = 0;
+      for (j = 0; j < 6; ++j)
+        instr |= (uint64_t)(buf[i + j + byte_pos])
+            << (8 * j);
+
+      norm = instr >> bit_res;
+
+      if (((norm >> 37) & 0x0F) == 0x05
+          && ((norm >> 9) & 0x07) == 0) {
+        addr = (norm >> 13) & 0x0FFFFF;
+        addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+        addr <<= 4;
+        addr -= s->pos + (uint32_t)i;
+        addr >>= 4;
+
+        norm &= ~((uint64_t)0x8FFFFF << 13);
+        norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+        norm |= (uint64_t)(addr & 0x100000)
+            << (36 - 20);
+
+        instr &= (1 << bit_res) - 1;
+        instr |= norm << bit_res;
+
+        for (j = 0; j < 6; j++)
+          buf[i + j + byte_pos]
+            = (uint8_t)(instr >> (8 * j));
+      }
+    }
+  }
+
+  return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+  size_t i;
+  uint32_t addr;
+
+  for (i = 0; i + 4 <= size; i += 4) {
+    if (buf[i + 3] == 0xEB) {
+      addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+          | ((uint32_t)buf[i + 2] << 16);
+      addr <<= 2;
+      addr -= s->pos + (uint32_t)i + 8;
+      addr >>= 2;
+      buf[i] = (uint8_t)addr;
+      buf[i + 1] = (uint8_t)(addr >> 8);
+      buf[i + 2] = (uint8_t)(addr >> 16);
+    }
+  }
+
+  return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+  size_t i;
+  uint32_t addr;
+
+  for (i = 0; i + 4 <= size; i += 2) {
+    if ((buf[i + 1] & 0xF8) == 0xF0
+        && (buf[i + 3] & 0xF8) == 0xF8) {
+      addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
+          | ((uint32_t)buf[i] << 11)
+          | (((uint32_t)buf[i + 3] & 0x07) << 8)
+          | (uint32_t)buf[i + 2];
+      addr <<= 1;
+      addr -= s->pos + (uint32_t)i + 4;
+      addr >>= 1;
+      buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+      buf[i] = (uint8_t)(addr >> 11);
+      buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+      buf[i + 2] = (uint8_t)addr;
+      i += 2;
+    }
+  }
+
+  return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+  size_t i;
+  uint32_t instr;
+
+  for (i = 0; i + 4 <= size; i += 4) {
+    instr = get_unaligned_be32(buf + i);
+    if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+      instr <<= 2;
+      instr -= s->pos + (uint32_t)i;
+      instr >>= 2;
+      instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+          | 0x40000000 | (instr & 0x3FFFFF);
+      put_unaligned_be32(instr, buf + i);
+    }
+  }
+
+  return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void bcj_apply(struct xz_dec_bcj *s,
+          uint8_t *buf, size_t *pos, size_t size)
+{
+  size_t filtered;
+
+  buf += *pos;
+  size -= *pos;
+
+  switch (s->type) {
+#ifdef XZ_DEC_X86
+  case BCJ_X86:
+    filtered = bcj_x86(s, buf, size);
+    break;
+#endif
+#ifdef XZ_DEC_POWERPC
+  case BCJ_POWERPC:
+    filtered = bcj_powerpc(s, buf, size);
+    break;
+#endif
+#ifdef XZ_DEC_IA64
+  case BCJ_IA64:
+    filtered = bcj_ia64(s, buf, size);
+    break;
+#endif
+#ifdef XZ_DEC_ARM
+  case BCJ_ARM:
+    filtered = bcj_arm(s, buf, size);
+    break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+  case BCJ_ARMTHUMB:
+    filtered = bcj_armthumb(s, buf, size);
+    break;
+#endif
+#ifdef XZ_DEC_SPARC
+  case BCJ_SPARC:
+    filtered = bcj_sparc(s, buf, size);
+    break;
+#endif
+  default:
+    /* Never reached but silence compiler warnings. */
+    filtered = 0;
+    break;
+  }
+
+  *pos += filtered;
+  s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+  size_t copy_size;
+
+  copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+  memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+  b->out_pos += copy_size;
+
+  s->temp.filtered -= copy_size;
+  s->temp.size -= copy_size;
+  memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+             struct xz_dec_lzma2 *lzma2,
+             struct xz_buf *b)
+{
+  size_t out_start;
+
+  /*
+   * Flush pending already filtered data to the output buffer. Return
+   * immediatelly if we couldn't flush everything, or if the next
+   * filter in the chain had already returned XZ_STREAM_END.
+   */
+  if (s->temp.filtered > 0) {
+    bcj_flush(s, b);
+    if (s->temp.filtered > 0)
+      return XZ_OK;
+
+    if (s->ret == XZ_STREAM_END)
+      return XZ_STREAM_END;
+  }
+
+  /*
+   * If we have more output space than what is currently pending in
+   * temp, copy the unfiltered data from temp to the output buffer
+   * and try to fill the output buffer by decoding more data from the
+   * next filter in the chain. Apply the BCJ filter on the new data
+   * in the output buffer. If everything cannot be filtered, copy it
+   * to temp and rewind the output buffer position accordingly.
+   *
+   * This needs to be always run when temp.size == 0 to handle a special
+   * case where the output buffer is full and the next filter has no
+   * more output coming but hasn't returned XZ_STREAM_END yet.
+   */
+  if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
+    out_start = b->out_pos;
+    memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+    b->out_pos += s->temp.size;
+
+    s->ret = xz_dec_lzma2_run(lzma2, b);
+    if (s->ret != XZ_STREAM_END
+        && (s->ret != XZ_OK || s->single_call))
+      return s->ret;
+
+    bcj_apply(s, b->out, &out_start, b->out_pos);
+
+    /*
+     * As an exception, if the next filter returned XZ_STREAM_END,
+     * we can do that too, since the last few bytes that remain
+     * unfiltered are meant to remain unfiltered.
+     */
+    if (s->ret == XZ_STREAM_END)
+      return XZ_STREAM_END;
+
+    s->temp.size = b->out_pos - out_start;
+    b->out_pos -= s->temp.size;
+    memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+
+    /*
+     * If there wasn't enough input to the next filter to fill
+     * the output buffer with unfiltered data, there's no point
+     * to try decoding more data to temp.
+     */
+    if (b->out_pos + s->temp.size < b->out_size)
+      return XZ_OK;
+  }
+
+  /*
+   * We have unfiltered data in temp. If the output buffer isn't full
+   * yet, try to fill the temp buffer by decoding more data from the
+   * next filter. Apply the BCJ filter on temp. Then we hopefully can
+   * fill the actual output buffer by copying filtered data from temp.
+   * A mix of filtered and unfiltered data may be left in temp; it will
+   * be taken care on the next call to this function.
+   */
+  if (b->out_pos < b->out_size) {
+    /* Make b->out{,_pos,_size} temporarily point to s->temp. */
+    s->out = b->out;
+    s->out_pos = b->out_pos;
+    s->out_size = b->out_size;
+    b->out = s->temp.buf;
+    b->out_pos = s->temp.size;
+    b->out_size = sizeof(s->temp.buf);
+
+    s->ret = xz_dec_lzma2_run(lzma2, b);
+
+    s->temp.size = b->out_pos;
+    b->out = s->out;
+    b->out_pos = s->out_pos;
+    b->out_size = s->out_size;
+
+    if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+      return s->ret;
+
+    bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+    /*
+     * If the next filter returned XZ_STREAM_END, we mark that
+     * everything is filtered, since the last unfiltered bytes
+     * of the stream are meant to be left as is.
+     */
+    if (s->ret == XZ_STREAM_END)
+      s->temp.filtered = s->temp.size;
+
+    bcj_flush(s, b);
+    if (s->temp.filtered > 0)
+      return XZ_OK;
+  }
+
+  return s->ret;
+}
+
+struct xz_dec_bcj *xz_dec_bcj_create(int single_call)
+{
+  struct xz_dec_bcj *s = malloc(sizeof(*s));
+  if (s != NULL)
+    s->single_call = single_call;
+
+  return s;
+}
+
+enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+{
+  switch (id) {
+#ifdef XZ_DEC_X86
+  case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+  case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+  case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+  case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+  case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+  case BCJ_SPARC:
+#endif
+    break;
+
+  default:
+    /* Unsupported Filter ID */
+    return XZ_OPTIONS_ERROR;
+  }
+
+  s->type = id;
+  s->ret = XZ_OK;
+  s->pos = 0;
+  s->x86_prev_mask = 0;
+  s->temp.filtered = 0;
+  s->temp.size = 0;
+
+  return XZ_OK;
+}
+
+#endif
+/*
+ * LZMA2 decoder
+ */
+
+
+// BEGIN xz_lzma2.h
+/*
+ * LZMA2 definitions
+ *
+ */
+
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ *  - Literal: One 8-bit byte
+ *  - Match: Repeat a chunk of data at some distance
+ *  - Long repeat: Multi-byte match at a recently seen distance
+ *  - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state {
+  STATE_LIT_LIT,
+  STATE_MATCH_LIT_LIT,
+  STATE_REP_LIT_LIT,
+  STATE_SHORTREP_LIT_LIT,
+  STATE_MATCH_LIT,
+  STATE_REP_LIT,
+  STATE_SHORTREP_LIT,
+  STATE_LIT_MATCH,
+  STATE_LIT_LONGREP,
+  STATE_LIT_SHORTREP,
+  STATE_NONLIT_MATCH,
+  STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void lzma_state_literal(enum lzma_state *state)
+{
+  if (*state <= STATE_SHORTREP_LIT_LIT)
+    *state = STATE_LIT_LIT;
+  else if (*state <= STATE_LIT_SHORTREP)
+    *state -= 3;
+  else
+    *state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void lzma_state_match(enum lzma_state *state)
+{
+  *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void lzma_state_long_rep(enum lzma_state *state)
+{
+  *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void lzma_state_short_rep(enum lzma_state *state)
+{
+  *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline int lzma_state_is_literal(enum lzma_state state)
+{
+  return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ *   - 0x001-0x0FF: Without match byte
+ *   - 0x101-0x1FF: With match byte; match bit is 0
+ *   - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length   Bits
+ *  2-9      4 = Choice=0 + 3 bits
+ * 10-17     5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273   10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t lzma_get_dist_state(uint32_t len)
+{
+  return len < DIST_STATES + MATCH_LEN_MIN
+      ? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ *   - distance slot: the highest two bits
+ *   - direct bits: 2-26 bits below the highest two bits
+ *   - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+
+// END xz_lzma2.h
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ *    start <= pos <= full <= end
+ *    pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ *    end == size
+ *    size <= size_max
+ *    allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary {
+  /* Beginning of the history buffer */
+  uint8_t *buf;
+
+  /* Old position in buf (before decoding more data) */
+  size_t start;
+
+  /* Position in buf */
+  size_t pos;
+
+  /*
+   * How full dictionary is. This is used to detect corrupt input that
+   * would read beyond the beginning of the uncompressed stream.
+   */
+  size_t full;
+
+  /* Write limit; we don't write to buf[limit] or later bytes. */
+  size_t limit;
+
+  /*
+   * End of the dictionary buffer. In multi-call mode, this is
+   * the same as the dictionary size. In single-call mode, this
+   * indicates the size of the output buffer.
+   */
+  size_t end;
+
+  /*
+   * Size of the dictionary as specified in Block Header. This is used
+   * together with "full" to detect corrupt input that would make us
+   * read beyond the beginning of the uncompressed stream.
+   */
+  uint32_t size;
+
+  /*
+   * Maximum allowed dictionary size in multi-call mode.
+   * This is ignored in single-call mode.
+   */
+  uint32_t size_max;
+
+  /*
+   * Amount of memory currently allocated for the dictionary.
+   * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+   * size_max is always the same as the allocated size.)
+   */
+  uint32_t allocated;
+
+  /* Operation mode */
+  enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec {
+  uint32_t range;
+  uint32_t code;
+
+  /*
+   * Number of initializing bytes remaining to be read
+   * by rc_read_init().
+   */
+  uint32_t init_bytes_left;
+
+  /*
+   * Buffer from which we read our input. It can be either
+   * temp.buf or the caller-provided input buffer.
+   */
+  const uint8_t *in;
+  size_t in_pos;
+  size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec {
+  /* Probability of match length being at least 10 */
+  uint16_t choice;
+
+  /* Probability of match length being at least 18 */
+  uint16_t choice2;
+
+  /* Probabilities for match lengths 2-9 */
+  uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+  /* Probabilities for match lengths 10-17 */
+  uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+  /* Probabilities for match lengths 18-273 */
+  uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec {
+  /* Distances of latest four matches */
+  uint32_t rep0;
+  uint32_t rep1;
+  uint32_t rep2;
+  uint32_t rep3;
+
+  /* Types of the most recently seen LZMA symbols */
+  enum lzma_state state;
+
+  /*
+   * Length of a match. This is updated so that dict_repeat can
+   * be called again to finish repeating the whole match.
+   */
+  uint32_t len;
+
+  /*
+   * LZMA properties or related bit masks (number of literal
+   * context bits, a mask dervied from the number of literal
+   * position bits, and a mask dervied from the number
+   * position bits)
+   */
+  uint32_t lc;
+  uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+  uint32_t pos_mask;         /* (1 << pb) - 1 */
+
+  /* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+  uint16_t is_match[STATES][POS_STATES_MAX];
+
+  /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+  uint16_t is_rep[STATES];
+
+  /*
+   * If 0, distance of a repeated match is rep0.
+   * Otherwise check is_rep1.
+   */
+  uint16_t is_rep0[STATES];
+
+  /*
+   * If 0, distance of a repeated match is rep1.
+   * Otherwise check is_rep2.
+   */
+  uint16_t is_rep1[STATES];
+
+  /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+  uint16_t is_rep2[STATES];
+
+  /*
+   * If 1, the repeated match has length of one byte. Otherwise
+   * the length is decoded from rep_len_decoder.
+   */
+  uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+  /*
+   * Probability tree for the highest two bits of the match
+   * distance. There is a separate probability tree for match
+   * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+   */
+  uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+  /*
+   * Probility trees for additional bits for match distance
+   * when the distance is in the range [4, 127].
+   */
+  uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+  /*
+   * Probability tree for the lowest four bits of a match
+   * distance that is equal to or greater than 128.
+   */
+  uint16_t dist_align[ALIGN_SIZE];
+
+  /* Length of a normal match */
+  struct lzma_len_dec match_len_dec;
+
+  /* Length of a repeated match */
+  struct lzma_len_dec rep_len_dec;
+
+  /* Probabilities of literals */
+  uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec {
+  /* Position in xz_dec_lzma2_run(). */
+  enum lzma2_seq {
+    SEQ_CONTROL,
+    SEQ_UNCOMPRESSED_1,
+    SEQ_UNCOMPRESSED_2,
+    SEQ_COMPRESSED_0,
+    SEQ_COMPRESSED_1,
+    SEQ_PROPERTIES,
+    SEQ_LZMA_PREPARE,
+    SEQ_LZMA_RUN,
+    SEQ_COPY
+  } sequence;
+
+  /* Next position after decoding the compressed size of the chunk. */
+  enum lzma2_seq next_sequence;
+
+  /* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+  uint32_t uncompressed;
+
+  /*
+   * Compressed size of LZMA chunk or compressed/uncompressed
+   * size of uncompressed chunk (64 KiB at maximum)
+   */
+  uint32_t compressed;
+
+  /*
+   * True if dictionary reset is needed. This is false before
+   * the first chunk (LZMA or uncompressed).
+   */
+  int need_dict_reset;
+
+  /*
+   * True if new LZMA properties are needed. This is false
+   * before the first LZMA chunk.
+   */
+  int need_props;
+};
+
+struct xz_dec_lzma2 {
+  /*
+   * The order below is important on x86 to reduce code size and
+   * it shouldn't hurt on other platforms. Everything up to and
+   * including lzma.pos_mask are in the first 128 bytes on x86-32,
+   * which allows using smaller instructions to access those
+   * variables. On x86-64, fewer variables fit into the first 128
+   * bytes, but this is still the best order without sacrificing
+   * the readability by splitting the structures.
+   */
+  struct rc_dec rc;
+  struct dictionary dict;
+  struct lzma2_dec lzma2;
+  struct lzma_dec lzma;
+
+  /*
+   * Temporary buffer which holds small number of input bytes between
+   * decoder calls. See lzma2_lzma() for details.
+   */
+  struct {
+    uint32_t size;
+    uint8_t buf[3 * LZMA_IN_REQUIRED];
+  } temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+  if (DEC_IS_SINGLE(dict->mode)) {
+    dict->buf = b->out + b->out_pos;
+    dict->end = b->out_size - b->out_pos;
+  }
+
+  dict->start = 0;
+  dict->pos = 0;
+  dict->limit = 0;
+  dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void dict_limit(struct dictionary *dict, size_t out_max)
+{
+  if (dict->end - dict->pos <= out_max)
+    dict->limit = dict->end;
+  else
+    dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static inline int dict_has_space(const struct dictionary *dict)
+{
+  return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)
+{
+  size_t offset = dict->pos - dist - 1;
+
+  if (dist >= dict->pos)
+    offset += dict->end;
+
+  return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void dict_put(struct dictionary *dict, uint8_t byte)
+{
+  dict->buf[dict->pos++] = byte;
+
+  if (dict->full < dict->pos)
+    dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static int dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+  size_t back;
+  uint32_t left;
+
+  if (dist >= dict->full || dist >= dict->size) return 0;
+
+  left = min_t(size_t, dict->limit - dict->pos, *len);
+  *len -= left;
+
+  back = dict->pos - dist - 1;
+  if (dist >= dict->pos)
+    back += dict->end;
+
+  do {
+    dict->buf[dict->pos++] = dict->buf[back++];
+    if (back == dict->end)
+      back = 0;
+  } while (--left > 0);
+
+  if (dict->full < dict->pos)
+    dict->full = dict->pos;
+
+  return 1;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b,
+            uint32_t *left)
+{
+  size_t copy_size;
+
+  while (*left > 0 && b->in_pos < b->in_size
+      && b->out_pos < b->out_size) {
+    copy_size = min(b->in_size - b->in_pos,
+        b->out_size - b->out_pos);
+    if (copy_size > dict->end - dict->pos)
+      copy_size = dict->end - dict->pos;
+    if (copy_size > *left)
+      copy_size = *left;
+
+    *left -= copy_size;
+
+    memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+    dict->pos += copy_size;
+
+    if (dict->full < dict->pos)
+      dict->full = dict->pos;
+
+    if (DEC_IS_MULTI(dict->mode)) {
+      if (dict->pos == dict->end)
+        dict->pos = 0;
+
+      memcpy(b->out + b->out_pos, b->in + b->in_pos,
+          copy_size);
+    }
+
+    dict->start = dict->pos;
+
+    b->out_pos += copy_size;
+    b->in_pos += copy_size;
+  }
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+  size_t copy_size = dict->pos - dict->start;
+
+  if (DEC_IS_MULTI(dict->mode)) {
+    if (dict->pos == dict->end)
+      dict->pos = 0;
+
+    memcpy(b->out + b->out_pos, dict->buf + dict->start,
+        copy_size);
+  }
+
+  dict->start = dict->pos;
+  b->out_pos += copy_size;
+  return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void rc_reset(struct rc_dec *rc)
+{
+  rc->range = (uint32_t)-1;
+  rc->code = 0;
+  rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static int rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+  while (rc->init_bytes_left > 0) {
+    if (b->in_pos == b->in_size) return 0;
+
+    rc->code = (rc->code << 8) + b->in[b->in_pos++];
+    --rc->init_bytes_left;
+  }
+
+  return 1;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline int rc_limit_exceeded(const struct rc_dec *rc)
+{
+  return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline int rc_is_finished(const struct rc_dec *rc)
+{
+  return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static inline void rc_normalize(struct rc_dec *rc)
+{
+  if (rc->range < RC_TOP_VALUE) {
+    rc->range <<= RC_SHIFT_BITS;
+    rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+  }
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static inline int rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+  uint32_t bound;
+  int bit;
+
+  rc_normalize(rc);
+  bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+  if (rc->code < bound) {
+    rc->range = bound;
+    *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+    bit = 0;
+  } else {
+    rc->range -= bound;
+    rc->code -= bound;
+    *prob -= *prob >> RC_MOVE_BITS;
+    bit = 1;
+  }
+
+  return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static inline uint32_t rc_bittree(struct rc_dec *rc,
+             uint16_t *probs, uint32_t limit)
+{
+  uint32_t symbol = 1;
+
+  do {
+    if (rc_bit(rc, &probs[symbol]))
+      symbol = (symbol << 1) + 1;
+    else
+      symbol <<= 1;
+  } while (symbol < limit);
+
+  return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static inline void rc_bittree_reverse(struct rc_dec *rc,
+                 uint16_t *probs,
+                 uint32_t *dest, uint32_t limit)
+{
+  uint32_t symbol = 1;
+  uint32_t i = 0;
+
+  do {
+    if (rc_bit(rc, &probs[symbol])) {
+      symbol = (symbol << 1) + 1;
+      *dest += 1 << i;
+    } else {
+      symbol <<= 1;
+    }
+  } while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+  uint32_t mask;
+
+  do {
+    rc_normalize(rc);
+    rc->range >>= 1;
+    rc->code -= rc->range;
+    mask = (uint32_t)0 - (rc->code >> 31);
+    rc->code += rc->range & mask;
+    *dest = (*dest << 1) + (mask + 1);
+  } while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+  uint32_t prev_byte = dict_get(&s->dict, 0);
+  uint32_t low = prev_byte >> (8 - s->lzma.lc);
+  uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+  return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void lzma_literal(struct xz_dec_lzma2 *s)
+{
+  uint16_t *probs;
+  uint32_t symbol;
+  uint32_t match_byte;
+  uint32_t match_bit;
+  uint32_t offset;
+  uint32_t i;
+
+  probs = lzma_literal_probs(s);
+
+  if (lzma_state_is_literal(s->lzma.state)) {
+    symbol = rc_bittree(&s->rc, probs, 0x100);
+  } else {
+    symbol = 1;
+    match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+    offset = 0x100;
+
+    do {
+      match_bit = match_byte & offset;
+      match_byte <<= 1;
+      i = offset + match_bit + symbol;
+
+      if (rc_bit(&s->rc, &probs[i])) {
+        symbol = (symbol << 1) + 1;
+        offset &= match_bit;
+      } else {
+        symbol <<= 1;
+        offset &= ~match_bit;
+      }
+    } while (symbol < 0x100);
+  }
+
+  dict_put(&s->dict, (uint8_t)symbol);
+  lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,
+         uint32_t pos_state)
+{
+  uint16_t *probs;
+  uint32_t limit;
+
+  if (!rc_bit(&s->rc, &l->choice)) {
+    probs = l->low[pos_state];
+    limit = LEN_LOW_SYMBOLS;
+    s->lzma.len = MATCH_LEN_MIN;
+  } else {
+    if (!rc_bit(&s->rc, &l->choice2)) {
+      probs = l->mid[pos_state];
+      limit = LEN_MID_SYMBOLS;
+      s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+    } else {
+      probs = l->high;
+      limit = LEN_HIGH_SYMBOLS;
+      s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS
+          + LEN_MID_SYMBOLS;
+    }
+  }
+
+  s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+  uint16_t *probs;
+  uint32_t dist_slot;
+  uint32_t limit;
+
+  lzma_state_match(&s->lzma.state);
+
+  s->lzma.rep3 = s->lzma.rep2;
+  s->lzma.rep2 = s->lzma.rep1;
+  s->lzma.rep1 = s->lzma.rep0;
+
+  lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+  probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+  dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+  if (dist_slot < DIST_MODEL_START) {
+    s->lzma.rep0 = dist_slot;
+  } else {
+    limit = (dist_slot >> 1) - 1;
+    s->lzma.rep0 = 2 + (dist_slot & 1);
+
+    if (dist_slot < DIST_MODEL_END) {
+      s->lzma.rep0 <<= limit;
+      probs = s->lzma.dist_special + s->lzma.rep0
+          - dist_slot - 1;
+      rc_bittree_reverse(&s->rc, probs,
+          &s->lzma.rep0, limit);
+    } else {
+      rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+      s->lzma.rep0 <<= ALIGN_BITS;
+      rc_bittree_reverse(&s->rc, s->lzma.dist_align,
+          &s->lzma.rep0, ALIGN_BITS);
+    }
+  }
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+  uint32_t tmp;
+
+  if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {
+    if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[
+        s->lzma.state][pos_state])) {
+      lzma_state_short_rep(&s->lzma.state);
+      s->lzma.len = 1;
+      return;
+    }
+  } else {
+    if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {
+      tmp = s->lzma.rep1;
+    } else {
+      if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {
+        tmp = s->lzma.rep2;
+      } else {
+        tmp = s->lzma.rep3;
+        s->lzma.rep3 = s->lzma.rep2;
+      }
+
+      s->lzma.rep2 = s->lzma.rep1;
+    }
+
+    s->lzma.rep1 = s->lzma.rep0;
+    s->lzma.rep0 = tmp;
+  }
+
+  lzma_state_long_rep(&s->lzma.state);
+  lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static int lzma_main(struct xz_dec_lzma2 *s)
+{
+  uint32_t pos_state;
+
+  /*
+   * If the dictionary was reached during the previous call, try to
+   * finish the possibly pending repeat in the dictionary.
+   */
+  if (dict_has_space(&s->dict) && s->lzma.len > 0)
+    dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+  /*
+   * Decode more LZMA symbols. One iteration may consume up to
+   * LZMA_IN_REQUIRED - 1 bytes.
+   */
+  while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {
+    pos_state = s->dict.pos & s->lzma.pos_mask;
+
+    if (!rc_bit(&s->rc, &s->lzma.is_match[
+        s->lzma.state][pos_state])) {
+      lzma_literal(s);
+    } else {
+      if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+        lzma_rep_match(s, pos_state);
+      else
+        lzma_match(s, pos_state);
+
+      if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+        return 0;
+    }
+  }
+
+  /*
+   * Having the range decoder always normalized when we are outside
+   * this function makes it easier to correctly handle end of the chunk.
+   */
+  rc_normalize(&s->rc);
+
+  return 1;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void lzma_reset(struct xz_dec_lzma2 *s)
+{
+  uint16_t *probs;
+  size_t i;
+
+  s->lzma.state = STATE_LIT_LIT;
+  s->lzma.rep0 = 0;
+  s->lzma.rep1 = 0;
+  s->lzma.rep2 = 0;
+  s->lzma.rep3 = 0;
+
+  /*
+   * All probabilities are initialized to the same value. This hack
+   * makes the code smaller by avoiding a separate loop for each
+   * probability array.
+   *
+   * This could be optimized so that only that part of literal
+   * probabilities that are actually required. In the common case
+   * we would write 12 KiB less.
+   */
+  probs = s->lzma.is_match[0];
+  for (i = 0; i < PROBS_TOTAL; ++i)
+    probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+  rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static int lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+  if (props > (4 * 5 + 4) * 9 + 8)
+    return 0;
+
+  s->lzma.pos_mask = 0;
+  while (props >= 9 * 5) {
+    props -= 9 * 5;
+    ++s->lzma.pos_mask;
+  }
+
+  s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+  s->lzma.literal_pos_mask = 0;
+  while (props >= 9) {
+    props -= 9;
+    ++s->lzma.literal_pos_mask;
+  }
+
+  s->lzma.lc = props;
+
+  if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+    return 0;
+
+  s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+  lzma_reset(s);
+
+  return 1;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static int lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+  size_t in_avail;
+  uint32_t tmp;
+
+  in_avail = b->in_size - b->in_pos;
+  if (s->temp.size > 0 || s->lzma2.compressed == 0) {
+    tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+    if (tmp > s->lzma2.compressed - s->temp.size)
+      tmp = s->lzma2.compressed - s->temp.size;
+    if (tmp > in_avail)
+      tmp = in_avail;
+
+    memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+    if (s->temp.size + tmp == s->lzma2.compressed) {
+      memset(s->temp.buf + s->temp.size + tmp, 0,
+          sizeof(s->temp.buf)
+            - s->temp.size - tmp);
+      s->rc.in_limit = s->temp.size + tmp;
+    } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {
+      s->temp.size += tmp;
+      b->in_pos += tmp;
+      return 1;
+    } else {
+      s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+    }
+
+    s->rc.in = s->temp.buf;
+    s->rc.in_pos = 0;
+
+    if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+      return 0;
+
+    s->lzma2.compressed -= s->rc.in_pos;
+
+    if (s->rc.in_pos < s->temp.size) {
+      s->temp.size -= s->rc.in_pos;
+      memmove(s->temp.buf, s->temp.buf + s->rc.in_pos,
+          s->temp.size);
+      return 1;
+    }
+
+    b->in_pos += s->rc.in_pos - s->temp.size;
+    s->temp.size = 0;
+  }
+
+  in_avail = b->in_size - b->in_pos;
+  if (in_avail >= LZMA_IN_REQUIRED) {
+    s->rc.in = b->in;
+    s->rc.in_pos = b->in_pos;
+
+    if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+      s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+    else
+      s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+    if (!lzma_main(s))
+      return 0;
+
+    in_avail = s->rc.in_pos - b->in_pos;
+    if (in_avail > s->lzma2.compressed) return 0;
+
+    s->lzma2.compressed -= in_avail;
+    b->in_pos = s->rc.in_pos;
+  }
+
+  in_avail = b->in_size - b->in_pos;
+  if (in_avail < LZMA_IN_REQUIRED) {
+    if (in_avail > s->lzma2.compressed)
+      in_avail = s->lzma2.compressed;
+
+    memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+    s->temp.size = in_avail;
+    b->in_pos += in_avail;
+  }
+
+  return 1;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+               struct xz_buf *b)
+{
+  uint32_t tmp;
+
+  while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {
+    switch (s->lzma2.sequence) {
+    case SEQ_CONTROL:
+      /*
+       * LZMA2 control byte
+       *
+       * Exact values:
+       *   0x00   End marker
+       *   0x01   Dictionary reset followed by
+       *          an uncompressed chunk
+       *   0x02   Uncompressed chunk (no dictionary reset)
+       *
+       * Highest three bits (s->control & 0xE0):
+       *   0xE0   Dictionary reset, new properties and state
+       *          reset, followed by LZMA compressed chunk
+       *   0xC0   New properties and state reset, followed
+       *          by LZMA compressed chunk (no dictionary
+       *          reset)
+       *   0xA0   State reset using old properties,
+       *          followed by LZMA compressed chunk (no
+       *          dictionary reset)
+       *   0x80   LZMA chunk (no dictionary or state reset)
+       *
+       * For LZMA compressed chunks, the lowest five bits
+       * (s->control & 1F) are the highest bits of the
+       * uncompressed size (bits 16-20).
+       *
+       * A new LZMA2 stream must begin with a dictionary
+       * reset. The first LZMA chunk must set new
+       * properties and reset the LZMA state.
+       *
+       * Values that don't match anything described above
+       * are invalid and we return XZ_DATA_ERROR.
+       */
+      tmp = b->in[b->in_pos++];
+
+      if (tmp == 0x00)
+        return XZ_STREAM_END;
+
+      if (tmp >= 0xE0 || tmp == 0x01) {
+        s->lzma2.need_props = 1;
+        s->lzma2.need_dict_reset = 0;
+        dict_reset(&s->dict, b);
+      } else if (s->lzma2.need_dict_reset) {
+        return XZ_DATA_ERROR;
+      }
+
+      if (tmp >= 0x80) {
+        s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+        s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+        if (tmp >= 0xC0) {
+          /*
+           * When there are new properties,
+           * state reset is done at
+           * SEQ_PROPERTIES.
+           */
+          s->lzma2.need_props = 0;
+          s->lzma2.next_sequence
+              = SEQ_PROPERTIES;
+
+        } else if (s->lzma2.need_props) {
+          return XZ_DATA_ERROR;
+
+        } else {
+          s->lzma2.next_sequence
+              = SEQ_LZMA_PREPARE;
+          if (tmp >= 0xA0)
+            lzma_reset(s);
+        }
+      } else {
+        if (tmp > 0x02)
+          return XZ_DATA_ERROR;
+
+        s->lzma2.sequence = SEQ_COMPRESSED_0;
+        s->lzma2.next_sequence = SEQ_COPY;
+      }
+
+      break;
+
+    case SEQ_UNCOMPRESSED_1:
+      s->lzma2.uncompressed
+          += (uint32_t)b->in[b->in_pos++] << 8;
+      s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+      break;
+
+    case SEQ_UNCOMPRESSED_2:
+      s->lzma2.uncompressed
+          += (uint32_t)b->in[b->in_pos++] + 1;
+      s->lzma2.sequence = SEQ_COMPRESSED_0;
+      break;
+
+    case SEQ_COMPRESSED_0:
+      s->lzma2.compressed
+          = (uint32_t)b->in[b->in_pos++] << 8;
+      s->lzma2.sequence = SEQ_COMPRESSED_1;
+      break;
+
+    case SEQ_COMPRESSED_1:
+      s->lzma2.compressed
+          += (uint32_t)b->in[b->in_pos++] + 1;
+      s->lzma2.sequence = s->lzma2.next_sequence;
+      break;
+
+    case SEQ_PROPERTIES:
+      if (!lzma_props(s, b->in[b->in_pos++]))
+        return XZ_DATA_ERROR;
+
+      s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+    case SEQ_LZMA_PREPARE:
+      if (s->lzma2.compressed < RC_INIT_BYTES)
+        return XZ_DATA_ERROR;
+
+      if (!rc_read_init(&s->rc, b))
+        return XZ_OK;
+
+      s->lzma2.compressed -= RC_INIT_BYTES;
+      s->lzma2.sequence = SEQ_LZMA_RUN;
+
+    case SEQ_LZMA_RUN:
+      /*
+       * Set dictionary limit to indicate how much we want
+       * to be encoded at maximum. Decode new data into the
+       * dictionary. Flush the new data from dictionary to
+       * b->out. Check if we finished decoding this chunk.
+       * In case the dictionary got full but we didn't fill
+       * the output buffer yet, we may run this loop
+       * multiple times without changing s->lzma2.sequence.
+       */
+      dict_limit(&s->dict, min_t(size_t,
+          b->out_size - b->out_pos,
+          s->lzma2.uncompressed));
+      if (!lzma2_lzma(s, b))
+        return XZ_DATA_ERROR;
+
+      s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+      if (s->lzma2.uncompressed == 0) {
+        if (s->lzma2.compressed > 0 || s->lzma.len > 0
+            || !rc_is_finished(&s->rc))
+          return XZ_DATA_ERROR;
+
+        rc_reset(&s->rc);
+        s->lzma2.sequence = SEQ_CONTROL;
+
+      } else if (b->out_pos == b->out_size
+          || (b->in_pos == b->in_size
+            && s->temp.size
+            < s->lzma2.compressed)) {
+        return XZ_OK;
+      }
+
+      break;
+
+    case SEQ_COPY:
+      dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+      if (s->lzma2.compressed > 0)
+        return XZ_OK;
+
+      s->lzma2.sequence = SEQ_CONTROL;
+      break;
+    }
+  }
+
+  return XZ_OK;
+}
+
+struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+               uint32_t dict_max)
+{
+  struct xz_dec_lzma2 *s = malloc(sizeof(*s));
+  if (s == NULL)
+    return NULL;
+
+  s->dict.mode = mode;
+  s->dict.size_max = dict_max;
+
+  if (DEC_IS_PREALLOC(mode)) {
+    s->dict.buf = malloc(dict_max);
+    if (s->dict.buf == NULL) {
+      free(s);
+      return NULL;
+    }
+  } else if (DEC_IS_DYNALLOC(mode)) {
+    s->dict.buf = NULL;
+    s->dict.allocated = 0;
+  }
+
+  return s;
+}
+
+enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+{
+  /* This limits dictionary size to 3 GiB to keep parsing simpler. */
+  if (props > 39)
+    return XZ_OPTIONS_ERROR;
+
+  s->dict.size = 2 + (props & 1);
+  s->dict.size <<= (props >> 1) + 11;
+
+  if (DEC_IS_MULTI(s->dict.mode)) {
+    if (s->dict.size > s->dict.size_max)
+      return XZ_MEMLIMIT_ERROR;
+
+    s->dict.end = s->dict.size;
+
+    if (DEC_IS_DYNALLOC(s->dict.mode)) {
+      if (s->dict.allocated < s->dict.size) {
+        free(s->dict.buf);
+        s->dict.buf = malloc(s->dict.size);
+        if (s->dict.buf == NULL) {
+          s->dict.allocated = 0;
+          return XZ_MEM_ERROR;
+        }
+      }
+    }
+  }
+
+  s->lzma.len = 0;
+
+  s->lzma2.sequence = SEQ_CONTROL;
+  s->lzma2.need_dict_reset = 1;
+
+  s->temp.size = 0;
+
+  return XZ_OK;
+}
+
+/*
+ * .xz Stream decoder
+ */
+
+
+// BEGIN xz_stream.h
+/*
+ * Definitions for handling the .xz file format
+ */
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ"
+#define HEADER_MAGIC_SIZE 6
+
+#define FOOTER_MAGIC "YZ"
+#define FOOTER_MAGIC_SIZE 2
+
+/*
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type)-1 / 2)
+#define VLI_UNKNOWN ((vli_type)-1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check {
+  XZ_CHECK_NONE = 0,
+  XZ_CHECK_CRC32 = 1,
+  XZ_CHECK_CRC64 = 4,
+  XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+// END xz_stream.h
+
+#define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash {
+  vli_type unpadded;
+  vli_type uncompressed;
+  uint32_t crc32;
+};
+
+struct xz_dec {
+  /* Position in dec_main() */
+  enum {
+    SEQ_STREAM_HEADER,
+    SEQ_BLOCK_START,
+    SEQ_BLOCK_HEADER,
+    SEQ_BLOCK_UNCOMPRESS,
+    SEQ_BLOCK_PADDING,
+    SEQ_BLOCK_CHECK,
+    SEQ_INDEX,
+    SEQ_INDEX_PADDING,
+    SEQ_INDEX_CRC32,
+    SEQ_STREAM_FOOTER
+  } sequence;
+
+  /* Position in variable-length integers and Check fields */
+  uint32_t pos;
+
+  /* Variable-length integer decoded by dec_vli() */
+  vli_type vli;
+
+  /* Saved in_pos and out_pos */
+  size_t in_start;
+  size_t out_start;
+
+  /* CRC32 or CRC64 value in Block or CRC32 value in Index */
+  uint64_t crc;
+
+  /* Type of the integrity check calculated from uncompressed data */
+  enum xz_check check_type;
+
+  /* Operation mode */
+  enum xz_mode mode;
+
+  /*
+   * True if the next call to xz_dec_run() is allowed to return
+   * XZ_BUF_ERROR.
+   */
+  int allow_buf_error;
+
+  /* Information stored in Block Header */
+  struct {
+    /*
+     * Value stored in the Compressed Size field, or
+     * VLI_UNKNOWN if Compressed Size is not present.
+     */
+    vli_type compressed;
+
+    /*
+     * Value stored in the Uncompressed Size field, or
+     * VLI_UNKNOWN if Uncompressed Size is not present.
+     */
+    vli_type uncompressed;
+
+    /* Size of the Block Header field */
+    uint32_t size;
+  } block_header;
+
+  /* Information collected when decoding Blocks */
+  struct {
+    /* Observed compressed size of the current Block */
+    vli_type compressed;
+
+    /* Observed uncompressed size of the current Block */
+    vli_type uncompressed;
+
+    /* Number of Blocks decoded so far */
+    vli_type count;
+
+    /*
+     * Hash calculated from the Block sizes. This is used to
+     * validate the Index field.
+     */
+    struct xz_dec_hash hash;
+  } block;
+
+  /* Variables needed when verifying the Index field */
+  struct {
+    /* Position in dec_index() */
+    enum {
+      SEQ_INDEX_COUNT,
+      SEQ_INDEX_UNPADDED,
+      SEQ_INDEX_UNCOMPRESSED
+    } sequence;
+
+    /* Size of the Index in bytes */
+    vli_type size;
+
+    /* Number of Records (matches block.count in valid files) */
+    vli_type count;
+
+    /*
+     * Hash calculated from the Records (matches block.hash in
+     * valid files).
+     */
+    struct xz_dec_hash hash;
+  } index;
+
+  /*
+   * Temporary buffer needed to hold Stream Header, Block Header,
+   * and Stream Footer. The Block Header is the biggest (1 KiB)
+   * so we reserve space according to that. buf[] has to be aligned
+   * to a multiple of four bytes; the size_t variables before it
+   * should guarantee this.
+   */
+  struct {
+    size_t pos;
+    size_t size;
+    uint8_t buf[1024];
+  } temp;
+
+  struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+  struct xz_dec_bcj *bcj;
+  int bcj_active;
+#endif
+};
+
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {
+  0,
+  4, 4, 4,
+  8, 8, 8,
+  16, 16, 16,
+  32, 32, 32,
+  64, 64, 64
+};
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static int fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+  size_t copy_size = min_t(size_t,
+      b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+  memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+  b->in_pos += copy_size;
+  s->temp.pos += copy_size;
+
+  if (s->temp.pos == s->temp.size) {
+    s->temp.pos = 0;
+    return 1;
+  }
+
+  return 0;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,
+         size_t *in_pos, size_t in_size)
+{
+  uint8_t byte;
+
+  if (s->pos == 0)
+    s->vli = 0;
+
+  while (*in_pos < in_size) {
+    byte = in[*in_pos];
+    ++*in_pos;
+
+    s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+    if ((byte & 0x80) == 0) {
+      /* Don't allow non-minimal encodings. */
+      if (byte == 0 && s->pos != 0)
+        return XZ_DATA_ERROR;
+
+      s->pos = 0;
+      return XZ_STREAM_END;
+    }
+
+    s->pos += 7;
+    if (s->pos == 7 * VLI_BYTES_MAX)
+      return XZ_DATA_ERROR;
+  }
+
+  return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 or CRC64 value if presence of
+ * the CRC32 or CRC64 field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+  enum xz_ret ret;
+
+  s->in_start = b->in_pos;
+  s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+  if (s->bcj_active)
+    ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+  else
+#endif
+    ret = xz_dec_lzma2_run(s->lzma2, b);
+
+  s->block.compressed += b->in_pos - s->in_start;
+  s->block.uncompressed += b->out_pos - s->out_start;
+
+  /*
+   * There is no need to separately check for VLI_UNKNOWN, since
+   * the observed sizes are always smaller than VLI_UNKNOWN.
+   */
+  if (s->block.compressed > s->block_header.compressed
+      || s->block.uncompressed
+        > s->block_header.uncompressed)
+    return XZ_DATA_ERROR;
+
+  if (s->check_type == XZ_CHECK_CRC32)
+    s->crc = xz_crc32(b->out + s->out_start,
+        b->out_pos - s->out_start, s->crc);
+  else if (s->check_type == XZ_CHECK_CRC64)
+    s->crc = xz_crc64(b->out + s->out_start,
+        b->out_pos - s->out_start, s->crc);
+
+  if (ret == XZ_STREAM_END) {
+    if (s->block_header.compressed != VLI_UNKNOWN
+        && s->block_header.compressed
+          != s->block.compressed)
+      return XZ_DATA_ERROR;
+
+    if (s->block_header.uncompressed != VLI_UNKNOWN
+        && s->block_header.uncompressed
+          != s->block.uncompressed)
+      return XZ_DATA_ERROR;
+
+    s->block.hash.unpadded += s->block_header.size
+        + s->block.compressed;
+
+    s->block.hash.unpadded += check_sizes[s->check_type];
+
+    s->block.hash.uncompressed += s->block.uncompressed;
+    s->block.hash.crc32 = xz_crc32(
+        (const uint8_t *)&s->block.hash,
+        sizeof(s->block.hash), s->block.hash.crc32);
+
+    ++s->block.count;
+  }
+
+  return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+  size_t in_used = b->in_pos - s->in_start;
+  s->index.size += in_used;
+  s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+  enum xz_ret ret;
+
+  do {
+    ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+    if (ret != XZ_STREAM_END) {
+      index_update(s, b);
+      return ret;
+    }
+
+    switch (s->index.sequence) {
+    case SEQ_INDEX_COUNT:
+      s->index.count = s->vli;
+
+      /*
+       * Validate that the Number of Records field
+       * indicates the same number of Records as
+       * there were Blocks in the Stream.
+       */
+      if (s->index.count != s->block.count)
+        return XZ_DATA_ERROR;
+
+      s->index.sequence = SEQ_INDEX_UNPADDED;
+      break;
+
+    case SEQ_INDEX_UNPADDED:
+      s->index.hash.unpadded += s->vli;
+      s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+      break;
+
+    case SEQ_INDEX_UNCOMPRESSED:
+      s->index.hash.uncompressed += s->vli;
+      s->index.hash.crc32 = xz_crc32(
+          (const uint8_t *)&s->index.hash,
+          sizeof(s->index.hash),
+          s->index.hash.crc32);
+      --s->index.count;
+      s->index.sequence = SEQ_INDEX_UNPADDED;
+      break;
+    }
+  } while (s->index.count > 0);
+
+  return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four or eight input bytes match the value
+ * of s->crc. s->pos must be zero when starting to validate the first byte.
+ * The "bits" argument allows using the same code for both CRC32 and CRC64.
+ */
+static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,
+        uint32_t bits)
+{
+  do {
+    if (b->in_pos == b->in_size)
+      return XZ_OK;
+
+    if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
+      return XZ_DATA_ERROR;
+
+    s->pos += 8;
+
+  } while (s->pos < bits);
+
+  s->crc = 0;
+  s->pos = 0;
+
+  return XZ_STREAM_END;
+}
+
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static int check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+  while (s->pos < check_sizes[s->check_type]) {
+    if (b->in_pos == b->in_size) return 0;
+
+    ++b->in_pos;
+    ++s->pos;
+  }
+
+  s->pos = 0;
+
+  return 1;
+}
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret dec_stream_header(struct xz_dec *s)
+{
+  if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+    return XZ_FORMAT_ERROR;
+
+  if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
+      != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+    return XZ_DATA_ERROR;
+
+  if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+    return XZ_OPTIONS_ERROR;
+
+  /*
+   * Of integrity checks, we support none (Check ID = 0),
+   * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
+   * However, if XZ_DEC_ANY_CHECK is defined, we will accept other
+   * check types too, but then the check won't be verified and
+   * a warning (XZ_UNSUPPORTED_CHECK) will be given.
+   */
+  s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+  if (s->check_type > XZ_CHECK_MAX)
+    return XZ_OPTIONS_ERROR;
+
+  if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+    return XZ_UNSUPPORTED_CHECK;
+
+  return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret dec_stream_footer(struct xz_dec *s)
+{
+  if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+    return XZ_DATA_ERROR;
+
+  if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+    return XZ_DATA_ERROR;
+
+  /*
+   * Validate Backward Size. Note that we never added the size of the
+   * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+   * instead of s->index.size / 4 - 1.
+   */
+  if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+    return XZ_DATA_ERROR;
+
+  if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+    return XZ_DATA_ERROR;
+
+  /*
+   * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+   * for the caller.
+   */
+  return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret dec_block_header(struct xz_dec *s)
+{
+  enum xz_ret ret;
+
+  /*
+   * Validate the CRC32. We know that the temp buffer is at least
+   * eight bytes so this is safe.
+   */
+  s->temp.size -= 4;
+  if (xz_crc32(s->temp.buf, s->temp.size, 0)
+      != get_le32(s->temp.buf + s->temp.size))
+    return XZ_DATA_ERROR;
+
+  s->temp.pos = 2;
+
+  /*
+   * Catch unsupported Block Flags. We support only one or two filters
+   * in the chain, so we catch that with the same test.
+   */
+#ifdef XZ_DEC_BCJ
+  if (s->temp.buf[1] & 0x3E)
+#else
+  if (s->temp.buf[1] & 0x3F)
+#endif
+    return XZ_OPTIONS_ERROR;
+
+  /* Compressed Size */
+  if (s->temp.buf[1] & 0x40) {
+    if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+          != XZ_STREAM_END)
+      return XZ_DATA_ERROR;
+
+    s->block_header.compressed = s->vli;
+  } else {
+    s->block_header.compressed = VLI_UNKNOWN;
+  }
+
+  /* Uncompressed Size */
+  if (s->temp.buf[1] & 0x80) {
+    if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+        != XZ_STREAM_END)
+      return XZ_DATA_ERROR;
+
+    s->block_header.uncompressed = s->vli;
+  } else {
+    s->block_header.uncompressed = VLI_UNKNOWN;
+  }
+
+#ifdef XZ_DEC_BCJ
+  /* If there are two filters, the first one must be a BCJ filter. */
+  s->bcj_active = s->temp.buf[1] & 0x01;
+  if (s->bcj_active) {
+    if (s->temp.size - s->temp.pos < 2)
+      return XZ_OPTIONS_ERROR;
+
+    ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+    if (ret != XZ_OK)
+      return ret;
+
+    /*
+     * We don't support custom start offset,
+     * so Size of Properties must be zero.
+     */
+    if (s->temp.buf[s->temp.pos++] != 0x00)
+      return XZ_OPTIONS_ERROR;
+  }
+#endif
+
+  /* Valid Filter Flags always take at least two bytes. */
+  if (s->temp.size - s->temp.pos < 2)
+    return XZ_DATA_ERROR;
+
+  /* Filter ID = LZMA2 */
+  if (s->temp.buf[s->temp.pos++] != 0x21)
+    return XZ_OPTIONS_ERROR;
+
+  /* Size of Properties = 1-byte Filter Properties */
+  if (s->temp.buf[s->temp.pos++] != 0x01)
+    return XZ_OPTIONS_ERROR;
+
+  /* Filter Properties contains LZMA2 dictionary size. */
+  if (s->temp.size - s->temp.pos < 1)
+    return XZ_DATA_ERROR;
+
+  ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+  if (ret != XZ_OK)
+    return ret;
+
+  /* The rest must be Header Padding. */
+  while (s->temp.pos < s->temp.size)
+    if (s->temp.buf[s->temp.pos++] != 0x00)
+      return XZ_OPTIONS_ERROR;
+
+  s->temp.pos = 0;
+  s->block.compressed = 0;
+  s->block.uncompressed = 0;
+
+  return XZ_OK;
+}
+
+static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+  enum xz_ret ret;
+
+  /*
+   * Store the start position for the case when we are in the middle
+   * of the Index field.
+   */
+  s->in_start = b->in_pos;
+
+  for (;;) {
+    switch (s->sequence) {
+    case SEQ_STREAM_HEADER:
+      /*
+       * Stream Header is copied to s->temp, and then
+       * decoded from there. This way if the caller
+       * gives us only little input at a time, we can
+       * still keep the Stream Header decoding code
+       * simple. Similar approach is used in many places
+       * in this file.
+       */
+      if (!fill_temp(s, b))
+        return XZ_OK;
+
+      /*
+       * If dec_stream_header() returns
+       * XZ_UNSUPPORTED_CHECK, it is still possible
+       * to continue decoding if working in multi-call
+       * mode. Thus, update s->sequence before calling
+       * dec_stream_header().
+       */
+      s->sequence = SEQ_BLOCK_START;
+
+      ret = dec_stream_header(s);
+      if (ret != XZ_OK)
+        return ret;
+
+    case SEQ_BLOCK_START:
+      /* We need one byte of input to continue. */
+      if (b->in_pos == b->in_size)
+        return XZ_OK;
+
+      /* See if this is the beginning of the Index field. */
+      if (b->in[b->in_pos] == 0) {
+        s->in_start = b->in_pos++;
+        s->sequence = SEQ_INDEX;
+        break;
+      }
+
+      /*
+       * Calculate the size of the Block Header and
+       * prepare to decode it.
+       */
+      s->block_header.size
+        = ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+      s->temp.size = s->block_header.size;
+      s->temp.pos = 0;
+      s->sequence = SEQ_BLOCK_HEADER;
+
+    case SEQ_BLOCK_HEADER:
+      if (!fill_temp(s, b))
+        return XZ_OK;
+
+      ret = dec_block_header(s);
+      if (ret != XZ_OK)
+        return ret;
+
+      s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+    case SEQ_BLOCK_UNCOMPRESS:
+      ret = dec_block(s, b);
+      if (ret != XZ_STREAM_END)
+        return ret;
+
+      s->sequence = SEQ_BLOCK_PADDING;
+
+    case SEQ_BLOCK_PADDING:
+      /*
+       * Size of Compressed Data + Block Padding
+       * must be a multiple of four. We don't need
+       * s->block.compressed for anything else
+       * anymore, so we use it here to test the size
+       * of the Block Padding field.
+       */
+      while (s->block.compressed & 3) {
+        if (b->in_pos == b->in_size)
+          return XZ_OK;
+
+        if (b->in[b->in_pos++] != 0)
+          return XZ_DATA_ERROR;
+
+        ++s->block.compressed;
+      }
+
+      s->sequence = SEQ_BLOCK_CHECK;
+
+    case SEQ_BLOCK_CHECK:
+      if (s->check_type == XZ_CHECK_CRC32) {
+        ret = crc_validate(s, b, 32);
+        if (ret != XZ_STREAM_END)
+          return ret;
+      }
+      else if (IS_CRC64(s->check_type)) {
+        ret = crc_validate(s, b, 64);
+        if (ret != XZ_STREAM_END)
+          return ret;
+      }
+      else if (!check_skip(s, b)) {
+        return XZ_OK;
+      }
+
+      s->sequence = SEQ_BLOCK_START;
+      break;
+
+    case SEQ_INDEX:
+      ret = dec_index(s, b);
+      if (ret != XZ_STREAM_END)
+        return ret;
+
+      s->sequence = SEQ_INDEX_PADDING;
+
+    case SEQ_INDEX_PADDING:
+      while ((s->index.size + (b->in_pos - s->in_start))
+          & 3) {
+        if (b->in_pos == b->in_size) {
+          index_update(s, b);
+          return XZ_OK;
+        }
+
+        if (b->in[b->in_pos++] != 0)
+          return XZ_DATA_ERROR;
+      }
+
+      /* Finish the CRC32 value and Index size. */
+      index_update(s, b);
+
+      /* Compare the hashes to validate the Index field. */
+      if (!memeq(&s->block.hash, &s->index.hash,
+          sizeof(s->block.hash)))
+        return XZ_DATA_ERROR;
+
+      s->sequence = SEQ_INDEX_CRC32;
+
+    case SEQ_INDEX_CRC32:
+      ret = crc_validate(s, b, 32);
+      if (ret != XZ_STREAM_END)
+        return ret;
+
+      s->temp.size = STREAM_HEADER_SIZE;
+      s->sequence = SEQ_STREAM_FOOTER;
+
+    case SEQ_STREAM_FOOTER:
+      if (!fill_temp(s, b))
+        return XZ_OK;
+
+      return dec_stream_footer(s);
+    }
+  }
+
+  /* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+  size_t in_start;
+  size_t out_start;
+  enum xz_ret ret;
+
+  if (DEC_IS_SINGLE(s->mode))
+    xz_dec_reset(s);
+
+  in_start = b->in_pos;
+  out_start = b->out_pos;
+  ret = dec_main(s, b);
+
+  if (DEC_IS_SINGLE(s->mode)) {
+    if (ret == XZ_OK)
+      ret = b->in_pos == b->in_size
+          ? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+    if (ret != XZ_STREAM_END) {
+      b->in_pos = in_start;
+      b->out_pos = out_start;
+    }
+
+  } else if (ret == XZ_OK && in_start == b->in_pos
+      && out_start == b->out_pos) {
+    if (s->allow_buf_error)
+      ret = XZ_BUF_ERROR;
+
+    s->allow_buf_error = 1;
+  } else {
+    s->allow_buf_error = 0;
+  }
+
+  return ret;
+}
+
+struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+{
+  struct xz_dec *s = malloc(sizeof(*s));
+  if (s == NULL)
+    return NULL;
+
+  s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+  s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+  if (s->bcj == NULL)
+    goto error_bcj;
+#endif
+
+  s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+  if (s->lzma2 == NULL)
+    goto error_lzma2;
+
+  xz_dec_reset(s);
+  return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+  free(s->bcj);
+error_bcj:
+#endif
+  free(s);
+  return NULL;
+}
+
+void xz_dec_reset(struct xz_dec *s)
+{
+  s->sequence = SEQ_STREAM_HEADER;
+  s->allow_buf_error = 0;
+  s->pos = 0;
+  s->crc = 0;
+  memset(&s->block, 0, sizeof(s->block));
+  memset(&s->index, 0, sizeof(s->index));
+  s->temp.pos = 0;
+  s->temp.size = STREAM_HEADER_SIZE;
+}
+
+void xz_dec_end(struct xz_dec *s)
+{
+  if (s != NULL) {
+    if (DEC_IS_MULTI((s->lzma2)->dict.mode))
+      free((s->lzma2)->dict.buf);
+    free(s->lzma2);
+
+#ifdef XZ_DEC_BCJ
+    free(s->bcj);
+#endif
+    free(s);
+  }
+}
diff --git a/toys/posix/README b/toys/posix/README
new file mode 100644 (file)
index 0000000..b0d6007
--- /dev/null
@@ -0,0 +1,5 @@
+Posix commands
+
+Commands defined in POSIX-2008, also known as the Single Unix
+Specification version 4, available online at
+http://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html
diff --git a/toys/posix/ash.c b/toys/posix/ash.c
new file mode 100644 (file)
index 0000000..edd75ab
--- /dev/null
@@ -0,0 +1,11180 @@
+/* ash.c - ash shell port.
+ *
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+USE_ASH(NEWTOY(ash, "?l", TOYFLAG_BIN))
+USE_ASH(OLDTOY(sh,ash, "?l", TOYFLAG_BIN))
+config ASH
+  bool "ash"
+  default y 
+  help
+    usage: ash [-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]
+
+    ASH shell Interpreter.
+
+config ASH_TEST
+  bool "ash built-in test"
+  default y
+  depends on ASH
+  select TEST
+
+config ASH_PRINTF
+  bool "ash built-in printf"
+  default y
+  depends on ASH
+  select PRINTF
+config ASH_KILL
+  bool "ash built-in kill"
+  default y
+  depends on ASH
+  select KILL
+
+*/
+
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *  must display the following acknowledgement:
+ *    This product includes software developed by the University of
+ *    California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+//==================================================================================
+//Header files.
+#define FOR_ash
+#include "toys.h"
+#include "lib/ash.h"
+//==================================================================================
+//Global variables.
+int rootpid; /* pid of main shell */
+int rootshell; /* true if we aren't a child of the main shell */
+STATIC union node *curcmd;
+STATIC union node *prevcmd;
+int errno;
+
+#if PROFILE
+short profile_buf[16384];
+int etext();
+#endif
+
+char *curdir;      /* current working directory */
+STATIC char *cdcomppath;
+
+struct jmploc *handler;
+int exception;
+volatile int suppressint;
+volatile int intpending;
+char *commandname;    /* name of command--printed on error */
+int evalskip;      /* set if we are skipping commands */
+STATIC int skipcount;  /* number of levels to skip */
+int loopnest;      /* current loop nesting level */
+int funcnest;      /* depth of function calls */
+
+struct strlist *cmdenviron;
+int exitstatus;      /* exit status of last command */
+STATIC struct tblentry *cmdtable[CMDTABLESIZE];
+STATIC int builtinloc = -1;    /* index in path of %builtin, or -1 */
+char *expdest;        /* output of current string */
+struct nodelist *argbackq;  /* list of back quote expressions */
+struct ifsregion ifsfirst;  /* first struct in list of ifs regions */
+struct ifsregion *ifslastp;  /* last struct in list */
+struct arglist exparg;    /* holds expanded arg list */
+
+#if UDIR
+/*
+ * Set if the last argument processed had /u/logname expanded.  This
+ * variable is read by the cd command.
+ */
+int didudir;
+#endif
+
+int plinno = 1;            /* input line number */
+int parsenleft;            /* copy of parsefile->nleft */
+
+int parselleft;            /* copy of parsefile->lleft */
+static int noalias = 0;       /* when set, don't handle aliases */
+
+char *parsenextc;          /* copy of parsefile->nextc */
+struct parsefile basepf;      /* top level input file */
+char basebuf[BUFSIZ];        /* buffer for top level input file */
+struct parsefile *parsefile = &basepf;  /* current input file */
+char *pushedstring;          /* copy of parsenextc when text pushed back */
+int pushednleft;          /* copy of parsenleft when text pushed back */
+STATIC int nmboxes;          /* number of mailboxes */
+STATIC time_t mailtime[MAXMBOXES];  /* times of mailboxes */
+struct stack_block stackbase;
+struct stack_block *stackp = &stackbase;
+char *stacknxt = stackbase.space;
+int stacknleft = MINSIZE;
+int sstrnleft;
+int herefd = -1;
+struct job *jobtab;        /* array of jobs */
+int njobs;            /* size of array */
+short backgndpid = -1;      /* pid of last background process */
+
+#if JOBS
+int initialpgrp;        /* pgrp of shell on invocation */
+short curjob;          /* current job */
+#endif
+
+char nullstr[1];        /* zero length string */
+char *arg0;            /* value of $0 */
+struct shparam shellparam;    /* current positional parameters */
+char **argptr;          /* argument list for builtin commands */
+char *optarg;          /* set by nextopt (like getopt) */
+char *optptr;          /* used by nextopt */
+char *minusc;          /* argument to -c option */
+struct nodelist *backquotelist;
+union node *redirnode;
+struct heredoc *heredoc;
+struct heredoc *heredoclist;  /* list of here documents to read */
+int parsebackquote;        /* nonzero if we are inside backquotes */
+int doprompt;          /* if set, prompt the user */
+int needprompt;          /* true if interactive and at start of line */
+int lasttoken;          /* last token read */
+int tokpushback;        /* last token pushed back */
+char *wordtext;          /* text of last word returned by readtoken */
+int checkkwd;           /* 1 == check for kwds, 2 == also eat newlines */
+int quoteflag;          /* set if (part of) last token was quoted */
+int startlinno;          /* line # where last token started */
+
+#define OPENBRACE '{'
+#define CLOSEBRACE '}'
+#define GDB_HACK 1         /* avoid local declarations which gdb can't handle */
+#ifdef GDB_HACK
+static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
+static const char types[] = "}-+?=";
+#endif
+
+struct redirtab *redirlist;
+
+/* We keep track of whether or not fd0 has been redirected.  This is for
+ * background commands, where we want to redirect fd0 to /dev/null only
+ * if it hasn't already been redirected.
+*/
+int fd0_redirected = 0;
+char *trap[MAXSIG+1];    /* trap handler commands */
+char sigmode[MAXSIG];    /* current value of signal */
+char gotsig[MAXSIG];    /* indicates specified signal received */
+int pendingsigs;      /* indicates some signal received */
+
+struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
+struct output errout = {NULL, 0, NULL, 100, 2, 0};;
+struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
+struct output *out1 = &output;
+struct output *out2 = &errout;
+
+int funcblocksize;    /* size of structures in function */
+int funcstringsize;    /* size of strings in node */
+#ifdef __STDC__
+pointer funcblock;    /* block to allocate function from */
+#else
+char *funcblock;    /* block to allocate function from */
+#endif
+char *funcstring;    /* block to allocate strings from */
+char *pathopt;       /* set by padvance */
+struct localvar *localvars;
+struct tblentry **lastcmdentry;
+char *expdir;
+
+static const short nodesize[24] = {
+    ALIGN(sizeof (struct nbinary)),
+    ALIGN(sizeof (struct ncmd)),
+    ALIGN(sizeof (struct npipe)),
+    ALIGN(sizeof (struct nredir)),
+    ALIGN(sizeof (struct nredir)),
+    ALIGN(sizeof (struct nredir)),
+    ALIGN(sizeof (struct nbinary)),
+    ALIGN(sizeof (struct nbinary)),
+    ALIGN(sizeof (struct nif)),
+    ALIGN(sizeof (struct nbinary)),
+    ALIGN(sizeof (struct nbinary)),
+    ALIGN(sizeof (struct nfor)),
+    ALIGN(sizeof (struct ncase)),
+    ALIGN(sizeof (struct nclist)),
+    ALIGN(sizeof (struct narg)),
+    ALIGN(sizeof (struct narg)),
+    ALIGN(sizeof (struct nfile)),
+    ALIGN(sizeof (struct nfile)),
+    ALIGN(sizeof (struct nfile)),
+    ALIGN(sizeof (struct ndup)),
+    ALIGN(sizeof (struct ndup)),
+    ALIGN(sizeof (struct nhere)),
+    ALIGN(sizeof (struct nhere)),
+    ALIGN(sizeof (struct nnot)),
+};
+
+int bltincmd();
+int bgcmd();
+int breakcmd();
+int cdcmd();
+int dotcmd();
+int echocmd();
+int evalcmd();
+int execcmd();
+int exitcmd();
+int exportcmd();
+int falsecmd();
+int fgcmd();
+int getoptscmd();
+int hashcmd();
+int jobidcmd();
+int jobscmd();
+int localcmd();
+int pwdcmd();
+int readcmd();
+int returncmd();
+int setcmd();
+int setvarcmd();
+int shiftcmd();
+int trapcmd();
+int truecmd();
+int umaskcmd();
+int unsetcmd();
+int waitcmd();
+int timescmd();
+int helpcmd();
+#if CFG_ASH_KILL
+int killcmd();
+#endif
+#if CFG_ASH_TEST
+int testcmd();
+#endif
+
+#if CFG_ASH_PRINTF
+int printfcmd();
+#endif
+int aliascmd();
+int unaliascmd();
+int typecmd();
+int ulimitcmd();
+int historycmd();
+int expcmd();
+
+
+int (*const builtinfunc[])() = {
+  bltincmd,
+  bgcmd,
+  breakcmd,
+  cdcmd,
+  dotcmd,
+  echocmd,
+  evalcmd,
+  execcmd,
+  exitcmd,
+  exportcmd,
+  falsecmd,
+  fgcmd,
+  getoptscmd,
+  hashcmd,
+  jobidcmd,
+  jobscmd,
+  localcmd,
+  pwdcmd,
+  readcmd,
+  returncmd,
+  setcmd,
+  setvarcmd,
+  shiftcmd,
+  trapcmd,
+  truecmd,
+  umaskcmd,
+  unsetcmd,
+  waitcmd,
+  timescmd,
+  helpcmd,
+#if CFG_ASH_KILL
+  killcmd,
+#endif
+#if CFG_ASH_TEST
+  testcmd,
+#endif
+#if CFG_ASH_PRINTF
+  printfcmd,
+#endif
+  aliascmd,
+  unaliascmd,
+  typecmd,
+  ulimitcmd,
+  historycmd,
+  expcmd,
+};
+
+/*builtins command functions. */
+const struct builtincmd builtincmd[] = {
+  {"command", 0},
+  {"bg", 1},
+  {"break", 2},
+  {"continue", 2},
+  {"cd", 3},
+  {"chdir", 3},
+  {".", 4},
+  {"source", 4},
+  {"echo", 5},
+  {"eval", 6},
+  {"exec", 7},
+  {"exit", 8},
+  {"export", 9},
+  {"readonly", 9},
+  {"false", 10},
+  {"fg", 11},
+  {"getopts", 12},
+  {"hash", 13},
+  {"jobid", 14},
+  {"jobs", 15},
+  {"local", 16},
+  {"pwd", 17},
+  {"read", 18},
+  {"return", 19},
+  {"set", 20},
+  {"setvar", 21},
+  {"shift", 22},
+  {"trap", 23},
+  {":", 24},
+  {"true", 24},
+  {"umask", 25},
+  {"unset", 26},
+  {"wait", 27},
+  {"times", 28},
+  {"help", 29},
+#if CFG_ASH_KILL
+  {"kill", 30},
+#endif
+#if CFG_ASH_TEST
+  {"test", 31},
+  {"[", 31},
+  {"[[", 31},
+#endif
+#if CFG_ASH_PRINTF
+  {"printf", 32},
+#endif
+  {"alias", 33},
+  {"unalias", 34},
+  {"type", 35},
+  {"ulimit", 36},
+  {"history", 37},
+  {"exp", 38},
+  {"let", 38},
+  {NULL, 0}
+};
+
+/*signames - signal names */
+char *const sigmesg[32] = {
+  0,
+  "Hangup",
+  "Interrupt",
+  "Quit",
+  "Illegal instruction",
+  "Trace/BPT trap",
+  "abort",
+  "Bus error",
+  "Floating exception",
+  "Killed",
+  "User signal 1",
+  "Memory fault",
+  "User signal 2",
+  "Broken pipe",
+  "Alarm call",
+  "Terminated",
+  0,
+  0,
+  0,
+  "Stopped",
+  "Stopped",
+  "Stopped (input)",
+  "Stopped (output)",
+  0,
+  "Time limit exceeded",
+  0,
+  0,
+  "Profiling alarm",
+  0,
+  0,
+  "Power fail",
+  "Bad system call",
+};
+
+/* syntax table used when not in quotes */
+const char basesyntax[257] = {
+    CEOF,  CWORD,   CCTL,  CCTL,
+    CCTL,  CCTL,  CCTL,  CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CSPCL,   CNL,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CSPCL,   CWORD,   CDQUOTE,
+    CWORD,   CVAR,  CWORD,   CSPCL,
+    CSQUOTE, CSPCL,   CSPCL,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CSPCL,   CSPCL,   CWORD,   CSPCL,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CBACK,   CWORD,   CWORD,
+    CWORD,   CBQUOTE, CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CSPCL,   CENDVAR, CWORD,
+    CWORD
+};
+
+/* syntax table used when in double quotes */
+const char dqsyntax[257] = {
+    CEOF,  CWORD,   CCTL,  CCTL,
+    CCTL,  CCTL,  CCTL,  CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CNL,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CCTL,  CENDQUOTE,
+    CWORD,   CVAR,  CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CCTL,
+    CWORD,   CWORD,   CCTL,  CWORD,
+    CCTL,  CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CCTL,
+    CWORD,   CWORD,   CCTL,  CWORD,
+    CCTL,  CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CCTL,  CBACK,   CWORD,   CWORD,
+    CWORD,   CBQUOTE, CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CENDVAR, CCTL,
+    CWORD
+};
+
+/* syntax table used when in single quotes */
+const char sqsyntax[257] = {
+    CEOF,  CWORD,   CCTL,  CCTL,
+    CCTL,  CCTL,  CCTL,  CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CNL,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CCTL,  CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CENDQUOTE,CWORD,  CWORD,   CCTL,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CCTL,  CWORD,
+    CCTL,  CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CCTL,  CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD,   CWORD,   CWORD,   CWORD,
+    CWORD
+};
+
+/* character classification table */
+const char is_type[257] = {
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     0,     0,
+    0,     0,     ISSPECL, 0,
+    ISSPECL, ISSPECL, 0,     0,
+    0,     0,     0,     ISSPECL,
+    0,     0,     ISSPECL, 0,
+    0,     ISDIGIT, ISDIGIT, ISDIGIT,
+    ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
+    ISDIGIT, ISDIGIT, ISDIGIT, 0,
+    0,     0,     0,     0,
+    ISSPECL, ISSPECL, ISUPPER, ISUPPER,
+    ISUPPER, ISUPPER, ISUPPER, ISUPPER,
+    ISUPPER, ISUPPER, ISUPPER, ISUPPER,
+    ISUPPER, ISUPPER, ISUPPER, ISUPPER,
+    ISUPPER, ISUPPER, ISUPPER, ISUPPER,
+    ISUPPER, ISUPPER, ISUPPER, ISUPPER,
+    ISUPPER, ISUPPER, ISUPPER, ISUPPER,
+    0,     0,     0,     0,
+    ISUNDER, 0,     ISLOWER, ISLOWER,
+    ISLOWER, ISLOWER, ISLOWER, ISLOWER,
+    ISLOWER, ISLOWER, ISLOWER, ISLOWER,
+    ISLOWER, ISLOWER, ISLOWER, ISLOWER,
+    ISLOWER, ISLOWER, ISLOWER, ISLOWER,
+    ISLOWER, ISLOWER, ISLOWER, ISLOWER,
+    ISLOWER, ISLOWER, ISLOWER, ISLOWER,
+    0,     0,     0,     0,
+    0
+};
+
+//var - environment variable (get, set etc.).
+#if ATTY
+struct var vatty;
+#endif
+struct var vifs;
+struct var vmail;
+struct var vmpath;
+struct var vpath;
+struct var vps1;
+struct var vps2;
+struct var vvers;
+#if ATTY
+struct var vterm;
+#endif
+
+const struct varinit varinit[] = {
+#if ATTY
+  {&vatty,  VSTRFIXED|VTEXTFIXED|VUNSET,  "ATTY="},
+#endif
+  {&vifs,   VSTRFIXED|VTEXTFIXED,       "IFS= \t\n"},
+  {&vmail,  VSTRFIXED|VTEXTFIXED|VUNSET,  "MAIL="},
+  {&vmpath,   VSTRFIXED|VTEXTFIXED|VUNSET,  "MAILPATH="},
+  {&vpath,  VSTRFIXED|VTEXTFIXED,       "PATH=:/bin:/usr/bin"},
+  /*
+   * vps1 depends on uid
+   */
+  {&vps2,   VSTRFIXED|VTEXTFIXED,       "PS2=> "},
+#if ATTY
+  {&vterm,  VSTRFIXED|VTEXTFIXED|VUNSET,  "TERM="},
+#endif
+  {NULL,    0,                NULL}
+};
+
+struct var *vartab[VTABSIZE];
+STATIC void unsetvar __P((char *));
+STATIC struct var **hashvar __P((char *));
+STATIC int varequal __P((char *, char *));
+
+#define TEMPSIZE 24
+#ifdef __STDC__
+static const char digit[16] = "0123456789ABCDEF";
+#else
+static const char digit[17] = "0123456789ABCDEF";
+#endif
+
+int is_interactive;
+FILE *tracefile;
+
+/*Shell command parser.*/
+void setparam(char **argv);
+void freeparam(struct shparam *param);
+STATIC int xxreadtoken();
+
+STATIC char *cmdnextc;
+STATIC int cmdnleft;
+STATIC void cmdtxt(), cmdputs();
+
+
+/* for keyboard input. */ 
+#define LEFT  0
+#define RIGHT   1
+
+enum CODES{
+  KEY_UP = 0x100,
+  KEY_DOWN,
+  KEY_RIGHT,
+  KEY_LEFT,
+  KEY_HOME,
+  KEY_END,
+  KEY_INSERT,
+  KEY_DELETE,
+  KEY_PAGEUP,
+  KEY_PAGEDN,
+  KEY_CTLLF,
+  KEY_CTLRT,
+  KEY_CTRLD,
+  KEY_CTRLU
+};
+
+typedef struct keycode_map_s {
+  char *key;
+  int code;
+}keycode_map_t;
+
+/*
+ * Table of error messages.
+ */
+struct errname {
+  short errcode;    /* error number */
+  short action;     /* operation which encountered the error */
+  char *msg;      /* text describing the error */
+};
+
+#define ALL (E_OPEN|E_CREAT|E_EXEC)
+STATIC const struct errname errormsg[] = {
+  {EINTR,   ALL,    "interrupted"},
+  {EACCES,  ALL,    "permission denied"},
+  {EIO,     ALL,    "I/O error"},
+  {ENOENT,  E_OPEN,   "no such file"},
+  {ENOENT,  E_CREAT,  "directory nonexistent"},
+  {ENOENT,  E_EXEC,   "not found"},
+  {ENOTDIR,   E_OPEN,   "no such file"},
+  {ENOTDIR,   E_CREAT,  "directory nonexistent"},
+  {ENOTDIR,   E_EXEC,   "not found"},
+  {EISDIR,  ALL,    "is a directory"},
+/*  {EMFILE,  ALL,    "too many open files"}, */
+  {ENFILE,  ALL,    "file table overflow"},
+  {ENOSPC,  ALL,    "file system full"},
+#ifdef EDQUOT
+  {EDQUOT,  ALL,    "disk quota exceeded"},
+#endif
+#ifdef ENOSR
+  {ENOSR,   ALL,    "no streams resources"},
+#endif
+  {ENXIO,   ALL,    "no such device or address"},
+  {EROFS,   ALL,    "read-only file system"},
+  {ETXTBSY,   ALL,    "text busy"},
+#ifdef SYSV
+  {EAGAIN,  E_EXEC,   "not enough memory"},
+#endif
+  {ENOMEM,  ALL,    "not enough memory"},
+#ifdef ENOLINK
+  {ENOLINK,   ALL,    "remote access failed"},
+#endif
+#ifdef EMULTIHOP
+  {EMULTIHOP, ALL,    "remote access failed"},
+#endif
+#ifdef ECOMM
+  {ECOMM,   ALL,    "remote access failed"},
+#endif
+#ifdef ESTALE
+  {ESTALE,  ALL,    "remote access failed"},
+#endif
+#ifdef ETIMEDOUT
+  {ETIMEDOUT, ALL,    "remote access failed"},
+#endif
+#ifdef ELOOP
+  {ELOOP,   ALL,    "symbolic link loop"},
+#endif
+  {E2BIG,   E_EXEC,   "argument list too long"},
+#ifdef ELIBACC
+  {ELIBACC,   E_EXEC,   "shared library missing"},
+#endif
+  {0,     0,      NULL}
+};
+
+#define MAXPWD PATH_MAX
+static int history_items = 0;
+
+//Done till here:
+
+//==================================================================================
+/*
+ * Initialization code.
+ */
+void init()
+{
+  char **envp;
+  extern char **environ;
+
+  initvar();
+  for (envp = environ ; *envp ; envp++) {
+    if (strchr(*envp, '='))
+      setvareq(*envp, VEXPORT|VTEXTFIXED);
+  }
+
+  {
+    extern char basebuf[];
+    basepf.nextc = basepf.buf = basebuf;
+  }
+}
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ */
+void reset()
+{
+  out1 = &output;
+  out2 = &errout;
+  if (memout.buf != NULL) {
+    ckfree(memout.buf);
+    memout.buf = NULL;
+  }
+
+  while (redirlist)
+    popredir();
+
+  tokpushback = 0;
+
+  if (exception != EXSHELLPROC)
+    parsenleft = 0;      /* clear input buffer */
+  popallfiles();
+
+  evalskip = 0;
+  loopnest = 0;
+  funcnest = 0;
+}
+
+/*
+ * This routine is called to initialize the shell to run a shell procedure.
+ */
+void initshellproc()
+{
+  shprocvar();
+
+    {
+      char *sm;
+
+      clear_traps();
+      for (sm = sigmode ; sm < sigmode + MAXSIG ; sm++) {
+        if (*sm == S_IGN)
+          *sm = S_HARD_IGN;
+      }
+    }
+
+    clearredir();
+
+    {
+      char *p;
+      for (p = optval ; p < optval + sizeof optval ; p++)
+        *p = 0;
+    }
+
+
+    {
+      backgndpid = -1;
+#if JOBS
+      jobctl = 0;
+#endif
+    }
+
+    popallfiles();
+    deletefuncs();
+    exitstatus = 0;
+}
+
+//==================================================================================
+/*
+ * Echo command.
+ */
+int echocmd(int argc, char **argv)
+{
+  register char **ap;
+  register char *p;
+  register char c;
+  int count;
+  int nnflag = 0;
+  int eeflag = 0;
+
+  ap = argv;
+  if (argc)
+    ap++;
+again:
+  if ((p = *ap) != NULL) {
+    if (equal(p, "-n")) {
+      nnflag++;
+      ap++;
+      goto again;
+    } else if (equal(p, "-e")) {
+      eeflag++;
+      ap++;
+      goto again;
+    } else if (equal(p, "-ne") || equal(p, "-en")) {
+      eeflag++;
+      nnflag++;
+      ap++;
+      goto again;
+    }
+  }
+  while ((p = *ap++) != NULL) {
+    while ((c = *p++) != '\0') {
+      if (c == '\\' && eeflag) {
+        switch (*p++) {
+        case 'b':  c = '\b';  break;
+        case 'c':  return 0;    /* exit */
+        case 'f':  c = '\f';  break;
+        case 'n':  c = '\n';  break;
+        case 'r':  c = '\r';  break;
+        case 't':  c = '\t';  break;
+        case 'v':  c = '\v';  break;
+        case '\\':  break;    /* c = '\\' */
+        case '0':
+          c = 0;
+          count = 3;
+          while (--count >= 0 && (unsigned)(*p - '0') < 8)
+            c = (c << 3) + (*p++ - '0');
+          break;
+        default:
+          p--;
+          break;
+        }
+      }
+      out1c(c);
+    }
+    if (*ap) out1c(' ');
+  }
+  if (! nnflag) out1c('\n');
+  return 0;
+}
+
+//==================================================================================
+//miscbltin commands
+/*
+ * The read builtin.  The -e option causes backslashes to escape the
+ * following character.
+ *
+ * This uses unbuffered input, which may be avoidable in some cases.
+ */
+int readcmd(int argc, char **argv)
+{
+  char **ap;
+  int backslash;
+  char c;
+  int eflag;
+  char *prompt;
+  char *ifs;
+  char *p;
+  int startword;
+  int status;
+  int i;
+
+  char *buffer = NULL;
+  int bufpos = 0; /* need to be able to hold -1 */
+
+  eflag = 0;
+  prompt = NULL;
+  while ((i = nextopt("ep:")) != '\0') {
+    if (i == 'p')
+      prompt = optarg;
+    else
+      eflag = 1;
+  }
+  if (prompt && isatty(0)) {
+    out2str(prompt);
+    flushall();
+  }
+  if ((ap = argptr) == NULL)
+    sh_error("arg count");
+  if ((ifs = bltinlookup("IFS", 1)) == NULL)
+    ifs = nullstr;
+  status = 0;
+  startword = 1;
+  backslash = 0;
+  STARTSTACKSTR(p);
+
+  for (;;) {
+
+    if ((bufpos & 0xff) == 0)
+      buffer = xrealloc(buffer, bufpos + 0x100);
+
+    if (read(0, &c, 1) != 1) {
+      status = 1;
+      break;
+    }
+
+    buffer[bufpos++] = c;
+
+    if (c == '\0')
+      continue;
+    if (backslash) {
+      backslash = 0;
+      if (c != '\n')
+        STPUTC(c, p);
+      continue;
+    }
+    if (eflag && c == '\\') {
+      backslash++;
+      continue;
+    }
+    if (c == '\n')
+      break;
+    if (startword && *ifs == ' ' && strchr(ifs, c)) {
+      continue;
+    }
+    startword = 0;
+    if (backslash && c == '\\') {
+      if (read(0, &c, 1) != 1) {
+        status = 1;
+        break;
+      }
+      STPUTC(c, p);
+    } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
+      STACKSTRNUL(p);
+      if(*ap) {
+        setvar(*ap, stackblock(), 0);
+        ap++;
+      }
+      startword = 1;
+      STARTSTACKSTR(p);
+    } else {
+      STPUTC(c, p);
+    }
+  }STACKSTRNUL(p);
+  STACKSTRNUL(p);
+  if(*ap == NULL) {
+    buffer[bufpos + 1] = '\0';
+    setvar("REPLY", buffer, 0);
+    if(buffer) free(buffer);
+    return status;
+  }
+  setvar(*ap, stackblock(), 0);
+  while (*++ap != NULL)
+    setvar(*ap, nullstr, 0);
+  if(buffer) free(buffer);
+  return status;
+}
+
+/*
+ * umask command.
+ */
+int umaskcmd(int argc, char **argv)
+{
+  int mask;
+  char *p;
+  int i, j;
+  int print_symbolically = 0;
+
+  while (nextopt("S") != '\0')
+    print_symbolically = 1;
+
+  if ((p = *argptr) == NULL) {
+    INTOFF;
+    mask = umask(0);
+    umask(mask);
+    INTON;
+    if(!print_symbolically)
+      out1fmt("%.4o\n", mask);  /* %#o might be better */
+  } else {
+    if (isdigit((unsigned char) *p)) {
+      mask = 0;
+      do {
+        if ((unsigned)(i = *p - '0') >= 8)
+          sh_error("Illegal number: %s", argv[1]);
+        mask = (mask << 3) + i;
+      } while (*++p != '\0');
+      umask(mask);
+    }
+    else {
+      INTOFF;
+      mask = umask(0);
+      umask(mask);
+      INTON;
+      mask = ~mask & 0777;
+      if(!m_parse(p, (mode_t *)&mask))
+        sh_error("Illegal mask: %s", p);
+      umask(~mask & 0777);
+    }
+  }
+
+  if(print_symbolically) {
+    const char who_chars[] = "ugo";
+    const char perms[] = "rwx";
+    const mode_t perm_mask[] = {
+        S_IRUSR, S_IWUSR, S_IXUSR,
+        S_IRGRP, S_IWGRP, S_IXGRP,
+        S_IROTH, S_IWOTH, S_IXOTH
+      };
+    char buf[20] = {0,};
+    int buf_index = 0;
+    i = j = 0;
+    while(j = 0, i < 3) {
+      buf[buf_index++] = who_chars[i];
+      buf[buf_index++] = '=';
+      while(j < 3) {
+        if ((mask & perm_mask[3 * i + j]) == 0)
+          buf[buf_index++] = perms[j];
+        j++;
+      }
+      if(++i != 3) {
+        buf[buf_index++] = ',';
+        buf[buf_index++] = ' ';
+      }
+    }
+    out1fmt("%s\n", buf);
+  }
+  return 0;
+}
+//==================================================================================
+//nodes - Routine for dealing with parsed shell commands.
+/*
+ * Make a copy of a parse tree.
+ */
+union node *copyfunc(union node *n)
+{
+  if (n == NULL)
+    return NULL;
+  funcblocksize = 0;
+  funcstringsize = 0;
+  calcsize(n);
+  funcblock = ckmalloc(funcblocksize + funcstringsize);
+  funcstring = funcblock + funcblocksize;
+  return copynode(n);
+}
+
+STATIC void calcsize(union node *n)
+{
+  if (n == NULL)
+    return;
+  funcblocksize += nodesize[n->type];
+  switch (n->type) {
+    case NSEMI:
+    case NAND:
+    case NOR:
+    case NWHILE:
+    case NUNTIL:
+      calcsize(n->nbinary.ch2);
+      calcsize(n->nbinary.ch1);
+      break;
+    case NCMD:
+      calcsize(n->ncmd.redirect);
+      calcsize(n->ncmd.args);
+      break;
+    case NPIPE:
+      sizenodelist(n->npipe.cmdlist);
+      break;
+    case NREDIR:
+    case NBACKGND:
+    case NSUBSHELL:
+      calcsize(n->nredir.redirect);
+      calcsize(n->nredir.n);
+      break;
+    case NIF:
+      calcsize(n->nif.elsepart);
+      calcsize(n->nif.ifpart);
+      calcsize(n->nif.test);
+      break;
+    case NFOR:
+      funcstringsize += strlen(n->nfor.var) + 1;
+      calcsize(n->nfor.body);
+      calcsize(n->nfor.args);
+      break;
+    case NCASE:
+      calcsize(n->ncase.cases);
+      calcsize(n->ncase.expr);
+      break;
+    case NCLIST:
+      calcsize(n->nclist.body);
+      calcsize(n->nclist.pattern);
+      calcsize(n->nclist.next);
+      break;
+    case NDEFUN:
+    case NARG:
+      sizenodelist(n->narg.backquote);
+      funcstringsize += strlen(n->narg.text) + 1;
+      calcsize(n->narg.next);
+      break;
+    case NTO:
+    case NFROM:
+    case NAPPEND:
+      calcsize(n->nfile.fname);
+      calcsize(n->nfile.next);
+      break;
+    case NTOFD:
+    case NFROMFD:
+      calcsize(n->ndup.next);
+      break;
+    case NHERE:
+    case NXHERE:
+      calcsize(n->nhere.doc);
+      calcsize(n->nhere.next);
+      break;
+    };
+}
+
+STATIC void sizenodelist(struct nodelist *lp)
+{
+  while (lp) {
+    funcblocksize += ALIGN(sizeof (struct nodelist));
+    calcsize(lp->n);
+    lp = lp->next;
+  }
+}
+
+STATIC union node *copynode(union node *n)
+{
+  union node *new;
+
+  if (n == NULL)
+    return NULL;
+  new = funcblock;
+  funcblock += nodesize[n->type];
+  switch (n->type) {
+    case NSEMI:
+    case NAND:
+    case NOR:
+    case NWHILE:
+    case NUNTIL:
+      new->nbinary.ch2 = copynode(n->nbinary.ch2);
+      new->nbinary.ch1 = copynode(n->nbinary.ch1);
+      break;
+    case NCMD:
+      new->ncmd.redirect = copynode(n->ncmd.redirect);
+      new->ncmd.args = copynode(n->ncmd.args);
+      new->ncmd.backgnd = n->ncmd.backgnd;
+      break;
+    case NPIPE:
+      new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+      new->npipe.backgnd = n->npipe.backgnd;
+      break;
+    case NREDIR:
+    case NBACKGND:
+    case NSUBSHELL:
+      new->nredir.redirect = copynode(n->nredir.redirect);
+      new->nredir.n = copynode(n->nredir.n);
+      break;
+    case NIF:
+      new->nif.elsepart = copynode(n->nif.elsepart);
+      new->nif.ifpart = copynode(n->nif.ifpart);
+      new->nif.test = copynode(n->nif.test);
+      break;
+    case NFOR:
+      new->nfor.var = nodesavestr(n->nfor.var);
+      new->nfor.body = copynode(n->nfor.body);
+      new->nfor.args = copynode(n->nfor.args);
+      break;
+    case NCASE:
+      new->ncase.cases = copynode(n->ncase.cases);
+      new->ncase.expr = copynode(n->ncase.expr);
+      break;
+    case NCLIST:
+      new->nclist.body = copynode(n->nclist.body);
+      new->nclist.pattern = copynode(n->nclist.pattern);
+      new->nclist.next = copynode(n->nclist.next);
+      break;
+    case NDEFUN:
+    case NARG:
+      new->narg.backquote = copynodelist(n->narg.backquote);
+      new->narg.text = nodesavestr(n->narg.text);
+      new->narg.next = copynode(n->narg.next);
+      break;
+    case NTO:
+    case NFROM:
+    case NAPPEND:
+      new->nfile.fname = copynode(n->nfile.fname);
+      new->nfile.fd = n->nfile.fd;
+      new->nfile.next = copynode(n->nfile.next);
+      break;
+    case NTOFD:
+    case NFROMFD:
+      new->ndup.dupfd = n->ndup.dupfd;
+      new->ndup.fd = n->ndup.fd;
+      new->ndup.next = copynode(n->ndup.next);
+      break;
+    case NHERE:
+    case NXHERE:
+      new->nhere.doc = copynode(n->nhere.doc);
+      new->nhere.fd = n->nhere.fd;
+      new->nhere.next = copynode(n->nhere.next);
+      break;
+  };
+  new->type = n->type;
+  return new;
+}
+
+STATIC struct nodelist *copynodelist(struct nodelist *lp)
+{
+  struct nodelist *start;
+  struct nodelist **lpp;
+
+  lpp = &start;
+  while (lp) {
+    *lpp = funcblock;
+    funcblock += ALIGN(sizeof (struct nodelist));
+    (*lpp)->n = copynode(lp->n);
+    lp = lp->next;
+    lpp = &(*lpp)->next;
+  }
+  *lpp = NULL;
+  return start;
+}
+
+STATIC char *nodesavestr(char *s)
+{
+  register char *p = s;
+  register char *q = funcstring;
+  char *rtn = funcstring;
+
+  while ((*q++ = *p++));
+  funcstring = q;
+  return rtn;
+}
+
+/*
+ * Free a parse tree.
+ */
+void freefunc(union node *n)
+{
+  if (n)
+    ckfree(n);
+}
+
+//==================================================================================
+//var - environment variable (get, set etc.).
+/*
+ * This routine initializes the builtin variables.  It is called when the
+ * shell is initialized and again when a shell procedure is spawned.
+ */
+void initvar()
+{
+  const struct varinit *ip;
+  struct var *vp;
+  struct var **vpp;
+
+  for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
+    if ((vp->flags & VEXPORT) == 0) {
+      vpp = hashvar(ip->text);
+      vp->next = *vpp;
+      *vpp = vp;
+      vp->text = ip->text;
+      vp->flags = ip->flags;
+    }
+  }
+  /*
+   * PS1 depends on uid
+   */
+  if ((vps1.flags & VEXPORT) == 0) {
+    vpp = hashvar("PS1=");
+    vps1.next = *vpp;
+    *vpp = &vps1;
+    vps1.text = getuid() ? "PS1=$ " : "PS1=# ";
+    vps1.flags = VSTRFIXED|VTEXTFIXED;
+  }
+}
+
+/*
+ * Set the value of a variable.  The flags argument is ored with the
+ * flags of the variable.  If val is NULL, the variable is unset.
+ */
+void setvar(char *name, char *val, int flags)
+{
+  char *p, *q;
+  int len;
+  int namelen;
+  char *nameeq;
+  int isbad;
+
+  isbad = 0;
+  p = name;
+  if (! is_name(*p++))
+    isbad = 1;
+  for (;;) {
+    if (! is_in_name(*p)) {
+      if (*p == '\0' || *p == '=')
+        break;
+      isbad = 1;
+    }
+    p++;
+  }
+  namelen = p - name;
+  if (isbad)
+    sh_error("%.*s: is read only", namelen, name);
+  len = namelen + 2;    /* 2 is space for '=' and '\0' */
+  if (val == NULL) {
+    flags |= VUNSET;
+  } else {
+    len += strlen(val);
+  }
+  p = nameeq = ckmalloc(len);
+  q = name;
+  while (--namelen >= 0)
+    *p++ = *q++;
+  *p++ = '=';
+  *p = '\0';
+  if (val)
+    scopy(val, p);
+  setvareq(nameeq, flags);
+}
+
+/*
+ * Same as setvar except that the variable and value are passed in
+ * the first argument as name=value.  Since the first argument will
+ * be actually stored in the table, it should not be a string that
+ * will go away.
+ */
+void setvareq(char *s, int flags)
+{
+  struct var *vp, **vpp;
+
+  vpp = hashvar(s);
+  flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
+  for (vp = *vpp ; vp ; vp = vp->next) {
+    if (varequal(s, vp->text)) {
+      if (vp->flags & VREADONLY) {
+        int len = strchr(s, '=') - s;
+        sh_error("%.*s: is read only", len, s);
+      }
+      INTOFF;
+      if (vp == &vpath)
+        changepath(s + 5);  /* 5 = strlen("PATH=") */
+      if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+        ckfree(vp->text);
+      vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET);
+      vp->flags |= flags;
+      vp->text = s;
+      if (vp == &vmpath || (vp == &vmail && ! mpathset()))
+        chkmail(1);
+      INTON;
+      return;
+    }
+  }
+  /* not found */
+  vp = ckmalloc(sizeof (*vp));
+  vp->flags = flags;
+  vp->text = s;
+  vp->next = *vpp;
+  *vpp = vp;
+}
+
+/*
+ * Process a linked list of variable assignments.
+ */
+void listsetvar(struct strlist *list)
+{
+  struct strlist *lp;
+
+  INTOFF;
+  for (lp = list ; lp ; lp = lp->next) {
+    setvareq(savestr(lp->text), 0);
+  }
+  INTON;
+}
+
+/*
+ * Find the value of a variable.  Returns NULL if not set.
+ */
+char *lookupvar(char *name)
+{
+  struct var *v;
+
+  for (v = *hashvar(name) ; v ; v = v->next) {
+    if (varequal(v->text, name)) {
+      if (v->flags & VUNSET)
+        return NULL;
+      return strchr(v->text, '=') + 1;
+    }
+  }
+  return NULL;
+}
+
+/*
+ * Search the environment of a builtin command.  If the second argument
+ * is nonzero, return the value of a variable even if it hasn't been
+ * exported.
+ */
+char *bltinlookup(char *name, int doall)
+{
+  struct strlist *sp;
+  struct var *v;
+
+  for (sp = cmdenviron ; sp ; sp = sp->next) {
+    if (varequal(sp->text, name))
+      return strchr(sp->text, '=') + 1;
+  }
+  for (v = *hashvar(name) ; v ; v = v->next) {
+    if (varequal(v->text, name)) {
+      if ((v->flags & VUNSET)
+       || (! doall && (v->flags & VEXPORT) == 0))
+        return NULL;
+      return strchr(v->text, '=') + 1;
+    }
+  }
+  return NULL;
+}
+
+/*
+ * Generate a list of exported variables.  This routine is used to construct
+ * the third argument to execve when executing a program.
+ */
+char **environment()
+{
+  int nenv;
+  struct var **vpp;
+  struct var *vp;
+  char **env, **ep;
+
+  nenv = 0;
+  for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+    for (vp = *vpp ; vp ; vp = vp->next)
+      if (vp->flags & VEXPORT)
+        nenv++;
+  }
+  ep = env = stalloc((nenv + 1) * sizeof *env);
+  for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+    for (vp = *vpp ; vp ; vp = vp->next)
+      if (vp->flags & VEXPORT)
+        *ep++ = vp->text;
+  }
+  *ep = NULL;
+  return env;
+}
+
+/*
+ * Called when a shell procedure is invoked to clear out nonexported
+ * variables.  It is also necessary to reallocate variables of with
+ * VSTACK set since these are currently allocated on the stack.
+ */
+void shprocvar()
+{
+  struct var **vpp;
+  struct var *vp, **prev;
+
+  for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+    for (prev = vpp ; (vp = *prev) != NULL ; ) {
+      if ((vp->flags & VEXPORT) == 0) {
+        *prev = vp->next;
+        if ((vp->flags & VTEXTFIXED) == 0)
+          ckfree(vp->text);
+        if ((vp->flags & VSTRFIXED) == 0)
+          ckfree(vp);
+      } else {
+        if (vp->flags & VSTACK) {
+          vp->text = savestr(vp->text);
+          vp->flags &=~ VSTACK;
+        }
+        prev = &vp->next;
+      }
+    }
+  }
+  initvar();
+}
+
+/*
+ * Command to list all variables which are set.  Currently this command
+ * is invoked from the set command when the set command is called without
+ * any variables.
+ */
+int showvarscmd(int argc, char **argv)
+{
+  struct var **vpp;
+  struct var *vp;
+
+  for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+    for (vp = *vpp ; vp ; vp = vp->next) {
+      if ((vp->flags & VUNSET) == 0)
+        out1fmt("%s\n", vp->text);
+    }
+  }
+  return 0;
+}
+
+/*
+ * The export and readonly commands.
+ */
+int exportcmd(int argc, char **argv)
+{
+  struct var **vpp;
+  struct var *vp;
+  char *name;
+  char *p;
+  int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+
+  listsetvar(cmdenviron);
+  if (argc > 1) {
+    while ((name = *argptr++) != NULL) {
+      if ((p = strchr(name, '=')) != NULL) {
+        p++;
+      } else {
+        vpp = hashvar(name);
+        for (vp = *vpp ; vp ; vp = vp->next) {
+          if (varequal(vp->text, name)) {
+            vp->flags |= flag;
+            goto found;
+          }
+        }
+      }
+      setvar(name, p, flag);
+found:;
+    }
+  } else {
+    for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+      for (vp = *vpp ; vp ; vp = vp->next) {
+        if (vp->flags & flag) {
+          out1str("export ");
+          out1str(vp->text);
+          out1c('\n');
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+/*
+ * The "local" command.
+ */
+int localcmd(int argc, char **argv)
+{
+  char *name;
+
+  while ((name = *argptr++) != NULL) {
+    mklocal(name);
+  }
+  return 0;
+}
+
+/*
+ * Make a variable a local variable.  When a variable is made local, it's
+ * value and flags are saved in a localvar structure.  The saved values
+ * will be restored when the shell function returns.  We handle the name
+ * "-" as a special case.
+ */
+void mklocal(char *name)
+{
+  struct localvar *lvp;
+  struct var **vpp;
+  struct var *vp;
+
+  INTOFF;
+  lvp = ckmalloc(sizeof (struct localvar));
+  if (name[0] == '-' && name[1] == '\0') {
+    lvp->text = ckmalloc(sizeof optval);
+    bcopy(optval, lvp->text, sizeof optval);
+    vp = NULL;
+  } else {
+    vpp = hashvar(name);
+    for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
+    if (vp == NULL) {
+      if (strchr(name, '='))
+        setvareq(savestr(name), VSTRFIXED);
+      else
+        setvar(name, NULL, VSTRFIXED);
+      vp = *vpp;  /* the new variable */
+      lvp->text = NULL;
+      lvp->flags = VUNSET;
+    } else {
+      lvp->text = vp->text;
+      lvp->flags = vp->flags;
+      vp->flags |= VSTRFIXED|VTEXTFIXED;
+      if (strchr(name, '='))
+        setvareq(savestr(name), 0);
+    }
+  }
+  lvp->vp = vp;
+  lvp->next = localvars;
+  localvars = lvp;
+  INTON;
+}
+
+/*
+ * Called after a function returns.
+ */
+void poplocalvars()
+{
+  struct localvar *lvp;
+  struct var *vp;
+
+  while ((lvp = localvars) != NULL) {
+    localvars = lvp->next;
+    vp = lvp->vp;
+    if (vp == NULL) {  /* $- saved */
+      bcopy(lvp->text, optval, sizeof optval);
+      ckfree(lvp->text);
+    } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+      unsetvar(vp->text);
+    } else {
+      if ((vp->flags & VTEXTFIXED) == 0)
+        ckfree(vp->text);
+      vp->flags = lvp->flags;
+      vp->text = lvp->text;
+    }
+    ckfree(lvp);
+  }
+}
+
+int setvarcmd(int argc, char **argv)
+{
+  if (argc <= 2)
+    return unsetcmd(argc, argv);
+  else if (argc == 3)
+    setvar(argv[1], argv[2], 0);
+  else
+    sh_error("List assignment not implemented");
+  return 0;
+}
+
+/*
+ * The unset builtin command.  We unset the function before we unset the
+ * variable to allow a function to be unset when there is a readonly variable
+ * with the same name.
+ */
+int unsetcmd(int argc, char **argv)
+{
+  char **ap;
+  int i;
+  int flg_func = 0;
+  int flg_var = 0;
+
+  while ((i = nextopt("vf")) != 0) {
+    if (i == 'f')
+      flg_func = 1;
+    else
+      flg_var = i;
+  }
+
+  if (flg_func == 0 && flg_var == 0)
+    flg_var = 1;
+
+  for (ap = argv + 1 ; *ap ; ap++) {
+     if (flg_func)
+    unsetfunc(*ap);
+     if (flg_var)
+    unsetvar(*ap);
+  }
+  return 0;
+}
+
+/*
+ * Unset the specified variable.
+ */
+STATIC void unsetvar(char *s)
+{
+  struct var **vpp;
+  struct var *vp;
+
+  vpp = hashvar(s);
+  for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
+    if (varequal(vp->text, s)) {
+      INTOFF;
+      if (*(strchr(vp->text, '=') + 1) != '\0'
+       || vp->flags & VREADONLY) {
+        setvar(s, nullstr, 0);
+      }
+      vp->flags &=~ VEXPORT;
+      vp->flags |= VUNSET;
+      if ((vp->flags & VSTRFIXED) == 0) {
+        if ((vp->flags & VTEXTFIXED) == 0)
+          ckfree(vp->text);
+        *vpp = vp->next;
+        ckfree(vp);
+      }
+      INTON;
+      return;
+    }
+  }
+}
+
+/*
+ * Find the appropriate entry in the hash table from the name.
+ */
+STATIC struct var **hashvar(register char *p)
+{
+  unsigned int hashval;
+
+  hashval = *p << 4;
+  while (*p && *p != '=')
+    hashval += *p++;
+  return &vartab[hashval % VTABSIZE];
+}
+
+/*
+ * Returns true if the two strings specify the same varable.  The first
+ * variable name is terminated by '='; the second may be terminated by
+ * either '=' or '\0'.
+ */
+STATIC int varequal(register char *p, register char *q)
+{
+  while (*p == *q++) {
+    if (*p++ == '=')
+      return 1;
+  }
+  if (*p == '=' && *(q - 1) == '\0')
+    return 1;
+  return 0;
+}
+
+//==================================================================================
+//output - formating outputs.
+/*
+ * Shell output routines.  We use our own output routines because:
+ *  When a builtin command is interrupted we have to discard
+ *    any pending output.
+ *  When a builtin command appears in back quotes, we want to
+ *    save the output of the command in a region obtained
+ *    via malloc, rather than doing a fork and reading the
+ *    output of the command via a pipe.
+ *  Our output routines may be smaller than the stdio routines.
+ */
+#ifdef notdef  /* no longer used */
+/*
+ * Set up an output file to write to memory rather than a file.
+ */
+void open_mem(char *block, int length, struct output *file)
+{
+  file->nextc = block;
+  file->nleft = --length;
+  file->fd = BLOCK_OUT;
+  file->flags = 0;
+}
+#endif
+
+
+void out1str(char *p)
+{
+  outstr(p, out1);
+}
+
+void out2str(char *p)
+{
+  outstr(p, out2);
+}
+
+void outstr(register char *p, register struct output *file)
+{
+  while (*p)
+    outc(*p++, file);
+}
+
+char out_junk[16];
+
+void emptyoutbuf(struct output *dest)
+{
+  int offset;
+
+  if (dest->fd == BLOCK_OUT) {
+    dest->nextc = out_junk;
+    dest->nleft = sizeof out_junk;
+    dest->flags |= OUTPUT_ERR;
+  } else if (dest->buf == NULL) {
+    INTOFF;
+    dest->buf = ckmalloc(dest->bufsize);
+    dest->nextc = dest->buf;
+    dest->nleft = dest->bufsize;
+    INTON;
+  } else if (dest->fd == MEM_OUT) {
+    offset = dest->bufsize;
+    INTOFF;
+    dest->bufsize <<= 1;
+    dest->buf = ckrealloc(dest->buf, dest->bufsize);
+    dest->nleft = dest->bufsize - offset;
+    dest->nextc = dest->buf + offset;
+    INTON;
+  } else {
+    flushout(dest);
+  }
+  dest->nleft--;
+}
+
+void flushall()
+{
+  flushout(&output);
+  flushout(&errout);
+}
+
+void flushout(struct output *dest)
+{
+  if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
+    return;
+  if (xxwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
+    dest->flags |= OUTPUT_ERR;
+  dest->nextc = dest->buf;
+  dest->nleft = dest->bufsize;
+}
+
+void freestdout()
+{
+  INTOFF;
+  if (output.buf) {
+    ckfree(output.buf);
+    output.buf = NULL;
+    output.nleft = 0;
+  }
+  INTON;
+}
+
+#ifdef __STDC__
+void outfmt(struct output *file, char *fmt, ...)
+{
+  va_list ap;
+
+  va_start(ap, fmt);
+  doformat(file, fmt, ap);
+  va_end(ap);
+}
+
+void out1fmt(char *fmt, ...)
+{
+  va_list ap;
+
+  va_start(ap, fmt);
+  doformat(out1, fmt, ap);
+  va_end(ap);
+}
+
+void fmtstr(char *outbuf, int length, char *fmt, ...)
+{
+  va_list ap;
+  struct output strout;
+
+  va_start(ap, fmt);
+  strout.nextc = outbuf;
+  strout.nleft = length;
+  strout.fd = BLOCK_OUT;
+  strout.flags = 0;
+  doformat(&strout, fmt, ap);
+  outc('\0', &strout);
+  if (strout.flags & OUTPUT_ERR)
+    outbuf[length - 1] = '\0';
+}
+
+#else /* not __STDC__ */
+
+void outfmt(va_alist)
+  va_dcl
+{
+  va_list ap;
+  struct output *file;
+  char *fmt;
+
+  va_start(ap);
+  file = va_arg(ap, struct output *);
+  fmt = va_arg(ap, char *);
+  doformat(file, fmt, ap);
+  va_end(ap);
+}
+
+void out1fmt(va_alist)
+  va_dcl
+{
+  va_list ap;
+  char *fmt;
+
+  va_start(ap);
+  fmt = va_arg(ap, char *);
+  doformat(out1, fmt, ap);
+  va_end(ap);
+}
+
+void fmtstr(va_alist)
+  va_dcl
+{
+  va_list ap;
+  struct output strout;
+  char *outbuf;
+  int length;
+  char *fmt;
+
+  va_start(ap);
+  outbuf = va_arg(ap, char *);
+  length = va_arg(ap, int);
+  fmt = va_arg(ap, char *);
+  strout.nextc = outbuf;
+  strout.nleft = length;
+  strout.fd = BLOCK_OUT;
+  strout.flags = 0;
+  doformat(&strout, fmt, ap);
+  outc('\0', &strout);
+  if (strout.flags & OUTPUT_ERR)
+    outbuf[length - 1] = '\0';
+}
+#endif /* __STDC__ */
+
+/*
+ * Formatted output.  This routine handles a subset of the printf formats:
+ * - Formats supported: d, u, o, X, s, and c.
+ * - The x format is also accepted but is treated like X.
+ * - The l modifier is accepted.
+ * - The - and # flags are accepted; # only works with the o format.
+ * - Width and precision may be specified with any format except c.
+ * - An * may be given for the width or precision.
+ * - The obsolete practice of preceding the width with a zero to get
+ *   zero padding is not supported; use the precision field.
+ * - A % may be printed by writing %% in the format string.
+ */
+void doformat(register struct output *dest, register char *f, va_list ap)
+{
+  register char c;
+  char temp[TEMPSIZE];
+  int flushleft;
+  int sharp;
+  int width;
+  int prec;
+  int islong;
+  char *p;
+  int sign;
+  long l;
+  unsigned long num;
+  unsigned base;
+  int len;
+  int size;
+  int pad;
+
+  while ((c = *f++) != '\0') {
+    if (c != '%') {
+      outc(c, dest);
+      continue;
+    }
+    flushleft = 0;
+    sharp = 0;
+    width = 0;
+    prec = -1;
+    islong = 0;
+    for (;;) {
+      if (*f == '-')
+        flushleft++;
+      else if (*f == '#')
+        sharp++;
+      else
+        break;
+      f++;
+    }
+    if (*f == '*') {
+      width = va_arg(ap, int);
+      f++;
+    } else {
+      while (is_digit(*f)) {
+        width = 10 * width + digit_val(*f++);
+      }
+    }
+    if (*f == '.') {
+      if (*++f == '*') {
+        prec = va_arg(ap, int);
+        f++;
+      } else {
+        prec = 0;
+        while (is_digit(*f)) {
+          prec = 10 * prec + digit_val(*f++);
+        }
+      }
+    }
+    if (*f == 'l') {
+      islong++;
+      f++;
+    }
+    switch (*f) {
+    case 'd':
+      if (islong)
+        l = va_arg(ap, long);
+      else
+        l = va_arg(ap, int);
+      sign = 0;
+      num = l;
+      if (l < 0) {
+        num = -l;
+        sign = 1;
+      }
+      base = 10;
+      goto number;
+    case 'u':
+      base = 10;
+      goto uns_number;
+    case 'o':
+      base = 8;
+      goto uns_number;
+    case 'x':
+      /* we don't implement 'x'; treat like 'X' */
+    case 'X':
+      base = 16;
+uns_number:    /* an unsigned number */
+      sign = 0;
+      if (islong)
+        num = va_arg(ap, unsigned long);
+      else
+        num = va_arg(ap, unsigned int);
+number:      /* process a number */
+      p = temp + TEMPSIZE - 1;
+      *p = '\0';
+      while (num) {
+        *--p = digit[num % base];
+        num /= base;
+      }
+      len = (temp + TEMPSIZE - 1) - p;
+      if (prec < 0)
+        prec = 1;
+      if (sharp && *f == 'o' && prec <= len)
+        prec = len + 1;
+      pad = 0;
+      if (width) {
+        size = len;
+        if (size < prec)
+          size = prec;
+        size += sign;
+        pad = width - size;
+        if (flushleft == 0) {
+          while (--pad >= 0)
+            outc(' ', dest);
+        }
+      }
+      if (sign)
+        outc('-', dest);
+      prec -= len;
+      while (--prec >= 0)
+        outc('0', dest);
+      while (*p)
+        outc(*p++, dest);
+      while (--pad >= 0)
+        outc(' ', dest);
+      break;
+    case 's':
+      p = va_arg(ap, char *);
+      pad = 0;
+      if (width) {
+        len = strlen(p);
+        if (prec >= 0 && len > prec)
+          len = prec;
+        pad = width - len;
+        if (flushleft == 0) {
+          while (--pad >= 0)
+            outc(' ', dest);
+        }
+      }
+      prec++;
+      while (--prec != 0 && *p)
+        outc(*p++, dest);
+      while (--pad >= 0)
+        outc(' ', dest);
+      break;
+    case 'c':
+      c = va_arg(ap, int);
+      outc(c, dest);
+      break;
+    default:
+      outc(*f, dest);
+      break;
+    }
+    f++;
+  }
+}
+
+/*
+ * Version of write which resumes after a signal is caught.
+ */
+int xxwrite(int fd, char *buf, int nbytes)
+{
+  int ntry;
+  int i;
+  int n;
+
+  n = nbytes;
+  ntry = 0;
+  for (;;) {
+    i = write(fd, buf, n);
+    if (i > 0) {
+      if ((n -= i) <= 0)
+        return nbytes;
+      buf += i;
+      ntry = 0;
+    } else if (i == 0) {
+      if (++ntry > 10)
+        return nbytes - n;
+    } else if (errno != EINTR) {
+      return -1;
+    }
+  }
+  /*Not reached*/
+  return 0;
+}
+
+//==================================================================================
+//trap - commmand to trap signals.
+/*
+ * Sigmode records the current value of the signal handlers for the various
+ * modes.  A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ */
+
+/*
+ * The trap builtin.
+ */
+int trapcmd(int argc, char **argv)
+{
+  char *action;
+  char **ap;
+  int signo;
+
+  if (argc <= 1) {
+    for (signo = 0 ; signo <= MAXSIG ; signo++) {
+      if (trap[signo] != NULL)
+        out1fmt("%d: %s\n", signo, trap[signo]);
+    }
+    return 0;
+  }
+  ap = argv + 1;
+  if (is_number(*ap))
+    action = NULL;
+  else
+    action = *ap++;
+  while (*ap) {
+    if ((signo = number(*ap)) < 0 || signo > MAXSIG)
+      sh_error("%s: bad trap", *ap);
+    INTOFF;
+    if (action)
+      action = savestr(action);
+    if (trap[signo])
+      ckfree(trap[signo]);
+    trap[signo] = action;
+    if (signo != 0)
+      setsignal(signo);
+    INTON;
+    ap++;
+  }
+  return 0;
+}
+
+/*
+ * Clear traps on a fork.
+ */
+void clear_traps()
+{
+  char **tp;
+
+  for (tp = trap ; tp <= &trap[MAXSIG] ; tp++) {
+    if (*tp && **tp) {  /* trap not NULL or SIG_IGN */
+      INTOFF;
+      ckfree(*tp);
+      *tp = NULL;
+      if (tp != &trap[0])
+        setsignal(tp - trap);
+      INTON;
+    }
+  }
+}
+
+/*
+ * Set the signal handler for the specified signal.  The routine figures
+ * out what it should be set to.
+ */
+int setsignal(int signo)
+{
+  int action;
+  sig_t sigact = 0;
+  char *t;
+  extern void onsig();
+
+  if ((t = trap[signo]) == NULL)
+    action = S_DFL;
+  else if (*t != '\0')
+    action = S_CATCH;
+  else
+    action = S_IGN;
+  if (rootshell && action == S_DFL) {
+    switch (signo) {
+    case SIGINT:
+      if (iflag)
+        action = S_CATCH;
+      break;
+    case SIGQUIT:
+#ifdef DEBUG
+      {
+      extern int debug;
+
+      if (debug)
+        break;
+      }
+#endif
+      /* FALLTHROUGH */
+    case SIGTERM:
+      if (iflag)
+        action = S_IGN;
+      break;
+#if JOBS
+    case SIGTSTP:
+    case SIGTTOU:
+      if (jflag)
+        action = S_IGN;
+      break;
+#endif
+    }
+  }
+  t = &sigmode[signo - 1];
+  if (*t == 0) {  /* current setting unknown */
+    /*
+     * There is a race condition here if action is not S_IGN.
+     * A signal can be ignored that shouldn't be.
+     */
+    if ((int)(sigact = signal(signo, SIG_IGN)) == -1)
+      sh_error("Signal system call failed");
+    if (sigact == SIG_IGN) {
+      *t = S_HARD_IGN;
+    } else {
+      *t = S_IGN;
+    }
+  }
+  if (*t == S_HARD_IGN || *t == action)
+    return 0;
+  switch (action) {
+    case S_DFL:  sigact = SIG_DFL;  break;
+    case S_CATCH:    sigact = onsig;    break;
+    case S_IGN:  sigact = SIG_IGN;  break;
+  }
+  *t = action;
+  return (int)signal(signo, sigact);
+}
+
+/*
+ * Ignore a signal.
+ */
+void ignoresig(int signo)
+{
+  if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
+    signal(signo, SIG_IGN);
+  }
+  sigmode[signo - 1] = S_HARD_IGN;
+}
+
+/*
+ * Signal handler.
+ */
+void onsig(int signo)
+{
+  struct termios set;
+  tcgetattr(0,&set);
+  set.c_lflag |= (ICANON | ECHO);
+  tcsetattr(0, TCSANOW, &set);
+  signal(signo, onsig);
+  if (signo == SIGINT && trap[SIGINT] == NULL) {
+    onint();
+    return;
+  }
+  gotsig[signo - 1] = 1;
+  pendingsigs++;
+}
+
+/*
+ * Called to execute a trap.  Perhaps we should avoid entering new trap
+ * handlers while we are executing a trap handler.
+ */
+void dotrap()
+{
+  int i;
+  int savestatus;
+
+  for (;;) {
+    for (i = 1 ; ; i++) {
+      if (gotsig[i - 1])
+        break;
+      if (i >= MAXSIG)
+        goto done;
+    }
+    gotsig[i - 1] = 0;
+    savestatus=exitstatus;
+    evalstring(trap[i]);
+    exitstatus=savestatus;
+  }
+done:
+  pendingsigs = 0;
+}
+
+/*
+ * Controls whether the shell is interactive or not.
+ */
+void setinteractive(int on)
+{
+  if (on == is_interactive)
+    return;
+  setsignal(SIGINT);
+  setsignal(SIGQUIT);
+  setsignal(SIGTERM);
+  is_interactive = on;
+}
+
+#if DEBUG == 2
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+int trace(fmt, a1, a2, a3, a4, a5, a6, a7, a8)
+  char *fmt;
+{
+#ifdef DEBUG
+  if (tracefile == NULL)
+    return -1;
+  fprintf(tracefile, fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+  if (strchr(fmt, '\n'))
+    fflush(tracefile);
+#endif
+  return 0;
+}
+/*
+ * Called to exit the shell.
+ */
+void exitshell(int status)
+{
+  struct jmploc loc1, loc2;
+  char *p;
+
+  save_history();
+  TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
+  if (setjmp(loc1.loc))  goto l1;
+  if (setjmp(loc2.loc))  goto l2;
+  handler = &loc1;
+  if ((p = trap[0]) != NULL && *p != '\0') {
+    trap[0] = NULL;
+    evalstring(p);
+  }
+l1:   handler = &loc2;      /* probably unnecessary */
+  flushall();
+#if JOBS
+  setjobctl(0);
+#endif
+l2:   exit(status);
+}
+
+//==================================================================================
+//show - used for debug printing.
+#ifdef DEBUG
+static void shtree();
+static void shcmd();
+static void sharg();
+static void indent();
+
+void showtree(union node *n)
+{
+  trputs("showtree called\n");
+  shtree(n, 1, NULL, stdout);
+}
+
+static void shtree(union node *n, int ind, char *pfx, FILE *fp)
+{
+  struct nodelist *lp;
+  char *s;
+
+  indent(ind, pfx, fp);
+  switch(n->type) {
+  case NSEMI:
+    s = "; ";
+    goto binop;
+  case NAND:
+    s = " && ";
+    goto binop;
+  case NOR:
+    s = " || ";
+binop:
+    shtree(n->nbinary.ch1, ind, NULL, fp);
+     /*  if (ind < 0) */
+      fputs(s, fp);
+    shtree(n->nbinary.ch2, ind, NULL, fp);
+    break;
+  case NCMD:
+    shcmd(n, fp);
+    if (ind >= 0)
+      putc('\n', fp);
+    break;
+  case NPIPE:
+    for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+      shcmd(lp->n, fp);
+      if (lp->next)
+        fputs(" | ", fp);
+    }
+    if (n->npipe.backgnd)
+      fputs(" &", fp);
+    if (ind >= 0)
+      putc('\n', fp);
+    break;
+  default:
+    fprintf(fp, "<node type %d>", n->type);
+    if (ind >= 0)
+      putc('\n', fp);
+    break;
+  }
+}
+
+static void shcmd(union node *cmd, FILE *fp)
+{
+  union node *np;
+  int first;
+  char *s = NULL;
+  int dftfd = 0;
+
+  first = 1;
+  for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
+    if (! first)
+      putchar(' ');
+    sharg(np, fp);
+    first = 0;
+  }
+  for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
+    if (! first)
+      putchar(' ');
+    switch (np->nfile.type) {
+      case NTO:  s = ">";  dftfd = 1; break;
+      case NAPPEND:  s = ">>"; dftfd = 1; break;
+      case NTOFD:  s = ">&"; dftfd = 1; break;
+      case NFROM:  s = "<";  dftfd = 0; break;
+      case NFROMFD:  s = "<&"; dftfd = 0; break;
+    }
+    if (np->nfile.fd != dftfd)
+      fprintf(fp, "%d", np->nfile.fd);
+    fputs(s, fp);
+    if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
+      fprintf(fp, "%d", np->ndup.dupfd);
+    } else {
+      sharg(np->nfile.fname, fp);
+    }
+    first = 0;
+  }
+}
+
+static void sharg(union node *arg, FILE *fp)
+{
+  char *p;
+  struct nodelist *bqlist;
+  int subtype;
+
+  if (arg->type != NARG) {
+    printf("<node type %d>\n", arg->type);
+    fflush(stdout);
+    abort();
+  }
+  bqlist = arg->narg.backquote;
+  for (p = arg->narg.text ; *p ; p++) {
+    switch (*p) {
+    case CTLESC:
+      putc(*++p, fp);
+      break;
+    case CTLVAR:
+      putc('$', fp);
+      putc('{', fp);
+      subtype = *++p;
+      if (subtype == VSLENGTH)
+        putc('#', fp);
+      while (*p != '=')
+        putc(*p++, fp);
+      if (subtype & VSNUL)
+        putc(':', fp);
+      switch (subtype & VSTYPE) {
+      case VSNORMAL:
+        putc('}', fp);
+        break;
+      case VSMINUS:
+        putc('-', fp);
+        break;
+      case VSPLUS:
+        putc('+', fp);
+        break;
+      case VSQUESTION:
+        putc('?', fp);
+        break;
+      case VSASSIGN:
+        putc('=', fp);
+        break;
+      case VSTRIMLEFT:
+        putc('#', fp);
+        break;
+      case VSTRIMLEFTMAX:
+        putc('#', fp);
+        putc('#', fp);
+        break;
+      case VSTRIMRIGHT:
+        putc('%', fp);
+        break;
+      case VSTRIMRIGHTMAX:
+        putc('%', fp);
+        putc('%', fp);
+        break;
+      case VSLENGTH:
+        break;
+      default:
+        printf("<subtype %d>", subtype);
+      }
+      break;
+    case CTLENDVAR:
+       putc('}', fp);
+       break;
+    case CTLBACKQ:
+    case CTLBACKQ|CTLQUOTE:
+      putc('$', fp);
+      putc('(', fp);
+      shtree(bqlist->n, -1, NULL, fp);
+      putc(')', fp);
+      break;
+    default:
+      putc(*p, fp);
+      break;
+    }
+  }
+}
+
+static void indent(int amount, char *pfx, FILE *fp)
+{
+  int i;
+
+  for (i = 0 ; i < amount ; i++) {
+    if (pfx && i == amount - 1)
+      fputs(pfx, fp);
+    putc('\t', fp);
+  }
+}
+#endif
+
+/*
+ * Debugging stuff.
+ */
+void trputc(int c)
+{
+#ifdef DEBUG
+  if (tracefile == NULL)
+    return;
+  putc(c, tracefile);
+  if (c == '\n')
+    fflush(tracefile);
+#endif
+}
+
+int trputs(char *s)
+{
+#ifdef DEBUG
+  if (tracefile == NULL)
+    return -1;
+  fputs(s, tracefile);
+  if (strchr(s, '\n'))
+    fflush(tracefile);
+#endif
+  return 0;
+}
+
+void trstring(char *s)
+{
+  register char *p;
+  char c;
+
+#ifdef DEBUG
+  if (tracefile == NULL)
+    return;
+  putc('"', tracefile);
+  for (p = s ; *p ; p++) {
+    switch (*p) {
+    case '\n':  c = 'n';  goto backslash;
+    case '\t':  c = 't';  goto backslash;
+    case '\r':  c = 'r';  goto backslash;
+    case '"':  c = '"';  goto backslash;
+    case '\\':  c = '\\';  goto backslash;
+    case CTLESC:  c = 'e';  goto backslash;
+    case CTLVAR:  c = 'v';  goto backslash;
+    case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
+    case CTLBACKQ:  c = 'q';  goto backslash;
+    case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
+backslash:    putc('\\', tracefile);
+      putc(c, tracefile);
+      break;
+    default:
+      if (*p >= ' ' && *p <= '~')
+        putc(*p, tracefile);
+      else {
+        putc('\\', tracefile);
+        putc(*p >> 6 & 03, tracefile);
+        putc(*p >> 3 & 07, tracefile);
+        putc(*p & 07, tracefile);
+      }
+      break;
+    }
+  }
+  putc('"', tracefile);
+#endif
+}
+
+void trargs(char **ap)
+{
+#ifdef DEBUG
+  if (tracefile == NULL)
+    return;
+  while (*ap) {
+    trstring(*ap++);
+    if (*ap)
+      putc(' ', tracefile);
+    else
+      putc('\n', tracefile);
+  }
+  fflush(tracefile);
+#endif
+}
+
+void opentrace()
+{
+  char s[100];
+  char *p;
+  char *getenv();
+
+#ifdef DEBUG
+  if (!debug)
+    return;
+  if ((p = getenv("HOME")) == NULL) {
+    if (getuid() == 0)
+      p = "/";
+    else
+      p = "/tmp";
+  }
+  scopy(p, s);
+  strcat(s, "/trace");
+  if ((tracefile = fopen(s, "a")) == NULL) {
+    fprintf(stderr, "Can't open %s\n", s);
+    return;
+  }
+#ifndef O_APPEND
+  {
+    int flags;
+    if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
+      fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
+  }
+#endif
+  fputs("\nTracing started.\n", tracefile);
+  fflush(tracefile);
+#endif
+}
+//==================================================================================
+//redir - Code for dealing with input/output redirection.
+/*
+ * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout, is saved in memory.
+ */
+STATIC void redirect(union node *redir, int flags)
+{
+  union node *n;
+  struct redirtab *sv = NULL;
+  int i;
+  int fd;
+  char memory[10];    /* file descriptors to write to memory */
+
+  for (i = 10 ; --i >= 0 ; )
+    memory[i] = 0;
+  memory[1] = flags & REDIR_BACKQ;
+  if (flags & REDIR_PUSH) {
+    sv = ckmalloc(sizeof (struct redirtab));
+    for (i = 0 ; i < 10 ; i++)
+      sv->renamed[i] = EMPTY;
+    sv->next = redirlist;
+    redirlist = sv;
+  }
+  for (n = redir ; n ; n = n->nfile.next) {
+    fd = n->nfile.fd;
+    if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
+      INTOFF;
+      if ((i = copyfd(fd, 10)) != EMPTY) {
+        sv->renamed[fd] = i;
+        close(fd);
+      }
+      INTON;
+      if (i == EMPTY)
+        sh_error("Out of file descriptors");
+    } else {
+      close(fd);
+    }
+    if (fd == 0)
+      fd0_redirected++;
+    openredirect(n, memory);
+  }
+  if (memory[1])
+    out1 = &memout;
+  if (memory[2])
+    out2 = &memout;
+}
+
+static int noclobber(const char *file_name)
+{
+  int fd = -1;
+  struct stat sbuf;
+
+  //If the file exists and is a regular file, set the error number and return.
+  fd = stat(file_name, &sbuf);
+  if( (fd == 0) && (S_ISREG(sbuf.st_mode)) ) {
+    errno = EEXIST;
+    return -1;
+  }
+
+  if(fd != 0) {
+    fd = open(file_name, O_WRONLY|O_CREAT|O_EXCL, 0666);
+    return fd;
+  }
+  return fd;
+}
+
+STATIC void openredirect(union node *redir, char memory[10])
+{
+  int fd = redir->nfile.fd;
+  char *fname;
+  int f;
+
+  /*
+   * We suppress interrupts so that we won't leave open file
+   * descriptors around.  This may not be such a good idea because
+   * an open of a device or a fifo can block indefinitely.
+   */
+  INTOFF;
+  memory[fd] = 0;
+  switch (redir->nfile.type) {
+  case NFROM:
+    fname = redir->nfile.expfname;
+    if ((f = open(fname, O_RDONLY)) < 0)
+      sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+movefd:
+    if (f != fd) {
+      copyfd(f, fd);
+      close(f);
+    }
+    break;
+  case NTO:
+    /* Take care of noclobber mode. */
+    if(Cflag) {
+      fname = redir->nfile.expfname;
+      if( (f = noclobber(fname)) < 0)
+        sh_error("cannot create %s: %s", fname, strerror(errno));
+      goto movefd;
+    }
+    fname = redir->nfile.expfname;
+#ifdef O_CREAT
+    if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+      sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+#else
+    if ((f = creat(fname, 0666)) < 0)
+      sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+#endif
+    goto movefd;
+  case NAPPEND:
+    fname = redir->nfile.expfname;
+#ifdef O_APPEND
+    if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+      sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+#else
+    if ((f = open(fname, O_WRONLY)) < 0
+     && (f = creat(fname, 0666)) < 0)
+      sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+    lseek(f, 0L, 2);
+#endif
+    goto movefd;
+  case NTOFD:
+  case NFROMFD:
+    if (redir->ndup.dupfd >= 0) {  /* if not ">&-" */
+      if (memory[redir->ndup.dupfd])
+        memory[fd] = 1;
+      else
+        copyfd(redir->ndup.dupfd, fd);
+    }
+    break;
+  case NHERE:
+  case NXHERE:
+    f = openhere(redir);
+    goto movefd;
+  default:
+    abort();
+  }
+  INTON;
+}
+
+/*
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+STATIC int openhere(union node *redir)
+{
+  int pip[2];
+  int len = 0;
+
+  if (pipe(pip) < 0)
+    sh_error("Pipe call failed");
+  if (redir->type == NHERE) {
+    len = strlen(redir->nhere.doc->narg.text);
+    if (len <= PIPESIZE) {
+      xxwrite(pip[1], redir->nhere.doc->narg.text, len);
+      goto out;
+    }
+  }
+  if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+    close(pip[0]);
+    signal(SIGINT, SIG_IGN);
+    signal(SIGQUIT, SIG_IGN);
+    signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+    signal(SIGTSTP, SIG_IGN);
+#endif
+    signal(SIGPIPE, SIG_DFL);
+    if (redir->type == NHERE)
+      xxwrite(pip[1], redir->nhere.doc->narg.text, len);
+    else
+      expandhere(redir->nhere.doc, pip[1]);
+    exit(0);
+  }
+out:
+  close(pip[1]);
+  return pip[0];
+}
+
+/*
+ * Undo the effects of the last redirection.
+ */
+void popredir()
+{
+  register struct redirtab *rp = redirlist;
+  int i;
+
+  for (i = 0 ; i < 10 ; i++) {
+    if (rp->renamed[i] != EMPTY) {
+      if (i == 0)
+        fd0_redirected--;
+      close(i);
+      if (rp->renamed[i] >= 0) {
+        copyfd(rp->renamed[i], i);
+        close(rp->renamed[i]);
+      }
+    }
+  }
+  INTOFF;
+  redirlist = rp->next;
+  ckfree(rp);
+  INTON;
+}
+
+/*
+ * Discard all saved file descriptors.
+ */
+void clearredir()
+{
+  register struct redirtab *rp;
+  int i;
+
+  for (rp = redirlist ; rp ; rp = rp->next) {
+    for (i = 0 ; i < 10 ; i++) {
+      if (rp->renamed[i] >= 0) {
+        close(rp->renamed[i]);
+      }
+      rp->renamed[i] = EMPTY;
+    }
+  }
+}
+
+/*
+ * Copy a file descriptor, like the F_DUPFD option of fcntl.  Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
+ */
+int copyfd(int from, int to)
+{
+#ifdef F_DUPFD
+  int newfd;
+
+  newfd = fcntl(from, F_DUPFD, to);
+  if (newfd < 0 && errno == EMFILE)
+    return EMPTY;
+  return newfd;
+#else
+  char toclose[32];
+  int i;
+  int newfd;
+  int e;
+
+  for (i = 0 ; i < to ; i++)
+    toclose[i] = 0;
+  INTOFF;
+  while ((newfd = dup(from)) >= 0 && newfd < to)
+    toclose[newfd] = 1;
+  e = errno;
+  for (i = 0 ; i < to ; i++) {
+    if (toclose[i])
+      close(i);
+  }
+  INTON;
+  if (newfd < 0 && e == EMFILE)
+    return EMPTY;
+  return newfd;
+#endif
+}
+
+/* Return true if fd 0 has already been redirected at least once.  */
+int fd0_redirected_p ()
+{
+  return fd0_redirected != 0;
+}
+
+//==================================================================================
+//Shell command parser.
+/*
+ * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
+ * valid parse tree indicating a blank line.)
+ */
+union node *parsecmd(int interact)
+{
+  int t;
+
+  doprompt = interact;
+  if (doprompt)
+    putprompt(ps1val());
+  needprompt = 0;
+  if ((t = readtoken()) == TEOF)
+    return NEOF;
+  if (t == TNL)
+    return NULL;
+  tokpushback++;
+  return list(1);
+}
+
+STATIC union node *list(int nlflag)
+{
+  union node *n1, *n2, *n3;
+
+  checkkwd = 2;
+  if (nlflag == 0 && tokendlist[peektoken()])
+    return NULL;
+  n1 = andor();
+  for (;;) {
+    switch (readtoken()) {
+    case TBACKGND:
+      if (n1->type == NCMD || n1->type == NPIPE) {
+        n1->ncmd.backgnd = 1;
+      } else if (n1->type == NREDIR) {
+        n1->type = NBACKGND;
+      } else {
+        n3 = (union node *)stalloc(sizeof (struct nredir));
+        n3->type = NBACKGND;
+        n3->nredir.n = n1;
+        n3->nredir.redirect = NULL;
+        n1 = n3;
+      }
+      goto tsemi;
+    case TNL:
+      tokpushback++;
+      /* fall through */
+tsemi:    case TSEMI:
+      if (readtoken() == TNL) {
+        parseheredoc();
+        if (nlflag)
+          return n1;
+      } else {
+        tokpushback++;
+      }
+      checkkwd = 2;
+      if (tokendlist[peektoken()])
+        return n1;
+      n2 = andor();
+      n3 = (union node *)stalloc(sizeof (struct nbinary));
+      n3->type = NSEMI;
+      n3->nbinary.ch1 = n1;
+      n3->nbinary.ch2 = n2;
+      n1 = n3;
+      break;
+    case TEOF:
+      if (heredoclist)
+        parseheredoc();
+      else
+        pungetc();    /* push back EOF on input */
+      return n1;
+    default:
+      if (nlflag)
+        synexpect(-1);
+      tokpushback++;
+      return n1;
+    }
+  }
+  /*not reached*/
+  return NULL;
+}
+
+STATIC union node *andor()
+{
+  union node *n1, *n2, *n3;
+  int t;
+
+  n1 = pipeline();
+  for (;;) {
+    if ((t = readtoken()) == TAND) {
+      t = NAND;
+    } else if (t == TOR) {
+      t = NOR;
+    } else {
+      tokpushback++;
+      return n1;
+    }
+    n2 = pipeline();
+    n3 = (union node *)stalloc(sizeof (struct nbinary));
+    n3->type = t;
+    n3->nbinary.ch1 = n1;
+    n3->nbinary.ch2 = n2;
+    n1 = n3;
+  }
+  /*not reached*/
+  return NULL;
+}
+
+STATIC union node *pipeline()
+{
+  union node *n1, *n2, *pipenode;
+  struct nodelist *lp, *prev;
+
+  int negate;
+
+  TRACE(("pipeline: entered\n"));
+
+  negate = 0;
+  checkkwd = 2;
+  while (readtoken() == TNOT) {
+      TRACE(("pipeline: TNOT recognized\n"));
+      negate = !negate;
+  }
+  tokpushback++;
+
+  n1 = command();
+  if (readtoken() == TPIPE) {
+    pipenode = (union node *)stalloc(sizeof (struct npipe));
+    pipenode->type = NPIPE;
+    pipenode->npipe.backgnd = 0;
+    lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+    pipenode->npipe.cmdlist = lp;
+    lp->n = n1;
+    do {
+      prev = lp;
+      lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+      lp->n = command();
+      prev->next = lp;
+    } while (readtoken() == TPIPE);
+    lp->next = NULL;
+    n1 = pipenode;
+  }
+  tokpushback++;
+  if (negate) {
+      TRACE(("negate pipeline\n"));
+      n2 = (union node *)stalloc(sizeof (struct nnot));
+      n2->type = NNOT;
+      n2->nnot.com = n1;
+      return n2;
+  } else
+    return n1;
+}
+
+STATIC union node *command()
+{
+  union node *n1 = NULL, *n2;
+  union node *ap, **app;
+  union node *cp, **cpp;
+  union node *redir, **rpp;
+  int t, negate = 0;
+
+  checkkwd = 2;
+  redir = 0;
+  rpp = &redir;
+  /* Check for redirection which may precede command */
+  while (readtoken() == TREDIR) {
+    *rpp = n2 = redirnode;
+    rpp = &n2->nfile.next;
+    parsefname();
+  }
+  tokpushback++;
+
+  while (readtoken() == TNOT) {
+      TRACE(("command: TNOT recognized\n"));
+      negate = !negate;
+  }
+  tokpushback++;
+
+
+  switch (readtoken()) {
+  case TIF:
+    n1 = (union node *)stalloc(sizeof (struct nif));
+    n1->type = NIF;
+    n1->nif.test = list(0);
+    if (readtoken() != TTHEN)
+      synexpect(TTHEN);
+    n1->nif.ifpart = list(0);
+    n2 = n1;
+    while (readtoken() == TELIF) {
+      n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
+      n2 = n2->nif.elsepart;
+      n2->type = NIF;
+      n2->nif.test = list(0);
+      if (readtoken() != TTHEN)
+        synexpect(TTHEN);
+      n2->nif.ifpart = list(0);
+    }
+    if (lasttoken == TELSE)
+      n2->nif.elsepart = list(0);
+    else {
+      n2->nif.elsepart = NULL;
+      tokpushback++;
+    }
+    if (readtoken() != TFI)
+      synexpect(TFI);
+    checkkwd = 1;
+    break;
+  case TWHILE:
+  case TUNTIL: {
+    int got;
+    n1 = (union node *)stalloc(sizeof (struct nbinary));
+    n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
+    n1->nbinary.ch1 = list(0);
+    if ((got=readtoken()) != TDO) {
+      TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
+      synexpect(TDO);
+    }
+    n1->nbinary.ch2 = list(0);
+    if (readtoken() != TDONE)
+      synexpect(TDONE);
+    checkkwd = 1;
+    break;
+  }
+  case TFOR:
+    if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
+      synerror("Bad for loop variable");
+    n1 = (union node *)stalloc(sizeof (struct nfor));
+    n1->type = NFOR;
+    n1->nfor.var = wordtext;
+    if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
+      app = &ap;
+      while (readtoken() == TWORD) {
+        n2 = (union node *)stalloc(sizeof (struct narg));
+        n2->type = NARG;
+        n2->narg.text = wordtext;
+        n2->narg.backquote = backquotelist;
+        *app = n2;
+        app = &n2->narg.next;
+      }
+      *app = NULL;
+      n1->nfor.args = ap;
+      /* A newline or semicolon is required here to end
+         the list.  */
+      if (lasttoken != TNL && lasttoken != TSEMI)
+        synexpect(-1);
+    } else {
+#ifndef GDB_HACK
+      static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
+                   '@', '=', '\0'};
+#endif
+      n2 = (union node *)stalloc(sizeof (struct narg));
+      n2->type = NARG;
+      n2->narg.text = (char *)argvars;
+      n2->narg.backquote = NULL;
+      n2->narg.next = NULL;
+      n1->nfor.args = n2;
+      /* A newline or semicolon is optional here. Anything
+         else gets pushed back so we can read it again.  */
+      if (lasttoken != TNL && lasttoken != TSEMI)
+        tokpushback++;
+    }
+    checkkwd = 2;
+    if ((t = readtoken()) == TDO)
+      t = TDONE;
+    else if (t == TBEGIN)
+      t = TEND;
+    else
+      synexpect(-1);
+    n1->nfor.body = list(0);
+    if (readtoken() != t)
+      synexpect(t);
+    checkkwd = 1;
+    break;
+  case TCASE:
+    n1 = (union node *)stalloc(sizeof (struct ncase));
+    n1->type = NCASE;
+    if (readtoken() != TWORD)
+      synexpect(TWORD);
+    n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
+    n2->type = NARG;
+    n2->narg.text = wordtext;
+    n2->narg.backquote = backquotelist;
+    n2->narg.next = NULL;
+    while (readtoken() == TNL);
+    if (lasttoken != TWORD || ! equal(wordtext, "in"))
+      synerror("expecting \"in\"");
+    cpp = &n1->ncase.cases;
+
+    noalias = 1;
+
+    while (checkkwd = 2, readtoken() == TWORD) {
+      *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
+      cp->type = NCLIST;
+      app = &cp->nclist.pattern;
+      for (;;) {
+        *app = ap = (union node *)stalloc(sizeof (struct narg));
+        ap->type = NARG;
+        ap->narg.text = wordtext;
+        ap->narg.backquote = backquotelist;
+        if (readtoken() != TPIPE)
+          break;
+        app = &ap->narg.next;
+        if (readtoken() != TWORD)
+          synexpect(TWORD);
+      }
+      ap->narg.next = NULL;
+
+      noalias = 0;
+
+      if (lasttoken != TRP)
+        synexpect(TRP);
+      cp->nclist.body = list(0);
+      if ((t = readtoken()) == TESAC)
+        tokpushback++;
+      else if (t != TENDCASE) {
+        noalias = 0;
+        synexpect(TENDCASE);
+      }
+      else {
+          noalias = 1;
+          checkkwd = 2;
+          readtoken();
+      }
+
+      cpp = &cp->nclist.next;
+    }
+    *cpp = NULL;
+
+    noalias = 0;
+
+    if (lasttoken != TESAC)
+      synexpect(TESAC);
+    checkkwd = 1;
+    break;
+  case TLP:
+    n1 = (union node *)stalloc(sizeof (struct nredir));
+    n1->type = NSUBSHELL;
+    n1->nredir.n = list(0);
+    n1->nredir.redirect = NULL;
+    if (readtoken() != TRP)
+      synexpect(TRP);
+    checkkwd = 1;
+    break;
+  case TBEGIN:
+    n1 = list(0);
+    if (readtoken() != TEND)
+      synexpect(TEND);
+    checkkwd = 1;
+    break;
+  /* Handle an empty command like other simple commands.  */
+  case TNL:
+  case TWORD:
+    tokpushback++;
+    n1 = simplecmd(rpp, redir);
+    goto checkneg;    
+  default:
+    synexpect(-1);
+  }
+
+  /* Now check for redirection which may follow command */
+  while (readtoken() == TREDIR) {
+    *rpp = n2 = redirnode;
+    rpp = &n2->nfile.next;
+    parsefname();
+  }
+  tokpushback++;
+  *rpp = NULL;
+  if (redir) {
+    if (n1->type != NSUBSHELL) {
+      n2 = (union node *)stalloc(sizeof (struct nredir));
+      n2->type = NREDIR;
+      n2->nredir.n = n1;
+      n1 = n2;
+    }
+    n1->nredir.redirect = redir;
+  }
+checkneg:
+  if (negate) {
+      TRACE(("negate command\n"));
+      n2 = (union node *)stalloc(sizeof (struct nnot));
+      n2->type = NNOT;
+      n2->nnot.com = n1;
+      return n2;
+  }
+  else
+    return n1;
+}
+
+#define _name(c)    ((c) == '_' || isalpha((unsigned char)(c)))
+#define _in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+/*           
+ * Return the pointer to the first char which is not part of a legal variable name
+ * (a letter or underscore followed by letters, underscores, and digits).
+ */          
+static const char*   
+endofname(const char *name)                                                                           
+{            
+  if (!_name(*name))
+    return name;   
+  while (*++name) {  
+    if (!_in_name(*name))
+      break;   
+  }
+  return name;
+}
+static int        
+isassignment(const char *p)
+{             
+  const char *q = endofname(p);                                                                         
+  if (p == q)     
+    return 0;     
+  return *q == '=';   
+}
+
+
+
+
+STATIC union node *simplecmd(union node **rpp, union node *redir)
+{
+  union node *args, **app;
+  union node **orig_rpp = rpp;
+  union node *n = NULL, *n2;
+  int negate = 0;
+  int savecheckkwd;
+  int is_double_brackets = 0;
+  /* If we don't have any redirections already, then we must reset
+     rpp to be the address of the local redir variable.  */
+  if (redir == 0)
+    rpp = &redir;
+
+  args = NULL;
+  app = &args;
+  /* We save the incoming value, because we need this for shell
+     functions.  There can not be a redirect or an argument between
+     the function name and the open parenthesis.  */
+  orig_rpp = rpp;
+
+  while (readtoken() == TNOT) {
+      TRACE(("simplcmd: TNOT recognized\n"));
+      negate = !negate;
+  }
+  tokpushback++;
+  savecheckkwd = 1;
+
+  for (;;) {
+    int t = 0;
+    checkkwd = savecheckkwd;
+    t = readtoken();
+    if ((t == TWORD) || (t == TAND || t == TOR)) {
+      if (t == TAND || t == TOR) {
+        if (!is_double_brackets) {
+          tokpushback = 1;
+          goto out;
+        }
+        wordtext = (char *) (t == TAND ? "-a" : "-o");
+      }
+      n = (union node *)stalloc(sizeof (struct narg));
+      n->type = NARG;
+      n->narg.text = wordtext;
+      if (strcmp("[[", wordtext) == 0)
+        is_double_brackets = 1;
+      else if (strcmp("]]", wordtext) == 0)
+        is_double_brackets = 0;
+      n->narg.backquote = backquotelist;
+      *app = n;
+      app = &n->narg.next;
+      if(!(savecheckkwd && isassignment(wordtext))) {
+        savecheckkwd = 0;
+      }
+    } else if (lasttoken == TREDIR) {
+      *rpp = n = redirnode;
+      rpp = &n->nfile.next;
+      parsefname();  /* read name of redirection file */
+    } else if (lasttoken == TLP && app == &args->narg.next
+            && rpp == orig_rpp) {
+      /* We have a function */
+      if (readtoken() != TRP)
+        synexpect(TRP);
+#ifdef notdef
+      if (! goodname(n->narg.text))
+        synerror("Bad function name");
+#endif
+      n->type = NDEFUN;
+      n->narg.next = command();
+      goto checkneg;      
+    } else {
+      tokpushback++;
+      break;
+    }
+  }
+out:
+  *app = NULL;
+  *rpp = NULL;
+  n = (union node *)stalloc(sizeof (struct ncmd));
+  n->type = NCMD;
+  n->ncmd.backgnd = 0;
+  n->ncmd.args = args;
+  n->ncmd.redirect = redir;
+checkneg:
+  if (negate) {
+      TRACE(("negate simplecmd\n"));
+      n2 = (union node *)stalloc(sizeof (struct nnot));
+      n2->type = NNOT;
+      n2->nnot.com = n;
+      return n2;
+  }
+  else
+       return n;
+}
+
+STATIC void parsefname()
+{
+  union node *n = redirnode;
+
+  if (readtoken() != TWORD)
+    synexpect(-1);
+  if (n->type == NHERE) {
+    struct heredoc *here = heredoc;
+    struct heredoc *p;
+    int i;
+
+    if (quoteflag == 0)
+      n->type = NXHERE;
+    TRACE(("Here document %d\n", n->type));
+    if (here->striptabs) {
+      while (*wordtext == '\t')
+        wordtext++;
+    }
+    if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
+      synerror("Illegal eof marker for << redirection");
+    rmescapes(wordtext);
+    here->eofmark = wordtext;
+    here->next = NULL;
+    if (heredoclist == NULL)
+      heredoclist = here;
+    else {
+      for (p = heredoclist ; p->next ; p = p->next);
+      p->next = here;
+    }
+  } else if (n->type == NTOFD || n->type == NFROMFD) {
+    if (is_digit(wordtext[0]))
+      n->ndup.dupfd = digit_val(wordtext[0]);
+    else if (wordtext[0] == '-')
+      n->ndup.dupfd = -1;
+    else
+      goto bad;
+    if (wordtext[1] != '\0') {
+bad:
+      synerror("Bad fd number");
+    }
+  } else {
+    n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
+    n = n->nfile.fname;
+    n->type = NARG;
+    n->narg.next = NULL;
+    n->narg.text = wordtext;
+    n->narg.backquote = backquotelist;
+  }
+}
+
+/*
+ * Input any here documents.
+ */
+STATIC void parseheredoc()
+{
+  struct heredoc *here;
+  union node *n;
+
+  while (heredoclist) {
+    here = heredoclist;
+    heredoclist = here->next;
+    if (needprompt) {
+      putprompt(ps2val());
+      needprompt = 0;
+    }
+    readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+        here->eofmark, here->striptabs);
+    n = (union node *)stalloc(sizeof (struct narg));
+    n->narg.type = NARG;
+    n->narg.next = NULL;
+    n->narg.text = wordtext;
+    n->narg.backquote = backquotelist;
+    here->here->nhere.doc = n;
+  }
+}
+
+STATIC int peektoken()
+{
+  int t;
+
+  t = readtoken();
+  tokpushback++;
+  return (t);
+}
+
+STATIC int readtoken()
+{
+  int t;
+  struct alias *ap;
+  int savecheckkwd = checkkwd;
+#ifdef DEBUG
+  int alreadyseen = tokpushback;
+#endif
+top:
+  t = xxreadtoken();
+
+  if (checkkwd) {
+    /*
+     * eat newlines
+     */
+    if (checkkwd == 2) {
+      checkkwd = 0;
+      while (t == TNL) {
+        parseheredoc();
+        t = xxreadtoken();
+      }
+    } else
+      checkkwd = 0;
+    /*
+     * check for keywords
+     */
+    if (t == TWORD && !quoteflag) {
+      register char *const *pp;
+
+      for (pp = parsekwd; *pp; pp++) {
+        if (**pp == *wordtext && equal(*pp, wordtext)) {
+          lasttoken = t = pp - parsekwd + KWDOFFSET;
+          TRACE(("keyword %s recognized\n", tokname[t]));          
+          goto out;
+        }
+      }
+      if(!noalias && (ap = lookupalias(wordtext, 1)) != NULL) {
+          pushstring(ap->val, strlen(ap->val), ap);
+          checkkwd = savecheckkwd;
+          goto top;
+      }
+    }
+out:
+    checkkwd = (t == TNOT) ? savecheckkwd : 0;
+  }
+#ifdef DEBUG
+  if (!alreadyseen)
+    TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
+  else
+    TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
+#endif
+  return (t);
+}
+
+/*
+ * Read the next input token.
+ * If the token is a word, we set backquotelist to the list of cmds in
+ *  backquotes.  We set quoteflag to true if any part of the word was
+ *  quoted.
+ * If the token is TREDIR, then we set redirnode to a structure containing
+ *  the redirection.
+ * In all cases, the variable startlinno is set to the number of the line
+ *  on which the token starts.
+ *
+ * [Change comment:  here documents and internal procedures]
+ * [Readtoken shouldn't have any arguments.  Perhaps we should make the
+ *  word parsing code into a separate routine.  In this case, readtoken
+ *  doesn't need to have any internal procedures, but parseword does.
+ *  We could also make parseoperator in essence the main routine, and
+ *  have parseword (readtoken1?) handle both words and redirection.]
+ */
+#define RETURN(token)  return lasttoken = token
+
+STATIC int xxreadtoken()
+{
+  register int c;
+
+  if (tokpushback) {
+    tokpushback = 0;
+    return lasttoken;
+  }
+  if (needprompt) {
+    putprompt(ps2val());
+    needprompt = 0;
+  }
+  startlinno = plinno;
+  for (;;) {  /* until token or start of word found */
+    c = pgetc_macro();
+    if (c == ' ' || c == '\t')
+      continue;    /* quick check for white space first */
+    switch (c) {
+    case ' ': case '\t':
+      continue;
+    case '#':
+      while ((c = pgetc()) != '\n' && c != PEOF);
+      pungetc();
+      continue;
+    case '\\':
+      if (pgetc() == '\n') {
+        startlinno = ++plinno;
+        if (doprompt)
+          putprompt(ps2val());
+        continue;
+      }
+      pungetc();
+      goto breakloop;
+    case '\n':
+      plinno++;
+      needprompt = doprompt;
+      RETURN(TNL);
+    case PEOF:
+      RETURN(TEOF);
+    case '&':
+      if (pgetc() == '&')
+        RETURN(TAND);
+      pungetc();
+      RETURN(TBACKGND);
+    case '|':
+      if (pgetc() == '|')
+        RETURN(TOR);
+      pungetc();
+      RETURN(TPIPE);
+    case ';':
+      if (pgetc() == ';')
+        RETURN(TENDCASE);
+      pungetc();
+      RETURN(TSEMI);
+    case '(':
+      RETURN(TLP);
+    case ')':
+      RETURN(TRP);
+    default:
+      goto breakloop;
+    }
+  }
+breakloop:
+  return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+#undef RETURN
+}
+
+/*
+ * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
+ * is not NULL, read a here document.  In the latter case, eofmark is the
+ * word which marks the end of the document and striptabs is true if
+ * leading tabs should be stripped from the document.  The argument firstc
+ * is the first character of the input token or document.
+ *
+ * Because C does not have internal subroutines, I have simulated them
+ * using goto's to implement the subroutine linkage.  The following macros
+ * will run code that appears at the end of readtoken1.
+ */
+#define CHECKEND()  {goto checkend; checkend_return:;}
+#define PARSEREDIR()  {goto parseredir; parseredir_return:;}
+#define PARSESUB()  {goto parsesub; parsesub_return:;}
+#define PARSEBACKQOLD()  {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
+#define PARSEBACKQNEW()  {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
+
+STATIC int readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs)
+{
+  register int c = firstc;
+  register char *out;
+  int len;
+  char line[EOFMARKLEN + 1];
+  struct nodelist *bqlist;
+  int quotef;
+  int dblquote;
+  int varnest;
+  int oldstyle;
+
+  startlinno = plinno;
+  dblquote = 0;
+  if (syntax == DQSYNTAX)
+    dblquote = 1;
+  quotef = 0;
+  bqlist = NULL;
+  varnest = 0;
+  STARTSTACKSTR(out);
+  loop: {  /* for each line, until end of word */
+#if ATTY
+    if (c == '\034' && doprompt
+     && attyset() && ! equal(termval(), "emacs")) {
+      attyline();
+      if (syntax == BASESYNTAX)
+        return readtoken();
+      c = pgetc();
+      goto loop;
+    }
+#endif
+    CHECKEND();  /* set c to PEOF if at end of here document */
+    for (;;) {  /* until end of line or end of word */
+      CHECKSTRSPACE(3, out);  /* permit 3 calls to USTPUTC */
+      switch(syntax[c]) {
+      case CNL:  /* '\n' */
+        if (syntax == BASESYNTAX)
+          goto endword;  /* exit outer loop */
+        USTPUTC(c, out);
+        plinno++;
+        if (doprompt) {
+          putprompt(ps2val());
+        }
+        c = pgetc();
+        goto loop;    /* continue outer loop */
+      case CWORD:
+        USTPUTC(c, out);
+        break;
+      case CCTL:
+        if (eofmark == NULL || dblquote)
+          USTPUTC(CTLESC, out);
+        USTPUTC(c, out);
+        break;
+      case CBACK:  /* backslash */
+        c = pgetc();
+        if (c == PEOF) {
+          USTPUTC('\\', out);
+          pungetc();
+        } else if (c == '\n') {
+          if (doprompt)
+            putprompt(ps2val());
+        } else {
+          if (dblquote && c != '\\' && c != '`' && c != '$'
+               && (c != '"' || eofmark != NULL))
+            USTPUTC('\\', out);
+          if (SQSYNTAX[c] == CCTL)
+            USTPUTC(CTLESC, out);
+          USTPUTC(c, out);
+          quotef++;
+        }
+        break;
+      case CSQUOTE:
+        syntax = SQSYNTAX;
+        break;
+      case CDQUOTE:
+        syntax = DQSYNTAX;
+        dblquote = 1;
+        break;
+      case CENDQUOTE:
+        if (eofmark) {
+          USTPUTC(c, out);
+        } else {
+          syntax = BASESYNTAX;
+          quotef++;
+          dblquote = 0;
+        }
+        break;
+      case CVAR:  /* '$' */
+        PARSESUB();    /* parse substitution */
+        break;
+      case CENDVAR:  /* '}' */
+        if (varnest > 0) {
+          varnest--;
+          USTPUTC(CTLENDVAR, out);
+        } else {
+          USTPUTC(c, out);
+        }
+        break;
+      case CBQUOTE:  /* '`' */
+        PARSEBACKQOLD();
+        break;
+      case CEOF:
+        goto endword;    /* exit outer loop */
+      default:
+        if (varnest == 0)
+          goto endword;  /* exit outer loop */
+        USTPUTC(c, out);
+      }
+      c = pgetc_macro();
+    }
+  }
+endword:
+  if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
+    synerror("Unterminated quoted string");
+  if (varnest != 0) {
+    startlinno = plinno;
+    synerror("Missing '}'");
+  }
+  USTPUTC('\0', out);
+  len = out - stackblock();
+  out = stackblock();
+  if (eofmark == NULL) {
+    if ((c == '>' || c == '<')
+     && quotef == 0
+     && len <= 2
+     && (*out == '\0' || is_digit(*out))) {
+      PARSEREDIR();
+      return lasttoken = TREDIR;
+    } else {
+      pungetc();
+    }
+  }
+  quoteflag = quotef;
+  backquotelist = bqlist;
+  grabstackblock(len);
+  wordtext = out;
+  return lasttoken = TWORD;
+/* end of readtoken routine */
+
+/*
+ * Check to see whether we are at the end of the here document.  When this
+ * is called, c is set to the first character of the next input line.  If
+ * we are at the end of the here document, this routine sets the c to PEOF.
+ */
+checkend: {
+  if (eofmark) {
+    if (striptabs) {
+      while (c == '\t')
+        c = pgetc();
+    }
+    if (c == *eofmark) {
+      if (pfgets(line, sizeof line) != NULL) {
+        register char *p, *q;
+
+        p = line;
+        for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
+        if (*p == '\n' && *q == '\0') {
+          c = PEOF;
+          plinno++;
+          needprompt = doprompt;
+        } else {          
+           pushstring(line, strlen(line), NULL);
+        }
+      }
+    }
+  }
+  goto checkend_return;
+}
+
+/*
+ * Parse a redirection operator.  The variable "out" points to a string
+ * specifying the fd to be redirected.  The variable "c" contains the
+ * first character of the redirection operator.
+ */
+parseredir: {
+  char fd = *out;
+  union node *np;
+
+  np = (union node *)stalloc(sizeof (struct nfile));
+  if (c == '>') {
+    np->nfile.fd = 1;
+    c = pgetc();
+    if (c == '>')
+      np->type = NAPPEND;
+    else if (c == '&')
+      np->type = NTOFD;
+    else {
+      np->type = NTO;
+      pungetc();
+    }
+  } else {  /* c == '<' */
+    np->nfile.fd = 0;
+    c = pgetc();
+    if (c == '<') {
+      if (sizeof (struct nfile) != sizeof (struct nhere)) {
+        np = (union node *)stalloc(sizeof (struct nhere));
+        np->nfile.fd = 0;
+      }
+      np->type = NHERE;
+      heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
+      heredoc->here = np;
+      if ((c = pgetc()) == '-') {
+        heredoc->striptabs = 1;
+      } else {
+        heredoc->striptabs = 0;
+        pungetc();
+      }
+    } else if (c == '&')
+      np->type = NFROMFD;
+    else {
+      np->type = NFROM;
+      pungetc();
+    }
+  }
+  if (fd != '\0')
+    np->nfile.fd = digit_val(fd);
+  redirnode = np;
+  goto parseredir_return;
+}
+
+
+/*
+ * Parse a substitution.  At this point, we have read the dollar sign
+ * and nothing else.
+ */
+
+parsesub: {
+       int subtype;
+       int typeloc;
+       int flags;
+       char *p;
+       static const char types[] = "}-+?=";
+
+       c = pgetc();
+       if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
+               USTPUTC('$', out);
+               pungetc();
+  } else if (c == '(') {       /* $(command) or $((arith)) */
+    PARSEBACKQNEW();
+  } else {
+               USTPUTC(CTLVAR, out);
+               typeloc = out - stackblock();
+               USTPUTC(VSNORMAL, out);
+               subtype = VSNORMAL;
+               if (c == OPENBRACE) {
+                       c = pgetc();
+                       if (c == '#') {
+                               if ((c = pgetc()) == CLOSEBRACE)
+                                       c = '#';
+                               else
+                                       subtype = VSLENGTH;
+                       }
+                       else
+                               subtype = 0;
+               }
+               if (is_name(c)) {
+                       do {
+                               STPUTC(c, out);
+                               c = pgetc();
+                       } while (is_in_name(c));
+               } else if (is_digit(c)) {
+                       do {
+                               USTPUTC(c, out);
+                               c = pgetc();
+                       } while (is_digit(c));
+               }
+               else if (is_special(c)) {
+                       USTPUTC(c, out);
+                       c = pgetc();
+               }
+               else
+badsub:                        synerror("Bad substitution");
+
+    if (c != '}' && subtype == VSLENGTH) {                                                                                                                                  
+      goto badsub;
+    }
+               STPUTC('=', out);
+               flags = 0;
+               if (subtype == 0) {
+                       switch (c) {
+                       case ':':
+                               flags = VSNUL;
+                               c = pgetc();
+                               /*FALLTHROUGH*/
+                       default:
+                               p = strchr(types, c);
+                               if (p == NULL)
+                                       goto badsub;
+                               subtype = p - types + VSNORMAL;
+                               break;
+                       case '%':
+                       case '#':
+                               {
+                                       int cc = c;
+                                       subtype = c == '#' ? VSTRIMLEFT :
+                                                            VSTRIMRIGHT;
+                                       c = pgetc();
+                                       if (c == cc)
+                                               subtype++;
+                                       else
+                                               pungetc();
+                                       break;
+                               }
+                       }
+               } else {
+                       pungetc();
+    }
+    if (dblquote)
+      flags |= VSQUOTE;
+    *(stackblock() + typeloc) = subtype | flags;
+    if (subtype != VSNORMAL)
+      varnest++;
+#if 0
+               if (ISDBLQUOTE() || arinest)
+                       flags |= VSQUOTE;
+               *(stackblock() + typeloc) = subtype | flags;
+               if (subtype != VSNORMAL) {
+                       varnest++;
+                       if (varnest >= maxnest) {
+                               dblquotep = ckrealloc(dblquotep, maxnest / 8);
+                               dblquotep[(maxnest / 32) - 1] = 0;
+                               maxnest += 32;
+                       }
+               }
+#endif
+  }
+  goto parsesub_return;
+}
+
+/*
+ * Called to parse command substitutions.  Newstyle is set if the command
+ * is enclosed inside $(...); nlpp is a pointer to the head of the linked
+ * list of commands (passed by reference), and savelen is the number of
+ * characters on the top of the stack which must be preserved.
+ */
+parsebackq: {
+  struct nodelist **nlpp;
+  int savepbq;
+  union node *n;
+  char *volatile str;
+  struct jmploc jmploc;
+  struct jmploc *volatile savehandler;
+  int savelen;
+
+  savepbq = parsebackquote;
+  if (setjmp(jmploc.loc)) {
+    if (str)
+      ckfree(str);
+    parsebackquote = 0;
+    handler = savehandler;
+    longjmp(handler->loc, 1);
+  }
+  INTOFF;
+  str = NULL;
+  savelen = out - stackblock();
+  if (savelen > 0) {
+    str = ckmalloc(savelen);
+    bcopy(stackblock(), str, savelen);
+  }
+  savehandler = handler;
+  handler = &jmploc;
+  INTON;
+  if (oldstyle) {
+    /* We must read until the closing backquote, giving special
+       treatment to some slashes, and then push the string and
+       reread it as input, interpreting it normally.  */
+    register char *out;
+    register int c;
+    int savelen;
+    char *str = NULL;
+
+    STARTSTACKSTR(out);
+    while ((c = pgetc ()) != '`') {
+      if (c == '\\') {
+        c = pgetc ();
+        if (c != '\\' && c != '`' && c != '$'
+          && (!dblquote || c != '"'))
+          STPUTC('\\', out);
+      }
+      STPUTC(c, out);
+    }
+    STPUTC('\0', out);
+    savelen = out - stackblock();
+    if (savelen > 0) {
+      str = ckmalloc(savelen);
+      bcopy(stackblock(), str, savelen);
+    }
+    setinputstring(str, 1);
+  }
+  nlpp = &bqlist;
+  while (*nlpp)
+    nlpp = &(*nlpp)->next;
+  *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+  (*nlpp)->next = NULL;
+  parsebackquote = oldstyle;
+  n = list(0);
+  if (!oldstyle && (readtoken() != TRP))
+    synexpect(TRP);
+  (*nlpp)->n = n;
+  /* Start reading from old file again, and clear tokpushback since
+     any pushed back token from the string is no longer relevant.  */
+  if (oldstyle) {
+    popfile();
+    tokpushback = 0;
+  }
+  while (stackblocksize() <= savelen)
+    growstackblock();
+  STARTSTACKSTR(out);
+  if (str) {
+    bcopy(str, out, savelen);
+    STADJUST(savelen, out);
+    INTOFF;
+    ckfree(str);
+    str = NULL;
+    INTON;
+  }
+  parsebackquote = savepbq;
+  handler = savehandler;
+  USTPUTC(CTLBACKQ + dblquote, out);
+  if (oldstyle)
+    goto parsebackq_oldreturn;
+  else
+    goto parsebackq_newreturn;
+  }
+
+} /* end of readtoken */
+
+
+#if ATTY
+/*
+ * Called to process a command generated by atty.  We execute the line,
+ * and catch any errors that occur so they don't propagate outside of
+ * this routine.
+ */
+STATIC void attyline()
+{
+  char line[256];
+  struct stackmark smark;
+  struct jmploc jmploc;
+  struct jmploc *volatile savehandler;
+
+  if (pfgets(line, sizeof line) == NULL)
+    return;        /* "can't happen" */
+  if (setjmp(jmploc.loc)) {
+    if (exception == EXERROR)
+      out2str("\033]D\n");
+    handler = savehandler;
+    longjmp(handler->loc, 1);
+  }
+  savehandler = handler;
+  handler = &jmploc;
+  setstackmark(&smark);
+  evalstring(line);
+  popstackmark(&smark);
+  handler = savehandler;
+  doprompt = 1;
+}
+
+/*
+ * Output a prompt for atty.  We output the prompt as part of the
+ * appropriate escape sequence.
+ */
+STATIC void putprompt(char *s)
+{
+  register char *p;
+
+  if (attyset() && ! equal(termval(), "emacs")) {
+    if (strchr(s, '\7'))
+      out2c('\7');
+    out2str("\033]P1;");
+    for (p = s ; *p ; p++) {
+      if ((unsigned)(*p - ' ') <= '~' - ' ')
+        out2c(*p);
+    }
+    out2c('\n');
+  } else {
+    out2str(s);
+  }
+}
+#endif
+
+/*
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
+ */
+STATIC int noexpand(char *text)
+{
+  register char *p;
+  register char c;
+
+  p = text;
+  while ((c = *p++) != '\0') {
+    if (c == CTLESC)
+      p++;
+    else if (BASESYNTAX[c] == CCTL)
+      return 0;
+  }
+  return 1;
+}
+
+/*
+ * Return true if the argument is a legal variable name (a letter or
+ * underscore followed by zero or more letters, underscores, and digits).
+ */
+int goodname(char *name)
+{
+  register char *p;
+
+  p = name;
+  if (! is_name(*p))
+    return 0;
+  while (*++p) {
+    if (! is_in_name(*p))
+      return 0;
+  }
+  return 1;
+}
+
+/*
+ * Called when an unexpected token is read during the parse.  The argument
+ * is the token that is expected, or -1 if more than one type of token can
+ * occur at this point.
+ */
+STATIC void synexpect(int token)
+{
+  char msg[64];
+
+  if (token >= 0) {
+    fmtstr(msg, 64, "%s unexpected (expecting %s)",
+      tokname[lasttoken], tokname[token]);
+  } else {
+    fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
+  }
+  synerror(msg);
+}
+
+STATIC void synerror(char *msg)
+{
+  if (commandname)
+    outfmt(&errout, "%s: %d: ", commandname, startlinno);
+  outfmt(&errout, "Syntax error: %s\n", msg);
+  sh_error((char *)NULL);
+}
+
+//==================================================================================
+//options - Process the shell command line arguments.
+/*
+ * Process the shell command line arguments.
+ */
+void procargs(int argc, char **argv)
+{
+  char *p;
+
+  argptr = argv;
+  if (argc > 0)
+    argptr++;
+  for (p = optval ; p < optval + sizeof optval - 1 ; p++)
+    *p = 2;
+  sh_options(1);
+  if (*argptr == NULL && minusc == NULL)
+    sflag = 1;
+  if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+    iflag = 1;
+  if (jflag == 2)
+    jflag = iflag;
+  for (p = optval ; p < optval + sizeof optval - 1 ; p++)
+    if (*p == 2)
+      *p = 0;
+  arg0 = argv[0];
+  if (sflag == 0 && minusc == NULL) {
+    commandname = arg0 = *argptr++;
+    setinputfile(commandname, 0);
+  }
+  shellparam.p = argptr;
+  /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
+  while (*argptr) {
+    shellparam.nparam++;
+    argptr++;
+  }
+  setinteractive(iflag);
+  setjobctl(jflag);
+  if(iflag) mflag = iflag;
+  for (p = optval ; p < optval + sizeof optval - 1 ; p++)
+    optlist[p - optval].val = *p;
+}
+
+static void set_opt_val(size_t i, int val)
+{
+  size_t j;
+  int flag;
+
+  if (val && (flag = optlist[i].opt_set)) {
+      /* some options (eg vi/emacs) are mutually exclusive */
+      for (j = 0; j < NOPTS; j++)
+        if (optlist[j].opt_set == flag) {
+          optlist[j].val = 0;
+          optval[j] = optlist[j].val;
+        }
+  }
+  optlist[i].val = val;
+  optval[i] = optlist[i].val;
+}
+
+static int check_plus_minus(char *name, int val)
+{
+  int i;
+
+  if (name == NULL) {
+    if (val) {
+      out1str("Current option settings\n");
+      for (i = 0; i < NOPTS; i++) {
+        if(i == 4 || i == 8)
+          continue;
+        out1fmt("%-16s%s\n", optlist[i].name, optlist[i].val ? "on" : "off");
+      }
+    } else {
+      for (i = 0; i < NOPTS; i++) {
+        if(i == 4 || i == 8)
+          continue;
+        out1fmt("set %co %s", "+-"[optlist[i].val], optlist[i].name);
+        out1str("\n");
+      }
+    }
+  } else {
+    for (i = 0; i < NOPTS; i++)
+      if (equal(name, optlist[i].name)) {
+        set_opt_val(i, val);
+        return 0;
+      }
+    sh_error("Illegal option -o %s", name);
+  }
+  return 0;
+}
+/*
+ * Process shell options.  The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ */
+STATIC void sh_options(int cmdline)
+{
+  register char *p;
+  int val = 0;
+  int c;
+
+  if (cmdline)
+    minusc = NULL;
+  while ((p = *argptr) != NULL) {
+    argptr++;
+    if ((c = *p++) == '-') {
+      val = 1;
+      if ((p[0] == '\0') || (p[0] == '-' && p[1] == '\0')) {
+        if (!cmdline) {
+          /* "-" means turn off -x and -v */
+          if (p[0] == '\0')
+            xflag = vflag = 0;
+          /* "--" means reset params */
+          else if (*argptr == NULL)
+            setparam(argptr);
+        }
+        break;    /* "-" or  "--" terminates options */
+      }
+    } else if (c == '+') {
+      val = 0;
+    } else {
+      argptr--;
+      break;
+    }
+    while ((c = *p++) != '\0') {
+      if (c == 'c' && cmdline) {
+        char *q;
+#ifdef NOHACK   /* removing this code allows sh -ce 'foo' for compat */
+        if (*p == '\0')
+#endif
+          q = *argptr++;
+        if (q == NULL || minusc != NULL)
+          sh_error("Bad -c option");
+        minusc = q;
+#ifdef NOHACK
+        break;
+#endif
+      }
+      else if (c == 'o') {
+        if (check_plus_minus(*argptr, val)) {
+          /* it already printed err message */
+          return; /* error */
+        }
+        if (*argptr)
+          argptr++;
+      } else {
+        setoption(c, val);
+      }
+    }
+    if (! cmdline)
+      break;
+    }
+}
+
+
+STATIC void setoption(char flag, int val)
+{
+  register char *p;
+
+  if ((p = strchr(optchar, flag)) == NULL)
+    sh_error("Illegal option -%c", flag);
+  optval[p - optchar] = val;
+  optlist[p - optchar].val = val;
+
+}
+
+/*
+ * Set the shell parameters.
+ */
+void setparam(char **argv)
+{
+  char **newparam;
+  char **ap;
+  int nparam;
+
+  for (nparam = 0 ; argv[nparam] ; nparam++);
+  ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
+  while (*argv) {
+    *ap++ = savestr(*argv++);
+  }
+  *ap = NULL;
+  freeparam(&shellparam);
+  shellparam.malloc = 1;
+  shellparam.nparam = nparam;
+  shellparam.p = newparam;
+  shellparam.optnext = NULL;
+}
+
+/*
+ * Free the list of positional parameters.
+ */
+void freeparam(struct shparam *param)
+{
+  char **ap;
+
+  if (param->malloc) {
+    for (ap = param->p ; *ap ; ap++)
+      ckfree(*ap);
+    ckfree(param->p);
+  }
+}
+
+/*
+ * The shift builtin command.
+ */
+int shiftcmd(int argc, char **argv)
+{
+  int n;
+  char **ap1, **ap2;
+
+  n = 1;
+  if (argc > 1)
+    n = number(argv[1]);
+  if (n > shellparam.nparam)
+    n = shellparam.nparam;
+  INTOFF;
+  shellparam.nparam -= n;
+  for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
+    if (shellparam.malloc)
+      ckfree(*ap1);
+  }
+  ap2 = shellparam.p;
+  while ((*ap2++ = *ap1++) != NULL);
+  shellparam.optnext = NULL;
+  INTON;
+  return 0;
+}
+
+/*
+ * The set command builtin.
+ */
+int setcmd(int argc, char **argv)
+{
+  char *p;
+  if (argc == 1)
+    return showvarscmd(argc, argv);
+  INTOFF;
+  sh_options(0);
+  setinteractive(iflag);
+  setjobctl(jflag);
+  for (p = optval ; p < optval + sizeof optval - 1 ; p++)
+     *p = optlist[p - optval].val;
+  if (*argptr != NULL) {
+    setparam(argptr);
+  }
+  INTON;
+  return 0;
+}
+
+/*
+ * The getopts builtin.  Shellparam.optnext points to the next argument
+ * to be processed.  Shellparam.optptr points to the next character to
+ * be processed in the current argument.  If shellparam.optnext is NULL,
+ * then it's the first time getopts has been called.
+ */
+int getoptscmd(int argc, char **argv)
+{
+  register char *p, *q;
+  char c;
+  char s[10];
+
+  if (argc != 3)
+    sh_error("Usage: getopts optstring var");
+  if (shellparam.optnext == NULL) {
+    shellparam.optnext = shellparam.p;
+    shellparam.optptr = NULL;
+  }
+  if ((p = shellparam.optptr) == NULL || *p == '\0') {
+    p = *shellparam.optnext;
+    if (p == NULL || *p != '-' || *++p == '\0') {
+atend:
+      fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1);
+      setvar("OPTIND", s, 0);
+      shellparam.optnext = NULL;
+      return 1;
+    }
+    shellparam.optnext++;
+    if (p[0] == '-' && p[1] == '\0')    /* check for "--" */
+      goto atend;
+  }
+  c = *p++;
+  for (q = argv[1] ; *q != c ; ) {
+    if (*q == '\0') {
+      out1fmt("Illegal option -%c\n", c);
+      c = '?';
+      goto out;
+    }
+    if (*++q == ':')
+      q++;
+  }
+  if (*++q == ':') {
+    if (*p == '\0' && (p = *shellparam.optnext) == NULL) {
+      out1fmt("No arg for -%c option\n", c);
+      c = '?';
+      goto out;
+    }
+    shellparam.optnext++;
+    setvar("OPTARG", p, 0);
+    p = NULL;
+  }
+out:
+  shellparam.optptr = p;
+  s[0] = c;
+  s[1] = '\0';
+  setvar(argv[2], s, 0);
+  return 0;
+}
+
+/*
+ * Standard option processing (a la getopt) for builtin routines.  The
+ * only argument that is passed to nextopt is the option string; the
+ * other arguments are unnecessary.  It return the character, or '\0' on
+ * end of input.
+ */
+int nextopt(char *optstring)
+{
+  register char *p, *q;
+  char c;
+
+  if ((p = optptr) == NULL || *p == '\0') {
+    p = *argptr;
+    if (p == NULL || *p != '-' || *++p == '\0')
+      return '\0';
+    argptr++;
+    if (p[0] == '-' && p[1] == '\0')    /* check for "--" */
+      return '\0';
+  }
+  c = *p++;
+  for (q = optstring ; *q != c ; ) {
+    if (*q == '\0')
+      sh_error("Illegal option -%c", c);
+    if (*++q == ':')
+      q++;
+  }
+  if (*++q == ':') {
+    if (*p == '\0' && (p = *argptr++) == NULL)
+      sh_error("No arg for -%c option", c);
+    optarg = p;
+    p = NULL;
+  }
+  optptr = p;
+  return c;
+}
+//==================================================================================
+//mystring - string manipulations.
+/*
+ * String functions.
+ *
+ *  equal(s1, s2)    Return true if strings are equal.
+ *  scopy(from, to)    Copy a string.
+ *  scopyn(from, to, n)  Like scopy, but checks for overflow.
+ *  strchr(s, c)    Find first occurance of c in s.
+ *  bcopy(from, to, n)  Copy a block of memory.
+ *  number(s)    Convert a string of digits to an integer.
+ *  is_number(s)    Return true if s is a string of digits.
+ */
+
+/*
+ * scopyn - copy a string from "from" to "to", truncating the string
+ *    if necessary.  "To" is always nul terminated, even if
+ *    truncation is performed.  "Size" is the size of "to".
+ */
+void scopyn(register char const *from, register char *to, register int size)
+{
+  while (--size > 0) {
+    if ((*to++ = *from++) == '\0')
+      return;
+  }
+  *to = '\0';
+}
+
+/*
+ * strchr - find first occurrence of a character in a string.
+ */
+#ifndef SYS5
+char *mystrchr(char const *s, register char charwanted)
+{
+  register char const *scan;
+  /*
+   * The odd placement of the two tests is so NUL is findable.
+   */
+  for (scan = s ; *scan != charwanted ; )  /* ++ moved down for opt. */
+    if (*scan++ == '\0')
+      return NULL;
+  return (char *)scan;
+}
+#endif
+
+/*
+ * bcopy - copy bytes
+ *
+ * This routine was derived from code by Henry Spencer.
+ */
+void mybcopy(const pointer src, pointer dst, register int length)
+{
+  register char *d = dst;
+  register char *s = src;
+
+  while (--length >= 0)
+    *d++ = *s++;
+}
+
+/*
+ * prefix -- see if pfx is a prefix of string.
+ */
+int prefix(register char const *pfx, register char const *string)
+{
+  while (*pfx) {
+    if (*pfx++ != *string++)
+      return 0;
+  }
+  return 1;
+}
+
+/*
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
+ */
+int number(const char *s)
+{
+  if (! is_number(s))
+    error2("Illegal number", (char *)s);
+  return atoi(s);
+}
+
+/*
+ * Check for a valid number.  This should be elsewhere.
+ */
+static int is_number(register const char *p)
+{
+  do {
+    if (! is_digit(*p))
+      return 0;
+  } while (*++p != '\0');
+  return 1;
+}
+
+//==================================================================================
+//jobs - job controls
+#if JOBS
+/*
+ * Turn job control on and off.
+ *
+ * Note:  This code assumes that the third arg to ioctl is a character
+ * pointer, which is true on Berkeley systems but not System V.  Since
+ * System V doesn't have job control yet, this isn't a problem now.
+ */
+int jobctl;
+
+void setjobctl(int on)
+{
+  if (on == jobctl || rootshell == 0)
+    return;
+  if (on) {
+    do { /* while we are in the background */
+      if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
+        out2str("ash: can't access tty; job control turned off\n");
+        jflag = 0;
+        return;
+      }
+      if (initialpgrp == -1)
+        initialpgrp = getpgid(0);
+      else if (initialpgrp != getpgid(0)) {
+        killpg(0, SIGTTIN);
+        continue;
+      }
+    } while (0);
+#ifdef OLD_TTY_DRIVER
+    {
+      int ldisc;
+      if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
+        out2str("ash: need new tty driver to run job control; job control turned off\n");
+        jflag = 0;
+        return;
+      }
+    }
+#endif
+    setsignal(SIGTSTP);
+    setsignal(SIGTTOU);
+    setpgid(0, rootpid);
+    ioctl(2, TIOCSPGRP, (char *)&rootpid);
+  } else { /* turning job control off */
+    setpgid(0, initialpgrp);
+    ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
+    setsignal(SIGTSTP);
+    setsignal(SIGTTOU);
+  }
+  jobctl = on;
+}
+#endif
+
+#if JOBS
+int fgcmd(int argc, char **argv)
+{
+       struct job *jp;
+       char *name = NULL;
+       int jobno;
+       int pgrp;
+       int status;
+       name = argv[1];
+       if(name == NULL) {
+               for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
+                       if (jp->used)
+                               continue;
+                       name = xmsprintf("%c%d",'%', jobno - 1);
+                       break;
+               }
+       }
+
+       jp = getjob(name);
+       if (jp->jobctl == 0)
+               sh_error("job not created under job control");
+       pgrp = jp->ps[0].pid;
+       ioctl(2, TIOCSPGRP, (char *)&pgrp);
+       restartjob(jp);
+       INTOFF;
+       status = waitforjob(jp);
+       INTON;
+       return status;
+}
+
+#if JOBS
+static int jobno(const struct job *jp)
+{
+  return jp - jobtab + 1;
+}
+#endif
+
+int bgcmd(int argc, char **argv)
+{
+  struct job *jp;
+
+  do {
+    jp = getjob(*++argv);
+    if (jp->jobctl == 0)
+      sh_error("job not created under job control");
+    printf("[%d]  %s\n", jobno(jp), jp->ps[0].cmd);
+    fflush(stdout);
+    restartjob(jp);
+  } while (--argc > 1);
+  return 0;
+}
+
+STATIC void restartjob(struct job *jp)
+{
+  struct procstat *ps;
+  int i;
+
+  if (jp->state == JOBDONE)
+    return;
+  INTOFF;
+  killpg(jp->ps[0].pid, SIGCONT);
+  for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
+    if ((ps->status & 0377) == 0177) {
+      ps->status = -1;
+      jp->state = 0;
+    }
+  }
+  INTON;
+}
+#endif
+
+int jobscmd(int argc, char **argv)
+{
+  showjobs(0);
+  return 0;
+}
+
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ *
+ * If the shell is interrupted in the process of creating a job, the
+ * result may be a job structure containing zero processes.  Such structures
+ * will be freed here.
+ */
+void showjobs(int change)
+{
+  int jobno;
+  int procno;
+  int i;
+  struct job *jp;
+  struct procstat *ps;
+  int col;
+  char s[64];
+
+  TRACE(("showjobs(%d) called\n", change));
+  while (dowait(0, (struct job *)NULL) > 0);
+  for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
+    if (! jp->used)
+      continue;
+    if (jp->nprocs == 0) {
+      freejob(jp);
+      continue;
+    }
+    if (change && ! jp->changed)
+      continue;
+    procno = jp->nprocs;
+    for (ps = jp->ps ; ; ps++) {  /* for each process */
+      if (ps == jp->ps)
+        fmtstr(s, 64, "[%d] %d ", jobno, ps->pid);
+      else
+        fmtstr(s, 64, "  %d ", ps->pid);
+
+      if(mflag)
+      out1str(s);
+      else
+        memset(s, 0, 64);
+
+      col = strlen(s);
+      s[0] = '\0';
+      if (ps->status == -1) {
+        /* don't print anything */
+      } else if ((ps->status & 0xFF) == 0) {
+        fmtstr(s, 64, "Exit %d", ps->status >> 8);
+      } else {
+        i = ps->status;
+#if JOBS
+        if ((i & 0xFF) == 0177)
+          i >>= 8;
+#endif
+        if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F])
+          scopy(sigmesg[i & 0x7F], s);
+        else
+          fmtstr(s, 64, "Signal %d", i & 0x7F);
+        if (i & 0x80)
+          strcat(s, " (core dumped)");
+      }
+      if(mflag)
+      out1str(s);
+      else
+        memset(s, 0, 64);
+      col += strlen(s);
+      if(mflag) {
+      do {
+        out1c(' ');
+        col++;
+      } while (col < 30);
+
+      out1str(ps->cmd);
+      out1c('\n');
+      }
+      if (--procno <= 0)
+        break;
+    }
+    jp->changed = 0;
+    if (jp->state == JOBDONE) {
+      freejob(jp);
+    }
+  }
+}
+
+/*
+ * Mark a job structure as unused.
+ */
+STATIC void freejob(struct job *jp)
+{
+  struct procstat *ps;
+  int i;
+
+  INTOFF;
+  for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
+    if (ps->cmd != nullstr)
+      ckfree(ps->cmd);
+  }
+  if (jp->ps != &jp->ps0)
+    ckfree(jp->ps);
+  jp->used = 0;
+#if JOBS
+  if (curjob == jp - jobtab + 1)
+    curjob = 0;
+#endif
+  INTON;
+}
+
+int waitcmd(int argc, char **argv)
+{
+  struct job *job;
+  int status;
+  struct job *jp;
+
+  if (argc > 1) {
+    job = getjob(argv[1]);
+  } else {
+    job = NULL;
+  }
+  for (;;) {  /* loop until process terminated or stopped */
+    if (job != NULL) {
+      if (job->state) {
+        status = job->ps[job->nprocs - 1].status;
+        if ((status & 0xFF) == 0)
+          status = status >> 8 & 0xFF;
+#if JOBS
+        else if ((status & 0xFF) == 0177)
+          status = (status >> 8 & 0x7F) + 128;
+#endif
+        else
+          status = (status & 0x7F) + 128;
+        if (! iflag)
+          freejob(job);
+        return status;
+      }
+    } else {
+      for (jp = jobtab ; ; jp++) {
+        if (jp >= jobtab + njobs) {  /* no running procs */
+          return 0;
+        }
+        if (jp->used && jp->state == 0)
+          break;
+      }
+    }
+    dowait(1, (struct job *)NULL);
+  }
+}
+
+int jobidcmd(int argc, char **argv)
+{
+  struct job *jp;
+  int i;
+
+  jp = getjob(argv[1]);
+  for (i = 0 ; i < jp->nprocs ; ) {
+    out1fmt("%d", jp->ps[i].pid);
+    out1c(++i < jp->nprocs? ' ' : '\n');
+  }
+  return 0;
+}
+
+/*
+ * Convert a job name to a job structure.
+ */
+STATIC struct job *getjob(char *name)
+{
+  int jobno;
+  register struct job *jp;
+  int pid;
+  int i;
+
+  if (name == NULL) {
+#if JOBS
+currentjob:
+    if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
+      sh_error("No current job");
+    return &jobtab[jobno - 1];
+#else
+    sh_error("No current job");
+#endif
+  } else if (name[0] == '%') {
+    if (is_digit(name[1])) {
+      jobno = number(name + 1);
+      if (jobno > 0 && jobno <= njobs
+       && jobtab[jobno - 1].used != 0)
+        return &jobtab[jobno - 1];
+#if JOBS
+    } else if (name[1] == '%' && name[2] == '\0') {
+      goto currentjob;
+#endif
+    } else {
+      register struct job *found = NULL;
+      for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
+        if (jp->used && jp->nprocs > 0
+         && prefix(name + 1, jp->ps[0].cmd)) {
+          if (found)
+            sh_error("%s: ambiguous", name);
+          found = jp;
+        }
+      }
+      if (found)
+        return found;
+    }
+  } else if (is_number(name)) {
+    pid = number(name);
+    for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
+      if (jp->used && jp->nprocs > 0
+       && jp->ps[jp->nprocs - 1].pid == pid)
+        return jp;
+    }
+  }
+  sh_error("No such job: %s", name);
+  return NULL;
+}
+
+static struct job *
+growjobtab(void)  
+{         
+  size_t length;   
+  ptrdiff_t offset;
+  struct job *j_new, *j_old;
+
+  length = njobs * sizeof(*j_new);
+  j_old = jobtab;  
+  j_new = ckrealloc(j_old, length + 4 * sizeof(*j_new));
+
+  offset = (char *)j_new - (char *)j_old;
+  if (offset) { 
+    /* Relocate pointers */
+    size_t l = length;
+
+    j_old = (struct job *)((char *)j_old + l);
+    while (l) { 
+      l -= sizeof(*j_new);
+      j_old--; 
+#define joff(p) ((struct job *)((char *)(p) + l))   
+#define jmove(p) (p) = (void *)((char *)(p) + offset)
+      if (joff(j_new)->ps == &j_old->ps0)
+        jmove(joff(j_new)->ps);
+    }     
+#undef joff     
+#undef jmove    
+  }       
+
+  jobtab = j_new;  
+  j_new = (struct job *)((char *)j_new + length);
+  j_old = j_new + 3;  
+  do {      
+    j_old->used = 0;
+  } while (--j_old >= j_new);
+  return j_new;
+}
+/*
+ * Return a new job structure,
+ */
+struct job *makejob(union node *node, int nprocs)
+{
+  int i;
+  struct job *jp;
+
+  INTOFF;
+  for (i = njobs, jp = jobtab ; ; jp++) {
+    if (--i < 0) {
+      if (njobs == 0) {
+        jobtab = ckmalloc(4 * sizeof jobtab[0]);
+        memset(jobtab, 0, (4 * sizeof jobtab[0]));
+      } else {
+        jp = growjobtab();
+      }
+      jp = jobtab + njobs;
+      njobs += 4;   
+      break;
+    }
+    if (jp->used == 0)
+      break;
+  }
+  memset(jp, 0, sizeof(struct job));
+  jp->state = 0;
+  jp->used = 1;
+  jp->changed = 0;
+  jp->nprocs = 0;
+#if JOBS
+  jp->jobctl = jobctl;
+#endif
+  if (nprocs > 1) {
+    jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
+  } else {
+    jp->ps = &jp->ps0;
+  }
+  INTON;
+  TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1));
+  return jp;
+}
+
+/*
+ * Fork of a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *  FORK_FG - Fork off a foreground process.
+ *  FORK_BG - Fork off a background process.
+ *  FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *       process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ */
+int forkshell(struct job *jp, union node *n, int mode)
+{
+  int pid;
+  int pgrp;
+
+  TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode));
+  INTOFF;
+  pid = fork();
+  if (pid == -1) {
+    TRACE(("Fork failed, errno=%d\n", errno));
+    INTON;
+    sh_error("Cannot fork");
+  }
+  if (pid == 0) {
+    int wasroot;
+
+    TRACE(("Child shell %d\n", getpid()));
+    wasroot = rootshell;
+    rootshell = 0;
+
+    closescript();
+    INTON;
+    clear_traps();
+#if JOBS
+    jobctl = 0;    /* do job control only in root shell */
+    if (wasroot && mode != FORK_NOJOB && jflag) {
+      if (jp == NULL || jp->nprocs == 0)
+        pgrp = getpid();
+      else
+        pgrp = jp->ps[0].pid;
+      setpgid(0, pgrp);
+      if ((mode == FORK_FG) && (pgrp > 0)) {
+        /*** this causes superfluous TIOCSPGRPS ***/
+        if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
+          sh_error("TIOCSPGRP failed, errno=%d\n", errno);
+      }
+      setsignal(SIGTSTP);
+      setsignal(SIGTTOU);
+    } else if (mode == FORK_BG) {
+      ignoresig(SIGINT);
+      ignoresig(SIGQUIT);
+      if ((jp == NULL || jp->nprocs == 0)
+        && ! fd0_redirected_p ()) {
+        close(0);
+        if (open("/dev/null", O_RDONLY) != 0)
+          sh_error("Can't open /dev/null");
+      }
+    }
+#else
+    if (mode == FORK_BG) {
+      ignoresig(SIGINT);
+      ignoresig(SIGQUIT);
+      if ((jp == NULL || jp->nprocs == 0)
+        && ! fd0_redirected_p ()) {
+        close(0);
+        if (open("/dev/null", O_RDONLY) != 0)
+          sh_error("Can't open /dev/null");
+      }
+    }
+#endif
+    if (wasroot && iflag) {
+      setsignal(SIGINT);
+      setsignal(SIGQUIT);
+      setsignal(SIGTERM);
+    }
+    return pid;
+  }
+  if (rootshell && mode != FORK_NOJOB && jflag) {
+    if (jp == NULL || jp->nprocs == 0)
+      pgrp = pid;
+    else
+      pgrp = jp->ps[0].pid;
+    setpgid(pid, pgrp);
+  }
+  if (mode == FORK_BG)
+    backgndpid = pid;    /* set $! */
+  if (jp) {
+    struct procstat *ps = &jp->ps[jp->nprocs++];
+    ps->pid = pid;
+    ps->status = -1;
+    ps->cmd = nullstr;
+    if (iflag && rootshell && n)
+      ps->cmd = commandtext(n);
+  }
+  INTON;
+  TRACE(("In parent shell:  child = %d\n", pid));
+  return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell.  This means that an infinite loop started by an inter-
+ * active user may be hard to kill.  With job control turned off, an
+ * interactive user may place an interactive program inside a loop.  If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop.  The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * forground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ */
+int waitforjob(register struct job *jp)
+{
+#if JOBS
+  int mypgrp = getpgid(0);
+#endif
+  int status;
+  int st;
+
+  INTOFF;
+  TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
+  while (jp->state == 0) {
+    dowait(1, jp);
+  }
+#if JOBS
+  if (jp->jobctl) {
+    if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
+      sh_error("TIOCSPGRP failed, errno=%d\n", errno);
+  }
+  if (jp->state == JOBSTOPPED)
+    curjob = jp - jobtab + 1;
+#endif
+  status = jp->ps[jp->nprocs - 1].status;
+  /* convert to 8 bits */
+  if ((status & 0xFF) == 0)
+    st = status >> 8 & 0xFF;
+#if JOBS
+  else if ((status & 0xFF) == 0177)
+    st = (status >> 8 & 0x7F) + 128;
+#endif
+  else
+    st = (status & 0x7F) + 128;
+  if (! JOBS || jp->state == JOBDONE)
+    freejob(jp);
+  CLEAR_PENDING_INT;
+  if ((status & 0x7F) == SIGINT)
+    kill(getpid(), SIGINT);
+  INTON;
+  return st;
+}
+
+/*
+ * Wait for a process to terminate.
+ */
+STATIC int dowait(int block, struct job *job)
+{
+  int pid;
+  int status;
+  struct procstat *sp;
+  struct job *jp;
+  struct job *thisjob;
+  int done;
+  int stopped;
+  int core;
+
+  TRACE(("dowait(%d) called\n", block));
+  do {
+    pid = waitproc(block, &status);
+    TRACE(("wait returns %d, status=%d\n", pid, status));
+  } while (pid == -1 && errno == EINTR);
+  if (pid <= 0)
+    return pid;
+  INTOFF;
+  thisjob = NULL;
+  for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
+    if (jp->used) {
+      done = 1;
+      stopped = 1;
+      for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
+        if (sp->pid == -1)
+          continue;
+        if (sp->pid == pid) {
+          TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
+          sp->status = status;
+          thisjob = jp;
+        }
+        if (sp->status == -1)
+          stopped = 0;
+        else if ((sp->status & 0377) == 0177)
+          done = 0;
+      }
+      if (stopped) {    /* stopped or done */
+        int state = done? JOBDONE : JOBSTOPPED;
+        if (jp->state != state) {
+          TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
+          jp->state = state;
+#if JOBS
+          if (done && curjob == jp - jobtab + 1)
+            curjob = 0;    /* no current job */
+#endif
+        }
+      }
+    }
+  }
+  INTON;
+  if (! rootshell || ! iflag || (job && thisjob == job)) {
+#if JOBS
+    if ((status & 0xFF) == 0177)
+      status >>= 8;
+#endif
+    core = status & 0x80;
+    status &= 0x7F;
+    if (status != 0 && status != SIGINT && status != SIGPIPE) {
+      if (thisjob != job)
+        outfmt(out2, "%d: ", pid);
+#if JOBS
+      if (status == SIGTSTP && rootshell && iflag)
+        outfmt(out2, "%%%d ", job - jobtab + 1);
+#endif
+      if (status <= MAXSIG && sigmesg[status])
+        out2str(sigmesg[status]);
+      else
+        outfmt(out2, "Signal %d", status);
+      if (core)
+        out2str(" - core dumped");
+      out2c('\n');
+      flushout(&errout);
+    } else {
+      TRACE(("Not printing status: status=%d\n", status));
+    }
+  } else {
+    TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
+    if (thisjob)
+      thisjob->changed = 1;
+  }
+  return pid;
+}
+
+/*
+ * Do a wait system call.  If job control is compiled in, we accept
+ * stopped processes.  If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call.  It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies.  The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process.  Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD.  What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler.  The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called.  If there are any
+ * children to be waited for, it will be.
+ *
+ * If neither SYSV nor BSD is defined, we don't implement nonblocking
+ * waits at all.  In this case, the user will not be informed when
+ * a background process until the next time she runs a real program
+ * (as opposed to running a builtin command or just typing return),
+ * and the jobs command may give out of date information.
+ */
+
+#ifdef SYSV
+STATIC int gotsigchild;
+
+STATIC int onsigchild()
+{
+  gotsigchild = 1;
+}
+#endif
+
+STATIC int waitproc(int block, int *status)
+{
+#ifdef BSD
+  int flags;
+
+#if JOBS
+  flags = WUNTRACED;
+#else
+  flags = 0;
+#endif
+  if (block == 0)
+    flags |= WNOHANG;
+  return wait3((int *)status, flags, (struct rusage *)NULL);
+#else
+#ifdef SYSV
+  int (*save)();
+
+  if (block == 0) {
+    gotsigchild = 0;
+    save = signal(SIGCLD, onsigchild);
+    signal(SIGCLD, save);
+    if (gotsigchild == 0)
+      return 0;
+  }
+  return wait(status);
+#else
+  if (block == 0)
+    return 0;
+  return wait(status);
+#endif
+#endif
+}
+
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command.
+ */
+STATIC char *commandtext(union node *n)
+{
+  char *name;
+
+  cmdnextc = name = ckmalloc(50);
+  cmdnleft = 50 - 4;
+  cmdtxt(n);
+  *cmdnextc = '\0';
+  return name;
+}
+
+STATIC void cmdtxt(union node *n)
+{
+  union node *np;
+  struct nodelist *lp;
+  char *p;
+  int i;
+  char s[2];
+
+  switch (n->type) {
+  case NSEMI:
+    cmdtxt(n->nbinary.ch1);
+    cmdputs("; ");
+    cmdtxt(n->nbinary.ch2);
+    break;
+  case NAND:
+    cmdtxt(n->nbinary.ch1);
+    cmdputs(" && ");
+    cmdtxt(n->nbinary.ch2);
+    break;
+  case NOR:
+    cmdtxt(n->nbinary.ch1);
+    cmdputs(" || ");
+    cmdtxt(n->nbinary.ch2);
+    break;
+  case NPIPE:
+    for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+      cmdtxt(lp->n);
+      if (lp->next)
+        cmdputs(" | ");
+    }
+    break;
+  case NSUBSHELL:
+    cmdputs("(");
+    cmdtxt(n->nredir.n);
+    cmdputs(")");
+    break;
+  case NREDIR:
+  case NBACKGND:
+    cmdtxt(n->nredir.n);
+    break;
+  case NIF:
+    cmdputs("if ");
+    cmdtxt(n->nif.test);
+    cmdputs("; then ");
+    cmdtxt(n->nif.ifpart);
+    cmdputs("...");
+    break;
+  case NWHILE:
+    cmdputs("while ");
+    goto until;
+  case NUNTIL:
+    cmdputs("until ");
+until:
+    cmdtxt(n->nbinary.ch1);
+    cmdputs("; do ");
+    cmdtxt(n->nbinary.ch2);
+    cmdputs("; done");
+    break;
+  case NFOR:
+    cmdputs("for ");
+    cmdputs(n->nfor.var);
+    cmdputs(" in ...");
+    break;
+  case NCASE:
+    cmdputs("case ");
+    cmdputs(n->ncase.expr->narg.text);
+    cmdputs(" in ...");
+    break;
+  case NDEFUN:
+    cmdputs(n->narg.text);
+    cmdputs("() ...");
+    break;
+  case NCMD:
+    for (np = n->ncmd.args ; np ; np = np->narg.next) {
+      cmdtxt(np);
+      if (np->narg.next)
+        cmdputs(" ");
+    }
+    for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
+      cmdputs(" ");
+      cmdtxt(np);
+    }
+    break;
+  case NARG:
+    cmdputs(n->narg.text);
+    break;
+  case NTO:
+    p = ">";  i = 1;  goto redir;
+  case NAPPEND:
+    p = ">>";  i = 1;  goto redir;
+  case NTOFD:
+    p = ">&";  i = 1;  goto redir;
+  case NFROM:
+    p = "<";  i = 0;  goto redir;
+  case NFROMFD:
+    p = "<&";  i = 0;  goto redir;
+redir:
+    if (n->nfile.fd != i) {
+      s[0] = n->nfile.fd + '0';
+      s[1] = '\0';
+      cmdputs(s);
+    }
+    cmdputs(p);
+    if (n->type == NTOFD || n->type == NFROMFD) {
+      s[0] = n->ndup.dupfd + '0';
+      s[1] = '\0';
+      cmdputs(s);
+    } else {
+      cmdtxt(n->nfile.fname);
+    }
+    break;
+  case NHERE:
+  case NXHERE:
+    cmdputs("<<...");
+    break;
+  default:
+    cmdputs("???");
+    break;
+  }
+}
+
+STATIC void cmdputs(char *s)
+{
+  register char *p, *q;
+  register char c;
+  int subtype = 0;
+
+  if (cmdnleft <= 0)
+    return;
+  p = s;
+  q = cmdnextc;
+  while ((c = *p++) != '\0') {
+    if (c == CTLESC)
+      *q++ = *p++;
+    else if (c == CTLVAR) {
+      *q++ = '$';
+      if (--cmdnleft > 0)
+        *q++ = '{';
+      subtype = *p++;
+      if ((subtype & VSTYPE) == VSLENGTH)
+        *q++ = '#';
+    } else if (c == '=' && subtype != 0) {
+      *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
+      subtype = 0;
+    } else if (c == CTLENDVAR) {
+      *q++ = '}';
+    } else if((c == CTLBACKQ) | (c == CTLBACKQ+CTLQUOTE))
+      cmdnleft++;    /* ignore it */
+    else
+      *q++ = c;
+    if (--cmdnleft <= 0) {
+      *q++ = '.';
+      *q++ = '.';
+      *q++ = '.';
+      break;
+    }
+  }
+  cmdnextc = q;
+}
+
+//==================================================================================
+//memalloc - Like malloc, but returns an error when out of space.
+pointer ckmalloc(int nbytes)
+{
+  register pointer p;
+  pointer malloc();
+
+  if ((p = malloc(nbytes)) == NULL)
+    sh_error("Out of space");
+  return p;
+}
+
+/*
+ * Same for realloc.
+ */
+pointer ckrealloc(register pointer p, int nbytes)
+{
+  pointer realloc();
+
+  if ((p = realloc(p, nbytes)) == NULL)
+    sh_error("Out of space");
+  return p;
+}
+
+/*
+ * Make a copy of a string in safe storage.
+ */
+char *savestr(char *s)
+{
+  register char *p;
+
+  p = ckmalloc(strlen(s) + 1);
+  scopy(s, p);
+  return p;
+}
+
+pointer stalloc(int nbytes)
+{
+  register char *p;
+
+  nbytes = ALIGN(nbytes);
+  if (nbytes > stacknleft) {
+    int blocksize;
+    struct stack_block *sp;
+
+    blocksize = nbytes;
+    if (blocksize < MINSIZE)
+      blocksize = MINSIZE;
+    INTOFF;
+    sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
+    sp->prev = stackp;
+    stacknxt = sp->space;
+    stacknleft = blocksize;
+    stackp = sp;
+    INTON;
+  }
+  p = stacknxt;
+  stacknxt += nbytes;
+  stacknleft -= nbytes;
+  return p;
+}
+
+void stunalloc(pointer p)
+{
+  if (p == NULL) {    /*DEBUG */
+    write(2, "stunalloc\n", 10);
+    abort();
+  }
+  stacknleft += stacknxt - (char *)p;
+  stacknxt = p;
+}
+
+void setstackmark(struct stackmark *mark)
+{
+  mark->stackp = stackp;
+  mark->stacknxt = stacknxt;
+  mark->stacknleft = stacknleft;
+}
+
+void popstackmark(struct stackmark *mark)
+{
+  struct stack_block *sp;
+
+  INTOFF;
+  while (stackp != mark->stackp) {
+    sp = stackp;
+    stackp = sp->prev;
+    ckfree(sp);
+  }
+  stacknxt = mark->stacknxt;
+  stacknleft = mark->stacknleft;
+  INTON;
+}
+
+/*
+ * When the parser reads in a string, it wants to stick the string on the
+ * stack and only adjust the stack pointer when it knows how big the
+ * string is.  Stackblock (defined in stack.h) returns a pointer to a block
+ * of space on top of the stack and stackblocklen returns the length of
+ * this block.  Growstackblock will grow this space by at least one byte,
+ * possibly moving it (like realloc).  Grabstackblock actually allocates the
+ * part of the block that has been used.
+ */
+void growstackblock()
+{
+  char *p;
+  int newlen = stacknleft * 2 + 100;
+  char *oldspace = stacknxt;
+  int oldlen = stacknleft;
+  struct stack_block *sp;
+
+  if (stacknxt == stackp->space && stackp != &stackbase) {
+    INTOFF;
+    sp = stackp;
+    stackp = sp->prev;
+    sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
+    sp->prev = stackp;
+    stackp = sp;
+    stacknxt = sp->space;
+    stacknleft = newlen;
+    INTON;
+  } else {
+    p = stalloc(newlen);
+    bcopy(oldspace, p, oldlen);
+    stacknxt = p;      /* free the space */
+    stacknleft += newlen;    /* we just allocated */
+  }
+}
+
+void grabstackblock(int len)
+{
+  len = ALIGN(len);
+  stacknxt += len;
+  stacknleft -= len;
+}
+
+/*
+ * The following routines are somewhat easier to use that the above.
+ * The user declares a variable of type STACKSTR, which may be declared
+ * to be a register.  The macro STARTSTACKSTR initializes things.  Then
+ * the user uses the macro STPUTC to add characters to the string.  In
+ * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
+ * grown as necessary.  When the user is done, she can just leave the
+ * string there and refer to it using stackblock().  Or she can allocate
+ * the space for it using grabstackstr().  If it is necessary to allow
+ * someone else to use the stack temporarily and then continue to grow
+ * the string, the user should use grabstack to allocate the space, and
+ * then call ungrabstr(p) to return to the previous mode of operation.
+ *
+ * USTPUTC is like STPUTC except that it doesn't check for overflow.
+ * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
+ * is space for at least one character.
+ */
+char *growstackstr()
+{
+  int len = stackblocksize();
+  if (herefd >= 0 && len >= 1024) {
+    xxwrite(herefd, stackblock(), len);
+    sstrnleft = len - 1;
+    return stackblock();
+  }
+  growstackblock();
+  sstrnleft = stackblocksize() - len - 1;
+  return stackblock() + len;
+}
+
+/*
+ * Called from CHECKSTRSPACE.
+ */
+char *makestrspace()
+{
+  int len = stackblocksize() - sstrnleft;
+  growstackblock();
+  sstrnleft = stackblocksize() - len;
+  return stackblock() + len;
+}
+
+void ungrabstackstr(char *s, char *p)
+{
+  stacknleft += stacknxt - s;
+  stacknxt = s;
+  sstrnleft = stacknleft - (p - s);
+}
+
+//==================================================================================
+//mail - Routines to check for mail.
+/*
+ * Print appropriate message(s) if mail has arrived.  If the argument is
+ * nozero, then the value of MAIL has changed, so we just update the
+ * values.
+ */
+void chkmail(int silent)
+{
+  register int i;
+  char *mpath;
+  char *p;
+  register char *q;
+  struct stackmark smark;
+  struct stat statb;
+
+  if (silent)
+    nmboxes = 10;
+  if (nmboxes == 0)
+    return;
+  setstackmark(&smark);
+  mpath = mpathset()? mpathval() : mailval();
+  for (i = 0 ; i < nmboxes ; i++) {
+    p = padvance(&mpath, nullstr);
+    if (p == NULL)
+      break;
+    if (*p == '\0')
+      continue;
+    for (q = p ; *q ; q++);
+    if (q[-1] != '/')
+      abort();
+    q[-1] = '\0';      /* delete trailing '/' */
+#ifdef notdef /* this is what the System V shell claims to do (it lies) */
+    if (stat(p, &statb) < 0)
+      statb.st_mtime = 0;
+    if (statb.st_mtime > mailtime[i] && ! silent) {
+      out2str(pathopt? pathopt : "you have mail");
+      out2c('\n');
+    }
+    mailtime[i] = statb.st_mtime;
+#else /* this is what it should do */
+    if (stat(p, &statb) < 0)
+      statb.st_size = 0;
+    if (statb.st_size > mailtime[i] && ! silent) {
+      out2str(pathopt? pathopt : "you have mail");
+      out2c('\n');
+    }
+    mailtime[i] = statb.st_size;
+#endif
+  }
+  nmboxes = i;
+  popstackmark(&smark);
+}
+
+//==================================================================================
+//input - reading scripts.
+/*
+ * Read a line from the script.
+ */
+char *pfgets(char *line, int len)
+{
+  register char *p = line;
+  int nleft = len;
+  int c;
+
+  while (--nleft > 0) {
+    c = pgetc_macro();
+    if (c == PEOF) {
+      if (p == line)
+        return NULL;
+      break;
+    }
+    *p++ = c;
+    if (c == '\n')
+      break;
+  }
+  *p = '\0';
+  return line;
+}
+
+/*
+ * Read a character from the script, returning PEOF on end of file.
+ * Nul characters in the input are silently discarded.
+ */
+static int pgetc()
+{
+  return pgetc_macro();
+}
+
+static void get_win_size(unsigned int * x, unsigned int *y)                          
+{                                         
+  struct winsize win;                             
+  char *temp;                                 
+
+  win.ws_row = 0;                               
+  win.ws_col = 0;                               
+  ioctl(0, TIOCGWINSZ, &win);                         
+  *y = (!win.ws_row)? ((temp = getenv("ROWS"))!=NULL)? atoi(temp):24:win.ws_row;                                                    
+  *x = (!win.ws_col)?((temp = getenv("COLUMN"))!=NULL)? atoi(temp):80:win.ws_col;
+}
+#ifdef CTRL
+#undef CTRL
+#endif
+
+#define CTRL(a) ((a) & ~0x40)
+
+static int cmpstring(const void *p1, const void *p2)
+{
+  return strcmp(* (char * const *) p1, * (char * const *) p2);
+}
+
+static char *contosp_strdup(char *input)
+{
+  char *tmp;
+  char *tmpbuf;
+  if(input == NULL) return NULL;
+  tmp = tmpbuf = xmalloc(strlen(input) *2);
+  if((*input == '~') && (*(input+1) == '/')){
+    struct passwd *pw;
+    uid_t uid = getuid();
+    pw = getpwuid(uid);
+    strcpy(tmpbuf, pw->pw_dir);
+    tmp =  tmpbuf + strlen(pw->pw_dir);
+    input++;
+  }
+  for (; *input != '\0'; input++, tmp++) {
+    if(*input == '\\'){
+      if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *(input+1)))
+        input++;
+    }
+    *tmp = *input;
+  }
+  *tmp = '\0';
+  return tmpbuf;
+}
+
+static char *sptocon_strdup(char *input, char last)
+{
+  char *tmp;
+  char *tmpbuf = xmalloc(strlen(input)*2);
+  for (tmp = tmpbuf; *input != '\0'; input++, tmp++) {
+    if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *input) && !last) {
+      *tmp = '\\';
+      tmp++;
+    }
+    *tmp = *input;
+  }
+  *tmp = '\0';
+  return tmpbuf;
+}
+
+static char *input_tab(int istab_pressed, char *inp_str, int *cnt)
+{
+  char lastslash;
+  char *pathlist = NULL; //':' separated list of paths
+  char *slash = NULL, *path, *filecomp = NULL;
+  char *back_inp_str = NULL, *back_pathlist = NULL;
+  char *ret = NULL, *sl_back = NULL, *filecomp_p = NULL;
+  static char **files_list = NULL;
+  static char match[NAME_MAX] = {0,};
+  static int count = 0;
+  int myindex, str_len, filecomp_len = 0;
+  static int max_line_len = 0;
+  //segragate dirpath and filename and generate a paths list
+  if(!inp_str) return NULL;
+  back_inp_str = inp_str;
+  back_pathlist = pathlist = xstrdup(pathval());
+  //segregation
+  lastslash = ((inp_str[strlen(inp_str) -1 ] == '\\') && (inp_str[strlen(inp_str) -2 ] != '\\'));
+
+  while(*inp_str && isspace(*inp_str)) inp_str++;    /*remove leading spaces */
+  while((slash = strchr(inp_str, ' '))){
+    if(*(slash-1) == '\\') sl_back = inp_str;
+    else sl_back = NULL;
+    inp_str = slash + 1;    //points past space ' '.
+    if(back_pathlist) free(back_pathlist);
+    back_pathlist = pathlist = xgetcwd();
+  }
+
+  if(sl_back) inp_str = sl_back;
+  inp_str = contosp_strdup(inp_str);
+  free(back_inp_str);
+
+  slash = strrchr(inp_str, '/');
+  if(slash) {
+    if(slash[1] == '\0') {
+      pathlist = inp_str;
+      filecomp = NULL;
+    }else {
+      if(back_pathlist) free(back_pathlist);
+      back_pathlist = pathlist = (char*)xmalloc(slash - inp_str + 2);
+      memcpy(pathlist, inp_str, slash - inp_str + 1);
+      pathlist[slash - inp_str + 1] = '\0';
+      filecomp = slash + 1;
+    }
+  }
+  else filecomp = (*inp_str == '\0')? NULL: inp_str;
+
+  if(istab_pressed) goto PRINT_GO;
+  else {
+    for(myindex = 0; myindex < count; myindex++)
+      free(files_list[myindex]);
+    free(files_list);
+    files_list = NULL;
+    count = 0;
+    match[0] = '\0';
+    max_line_len = 0;
+  }
+
+  files_list = (char**)xmalloc(256 * sizeof(char*));
+  if(filecomp){
+    filecomp_p = sptocon_strdup(filecomp, 0);
+    filecomp_len = strlen(filecomp_p);
+  }
+
+  while((path = strsep(&pathlist, ":"))) {
+    DIR *dirp = opendir(path);
+    struct dirent *ent = NULL;
+    if(!dirp) continue;
+    while((ent = readdir(dirp)) != NULL) {
+      if(filecomp && (strncmp(filecomp, ent->d_name, strlen(filecomp)) == 0)) {
+        if ((ent->d_name[0] == '.') && ((!ent->d_name[1])
+            || ((ent->d_name[1] == '.' && !ent->d_name[2]))))
+          continue;
+
+        if(ent->d_type == DT_DIR)
+          files_list[count] = xmsprintf("%s%s", ent->d_name, "/");
+        else files_list[count] = strdup(ent->d_name);
+
+        str_len = strlen(files_list[count]);
+        if(str_len > max_line_len) max_line_len = str_len;
+
+        count++;
+        if(count == 1) strcpy(match, files_list[0]);
+        else {
+          int pos = 0;
+          for(pos = 0; match[pos] && ent->d_name[pos] && match[pos] == ent->d_name[pos]; pos++);
+          if(pos) match[pos] = '\0';
+          if(count%256 == 0)
+            files_list = (char**)realloc(files_list, (count + 256) * sizeof(char*));
+        }
+      }
+      else if(!filecomp) {
+         if((ent->d_name[0] == '.') && ((!ent->d_name[1])
+            || ((ent->d_name[1] == '.' && !ent->d_name[2]))))
+          continue;
+
+        if(ent->d_type == DT_DIR)
+          files_list[count] = xmsprintf("%s%s", ent->d_name, "/");
+        else files_list[count] = strdup(ent->d_name);
+
+        str_len = strlen(files_list[count]);
+        if(str_len > max_line_len) max_line_len = str_len;
+
+        count++;
+        if(count%256 == 0) {
+          files_list = (char**)xrealloc(files_list, (count + 256) * sizeof(char*));
+        }
+      }
+    }
+    closedir(dirp);
+  }
+PRINT_GO:
+  *cnt = count;
+  if(count > 1) {
+    unsigned x = 0, y = 0, word_perline;
+    get_win_size(&x, &y);
+    word_perline = x/(max_line_len + 2);
+    if(!word_perline) word_perline = 1;
+
+    qsort(files_list, count, sizeof(char*), cmpstring);
+    for( myindex = 0; istab_pressed && (myindex < count); myindex++) {
+      char *filelistindex_p = sptocon_strdup(files_list[myindex], 0);
+      if(myindex % word_perline == 0) printf("\n");
+      printf("%-*s  " , max_line_len, filelistindex_p);
+      free(filelistindex_p);
+    }
+  } else if(count == 1){
+    char *mstr = NULL;
+    if(files_list[0][strlen(files_list[0]) -1] != 0x2F) //char '/'
+    {
+      char *filelist_p = sptocon_strdup(files_list[0], 0);
+      mstr = xmsprintf("%s%c", filelist_p, ' ');
+      free(filelist_p);
+    }
+    else mstr = sptocon_strdup(files_list[0], 0);
+    ret = strdup(mstr + filecomp_len);
+    free(mstr);
+    goto free_ret;
+  }else{
+    ret = NULL;
+    goto free_ret;
+  }
+  ret = (!filecomp)?NULL:(strlen(match) == strlen(filecomp))?NULL:(sptocon_strdup(match + filecomp_len, lastslash));
+
+free_ret :
+  if(filecomp_p) free(filecomp_p);
+  if(back_pathlist) free(back_pathlist);
+  free(inp_str);
+  return ret;
+}
+
+void move_cursor(int dir, int num)
+{
+  switch(dir)
+  {
+    case LEFT:
+      printf("\033[%dD",num);
+      break;
+    case RIGHT:
+      printf("\033[%dC",num);
+      break;
+  }
+}
+
+int get_key_code(char *ch, int i)
+{
+
+  static keycode_map_t type2[] = {
+    {"OA",KEY_UP},
+    {"OB",KEY_DOWN},
+    {"OC",KEY_RIGHT},
+    {"OD",KEY_LEFT},
+    {"OH",KEY_HOME},
+    {"OF",KEY_END},
+    {"[A",KEY_UP},
+    {"[B",KEY_DOWN},
+    {"[C",KEY_RIGHT},
+    {"[D",KEY_LEFT},
+    {"[H",KEY_HOME},
+    {"[F",KEY_END},
+    {NULL, 0}
+  };
+
+  static keycode_map_t type3[] = {
+    {"[1~", KEY_HOME},
+    {"[2~", KEY_INSERT},
+    {"[3~", KEY_DELETE},
+    {"[4~", KEY_END},
+    {"[5~", KEY_PAGEUP},
+    {"[6~", KEY_PAGEDN},
+    {"[7~", KEY_HOME},
+    {"[8~", KEY_END},
+    {NULL, 0}
+  };
+
+  static keycode_map_t type5[] = {
+    {"[1;5A", KEY_CTRLU},
+    {"[1;5B", KEY_CTRLD},
+    {"[1;5C", KEY_CTLRT},
+    {"[1;5D", KEY_CTLLF},
+    {NULL, 0}
+  };
+
+  void *keytable[5] = {NULL, type2, type3, NULL, type5};
+
+  if( i > 5) return -1; //TODO
+  keycode_map_t *table;
+  table = (keycode_map_t*)keytable[i-1];
+
+  while(table && table->key) {
+    if(strncmp(ch, table->key, i) == 0)
+      return table->code;
+    table++;
+  }
+  return -1;
+}
+
+char *reverse_serach(int *index, char *buf, char *bk_up, int *curpos)
+{
+  struct termios old_set, new_set;
+  char search_str[BUFSIZ] = {0,};
+  char *p = NULL, *match = NULL, *data = NULL;
+  char *backup_str = NULL;
+  int len = 0, match_found = 0; //intaily no match
+  int i = 0, psval_len = strlen(ps1val());
+  int ctl_c = 0;
+  static char ch[10] = {0,};
+  tcgetattr(0, &old_set);
+  new_set = old_set;
+  new_set.c_lflag &= ~(ISIG);
+  tcsetattr(0, TCSANOW, &new_set);
+
+  if(*index > 0) {
+    backup_str = xzalloc((*index) + 1); //+1 for null termination
+    memcpy(backup_str, buf, *curpos);
+    if(bk_up) {
+      memcpy(&backup_str[*curpos], bk_up, *index-*curpos);
+    }   
+  }
+  struct double_list *head = NULL;
+  struct double_list *tail = NULL;
+  struct double_list *cur = NULL;
+
+  if(hist_list) {
+    head = hist_list;
+    tail = hist_list->prev;
+    hist_list->prev->next = NULL;
+    hist_list->prev = NULL;
+    cur = tail;
+  }
+  move_cursor(LEFT, *curpos + psval_len);
+  printf("\033[K");
+  if(match)
+    p = xmsprintf("(reverse-i-search)'%s': %s" , search_str, data);
+  else {
+    if(*index > 0) p = xmsprintf("(reverse-i-search)'%s': %s", search_str, backup_str);
+    else p = xmsprintf("(reverse-i-search)'%s':", search_str);
+  }
+  printf("%s", p);
+  fflush(stdout);
+  if(data) {
+    free(data);
+    data = NULL;
+  }
+
+  while(1) {
+    len = strlen(search_str);
+    i = read(0, ch, 10);
+    switch(*ch) {
+      case (CTRL('R')):
+          if(cur) {
+          cur = cur->prev;
+        }
+        break;
+      case '\b':
+      case '\x7f':
+        if(len != 0)
+          len--;
+        search_str[len] = '\0';
+        cur = tail;
+        break;
+         case CTRL('C'):
+        ctl_c = 1;
+        goto ret;
+      default :
+        if (*ch < ' ') {
+          fflush(stdout);
+          goto ret;
+        }
+        if(i > 0) { 
+          search_str[len] = *ch;
+          search_str[len+1] = '\0';
+        }
+        break;
+      }
+      
+      while(cur) {
+          match = strstr(cur->data, search_str);
+        if(match) {
+          match_found = 1;
+          *curpos = strlen(p) + (match - cur->data);
+          data = xstrdup(cur->data);
+          char *ptr = strstr(data, "\n");
+          if(ptr) *ptr = '\0';
+          memset(buf ,0, BUFSIZ);
+          memcpy(buf, data, strlen(data));
+          free(p);
+          move_cursor(LEFT, *curpos + psval_len);
+          printf("\033[K");
+          if(match)
+            p = xmsprintf("(reverse-i-search)'%s': %s" , search_str, data);
+          else {
+            if(*index > 0) p = xmsprintf("(reverse-i-search)'%s': %s", search_str, backup_str);
+            else p = xmsprintf("(reverse-i-search)'%s':", search_str);
+          }
+          printf("%s", p);
+          fflush(stdout);
+          if(data) {
+            free(data);
+            data = NULL;
+          }
+          break;
+        }
+        cur = cur->prev;
+      }
+         continue;
+  }
+ret:
+  tcsetattr(0, TCSANOW, &old_set); 
+  move_cursor(LEFT, *curpos + strlen(p));
+  printf("\033[K");
+  printf("%s", ps1val());
+  if(match_found){
+    printf("%s", buf);
+    *index = *curpos = strlen(buf);
+  }
+  else {
+    if(*index > 0) {
+      printf("%s", backup_str);
+      memcpy(buf, backup_str, strlen(backup_str));
+      *index = *curpos = strlen(backup_str);
+    }
+    else *index = *curpos = 0;
+  }
+  fflush(stdout);
+  ch[i] = '\0';
+  if(head) {
+    head->prev = tail;
+    tail->next = head;
+  }
+  if(ctl_c) {
+    move_cursor(LEFT, *curpos + psval_len);
+    *curpos = *index = 0;
+    printf("\033[K");
+    printf("%s", ps1val());
+    fflush(stdout);
+  }
+  if(backup_str) free(backup_str);
+  if(p) free(p);
+  return ch;
+}
+
+int read_term(int fd, char *p, int size)
+{
+  struct termios old_set, new_set;
+  char buf[size], *backup = NULL;
+  static char *bk_str = NULL;
+  int index = 0, curpos = 0;
+  int last_tab = 0;
+  static int i = 0;
+  static char *rem_str = NULL;
+  tcgetattr(parsefile->fd, &old_set);
+  old_set.c_iflag |= ICRNL;
+  new_set = old_set;
+  new_set.c_lflag &= ~(ICANON | ECHO | ECHONL);
+  new_set.c_cc[VMIN] = 1;
+  new_set.c_cc[VTIME] = 0;
+  tcsetattr(parsefile->fd, TCSANOW, &new_set);
+
+  while(1) {
+
+    if(i == 0)
+      i = read(fd, buf + curpos, 10);
+    else if(rem_str){
+      memcpy(buf, rem_str, i);
+      free(rem_str);
+      rem_str = NULL;
+    }
+    if( i <= 0) {
+      //handle error
+      index = i;
+      goto RET_HANDLE;
+    }
+    if(buf[curpos] != '\t') last_tab = 0;
+    else last_tab++;
+read:
+    switch(buf[curpos]) {
+      case '\t':
+        {
+          if(!isatty(parsefile->fd)) {
+                 index++;
+                 curpos++;
+                 break;
+          }
+          char *ret = NULL;
+          int retlen = 0, cnt = 0;
+          ret = input_tab((last_tab > 1) ?1:0, strndup(buf, curpos), &cnt);
+          if(ret && ret[0]){
+            retlen = strlen(ret);
+            memcpy(buf+curpos, ret, retlen);
+            index += retlen;
+            curpos += retlen;
+            last_tab = 0;
+          }
+          if(backup) memcpy(buf+curpos, backup, index - curpos);
+          buf[index] = '\0';
+          if(ret && ret[0]) {
+            if(cnt > 1 && (last_tab > 1))
+              printf("\n%s%s", ps1val(), buf);
+            else{
+              if((curpos - retlen) > 0) move_cursor(LEFT, curpos - retlen);
+              printf("\033[K%s", buf);
+            }
+            if(curpos != index) move_cursor(LEFT, index - curpos);
+          }
+          else if((cnt > 1) && (last_tab > 1)) {
+            printf("\n%s%s", ps1val(),buf);
+            if(curpos != index) move_cursor(LEFT, index - curpos);
+          }
+          fflush(stdout);
+          if(ret) free(ret);
+        }
+        break;
+      case '\b':   /* ^H FALLTHROUGH*/
+      case '\x7f': /* DEL */
+        if(curpos > 0) {
+          printf("\010 \010");
+          if(curpos != index) {
+            printf("\033[K%s", backup);
+            move_cursor(LEFT, index - curpos);
+          }
+          fflush(stdout);
+          index--;
+          curpos--;
+        }
+        break;
+      case CTRL('D'): //EOF [\004]
+        if(index > 0) {
+          --i;
+          continue;
+        }
+        memcpy(p, buf, index);
+        goto RET_HANDLE;
+        break; /*NOT REACHED */
+      case CTRL('K'):
+        if(curpos != index) {
+          printf("\033[K");
+          index = curpos;
+          fflush(stdout);
+        }
+        break;
+      case CTRL('U'):
+        if(curpos > 0) {
+          move_cursor(LEFT, curpos);
+          printf("\033[K");
+          index -= curpos;
+          curpos = 0;
+          if(index > 0) {
+            printf("%s",backup);
+            memcpy(buf, backup, index);
+            move_cursor(LEFT, index);
+          }
+          fflush(stdout);
+        }
+        break;
+
+      case CTRL('L'):
+        printf("\033[2J");
+        printf("\033[H");
+
+        buf[index] = '\0';
+        printf("%s", ps1val());
+        if(index == curpos)
+          printf("%s", buf);
+        else {
+          buf[curpos] = '\0';
+          printf("%s%s", buf, backup);
+          move_cursor(LEFT, index - curpos);
+        }
+        fflush(stdout);
+        break;
+      case CTRL('W'):
+        {
+          int j = curpos;
+          if(curpos > 0) {
+            if(!backup && (curpos < index))
+              backup = strndup(buf+curpos, index - curpos);
+            while(j > 0 && isspace(buf[j-1])) j--;
+            if( j > 0){
+              while(--j > 0 && !isspace(buf[j]));
+              if(isspace(buf[j])) j++;
+            }
+            move_cursor(LEFT, curpos - j);
+            printf("\033[K");
+            if(backup){
+              printf("%s",backup);
+              memcpy(buf + j, backup, index - curpos);
+            }
+            index = index - curpos + j ;
+            curpos = j;
+            if((index - curpos) > 0)
+              move_cursor(LEFT, index - curpos);
+            fflush(stdout);
+          }
+        }
+        break;
+      case CTRL('A'):
+        {
+          buf[curpos] = '\033';
+          buf[curpos+1] =  'O';
+          buf[curpos+2] = 'H';
+          i = 3;
+          goto ESCAPE;
+        }
+      case CTRL('E'): 
+        {
+          buf[curpos] = '\033';
+          buf[curpos+1] =  'O';
+          buf[curpos+2] = 'F';
+          i = 3;
+          goto ESCAPE;
+        }
+      case CTRL('N'): //For Key Down.
+        {
+          buf[curpos] = '\033';
+          buf[curpos+1] =  '[';
+          buf[curpos+2] = 'B';
+          i = 3;
+          goto ESCAPE;
+        }
+      case CTRL('P'): //For Key Up.
+        {
+          buf[curpos] = '\033';
+          buf[curpos+1] =  '[';
+          buf[curpos+2] = 'A';
+          i = 3;
+          goto ESCAPE;
+        }
+      case '\033': //escape key seq.
+ESCAPE:
+        {
+          int key, ret = 0;
+          char key_buf[10];
+          i--;
+          if(i) {
+            memcpy(key_buf, buf+curpos+1, i);
+          }
+          if(backup) {
+            memcpy(buf+curpos, backup, index - curpos);
+            free(backup);
+            backup = NULL;
+          }
+          if(i) {
+            key = get_key_code(key_buf, i);
+            switch(key) {
+              case KEY_LEFT:
+                if(curpos > 0) {
+                  move_cursor(LEFT, 1);
+                  fflush(stdout);
+                  ret = -1;
+                }
+                break;
+              case KEY_RIGHT:
+                if(index > curpos) {
+                  move_cursor(RIGHT, 1);
+                  fflush(stdout);
+                  ret = 1;
+                }
+                break;
+              case KEY_DELETE:
+                if(backup) free(backup);
+                backup = strndup(buf+curpos+1, index - curpos -1);
+                if(curpos >= 0 && curpos < index) {
+                  if(curpos != index) {
+                    printf("\033[K");
+                    printf("%s",backup);
+                    if((index - curpos) > 1) move_cursor(LEFT, index - curpos -1);
+                  }
+                  fflush(stdout);
+                  index--;
+                  memcpy(buf+curpos, backup, index - curpos);
+                }
+                free(backup);
+                backup = NULL;
+                break;
+              case KEY_HOME:
+                if(curpos != 0){
+                  move_cursor(LEFT, curpos);
+                  curpos = 0;
+                  fflush(stdout);
+                }
+                break;
+              case KEY_END:
+                if(curpos != index){
+                  move_cursor(RIGHT, index - curpos);
+                  curpos = index;
+                  fflush(stdout);
+                }
+                break;
+              case KEY_CTLLF:
+                {
+                  int j = curpos;
+                  if(curpos > 0) {
+                    while(j > 0 && isspace(buf[j-1])) j--;
+                    if( j > 0){
+                      while(--j > 0 && !isspace(buf[j]));
+                      if(isspace(buf[j])) j++;
+                    }
+                    move_cursor(LEFT, curpos - j);
+                    curpos = j;
+                    fflush(stdout);
+                  }
+                }
+                break;
+              case KEY_CTLRT:
+                {
+                  int j = curpos;
+                  if(curpos < index) {
+                    while(j <= index && isspace(buf[j])) j++;
+                    if( j < index)
+                      while(++j < index && !isspace(buf[j]));
+                    move_cursor(RIGHT, j - curpos);
+                    curpos = j;
+                    fflush(stdout);
+                  }
+                }
+                break;
+              case KEY_CTRLD:
+              case KEY_CTRLU:
+                break;
+              case KEY_UP:
+              case KEY_DOWN:
+                {
+                  if(!bk_str && (key == KEY_UP))
+                    bk_str = xstrndup(buf, index);
+
+                  int len = 0;
+                  char *cmd_str = NULL;
+                  if(!hist_list)
+                    break;
+                  struct double_list *head = hist_list;
+                  struct double_list *tail = hist_list->prev;
+                  cmd_str = search_in_history(key);
+                  head->prev = tail;
+                  tail->next = head;
+                  if(cmd_str == NULL && key == KEY_UP)
+                    break;
+                  if(cmd_str == NULL && key == KEY_DOWN) {
+                    if(curpos) {
+                      if(bk_str) {
+                        move_cursor(LEFT, curpos);
+                        printf("\033[K");
+                        printf("%s", bk_str);
+                        fflush(stdout);
+                        index = curpos = strlen(bk_str);
+                        memcpy(buf, bk_str, index);
+                        free(bk_str);
+                        bk_str = NULL;
+                      }
+                    }
+                  }
+                  else {
+                    cmd_str = xstrdup(cmd_str);
+                    len = strlen(cmd_str);
+                    if(cmd_str[len-1] == '\n') {
+                      len--;
+                      cmd_str[len] = '\0';
+                    }
+                    memcpy(buf, cmd_str, len);
+                    if(curpos) {
+                      move_cursor(LEFT, curpos);
+                      printf("\033[K");
+                      curpos = 0;
+                    }
+                    printf("%s", cmd_str);
+                    index = len;
+                    curpos = len;
+                    fflush(stdout);
+                    free(cmd_str);
+                  }
+                }
+                break;
+            }
+          }
+          curpos += ret;
+          if(backup) free(backup);
+          backup = strndup(buf+curpos, index - curpos);
+          i = 1;
+        }
+        break;
+      case '\n':
+        index++;
+        curpos++;
+        if(curpos == index)
+          memcpy(p, buf, index);
+        else {
+          memcpy(p, buf, curpos -1);
+          memcpy(p+curpos -1, backup, index - curpos);
+          p[index -1] = '\n';
+          free(backup);
+          backup = NULL;
+        }
+        if(isatty(parsefile->fd)) printf("\n");
+        fflush(stdout);
+        --i;
+        if(i > 0) {
+          rem_str = xmalloc(i + 1);
+          memcpy(rem_str, buf + curpos, i);
+          rem_str[i] = '\0';
+        }
+        if(bk_str) {
+          free(bk_str);
+          bk_str = NULL;
+        }
+        goto RET_HANDLE;
+        break; /*NOT REACHED */
+      case CTRL('R'):
+        {   
+          char *ch = NULL;
+          ch = reverse_serach(&index, buf, backup, &curpos);
+          if(backup) {
+            free(backup);
+            backup = NULL;
+          }
+          if(ch) {
+            memcpy(buf+curpos, ch, strlen(ch));
+            i = strlen(ch);
+          }
+          else
+            break;
+          goto read;
+        }   
+        break;
+      default:
+        if(buf[curpos] < ' ' || buf[curpos] > 255)
+          break;
+        if(isatty(parsefile->fd)) {
+          putchar(buf[curpos]);
+
+          if(curpos != index /* && backup*/) {
+            printf("%s",backup);
+            move_cursor(LEFT, index - curpos);
+          }
+          fflush(stdout);
+        }
+        index++;
+        curpos++;
+        break;
+    }
+    i--;
+  } //while(1)
+RET_HANDLE:
+  if(backup) free(backup);
+  tcsetattr(fd, TCSANOW, &old_set);
+  return index;
+}
+
+/*
+ * Refill the input buffer and return the next input character:
+ *
+ * 1) If a string was pushed back on the input, switch back to the regular
+ *  buffer.
+ * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
+ *  from a string so we can't refill the buffer, return EOF.
+ * 3) Call read to read in the characters.
+ * 4) Delete all nul characters from the buffer.
+ */
+int preadbuffer()
+{
+  register char *p, *q;
+  register int i;
+
+  if (parsefile->strpush) {
+    popstring();
+    if (--parsenleft >= 0)
+      return (*parsenextc++);
+  }
+  if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+    return PEOF;
+  flushout(&output);
+  flushout(&errout);
+retry:
+  p = parsenextc = parsefile->buf;
+  if(parsefile->fd != STDIN_FILENO)
+    parselleft = i = read(parsefile->fd, p, BUFSIZ);
+  else
+    parselleft = i = read_term(parsefile->fd, p, BUFSIZ);
+  if ((i > 1) && isatty(parsefile->fd))
+    add_to_history(p);
+
+  if (i <= 0) {
+    if (i < 0) {
+      if (errno == EINTR)
+        goto retry;
+      if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
+        int flags = fcntl(0, F_GETFL, 0);
+        if (flags >= 0 && flags & O_NONBLOCK) {
+          flags &=~ O_NONBLOCK;
+          if (fcntl(0, F_SETFL, flags) >= 0) {
+            out2str("sh: turning off NDELAY mode\n");
+            goto retry;
+          }
+        }
+      }
+    }
+    parselleft =  parsenleft = EOF_NLEFT;
+    return PEOF;
+  }
+  parsenleft = i - 1;
+
+  
+  /* delete nul characters */
+  for (;;) {
+    if (*p++ == '\0')
+      break;
+    if ((parselleft = --i) <= 0) {
+      q = p;
+      goto ret;
+    }
+  }
+  q = p - 1;
+  while (--i > 0) {
+    if (*p != '\0')
+      *q++ = *p;
+    p++;
+  }
+  if (q == parsefile->buf)
+    goto retry;      /* buffer contained nothing but nuls */
+  parsenleft = q - parsefile->buf - 1;
+
+ret:
+  if (vflag) {
+    char save = *q;
+    *q = '\0';
+    out2str(parsenextc);
+    flushout(out2);
+    *q = save;                                                                                                                                                                                   
+  }
+ return *parsenextc++;
+}
+
+/*
+ * Undo the last call to pgetc.  Only one character may be pushed back.
+ * PEOF may be pushed back.
+ */
+void pungetc()
+{
+  parsenleft++;
+  parsenextc--;
+}
+
+/*
+ * Push a string back onto the input.  This code doesn't work if the user
+ * tries to push back more than one string at once.
+ */
+void ppushback(char *string, int length)
+{
+  pushedstring = parsenextc;
+  pushednleft = parsenleft;
+  parsenextc = string;
+  parsenleft = length;
+}
+
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+void pushstring(char *s, int len, void *ap)
+{
+    struct strpush *sp;
+
+    INTOFF;
+/*debugprintf("*** calling pushstring: %s, %d\n", s, len);*/
+    if (parsefile->strpush) {
+        sp = ckmalloc(sizeof (struct strpush));
+        sp->prev = parsefile->strpush;
+        parsefile->strpush = sp;
+    } else
+        sp = parsefile->strpush = &(parsefile->basestrpush);
+    sp->prevstring = parsenextc;
+    sp->prevnleft = parsenleft;
+    sp->prevlleft = parselleft;
+    sp->ap = (struct alias *)ap;
+    if (ap)
+        ((struct alias *)ap)->flag |= ALIASINUSE;
+    parsenextc = s;
+    parsenleft = len;
+    INTON;
+}
+
+void popstring(void)
+{
+    struct strpush *sp = parsefile->strpush;
+
+    INTOFF;
+    if (sp->ap) {
+      checkkwd |= 1;
+      sp->ap->flag &= ~ALIASINUSE;
+    }
+
+    parsenextc = sp->prevstring;
+    parsenleft = sp->prevnleft;
+    parselleft = sp->prevlleft;
+/*debugprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+    if (sp->ap)
+        sp->ap->flag &= ~ALIASINUSE;
+    parsefile->strpush = sp->prev;
+    if (sp != &(parsefile->basestrpush))
+        ckfree(sp);
+    INTON;
+}
+
+
+/*
+ * Set the input to take input from a file.  If push is set, push the
+ * old input onto the stack first.
+ */
+void setinputfile(char *fname, int push)
+{
+  int fd;
+  int fd2;
+
+  INTOFF;
+  if ((fd = open(fname, O_RDONLY)) < 0)
+    sh_error("Can't open %s", fname);
+  if (fd < 10) {
+    fd2 = copyfd(fd, 10);
+    close(fd);
+    if (fd2 < 0)
+      sh_error("Out of file descriptors");
+    fd = fd2;
+  }
+  setinputfd(fd, push);
+  INTON;
+}
+
+/*
+ * Like setinputfile, but takes an open file descriptor.  Call this with
+ * interrupts off.
+ */
+void setinputfd(int fd, int push)
+{
+  if (push) {
+    pushfile();
+    parsefile->buf = ckmalloc(BUFSIZ);
+  }
+  if (parsefile->fd > 0)
+    close(parsefile->fd);
+  parsefile->fd = fd;
+  if (parsefile->buf == NULL)
+    parsefile->buf = ckmalloc(BUFSIZ);
+  parselleft = parsenleft = 0;
+  plinno = 1;
+}
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+void setinputstring(char *string, int push)
+{
+  INTOFF;
+  if (push)
+    pushfile();
+  parsenextc = string;
+  parselleft = parsenleft = strlen(string);
+  parsefile->buf = NULL;
+  plinno = 1;
+  INTON;
+}
+
+/*
+ * To handle the "." command, a stack of input files is used.  Pushfile
+ * adds a new entry to the stack and popfile restores the previous level.
+ */
+STATIC void pushfile()
+{
+  struct parsefile *pf;
+
+  parsefile->nleft = parsenleft;
+  parsefile->lleft = parselleft;
+  parsefile->nextc = parsenextc;
+  parsefile->linno = plinno;
+  pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
+  pf->prev = parsefile;
+  pf->fd = -1;
+  pf->strpush = NULL;
+  pf->basestrpush.prev = NULL;
+  parsefile = pf;
+}
+
+void popfile()
+{
+  struct parsefile *pf = parsefile;
+
+  INTOFF;
+  if (pf->fd >= 0)
+    close(pf->fd);
+  if (pf->buf)
+    ckfree(pf->buf);
+  while (pf->strpush)
+    popstring();
+  parsefile = pf->prev;
+  ckfree(pf);
+  parsenleft = parsefile->nleft;
+  parselleft = parsefile->lleft;
+  parsenextc = parsefile->nextc;
+  plinno = parsefile->linno;
+  INTON;
+}
+
+/*
+ * Return to top level.
+ */
+void popallfiles()
+{
+  while (parsefile != &basepf)
+    popfile();
+}
+
+/*
+ * Close the file(s) that the shell is reading commands from.  Called
+ * after a fork is done.
+ */
+void closescript()
+{
+  popallfiles();
+  if (parsefile->fd > 0) {
+    close(parsefile->fd);
+    parsefile->fd = 0;
+  }
+}
+
+//==================================================================================
+//expand - Shell variable expand
+/*
+ * Routines to expand arguments to commands.  We have to deal with
+ * backquotes, shell variables, and file metacharacters.
+ */
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+void expandhere(union node *arg, int fd)
+{
+  herefd = fd;
+  expandarg(arg, (struct arglist *)NULL, 0);
+  xxwrite(fd, stackblock(), expdest - stackblock());
+}
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist.  If full is true,
+ * perform splitting and file name expansion.  When arglist is NULL, perform
+ * here document expansion.
+ */
+void expandarg(union node *arg, struct arglist *arglist, int full)
+{
+  struct strlist *sp;
+  char *p;
+
+#if UDIR
+  didudir = 0;
+#endif
+  argbackq = arg->narg.backquote;
+  STARTSTACKSTR(expdest);
+  ifsfirst.next = NULL;
+  ifslastp = NULL;
+  argstr(arg->narg.text, full);
+  if (arglist == NULL)
+    return;      /* here document expanded */
+  STPUTC('\0', expdest);
+  p = grabstackstr(expdest);
+  exparg.lastp = &exparg.list;
+  if (full) {
+    ifsbreakup(p, &exparg);
+    *exparg.lastp = NULL;
+    exparg.lastp = &exparg.list;
+    expandmeta(exparg.list);
+  } else {
+    sp = (struct strlist *)stalloc(sizeof (struct strlist));
+    sp->text = p;
+    *exparg.lastp = sp;
+    exparg.lastp = &sp->next;
+  }
+  while (ifsfirst.next != NULL) {
+    struct ifsregion *ifsp;
+    INTOFF;
+    ifsp = ifsfirst.next->next;
+    ckfree(ifsfirst.next);
+    ifsfirst.next = ifsp;
+    INTON;
+  }
+  *exparg.lastp = NULL;
+  if (exparg.list) {
+    *arglist->lastp = exparg.list;
+    arglist->lastp = exparg.lastp;
+  }
+}
+
+STATIC char *exptilde(char *p, int flag)
+{
+  char c, *startp = p;
+  struct passwd *pw;
+  const char *home;
+  int quotes = flag & (EXP_FULL | EXP_CASE);
+
+  while ((c = *p) != '\0') {
+    switch(c) {
+    case CTLESC:
+      return (startp);
+    case '\210' /*CTLQUOTEMARK*/:
+      return (startp);
+    case ':':
+      if (flag & EXP_VARTILDE)
+        goto done;
+      break;
+    case '/':
+      goto done;
+    }
+    p++;
+  }
+done:
+  *p = '\0';
+  if (*(startp+1) == '\0') {
+    if ((home = lookupvar("HOME")) == NULL)
+      goto lose;
+  } else {
+    if ((pw = getpwnam(startp+1)) == NULL)
+      goto lose;
+    home = pw->pw_dir;
+  }
+  if (*home == '\0')
+    goto lose;
+  *p = c;
+  while ((c = *home++) != '\0') {
+    if (quotes && SQSYNTAX[(int)c] == CCTL)
+      STPUTC(CTLESC, expdest);
+    STPUTC(c, expdest);
+  }
+  return (p);
+lose:
+  *p = c;
+  return (startp);
+}
+
+/*
+ * Perform variable and command substitution.  If full is set, output CTLESC
+ * characters to allow for further processing.  If full is not set, treat
+ * $@ like $* since no splitting will be performed.
+ */
+STATIC void argstr(register char *p, int full)
+{
+  char c;
+  int firsteq = 1;
+
+  if (*p == '~' && (full & (EXP_TILDE | EXP_VARTILDE)))
+    p = exptilde(p, full);
+
+  for (;;) {
+    switch (c = *p++) {
+    case '\0':
+    case CTLENDVAR:
+      goto breakloop;
+    case CTLESC:
+      if (full)
+        STPUTC(c, expdest);
+      c = *p++;
+      STPUTC(c, expdest);
+      break;
+    case CTLVAR:
+      p = evalvar(p, full);
+      break;
+    case CTLBACKQ:
+    case CTLBACKQ|CTLQUOTE:
+      expbackq(argbackq->n, c & CTLQUOTE, full);
+      argbackq = argbackq->next;
+      break;
+    case ':':
+    case '=':
+      /*
+       * sort of a hack - expand tildes in variable
+       * assignments (after the first '=' and after ':'s).
+       */
+      STPUTC(c, expdest);
+      if (full & EXP_VARTILDE && *p == '~') {
+        if (c == '=') {
+          if (firsteq)
+            firsteq = 0;
+          else
+            break;
+        }
+        p = exptilde(p, full);
+      }
+      break;
+    default:
+      STPUTC(c, expdest);
+    }
+  }
+breakloop:;
+}
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+STATIC void expbackq(union node *cmd, int quoted, int full)
+{
+  struct backcmd in;
+  int i;
+  char buf[128];
+  char *p;
+  char *dest = expdest;
+  struct ifsregion saveifs, *savelastp;
+  struct nodelist *saveargbackq;
+  char lastc;
+  int startloc = dest - stackblock();
+  char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
+  int saveherefd;
+
+  INTOFF;
+  saveifs = ifsfirst;
+  savelastp = ifslastp;
+  saveargbackq = argbackq;
+  saveherefd = herefd;
+  herefd = -1;
+  p = grabstackstr(dest);
+  evalbackcmd(cmd, &in);
+  ungrabstackstr(p, dest);
+  ifsfirst = saveifs;
+  ifslastp = savelastp;
+  argbackq = saveargbackq;
+  herefd = saveherefd;
+
+  p = in.buf;
+  lastc = '\0';
+  for (;;) {
+    if (--in.nleft < 0) {
+      if (in.fd < 0)
+        break;
+      while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
+      TRACE(("expbackq: read returns %d\n", i));
+      if (i <= 0)
+        break;
+      p = buf;
+      in.nleft = i - 1;
+    }
+    lastc = *p++;
+    if (lastc != '\0') {
+      if (full && syntax[lastc] == CCTL)
+        STPUTC(CTLESC, dest);
+      STPUTC(lastc, dest);
+    }
+  }
+  if (lastc == '\n') {
+    STUNPUTC(dest);
+  }
+  if (in.fd >= 0)
+    close(in.fd);
+  if (in.buf)
+    ckfree(in.buf);
+  if (in.jp)
+    exitstatus = waitforjob(in.jp);
+  if (quoted == 0)
+    recordregion(startloc, dest - stackblock(), 0);
+  TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+    (dest - stackblock()) - startloc,
+    (dest - stackblock()) - startloc,
+    stackblock() + startloc));
+  expdest = dest;
+  INTON;
+}
+
+STATIC int
+subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags)
+{
+       char *startp;
+       char *loc = NULL;
+       char *q;
+       int c = 0;
+       int saveherefd = herefd;
+       struct nodelist *saveargbackq = argbackq;
+       int amount, how;
+
+       herefd = -1;
+       switch (subtype) {
+       case VSTRIMLEFT:
+       case VSTRIMLEFTMAX:
+       case VSTRIMRIGHT:
+       case VSTRIMRIGHTMAX:
+               how = (varflags & VSQUOTE) ? 0 : EXP_CASE;
+               break;
+       default:
+               how = 0;
+               break;
+       }
+       argstr(p, how);
+       STACKSTRNUL(expdest);
+       herefd = saveherefd;
+       argbackq = saveargbackq;
+       startp = stackblock() + startloc;
+       if (str == NULL)
+           str = stackblock() + strloc;
+
+       switch (subtype) {
+       case VSASSIGN:
+               setvar(str, startp, 0);
+               amount = startp - expdest;
+               STADJUST(amount, expdest);
+               varflags &= ~VSNUL;
+               return 1;
+
+       case VSQUESTION:
+               if (*p != CTLENDVAR) {
+                       outfmt(&errout, "%s\n", startp);
+                       error(NULL);
+               }
+               error("%.*s: parameter %snot set",
+                     (int)(p - str - 1),
+                     str, (varflags & VSNUL) ? "null or "
+                                             : nullstr);
+               /* NOTREACHED */
+
+       case VSTRIMLEFT:
+               for (loc = startp; loc < str; loc++) {
+                       c = *loc;
+                       *loc = '\0';
+                       if (patmatch(str, startp/*, varflags & VSQUOTE*/))
+                               goto recordleft;
+                       *loc = c;
+                       if ((varflags & VSQUOTE) && *loc == CTLESC)
+                               loc++;
+               }
+               return 0;
+
+       case VSTRIMLEFTMAX:
+               for (loc = str - 1; loc >= startp;) {
+                       c = *loc;
+                       *loc = '\0';
+                       if (patmatch(str, startp/*, varflags & VSQUOTE*/))
+                               goto recordleft;
+                       *loc = c;
+                       loc--;
+                       if ((varflags & VSQUOTE) && loc > startp &&
+                           *(loc - 1) == CTLESC) {
+                               for (q = startp; q < loc; q++)
+                                       if (*q == CTLESC)
+                                               q++;
+                               if (q > loc)
+                                       loc--;
+                       }
+               }
+               return 0;
+
+       case VSTRIMRIGHT:
+               for (loc = str - 1; loc >= startp;) {
+                       if (patmatch(str, loc/*, varflags & VSQUOTE*/))
+                               goto recordright;
+                       loc--;
+                       if ((varflags & VSQUOTE) && loc > startp &&
+                           *(loc - 1) == CTLESC) { 
+                               for (q = startp; q < loc; q++)
+                                       if (*q == CTLESC)
+                                               q++;
+                               if (q > loc)
+                                       loc--;
+                       }
+               }
+               return 0;
+
+       case VSTRIMRIGHTMAX:
+               for (loc = startp; loc < str - 1; loc++) {
+                       if (patmatch(str, loc/*, varflags & VSQUOTE*/))
+                               goto recordright;
+                       if ((varflags & VSQUOTE) && *loc == CTLESC)
+                               loc++;
+               }
+               return 0;
+
+       default:
+               abort();
+       }
+
+recordleft:
+       *loc = c;
+       amount = ((str - 1) - (loc - startp)) - expdest;
+       STADJUST(amount, expdest);
+       while (loc != str - 1)
+               *startp++ = *loc++;
+       return 1;
+
+recordright:
+       amount = loc - expdest;
+       STADJUST(amount, expdest);
+       STPUTC('\0', expdest);
+       STADJUST(-1, expdest);
+       return 1;
+}
+
+STATIC char *
+cvtnum(int num, char *buf)
+{
+  char temp[32];
+  int neg = num < 0;
+  char *p = temp + 31;
+
+  temp[31] = '\0';
+
+  do {
+    *--p = num % 10 + '0';
+  } while ((num /= 10) != 0);
+
+  if (neg)
+    *--p = '-';
+
+  while (*p)
+    STPUTC(*p++, buf);
+  return buf;
+}
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+
+STATIC char *
+evalvar(char *p, int flag)
+{
+       int subtype;
+       int varflags;
+       char *var;
+       char *val;
+       int patloc;
+       int c;
+       int set;
+       int special;
+       int startloc;
+       int varlen;
+       int apply_ifs;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       varflags = (unsigned char)*p++;
+       subtype = varflags & VSTYPE;
+       var = p;
+       special = !is_name(*p);
+       p = strchr(p, '=') + 1;
+
+again: /* jump here after setting a variable with ${var=text} */
+       if (special) {
+               set = varisset(*var/*, varflags & VSNUL*/);
+               val = NULL;
+       } else {
+               val = lookupvar(var);
+               if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
+                       val = NULL;
+                       set = 0;
+               } else
+                       set = 1;
+       }
+
+       varlen = 0;
+       startloc = expdest - stackblock();
+
+       if (!set && uflag) {
+               switch (subtype) {
+               case VSNORMAL:
+               case VSTRIMLEFT:
+               case VSTRIMLEFTMAX:
+               case VSTRIMRIGHT:
+               case VSTRIMRIGHTMAX:
+               case VSLENGTH:
+                       error("%.*s: parameter not set",
+                           (int)(p - var - 1), var);
+                       /* NOTREACHED */
+               }
+       }
+
+       if (set && subtype != VSPLUS) {
+               /* insert the value of the variable */
+               if (special) {
+                       varvalue(*var, varflags & VSQUOTE,/* subtype,*/ flag);
+                       if (subtype == VSLENGTH) {
+                               varlen = expdest - stackblock() - startloc;
+                               STADJUST(-varlen, expdest);
+                       }
+               } else {
+                       char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
+                                                                 : BASESYNTAX;
+
+                       if (subtype == VSLENGTH) {
+                               for (;*val; val++)
+                                       varlen++;
+                       } else {
+                               while (*val) {
+                                       if (quotes && syntax[(int)*val] == CCTL)
+                                               STPUTC(CTLESC, expdest);
+                                       STPUTC(*val++, expdest);
+                               }
+
+                       }
+               }
+       }
+
+
+       apply_ifs = ((varflags & VSQUOTE) == 0 ||
+               (*var == '@' && shellparam.nparam != 1));
+
+       switch (subtype) {
+       case VSLENGTH:
+               expdest = cvtnum(varlen, expdest);
+               break;
+
+       case VSNORMAL:
+               break;
+
+       case VSPLUS:
+               set = !set;
+               /* FALLTHROUGH */
+       case VSMINUS:
+               if (!set) {
+                       argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
+                       /*
+                        * ${x-a b c} doesn't get split, but removing the
+                        * 'apply_ifs = 0' apparently breaks ${1+"$@"}..
+                        * ${x-'a b' c} should generate 2 args.
+                        */
+                       /* We should have marked stuff already */
+                       apply_ifs = 0;
+               }
+               break;
+
+       case VSTRIMLEFT:
+       case VSTRIMLEFTMAX:
+       case VSTRIMRIGHT:
+       case VSTRIMRIGHTMAX:
+               if (!set)
+                       break;
+               /*
+                * Terminate the string and start recording the pattern
+                * right after it
+                */
+               STPUTC('\0', expdest);
+               patloc = expdest - stackblock();
+               if (subevalvar(p, NULL, patloc, subtype,
+                              startloc, varflags) == 0) {
+                       int amount = (expdest - stackblock() - patloc) + 1;
+                       STADJUST(-amount, expdest);
+               }
+               /* Remove any recorded regions beyond start of variable */
+               //removerecordregions(startloc);
+               apply_ifs = 1;
+               break;
+
+       case VSASSIGN:
+       case VSQUESTION:
+               if (set)
+                       break;
+               if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
+                       varflags &= ~VSNUL;
+                       /* 
+                        * Remove any recorded regions beyond 
+                        * start of variable 
+                        */
+               //      removerecordregions(startloc);
+                       goto again;
+               }
+               apply_ifs = 0;
+               break;
+
+       default:
+               abort();
+       }
+
+       if (apply_ifs)
+               recordregion(startloc, expdest - stackblock(),
+                            varflags & VSQUOTE);
+
+       if (subtype != VSNORMAL) {      /* skip to end of alternative */
+               int nesting = 1;
+               for (;;) {
+                       if ((c = *p++) == CTLESC)
+                               p++;
+                       else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+                               if (set)
+                                       argbackq = argbackq->next;
+                       } else if (c == CTLVAR) {
+                               if ((*p++ & VSTYPE) != VSNORMAL)
+                                       nesting++;
+                       } else if (c == CTLENDVAR) {
+                               if (--nesting == 0)
+                                       break;
+                       }
+               }
+       }
+       return p;
+}
+
+
+/*
+ * Test whether a specialized variable is set.
+ */
+STATIC int varisset(int name)
+{
+  char **ap;
+
+  if (name == '!') {
+    if (backgndpid == -1)
+      return 0;
+  } else if (name == '@' || name == '*') {
+    if (*shellparam.p == NULL)
+      return 0;
+  } else if ((unsigned)(name -= '1') <= '9' - '1') {
+    ap = shellparam.p;
+    do {
+      if (*ap++ == NULL)
+        return 0;
+    } while (--name >= 0);
+  }
+  return 1;
+}
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ */
+STATIC void varvalue(int name, int quoted, int allow_split)
+{
+  int num;
+  char temp[32];
+  char *p;
+  int i;
+  extern int exitstatus;
+  char sep;
+  char **ap;
+  char const *syntax;
+
+  switch (name) {
+  case '$':
+    num = rootpid;
+    goto numvar;
+  case '?':
+    num = exitstatus;
+    goto numvar;
+  case '#':
+    num = shellparam.nparam;
+    goto numvar;
+  case '!':
+    num = backgndpid;
+numvar:
+    p = temp + 31;
+    temp[31] = '\0';
+    do {
+      *--p = num % 10 + '0';
+    } while ((num /= 10) != 0);
+    while (*p)
+      STPUTC(*p++, expdest);
+    break;
+  case '-':
+    for (i = 0 ; optchar[i] ; i++) {
+      if (optval[i])
+        STPUTC(optchar[i], expdest);
+    }
+    break;
+  case '@':
+    if (allow_split) {
+      sep = '\0';
+      goto allargs;
+    }
+    /* fall through */
+  case '*':
+    sep = ' ';
+allargs:
+    /* Only emit CTLESC if we will do further processing,
+       i.e. if allow_split is set.  */
+    syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
+    for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
+      /* should insert CTLESC characters */
+      while (*p) {
+        if (syntax[*p] == CCTL)
+          STPUTC(CTLESC, expdest);
+        STPUTC(*p++, expdest);
+      }
+      if (*ap)
+        STPUTC(sep, expdest);
+    }
+    break;
+  case '0':
+    p = arg0;
+string:
+    /* Only emit CTLESC if we will do further processing,
+       i.e. if allow_split is set.  */
+    syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
+    while (*p) {
+      if (syntax[*p] == CCTL)
+        STPUTC(CTLESC, expdest);
+      STPUTC(*p++, expdest);
+    }
+    break;
+  default:
+    if ((unsigned)(name -= '1') <= '9' - '1') {
+      p = shellparam.p[name];
+      goto string;
+    }
+    break;
+  }
+}
+
+/*
+ * Record the the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+STATIC void recordregion(int start, int end, int nulonly)
+{
+  register struct ifsregion *ifsp;
+
+  if (ifslastp == NULL) {
+    ifsp = &ifsfirst;
+  } else {
+    ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
+    ifslastp->next = ifsp;
+  }
+  ifslastp = ifsp;
+  ifslastp->next = NULL;
+  ifslastp->begoff = start;
+  ifslastp->endoff = end;
+  ifslastp->nulonly = nulonly;
+}
+
+/*
+ * Break the argument string into pieces based upon IFS and add the
+ * strings to the argument list.  The regions of the string to be
+ * searched for IFS characters have been stored by recordregion.
+ */
+STATIC void ifsbreakup(char *string, struct arglist *arglist)
+{
+  struct ifsregion *ifsp;
+  struct strlist *sp;
+  char *start;
+  register char *p;
+  char *q;
+  char *ifs;
+
+  start = string;
+  if (ifslastp != NULL) {
+    ifsp = &ifsfirst;
+    do {
+      p = string + ifsp->begoff;
+      ifs = ifsp->nulonly? nullstr : ifsval();
+      while (p < string + ifsp->endoff) {
+        q = p;
+        if (*p == CTLESC)
+          p++;
+        if (strchr(ifs, *p++)) {
+          if (q > start || *ifs != ' ') {
+            *q = '\0';
+            sp = (struct strlist *)stalloc(sizeof *sp);
+            sp->text = start;
+            *arglist->lastp = sp;
+            arglist->lastp = &sp->next;
+          }
+          if (*ifs == ' ') {
+            for (;;) {
+              if (p >= string + ifsp->endoff)
+                break;
+              q = p;
+              if (*p == CTLESC)
+                p++;
+              if (strchr(ifs, *p++) == NULL) {
+                p = q;
+                break;
+              }
+            }
+          }
+          start = p;
+        }
+      }
+    } while ((ifsp = ifsp->next) != NULL);
+    if (*start || (*ifs != ' ' && start > string)) {
+      sp = (struct strlist *)stalloc(sizeof *sp);
+      sp->text = start;
+      *arglist->lastp = sp;
+      arglist->lastp = &sp->next;
+    }
+  } else {
+    sp = (struct strlist *)stalloc(sizeof *sp);
+    sp->text = start;
+    *arglist->lastp = sp;
+    arglist->lastp = &sp->next;
+  }
+}
+
+/*
+ * Expand shell metacharacters.  At this point, the only control characters
+ * should be escapes.  The results are stored in the list exparg.
+ */
+STATIC void expandmeta(struct strlist *str)
+{
+  char *p;
+  struct strlist **savelastp;
+  struct strlist *sp;
+  char c;
+
+  while (str) {
+    if (fflag)
+      goto nometa;
+    p = str->text;
+#if UDIR
+    if (p[0] == '/' && p[1] == 'u' && p[2] == '/')
+      str->text = p = expudir(p);
+#endif
+    for (;;) {      /* fast check for meta chars */
+      if ((c = *p++) == '\0')
+        goto nometa;
+      if (c == '*' || c == '?' || c == '[' || c == '!')
+        break;
+    }
+    savelastp = exparg.lastp;
+    INTOFF;
+    if (expdir == NULL)
+      expdir = ckmalloc(4096); /* I hope this is big enough */
+    expmeta(expdir, str->text);
+    ckfree(expdir);
+    expdir = NULL;
+    INTON;
+    if (exparg.lastp == savelastp) {
+      if (! zflag) {
+nometa:
+        *exparg.lastp = str;
+        rmescapes(str->text);
+        exparg.lastp = &str->next;
+      }
+    } else {
+      *exparg.lastp = NULL;
+      *savelastp = sp = expsort(*savelastp);
+      while (sp->next != NULL)
+        sp = sp->next;
+      exparg.lastp = &sp->next;
+    }
+    str = str->next;
+  }
+}
+
+
+#if UDIR
+/*
+ * Expand /u/username into the home directory for the specified user.
+ * We could use the getpw stuff here, but then we would have to load
+ * in stdio and who knows what else.
+ */
+
+#define MAXLOGNAME 32
+#define MAXPWLINE 128
+
+char *pfgets();
+
+STATIC char *expudir(char *path)
+{
+  register char *p, *q, *r;
+  char name[MAXLOGNAME];
+  char line[MAXPWLINE];
+  int i;
+
+  r = path;        /* result on failure */
+  p = r + 3;      /* the 3 skips "/u/" */
+  q = name;
+  while (*p && *p != '/') {
+    if (q >= name + MAXLOGNAME - 1)
+      return r;    /* fail, name too long */
+    *q++ = *p++;
+  }
+  *q = '\0';
+  setinputfile("/etc/passwd", 1);
+  q = line + strlen(name);
+  while (pfgets(line, MAXPWLINE) != NULL) {
+    if (line[0] == name[0] && prefix(name, line) && *q == ':') {
+      /* skip to start of home directory */
+      i = 4;
+      do {
+        while (*++q && *q != ':');
+      } while (--i > 0);
+      if (*q == '\0')
+        break;    /* fail, corrupted /etc/passwd */
+      q++;
+      for (r = q ; *r && *r != '\n' && *r != ':' ; r++);
+      *r = '\0';    /* nul terminate home directory */
+      i = r - q;    /* i = strlen(q) */
+      r = stalloc(i + strlen(p) + 1);
+      scopy(q, r);
+      scopy(p, r + i);
+      TRACE(("expudir converts %s to %s\n", path, r));
+      didudir = 1;
+      path = r;    /* succeed */
+      break;
+    }
+  }
+  popfile();
+  return r;
+}
+#endif
+
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+STATIC void expmeta(char *enddir, char *name)
+{
+  register char *p;
+  char *q;
+  char *start;
+  char *endname;
+  int metaflag;
+  struct stat statb;
+  DIR *dirp;
+  struct dirent *dp;
+  int atend;
+  int matchdot;
+
+  metaflag = 0;
+  start = name;
+  for (p = name ; ; p++) {
+    if (*p == '*' || *p == '?')
+      metaflag = 1;
+    else if (*p == '[') {
+      q = p + 1;
+      if (*q == '!')
+        q++;
+      for (;;) {
+        if (*q == CTLESC)
+          q++;
+        if (*q == '/' || *q == '\0')
+          break;
+        if (*++q == ']') {
+          metaflag = 1;
+          break;
+        }
+      }
+    } else if (*p == '!' && p[1] == '!'  && (p == name || p[-1] == '/')) {
+      metaflag = 1;
+    } else if (*p == '\0')
+      break;
+    else if (*p == CTLESC)
+      p++;
+    if (*p == '/') {
+      if (metaflag)
+        break;
+      start = p + 1;
+    }
+  }
+  if (metaflag == 0) {  /* we've reached the end of the file name */
+    if (enddir != expdir)
+      metaflag++;
+    for (p = name ; ; p++) {
+      if (*p == CTLESC)
+        p++;
+      *enddir++ = *p;
+      if (*p == '\0')
+        break;
+    }
+    if (metaflag == 0 || stat(expdir, &statb) >= 0)
+      addfname(expdir);
+    return;
+  }
+  endname = p;
+  if (start != name) {
+    p = name;
+    while (p < start) {
+      if (*p == CTLESC)
+        p++;
+      *enddir++ = *p++;
+    }
+  }
+  if (enddir == expdir) {
+    p = ".";
+  } else if (enddir == expdir + 1 && *expdir == '/') {
+    p = "/";
+  } else {
+    p = expdir;
+    enddir[-1] = '\0';
+  }
+  if ((dirp = opendir(p)) == NULL)
+    return;
+  if (enddir != expdir)
+    enddir[-1] = '/';
+  if (*endname == 0) {
+    atend = 1;
+  } else {
+    atend = 0;
+    *endname++ = '\0';
+  }
+  matchdot = 0;
+  if( (start[0] == '.') || (start[0] == CTLESC && start[1] == '.'))
+    matchdot++;
+  while (! int_pending() && (dp = readdir(dirp)) != NULL) {
+    if (dp->d_name[0] == '.' && ! matchdot)
+      continue;
+    if (patmatch(start, dp->d_name)) {
+      if (atend) {
+        scopy(dp->d_name, enddir);
+        addfname(expdir);
+      } else {
+        char *q;
+        for (p = enddir, q = dp->d_name ; (*p++ = *q++) ;);
+        p[-1] = '/';
+        expmeta(p, endname);
+      }
+    }
+  }
+  closedir(dirp);
+  if (! atend)
+    endname[-1] = '/';
+}
+
+/*
+ * Add a file name to the list.
+ */
+STATIC void addfname(char *name)
+{
+  char *p;
+  struct strlist *sp;
+
+  p = stalloc(strlen(name) + 1);
+  scopy(name, p);
+  sp = (struct strlist *)stalloc(sizeof *sp);
+  sp->text = p;
+  *exparg.lastp = sp;
+  exparg.lastp = &sp->next;
+}
+
+/*
+ * Sort the results of file name expansion.  It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+STATIC struct strlist *expsort(struct strlist *str)
+{
+  int len;
+  struct strlist *sp;
+
+  len = 0;
+  for (sp = str ; sp ; sp = sp->next)
+    len++;
+  return msort(str, len);
+}
+
+STATIC struct strlist *msort(struct strlist *list, int len)
+{
+  struct strlist *p, *q = NULL;
+  struct strlist **lpp;
+  int half;
+  int n;
+
+  if (len <= 1)
+    return list;
+  half = len >> 1;
+  p = list;
+  for (n = half ; --n >= 0 ; ) {
+    q = p;
+    p = p->next;
+  }
+  q->next = NULL;      /* terminate first half of list */
+  q = msort(list, half);    /* sort first half of list */
+  p = msort(p, len - half);    /* sort second half */
+  lpp = &list;
+  for (;;) {
+    if (strcmp(p->text, q->text) < 0) {
+      *lpp = p;
+      lpp = &p->next;
+      if ((p = *lpp) == NULL) {
+        *lpp = q;
+        break;
+      }
+    } else {
+      *lpp = q;
+      lpp = &q->next;
+      if ((q = *lpp) == NULL) {
+        *lpp = p;
+        break;
+      }
+    }
+  }
+  return list;
+}
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+int patmatch(char *pattern, char *string)
+{
+  if (pattern[0] == '!' && pattern[1] == '!')
+    return 1 - pmatch(pattern + 2, string);
+  else
+    return pmatch(pattern, string);
+}
+
+STATIC int pmatch(char *pattern, char *string)
+{
+  register char *p, *q;
+  register char c;
+
+  p = pattern;
+  q = string;
+  for (;;) {
+    switch (c = *p++) {
+    case '\0':
+      goto breakloop;
+    case CTLESC:
+      if (*q++ != *p++)
+        return 0;
+      break;
+    case '?':
+      if (*q++ == '\0')
+        return 0;
+      break;
+    case '*':
+      c = *p;
+      if (c != CTLESC && c != '?' && c != '*' && c != '[') {
+        while (*q != c) {
+          if (*q == '\0')
+            return 0;
+          q++;
+        }
+      }
+      do {
+        if (pmatch(p, q))
+          return 1;
+      } while (*q++ != '\0');
+      return 0;
+    case '[': {
+      char *endp;
+      int invert, found;
+      char chr;
+
+      endp = p;
+      if (*endp == '!')
+        endp++;
+      for (;;) {
+        if (*endp == '\0')
+          goto dft;    /* no matching ] */
+        if (*endp == CTLESC)
+          endp++;
+        if (*++endp == ']')
+          break;
+      }
+      invert = 0;
+      if (*p == '!') {
+        invert++;
+        p++;
+      }
+      found = 0;
+      chr = *q++;
+      c = *p++;
+      do {
+        if (c == CTLESC)
+          c = *p++;
+        if (*p == '-' && p[1] != ']') {
+          p++;
+          if (*p == CTLESC)
+            p++;
+          if (chr >= c && chr <= *p)
+            found = 1;
+          p++;
+        } else {
+          if (chr == c)
+            found = 1;
+        }
+      } while ((c = *p++) != ']');
+      if (found == invert)
+        return 0;
+      break;
+    }
+dft:    default:
+      if (*q++ != c)
+        return 0;
+      break;
+    }
+  }
+breakloop:
+  if (*q != '\0')
+    return 0;
+  return 1;
+}
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+void rmescapes(char *str)
+{
+  register char *p, *q;
+
+  p = str;
+  while (*p != CTLESC) {
+    if (*p++ == '\0')
+      return;
+  }
+  q = p;
+  while (*p) {
+    if (*p == CTLESC)
+      p++;
+    *q++ = *p++;
+  }
+  *q = '\0';
+}
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+int casematch(union node *pattern, char *val)
+{
+  struct stackmark smark;
+  int result;
+  char *p;
+
+  setstackmark(&smark);
+  argbackq = pattern->narg.backquote;
+  STARTSTACKSTR(expdest);
+  ifslastp = NULL;
+  /* Preserve any CTLESC characters inserted previously, so that
+     we won't expand reg exps which are inside strings.  */
+  argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+  STPUTC('\0', expdest);
+  p = grabstackstr(expdest);
+  result = patmatch(p, val);
+  popstackmark(&smark);
+  return result;
+}
+
+//==================================================================================
+//exec - execute shell commands.
+#ifdef  BSD
+#undef BSD
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+
+/*
+ * Exec a program.  Never returns.  If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+void shellexec(char **argv, char **envp, char *path, int index)
+{
+  char *cmdname;
+  int e;
+
+  if (strchr(argv[0], '/') != NULL) {
+    tryexec(argv[0], argv, envp);
+    e = errno;
+  } else {
+    e = ENOENT;
+    while ((cmdname = padvance(&path, argv[0])) != NULL) {
+      if (--index < 0 && pathopt == NULL) {
+        tryexec(cmdname, argv, envp);
+        if (errno != ENOENT && errno != ENOTDIR)
+          e = errno;
+      }
+      stunalloc(cmdname);
+    }
+  }
+  error2(argv[0], errmsg(e, E_EXEC));
+}
+
+STATIC void tryexec(char *cmd, char **argv, char **envp)
+{
+  int e;
+  char *p;
+
+#ifdef SYSV
+  do {
+    execve(cmd, argv, envp);
+  } while (errno == EINTR);
+#else
+  execve(cmd, argv, envp);
+#endif
+  e = errno;
+  if (e == ENOEXEC) {
+    initshellproc();
+    setinputfile(cmd, 0);
+    commandname = arg0 = savestr(argv[0]);
+#ifndef BSD
+    pgetc(); pungetc();    /* fill up input buffer */
+    p = parsenextc;
+    if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
+      argv[0] = cmd;
+      execinterp(argv, envp);
+    }
+#endif
+    setparam(argv + 1);
+    exraise(EXSHELLPROC);
+    /*NOTREACHED*/
+  }
+  errno = e;
+}
+
+#ifndef BSD
+/*
+ * Execute an interpreter introduced by "#!", for systems where this
+ * feature has not been built into the kernel.  If the interpreter is
+ * the shell, return (effectively ignoring the "#!").  If the execution
+ * of the interpreter fails, exit.
+ *
+ * This code peeks inside the input buffer in order to avoid actually
+ * reading any input.  It would benefit from a rewrite.
+ */
+#define NEWARGS 5
+
+STATIC void execinterp(char **argv, char **envp)
+{
+  int n;
+  char *inp;
+  char *outp;
+  char c = '\0';
+  char *p;
+  char **ap;
+  char *newargs[NEWARGS];
+  int i;
+  char **ap2;
+  char **new;
+
+  n = parsenleft - 2;
+  inp = parsenextc + 2;
+  ap = newargs;
+  for (;;) {
+    while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
+      inp++;
+    if (n < 0)
+      goto bad;
+    if ((c = *inp++) == '\n')
+      break;
+    if (ap == &newargs[NEWARGS])
+bad:      sh_error("Bad #! line");
+    STARTSTACKSTR(outp);
+    do {
+      STPUTC(c, outp);
+    } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
+    STPUTC('\0', outp);
+    n++, inp--;
+    *ap++ = grabstackstr(outp);
+  }
+  if (ap == newargs + 1) {  /* if no args, maybe no exec is needed */
+    p = newargs[0];
+    for (;;) {
+      if (equal(p, "sh") || equal(p, "ash")) {
+        return;
+      }
+      while (*p != '/') {
+        if (*p == '\0')
+          goto break2;
+        p++;
+      }
+      p++;
+    }
+break2:;
+  }
+  i = (char *)ap - (char *)newargs;    /* size in bytes */
+  if (i == 0)
+    sh_error("Bad #! line");
+  for (ap2 = argv ; *ap2++ != NULL ; );
+  new = ckmalloc(i + ((char *)ap2 - (char *)argv));
+  ap = newargs, ap2 = new;
+  while ((i -= sizeof (char **)) >= 0)
+    *ap2++ = *ap++;
+  ap = argv;
+  while ((*ap2++ = *ap++));
+  shellexec(new, envp, pathval(), 0);
+}
+#endif
+
+/*
+ * Do a path search.  The variable path (passed by reference) should be
+ * set to the start of the path before the first call; padvance will update
+ * this value as it proceeds.  Successive calls to padvance will return
+ * the possible path expansions in sequence.  If an option (indicated by
+ * a percent sign) appears in the path entry then the global variable
+ * pathopt will be set to point to it; otherwise pathopt will be set to
+ * NULL.
+ */
+char *padvance(char **path, char *name)
+{
+  register char *p, *q;
+  char *start;
+  int len;
+
+  if (*path == NULL)
+    return NULL;
+  start = *path;
+  for (p = start ; *p && *p != ':' && *p != '%' ; p++);
+  len = p - start + strlen(name) + 2;  /* "2" is for '/' and '\0' */
+  while (stackblocksize() < len)
+    growstackblock();
+  q = stackblock();
+  if (p != start) {
+    bcopy(start, q, p - start);
+    q += p - start;
+    *q++ = '/';
+  }
+  strcpy(q, name);
+  pathopt = NULL;
+  if (*p == '%') {
+    pathopt = ++p;
+    while (*p && *p != ':')  p++;
+  }
+  if (*p == ':')
+    *path = p + 1;
+  else
+    *path = NULL;
+  return stalloc(len);
+}
+
+/*** Command hashing code ***/
+int hashcmd(int argc, char **argv)
+{
+  struct tblentry **pp;
+  struct tblentry *cmdp;
+  int c;
+  int verbose;
+  struct cmdentry entry;
+  char *name;
+
+  if (argc <= 1) {
+    for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+      for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+        if(cmdp->cmdtype != CMDBUILTIN) printentry(cmdp);
+      }
+    }
+    return 0;
+  }
+  verbose = 0;
+  while ((c = nextopt("r")) != '\0') {
+    if (c == 'r') {
+      clearcmdentry(0);
+      return 0;
+    }
+  }
+  while ((name = *argptr) != NULL) {
+    if ((cmdp = cmdlookup(name, 0)) != NULL
+     && (cmdp->cmdtype == CMDNORMAL
+      || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+      delete_cmd_entry();
+    find_command(name, &entry, 1);
+    if (verbose) {
+      if (entry.cmdtype != CMDUNKNOWN) {  /* if no error msg */
+        cmdp = cmdlookup(name, 0);
+        printentry(cmdp);
+      }
+      flushall();
+    }
+    argptr++;
+  }
+  return 0;
+}
+
+STATIC void printentry(struct tblentry *cmdp)
+{
+  int index;
+  char *path;
+  char *name;
+
+  if (cmdp->cmdtype == CMDNORMAL) {
+    index = cmdp->param.index;
+    path = pathval();
+    do {
+      name = padvance(&path, cmdp->cmdname);
+      stunalloc(name);
+    } while (--index >= 0);
+    out1str(name);
+  } else if (cmdp->cmdtype == CMDBUILTIN) {
+    out1fmt("builtin %s", cmdp->cmdname);
+  } else if (cmdp->cmdtype == CMDFUNCTION) {
+    out1fmt("function %s", cmdp->cmdname);
+#ifdef DEBUG
+  } else {
+    sh_error("internal error: cmdtype %d", cmdp->cmdtype);
+#endif
+  }
+  if (cmdp->rehash)
+    out1c('*');
+  out1c('\n');
+}
+
+/*
+ * Resolve a command name.  If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+void find_command(char *name, struct cmdentry *entry, int printerr)
+{
+  struct tblentry *cmdp;
+  int index;
+  int prev;
+  char *path;
+  char *fullname;
+  struct stat statb;
+  int e;
+  int i;
+
+  /* If name contains a slash, don't use the hash table */
+  if (strchr(name, '/') != NULL) {
+    entry->cmdtype = CMDNORMAL;
+    entry->u.index = 0;
+    return;
+  }
+
+  /* If name is in the table, and not invalidated by cd, we're done */
+  if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
+    goto success;
+
+  /* If %builtin not in path, check for builtin next */
+  if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
+    INTOFF;
+    cmdp = cmdlookup(name, 1);
+    cmdp->cmdtype = CMDBUILTIN;
+    cmdp->param.index = i;
+    INTON;
+    goto success;
+  }
+
+  /* We have to search path. */
+  prev = -1;    /* where to start */
+  if (cmdp) {    /* doing a rehash */
+    if (cmdp->cmdtype == CMDBUILTIN)
+      prev = builtinloc;
+    else
+      prev = cmdp->param.index;
+  }
+
+  path = pathval();
+  e = ENOENT;
+  index = -1;
+loop:
+  while ((fullname = padvance(&path, name)) != NULL) {
+    stunalloc(fullname);
+    index++;
+    if (pathopt) {
+      if (prefix("builtin", pathopt)) {
+        if ((i = find_builtin(name)) < 0)
+          goto loop;
+        INTOFF;
+        cmdp = cmdlookup(name, 1);
+        cmdp->cmdtype = CMDBUILTIN;
+        cmdp->param.index = i;
+        INTON;
+        goto success;
+      } else if (prefix("func", pathopt)) {
+        /* handled below */
+      } else {
+        goto loop;  /* ignore unimplemented options */
+      }
+    }
+    /* if rehash, don't redo absolute path names */
+    if (fullname[0] == '/' && index <= prev) {
+      if (index < prev)
+        goto loop;
+      TRACE(("searchexec \"%s\": no change\n", name));
+      goto success;
+    }
+    while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+      if (errno == EINTR)
+        continue;
+#endif
+      if (errno != ENOENT && errno != ENOTDIR)
+        e = errno;
+      goto loop;
+    }
+    e = EACCES;  /* if we fail, this will be the error */
+    if ((statb.st_mode & S_IFMT) != S_IFREG)
+      goto loop;
+    if (pathopt) {    /* this is a %func directory */
+      stalloc(strlen(fullname) + 1);
+      readcmdfile(fullname);
+      if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
+        sh_error("%s not defined in %s", name, fullname);
+      stunalloc(fullname);
+      goto success;
+    }
+    if (statb.st_uid == geteuid()) {
+      if ((statb.st_mode & 0100) == 0)
+        goto loop;
+    } else if (statb.st_gid == getegid()) {
+      if ((statb.st_mode & 010) == 0)
+        goto loop;
+    } else {
+      if ((statb.st_mode & 01) == 0) {
+#ifdef  BSD
+        if ((statb.st_mode & 010) == 0)
+          goto loop;
+        /* Are you in this group too? */
+        {
+          int group_list[NGROUPS];
+          int ngroups, i;
+
+          ngroups = getgroups(NGROUPS, group_list);
+          for (i = 0; i < ngroups; i++)
+            if (statb.st_gid == group_list[i])
+              goto Found;
+        }
+#endif
+        goto loop;
+      }
+    }
+#ifdef  BSD
+  Found:
+#endif
+    TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+    INTOFF;
+    cmdp = cmdlookup(name, 1);
+    cmdp->cmdtype = CMDNORMAL;
+    cmdp->param.index = index;
+    INTON;
+    goto success;
+  }
+
+  /* We failed.  If there was an entry for this command, delete it */
+  if (cmdp)
+    delete_cmd_entry();
+  if (printerr)
+    outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
+  entry->cmdtype = CMDUNKNOWN;
+  return;
+
+success:
+  cmdp->rehash = 0;
+  entry->cmdtype = cmdp->cmdtype;
+  entry->u = cmdp->param;
+}
+
+/*
+ * Search the table of builtin commands.
+ */
+int find_builtin(char *name)
+{
+  const register struct builtincmd *bp;
+
+  for (bp = builtincmd ; bp->name ; bp++) {
+    if (*bp->name == *name && equal(bp->name, name))
+      return bp->code;
+  }
+  return -1;
+}
+
+/*
+ * Called when a cd is done.  Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+void hashcd()
+{
+  struct tblentry **pp;
+  struct tblentry *cmdp;
+
+  for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+    for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+      if (cmdp->cmdtype == CMDNORMAL
+       || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+        cmdp->rehash = 1;
+    }
+  }
+}
+
+/*
+ * Called before PATH is changed.  The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.  Called with
+ * interrupts off.
+ */
+void changepath(char *newval)
+{
+  char *old, *new;
+  int index;
+  int firstchange;
+  int bltin;
+
+  old = pathval();
+  new = newval;
+  firstchange = 9999;  /* assume no change */
+  index = 0;
+  bltin = -1;
+  for (;;) {
+    if (*old != *new) {
+      firstchange = index;
+      if ((*old == '\0' && *new == ':')
+       || (*old == ':' && *new == '\0'))
+        firstchange++;
+      old = new;  /* ignore subsequent differences */
+    }
+    if (*new == '\0')
+      break;
+    if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
+      bltin = index;
+    if (*new == ':') {
+      index++;
+    }
+    new++, old++;
+  }
+  if (builtinloc < 0 && bltin >= 0)
+    builtinloc = bltin;    /* zap builtins */
+  if (builtinloc >= 0 && bltin < 0)
+    firstchange = 0;
+  clearcmdentry(firstchange);
+  builtinloc = bltin;
+}
+
+/*
+ * Clear out command entries.  The argument specifies the first entry in
+ * PATH which has changed.
+ */
+STATIC void clearcmdentry(int firstchange)
+{
+  struct tblentry **tblp;
+  struct tblentry **pp;
+  struct tblentry *cmdp;
+
+  INTOFF;
+  for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+    pp = tblp;
+    while ((cmdp = *pp) != NULL) {
+      if ((cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange)
+       || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange)) {
+        *pp = cmdp->next;
+        ckfree(cmdp);
+      } else {
+        pp = &cmdp->next;
+      }
+    }
+  }
+  INTON;
+}
+
+/*
+ * Delete all functions.
+ */
+void deletefuncs()
+{
+  struct tblentry **tblp;
+  struct tblentry **pp;
+  struct tblentry *cmdp;
+
+  INTOFF;
+  for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+    pp = tblp;
+    while ((cmdp = *pp) != NULL) {
+      if (cmdp->cmdtype == CMDFUNCTION) {
+        *pp = cmdp->next;
+        freefunc(cmdp->param.func);
+        ckfree(cmdp);
+      } else {
+        pp = &cmdp->next;
+      }
+    }
+  }
+  INTON;
+}
+
+/*
+ * Locate a command in the command hash table.  If "add" is nonzero,
+ * add the command to the table if it is not already present.  The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ */
+STATIC struct tblentry *cmdlookup(char *name, int add)
+{
+  int hashval;
+  register char *p;
+  struct tblentry *cmdp;
+  struct tblentry **pp;
+
+  p = name;
+  hashval = *p << 4;
+  while (*p)
+    hashval += *p++;
+  hashval &= 0x7FFF;
+  pp = &cmdtable[hashval % CMDTABLESIZE];
+  for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+    if (equal(cmdp->cmdname, name))
+      break;
+    pp = &cmdp->next;
+  }
+  if (add && cmdp == NULL) {
+    INTOFF;
+    cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
+          + strlen(name) + 1);
+    cmdp->next = NULL;
+    cmdp->cmdtype = CMDUNKNOWN;
+    cmdp->rehash = 0;
+    strcpy(cmdp->cmdname, name);
+    INTON;
+  }
+  lastcmdentry = pp;
+  return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+STATIC void delete_cmd_entry()
+{
+  struct tblentry *cmdp;
+
+  INTOFF;
+  cmdp = *lastcmdentry;
+  *lastcmdentry = cmdp->next;
+  ckfree(cmdp);
+  INTON;
+}
+
+#ifdef notdef
+void getcmdentry(char *name, struct cmdentry *entry)
+{
+  struct tblentry *cmdp = cmdlookup(name, 0);
+
+  if (cmdp) {
+    entry->u = cmdp->param;
+    entry->cmdtype = cmdp->cmdtype;
+  } else {
+    entry->cmdtype = CMDUNKNOWN;
+    entry->u.index = 0;
+  }
+}
+#endif
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name.
+ */
+void addcmdentry(char *name, struct cmdentry *entry)
+{
+  struct tblentry *cmdp;
+
+  INTOFF;
+  cmdp = cmdlookup(name, 1);
+  if (cmdp->cmdtype == CMDFUNCTION) {
+    freefunc(cmdp->param.func);
+  }
+  cmdp->cmdtype = entry->cmdtype;
+  cmdp->param = entry->u;
+  INTON;
+}
+
+/*
+ * Define a shell function.
+ */
+void defun(char *name, union node *func)
+{
+  struct cmdentry entry;
+
+  INTOFF;
+  entry.cmdtype = CMDFUNCTION;
+  entry.u.func = copyfunc(func);
+  addcmdentry(name, &entry);
+  INTON;
+}
+
+/*
+ * Delete a function if it exists.
+ */
+void unsetfunc(char *name)
+{
+  struct tblentry *cmdp;
+
+  if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
+    freefunc(cmdp->param.func);
+    delete_cmd_entry();
+  }
+}
+#endif
+
+#ifndef  BSD
+  #define BSD
+#endif
+
+//==================================================================================
+//eval - Evaluate commands.
+/*
+ * Called to reset things after an exception.
+ */
+
+/*
+ * The eval commmand.
+ */
+int evalcmd(int argc, char **argv)
+{
+  char *p;
+  char *concat;
+  char **ap;
+
+  if (argc > 1) {
+    p = argv[1];
+    if (argc > 2) {
+      STARTSTACKSTR(concat);
+      ap = argv + 2;
+      for (;;) {
+        while (*p)
+          STPUTC(*p++, concat);
+        if ((p = *ap++) == NULL)
+          break;
+        STPUTC(' ', concat);
+      }
+      STPUTC('\0', concat);
+      p = grabstackstr(concat);
+    }
+    evalstring(p);
+  }
+  return exitstatus;
+}
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+void evalstring(char *s)
+{
+  union node *n;
+  struct stackmark smark;
+
+  setstackmark(&smark);
+  setinputstring(s, 1);
+  while ((n = parsecmd(0)) != NEOF) {
+    evaltree(n, 0);
+    popstackmark(&smark);
+  }
+  popfile();
+  popstackmark(&smark);
+}
+
+/*
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
+ */
+void evaltree(union node *n, int flags)
+{
+  if (n == NULL) {
+    TRACE(("evaltree(NULL) called\n"));
+    return;
+  }
+  TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type));
+  switch (n->type) {
+  case NSEMI:
+    evaltree(n->nbinary.ch1, 0);
+    if (evalskip)
+      goto out;
+    evaltree(n->nbinary.ch2, flags);
+    break;
+  case NAND:
+    evaltree(n->nbinary.ch1, EV_TESTED);
+    if (evalskip || exitstatus != 0)
+      goto out;
+    evaltree(n->nbinary.ch2, flags);
+    break;
+  case NOR:
+    evaltree(n->nbinary.ch1, EV_TESTED);
+    if (evalskip || exitstatus == 0)
+      goto out;
+    evaltree(n->nbinary.ch2, flags);
+    break;
+  case NREDIR:
+    expredir(n->nredir.redirect);
+    redirect(n->nredir.redirect, REDIR_PUSH);
+    evaltree(n->nredir.n, flags);
+    popredir();
+    break;
+  case NSUBSHELL:
+    evalsubshell(n, flags);
+    break;
+  case NBACKGND:
+    evalsubshell(n, flags);
+    break;
+  case NIF: {
+    int status = 0;
+
+    evaltree(n->nif.test, EV_TESTED);
+    if (evalskip)
+      goto out;
+    if (exitstatus == 0) {
+      evaltree(n->nif.ifpart, flags);
+      status = exitstatus;
+    } else if (n->nif.elsepart) {
+      evaltree(n->nif.elsepart, flags);
+      status = exitstatus;
+    }
+    exitstatus = status;
+    break;
+  }
+  case NWHILE:
+  case NUNTIL:
+    evalloop(n);
+    break;
+  case NFOR:
+    evalfor(n);
+    break;
+  case NCASE:
+    evalcase(n, flags);
+    break;
+  case NDEFUN:
+    defun(n->narg.text, n->narg.next);
+    exitstatus = 0;
+    break;
+  case NNOT:
+    evaltree(n->nnot.com, EV_TESTED);
+    exitstatus = !exitstatus;
+    break;
+  case NPIPE:
+    evalpipe(n);
+    break;
+  case NCMD:
+    evalcommand(n, flags, (struct backcmd *)NULL);
+    break;
+  default:
+    out1fmt("Node type = %d\n", n->type);
+    flushout(&output);
+    break;
+  }
+out:
+  if (pendingsigs)
+    dotrap();
+  if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
+    exitshell(exitstatus);
+}
+
+STATIC void evalloop(union node *n)
+{
+  int status;
+
+  loopnest++;
+  status = 0;
+  for (;;) {
+    evaltree(n->nbinary.ch1, EV_TESTED);
+    if (evalskip) {
+skipping:    if (evalskip == SKIPCONT && --skipcount <= 0) {
+        evalskip = 0;
+        continue;
+      }
+      if (evalskip == SKIPBREAK && --skipcount <= 0)
+        evalskip = 0;
+      break;
+    }
+    if (n->type == NWHILE) {
+      if (exitstatus != 0)
+        break;
+    } else {
+      if (exitstatus == 0)
+        break;
+    }
+    evaltree(n->nbinary.ch2, 0);
+    status = exitstatus;
+    if (evalskip)
+      goto skipping;
+  }
+  loopnest--;
+  exitstatus = status;
+}
+
+STATIC void evalfor(union node *n)
+{
+  struct arglist arglist;
+  union node *argp;
+  struct strlist *sp;
+  struct stackmark smark;
+
+  setstackmark(&smark);
+  arglist.lastp = &arglist.list;
+  for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
+    expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+    if (evalskip)
+      goto out;
+  }
+  *arglist.lastp = NULL;
+
+  exitstatus = 0;
+  loopnest++;
+  for (sp = arglist.list ; sp ; sp = sp->next) {
+    setvar(n->nfor.var, sp->text, 0);
+    evaltree(n->nfor.body, 0);
+    if (evalskip) {
+      if (evalskip == SKIPCONT && --skipcount <= 0) {
+        evalskip = 0;
+        continue;
+      }
+      if (evalskip == SKIPBREAK && --skipcount <= 0)
+        evalskip = 0;
+      break;
+    }
+  }
+  loopnest--;
+out:
+  popstackmark(&smark);
+}
+
+STATIC void evalcase(union node *n, int flags)
+{
+  union node *cp;
+  union node *patp;
+  struct arglist arglist;
+  struct stackmark smark;
+
+  setstackmark(&smark);
+  arglist.lastp = &arglist.list;
+  expandarg(n->ncase.expr, &arglist, EXP_TILDE);
+  for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
+    for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
+      if (casematch(patp, arglist.list->text)) {
+        if (evalskip == 0) {
+          evaltree(cp->nclist.body, flags);
+        }
+        goto out;
+      }
+    }
+  }
+out:
+  popstackmark(&smark);
+}
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+STATIC void evalsubshell(union node *n, int flags)
+{
+  struct job *jp;
+  int backgnd = (n->type == NBACKGND);
+
+  expredir(n->nredir.redirect);
+  jp = makejob(n, 1);
+  if (forkshell(jp, n, backgnd) == 0) {
+    if (backgnd)
+      flags &=~ EV_TESTED;
+    redirect(n->nredir.redirect, 0);
+    evaltree(n->nredir.n, flags | EV_EXIT);  /* never returns */
+  }
+  if (! backgnd) {
+    INTOFF;
+    exitstatus = waitforjob(jp);
+    INTON;
+  }
+}
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+STATIC void expredir(union node *n)
+{
+  register union node *redir;
+
+  for (redir = n ; redir ; redir = redir->nfile.next) {
+    if (redir->type == NFROM
+     || redir->type == NTO
+     || redir->type == NAPPEND) {
+      struct arglist fn;
+      fn.lastp = &fn.list;
+      expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+      redir->nfile.expfname = fn.list->text;
+    }
+  }
+}
+
+/*
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+STATIC void evalpipe(union node *n)
+{
+  struct job *jp;
+  struct nodelist *lp;
+  int pipelen;
+  int prevfd;
+  int pip[2];
+
+  TRACE(("evalpipe(0x%x) called\n", (int)n));
+  pipelen = 0;
+  for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
+    pipelen++;
+  INTOFF;
+  jp = makejob(n, pipelen);
+  prevfd = -1;
+  for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+    prehash(lp->n);
+    pip[1] = -1;
+    if (lp->next) {
+      if (pipe(pip) < 0) {
+        close(prevfd);
+        sh_error("Pipe call failed");
+      }
+    }
+    if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+      INTON;
+      if (prevfd > 0) {
+        close(0);
+        copyfd(prevfd, 0);
+        close(prevfd);
+      }
+      if (pip[1] >= 0) {
+        close(pip[0]);
+        if (pip[1] != 1) {
+          close(1);
+          copyfd(pip[1], 1);
+          close(pip[1]);
+        }
+      }
+      evaltree(lp->n, EV_EXIT);
+    }
+    if (prevfd >= 0)
+      close(prevfd);
+    prevfd = pip[0];
+    close(pip[1]);
+  }
+  INTON;
+  if (n->npipe.backgnd == 0) {
+    INTOFF;
+    exitstatus = waitforjob(jp);
+    TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
+    INTON;
+  }
+}
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+void evalbackcmd(union node *n, struct backcmd *result)
+{
+  int pip[2];
+  struct job *jp;
+  struct stackmark smark;    /* unnecessary */
+
+  setstackmark(&smark);
+  result->fd = -1;
+  result->buf = NULL;
+  result->nleft = 0;
+  result->jp = NULL;
+  if(n == NULL)
+    return;
+  if (n->type == NCMD) {
+    evalcommand(n, EV_BACKCMD, result);
+  } else {
+    if (pipe(pip) < 0)
+      sh_error("Pipe call failed");
+    jp = makejob(n, 1);
+    if (forkshell(jp, n, FORK_NOJOB) == 0) {
+      FORCEINTON;
+      close(pip[0]);
+      if (pip[1] != 1) {
+        close(1);
+        copyfd(pip[1], 1);
+        close(pip[1]);
+      }
+      evaltree(n, EV_EXIT);
+    }
+    close(pip[1]);
+    result->fd = pip[0];
+    result->jp = jp;
+  }
+  popstackmark(&smark);
+  TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+    result->fd, result->buf, result->nleft, result->jp));
+}
+
+/*
+ * Execute a simple command.
+ */
+STATIC void evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
+{
+  struct stackmark smark;
+  union node *argp;
+  struct arglist arglist;
+  struct arglist varlist;
+  char **argv;
+  int argc;
+  char **envp;
+  int varflag;
+  struct strlist *sp;
+  register char *p;
+  int mode;
+  int pip[2];
+  struct cmdentry cmdentry;
+  struct job *jp;
+  struct jmploc jmploc;
+  struct jmploc *volatile savehandler = NULL;
+  char *volatile savecmdname;
+  volatile struct shparam saveparam;
+  struct localvar *volatile savelocalvars;
+  volatile int e;
+  char *lastarg;
+
+  /* First expand the arguments. */
+  TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
+  setstackmark(&smark);
+  arglist.lastp = &arglist.list;
+  varlist.lastp = &varlist.list;
+  varflag = 1;
+  for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
+    p = argp->narg.text;
+    if (varflag && is_name(*p)) {
+      do {
+        p++;
+      } while (is_in_name(*p));
+      if (*p == '=') {
+        expandarg(argp, &varlist, 0);
+        continue;
+      }
+    }
+    expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_VARTILDE);
+    varflag = 0;
+  }
+  *arglist.lastp = NULL;
+  *varlist.lastp = NULL;
+  expredir(cmd->ncmd.redirect);
+  argc = 0;
+  for (sp = arglist.list ; sp ; sp = sp->next)
+    argc++;
+  argv = stalloc(sizeof (char *) * (argc + 1));
+  for (sp = arglist.list ; sp ; sp = sp->next)
+    *argv++ = sp->text;
+
+  *argv = NULL;
+  lastarg = NULL;
+  if (iflag && funcnest == 0 && argc > 0)
+    lastarg = argv[-1];
+  argv -= argc;
+
+  /* Print the command if xflag is set. */
+  if (xflag) {
+    outc('+', &errout);
+    for (sp = varlist.list ; sp ; sp = sp->next) {
+      outc(' ', &errout);
+      out2str(sp->text);
+    }
+    for (sp = arglist.list ; sp ; sp = sp->next) {
+      outc(' ', &errout);
+      out2str(sp->text);
+    }
+    outc('\n', &errout);
+    flushout(&errout);
+  }
+
+  /* Now locate the command. */
+  if (argc == 0) {
+    cmdentry.cmdtype = CMDBUILTIN;
+    cmdentry.u.index = BLTINCMD;
+  } else {
+    find_command(argv[0], &cmdentry, 1);
+    if (cmdentry.cmdtype == CMDUNKNOWN) {  /* command not found */
+      exitstatus = 2;
+      redirect(cmd->ncmd.redirect, REDIR_PUSH);
+      flushout(&errout);
+      popredir();
+      return;
+    }
+    /* implement the bltin builtin here */
+    if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
+      for (;;) {
+        argv++;
+        if (--argc == 0)
+          break;
+        if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
+          outfmt(&errout, "%s: not found\n", *argv);
+          exitstatus = 2;
+          flushout(&errout);
+          return;
+        }
+        if (cmdentry.u.index != BLTINCMD)
+          break;
+      }
+    }
+  }
+
+  /* Fork off a child process if necessary. */
+  if (cmd->ncmd.backgnd
+   || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
+   || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN
+   || cmdentry.u.index == DOTCMD || cmdentry.u.index == EVALCMD)))
+
+  {
+    jp = makejob(cmd, 1);
+    mode = cmd->ncmd.backgnd;
+    if (flags & EV_BACKCMD) {
+      mode = FORK_NOJOB;
+      if (pipe(pip) < 0)
+        sh_error("Pipe call failed");
+    }
+    if (forkshell(jp, cmd, mode) != 0)
+      goto parent;  /* at end of routine */
+    if (flags & EV_BACKCMD) {
+      FORCEINTON;
+      close(pip[0]);
+      if (pip[1] != 1) {
+        close(1);
+        copyfd(pip[1], 1);
+        close(pip[1]);
+      }
+    }
+    flags |= EV_EXIT;
+  }
+
+  /* This is the child process if a fork occurred. */
+  /* Execute the command. */
+  if (cmdentry.cmdtype == CMDFUNCTION) {
+    trputs("Shell function:  ");  trargs(argv);
+    redirect(cmd->ncmd.redirect, REDIR_PUSH);
+    saveparam = shellparam;
+    shellparam.malloc = 0;
+    shellparam.nparam = argc - 1;
+    shellparam.p = argv + 1;
+    shellparam.optnext = NULL;
+    INTOFF;
+    savelocalvars = localvars;
+    localvars = NULL;
+    INTON;
+    if (setjmp(jmploc.loc)) {
+      if (exception == EXSHELLPROC)
+        freeparam((struct shparam *)&saveparam);
+      else {
+        freeparam(&shellparam);
+        shellparam = saveparam;
+      }
+      poplocalvars();
+      localvars = savelocalvars;
+      handler = savehandler;
+      longjmp(handler->loc, 1);
+    }
+    savehandler = handler;
+    handler = &jmploc;
+    for (sp = varlist.list ; sp ; sp = sp->next)
+      mklocal(sp->text);
+    funcnest++;
+    evaltree(cmdentry.u.func, 0);
+    funcnest--;
+    INTOFF;
+    poplocalvars();
+    localvars = savelocalvars;
+    freeparam(&shellparam);
+    shellparam = saveparam;
+    handler = savehandler;
+    popredir();
+    INTON;
+    if (evalskip == SKIPFUNC) {
+      evalskip = 0;
+      skipcount = 0;
+    }
+    if (flags & EV_EXIT)
+      exitshell(exitstatus);
+  } else if (cmdentry.cmdtype == CMDBUILTIN) {
+    trputs("builtin command:  ");  trargs(argv);
+    mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
+    if (flags == EV_BACKCMD) {
+      memout.nleft = 0;
+      memout.nextc = memout.buf;
+      memout.bufsize = 64;
+      mode |= REDIR_BACKQ;
+    }
+    redirect(cmd->ncmd.redirect, mode);
+    savecmdname = commandname;
+    cmdenviron = varlist.list;
+    e = -1;
+    if (setjmp(jmploc.loc)) {
+      e = exception;
+      exitstatus = (e == EXINT)? SIGINT+128 : 2;
+      goto cmddone;
+    }
+    savehandler = handler;
+    handler = &jmploc;
+    commandname = argv[0];
+    argptr = argv + 1;
+    optptr = NULL;      /* initialize nextopt */
+    exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
+    flushall();
+cmddone:
+    out1 = &output;
+    out2 = &errout;
+    freestdout();
+    if (e != EXSHELLPROC) {
+      commandname = savecmdname;
+      if (flags & EV_EXIT) {
+        exitshell(exitstatus);
+      }
+    }
+    handler = savehandler;
+    if (e != -1) {
+      if (e != EXERROR || cmdentry.u.index == BLTINCMD
+               || cmdentry.u.index == DOTCMD
+               || cmdentry.u.index == EVALCMD
+               || cmdentry.u.index == EXECCMD)
+        exraise(e);
+      FORCEINTON;
+    }
+    if (cmdentry.u.index != EXECCMD)
+      popredir();
+    if (flags == EV_BACKCMD) {
+      backcmd->buf = memout.buf;
+      backcmd->nleft = memout.nextc - memout.buf;
+      memout.buf = NULL;
+    }
+  } else {
+    trputs("normal command:  ");  trargs(argv);
+    clearredir();
+    redirect(cmd->ncmd.redirect, 0);
+    if (varlist.list) {
+      p = stalloc(strlen(pathval()) + 1);
+      scopy(pathval(), p);
+    } else {
+      p = pathval();
+    }
+    for (sp = varlist.list ; sp ; sp = sp->next)
+      setvareq(sp->text, VEXPORT|VSTACK);
+    envp = environment();
+    shellexec(argv, envp, p, cmdentry.u.index);
+    /*NOTREACHED*/
+  }
+  goto out;
+
+parent:  /* parent process gets here (if we forked) */
+  if (mode == 0) {  /* argument to fork */
+    INTOFF;
+    exitstatus = waitforjob(jp);
+    INTON;
+  } else if (mode == 2) {
+    backcmd->fd = pip[0];
+    close(pip[1]);
+    backcmd->jp = jp;
+  }
+
+out:
+  if (lastarg)
+    setvar("_", lastarg, 0);
+  popstackmark(&smark);
+}
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+STATIC void prehash(union node *n)
+{
+  struct cmdentry entry;
+
+  if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
+    find_command(n->ncmd.args->narg.text, &entry, 0);
+}
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+/*
+ * No command given, or a bltin command with no arguments.  Set the
+ * specified variables.
+ */
+int bltincmd(int argc, char **argv)
+{
+  listsetvar(cmdenviron);
+  return exitstatus;
+}
+
+/*
+ * Handle break and continue commands.  Break, continue, and return are
+ * all handled by setting the evalskip flag.  The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them.  The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return.  (The latter is always 1.)  It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+int breakcmd(int argc, char **argv)
+{
+  int n;
+
+  n = 1;
+  if (argc > 1)
+    n = number(argv[1]);
+  if (n > loopnest)
+    n = loopnest;
+  if (n > 0) {
+    evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+    skipcount = n;
+  }
+  return 0;
+}
+
+/*
+ * The return command.
+ */
+int returncmd(int argc, char **argv)
+{
+  int ret;
+  ret = exitstatus;
+
+  if (argc > 1)
+    ret = number(argv[1]);
+  if (funcnest) {
+    evalskip = SKIPFUNC;
+    skipcount = 1;
+  }
+  return ret;
+}
+
+int falsecmd(int argc, char **argv)
+{
+  return 1;
+}
+
+int truecmd(int argc, char **argv)
+{
+  return 0;
+}
+
+int execcmd(int argc, char **argv)
+{
+  if (argc > 1) {
+    iflag = 0;    /* exit on error */
+    setinteractive(0);
+#if JOBS
+    jflag = 0;
+    setjobctl(0);
+#endif
+    shellexec(argv + 1, environment(), pathval(), 0);
+
+  }
+  return 0;
+}
+
+//==================================================================================
+//error - Handle Errors and exceptions.
+/*
+ * Called to raise an exception.  Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler.  The type of exception is
+ * stored in the global variable "exception".
+ */
+void exraise(int e)
+{
+  if (handler == NULL)
+    abort();
+  exception = e;
+  longjmp(handler->loc, 1);
+}
+
+/*
+ * Called from trap.c when a SIGINT is received.  (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.)  Suppressint is nonzero when interrupts
+ * are held using the INTOFF macro.  The call to _exit is necessary because
+ * there is a short period after a fork before the signal handlers are
+ * set to the appropriate value for the child.  (The test for iflag is
+ * just defensive programming.)
+ */
+static void onint()
+{
+  if (suppressint) {
+    intpending++;
+    return;
+  }
+  intpending = 0;
+#ifdef BSD
+  {
+    sigset_t sigmask;
+    sigemptyset(&sigmask);
+    sigprocmask (SIG_SETMASK, &sigmask, NULL);
+
+  }
+#endif
+  if (rootshell && iflag)
+    exraise(EXINT);
+  else
+    exit(128 + SIGINT);
+}
+
+void error2(char *a, char *b)
+{
+  sh_error("%s: %s", a, b);
+}
+
+/*
+ * Error is called to raise the error exception.  If the first argument
+ * is not NULL then error prints an error message using printf style
+ * formatting.  It then raises the error exception.
+ */
+#ifdef __STDC__
+
+void sh_error(char *msg, ...)
+{
+#else
+void sh_error(va_alist)
+  va_dcl
+{
+  char *msg;
+#endif
+  va_list ap;
+
+  CLEAR_PENDING_INT;
+  INTOFF;
+#ifdef __STDC__
+  va_start(ap, msg);
+#else
+  va_start(ap);
+  msg = va_arg(ap, char *);
+#endif
+#ifdef DEBUG
+  if (msg)
+    TRACE(("error(\"%s\") pid=%d\n", msg, getpid()));
+  else
+    TRACE(("error(NULL) pid=%d\n", getpid()));
+#endif
+  if (msg) {
+    if (commandname)
+      outfmt(&errout, "%s: ", commandname);
+    doformat(&errout, msg, ap);
+    out2c('\n');
+  }
+  va_end(ap);
+  flushall();
+  exraise(EXERROR);
+}
+
+/*
+ * Return a string describing an error.  The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+char *errmsg(int e, int action)
+{
+  struct errname const *ep;
+  static char buf[12];
+
+  for (ep = errormsg ; ep->errcode ; ep++) {
+    if (ep->errcode == e && (ep->action & action) != 0)
+      return ep->msg;
+  }
+  fmtstr(buf, sizeof buf, "error %d", e);
+  return buf;
+}
+
+//==================================================================================
+//dirent - operate on directories.
+#if ! DIRENT
+#ifdef BSD
+
+/*
+ * The BSD opendir routine doesn't check that what is being opened is a
+ * directory, so we have to include the check in a wrapper routine.
+ */
+#undef opendir
+DIR *myopendir(char *dirname)      /* name of directory */
+{
+  struct stat statb;
+
+  if (stat(dirname, &statb) != 0 || ! S_ISDIR(statb.st_mode)) {
+    errno = ENOTDIR;
+    return NULL;    /* not a directory */
+  }
+  return opendir(dirname);
+}
+
+#else /* not BSD */
+
+/*
+ * Dirent routines for old style file systems.
+ */
+#ifdef __STDC__
+pointer malloc(unsigned);
+void free(pointer);
+int open(char *, int, ...);
+int close(int);
+int fstat(int, struct stat *);
+#else
+pointer malloc();
+void free();
+int open();
+int close();
+int fstat();
+#endif
+
+DIR *opendir(char *dirname)  /* name of directory */
+{
+  register DIR  *dirp;    /* -> malloc'ed storage */
+  register int  fd;    /* file descriptor for read */
+  struct stat  statb;    /* result of fstat() */
+
+#ifdef O_NDELAY
+  fd = open(dirname, O_RDONLY|O_NDELAY);
+#else
+  fd = open(dirname, O_RDONLY);
+#endif
+  if (fd < 0)
+    return NULL;    /* errno set by open() */
+
+  if (fstat(fd, &statb) != 0 || !S_ISDIR(statb.st_mode)) {
+    (void)close(fd);
+    errno = ENOTDIR;
+    return NULL;    /* not a directory */
+  }
+
+  if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
+    (void)close(fd);
+    errno = ENOMEM;
+    return NULL;    /* not enough memory */
+  }
+
+  dirp->dd_fd = fd;
+  dirp->dd_nleft = 0;    /* refill needed */
+
+  return dirp;
+}
+
+int closedir(register DIR *dirp)    /* stream from opendir() */
+{
+  register int fd;
+
+  if (dirp == NULL) {
+    errno = EFAULT;
+    return -1;      /* invalid pointer */
+  }
+
+  fd = dirp->dd_fd;
+  free((pointer)dirp);
+  return close(fd);
+}
+
+struct dirent *readdir(register DIR *dirp)    /* stream from opendir() */
+{
+  register struct direct *dp;
+  register char *p, *q;
+  register int i;
+
+  do {
+    if ((dirp->dd_nleft -= sizeof (struct direct)) < 0) {
+      if ((i = read(dirp->dd_fd,
+             (char *)dirp->dd_buf,
+             DIRBUFENT*sizeof(struct direct))) <= 0) {
+        if (i == 0)
+          errno = 0;  /* unnecessary */
+        return NULL;    /* EOF or error */
+      }
+      dirp->dd_loc = dirp->dd_buf;
+      dirp->dd_nleft = i - sizeof (struct direct);
+    }
+    dp = dirp->dd_loc++;
+  } while (dp->d_ino == 0);
+  dirp->dd_entry.d_ino = dp->d_ino;
+
+  /* now copy the name, nul terminating it */
+  p = dp->d_name;
+  q = dirp->dd_entry.d_name;
+  i = DIRSIZ;
+  while (--i >= 0 && *p != '\0')
+    *q++ = *p++;
+  *q = '\0';
+  return &dirp->dd_entry;
+}
+
+#endif /* BSD */
+#endif /* DIRENT */
+
+//==================================================================================
+//cd - cd command.
+/*
+ * The cd and pwd commands.
+ */
+int cdcmd(int argc, char **argv)
+{
+#define IS_ALONE_DASH(s) ((s)[0] == '-' && !(s)[1])
+#define IS_ALONE_TILDE(s) ((s)[0] == '~' && !(s)[1])
+  char *dest;
+  char *path;
+  char *p;
+  struct stat statb;
+  char *padvance();
+
+  nextopt(nullstr);
+
+  dest = *argptr;
+  if(!dest || IS_ALONE_TILDE(dest)) {
+    dest = bltinlookup("HOME", 1);
+  }
+  else if(IS_ALONE_DASH(dest)) {
+    dest = bltinlookup("OLDPWD", 1);
+  }
+  if(!dest)
+    dest = nullstr;
+  if (((*dest == '.') && (dest[1] == '\0')) ||
+      ((*dest == '.') && (dest[1] == '/') && (dest[2] == '\0')) ) {
+    path = nullstr;
+    if((p = padvance(&path, dest)) != NULL) {
+      getpwd();
+      if(docd(curdir, strcmp(curdir, dest)) < 0)
+        sh_error("can't cd to %s", dest);
+    }
+    return 0;
+  }
+  if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
+    path = nullstr;
+  while ((p = padvance(&path, dest)) != NULL) {
+    if (stat(p, &statb) >= 0
+     && (statb.st_mode & S_IFMT) == S_IFDIR
+     && docd(p, strcmp(p, dest)) >= 0)
+      return 0;
+  }
+  sh_error("can't cd to %s", dest);
+#undef IS_ALONE_DASH
+#undef IS_ALONE_TILDE
+  return 0;
+}
+
+static void setpwd(char *new_val)
+{
+  char *oldcur = bltinlookup("PWD", 1);
+  setvar("OLDPWD", oldcur, VEXPORT);
+  setvar("PWD", new_val, VEXPORT);
+  return;
+}
+
+/*
+ * Actually do the chdir.  If the name refers to symbolic links, we
+ * compute the actual directory name before doing the cd.  In an
+ * interactive shell, print the directory name if "print" is nonzero
+ * or if the name refers to a symbolic link.  We also print the name
+ * if "/u/logname" was expanded in it, since this is similar to a
+ * symbolic link.  (The check for this breaks if the user gives the
+ * cd command some additional, unused arguments.)
+ */
+#if SYMLINKS == 0
+
+STATIC int docd(char *dest, int print)
+{
+#if UDIR
+  if (didudir)
+    print = 1;
+#endif
+  INTOFF;
+  if (chdir(dest) < 0) {
+    INTON;
+    return -1;
+  }
+  updatepwd(dest);
+  INTON;
+#ifdef not
+  if (print && iflag)
+    out1fmt("%s\n", stackblock());
+#endif
+  return 0;
+}
+
+#else
+
+STATIC int docd(char *dest, int print)
+{
+  register char *p;
+  register char *q;
+  char *symlink;
+  char *component;
+  struct stat statb;
+  int first;
+  int i;
+
+  TRACE(("docd(\"%s\", %d) called\n", dest, print));
+#if UDIR
+  if (didudir)
+    print = 1;
+#endif
+
+top:
+  cdcomppath = dest;
+  STARTSTACKSTR(p);
+  if (*dest == '/') {
+    STPUTC('/', p);
+    cdcomppath++;
+  }
+  first = 1;
+  while ((q = getcomponent()) != NULL) {
+    if ((q[0] == '\0') || (q[0] == '.' && q[1] == '\0'))
+      continue;
+    if (! first)
+      STPUTC('/', p);
+    first = 0;
+    component = q;
+    while (*q)
+      STPUTC(*q++, p);
+    if (equal(component, ".."))
+      continue;
+    STACKSTRNUL(p);
+    if (lstat(stackblock(), &statb) < 0)
+      sh_error("lstat %s failed", stackblock());
+    if ((statb.st_mode & S_IFMT) != S_IFLNK)
+      continue;
+
+    /* Hit a symbolic link.  We have to start all over again. */
+    print = 1;
+    STPUTC('\0', p);
+    symlink = grabstackstr(p);
+    i = (int)statb.st_size + 2;    /* 2 for '/' and '\0' */
+    if (cdcomppath != NULL)
+      i += strlen(cdcomppath);
+    p = stalloc(i);
+    if (readlink(symlink, p, (int)statb.st_size) < 0) {
+      sh_error("readlink %s failed", stackblock());
+    }
+    if (cdcomppath != NULL) {
+      p[(int)statb.st_size] = '/';
+      scopy(cdcomppath, p + (int)statb.st_size + 1);
+    } else {
+      p[(int)statb.st_size] = '\0';
+    }
+    if (p[0] != '/') {  /* relative path name */
+      char *r;
+      q = r = symlink;
+      while (*q) {
+        if (*q++ == '/')
+          r = q;
+      }
+      *r = '\0';
+      dest = stalloc(strlen(symlink) + strlen(p) + 1);
+      scopy(symlink, dest);
+      strcat(dest, p);
+    } else {
+      dest = p;
+    }
+    goto top;
+  }
+  STPUTC('\0', p);
+  p = grabstackstr(p);
+  INTOFF;
+  if (chdir(p) < 0) {
+    INTON;
+    return -1;
+  }
+  updatepwd(p);
+  setpwd(curdir);
+  INTON;
+#ifdef not
+  if (print && iflag)
+    out1fmt("%s\n", p);
+#endif
+  return 0;
+}
+#endif /* SYMLINKS */
+
+/*
+ * Get the next component of the path name pointed to by cdcomppath.
+ * This routine overwrites the string pointed to by cdcomppath.
+ */
+STATIC char *getcomponent()
+{
+  register char *p;
+  char *start;
+
+  if ((p = cdcomppath) == NULL)
+    return NULL;
+  start = cdcomppath;
+  while (*p != '/' && *p != '\0')
+    p++;
+  if (*p == '\0') {
+    cdcomppath = NULL;
+  } else {
+    *p++ = '\0';
+    cdcomppath = p;
+  }
+  return start;
+}
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.  We also call hashcd to let the routines in exec.c know
+ * that the current directory has changed.
+ */
+void hashcd();
+
+STATIC void updatepwd(char *dir)
+{
+  char *new;
+  char *p;
+
+  hashcd();        /* update command hash table */
+  cdcomppath = stalloc(strlen(dir) + 1);
+  scopy(dir, cdcomppath);
+  STARTSTACKSTR(new);
+  if (*dir != '/') {
+    if (curdir == NULL) {
+      getpwd();
+      return;
+    }
+    p = curdir;
+    while (*p)
+      STPUTC(*p++, new);
+    if (p[-1] == '/')
+      STUNPUTC(new);
+  }
+  while ((p = getcomponent()) != NULL) {
+    if (equal(p, "..")) {
+      while (new > stackblock() && (STUNPUTC(new), *new) != '/');
+    } else if (*p != '\0' && ! equal(p, ".")) {
+      STPUTC('/', new);
+      while (*p)
+        STPUTC(*p++, new);
+    }
+  }
+  if (new == stackblock())
+    STPUTC('/', new);
+  STACKSTRNUL(new);
+  if (curdir)
+    ckfree(curdir);
+  curdir = savestr(stackblock());
+}
+
+int pwdcmd(int argc, char **argv)
+{
+  getpwd();
+  out1str(curdir);
+  out1c('\n');
+  return 0;
+}
+
+/*
+ * Run /bin/pwd to find out what the current directory is.  We suppress
+ * interrupts throughout most of this, but the user can still break out
+ * of it by killing the pwd program.  If we already know the current
+ * directory, this routine returns immediately.
+ */
+STATIC void getpwd()
+{
+  char buf[MAXPWD];
+  char *p;
+  int i;
+  int status;
+  struct job *jp;
+  int pip[2];
+
+  if (curdir)
+    return;
+  INTOFF;
+  if (pipe(pip) < 0)
+    sh_error("Pipe call failed");
+  jp = makejob((union node *)NULL, 1);
+  if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
+    close(pip[0]);
+    if (pip[1] != 1) {
+      close(1);
+      copyfd(pip[1], 1);
+      close(pip[1]);
+    }
+    execl("/bin/pwd", "pwd", (char *)0);
+    /* sh_error("Cannot exec /bin/pwd");*/
+    out2str("Cannot exec /bin/pwd\n");
+    flushall();
+    _exit(1);
+  }
+  close(pip[1]);
+  pip[1] = -1;
+  p = buf;
+  while( ((i = read(pip[0], p, buf + MAXPWD - p)) > 0) || (i == -1 && errno == EINTR)) {
+    if (i > 0)
+      p += i;
+  }
+  close(pip[0]);
+  pip[0] = -1;
+  status = waitforjob(jp);
+  if (status != 0)
+    sh_error((char *)0);
+  if (i < 0 || p == buf || p[-1] != '\n')
+    sh_error("pwd command failed");
+  p[-1] = '\0';
+  curdir = savestr(buf);
+  INTON;
+}
+
+//==================================================================================
+/*
+ * Main routine.  We initialize things, parse the arguments, execute
+ * profiles if we're a login shell, and then call cmdloop to execute
+ * commands.  The setjmp call sets up the location to jump to when an
+ * exception occurs.  When an exception occurs the variable "state"
+ * is used to figure out how far we had gotten.
+ */
+
+void ash_main(void)
+{
+  int argc = 0;
+  char **argv = toys.argv;
+  struct jmploc jmploc;
+  struct stackmark smark;
+  volatile int state;
+  char *shinit;
+
+  while(*argv++)
+    argc++;
+   argv = toys.argv;
+
+   if(argv[1] && (strcmp(argv[1], "--help") == 0)) {
+     show_help();
+     exit(1);
+   }
+
+#if PROFILE
+  monitor(4, etext, profile_buf, sizeof profile_buf, 50);
+#endif
+  state = 0;
+  if (setjmp(jmploc.loc)) {
+    /*
+     * When a shell procedure is executed, we raise the
+     * exception EXSHELLPROC to clean up before executing
+     * the shell procedure.
+     */
+    if (exception == EXSHELLPROC) {
+      rootpid = getpid();
+      rootshell = 1;
+      minusc = NULL;
+      state = 3;
+    } else if (state == 0 || iflag == 0 || ! rootshell)
+      exitshell(2);
+    reset();
+#if ATTY
+    if (exception == EXINT
+     && (! attyset() || equal(termval(), "emacs"))) {
+#else
+    if (exception == EXINT) {
+#endif
+      out2c('\n');
+      flushout(&errout);
+    }
+    popstackmark(&smark);
+    FORCEINTON;        /* enable interrupts */
+    if (state == 1)
+      goto state1;
+    else if (state == 2)
+      goto state2;
+    else
+      goto state3;
+  }
+  handler = &jmploc;
+#ifdef DEBUG
+  opentrace();
+  trputs("Shell args:  ");  trargs(argv);
+#endif
+  rootpid = getpid();
+  rootshell = 1;
+  init();
+  setstackmark(&smark);
+  procargs(argc, argv);
+
+  get_history();
+  if(hist_list)
+    key_list_ptr = hist_list->prev;
+  //if (argv[0] && argv[0][0] == '-') { // toybox dependency to pass the complete arg name
+  if (toys.optflags & FLAG_l) {
+    state = 1;
+    read_profile("/etc/profile");
+state1:
+    state = 2;
+    read_profile(".profile");
+  } else if ((sflag || minusc) && (shinit = getenv("SHINIT")) != NULL) {
+    state = 2;
+    evalstring(shinit);
+  }
+state2:
+  state = 3;
+  if (minusc) {
+    evalstring(minusc);
+  }
+  if (sflag || minusc == NULL) {
+state3:
+    cmdloop(1);
+  }
+#if PROFILE
+  monitor(0);
+#endif
+  exitshell(exitstatus);
+}
+
+/*
+ * Read and execute commands.  "Top" is nonzero for the top level command
+ * loop; it turns on prompting if the shell is interactive.
+ */
+void cmdloop(int top)
+{
+  union node *n;
+  struct stackmark smark;
+  int inter;
+  int numeof;
+
+  TRACE(("cmdloop(%d) called\n", top));
+  setstackmark(&smark);
+  numeof = 0;
+  for (;;) {
+    if (pendingsigs)
+      dotrap();
+    inter = 0;
+    if (iflag && top) {
+      inter++;
+      showjobs(1);
+      chkmail(0);
+      flushout(&output);
+    }
+    n = parsecmd(inter);
+#ifdef DEBUG
+    /* showtree(n); */
+#endif
+    if (n == NEOF) {
+      if (Iflag == 0 || numeof >= 50)
+        break;
+      out2str("\nUse \"exit\" to leave shell.\n");
+      numeof++;
+    } else if (n != NULL && nflag == 0) {
+      if (inter) {
+        INTOFF;
+        if (prevcmd)
+          freefunc(prevcmd);
+        prevcmd = curcmd;
+        curcmd = copyfunc(n);
+        INTON;
+      }
+      evaltree(n, 0);
+#ifdef notdef
+      if (exitstatus)            /*DEBUG*/
+        outfmt(&errout, "Exit status 0x%X\n", exitstatus);
+#endif
+    }
+    popstackmark(&smark);
+  }
+  popstackmark(&smark);    /* unnecessary */
+}
+
+/*
+ * Read /etc/profile or .profile.  Return on error.
+ */
+STATIC void read_profile(char *name)
+{
+  int fd;
+
+  INTOFF;
+  if ((fd = open(name, O_RDONLY)) >= 0)
+    setinputfd(fd, 1);
+  INTON;
+  if (fd < 0)
+    return;
+  cmdloop(0);
+  popfile();
+}
+
+/*
+ * Read a file containing shell functions.
+ */
+void readcmdfile(char *name)
+{
+  int fd;
+
+  INTOFF;
+  if ((fd = open(name, O_RDONLY)) >= 0)
+    setinputfd(fd, 1);
+  else
+    sh_error("Can't open %s", name);
+  INTON;
+  cmdloop(0);
+  popfile();
+}
+
+/*
+ * Take commands from a file.  To be compatable we should do a path
+ * search for the file, but a path search doesn't make any sense.
+ */
+int dotcmd(int argc, char **argv)
+{
+  exitstatus = 0;
+  if (argc >= 2) {    /* That's what SVR2 does */
+    setinputfile(argv[1], 1);
+    commandname = argv[1];
+    cmdloop(0);
+    popfile();
+  }
+  return exitstatus;
+}
+
+int exitcmd(int argc, char **argv)
+{
+  if (argc > 1)
+    exitstatus = number(argv[1]);
+  exitshell(exitstatus);
+  return 0;
+}
+
+#ifdef notdef
+/*
+ * Should never be called.
+ */
+void exit(exitstatus)
+{
+  _exit(exitstatus);
+}
+#endif
+
+
+//==================================================================================
+/*
+ * formating times output.
+ */
+void print_times(long c_tck, clock_t xtime)
+{
+  unsigned long s_time;
+  s_time = xtime / c_tck;
+  xtime = xtime % c_tck;
+  out1fmt("%lum%lu.%lus ", (s_time / 60), (s_time % 60), (xtime * 1000) / c_tck);
+  return;
+}
+
+/*
+ * times command.
+ */
+int timescmd(int argc, char **argv)
+{
+  long c_tck = sysconf(_SC_CLK_TCK);
+  struct tms tms_buf;
+  if(c_tck == -1) {
+    sh_error("sysconf failed to get clock ticks");
+    return errno;
+  }
+  if(times(&tms_buf) == (clock_t)-1) {
+    sh_error("Error in getting times");
+    return errno;
+  }
+  print_times(c_tck, tms_buf.tms_utime);
+  print_times(c_tck, tms_buf.tms_stime);
+  out1fmt("%c",'\n');
+  print_times(c_tck, tms_buf.tms_cutime);
+  print_times(c_tck, tms_buf.tms_cstime);
+  out1fmt("%c",'\n');
+  return 0;
+}
+
+/*
+ * ash help command.
+ */
+int helpcmd(int argc, char **argv)
+{
+  const register struct builtincmd *bp;
+  int i = 1;
+  for(bp = builtincmd; bp->name ; bp++) {
+    if((i % 5) == 0) {
+      out1fmt("\n");
+    }
+    out1fmt("%s, ", bp->name);
+    i++;
+  }
+  out1fmt("%c", '\n');
+  return 0;
+}
+
+/*
+ * Kill commmand.
+ */
+#if CFG_ASH_KILL
+int killcmd(int argc, char **argv)
+{
+  int status = 0;
+  pid_t pid = fork();
+  if(pid == 0)
+    toy_exec(argv);
+  else {
+    waitpid(pid, &status, 0);
+    return WEXITSTATUS(status);
+  }
+  return status;
+}
+#endif
+
+/*
+ * test command.
+ */
+#if CFG_ASH_TEST
+int testcmd(int argc, char **argv)
+{
+  int status;
+  pid_t pid = fork();
+  if(pid == 0)
+    toy_exec(argv);
+  else {
+       waitpid(pid, &status, 0);
+       return WEXITSTATUS(status);
+  }
+  return status;
+}
+#endif
+
+/*
+ * printf command.
+ */
+#if CFG_ASH_PRINTF
+int printfcmd(int argc, char **argv)
+{
+  pid_t pid = fork();
+  if(pid == 0)
+    toy_exec(argv);
+  else {
+    int status;
+    waitpid(pid, &status, 0);
+    return WEXITSTATUS(status);
+  }
+  return 0;
+}
+#endif
+//==================================================================================
+//alias command
+static void setalias(char *name, char *val)
+{
+  struct alias *ap, **app;
+
+  app = hashalias(name);
+  for (ap = *app; ap; ap = ap->next) {
+    if (equal(name, ap->name)) {
+      INTOFF;
+      ckfree(ap->val);
+      ap->val  = savestr(val);
+      INTON;
+      return;
+    }
+  }
+  /* not found */
+  INTOFF;
+  ap = ckmalloc(sizeof (struct alias));
+  ap->name = savestr(name);
+  ap->flag = 0;
+  /*
+   * XXX - HACK: in order that the parser will not finish reading the
+   * alias value off the input before processing the next alias, we
+   * dummy up an extra space at the end of the alias.  This is a crock
+   * and should be re-thought.  The idea (if you feel inclined to help)
+   * is to avoid alias recursions.  The mechanism used is: when
+   * expanding an alias, the value of the alias is pushed back on the
+   * input as a string and a pointer to the alias is stored with the
+   * string.  The alias is marked as being in use.  When the input
+   * routine finishes reading the string, it markes the alias not
+   * in use.  The problem is synchronization with the parser.  Since
+   * it reads ahead, the alias is marked not in use before the
+   * resulting token(s) is next checked for further alias sub.  The
+   * H A C K is that we add a little fluff after the alias value
+   * so that the string will not be exhausted.  This is a good
+   * idea ------- ***NOT***
+   */
+#ifdef notyet
+  ap->val = savestr(val);
+#else /* hack */
+  {
+  int len = strlen(val);
+  ap->val = ckmalloc(len + 2);
+  memcpy(ap->val, val, len);
+  ap->val[len] = ' ';  /* fluff */
+  ap->val[len+1] = '\0';
+  }
+#endif
+  ap->next = *app;
+  *app = ap;
+  INTON;
+}
+/*
+ * Serach for aliases.
+ */
+struct alias *lookupalias(char *name, int check)
+{
+  struct alias *ap = *hashalias(name);
+
+  for (; ap; ap = ap->next) {
+    if (equal(name, ap->name)) {
+      if (check && (ap->flag & ALIASINUSE))
+        return (NULL);
+      return (ap);
+    }
+  }
+
+  return (NULL);
+}
+/*
+ * get alias name in text format.
+ */
+char *get_alias_text(char *name)
+{
+  struct alias *ap;
+
+  ap = lookupalias(name, 0);
+  if (ap == NULL)
+    return NULL;
+  return ap->val;
+}
+
+/*
+ * Command to list all variables which are set.  Currently this command
+ * is invoked from the set command when the set command is called without
+ * any variables.
+ */
+void print_quoted(const char *p)
+{
+  const char *q;
+
+  if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) {
+    out1fmt("%s", p);
+    return;
+  }
+  while (*p) {
+    if (*p == '\'') {
+      out1fmt("\\'");
+      p++;
+      continue;
+    }
+    q = index(p, '\'');
+    if (!q) {
+      out1fmt("'%s'", p );
+      return;
+    }
+    out1fmt("'%.*s'", (int)(q - p), p );
+    p = q;
+  }
+}
+
+/*
+ * alias command.
+ */
+int aliascmd(int argc, char **argv)
+{
+  char *n, *v;
+  int ret = 0;
+  struct alias *ap;
+
+  if (argc == 1) {
+    int i;
+
+    for (i = 0; i < ATABSIZE; i++)
+      for (ap = atab[i]; ap; ap = ap->next) {
+        if (*ap->name != '\0') {
+          out1fmt("alias %s=", ap->name);
+          print_quoted(ap->val);
+          out1c('\n');
+        }
+      }
+    return (0);
+  }
+  while ((n = *++argv) != NULL) {
+    if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
+      if ((ap = lookupalias(n, 0)) == NULL) {
+        outfmt(out2, "alias: %s not found\n", n);
+        ret = 1;
+      } else {
+        out1fmt("alias %s=", n);
+        print_quoted(ap->val);
+        out1c('\n');
+      }
+    } else {
+      *v++ = '\0';
+      setalias(n, v);
+    }
+  }
+  return (ret);
+}
+/*
+ * prepare hash alias table.
+ */
+static struct alias **hashalias(char *p)
+{
+  unsigned int hashval;
+
+  hashval = *p << 4;
+  while (*p)
+    hashval+= *p++;
+  return &atab[hashval % ATABSIZE];
+}
+/*
+ * Remove aliases.
+ */
+void rmaliases(void)
+{
+  struct alias *ap, *tmp;
+  int i;
+
+  INTOFF;
+  for (i = 0; i < ATABSIZE; i++) {
+    ap = atab[i];
+    atab[i] = NULL;
+    while (ap) {
+      ckfree(ap->name);
+      ckfree(ap->val);
+      tmp = ap;
+      ap = ap->next;
+      ckfree(tmp);
+    }
+  }
+  INTON;
+}
+/*
+ * remove alias.
+ */
+static int unalias(char *name)
+{
+  struct alias *ap, **app;
+
+  app = hashalias(name);
+
+  for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
+    if (equal(name, ap->name)) {
+      /*
+       * if the alias is currently in use (i.e. its
+       * buffer is being used by the input routine) we
+       * just null out the name instead of freeing it.
+       * We could clear it out later, but this situation
+       * is so rare that it hardly seems worth it.
+       */
+      if (ap->flag & ALIASINUSE)
+        *ap->name = '\0';
+      else {
+        INTOFF;
+        *app = ap->next;
+        ckfree(ap->name);
+        ckfree(ap->val);
+        ckfree(ap);
+        INTON;
+      }
+      return (0);
+    }
+  }
+
+  return (1);
+}
+/*
+ * unalias command.
+ */
+int unaliascmd(int argc, char **argv)
+{
+  int i;
+
+  while ((i = nextopt("a")) != '\0') {
+    if (i == 'a') {
+      rmaliases();
+      return (0);
+    }
+  }
+  for (i = 0; *argptr; argptr++)
+    i = unalias(*argptr);
+
+  return (i);
+}
+/*
+ * type command.
+ */
+int typecmd(int argc, char **argv)
+{
+  struct cmdentry entry;
+  struct tblentry *cmdp;
+  register char *const *pp;
+  struct alias *ap;
+  int err = 0;
+  char *arg;
+  int c;
+  int V_flag = 0;
+  int v_flag = 0;
+  int p_flag = 0;
+
+  while ((c = nextopt("vVp")) != 0) {
+    switch (c) {
+      case 'v': v_flag = 1; break;
+      case 'V': V_flag = 1; break;
+      case 'p': p_flag = 1; break;
+    }
+  }
+  if (p_flag && (v_flag || V_flag))
+    sh_error("cannot specify -p with -v or -V");
+
+  while ((arg = *argptr++)) {
+    if (!v_flag)
+      out1str(arg);
+    /* First look at the keywords */
+    for (pp = parsekwd; *pp; pp++)
+      if (**pp == *arg && equal(*pp, arg))
+        break;
+
+    if (*pp) {
+      if (v_flag)
+        err = 1;
+      else
+        out1str(" is a shell keyword\n");
+      continue;
+    }
+
+    /* Then look at the aliases */
+    if ((ap = lookupalias(arg, 1)) != NULL) {
+      if (!v_flag)
+        out1fmt(" is an alias for \n");
+      out1fmt("%s\n", ap->val);
+      continue;
+    }
+
+    /* Then check if it is a tracked alias */
+    if ((cmdp = cmdlookup(arg, 0)) != NULL) {
+      entry.cmdtype = cmdp->cmdtype;
+      entry.u = cmdp->param;
+    }
+    else {
+      /* Finally use brute force */
+      find_command(arg, &entry, 2);
+    }
+
+    switch (entry.cmdtype) {
+      case CMDNORMAL: {
+        if (strchr(arg, '/') == NULL) {
+          char *path = pathval();
+          char *name;
+          int j = entry.u.index;
+          do {
+            name = padvance(&path, arg);
+            stunalloc(name);
+          } while (--j >= 0);
+          if (!v_flag)
+            out1fmt(" is%s ", cmdp ? " a tracked alias for" : "");
+            out1fmt("%s\n", name);
+          }
+          else {
+            if (access(arg, X_OK) == 0) {
+              if (!v_flag)
+                out1fmt(" is ");
+              out1fmt("%s\n", arg);
+            }
+            else {
+              if (!v_flag)
+                out1fmt(": %s\n", strerror(errno));
+              else
+                err = 126;
+            }
+          }
+        break;
+      }
+      case CMDFUNCTION:
+        if (!v_flag)
+          out1str(" is a shell function\n");
+        else
+          out1fmt("%s\n", arg);
+        break;
+
+      case CMDBUILTIN:
+        if (!v_flag)
+          out1str(" is a shell builtin\n");
+        else
+          out1fmt("%s\n", arg);
+        break;
+
+      case CMDSPLBLTIN:
+        if (!v_flag)
+          out1str(" is a special shell builtin\n");
+        else
+          out1fmt("%s\n", arg);
+        break;
+
+      default:
+           out1str("\n");
+        err = 127;
+        break;
+      }
+  }
+  return err;
+}
+/*
+ * ulimit command.
+ */
+int ulimitcmd(int argc, char **argv)
+{
+  quad_t val = 0;
+  register int c;
+  enum { SOFT = 0x1, HARD = 0x2 }
+          how = SOFT | HARD;
+  const struct limits *l;
+  struct rlimit limit;
+  int set, all = 0;
+  int optc, what;
+
+  what = 'f';
+  while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
+    switch (optc) {
+    case 'H':
+      how = HARD;
+      break;
+    case 'S':
+      how = SOFT;
+      break;
+    case 'a':
+      all = 1;
+      break;
+    default:
+      what = optc;
+      break;
+    }
+
+  for (l = limits; l->name && l->option != what; l++) {/*do nothing*/;}
+  if (!l->name)
+    sh_error("ulimit: internal error (%c)\n", what);
+
+  set = *argptr ? 1 : 0;
+  if (set) {
+    char *p = *argptr;
+
+    if (strcmp(p, "unlimited") == 0)
+      val = RLIM_INFINITY;
+    else {
+      val = (quad_t) 0;
+
+      while ((c = *p++) >= '0' && c <= '9') {
+        val = (val * 10) + (long)(c - '0');
+        if (val < (quad_t) 0)
+          break;
+      }
+      if (c)
+        sh_error("ulimit: bad number\n");
+      val *= l->factor;
+    }
+  }
+  if (all) {
+    for (l = limits; l->name; l++) {
+      getrlimit(l->cmd, &limit);
+      if (how & SOFT)
+        val = limit.rlim_cur;
+      else if (how & HARD)
+        val = limit.rlim_max;
+
+      out1fmt("%-20s ", l->name);
+      if (val == RLIM_INFINITY)
+        out1fmt("unlimited\n");
+      else {
+        val /= l->factor;
+        out1fmt("%ld\n", (long) val);
+      }
+    }
+    return 0;
+  }
+
+  getrlimit(l->cmd, &limit);
+  if (set) {
+    if (how & SOFT)
+      limit.rlim_cur = val;
+    if (how & HARD)
+      limit.rlim_max = val;
+    if (setrlimit(l->cmd, &limit) < 0)
+      sh_error("ulimit: bad limit\n");
+  } else {
+    if (how & SOFT)
+      val = limit.rlim_cur;
+    else if (how & HARD)
+      val = limit.rlim_max;
+  }
+
+  if (!set) {
+    if (val == RLIM_INFINITY)
+      out1fmt("unlimited\n");
+    else {
+      val /= l->factor;
+      out1fmt("%ld\n", (long) val);
+    }
+  }
+  return 0;
+}
+//==================================================================================
+//For History
+/*
+ * get history from ash_history file.
+ */
+void get_history()
+{
+  FILE *fp;
+  char *file_path;
+  char buf[BUFSIZ] = {0,};
+
+  struct passwd *pw;
+  uid_t uid = getuid();
+  pw = getpwuid(uid);
+  file_path = xmsprintf("%s/%s", pw->pw_dir, "ash_history");
+
+  fp = fopen(file_path, "a+");
+  if(!fp) {
+    free(file_path);
+    return;
+  }
+  while(fgets(buf, BUFSIZ, fp)) {
+    char *history_cmd = xstrdup(buf);
+    dlist_add(&hist_list, history_cmd);
+    history_items++;
+  }
+  free(file_path);
+  fclose(fp);
+  return;
+}
+/*
+ *seach history.
+ */
+char *search_in_history(int key)
+{
+  char *str;
+  if(key_list_ptr == NULL)
+    return NULL;
+
+  if(hist_list == NULL)
+    return NULL;
+
+  hist_list->prev->next = NULL;
+  hist_list->prev = NULL;
+
+  if(key == KEY_UP) {
+    if(key_list_ptr->prev == NULL) //we are at top.
+      return key_list_ptr->data;
+    if((key_list_ptr->next == NULL) && !up_key_pressed && enter_flag) {//we are at bottom
+      str = key_list_ptr->data;
+      up_key_pressed = 1;
+      enter_flag = 0;
+      return str;
+    }
+    key_list_ptr = key_list_ptr->prev;
+    str = key_list_ptr->data;
+    up_key_pressed = 0;
+    return str;
+  }
+  else {
+    if(key_list_ptr->next == NULL) {//we are at bottom.
+      enter_flag = 1;
+      up_key_pressed = 0;
+      return NULL;
+    }
+
+    key_list_ptr = key_list_ptr->next;
+    str = key_list_ptr->data;
+    if(key_list_ptr->next == NULL)
+      up_key_pressed = 0;
+    enter_flag = 0;
+    return str;
+  }
+}
+/*
+ * write history to ash_history file.
+ */
+void save_history()
+{
+  FILE *fp;
+  struct passwd *pw;
+
+  if(!hist_list)
+    return;
+  uid_t uid = getuid();
+  pw = getpwuid(uid);
+  char *file_path;
+  file_path = xmsprintf("%s/%s", pw->pw_dir, "ash_history");
+  fp = xfopen(file_path, "w");
+
+  struct double_list *temp_list = hist_list;
+  temp_list->prev->next = NULL;
+  while(temp_list) {
+    struct double_list *tmp_ptr = temp_list;
+    fputs(temp_list->data, fp);
+    temp_list = temp_list->next;
+    free(tmp_ptr->data);
+    free(tmp_ptr);
+  }
+  free(file_path);
+  fclose(fp);
+  return;
+}
+/*
+ * add command to history list.
+ */
+void add_to_history(const char *p)
+{
+  int i = 0;
+  char *history_cmd = (char *)p;
+
+  while(history_cmd[i] != '\n')
+    i++;
+  history_cmd = xzalloc(i+2);
+  memcpy(history_cmd, p, i+1);
+
+  enter_flag = 1;
+  up_key_pressed = 0;
+
+  if(history_items > 0) {
+    if(strcmp(hist_list->prev->data, history_cmd) == 0) {
+      free(history_cmd);
+      key_list_ptr = hist_list->prev;
+      up_key_pressed = 0;
+      return;
+    }
+  }
+  if(history_items == NUM_OF_HISTORY_ITEMS) {
+    struct double_list *temp = hist_list;
+    hist_list->prev->next = hist_list->next;
+    hist_list->next->prev = hist_list->prev;
+    hist_list = hist_list->next;
+
+    free(temp->data);
+    free(temp);
+    dlist_add(&hist_list, history_cmd);
+    key_list_ptr = hist_list->prev;
+    return;
+  }
+
+  dlist_add(&hist_list, history_cmd);
+  key_list_ptr = hist_list->prev;
+  history_items++;
+  return;
+}
+/*
+ * history command.
+ */
+int historycmd(int argc, char **argv)
+{
+  struct double_list *tmp_ptr = hist_list;
+  int list_items = 0;
+
+  if(!tmp_ptr)
+    return -1;
+
+  do {
+    xprintf("%5d   %s", list_items, tmp_ptr->data);
+    tmp_ptr = tmp_ptr->next;
+    list_items++;
+  }while(tmp_ptr != hist_list);
+  return 0;
+}
+
+//==================================================================================
+//for let command:
+#ifdef ECHO
+# undef ECHO
+#include "lib/ash_lexyyc"
+#include "lib/ash_ytabc"
+#endif
+//==================================================================================
diff --git a/toys/posix/awk.c b/toys/posix/awk.c
new file mode 100644 (file)
index 0000000..557bb5f
--- /dev/null
@@ -0,0 +1,4726 @@
+/* awk.c - awk implementation.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ *           Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/awk.html
+
+USE_AWK(NEWTOY(awk, "f*F:v*ds", TOYFLAG_USR|TOYFLAG_BIN))
+
+config AWK
+  bool "awk"
+  default y
+  help
+    usage: awk [OPTIONS] [AWK_PROGRAM] [FILE]...
+
+    -v VAR=VAL    Set variable
+    -F SEP      Use SEP as field separator
+    -f FILE     Read program from FILE
+*/
+
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define FOR_awk
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <math.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <time.h>
+
+#include "toys.h"
+#include "lib/awk.h"
+#include "lib/awk_ytabc"
+
+#define  DEBUG
+#define  MAX_PFILE    20          /* max number of -f's */
+#define  HAT        (NCHARS+2)      /* matches ^ in regular expr */
+#define  NFA        20          /* cache this many dynamic fa's */
+#define MAXLIN       22
+#define type(v)      (v)->nobj      /* badly overloaded here */
+#define info(v)      (v)->ntype      /* badly overloaded here */
+#define left(v)      (v)->narg[0]
+#define right(v)    (v)->narg[1]
+#define parent(v)    (v)->nnext
+#define LEAF      case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
+#define ELEAF      case EMPTYRE:    /* empty string in regexp */
+#define UNARY      case STAR: case PLUS: case QUEST:
+#define  MAXFLD      2
+#define  FULLTAB      2          /* rehash when table gets this x full */
+#define  GROWTAB      4          /* grow table by this factor */
+#define  RET(x)      { if(dbg)printf("lex %s\n", tokname(x)); return(x); }
+#define tempfree(x)    if (istemp(x)) tfree(x); else
+#define  NARGS      50          /* max args in a call */
+#define  MAXNUMSIZE    50
+#define PA2NUM      50          /* max number of pat,pat patterns allowed */
+#define isoctdigit(c)  ((c) >= '0' && (c) <= '7')  /* multiple use of arg */
+#define  NFA        20          /* cache this many dynamic fa's */
+
+struct Frame {        /* stack frame for awk function calls */
+  int nargs;        /* number of arguments in this call */
+  Cell *fcncell;      /* pointer to Cell for function */
+  Cell **args;      /* pointer to array of arguments after execute */
+  Cell *retval;      /* return value */
+};
+
+typedef struct Keyword {
+  const char *word;
+  int  sub;
+  int  type;
+} Keyword;
+
+struct files {
+  FILE  *fp;
+  const char  *fname;
+  int  mode;        /* '|', 'a', 'w' => LE/LT, GT */
+} *files;
+
+GLOBALS(
+  struct arg_list *vars;
+  char *sep;
+  struct arg_list *prog_files;
+)
+
+struct charclass {
+  const char *cc_name;
+  int cc_namelen;
+  int (*cc_func)(int);
+} charclasses[] = {
+  { "alnum",  5,  isalnum },
+  { "alpha",  5,  isalpha },
+#ifndef HAS_ISBLANK
+  { "blank",  5,  isspace }, /* was isblank */
+#else
+  { "blank",  5,  isblank },
+#endif
+  { "cntrl",  5,  iscntrl },
+  { "digit",  5,  isdigit },
+  { "graph",  5,  isgraph },
+  { "lower",  5,  islower },
+  { "print",  5,  isprint },
+  { "punct",  5,  ispunct },
+  { "space",  5,  isspace },
+  { "upper",  5,  isupper },
+  { "xdigit",  6,  isxdigit },
+  { NULL,    0,  NULL },
+};
+
+extern int  infunc;
+extern char  **environ;
+
+jmp_buf env;
+Node  *winner = NULL;    /* root of parse tree */
+Cell  *tmps;        /* free temporary cells for execution */
+static Cell  truecell  ={ OBOOL, BTRUE, 0, 0, 1.0, NUM };
+Cell  *True  = &truecell;
+static Cell  falsecell  ={ OBOOL, BFALSE, 0, 0, 0.0, NUM };
+Cell  *False  = &falsecell;
+static Cell  breakcell  ={ OJUMP, JBREAK, 0, 0, 0.0, NUM };
+Cell  *jbreak  = &breakcell;
+static Cell  contcell  ={ OJUMP, JCONT, 0, 0, 0.0, NUM };
+Cell  *jcont  = &contcell;
+static Cell  nextcell  ={ OJUMP, JNEXT, 0, 0, 0.0, NUM };
+Cell  *jnext  = &nextcell;
+static Cell  nextfilecell  ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM };
+Cell  *jnextfile  = &nextfilecell;
+static Cell  exitcell  ={ OJUMP, JEXIT, 0, 0, 0.0, NUM };
+Cell  *jexit  = &exitcell;
+static Cell  retcell    ={ OJUMP, JRET, 0, 0, 0.0, NUM };
+Cell  *jret  = &retcell;
+static Cell  tempcell  ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE };
+Node  *curnode = NULL;  /* the node being executed, for debugging */
+
+int  lineno  = 1;
+int  bracecnt = 0;
+int  brackcnt  = 0;
+int  parencnt = 0;
+int  *setvec;
+int  *tmpset;
+int  maxsetvec = 0;
+int  rtok;          /* next token in current re */
+int  rlxval;
+int  recsize  = RECSIZE;
+int  fieldssize = RECSIZE;
+int  patlen;
+int  nfields  = MAXFLD;    /* last allocated slot for $i */
+int  donefld;        /* 1 = implies rec broken into fields */
+int  donerec;        /* 1 = record is valid (no flds have changed) */
+int  lastfld  = 0;      /* last used field */
+int  argno  = 1;      /* current input argument number */
+int  nfatab  = 0;      /* entries in fatab */
+int nfiles;
+int  sc  = 0;        /* 1 => return a } right now */
+int  reg  = 0;        /* 1 => return a REGEXPR now */
+int  compile_time = 2;    /* for error printing: 2 = cmdline, 1 = compile, 0 = running */
+int  npfile = 0;        /* number of filenames */
+int  curpfile = 0;      /* current filename */
+int  safe  = 0;      /* 1 => "safe" mode */
+int  nframe = 0;        /* number of frames allocated */
+int  paircnt;        /* number of them in use */
+int  pairstack[PA2NUM];    /* state of each pat,pat */
+int  dbg  = 0;
+
+Array  *symtab;      /* main symbol table */
+Array  *ARGVtab;      /* symbol table containing ARGV[...] */
+Array  *ENVtab;      /* symbol table containing ENVIRON[...] */
+
+char  **FS;        /* initial field sep */
+char  **RS;        /* initial record sep */
+char  **OFS;        /* output field sep */
+char  **ORS;        /* output record sep */
+char  **OFMT;        /* output format for numbers */
+char  **CONVFMT;      /* format for conversions in getsval */
+char  **SUBSEP;      /* subscript separator for a[i,j,k]; default \034 */
+char  **FILENAME;      /* current filename argument */
+char  *file  = "";
+char  *record;
+char  *fields;
+char  *patbeg;
+char  inputFS[100] = " ";
+char  *pfile[MAX_PFILE];  /* program filenames from -f's */
+char  ebuf[300];
+char  *ep = ebuf;
+char  yysbuf[100];    /* pushback buffer */
+char  *yysptr = yysbuf;
+char  *cmdname;      /* gets argv[0] for error messages */
+char  *lexprog;      /* points to program argument if it exists */
+
+Awkfloat *NF;        /* number of fields in current record */
+Awkfloat *ERRNO;      /* global error no. */
+Awkfloat *NR;        /* number of current record */
+Awkfloat *FNR;        /* number of current record in current file */
+Awkfloat *ARGC;        /* number of arguments from command line */
+Awkfloat *RSTART;      /* start of re matched with ~; origin 1 (!) */
+Awkfloat *RLENGTH;      /* length of same */
+Awkfloat srand_seed = 1;
+
+Cell  *fsloc;        /* FS */
+Cell  *nrloc;        /* NR */
+Cell  *nfloc;        /* NF */
+Cell  *errloc;      /* ERRNO */
+Cell  *fnrloc;      /* FNR */
+Cell  *rstartloc;      /* RSTART */
+Cell  *rlengthloc;    /* RLENGTH */
+Cell  *symtabloc;      /* SYMTAB */
+Cell  *nullloc;      /* a guaranteed empty cell */
+Cell  *literal0;
+Cell  **fldtab;      /* pointers to Cells */
+
+Node  *nullnode;      /* zero&null, converted into a node for comparisons */
+
+static int     setcnt;
+static int     poscnt;
+static int     firsttime = 1;
+static uschar  *rlxstr;
+static uschar  *prestr;  /* current position in current re */
+static uschar  *lastre;  /* origin of last re */
+static Cell    dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
+static Cell    dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
+
+fa    *fatab[NFA];
+FILE  *infile  = NULL;
+FILE  *yyin = 0;
+
+const char  *version = "version 2012_09_14";
+
+struct Frame *fp = NULL;  /* frame pointer. bottom level unused */
+struct Frame *frame = NULL;  /* base of stack frames; dynamically allocated */
+
+int  word(char *);
+int  string(void);
+int  regexpr(void);
+
+Keyword keywords[] ={  /* keep sorted: binary searched */
+  { "BEGIN",  XBEGIN,    XBEGIN },
+  { "END",  XEND,    XEND },
+  { "ERRNO",  VARERR,   VARERR },
+  { "NF",    VARNF,    VARNF },
+  { "atan2",  FATAN,    BLTIN },
+  { "and",  BITAND,   BITAND },
+  { "break",  BREAK,    BREAK },
+  { "close",  CLOSE,    CLOSE },
+  { "compl",  BITCOMPL,   BITCOMPL },
+  { "continue",  CONTINUE,  CONTINUE },
+  { "cos",  FCOS,    BLTIN },
+  { "delete",  DELETE,    DELETE },
+  { "do",    DO,    DO },
+  { "else",  ELSE,    ELSE },
+  { "exit",  EXIT,    EXIT },
+  { "exp",  FEXP,    BLTIN },
+  { "fflush",  FFLUSH,    BLTIN },
+  { "for",  FOR,    FOR },
+  { "func",  FUNC,    FUNC },
+  { "function",  FUNC,    FUNC },
+  { "getline",  GETLINE,  GETLINE },
+  { "gsub",  GSUB,    GSUB },
+  { "if",    IF,    IF },
+  { "in",    IN,    IN },
+  { "index",  INDEX,    INDEX },
+  { "int",  FINT,    BLTIN },
+  { "length",  FLENGTH,  BLTIN },
+  { "log",  FLOG,    BLTIN },
+  { "lshift", BITLSHIFT,  BITLSHIFT },
+  { "match",  MATCHFCN,  MATCHFCN },
+  { "mktime", FMKTIME,   BLTIN   },
+  { "next",  NEXT,    NEXT },
+  { "nextfile",  NEXTFILE,  NEXTFILE },
+  { "or",   BITOR,    BITOR },
+  { "print",  PRINT,    PRINT },
+  { "printf",  PRINTF,    PRINTF },
+  { "rand",  FRAND,    BLTIN },
+  { "return",  RETURN,    RETURN },
+  { "rshift", BITRSHIFT,  BITRSHIFT },
+  { "sin",  FSIN,    BLTIN },
+  { "split",  SPLIT,    SPLIT },
+  { "sprintf",  SPRINTF,  SPRINTF },
+  { "sqrt",  FSQRT,    BLTIN },
+  { "srand",  FSRAND,    BLTIN },
+  { "strftime", FSTRFTIME, BLTIN },
+  { "sub",  SUB,    SUB },
+  { "substr",  SUBSTR,    SUBSTR },
+  { "system",  FSYSTEM,  BLTIN },
+  { "systime", FSYSTIME,  BLTIN },
+  { "tolower",  FTOLOWER,  BLTIN },
+  { "toupper",  FTOUPPER,  BLTIN },
+  { "while",  WHILE,    WHILE },
+  { "xor",  BITXOR,   BITXOR },
+};
+
+/* buffer memory management */
+
+/* pbuf:  address of pointer to buffer being managed
+ * psiz:  address of buffer size variable
+ * minlen:  minimum length of buffer needed
+ * quantum: buffer size quantum
+ * pbptr:   address of movable pointer into buffer, or 0 if none
+ * whatrtn: name of the calling routine if failure should cause fatal error
+ *
+ * return   0 for realloc failure, !=0 for success
+ */
+
+int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
+  const char *whatrtn)
+{
+  if (minlen > *psiz) {
+    char *tbuf;
+    int rminlen = quantum ? minlen % quantum : 0;
+    int boff = pbptr ? *pbptr - *pbuf : 0;
+
+    if (rminlen) minlen += quantum - rminlen; /* round up to next multiple of quantum */
+    tbuf = (char *) realloc(*pbuf, minlen);
+    dprintf(("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, *pbuf, tbuf));
+    if (tbuf == NULL) {
+      if (whatrtn) FATAL("out of memory in %s", whatrtn);
+      return 0;
+    }
+    *pbuf = tbuf;
+    *psiz = minlen;
+    if (pbptr) *pbptr = tbuf + boff;
+  }
+  return 1;
+}
+
+void run(Node *a)  /* execution of parse tree starts here */
+{
+  extern void stdinit(void);
+  stdinit();
+  execute(a);
+  closeall();
+}
+
+Cell *execute(Node *u)  /* execute a node of the parse tree */
+{
+  Cell *(*proc)(Node **, int);
+  Cell *x;
+  Node *a;
+
+  if (u == NULL) return(True);
+  for (a = u; ; a = a->nnext) {
+    curnode = a;
+    if (isvalue(a)) {
+      x = (Cell *) (a->narg[0]);
+      if (isfld(x) && !donefld) fldbld();
+      else if (isrec(x) && !donerec) recbld();
+      return(x);
+    }
+    if (notlegal(a->nobj)) FATAL("illegal statement"); /* probably a Cell* but too risky to print */
+    proc = proctab[a->nobj-FIRSTTOKEN];
+    x = (*proc)(a->narg, a->nobj);
+    if (isfld(x) && !donefld) fldbld();
+    else if (isrec(x) && !donerec) recbld();
+    if (isexpr(a)) return(x);
+    if (isjump(x)) return(x);
+    if (a->nnext == NULL) return(x);
+    tempfree(x);
+  }
+}
+
+Cell *program(Node **a, int n)  /* execute an awk program */
+{                /* a[0] = BEGIN, a[1] = body, a[2] = END */
+  Cell *x;
+
+  if (setjmp(env) != 0) goto ex;
+  if (a[0]) {    /* BEGIN */
+    x = execute(a[0]);
+    if (isexit(x)) return(True);
+    if (isjump(x))
+      FATAL("illegal break, continue, next or nextfile from BEGIN");
+    tempfree(x);
+  }
+  if (a[1] || a[2])
+    while (getrec(&record, &recsize, 1) > 0) {
+      x = execute(a[1]);
+      if (isexit(x)) break;
+      tempfree(x);
+    }
+  ex:
+  if (setjmp(env) != 0) goto ex1; /* handles exit within END */
+  if (a[2]) {    /* END */
+    x = execute(a[2]);
+    if (isbreak(x) || isnext(x) || iscont(x))
+      FATAL("illegal break, continue, next or nextfile from END");
+    tempfree(x);
+  }
+  ex1:
+  return(True);
+}
+
+Cell *call(Node **a, int n)  /* function call.  very kludgy and fragile */
+{
+  static Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE };
+  int i, ncall, ndef;
+  int freed = 0; /* handles potential double freeing when fcn & param share a tempcell */
+  Node *x;
+  Cell *args[NARGS], *oargs[NARGS];  /* BUG: fixed size arrays */
+  Cell *y, *z, *fcn;
+  char *s;
+
+  fcn = execute(a[0]);  /* the function itself */
+  s = fcn->nval;
+  if (!isfcn(fcn))
+    FATAL("calling undefined function %s", s);
+  if (frame == NULL) {
+    fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame));
+    if (frame == NULL)
+      FATAL("out of space for stack frames calling %s", s);
+  }
+  for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) ncall++; /* args in call */
+  ndef = (int) fcn->fval;      /* args in defn */
+  dprintf( ("calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) );
+  if (ncall > ndef)
+    WARNING("function %s called with %d args, uses only %d", s, ncall, ndef);
+  if (ncall + ndef > NARGS)
+    FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS);
+  for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) {  /* get call args */
+    dprintf( ("evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) );
+    y = execute(x);
+    oargs[i] = y;
+    dprintf( ("args[%d]: %s %f <%s>, t=%o\n",
+         i, NN(y->nval), y->fval, isarr(y) ? "(array)" : NN(y->sval), y->tval) );
+    if (isfcn(y))
+      FATAL("can't use function %s as argument in %s", y->nval, s);
+    if (isarr(y)) args[i] = y;  /* arrays by ref */
+    else args[i] = copycell(y);
+    tempfree(y);
+  }
+  for ( ; i < ndef; i++) {  /* add null args for ones not provided */
+    args[i] = gettemp();
+    *args[i] = newcopycell;
+  }
+  fp++;  /* now ok to up frame */
+  if (fp >= frame + nframe) {
+    int dfp = fp - frame;  /* old index */
+    frame = (struct Frame *)
+    realloc((char *) frame, (nframe += 100) * sizeof(struct Frame));
+    if (frame == NULL) FATAL("out of space for stack frames in %s", s);
+    fp = frame + dfp;
+  }
+  fp->fcncell = fcn;
+  fp->args = args;
+  fp->nargs = ndef;  /* number defined with (excess are locals) */
+  fp->retval = gettemp();
+
+  dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+  y = execute((Node *)(fcn->sval));  /* execute body */
+  dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+
+  for (i = 0; i < ndef; i++) {
+    Cell *t = fp->args[i];
+    if (isarr(t)) {
+      if (t->csub == CCOPY) {
+        if (i >= ncall) {
+          freesymtab(t);
+          t->csub = CTEMP;
+          tempfree(t);
+        } else {
+          oargs[i]->tval = t->tval;
+          oargs[i]->tval &= ~(STR|NUM|DONTFREE);
+          oargs[i]->sval = t->sval;
+          tempfree(t);
+        }
+      }
+    } else if (t != y) {  /* kludge to prevent freeing twice */
+      t->csub = CTEMP;
+      tempfree(t);
+    } else if (t == y && t->csub == CCOPY) {
+      t->csub = CTEMP;
+      tempfree(t);
+      freed = 1;
+    }
+  }
+  tempfree(fcn);
+  if (isexit(y) || isnext(y)) return y;
+  if (freed == 0) {tempfree(y);}  /* don't free twice! */
+  z = fp->retval;      /* return value */
+  dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) );
+  fp--;
+  return(z);
+}
+
+Cell *copycell(Cell *x)  /* make a copy of a cell in a temp */
+{
+  Cell *y;
+
+  y = gettemp();
+  y->csub = CCOPY;  /* prevents freeing until call is over */
+  y->nval = x->nval;  /* BUG? */
+  if (isstr(x)) y->sval = tostring(x->sval);
+  y->fval = x->fval;
+  y->tval = x->tval & ~(CON|FLD|REC|DONTFREE);  /* copy is not constant or field */
+              /* is DONTFREE right? */
+  return y;
+}
+
+Cell *arg(Node **a, int n)  /* nth argument of a function */
+{
+
+  n = ptoi(a[0]);  /* argument number, counting from 0 */
+  dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) );
+  if (n+1 > fp->nargs)
+    FATAL("argument #%d of function %s was not supplied",
+      n+1, fp->fcncell->nval);
+  return fp->args[n];
+}
+
+Cell *jump(Node **a, int n)  /* break, continue, next, nextfile, return */
+{
+  Cell *y;
+
+  switch (n) {
+  case EXIT:
+    if (a[0] != NULL) {
+      y = execute(a[0]);
+      errorflag = (int) getfval(y);
+      tempfree(y);
+    }
+    longjmp(env, 1);
+  case RETURN:
+    if (a[0] != NULL) {
+      y = execute(a[0]);
+      if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+        setsval(fp->retval, getsval(y));
+        fp->retval->fval = getfval(y);
+        fp->retval->tval |= NUM;
+      }
+      else if (y->tval & STR) setsval(fp->retval, getsval(y));
+      else if (y->tval & NUM) setfval(fp->retval, getfval(y));
+      else FATAL("bad type variable %d", y->tval); /* can't happen */
+      tempfree(y);
+    }
+    return(jret);
+  case NEXT: return(jnext);
+  case NEXTFILE:
+    nextfile();
+    return(jnextfile);
+  case BREAK: return(jbreak);
+  case CONTINUE: return(jcont);
+  default: FATAL("illegal jump type %d", n);   /* can't happen */
+  }
+  return 0;  /* not reached */
+}
+
+Cell *awkgetline(Node **a, int n)  /* get next line from specific input */
+{    /* a[0] is variable, a[1] is operator, a[2] is filename */
+  Cell *r, *x;
+  extern Cell **fldtab;
+  FILE *fp;
+  char *buf;
+  int bufsize = recsize;
+  int mode;
+
+  if ((buf = (char *) malloc(bufsize)) == NULL)
+    FATAL("out of memory in getline");
+
+  fflush(stdout);  /* in case someone is waiting for a prompt */
+  r = gettemp();
+  if (a[1] != NULL) {    /* getline < file */
+    x = execute(a[2]);    /* filename */
+    mode = ptoi(a[1]);
+    if (mode == '|')    /* input pipe */
+      mode = LE;  /* arbitrary flag */
+    fp = openfile(mode, getsval(x));
+    tempfree(x);
+    if (fp == NULL) n = -1;
+    else n = readrec(&buf, &bufsize, fp);
+    if (n <= 0) {
+      ;
+    } else if (a[0] != NULL) {  /* getline var <file */
+      x = execute(a[0]);
+      setsval(x, buf);
+      tempfree(x);
+    } else {      /* getline <file */
+      setsval(fldtab[0], buf);
+      if (is_number(fldtab[0]->sval)) {
+        fldtab[0]->fval = atof(fldtab[0]->sval);
+        fldtab[0]->tval |= NUM;
+      }
+    }
+  } else {      /* bare getline; use current input */
+    if (a[0] == NULL)  /* getline */
+      n = getrec(&record, &recsize, 1);
+    else {      /* getline var */
+      n = getrec(&buf, &bufsize, 0);
+      x = execute(a[0]);
+      setsval(x, buf);
+      tempfree(x);
+    }
+  }
+  setfval(r, (Awkfloat) n);
+  free(buf);
+  return r;
+}
+
+Cell *getnf(Node **a, int n)  /* get NF */
+{
+  if (donefld == 0) fldbld();
+  return (Cell *) a[0];
+}
+
+Cell *geterrno(Node **a, int n)  /* get ERRNO */
+{
+  setfval(errloc, (Awkfloat) errno);
+  return (Cell *) a[0];
+}
+
+Cell *array(Node **a, int n)  /* a[0] is symtab, a[1] is list of subscripts */
+{
+  Cell *x, *y, *z;
+  char *s;
+  Node *np;
+  char *buf;
+  int bufsz = recsize;
+  int nsub = strlen(*SUBSEP);
+
+  if ((buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of memory in array");
+
+  x = execute(a[0]);  /* Cell* for symbol table */
+  buf[0] = 0;
+  for (np = a[1]; np; np = np->nnext) {
+    y = execute(np);  /* subscript */
+    s = getsval(y);
+    if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "array"))
+      FATAL("out of memory for %s[%s...]", x->nval, buf);
+    strcat(buf, s);
+    if (np->nnext) strcat(buf, *SUBSEP);
+    tempfree(y);
+  }
+  if (!isarr(x)) {
+       dprintf( ("making %s into an array\n", NN(x->nval)) );
+    if (freeable(x)) xfree(x->sval);
+    x->tval &= ~(STR|NUM|DONTFREE);
+    x->tval |= ARR;
+    x->sval = (char *) makesymtab(NSYMTAB);
+  }
+  z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval);
+  z->ctype = OCELL;
+  z->csub = CVAR;
+  tempfree(x);
+  free(buf);
+  return(z);
+}
+
+Cell *awkdelete(Node **a, int n)  /* a[0] is symtab, a[1] is list of subscripts */
+{
+  Cell *x, *y;
+  Node *np;
+  char *s;
+  int nsub = strlen(*SUBSEP);
+
+  x = execute(a[0]);  /* Cell* for symbol table */
+  if (!isarr(x)) return True;
+  if (a[1] == 0) {  /* delete the elements, not the table */
+    freesymtab(x);
+    x->tval &= ~STR;
+    x->tval |= ARR;
+    x->sval = (char *) makesymtab(NSYMTAB);
+  } else {
+    int bufsz = recsize;
+    char *buf;
+    if ((buf = (char *) malloc(bufsz)) == NULL)
+      FATAL("out of memory in adelete");
+    buf[0] = 0;
+    for (np = a[1]; np; np = np->nnext) {
+      y = execute(np);  /* subscript */
+      s = getsval(y);
+      if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "awkdelete"))
+        FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+      strcat(buf, s);
+      if (np->nnext) strcat(buf, *SUBSEP);
+      tempfree(y);
+    }
+    freeelem(x, buf);
+    free(buf);
+  }
+  tempfree(x);
+  return True;
+}
+
+Cell *intest(Node **a, int n)  /* a[0] is index (list), a[1] is symtab */
+{
+  Cell *x, *ap, *k;
+  Node *p;
+  char *buf;
+  char *s;
+  int bufsz = recsize;
+  int nsub = strlen(*SUBSEP);
+
+  ap = execute(a[1]);  /* array name */
+  if (!isarr(ap)) {
+       dprintf( ("making %s into an array\n", ap->nval) );
+    if (freeable(ap)) xfree(ap->sval);
+    ap->tval &= ~(STR|NUM|DONTFREE);
+    ap->tval |= ARR;
+    ap->sval = (char *) makesymtab(NSYMTAB);
+  }
+  if ((buf = (char *) malloc(bufsz)) == NULL) {
+    FATAL("out of memory in intest");
+  }
+  buf[0] = 0;
+  for (p = a[0]; p; p = p->nnext) {
+    x = execute(p);  /* expr */
+    s = getsval(x);
+    if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "intest"))
+      FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+    strcat(buf, s);
+    tempfree(x);
+    if (p->nnext) strcat(buf, *SUBSEP);
+  }
+  k = lookup(buf, (Array *) ap->sval);
+  tempfree(ap);
+  free(buf);
+  if (k == NULL) return(False);
+  else return(True);
+}
+
+Cell *matchop(Node **a, int n)  /* ~ and match() */
+{
+  Cell *x, *y;
+  char *s, *t;
+  int i;
+  fa *pfa;
+  int (*mf)(fa *, const char *) = match, mode = 0;
+
+  if (n == MATCHFCN) {
+    mf = pmatch;
+    mode = 1;
+  }
+  x = execute(a[1]);  /* a[1] = target text */
+  s = getsval(x);
+  if (a[0] == 0)    /* a[1] == 0: already-compiled reg expr */
+    i = (*mf)((fa *) a[2], s);
+  else {
+    y = execute(a[2]);  /* a[2] = regular expr */
+    t = getsval(y);
+    pfa = makedfa(t, mode);
+    i = (*mf)(pfa, s);
+    tempfree(y);
+  }
+  tempfree(x);
+  if (n == MATCHFCN) {
+    int start = patbeg - s + 1;
+    if (patlen < 0) start = 0;
+    setfval(rstartloc, (Awkfloat) start);
+    setfval(rlengthloc, (Awkfloat) patlen);
+    x = gettemp();
+    x->tval = NUM;
+    x->fval = start;
+    return x;
+  } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
+    return(True);
+  else return(False);
+}
+
+Cell *boolop(Node **a, int n)  /* a[0] || a[1], a[0] && a[1], !a[0] */
+{
+  Cell *x, *y;
+  int i;
+
+  x = execute(a[0]);
+  i = istrue(x);
+  tempfree(x);
+  switch (n) {
+  case BOR:
+    if (i) return(True);
+    y = execute(a[1]);
+    i = istrue(y);
+    tempfree(y);
+    if (i) return(True);
+    else return(False);
+  case AND:
+    if ( !i ) return(False);
+    y = execute(a[1]);
+    i = istrue(y);
+    tempfree(y);
+    if (i) return(True);
+    else return(False);
+  case NOT:
+    if (i) return(False);
+    else return(True);
+  default:  /* can't happen */
+    FATAL("unknown boolean operator %d", n);
+    break;
+  }
+  return 0;  /*NOTREACHED*/
+}
+
+Cell *bitops(Node **a, int n)  /* or(a[0],a[1]) and(a[0],a[1]) compl(a[0]) */
+{                /* lshit(a[0], a[1]) ... etc */
+  Cell *x, *y, *z;
+  Awkfloat s1, s2, r = 0;
+  x = execute(a[0]);
+  s1 = getfval(x);
+  y = execute(a[1]);
+  s2 = getfval(y);
+
+  switch (n) {
+    case BITOR:
+      r = (Awkfloat)((unsigned long)s1 | (unsigned long)s2);
+      break;
+    case BITAND:
+      r = (Awkfloat)((unsigned long)s1 & (unsigned long)s2);
+      break;
+    case BITCOMPL:
+      r = (Awkfloat)(~ (unsigned long) s1);
+      break;
+    case BITXOR:
+      r = (Awkfloat)((unsigned long)s1 ^ (unsigned long)s2);
+      break;
+    case BITLSHIFT:
+      r = (Awkfloat)((unsigned long)s1 << (unsigned long)s2);
+      break;
+    case BITRSHIFT:
+      r = (Awkfloat)((unsigned long)s1 >> (unsigned long)s2);
+      break;
+    default:  /* can't happen */
+      FATAL("unknown boolean operator %d", n);
+      break;
+  }
+
+  z = gettemp();
+  setfval(z, r);
+  tempfree(x);
+  tempfree(y);
+  return(z);
+}
+
+Cell *relop(Node **a, int n)  /* a[0 < a[1], etc. */
+{
+  int i;
+  Cell *x, *y;
+  Awkfloat j;
+
+  x = execute(a[0]);
+  y = execute(a[1]);
+  if (x->tval&NUM && y->tval&NUM) {
+    j = x->fval - y->fval;
+    i = j<0? -1: (j>0? 1: 0);
+  } else i = strcmp(getsval(x), getsval(y));
+
+  tempfree(x);
+  tempfree(y);
+  switch (n) {
+  case LT:  if (i<0) return(True);
+      else return(False);
+  case LE:  if (i<=0) return(True);
+      else return(False);
+  case NE:  if (i!=0) return(True);
+      else return(False);
+  case EQ:  if (i == 0) return(True);
+      else return(False);
+  case GE:  if (i>=0) return(True);
+      else return(False);
+  case GT:  if (i>0) return(True);
+      else return(False);
+  default:  /* can't happen */
+    FATAL("unknown relational operator %d", n);
+  }
+  return 0;  /*NOTREACHED*/
+}
+
+void tfree(Cell *a)  /* free a tempcell */
+{
+  if (freeable(a)) {
+    dprintf( ("freeing %s %s %o\n", NN(a->nval), NN(a->sval), a->tval) );
+    xfree(a->sval);
+  }
+  if (a == tmps) FATAL("tempcell list is curdled");
+  a->cnext = tmps;
+  tmps = a;
+}
+
+Cell *gettemp(void)  /* get a tempcell */
+{  int i;
+  Cell *x;
+
+  if (!tmps) {
+    tmps = (Cell *) calloc(100, sizeof(Cell));
+    if (!tmps) FATAL("out of space for temporaries");
+    for(i = 1; i < 100; i++) tmps[i-1].cnext = &tmps[i];
+    tmps[i-1].cnext = 0;
+  }
+  x = tmps;
+  tmps = x->cnext;
+  *x = tempcell;
+  return(x);
+}
+
+Cell *indirect(Node **a, int n)  /* $( a[0] ) */
+{
+  Awkfloat val;
+  Cell *x;
+  int m;
+  char *s;
+
+  x = execute(a[0]);
+  val = getfval(x);  /* freebsd: defend against super large field numbers */
+  if ((Awkfloat)INT_MAX < val)
+    FATAL("trying to access out of range field %s", x->nval);
+  m = (int) val;
+  if (m == 0 && !is_number(s = getsval(x)))  /* suspicion! */
+    FATAL("illegal field $(%s), name \"%s\"", s, x->nval);
+    /* BUG: can x->nval ever be null??? */
+  tempfree(x);
+  x = fieldadr(m);
+  x->ctype = OCELL;  /* BUG?  why are these needed? */
+  x->csub = CFLD;
+  return(x);
+}
+
+Cell *substr(Node **a, int nnn)    /* substr(a[0], a[1], a[2]) */
+{
+  int k, m, n;
+  char *s;
+  int temp;
+  Cell *x, *y, *z = 0;
+
+  x = execute(a[0]);
+  y = execute(a[1]);
+  if (a[2] != 0)
+    z = execute(a[2]);
+  s = getsval(x);
+  k = strlen(s) + 1;
+  if (k <= 1) {
+    tempfree(x);
+    tempfree(y);
+    if (a[2] != 0){ tempfree(z);}
+    x = gettemp();
+    setsval(x, "");
+    return(x);
+  }
+  m = (int) getfval(y);
+  if (m <= 0) m = 1;
+  else if (m > k) m = k;
+  tempfree(y);
+  if (a[2] != 0) {
+    n = (int) getfval(z);
+    tempfree(z);
+  } else n = k - 1;
+  if (n < 0) n = 0;
+  else if (n > k - m) n = k - m;
+  dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) );
+  y = gettemp();
+  temp = s[n+m-1];  /* with thanks to John Linderman */
+  s[n+m-1] = '\0';
+  setsval(y, s + m - 1);
+  s[n+m-1] = temp;
+  tempfree(x);
+  return(y);
+}
+
+Cell *sindex(Node **a, int nnn)    /* index(a[0], a[1]) */
+{
+  Cell *x, *y, *z;
+  char *s1, *s2, *p1, *p2, *q;
+  Awkfloat v = 0.0;
+
+  x = execute(a[0]);
+  s1 = getsval(x);
+  y = execute(a[1]);
+  s2 = getsval(y);
+
+  z = gettemp();
+  for (p1 = s1; *p1 != '\0'; p1++) {
+    for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++)
+      ;
+    if (*p2 == '\0') {
+      v = (Awkfloat) (p1 - s1 + 1);  /* origin 1 */
+      break;
+    }
+  }
+  tempfree(x);
+  tempfree(y);
+  setfval(z, v);
+  return(z);
+}
+
+int format(char **pbuf, int *pbufsize, const char *s, Node *a)  /* printf-like conversions */
+{
+  char *fmt;
+  char *p, *t;
+  const char *os;
+  Cell *x;
+  int flag = 0, n;
+  int fmtwd; /* format width */
+  int fmtsz = recsize;
+  char *buf = *pbuf;
+  int bufsize = *pbufsize;
+
+  os = s;
+  p = buf;
+  if ((fmt = (char *) malloc(fmtsz)) == NULL)
+    FATAL("out of memory in format()");
+  while (*s) {
+    adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format1");
+    if (*s != '%') {
+      *p++ = *s++;
+      continue;
+    }
+    if (*(s+1) == '%') {
+      *p++ = '%';
+      s += 2;
+      continue;
+    }
+    /* have to be real careful in case this is a huge number, eg, %100000d */
+    fmtwd = atoi(s+1);
+    if (fmtwd < 0) fmtwd = -fmtwd;
+    adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format2");
+    for (t = fmt; (*t++ = *s) != '\0'; s++) {
+      if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, "format3"))
+        FATAL("format item %.30s... ran format() out of memory", os);
+      if (isalpha((uschar)*s) && *s != 'l' && *s != 'h' && *s != 'L')
+        break;  /* the ansi panoply */
+      if (*s == '*') {
+        x = execute(a);
+        a = a->nnext;
+        sprintf(t-1, "%d", fmtwd=(int) getfval(x));
+        if (fmtwd < 0) fmtwd = -fmtwd;
+        adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+        t = fmt + strlen(fmt);
+        tempfree(x);
+      }
+    }
+    *t = '\0';
+    if (fmtwd < 0) fmtwd = -fmtwd;
+    adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format4");
+
+    switch (*s) {
+    case 'f': /*FALL_THROUGH*/
+    case 'e': /*FALL_THROUGH*/
+    case 'g': /*FALL_THROUGH*/
+    case 'E': /*FALL_THROUGH*/
+    case 'G':
+      flag = 'f';
+      break;
+    case 'd': /*FALL_THROUGH*/
+    case 'i':
+      flag = 'd';
+      if(*(s-1) == 'l') break;
+      *(t-1) = 'l';
+      *t = 'd';
+      *++t = '\0';
+      break;
+    case 'o': /*FALL_THROUGH*/
+    case 'x': /*FALL_THROUGH*/
+    case 'X': /*FALL_THROUGH*/
+    case 'u':
+      flag = *(s-1) == 'l' ? 'd' : 'u';
+      break;
+    case 's':
+      flag = 's';
+      break;
+    case 'c':
+      flag = 'c';
+      break;
+    default:
+      WARNING("weird printf conversion %s", fmt);
+      flag = '?';
+      break;
+    }
+    if (a == NULL) FATAL("not enough args in printf(%s)", os);
+    x = execute(a);
+    a = a->nnext;
+    n = MAXNUMSIZE;
+    if (fmtwd > n) n = fmtwd;
+    adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format5");
+    switch (flag) {
+    case '?':  sprintf(p, "%s", fmt);  /* unknown, so dump it too */
+      t = getsval(x);
+      n = strlen(t);
+      if (fmtwd > n) n = fmtwd;
+      adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format6");
+      p += strlen(p);
+      sprintf(p, "%s", t);
+      break;
+    case 'f':  sprintf(p, fmt, getfval(x)); break;
+    case 'd':  sprintf(p, fmt, (long) getfval(x)); break;
+    case 'u':  sprintf(p, fmt, (int) getfval(x)); break;
+    case 's':
+      t = getsval(x);
+      n = strlen(t);
+      if (fmtwd > n) n = fmtwd;
+      if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format7"))
+        FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t);
+      sprintf(p, fmt, t);
+      break;
+    case 'c':
+      if (isnum(x)) {
+        if (getfval(x)) sprintf(p, fmt, (int) getfval(x));
+        else {
+          *p++ = '\0'; /* explicit null byte */
+          *p = '\0';   /* next output will start here */
+        }
+      } else sprintf(p, fmt, getsval(x)[0]);
+      break;
+    default:
+      FATAL("can't happen: bad conversion %c in format()", flag);
+    }
+    tempfree(x);
+    p += strlen(p);
+    s++;
+  }
+  *p = '\0';
+  free(fmt);
+  for ( ; a; a = a->nnext) execute(a);  /* evaluate any remaining args */
+  *pbuf = buf;
+  *pbufsize = bufsize;
+  return p - buf;
+}
+
+Cell *awksprintf(Node **a, int n)    /* sprintf(a[0]) */
+{
+  Cell *x;
+  Node *y;
+  char *buf;
+  int bufsz=3*recsize;
+
+  if ((buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of memory in awksprintf");
+  y = a[0]->nnext;
+  x = execute(a[0]);
+  if (format(&buf, &bufsz, getsval(x), y) == -1)
+    FATAL("sprintf string %.30s... too long.  can't happen.", buf);
+  tempfree(x);
+  x = gettemp();
+  x->sval = buf;
+  x->tval = STR;
+  return(x);
+}
+
+Cell *awkprintf(Node **a, int n)    /* printf */
+{  /* a[0] is list of args, starting with format string */
+  /* a[1] is redirection operator, a[2] is redirection file */
+  FILE *fp;
+  Cell *x;
+  Node *y;
+  char *buf;
+  int len;
+  int bufsz=3*recsize;
+
+  if ((buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of memory in awkprintf");
+  y = a[0]->nnext;
+  x = execute(a[0]);
+  if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
+    FATAL("printf string %.30s... too long.  can't happen.", buf);
+  tempfree(x);
+  if (a[1] == NULL) {
+    /* fputs(buf, stdout); */
+    fwrite(buf, len, 1, stdout);
+    if (ferror(stdout)) FATAL("write error on stdout");
+  } else {
+    fp = redirect(ptoi(a[1]), a[2]);
+    /* fputs(buf, fp); */
+    fwrite(buf, len, 1, fp);
+    fflush(fp);
+    if (ferror(fp)) FATAL("write error on %s", filename(fp));
+  }
+  free(buf);
+  return(True);
+}
+
+Cell *arith(Node **a, int n)  /* a[0] + a[1], etc.  also -a[0] */
+{
+  Awkfloat i, j = 0;
+  double v;
+  Cell *x, *y, *z;
+
+  x = execute(a[0]);
+  i = getfval(x);
+  tempfree(x);
+  if (n != UMINUS) {
+    y = execute(a[1]);
+    j = getfval(y);
+    tempfree(y);
+  }
+  z = gettemp();
+  switch (n) {
+  case ADD:
+    i += j;
+    break;
+  case MINUS:
+    i -= j;
+    break;
+  case MULT:
+    i *= j;
+    break;
+  case DIVIDE:
+    if (j == 0) FATAL("division by zero");
+    i /= j;
+    break;
+  case MOD:
+    if (j == 0) FATAL("division by zero in mod");
+    modf(i/j, &v);
+    i = i - j * v;
+    break;
+  case UMINUS:
+    i = -i;
+    break;
+  case POWER:
+    if (j >= 0 && modf(j, &v) == 0.0) i = ipow(i, (int) j); /* pos integer exponent */
+    else i = errcheck(pow(i, j), "pow");
+    break;
+  default: FATAL("illegal arithmetic operator %d", n); /* can't happen */
+  }
+  setfval(z, i);
+  return(z);
+}
+
+double ipow(double x, int n)  /* x**n.  ought to be done by pow, but isn't always */
+{
+  double v;
+  if (n <= 0) return 1;
+  v = ipow(x, n/2);
+  if (n % 2 == 0) return v * v;
+  else return x * v * v;
+}
+
+Cell *incrdecr(Node **a, int n)    /* a[0]++, etc. */
+{
+  Cell *x, *z;
+  int k;
+  Awkfloat xf;
+
+  x = execute(a[0]);
+  xf = getfval(x);
+  k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
+  if (n == PREINCR || n == PREDECR) {
+    setfval(x, xf + k);
+    return(x);
+  }
+  z = gettemp();
+  setfval(z, xf);
+  setfval(x, xf + k);
+  tempfree(x);
+  return(z);
+}
+
+Cell *assign(Node **a, int n)  /* a[0] = a[1], a[0] += a[1], etc. */
+{    /* this is subtle; don't muck with it. */
+  Cell *x, *y;
+  Awkfloat xf, yf;
+  double v;
+
+  y = execute(a[1]);
+  x = execute(a[0]);
+  if (n == ASSIGN) {  /* ordinary assignment */
+    if (x == y && !(x->tval & (FLD|REC)))  /* self-assignment: */
+      ;    /* leave alone unless it's a field */
+    else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+      setsval(x, getsval(y));
+      x->fval = getfval(y);
+      x->tval |= NUM;
+      if(x == errloc) errno = x->fval;
+    }
+    else if (isstr(y)) setsval(x, getsval(y));
+    else if (isnum(y)) setfval(x, getfval(y));
+    else funnyvar(y, "read value of");
+    tempfree(y);
+    return(x);
+  }
+  xf = getfval(x);
+  yf = getfval(y);
+  switch (n) {
+  case ADDEQ:
+    xf += yf;
+    break;
+  case SUBEQ:
+    xf -= yf;
+    break;
+  case MULTEQ:
+    xf *= yf;
+    break;
+  case DIVEQ:
+    if (yf == 0) FATAL("division by zero in /=");
+    xf /= yf;
+    break;
+  case MODEQ:
+    if (yf == 0) FATAL("division by zero in %%=");
+    modf(xf/yf, &v);
+    xf = xf - yf * v;
+    break;
+  case POWEQ:
+    if (yf >= 0 && modf(yf, &v) == 0.0) xf = ipow(xf, (int) yf); /* pos integer exponent */
+    else xf = errcheck(pow(xf, yf), "pow");
+    break;
+  default:
+    FATAL("illegal assignment operator %d", n);
+    break;
+  }
+  tempfree(y);
+  setfval(x, xf);
+  return(x);
+}
+
+Cell *cat(Node **a, int q)  /* a[0] cat a[1] */
+{
+  Cell *x, *y, *z;
+  int n1, n2;
+  char *s;
+
+  x = execute(a[0]);
+  y = execute(a[1]);
+  getsval(x);
+  getsval(y);
+  n1 = strlen(x->sval);
+  n2 = strlen(y->sval);
+  s = (char *) malloc(n1 + n2 + 1);
+  if (s == NULL)
+    FATAL("out of space concatenating %.15s... and %.15s...",
+      x->sval, y->sval);
+  strcpy(s, x->sval);
+  strcpy(s+n1, y->sval);
+  tempfree(x);
+  tempfree(y);
+  z = gettemp();
+  z->sval = s;
+  z->tval = STR;
+  return(z);
+}
+
+Cell *pastat(Node **a, int n)  /* a[0] { a[1] } */
+{
+  Cell *x;
+
+  if (a[0] == 0) x = execute(a[1]);
+  else {
+    x = execute(a[0]);
+    if (istrue(x)) {
+      tempfree(x);
+      x = execute(a[1]);
+    }
+  }
+  return x;
+}
+
+Cell *dopa2(Node **a, int n)  /* a[0], a[1] { a[2] } */
+{
+  Cell *x;
+  int pair;
+
+  pair = ptoi(a[3]);
+  if (pairstack[pair] == 0) {
+    x = execute(a[0]);
+    if (istrue(x)) pairstack[pair] = 1;
+    tempfree(x);
+  }
+  if (pairstack[pair] == 1) {
+    x = execute(a[1]);
+    if (istrue(x)) pairstack[pair] = 0;
+    tempfree(x);
+    x = execute(a[2]);
+    return(x);
+  }
+  return(False);
+}
+
+Cell *split(Node **a, int nnn)  /* split(a[0], a[1], a[2]); a[3] is type */
+{
+  Cell *x = 0, *y, *ap;
+  char *s;
+  int sep;
+  char *t, temp, num[50], *fs = 0;
+  int n, tempstat, arg3type;
+
+  y = execute(a[0]);  /* source string */
+  s = getsval(y);
+  arg3type = ptoi(a[3]);
+  if (a[2] == 0) fs = *FS; /* fs string */
+  else if (arg3type == STRING) {  /* split(str,arr,"string") */
+    x = execute(a[2]);
+    fs = getsval(x);
+  } else if (arg3type == REGEXPR) fs = "(regexpr)";  /* split(str,arr,/regexpr/) */
+  else FATAL("illegal type of split");
+  sep = *fs;
+  ap = execute(a[1]);  /* array name */
+  freesymtab(ap);
+  dprintf( ("split: s=|%s|, a=%s, sep=|%s|\n", s, NN(ap->nval), fs) );
+  ap->tval &= ~STR;
+  ap->tval |= ARR;
+  ap->sval = (char *) makesymtab(NSYMTAB);
+
+  n = 0;
+  if (arg3type == REGEXPR && strlen((char*) ((fa*) a[2])->restr) == 0) {
+    /* split(s, a, //); have to arrange that it looks like empty sep */
+    arg3type = 0;
+    fs = "";
+    sep = 0;
+  }
+  if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) {  /* reg expr */
+    fa *pfa;
+    if (arg3type == REGEXPR) pfa = (fa *) a[2];  /* it's ready already */
+    else pfa = makedfa(fs, 1);
+    if (nematch(pfa,s)) {
+      tempstat = pfa->initstat;
+      pfa->initstat = 2;
+      do {
+        n++;
+        sprintf(num, "%d", n);
+        temp = *patbeg;
+        *patbeg = '\0';
+        if (is_number(s)) setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval);
+        else setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
+        *patbeg = temp;
+        s = patbeg + patlen;
+        if (*(patbeg+patlen-1) == 0 || *s == 0) {
+          n++;
+          sprintf(num, "%d", n);
+          setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
+          pfa->initstat = tempstat;
+          goto spdone;
+        }
+      } while (nematch(pfa,s));
+      pfa->initstat = tempstat;   /* bwk: has to be here to reset */
+              /* cf gsub and refldbld */
+    }
+    n++;
+    sprintf(num, "%d", n);
+    if (is_number(s)) setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval);
+    else setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
+  spdone:
+    pfa = NULL;
+  } else if (sep == ' ') {
+    for (n = 0; ; ) {
+      while (*s == ' ' || *s == '\t' || *s == '\n') s++;
+      if (*s == 0) break;
+      n++;
+      t = s;
+      do s++;
+      while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0');
+      temp = *s;
+      *s = '\0';
+      sprintf(num, "%d", n);
+      if (is_number(t)) setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+      else setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+      *s = temp;
+      if (*s != 0) s++;
+    }
+  } else if (sep == 0) {  /* new: split(s, a, "") => 1 char/elem */
+    for (n = 0; *s != 0; s++) {
+      char buf[2];
+      n++;
+      sprintf(num, "%d", n);
+      buf[0] = *s;
+      buf[1] = 0;
+      if (isdigit((uschar)buf[0])) setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval);
+      else setsymtab(num, buf, 0.0, STR, (Array *) ap->sval);
+    }
+  } else if (*s != 0) {
+    for (;;) {
+      n++;
+      t = s;
+      while (*s != sep && *s != '\n' && *s != '\0')s++;
+      temp = *s;
+      *s = '\0';
+      sprintf(num, "%d", n);
+      if (is_number(t)) setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+      else setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+      *s = temp;
+      if (*s++ == 0) break;
+    }
+  }
+  tempfree(ap);
+  tempfree(y);
+  if (a[2] != 0 && arg3type == STRING) { tempfree(x); }
+  x = gettemp();
+  x->tval = NUM;
+  x->fval = n;
+  return(x);
+}
+
+Cell *condexpr(Node **a, int n)  /* a[0] ? a[1] : a[2] */
+{
+  Cell *x;
+
+  x = execute(a[0]);
+  if (istrue(x)) {
+    tempfree(x);
+    x = execute(a[1]);
+  } else {
+    tempfree(x);
+    x = execute(a[2]);
+  }
+  return(x);
+}
+
+Cell *ifstat(Node **a, int n)  /* if (a[0]) a[1]; else a[2] */
+{
+  Cell *x;
+
+  x = execute(a[0]);
+  if (istrue(x)) {
+    tempfree(x);
+    x = execute(a[1]);
+  } else if (a[2] != 0) {
+    tempfree(x);
+    x = execute(a[2]);
+  }
+  return(x);
+}
+
+Cell *whilestat(Node **a, int n)  /* while (a[0]) a[1] */
+{
+  Cell *x;
+
+  for (;;) {
+    x = execute(a[0]);
+    if (!istrue(x)) return(x);
+    tempfree(x);
+    x = execute(a[1]);
+    if (isbreak(x)) {
+      x = True;
+      return(x);
+    }
+    if (isnext(x) || isexit(x) || isret(x)) return(x);
+    tempfree(x);
+  }
+}
+
+Cell *dostat(Node **a, int n)  /* do a[0]; while(a[1]) */
+{
+  Cell *x;
+
+  for (;;) {
+    x = execute(a[0]);
+    if (isbreak(x)) return True;
+    if (isnext(x) || isexit(x) || isret(x)) return(x);
+    tempfree(x);
+    x = execute(a[1]);
+    if (!istrue(x)) return(x);
+    tempfree(x);
+  }
+}
+
+Cell *forstat(Node **a, int n)  /* for (a[0]; a[1]; a[2]) a[3] */
+{
+  Cell *x;
+
+  x = execute(a[0]);
+  tempfree(x);
+  for (;;) {
+    if (a[1]!=0) {
+      x = execute(a[1]);
+      if (!istrue(x)) return(x);
+      else tempfree(x);
+    }
+    x = execute(a[3]);
+    if (isbreak(x)) return True;     /* turn off break */
+    if (isnext(x) || isexit(x) || isret(x)) return(x);
+    tempfree(x);
+    x = execute(a[2]);
+    tempfree(x);
+  }
+}
+
+Cell *instat(Node **a, int n)  /* for (a[0] in a[1]) a[2] */
+{
+  Cell *x, *vp, *arrayp, *cp, *ncp;
+  Array *tp;
+  int i;
+
+  vp = execute(a[0]);
+  arrayp = execute(a[1]);
+  if (!isarr(arrayp)) return True;
+  tp = (Array *) arrayp->sval;
+  tempfree(arrayp);
+  for (i = 0; i < tp->size; i++) {  /* this routine knows too much */
+    for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
+      setsval(vp, cp->nval);
+      ncp = cp->cnext;
+      x = execute(a[2]);
+      if (isbreak(x)) {
+        tempfree(vp);
+        return True;
+      }
+      if (isnext(x) || isexit(x) || isret(x)) {
+        tempfree(vp);
+        return(x);
+      }
+      tempfree(x);
+    }
+  }
+  return True;
+}
+
+Cell *bltin(Node **a, int n)  /* builtin functions. a[0] is type, a[1] is arg list */
+{
+  Cell *x, *y;
+  Awkfloat u;
+  int t;
+  Awkfloat tmp;
+  char *p, *buf;
+  Node *nextarg;
+  FILE *fp;
+  struct tm stm;
+  void flush_all(void);
+
+  t = ptoi(a[0]);
+  x = execute(a[1]);
+  nextarg = a[1]->nnext;
+  switch (t) {
+  case FLENGTH:
+    if (isarr(x)) u = ((Array *) x->sval)->nelem;  /* GROT.  should be function*/
+    else u = strlen(getsval(x));
+    break;
+  case FLOG:
+    u = errcheck(log(getfval(x)), "log"); break;
+  case FINT:
+    modf(getfval(x), &u); break;
+  case FEXP:
+    u = errcheck(exp(getfval(x)), "exp"); break;
+  case FSQRT:
+    u = errcheck(sqrt(getfval(x)), "sqrt"); break;
+  case FSIN:
+    u = sin(getfval(x)); break;
+  case FCOS:
+    u = cos(getfval(x)); break;
+  case FATAN:
+    if (nextarg == 0) {
+      WARNING("atan2 requires two arguments; returning 1.0");
+      u = 1.0;
+    } else {
+      y = execute(a[1]->nnext);
+      u = atan2(getfval(x), getfval(y));
+      tempfree(y);
+      nextarg = nextarg->nnext;
+    }
+    break;
+  case FSYSTEM:
+    fflush(stdout);    /* in case something is buffered already */
+    u = (Awkfloat) system(getsval(x)) / 256;   /* 256 is unix-dep */
+    break;
+  case FRAND: /* in principle, rand() returns something in 0..RAND_MAX */
+    u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX;
+    break;
+  case FSRAND:
+    if (isrec(x)) u = time((time_t *)0);   /* no argument provided */
+    else u = getfval(x);
+    tmp = u;
+    srand((unsigned int) u);
+    u = srand_seed;
+    srand_seed = tmp;
+    break;
+  case FTOUPPER: /*FALL_THROUGH*/
+  case FTOLOWER:
+    buf = tostring(getsval(x));
+    if (t == FTOUPPER) {
+      for (p = buf; *p; p++)
+        if (islower((uschar) *p)) *p = toupper((uschar)*p);
+    } else {
+      for (p = buf; *p; p++)
+        if (isupper((uschar) *p)) *p = tolower((uschar)*p);
+    }
+    tempfree(x);
+    x = gettemp();
+    setsval(x, buf);
+    free(buf);
+    return x;
+  case FFLUSH:
+    if (isrec(x) || strlen(getsval(x)) == 0) {
+      flush_all();  /* fflush() or fflush("") -> all */
+      u = 0;
+    } else if ((fp = openfile(FFLUSH, getsval(x))) == NULL) u = EOF;
+    else u = fflush(fp);
+    break;
+  case FMKTIME:
+    sscanf(getsval(x),"%d %d %d %d %d %d %d", &stm.tm_year, &stm.tm_mon, &stm.tm_mday, &stm.tm_hour, &stm.tm_min, &stm.tm_sec, &stm.tm_isdst);
+    stm.tm_year -= 1900;
+    stm.tm_mon--;
+    u = mktime(&stm);
+    break;
+  case FSYSTIME:
+    u = time(NULL);
+    break;
+  case FSTRFTIME:
+    {
+      char outstr[200];
+      time_t t;
+      struct tm *tmp;
+
+      if(nextarg == NULL) t = time(NULL);
+      else t = getfval(execute(nextarg));
+
+      tmp = localtime(&t);
+      if (tmp == NULL) {
+        perror("localtime");
+        exit(EXIT_FAILURE);
+      }
+      strftime(outstr, sizeof(outstr), getsval(x), tmp);
+      tempfree(x);
+      x = gettemp();
+      setsval(x, outstr);
+      return(x);
+    }
+    break;
+  default:  /* can't happen */
+    FATAL("illegal function type %d", t);
+    break;
+  }
+  tempfree(x);
+  x = gettemp();
+  setfval(x, u);
+  if (nextarg != 0) {
+    WARNING("warning: function has too many arguments");
+    for ( ; nextarg; nextarg = nextarg->nnext) execute(nextarg);
+  }
+  return(x);
+}
+
+Cell *printstat(Node **a, int n)  /* print a[0] */
+{
+  Node *x;
+  Cell *y;
+  FILE *fp;
+
+  if (a[1] == 0) fp = stdout;  /* a[1] is redirection operator, a[2] is file */
+  else fp = redirect(ptoi(a[1]), a[2]);
+  for (x = a[0]; x != NULL; x = x->nnext) {
+    y = execute(x);
+    fputs(getpssval(y), fp);
+    tempfree(y);
+    if (x->nnext == NULL) fputs(*ORS, fp);
+    else fputs(*OFS, fp);
+  }
+  if (a[1] != 0) fflush(fp);
+  if (ferror(fp)) FATAL("write error on %s", filename(fp));
+  return(True);
+}
+
+Cell *nullproc(Node **a, int n)
+{
+  return 0;
+}
+
+FILE *redirect(int a, Node *b)  /* set up all i/o redirections */
+{
+  FILE *fp;
+  Cell *x;
+  char *fname;
+
+  x = execute(b);
+  fname = getsval(x);
+  fp = openfile(a, fname);
+  if (fp == NULL) FATAL("can't open file %s", fname);
+  tempfree(x);
+  return fp;
+}
+
+void stdinit(void)  /* in case stdin, etc., are not constants */
+{
+  nfiles = FOPEN_MAX;
+  files = calloc(nfiles, sizeof(*files));
+  if (files == NULL) FATAL("can't allocate file memory for %u files", nfiles);
+  files[0].fp = stdin;
+  files[0].fname = "/dev/stdin";
+  files[0].mode = LT;
+  files[1].fp = stdout;
+  files[1].fname = "/dev/stdout";
+  files[1].mode = GT;
+  files[2].fp = stderr;
+  files[2].fname = "/dev/stderr";
+  files[2].mode = GT;
+}
+
+FILE *openfile(int a, const char *us)
+{
+  const char *s = us;
+  int i, m;
+  FILE *fp = 0;
+
+  if (*s == '\0') FATAL("null file name in print or getline");
+  for (i=0; i < nfiles; i++)
+    if (files[i].fname && strcmp(s, files[i].fname) == 0) {
+      if (a == files[i].mode || (a==APPEND && files[i].mode==GT)) return files[i].fp;
+      if (a == FFLUSH) return files[i].fp;
+    }
+  if (a == FFLUSH) return NULL;  /* didn't find it, so don't create it! */
+
+  for (i=0; i < nfiles; i++) if (files[i].fp == 0) break;
+  if (i >= nfiles) {
+    struct files *nf;
+    int nnf = nfiles + FOPEN_MAX;
+    nf = realloc(files, nnf * sizeof(*nf));
+    if (nf == NULL) FATAL("cannot grow files for %s and %d files", s, nnf);
+    memset(&nf[nfiles], 0, FOPEN_MAX * sizeof(*nf));
+    nfiles = nnf;
+    files = nf;
+  }
+  fflush(stdout);  /* force a semblance of order */
+  m = a;
+  if (a == GT) fp = fopen(s, "w");
+  else if (a == APPEND) {
+    fp = fopen(s, "a");
+    m = GT;  /* so can mix > and >> */
+  } else if (a == '|') fp = popen(s, "w");
+  else if (a == LE) fp = popen(s, "r"); /* input pipe */
+  else if (a == LT) fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r");  /* "-" is stdin getline <file */
+  else FATAL("illegal redirection %d", a); /* can't happen */
+  if (fp != NULL) {
+    files[i].fname = tostring(s);
+    files[i].fp = fp;
+    files[i].mode = m;
+  }
+  return fp;
+}
+
+const char *filename(FILE *fp)
+{
+  int i;
+
+  for (i = 0; i < nfiles; i++) if (fp == files[i].fp) return files[i].fname;
+  return "???";
+}
+
+Cell *closefile(Node **a, int n)
+{
+  Cell *x;
+  int i, stat;
+
+  x = execute(a[0]);
+  getsval(x);
+  stat = -1;
+  for (i = 0; i < nfiles; i++) {
+    if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) {
+      if (ferror(files[i].fp))
+        WARNING( "i/o error occurred on %s", files[i].fname );
+      if (files[i].mode == '|' || files[i].mode == LE) {
+        stat = pclose(files[i].fp);
+        files[i].fp = NULL;
+      }
+      else {
+        stat = fclose(files[i].fp);
+        files[i].fp = NULL;
+      }
+      if (stat == EOF)
+        WARNING( "i/o error occurred closing %s", files[i].fname );
+      if (i > 2) xfree(files[i].fname); /* don't do /dev/std... */
+      files[i].fname = NULL;  /* watch out for ref thru this */
+      files[i].fp = NULL;
+    }
+  }
+  tempfree(x);
+  x = gettemp();
+  setfval(x, (Awkfloat) stat);
+  return(x);
+}
+
+void closeall(void)
+{
+  int i, stat;
+
+  for (i = 0; i < FOPEN_MAX; i++) {
+    if (files[i].fp) {
+      if (ferror(files[i].fp))
+        WARNING( "i/o error occurred on %s", files[i].fname );
+      if (files[i].mode == '|' || files[i].mode == LE) {
+        stat = pclose(files[i].fp);
+        files[i].fp = NULL;
+      }
+      else {
+        stat = fclose(files[i].fp);
+        files[i].fp = NULL;
+      }
+      if (stat == EOF)
+        WARNING( "i/o error occurred while closing %s", files[i].fname );
+    }
+  }
+}
+
+void flush_all(void)
+{
+  int i;
+  for (i = 0; i < nfiles; i++)
+    if (files[i].fp) fflush(files[i].fp);
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr);
+
+Cell *sub(Node **a, int nnn)  /* substitute command */
+{
+  char *sptr, *pb, *q;
+  Cell *x, *y, *result;
+  char *t, *buf;
+  fa *pfa;
+  int bufsz = recsize;
+
+  if ((buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of memory in sub");
+  x = execute(a[3]);  /* target string */
+  t = getsval(x);
+  if (a[0] == 0)    /* 0 => a[1] is already-compiled regexpr */
+    pfa = (fa *) a[1];  /* regular expression */
+  else {
+    y = execute(a[1]);
+    pfa = makedfa(getsval(y), 1);
+    tempfree(y);
+  }
+  y = execute(a[2]);  /* replacement string */
+  result = False;
+  if (pmatch(pfa, t)) {
+    sptr = t;
+    adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub");
+    pb = buf;
+    while (sptr < patbeg) *pb++ = *sptr++;
+    sptr = getsval(y);
+    while (*sptr != 0) {
+      adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub");
+      if (*sptr == '\\') backsub(&pb, &sptr);
+      else if (*sptr == '&') {
+        char *counter;
+        sptr++;
+        adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub");
+        counter = patbeg+patlen;
+        for (q = patbeg; q < counter; ) *pb++ = *q++;
+      } else *pb++ = *sptr++;
+    }
+    *pb = '\0';
+    if (pb > buf + bufsz)
+      FATAL("sub result1 %.30s too big; can't happen", buf);
+    sptr = patbeg + patlen;
+    if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
+      adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub");
+      while ((*pb++ = *sptr++) != 0)
+        ;
+    }
+    if (pb > buf + bufsz)
+      FATAL("sub result2 %.30s too big; can't happen", buf);
+    setsval(x, buf);  /* BUG: should be able to avoid copy */
+    result = True;;
+  }
+  tempfree(x);
+  tempfree(y);
+  free(buf);
+  return result;
+}
+
+Cell *gsub(Node **a, int nnn)  /* global substitute */
+{
+  Cell *x, *y;
+  char *rptr, *sptr, *t, *pb, *q;
+  char *buf;
+  fa *pfa;
+  int mflag, tempstat, num;
+  int bufsz = recsize;
+
+  if ((buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of memory in gsub");
+  mflag = 0;  /* if mflag == 0, can replace empty string */
+  num = 0;
+  x = execute(a[3]);  /* target string */
+  t = getsval(x);
+  if (a[0] == 0)    /* 0 => a[1] is already-compiled regexpr */
+    pfa = (fa *) a[1];  /* regular expression */
+  else {
+    y = execute(a[1]);
+    pfa = makedfa(getsval(y), 1);
+    tempfree(y);
+  }
+  y = execute(a[2]);  /* replacement string */
+  if (pmatch(pfa, t)) {
+    tempstat = pfa->initstat;
+    pfa->initstat = 2;
+    pb = buf;
+    rptr = getsval(y);
+    do {
+      if (patlen == 0 && *patbeg != 0) {  /* matched empty string */
+        if (mflag == 0) {  /* can replace empty */
+          num++;
+          sptr = rptr;
+          while (*sptr != 0) {
+            adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+            if (*sptr == '\\') backsub(&pb, &sptr);
+            else if (*sptr == '&') {
+              char *counter;
+              sptr++;
+              adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+              counter = patbeg+patlen;
+              for (q = patbeg; q < counter; ) *pb++ = *q++;
+            } else *pb++ = *sptr++;
+          }
+        }
+        if (*t == 0) goto done;   /* at end */
+        adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub");
+        *pb++ = *t++;
+        if (pb > buf + bufsz)  /* BUG: not sure of this test */
+          FATAL("gsub result0 %.30s too big; can't happen", buf);
+        mflag = 0;
+      }
+      else {  /* matched nonempty string */
+        num++;
+        sptr = t;
+        adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub");
+        while (sptr < patbeg) *pb++ = *sptr++;
+        sptr = rptr;
+        while (*sptr != 0) {
+          adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+          if (*sptr == '\\') backsub(&pb, &sptr);
+          else if (*sptr == '&') {
+            char *counter; 
+            sptr++;
+            adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+            counter = patbeg+patlen; 
+            for (q = patbeg; q < counter; ) *pb++ = *q++;
+          } else *pb++ = *sptr++;
+        }
+        t = patbeg + patlen;
+        if (patlen == 0 || *t == 0 || *(t-1) == 0) goto done;
+        if (pb > buf + bufsz)
+          FATAL("gsub result1 %.30s too big; can't happen", buf);
+        mflag = 1;
+      }
+    } while (pmatch(pfa,t));
+    sptr = t;
+    adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub");
+    while ((*pb++ = *sptr++) != 0)
+      ;
+  done:  if (pb < buf + bufsz) *pb = '\0';
+    else if (*(pb-1) != '\0')
+      FATAL("gsub result2 %.30s truncated; can't happen", buf);
+    setsval(x, buf);  /* BUG: should be able to avoid copy + free */
+    pfa->initstat = tempstat;
+  }
+  tempfree(x);
+  tempfree(y);
+  x = gettemp();
+  x->tval = NUM;
+  x->fval = num;
+  free(buf);
+  return(x);
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr)  /* handle \\& variations */
+{            /* sptr[0] == '\\' */
+  char *pb = *pb_ptr, *sptr = *sptr_ptr;
+
+  if (sptr[1] == '\\') {
+    if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
+      *pb++ = '\\';
+      *pb++ = '&';
+      sptr += 4;
+    } else if (sptr[2] == '&') {  /* \\& -> \ + matched */
+      *pb++ = '\\';
+      sptr += 2;
+    } else {      /* \\x -> \\x */
+      *pb++ = *sptr++;
+      *pb++ = *sptr++;
+    }
+  } else if (sptr[1] == '&') {  /* literal & */
+    sptr++;
+    *pb++ = *sptr++;
+  } else        /* literal \ */
+    *pb++ = *sptr++;
+
+  *pb_ptr = pb;
+  *sptr_ptr = sptr;
+}
+
+static int awk_peek(void)
+{
+  int c = input();
+  unput(c);
+  return c;
+}
+
+int gettok(char **pbuf, int *psz)  /* get next input token */
+{
+  int c, retc;
+  char *buf = *pbuf;
+  int sz = *psz;
+  char *bp = buf;
+
+  c = input();
+  if (c == 0) return 0;
+  buf[0] = c;
+  buf[1] = 0;
+  if (!isalnum(c) && c != '.' && c != '_') return c;
+
+  *bp++ = c;
+  if (isalpha(c) || c == '_') {  /* it's a varname */
+    for ( ; (c = input()) != 0; ) {
+      if (bp-buf >= sz)
+        if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
+          FATAL( "out of space for name %.10s...", buf );
+      if (isalnum(c) || c == '_') *bp++ = c;
+      else {
+        *bp = 0;
+        unput(c);
+        break;
+      }
+    }
+    *bp = 0;
+    retc = 'a';  /* alphanumeric */
+  } else {  /* maybe it's a number, but could be . */
+    char *rem;
+    double unused __attribute__((unused));
+    /* read input until can't be a number */
+    for ( ; (c = input()) != 0; ) {
+      if (bp-buf >= sz)
+        if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
+          FATAL( "out of space for number %.10s...", buf );
+      if (isdigit(c) || c == 'e' || c == 'E'
+        || c == '.' || c == '+' || c == '-'
+        || c == 'x' || c == 'X' || ((c >= 'a' && c<= 'f') || (c >= 'A' && c<= 'F')))
+        *bp++ = c;
+      else {
+        unput(c);
+        break;
+      }
+    }
+    *bp = 0;
+    unused = strtod(buf, &rem);  /* parse the number */
+    if (rem == buf) {  /* it wasn't a valid number at all */
+      buf[1] = 0;  /* return one character as token */
+      retc = buf[0];  /* character is its own type */
+      unputstr(rem+1); /* put rest back for later */
+    } else {  /* some prefix was a number */
+      unputstr(rem);  /* put rest back for later */
+      rem[0] = 0;  /* truncate buf after number part */
+      retc = '0';  /* type is number */
+    }
+  }
+  *pbuf = buf;
+  *psz = sz;
+  return retc;
+}
+
+Awkfloat str_to_double(char *s)
+{
+  if(*s == '0' && isdigit(s[1])) {
+    int i, j, k, c, fra, flag, rem;
+    Awkfloat rem1 = 0.0;
+    int a[20], b[20];
+
+    c = fra = flag = rem = 0;
+
+    for(i=0,j=0,k=0;i<strlen(s);i++)
+    {
+      if(s[i]=='.') flag=1;
+      else if(flag==0) a[j++]=s[i]-48;
+      else if(flag==1) b[k++]=s[i]-48;
+    }
+    c=j;
+    fra=k;
+    for(j=0,i=c-1;j<c;j++,i--)
+      rem = rem +(a[j] * pow(8,i));
+    for(k=0,i=1;k<fra;k++,i++)
+      rem1 = rem1 +(b[k] / pow(8,i));
+    rem1=rem+rem1;
+    return rem1;
+  }
+  else return atof(s);
+}
+
+int yylex(void)
+{
+  int c;
+  static char *buf = 0;
+  static int bufsize = 5; /* BUG: setting this small causes core dump! */
+
+  if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL)
+    FATAL( "out of space in yylex" );
+  if (sc) {
+    sc = 0;
+    RET('}');
+  }
+  if (reg) {
+    reg = 0;
+    return regexpr();
+  }
+  for (;;) {
+    c = gettok(&buf, &bufsize);
+    if (c == 0) return 0;
+    if (isalpha(c) || c == '_') return word(buf);
+    if (isdigit(c)) {
+      yylval.cp = setsymtab(buf, tostring(buf), str_to_double(buf), CON|NUM, symtab);
+      /* should this also have STR set? */
+      RET(NUMBER);
+    }
+
+    yylval.i = c;
+    switch (c) {
+    case '\n':  RET(NL); /* {EOL} */
+    case '\r':  /* assume \n is coming */ /*FALL_THROUGH*/
+    case ' ':  /* {WS}+ */ /*FALL_THROUGH*/
+    case '\t': break;
+    case '#':  /* #.* strip comments */
+      while ((c = input()) != '\n' && c != 0)
+        ;
+      unput(c);
+      break;
+    case ';': RET(';');
+    case '\\':
+      if (awk_peek() == '\n') input();
+      else if (awk_peek() == '\r') {
+        input(); input();  /* \n */
+        lineno++;
+      } else RET(c);
+      break;
+    case '&':
+      if (awk_peek() == '&') {
+        input(); RET(AND);
+      } else RET('&');
+    case '|':
+      if (awk_peek() == '|') {
+        input(); RET(BOR);
+      } else RET('|');
+    case '!':
+      if (awk_peek() == '=') {
+        input(); yylval.i = NE; RET(NE);
+      } else if (awk_peek() == '~') {
+        input(); yylval.i = NOTMATCH; RET(MATCHOP);
+      } else RET(NOT);
+    case '~':
+      yylval.i = MATCH;
+      RET(MATCHOP);
+    case '<':
+      if (awk_peek() == '=') {
+        input(); yylval.i = LE; RET(LE);
+      } else {
+        yylval.i = LT; RET(LT);
+      }
+    case '=':
+      if (awk_peek() == '=') {
+        input(); yylval.i = EQ; RET(EQ);
+      } else {
+        yylval.i = ASSIGN; RET(ASGNOP);
+      }
+    case '>':
+      if (awk_peek() == '=') {
+        input(); yylval.i = GE; RET(GE);
+      } else if (awk_peek() == '>') {
+        input(); yylval.i = APPEND; RET(APPEND);
+      } else {
+        yylval.i = GT; RET(GT);
+      }
+    case '+':
+      if (awk_peek() == '+') {
+        input(); yylval.i = INCR; RET(INCR);
+      } else if (awk_peek() == '=') {
+        input(); yylval.i = ADDEQ; RET(ASGNOP);
+      } else
+        RET('+');
+    case '-':
+      if (awk_peek() == '-') {
+        input(); yylval.i = DECR; RET(DECR);
+      } else if (awk_peek() == '=') {
+        input(); yylval.i = SUBEQ; RET(ASGNOP);
+      } else
+        RET('-');
+    case '*':
+      if (awk_peek() == '=') {  /* *= */
+        input(); yylval.i = MULTEQ; RET(ASGNOP);
+      } else if (awk_peek() == '*') {  /* ** or **= */
+        input();  /* eat 2nd * */
+        if (awk_peek() == '=') {
+          input(); yylval.i = POWEQ; RET(ASGNOP);
+        } else {
+          RET(POWER);
+        }
+      } else
+        RET('*');
+    case '/':
+      RET('/');
+    case '%':
+      if (awk_peek() == '=') {
+        input(); yylval.i = MODEQ; RET(ASGNOP);
+      } else
+        RET('%');
+    case '^':
+      if (awk_peek() == '=') {
+        input(); yylval.i = POWEQ; RET(ASGNOP);
+      } else
+        RET(POWER);
+
+    case '$':/* BUG: awkward, if not wrong */
+      c = gettok(&buf, &bufsize);
+      if (isalpha(c)) {
+        if (strcmp(buf, "NF") == 0) {  /* very special */
+          unputstr("(NF)");
+          RET(INDIRECT);
+        }
+        c = awk_peek();
+        if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
+          unputstr(buf);
+          RET(INDIRECT);
+        }
+        yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab);
+        RET(IVAR);
+      } else if (c == 0) {  /*  */
+        SYNTAX( "unexpected end of input after $" );
+        RET(';');
+      } else {
+        unputstr(buf);
+        RET(INDIRECT);
+      }
+    case '}':
+      if (--bracecnt < 0) SYNTAX( "extra }" );
+      sc = 1;
+      RET(';');
+    case ']':
+      if (--brackcnt < 0) SYNTAX( "extra ]" );
+      RET(']');
+    case ')':
+      if (--parencnt < 0) SYNTAX( "extra )" );
+      RET(')');
+    case '{':
+      bracecnt++;
+      RET('{');
+    case '[':
+      brackcnt++;
+      RET('[');
+    case '(':
+      parencnt++;
+      RET('(');
+    case '"':
+      return string();  /* BUG: should be like tran.c ? */
+
+    default:
+      RET(c);
+    }
+  }
+}
+
+int string(void)
+{
+  int c, n;
+  char *s, *bp;
+  static char *buf = 0;
+  static int bufsz = 500;
+
+  if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of space for strings");
+  for (bp = buf; (c = input()) != '"'; ) {
+    if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string"))
+      FATAL("out of space for string %.10s...", buf);
+    switch (c) {
+      case '\n': /*FALL_THROUGH*/
+      case '\r': /*FALL_THROUGH*/
+      case 0:
+        SYNTAX( "non-terminated string %.10s...", buf );
+        lineno++;
+        if (c == 0)  FATAL( "giving up" );
+        break;
+      case '\\':
+        c = input();
+        switch (c) {
+          case '"': *bp++ = '"'; break;
+          case 'n': *bp++ = '\n'; break;
+          case 't': *bp++ = '\t'; break;
+          case 'f': *bp++ = '\f'; break;
+          case 'r': *bp++ = '\r'; break;
+          case 'b': *bp++ = '\b'; break;
+          case 'v': *bp++ = '\v'; break;
+          case 'a': *bp++ = '\007'; break;
+          case '\\': *bp++ = '\\'; break;
+
+           
+          case '0': /*FALL_THROUGH*/
+          case '1': /*FALL_THROUGH*/
+          case '2': /*FALL_THROUGH*/ /* octal: \d \dd \ddd */
+          case '3': /*FALL_THROUGH*/
+          case '4': /*FALL_THROUGH*/
+          case '5': /*FALL_THROUGH*/
+          case '6': /*FALL_THROUGH*/
+          case '7':
+                 n = c - '0';
+                 if ((c = awk_peek()) >= '0' && c < '8') {
+                   n = 8 * n + input() - '0';
+                   if ((c = awk_peek()) >= '0' && c < '8')
+                     n = 8 * n + input() - '0';
+                 }
+                 *bp++ = n;
+                 break;
+
+          case 'x':  /* hex  \x0-9a-fA-F + */
+                 {
+                   char xbuf[100], *px;
+                   for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) {
+                     if (isdigit(c)
+                         || (c >= 'a' && c <= 'f')
+                         || (c >= 'A' && c <= 'F'))
+                       *px++ = c;
+                     else break;
+                   }
+                   *px = 0;
+                   unput(c);
+                   sscanf(xbuf, "%x", (unsigned int *) &n);
+                   *bp++ = n;
+                   break;
+                 }
+
+          default:
+                 *bp++ = c;
+                 break;
+        }
+        break;
+      default:
+        *bp++ = c;
+        break;
+    }
+  }
+  *bp = 0;
+  s = tostring(buf);
+  *bp++ = ' ';
+  *bp++ = 0;
+  yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
+  RET(STRING);
+}
+
+
+int binsearch(char *w, Keyword *kp, int n)
+{
+  int cond, low, mid, high;
+
+  low = 0;
+  high = n - 1;
+  while (low <= high) {
+    mid = (low + high) / 2;
+    if ((cond = strcmp(w, kp[mid].word)) < 0)
+      high = mid - 1;
+    else if (cond > 0) low = mid + 1;
+    else return mid;
+  }
+  return -1;
+}
+
+int word(char *w)
+{
+  Keyword *kp;
+  int c, n;
+
+  n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
+  /* BUG: this ought to be inside the if; in theory could fault (daniel barrett) */
+  kp = keywords + n;
+  if (n != -1) {  /* found in table */
+    yylval.i = kp->sub;
+    switch (kp->type) {  /* special handling */
+      case BLTIN:
+        if (kp->sub == FSYSTEM && safe)
+          SYNTAX( "system is unsafe" );
+        RET(kp->type);
+      case FUNC:
+        if (infunc) SYNTAX( "illegal nested function" );
+        RET(kp->type);
+      case RETURN:
+        if (!infunc) SYNTAX( "return not in function" );
+        RET(kp->type);
+      case VARNF:
+        yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
+        RET(VARNF);
+      case VARERR:
+        yylval.cp = setsymtab("ERRNO", "", (Awkfloat)errno, NUM, symtab);
+        RET(VARERR);
+      default:
+        RET(kp->type);
+    }
+  }
+  c = awk_peek();  /* look for '(' */
+  if (c != '(' && infunc && (n=isarg(w)) >= 0) {
+    yylval.i = n;
+    RET(ARG);
+  } else {
+    yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
+    if (c == '(') {
+      RET(CALL);
+    }
+    else {
+      RET(VAR);
+    }
+  }
+}
+
+void startreg(void)  /* next call to yylex will return a regular expression */
+{
+  reg = 1;
+}
+
+int regexpr(void)
+{
+  int c;
+  static char *buf = 0;
+  static int bufsz = 500;
+  char *bp;
+
+  if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+    FATAL("out of space for rex expr");
+  bp = buf;
+  for ( ; (c = input()) != '/' && c != 0; ) {
+    if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr"))
+      FATAL("out of space for reg expr %.10s...", buf);
+    if (c == '\n') {
+      SYNTAX( "newline in regular expression %.10s...", buf );
+      unput('\n');
+      break;
+    } else if (c == '\\') {
+      *bp++ = '\\';
+      *bp++ = input();
+    } else *bp++ = c;
+  }
+  *bp = 0;
+  if (c == 0) SYNTAX("non-terminated regular expression %.10s...", buf);
+  yylval.s = tostring(buf);
+  unput('/');
+  RET(REGEXPR);
+}
+
+int input(void)  /* get next lexical input character */
+{
+  int c;
+  extern char *lexprog;
+
+  if (yysptr > yysbuf)
+    c = (uschar)*--yysptr;
+  else if (lexprog != NULL) {  /* awk '...' */
+    if ((c = (uschar)*lexprog) != 0)
+      lexprog++;
+  } else c = pgetc();    /* awk -f ... */
+  if (c == '\n') lineno++;
+  else if (c == EOF) c = 0;
+  if (ep >= ebuf + sizeof ebuf)
+    ep = ebuf;
+  return *ep++ = c;
+}
+
+void unput(int c)  /* put lexical character back on input */
+{
+  if (c == '\n') lineno--;
+  if (yysptr >= yysbuf + sizeof(yysbuf))
+    FATAL("pushed back too much: %.20s...", yysbuf);
+  *yysptr++ = c;
+  if (--ep < ebuf) ep = ebuf + sizeof(ebuf) - 1;
+}
+
+void unputstr(const char *s)  /* put a string back on input */
+{
+  int i;
+
+  for (i = strlen(s)-1; i >= 0; i--)
+    unput(s[i]);
+}
+
+
+Node *nodealloc(int n)
+{
+  Node *x;
+
+  x = (Node *) xmalloc(sizeof(Node) + (n-1)*sizeof(Node *));
+  x->nnext = NULL;
+  x->lineno = lineno;
+  return(x);
+}
+
+Node *exptostat(Node *a)
+{
+  a->ntype = NSTAT;
+  return(a);
+}
+
+Node *node1(int a, Node *b)
+{
+  Node *x;
+
+  x = nodealloc(1);
+  x->nobj = a;
+  x->narg[0]=b;
+  return(x);
+}
+
+Node *node2(int a, Node *b, Node *c)
+{
+  Node *x;
+
+  x = nodealloc(2);
+  x->nobj = a;
+  x->narg[0] = b;
+  x->narg[1] = c;
+  return(x);
+}
+
+Node *node3(int a, Node *b, Node *c, Node *d)
+{
+  Node *x;
+
+  x = nodealloc(3);
+  x->nobj = a;
+  x->narg[0] = b;
+  x->narg[1] = c;
+  x->narg[2] = d;
+  return(x);
+}
+
+Node *node4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+  Node *x;
+
+  x = nodealloc(4);
+  x->nobj = a;
+  x->narg[0] = b;
+  x->narg[1] = c;
+  x->narg[2] = d;
+  x->narg[3] = e;
+  return(x);
+}
+
+Node *stat1(int a, Node *b)
+{
+  Node *x;
+
+  x = node1(a, b);
+  x->ntype = NSTAT;
+  return(x);
+}
+
+Node *stat2(int a, Node *b, Node *c)
+{
+  Node *x;
+
+  x = node2(a, b, c);
+  x->ntype = NSTAT;
+  return(x);
+}
+
+Node *stat3(int a, Node *b, Node *c, Node *d)
+{
+  Node *x;
+
+  x = node3(a, b, c, d);
+  x->ntype = NSTAT;
+  return(x);
+}
+
+Node *stat4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+  Node *x;
+
+  x = node4(a, b, c, d, e);
+  x->ntype = NSTAT;
+  return(x);
+}
+
+Node *op1(int a, Node *b)
+{
+  Node *x;
+
+  x = node1(a, b);
+  x->ntype = NEXPR;
+  return(x);
+}
+
+Node *op2(int a, Node *b, Node *c)
+{
+  Node *x;
+
+  x = node2(a, b, c);
+  x->ntype = NEXPR;
+  return(x);
+}
+
+Node *op3(int a, Node *b, Node *c, Node *d)
+{
+  Node *x;
+
+  x = node3(a, b, c, d);
+  x->ntype = NEXPR;
+  return(x);
+}
+
+Node *op4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+  Node *x;
+
+  x = node4(a, b, c, d, e);
+  x->ntype = NEXPR;
+  return(x);
+}
+
+Node *celltonode(Cell *a, int b)
+{
+  Node *x;
+
+  a->ctype = OCELL;
+  a->csub = b;
+  x = node1(0, (Node *) a);
+  x->ntype = NVALUE;
+  return(x);
+}
+
+Node *rectonode(void)  /* make $0 into a Node */
+{
+  extern Cell *literal0;
+  return op1(INDIRECT, celltonode(literal0, CUNK));
+}
+
+Node *makearr(Node *p)
+{
+  Cell *cp;
+
+  if (isvalue(p)) {
+    cp = (Cell *) (p->narg[0]);
+    if (isfcn(cp))
+      SYNTAX( "%s is a function, not an array", cp->nval );
+    else if (!isarr(cp)) {
+      xfree(cp->sval);
+      cp->sval = (char *) makesymtab(NSYMTAB);
+      cp->tval = ARR;
+    }
+  }
+  return p;
+}
+
+Node *pa2stat(Node *a, Node *b, Node *c)  /* pat, pat {...} */
+{
+  Node *x;
+
+  x = node4(PASTAT2, a, b, c, itonp(paircnt));
+  if (paircnt++ >= PA2NUM)
+    SYNTAX( "limited to %d pat,pat statements", PA2NUM );
+  x->ntype = NSTAT;
+  return(x);
+}
+
+Node *linkum(Node *a, Node *b)
+{
+  Node *c;
+
+  if (errorflag) return a;  /* don't link things that are wrong */
+
+  if (a == NULL) return(b);
+  else if (b == NULL) return(a);
+  for (c = a; c->nnext != NULL; c = c->nnext)
+    ;
+  c->nnext = b;
+  return(a);
+}
+
+void defn(Cell *v, Node *vl, Node *st)  /* turn on FCN bit in definition, */
+{          /*   body of function, arglist */
+  Node *p;
+  int n;
+
+  if (isarr(v)) {
+    SYNTAX( "`%s' is an array name and a function name", v->nval );
+    return;
+  }
+  if (isarg(v->nval) != -1) {
+    SYNTAX( "`%s' is both function name and argument name", v->nval );
+    return;
+  }
+
+  v->tval = FCN;
+  v->sval = (char *) st;
+  n = 0;  /* count arguments */
+  for (p = vl; p; p = p->nnext)
+    n++;
+  v->fval = n;
+  dprintf( ("defining func %s (%d args)\n", v->nval, n) );
+}
+
+int isarg(const char *s)    /* is s in argument list for current function? */
+{      /* return -1 if not, otherwise arg # */
+  extern Node *arglist;
+  Node *p = arglist;
+  int n;
+
+  for (n = 0; p != 0; p = p->nnext, n++)
+    if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
+      return n;
+  return -1;
+}
+
+int ptoi(void *p)  /* convert pointer to integer */
+{
+  return (int) (long) p;  /* swearing that p fits, of course */
+}
+
+Node *itonp(int i)  /* and vice versa */
+{
+  return (Node *) (long) i;
+}
+
+void syminit(void)  /* initialize symbol table with builtin vars */
+{
+  literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
+  /* this is used for if(x)... tests: */
+  nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
+  nullnode = celltonode(nullloc, CCON);
+
+  fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
+  FS = &fsloc->sval;
+  RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+  OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
+  ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+  OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
+  CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
+  FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
+  nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
+  NF = &nfloc->fval;
+  errloc = setsymtab("ERRNO" , "", 0.0, NUM, symtab);
+  ERRNO = &errloc->fval;
+  nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
+  NR = &nrloc->fval;
+  fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
+  FNR = &fnrloc->fval;
+  SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
+  rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
+  RSTART = &rstartloc->fval;
+  rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
+  RLENGTH = &rlengthloc->fval;
+  symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
+  symtabloc->sval = (char *) symtab;
+}
+
+void arginit(int ac, char **av)  /* set up ARGV and ARGC */
+{
+  Cell *cp;
+  int i;
+  char temp[50];
+
+  ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
+  cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
+  ARGVtab = makesymtab(NSYMTAB);  /* could be (int) ARGC as well */
+  cp->sval = (char *) ARGVtab;
+  for (i = 0; i < ac; i++) {
+    sprintf(temp, "%d", i);
+    if (is_number(*av))
+      setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
+    else setsymtab(temp, *av, 0.0, STR, ARGVtab);
+    av++;
+  }
+}
+
+void envinit(char **envp)  /* set up ENVIRON variable */
+{
+  Cell *cp;
+  char *p;
+
+  cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
+  ENVtab = makesymtab(NSYMTAB);
+  cp->sval = (char *) ENVtab;
+  for ( ; *envp; envp++) {
+    if ((p = strchr(*envp, '=')) == NULL)
+      continue;
+    if( p == *envp ) continue; /* no left hand side name in env string */
+
+    *p++ = 0;  /* split into two strings at = */
+    if (is_number(p)) setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
+    else setsymtab(*envp, p, 0.0, STR, ENVtab);
+    p[-1] = '=';  /* restore in case env is passed down to a shell */
+  }
+}
+
+Array *makesymtab(int n)  /* make a new symbol table */
+{
+  Array *ap;
+  Cell **tp;
+
+  ap = (Array *) xmalloc(sizeof(Array));
+  tp = (Cell **) calloc(n, sizeof(Cell *));
+  if (ap == NULL || tp == NULL)
+    FATAL("out of space in makesymtab");
+  ap->nelem = 0;
+  ap->size = n;
+  ap->tab = tp;
+  return(ap);
+}
+
+void freesymtab(Cell *ap)  /* free a symbol table */
+{
+  Cell *cp, *temp;
+  Array *tp;
+  int i;
+
+  if (!isarr(ap))  return;
+  tp = (Array *) ap->sval;
+  if (tp == NULL)  return;
+
+  for (i = 0; i < tp->size; i++) {
+    for (cp = tp->tab[i]; cp != NULL; cp = temp) {
+      xfree(cp->nval);
+      if (freeable(cp)) xfree(cp->sval);
+      temp = cp->cnext;  /* avoids freeing then using */
+      free(cp);
+      tp->nelem--;
+    }
+    tp->tab[i] = 0;
+  }
+  if (tp->nelem != 0)
+    WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
+  free(tp->tab);
+  free(tp);
+}
+
+void freeelem(Cell *ap, const char *s)  /* free elem s from ap (i.e., ap["s"] */
+{
+  Array *tp;
+  Cell *p, *prev = NULL;
+  int h;
+
+  tp = (Array *) ap->sval;
+  h = hash(s, tp->size);
+  for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
+    if (strcmp(s, p->nval) == 0) {
+      if (prev == NULL)  /* 1st one */
+        tp->tab[h] = p->cnext;
+      else      /* middle somewhere */
+        prev->cnext = p->cnext;
+      if (freeable(p)) xfree(p->sval);
+      free(p->nval);
+      free(p);
+      tp->nelem--;
+      return;
+    }
+}
+
+Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
+{
+  int h;
+  Cell *p;
+
+  if (n != NULL && (p = lookup(n, tp)) != NULL) {
+    dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
+          (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) );
+    return(p);
+  }
+  p = (Cell *) malloc(sizeof(Cell));
+  if (p == NULL)
+    FATAL("out of space for symbol table at %s", n);
+  p->nval = tostring(n);
+  p->sval = s ? tostring(s) : tostring("");
+  p->fval = f;
+  p->tval = t;
+  p->csub = CUNK;
+  p->ctype = OCELL;
+  tp->nelem++;
+  if (tp->nelem > FULLTAB * tp->size)
+    rehash(tp);
+  h = hash(n, tp->size);
+  p->cnext = tp->tab[h];
+  tp->tab[h] = p;
+  dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
+        (void*)p, p->nval, p->sval, p->fval, p->tval) );
+  return(p);
+}
+
+int hash(const char *s, int n)  /* form hash value for string s */
+{
+  unsigned hashval;
+
+  for (hashval = 0; *s != '\0'; s++)
+    hashval = (*s + 31 * hashval);
+  return hashval % n;
+}
+
+void rehash(Array *tp)  /* rehash items in small table into big one */
+{
+  int i, nh, nsz;
+  Cell *cp, *op, **np;
+
+  nsz = GROWTAB * tp->size;
+  np = (Cell **) calloc(nsz, sizeof(Cell *));
+  if (np == NULL)    /* can't do it, but can keep running. */
+    return;    /* someone else will run out later. */
+  for (i = 0; i < tp->size; i++) {
+    for (cp = tp->tab[i]; cp; cp = op) {
+      op = cp->cnext;
+      nh = hash(cp->nval, nsz);
+      cp->cnext = np[nh];
+      np[nh] = cp;
+    }
+  }
+  free(tp->tab);
+  tp->tab = np;
+  tp->size = nsz;
+}
+
+Cell *lookup(const char *s, Array *tp)  /* look for s in tp */
+{
+  Cell *p;
+  int h;
+
+  h = hash(s, tp->size);
+  for (p = tp->tab[h]; p != NULL; p = p->cnext)
+    if (strcmp(s, p->nval) == 0) return(p);  /* found it */
+  return(NULL);      /* not found */
+}
+
+Awkfloat setfval(Cell *vp, Awkfloat f)  /* set float val of a Cell */
+{
+  int fldno;
+
+  if ((vp->tval & (NUM | STR)) == 0)
+    funnyvar(vp, "assign to");
+  if (isfld(vp)) {
+    donerec = 0;  /* mark $0 invalid */
+    fldno = atoi(vp->nval);
+    if (fldno > *NF)
+      newfld(fldno);
+    dprintf( ("setting field %d to %g\n", fldno, f) );
+  } else if (isrec(vp)) {
+    donefld = 0;  /* mark $1... invalid */
+    donerec = 1;
+  }
+  if (freeable(vp)) xfree(vp->sval); /* free any previous string */
+  vp->tval &= ~STR;  /* mark string invalid */
+  vp->tval |= NUM;  /* mark number ok */
+  dprintf( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) );
+  return vp->fval = f;
+}
+
+void funnyvar(Cell *vp, const char *rw)
+{
+  if (isarr(vp))
+    FATAL("can't %s %s; it's an array name.", rw, vp->nval);
+  if (vp->tval & FCN)
+    FATAL("can't %s %s; it's a function.", rw, vp->nval);
+  WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
+      vp, vp->nval, vp->sval, vp->fval, vp->tval);
+}
+
+char *setsval(Cell *vp, const char *s)  /* set string val of a Cell */
+{
+  char *t;
+  int fldno;
+
+  dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
+        (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
+  if ((vp->tval & (NUM | STR)) == 0)
+    funnyvar(vp, "assign to");
+  if (isfld(vp)) {
+    donerec = 0;  /* mark $0 invalid */
+    fldno = atoi(vp->nval);
+    if (fldno > *NF)
+      newfld(fldno);
+    dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
+  } else if (isrec(vp)) {
+    donefld = 0;  /* mark $1... invalid */
+    donerec = 1;
+  }
+  t = tostring(s);  /* in case it's self-assign */
+  if (freeable(vp)) xfree(vp->sval);
+  vp->tval &= ~NUM;
+  vp->tval |= STR;
+  vp->tval &= ~DONTFREE;
+  dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
+        (void*)vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) );
+  return(vp->sval = t);
+}
+
+Awkfloat getfval(Cell *vp)  /* get float val of a Cell */
+{
+  if ((vp->tval & (NUM | STR)) == 0)
+    funnyvar(vp, "read value of");
+  if (isfld(vp) && donefld == 0) fldbld();
+  else if (isrec(vp) && donerec == 0)  recbld();
+  if (!isnum(vp)) {  /* not a number */
+    vp->fval = atof(vp->sval);  /* best guess */
+    if (is_number(vp->sval) && !(vp->tval&CON))
+      vp->tval |= NUM;  /* make NUM only sparingly */
+  }
+  dprintf( ("getfval %p: %s = %g, t=%o\n",
+        (void*)vp, NN(vp->nval), vp->fval, vp->tval) );
+  return(vp->fval);
+}
+
+static char *get_str_val(Cell *vp, char **fmt)    /* get string val of a Cell */
+{
+  char s[100];  /* BUG: unchecked */
+  double dtemp;
+
+  if ((vp->tval & (NUM | STR)) == 0)
+    funnyvar(vp, "read value of");
+  if (isfld(vp) && donefld == 0) fldbld();
+  else if (isrec(vp) && donerec == 0)  recbld();
+  if (isstr(vp) == 0) {
+    if (freeable(vp)) xfree(vp->sval);
+    if (modf(vp->fval, &dtemp) == 0)  /* it's integral */
+      sprintf(s, "%.30g", vp->fval);
+    else sprintf(s, *fmt, vp->fval);
+    vp->sval = tostring(s);
+    vp->tval &= ~DONTFREE;
+    vp->tval |= STR;
+  }
+  dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n",
+        (void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) );
+  return(vp->sval);
+}
+
+char *getsval(Cell *vp)     /* get string val of a Cell */
+{
+  return get_str_val(vp, CONVFMT);
+}
+
+char *getpssval(Cell *vp)   /* get string val of a Cell for print */
+{
+  return get_str_val(vp, OFMT);
+}
+
+
+char *tostring(const char *s)  /* make a copy of string s */
+{
+  char *p;
+
+  p = (char *) malloc(strlen(s)+1);
+  if (p == NULL)
+    FATAL("out of space in tostring on %s", s);
+  strcpy(p, s);
+  return(p);
+}
+
+char *qstring(const char *is, int delim)  /* collect string up to next delim */
+{
+  const char *os = is;
+  int c, n;
+  uschar *s = (uschar *) is;
+  uschar *buf, *bp;
+
+  if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
+    FATAL( "out of space in qstring(%s)", s);
+  for (bp = buf; (c = *s) != delim; s++) {
+    if (c == '\n')
+      SYNTAX( "newline in string %.20s...", os );
+    else if (c != '\\') *bp++ = c;
+    else {  /* \something */
+      c = *++s;
+      if (c == 0) {  /* \ at end */
+        *bp++ = '\\';
+        break;  /* for loop */
+      }
+      switch (c) {
+        case '\\':  *bp++ = '\\'; break;
+        case 'n':  *bp++ = '\n'; break;
+        case 't':  *bp++ = '\t'; break;
+        case 'b':  *bp++ = '\b'; break;
+        case 'f':  *bp++ = '\f'; break;
+        case 'r':  *bp++ = '\r'; break;
+        default:
+              if (!isdigit(c)) {
+                *bp++ = c;
+                break;
+              }
+              n = c - '0';
+              if (isdigit(s[1])) {
+                n = 8 * n + *++s - '0';
+                if (isdigit(s[1]))
+                  n = 8 * n + *++s - '0';
+              }
+              *bp++ = n;
+              break;
+      }
+    }
+  }
+  *bp++ = 0;
+  return (char *) buf;
+}
+
+void recinit(unsigned int n)
+{
+  if ( (record = (char *) malloc(n)) == NULL
+    || (fields = (char *) malloc(n+1)) == NULL
+    || (fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *))) == NULL
+    || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
+    FATAL("out of space for $0 and fields");
+  *fldtab[0] = dollar0;
+  fldtab[0]->sval = record;
+  fldtab[0]->nval = tostring("0");
+  makefields(1, nfields);
+}
+
+void makefields(int n1, int n2)    /* create $n1..$n2 inclusive */
+{
+  char temp[50];
+  int i;
+
+  for (i = n1; i <= n2; i++) {
+    fldtab[i] = (Cell *) xmalloc(sizeof (struct Cell));
+    *fldtab[i] = dollar1;
+    sprintf(temp, "%d", i);
+    fldtab[i]->nval = tostring(temp);
+  }
+}
+
+void initgetrec(void)
+{
+  int i;
+  char *p;
+
+  for (i = 1; i < *ARGC; i++) {
+    p = getargv(i); /* find 1st real filename */
+    if (p == NULL || *p == '\0') {  /* deleted or zapped */
+      argno++;
+      continue;
+    }
+    if (!isclvar(p)) {
+      setsval(lookup("FILENAME", symtab), p);
+      return;
+    }
+    setclvar(p);  /* a commandline assignment before filename */
+    argno++;
+  }
+  infile = stdin;    /* no filenames, so use stdin */
+}
+
+int getrec(char **pbuf, int *pbufsize, int isrecord)  /* get next input record */
+{      /* note: cares whether buf == record */
+  int c;
+  char *buf = *pbuf;
+  uschar saveb0;
+  int bufsize = *pbufsize, savebufsize = bufsize;
+
+  if (firsttime) {
+    firsttime = 0;
+    initgetrec();
+  }
+  dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
+        *RS, *FS, *ARGC, *FILENAME) );
+  if (isrecord) {
+    donefld = 0;
+    donerec = 1;
+  }
+  saveb0 = buf[0];
+  buf[0] = 0;
+  while (argno < *ARGC || infile == stdin) {
+    dprintf( ("argno=%d, file=|%s|\n", argno, file) );
+    if (infile == NULL) {  /* have to open a new file */
+      file = getargv(argno);
+      if (file == NULL || *file == '\0') {  /* deleted or zapped */
+        argno++;
+        continue;
+      }
+      if (isclvar(file)) {  /* a var=value arg */
+        setclvar(file);
+        argno++;
+        continue;
+      }
+      *FILENAME = file;
+      dprintf( ("opening file %s\n", file) );
+      if (*file == '-' && *(file+1) == '\0')
+        infile = stdin;
+      else if ((infile = fopen(file, "r")) == NULL)
+        FATAL("can't open file %s", file);
+      setfval(fnrloc, 0.0);
+    }
+    c = readrec(&buf, &bufsize, infile);
+    if (c != 0 || buf[0] != '\0') {  /* normal record */
+      if (isrecord) {
+        if (freeable(fldtab[0]))
+          xfree(fldtab[0]->sval);
+        fldtab[0]->sval = buf;  /* buf == record */
+        fldtab[0]->tval = REC | STR | DONTFREE;
+        if (is_number(fldtab[0]->sval)) {
+          fldtab[0]->fval = atof(fldtab[0]->sval);
+          fldtab[0]->tval |= NUM;
+        }
+      }
+      setfval(nrloc, nrloc->fval+1);
+      setfval(fnrloc, fnrloc->fval+1);
+      *pbuf = buf;
+      *pbufsize = bufsize;
+      return 1;
+    }
+    /* EOF arrived on this file; set up next */
+    if (infile != stdin) fclose(infile);
+    infile = NULL;
+    argno++;
+  }
+  buf[0] = saveb0;
+  *pbuf = buf;
+  *pbufsize = savebufsize;
+  return 0;  /* true end of file */
+}
+
+void nextfile(void)
+{
+  if (infile != NULL && infile != stdin)
+    fclose(infile);
+  infile = NULL;
+  argno++;
+}
+
+int readrec(char **pbuf, int *pbufsize, FILE *inf)  /* read one record into buf */
+{
+  int sep, c;
+  char *rr, *buf = *pbuf;
+  int bufsize = *pbufsize;
+
+  if (strlen(*FS) >= sizeof(inputFS))
+    FATAL("field separator %.10s... is too long", *FS);
+  /*fflush(stdout); avoids some buffering problem but makes it 25% slower*/
+  strcpy(inputFS, *FS);  /* for subsequent field splitting */
+  if ((sep = **RS) == 0) {
+    sep = '\n';
+    while ((c=getc(inf)) == '\n' && c != EOF)  /* skip leading \n's */
+      ;
+    if (c != EOF) ungetc(c, inf);
+  }
+  for (rr = buf; ; ) {
+    for (; (c=getc(inf)) != sep && c != EOF; ) {
+      if (rr-buf+1 > bufsize)
+        if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
+          FATAL("input record `%.30s...' too long", buf);
+      *rr++ = c;
+    }
+    if (**RS == sep || c == EOF) break;
+    if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
+      break;
+    if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
+      FATAL("input record `%.30s...' too long", buf);
+    *rr++ = '\n';
+    *rr++ = c;
+  }
+  if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
+    FATAL("input record `%.30s...' too long", buf);
+  *rr = 0;
+  dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
+  *pbuf = buf;
+  *pbufsize = bufsize;
+  return c == EOF && rr == buf ? 0 : 1;
+}
+
+char *getargv(int n)  /* get ARGV[n] */
+{
+  Cell *x;
+  char *s, temp[50];
+  extern Array *ARGVtab;
+
+  sprintf(temp, "%d", n);
+  if (lookup(temp, ARGVtab) == NULL) return NULL;
+  x = setsymtab(temp, "", 0.0, STR, ARGVtab);
+  s = getsval(x);
+  dprintf( ("getargv(%d) returns |%s|\n", n, s) );
+  return s;
+}
+
+void setclvar(char *s)  /* set var=value from s */
+{
+  char *p;
+  Cell *q;
+
+  for (p=s; *p != '='; p++)
+    ;
+  *p++ = 0;
+  p = qstring(p, '\0');
+  q = setsymtab(s, p, 0.0, STR, symtab);
+  setsval(q, p);
+  if (is_number(q->sval)) {
+    q->fval = atof(q->sval);
+    q->tval |= NUM;
+  }
+  dprintf( ("command line set %s to |%s|\n", s, p) );
+}
+
+
+void fldbld(void)  /* create fields from current record */
+{
+  /* this relies on having fields[] the same length as $0 */
+  /* the fields are all stored in this one array with \0's */
+  /* possibly with a final trailing \0 not associated with any field */
+  char *r, *fr, sep;
+  Cell *p;
+  int i, j, n;
+
+  if (donefld) return;
+  if (!isstr(fldtab[0])) getsval(fldtab[0]);
+  r = fldtab[0]->sval;
+  n = strlen(r);
+  if (n > fieldssize) {
+    xfree(fields);
+    if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
+      FATAL("out of space for fields in fldbld %d", n);
+    fieldssize = n;
+  }
+  fr = fields;
+  i = 0;  /* number of fields accumulated here */
+  strcpy(inputFS, *FS);
+  if (strlen(inputFS) > 1) {  /* it's a regular expression */
+    i = refldbld(r, inputFS);
+  } else if ((sep = *inputFS) == ' ') {  /* default whitespace */
+    for (i = 0; ; ) {
+      while (*r == ' ' || *r == '\t' || *r == '\n')
+        r++;
+      if (*r == 0) break;
+      i++;
+      if (i > nfields) growfldtab(i);
+      if (freeable(fldtab[i])) xfree(fldtab[i]->sval);
+      fldtab[i]->sval = fr;
+      fldtab[i]->tval = FLD | STR | DONTFREE;
+      do
+        *fr++ = *r++;
+      while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
+      *fr++ = 0;
+    }
+    *fr = 0;
+  } else if ((sep = *inputFS) == 0) {    /* new: FS="" => 1 char/field */
+    for (i = 0; *r != 0; r++) {
+      char buf[2];
+      i++;
+      if (i > nfields) growfldtab(i);
+      if (freeable(fldtab[i])) xfree(fldtab[i]->sval);
+      buf[0] = *r;
+      buf[1] = 0;
+      fldtab[i]->sval = tostring(buf);
+      fldtab[i]->tval = FLD | STR;
+    }
+    *fr = 0;
+  } else if (*r != 0) {  /* if 0, it's a null field */
+    /* subtlecase : if length(FS) == 1 && length(RS > 0)
+     * \n is NOT a field separator (cf awk book 61,84).
+     * this variable is tested in the inner while loop.
+     */
+    int rtest = '\n';  /* normal case */
+    if (strlen(*RS) > 0) rtest = '\0';
+    for (;;) {
+      i++;
+      if (i > nfields) growfldtab(i);
+      if (freeable(fldtab[i])) xfree(fldtab[i]->sval);
+      fldtab[i]->sval = fr;
+      fldtab[i]->tval = FLD | STR | DONTFREE;
+      while (*r != sep && *r != rtest && *r != '\0')  /* \n is always a separator */
+        *fr++ = *r++;
+      *fr++ = 0;
+      if (*r++ == 0) break;
+    }
+    *fr = 0;
+  }
+  if (i > nfields)
+    FATAL("record `%.30s...' has too many fields; can't happen", r);
+  cleanfld(i+1, lastfld);  /* clean out junk from previous record */
+  lastfld = i;
+  donefld = 1;
+  for (j = 1; j <= lastfld; j++) {
+    p = fldtab[j];
+    if(is_number(p->sval)) {
+      p->fval = atof(p->sval);
+      p->tval |= NUM;
+    }
+  }
+  setfval(nfloc, (Awkfloat) lastfld);
+  if (dbg) {
+    for (j = 0; j <= lastfld; j++) {
+      p = fldtab[j];
+      printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
+    }
+  }
+}
+
+void cleanfld(int n1, int n2)  /* clean out fields n1 .. n2 inclusive */
+{        /* nvals remain intact */
+  Cell *p;
+  int i;
+
+  for (i = n1; i <= n2; i++) {
+    p = fldtab[i];
+    if (freeable(p)) xfree(p->sval);
+    p->sval = "";
+    p->tval = FLD | STR | DONTFREE;
+  }
+}
+
+void newfld(int n)  /* add field n after end of existing lastfld */
+{
+  if (n > nfields) growfldtab(n);
+  cleanfld(lastfld+1, n);
+  lastfld = n;
+  setfval(nfloc, (Awkfloat) n);
+}
+
+Cell *fieldadr(int n)  /* get nth field */
+{
+  if (n < 0)
+    FATAL("trying to access out of range field %d", n);
+  if (n > nfields)  /* fields after NF are empty */
+    growfldtab(n);  /* but does not increase NF */
+  return(fldtab[n]);
+}
+
+void growfldtab(int n)  /* make new fields up to at least $n */
+{
+  int nf = 2 * nfields;
+  size_t s;
+
+  if (n > nf)  nf = n;
+  s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
+  if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
+    fldtab = (Cell **) realloc(fldtab, s);
+  else xfree(fldtab);        /* overflow sizeof int */
+  if (fldtab == NULL)
+    FATAL("out of space creating %d fields", nf);
+  makefields(nfields+1, nf);
+  nfields = nf;
+}
+
+int refldbld(const char *rec, const char *fs)  /* build fields from reg expr in FS */
+{
+  /* this relies on having fields[] the same length as $0 */
+  /* the fields are all stored in this one array with \0's */
+  char *fr;
+  int i, tempstat, n;
+  fa *pfa;
+
+  n = strlen(rec);
+  if (n > fieldssize) {
+    xfree(fields);
+    if ((fields = (char *) malloc(n+1)) == NULL)
+      FATAL("out of space for fields in refldbld %d", n);
+    fieldssize = n;
+  }
+  fr = fields;
+  *fr = '\0';
+  if (*rec == '\0') return 0;
+  pfa = makedfa(fs, 1);
+  dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
+  tempstat = pfa->initstat;
+  for (i = 1; ; i++) {
+    if (i > nfields) growfldtab(i);
+    if (freeable(fldtab[i])) xfree(fldtab[i]->sval);
+    fldtab[i]->tval = FLD | STR | DONTFREE;
+    fldtab[i]->sval = fr;
+    dprintf( ("refldbld: i=%d\n", i) );
+    if (nematch(pfa, rec)) {
+      pfa->initstat = 2;  /* horrible coupling to b.c */
+      dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
+      strncpy(fr, rec, patbeg-rec);
+      fr += patbeg - rec + 1;
+      *(fr-1) = '\0';
+      rec = patbeg + patlen;
+    } else {
+      dprintf( ("no match %s\n", rec) );
+      strcpy(fr, rec);
+      pfa->initstat = tempstat;
+      break;
+    }
+  }
+  return i;
+}
+
+void recbld(void)  /* create $0 from $1..$NF if necessary */
+{
+  int i;
+  char *r, *p;
+
+  if (donerec == 1) return;
+  r = record;
+  for (i = 1; i <= *NF; i++) {
+    p = getsval(fldtab[i]);
+    if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
+      FATAL("created $0 `%.30s...' too long", record);
+    while ((*r = *p++) != 0)
+      r++;
+    if (i < *NF) {
+      if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
+        FATAL("created $0 `%.30s...' too long", record);
+      for (p = *OFS; (*r = *p++) != 0; )
+        r++;
+    }
+  }
+  if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
+    FATAL("built giant record `%.30s...'", record);
+  *r = '\0';
+  dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
+
+  if (freeable(fldtab[0])) xfree(fldtab[0]->sval);
+  fldtab[0]->tval = REC | STR | DONTFREE;
+  fldtab[0]->sval = record;
+
+  dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
+  dprintf( ("recbld = |%s|\n", record) );
+  donerec = 1;
+}
+
+int  errorflag  = 0;
+
+void yyerror(const char *s)
+{
+  SYNTAX("%s", s);
+}
+
+void SYNTAX(const char *fmt, ...)
+{
+  extern char *cmdname, *curfname;
+  static int been_here = 0;
+  va_list varg;
+
+  if(toys.exitval == 0) toys.exitval++;
+  if (been_here++ > 2) return;
+  fprintf(stderr, "%s: ", cmdname);
+  va_start(varg, fmt);
+  vfprintf(stderr, fmt, varg);
+  va_end(varg);
+  fprintf(stderr, " at source line %d", lineno);
+  if (curfname != NULL)
+    fprintf(stderr, " in function %s", curfname);
+  if (compile_time == 1 && cursource() != NULL)
+    fprintf(stderr, " source file %s", cursource());
+  fprintf(stderr, "\n");
+  errorflag = 2;
+  eprint();
+}
+
+void fpecatch(int n)
+{
+  FATAL("floating point exception %d", n);
+}
+
+extern int bracecnt, brackcnt, parencnt;
+
+void bracecheck(void)
+{
+  int c;
+  static int beenhere = 0;
+
+  if (beenhere++) return;
+  while ((c = input()) != EOF && c != '\0')
+    bclass(c);
+  bcheck2(bracecnt, '{', '}');
+  bcheck2(brackcnt, '[', ']');
+  bcheck2(parencnt, '(', ')');
+}
+
+void bcheck2(int n, int c1, int c2)
+{
+  if (n == 1)  fprintf(stderr, "\tmissing %c\n", c2);
+  else if (n > 1)  fprintf(stderr, "\t%d missing %c's\n", n, c2);
+  else if (n == -1) fprintf(stderr, "\textra %c\n", c2);
+  else if (n < -1) fprintf(stderr, "\t%d extra %c's\n", -n, c2);
+}
+
+void FATAL(const char *fmt, ...)
+{
+  extern char *cmdname;
+  va_list varg;
+
+  if(toys.exitval == 0) toys.exitval++;
+  fflush(stdout);
+  fprintf(stderr, "%s: ", cmdname);
+  va_start(varg, fmt);
+  vfprintf(stderr, fmt, varg);
+  va_end(varg);
+  error();
+  if (dbg > 1)    /* core dump if serious debugging on */
+    abort();
+  exit(2);
+}
+
+void WARNING(const char *fmt, ...)
+{
+  extern char *cmdname;
+  va_list varg;
+
+  fflush(stdout);
+  fprintf(stderr, "%s: ", cmdname);
+  va_start(varg, fmt);
+  vfprintf(stderr, fmt, varg);
+  va_end(varg);
+  error();
+}
+
+void error()
+{
+  extern Node *curnode;
+
+  fprintf(stderr, "\n");
+  if (compile_time != 2 && NR && *NR > 0) {
+    fprintf(stderr, " input record number %d", (int) (*FNR));
+    if (strcmp(*FILENAME, "-") != 0)
+      fprintf(stderr, ", file %s", *FILENAME);
+    fprintf(stderr, "\n");
+  }
+  if (compile_time != 2 && curnode)
+    fprintf(stderr, " source line number %d", curnode->lineno);
+  else if (compile_time != 2 && lineno)
+    fprintf(stderr, " source line number %d", lineno);
+  if (compile_time == 1 && cursource() != NULL)
+    fprintf(stderr, " source file %s", cursource());
+  fprintf(stderr, "\n");
+  eprint();
+}
+
+void eprint(void)  /* try to print context around error */
+{
+  char *p, *q;
+  int c;
+  static int been_here = 0;
+  extern char ebuf[], *ep;
+
+  if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
+    return;
+  p = ep - 1;
+  if (p > ebuf && *p == '\n')  p--;
+  for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
+    ;
+  while (*p == '\n') p++;
+  fprintf(stderr, " context is\n\t");
+  for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
+    ;
+  for ( ; p < q; p++)
+    if (*p)  putc(*p, stderr);
+  fprintf(stderr, " >>> ");
+  for ( ; p < ep; p++)
+    if (*p)  putc(*p, stderr);
+  fprintf(stderr, " <<< ");
+  if (*ep)
+    while ((c = input()) != '\n' && c != '\0' && c != EOF) {
+      putc(c, stderr);
+      bclass(c);
+    }
+  putc('\n', stderr);
+  ep = ebuf;
+}
+
+void bclass(int c)
+{
+  switch (c) {
+  case '{': bracecnt++; break;
+  case '}': bracecnt--; break;
+  case '[': brackcnt++; break;
+  case ']': brackcnt--; break;
+  case '(': parencnt++; break;
+  case ')': parencnt--; break;
+  default : break;
+  }
+}
+
+double errcheck(double x, const char *s)
+{
+
+  if (errno == EDOM) {
+    errno = 0;
+    WARNING("%s argument out of domain", s);
+    x = 1;
+  } else if (errno == ERANGE) {
+    errno = 0;
+    WARNING("%s result out of range", s);
+    x = 1;
+  }
+  return x;
+}
+
+int isclvar(const char *s)  /* is s of form var=something ? */
+{
+  const char *os = s;
+
+  if (!isalpha((uschar) *s) && *s != '_')
+    return 0;
+  for ( ; *s; s++)
+    if (!(isalnum((uschar) *s) || *s == '_'))
+      break;
+  return *s == '=' && s > os && *(s+1) != '=';
+}
+
+/* strtod is supposed to be a proper test of what's a valid number */
+/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
+/* wrong: violates 4.10.1.4 of ansi C standard */
+
+int is_number(const char *s)
+{
+  double r;
+  char *ep;
+  errno = 0;
+  r = strtod(s, &ep);
+  if (ep == s || r == HUGE_VAL || errno == ERANGE)
+    return 0;
+  while (*ep == ' ' || *ep == '\t' || *ep == '\n')
+    ep++;
+  if (*ep == '\0') return 1;
+  else return 0;
+}
+
+/* encoding in tree Nodes:
+  leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
+    left is index, right contains value or pointer to value
+  unary (STAR, PLUS, QUEST): left is child, right is null
+  binary (CAT, OR): left and right are children
+  parent contains pointer to parent
+*/
+
+fa *makedfa(const char *s, int anchor)  /* returns dfa for reg expr s */
+{
+  int i, use, nuse;
+  fa *pfa;
+  static int now = 1;
+
+  if (setvec == 0) {  /* first time through any RE */
+    maxsetvec = MAXLIN;
+    setvec = (int *) malloc(maxsetvec * sizeof(int));
+    tmpset = (int *) malloc(maxsetvec * sizeof(int));
+    if (setvec == 0 || tmpset == 0)
+      overflo("out of space initializing makedfa");
+  }
+
+  if (compile_time)  /* a constant for sure */
+    return mkdfa(s, anchor);
+  for (i = 0; i < nfatab; i++)  /* is it there already? */
+    if (fatab[i]->anchor == anchor
+      && strcmp((const char *) fatab[i]->restr, s) == 0) {
+      fatab[i]->use = now++;
+      return fatab[i];
+    }
+  pfa = mkdfa(s, anchor);
+  if (nfatab < NFA) {  /* room for another */
+    fatab[nfatab] = pfa;
+    fatab[nfatab]->use = now++;
+    nfatab++;
+    return pfa;
+  }
+  use = fatab[0]->use;  /* replace least-recently used */
+  nuse = 0;
+  for (i = 1; i < nfatab; i++)
+    if (fatab[i]->use < use) {
+      use = fatab[i]->use;
+      nuse = i;
+    }
+  freefa(fatab[nuse]);
+  fatab[nuse] = pfa;
+  pfa->use = now++;
+  return pfa;
+}
+
+fa *mkdfa(const char *s, int anchor)  /* does the real work of making a dfa */
+        /* anchor = 1 for anchored matches, else 0 */
+{
+  Node *p, *p1;
+  fa *f;
+
+  p = reparse(s);
+  p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
+    /* put ALL STAR in front of reg.  exp. */
+  p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
+    /* put FINAL after reg.  exp. */
+
+  poscnt = 0;
+  penter(p1);  /* enter parent pointers and leaf indices */
+  if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL)
+    overflo("out of space for fa");
+  f->accept = poscnt-1;  /* penter has computed number of positions in re */
+  cfoll(f, p1);  /* set up follow sets */
+  freetr(p1);
+  if ((f->posns[0] = (int *) calloc(1, *(f->re[0].lfollow)*sizeof(int))) == NULL)
+      overflo("out of space in makedfa");
+  if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL)
+    overflo("out of space in makedfa");
+  *f->posns[1] = 0;
+  f->initstat = makeinit(f, anchor);
+  f->anchor = anchor;
+  f->restr = (uschar *) tostring(s);
+  return f;
+}
+
+int makeinit(fa *f, int anchor)
+{
+  int i, k;
+
+  f->curstat = 2;
+  f->out[2] = 0;
+  f->reset = 0;
+  k = *(f->re[0].lfollow);
+  xfree(f->posns[2]);
+  if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
+    overflo("out of space in makeinit");
+  for (i=0; i <= k; i++) {
+    (f->posns[2])[i] = (f->re[0].lfollow)[i];
+  }
+  if ((f->posns[2])[1] == f->accept) f->out[2] = 1;
+  for (i=0; i < NCHARS; i++)
+    f->gototab[2][i] = 0;
+  f->curstat = cgoto(f, 2, HAT);
+  if (anchor) {
+    *f->posns[2] = k-1;  /* leave out position 0 */
+    for (i=0; i < k; i++) {
+      (f->posns[0])[i] = (f->posns[2])[i];
+    }
+
+    f->out[0] = f->out[2];
+    if (f->curstat != 2) --(*f->posns[f->curstat]);
+  }
+  return f->curstat;
+}
+
+void penter(Node *p)  /* set up parent pointers and leaf indices */
+{
+  switch (type(p)) {
+  ELEAF /*FALL_THROUGH*/
+  LEAF
+    info(p) = poscnt;
+    poscnt++;
+    break;
+  UNARY
+    penter(left(p));
+    parent(left(p)) = p;
+    break;
+  case CAT: /*FALL_THROUGH*/
+  case OR:
+    penter(left(p));
+    penter(right(p));
+    parent(left(p)) = p;
+    parent(right(p)) = p;
+    break;
+  default:  /* can't happen */
+    FATAL("can't happen: unknown type %d in penter", type(p));
+    break;
+  }
+}
+
+void freetr(Node *p)  /* free parse tree */
+{
+  switch (type(p)) {
+  ELEAF /*FALL_THROUGH*/
+  LEAF
+    xfree(p);
+    break;
+  UNARY
+    freetr(left(p));
+    xfree(p);
+    break;
+  case CAT: /*FALL_THROUGH*/
+  case OR:
+    freetr(left(p));
+    freetr(right(p));
+    xfree(p);
+    break;
+  default:  /* can't happen */
+    FATAL("can't happen: unknown type %d in freetr", type(p));
+    break;
+  }
+}
+
+/* in the parsing of regular expressions, metacharacters like . have */
+/* to be seen literally;  \056 is not a metacharacter. */
+
+int hexstr(uschar **pp)  /* find and eval hex string at pp, return new p */
+{      /* only pick up one 8-bit byte (2 chars) */
+  uschar *p;
+  int n = 0;
+  int i;
+
+  for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) {
+    if (isdigit(*p))
+      n = 16 * n + *p - '0';
+    else if (*p >= 'a' && *p <= 'f')
+      n = 16 * n + *p - 'a' + 10;
+    else if (*p >= 'A' && *p <= 'F')
+      n = 16 * n + *p - 'A' + 10;
+  }
+  *pp = (uschar *) p;
+  return n;
+}
+
+int quoted(uschar **pp)  /* pick up next thing after a \\ */
+      /* and increment *pp */
+{
+  uschar *p = *pp;
+  int c;
+
+  if ((c = *p++) == 't') c = '\t';
+  else if (c == 'n') c = '\n';
+  else if (c == 'f') c = '\f';
+  else if (c == 'r') c = '\r';
+  else if (c == 'b') c = '\b';
+  else if (c == '\\')  c = '\\';
+  else if (c == 'x') {  /* hexadecimal goo follows */
+    c = hexstr(&p);  /* this adds a null if number is invalid */
+  } else if (isoctdigit(c)) {  /* \d \dd \ddd */
+    int n = c - '0';
+    if (isoctdigit(*p)) {
+      n = 8 * n + *p++ - '0';
+      if (isoctdigit(*p))  n = 8 * n + *p++ - '0';
+    }
+    c = n;
+  } /* else */
+  /* c = c; */
+  *pp = p;
+  return c;
+}
+
+char *cclenter(const char *argp)  /* add a character class */
+{
+  int i, c, c2;
+  uschar *p = (uschar *) argp;
+  uschar *op, *bp;
+  static uschar *buf = 0;
+  static int bufsz = 100;
+
+  op = p;
+  if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL)
+    FATAL("out of space for character class [%.10s...] 1", p);
+  bp = buf;
+  for (i = 0; (c = *p++) != 0; ) {
+    if (c == '\\') c = quoted(&p);
+    else if (c == '-' && i > 0 && bp[-1] != 0) {
+      if (*p != 0) {
+        c = bp[-1];
+        c2 = *p++;
+        if (c2 == '\\')  c2 = quoted(&p);
+        if (c > c2) {  /* empty; ignore */
+          bp--;
+          i--;
+          continue;
+        }
+        while (c < c2) {
+          if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1"))
+            FATAL("out of space for character class [%.10s...] 2", p);
+          *bp++ = ++c;
+          i++;
+        }
+        continue;
+      }
+    }
+    if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter2"))
+      FATAL("out of space for character class [%.10s...] 3", p);
+    *bp++ = c;
+    i++;
+  }
+  *bp = 0;
+  dprintf( ("cclenter: in = |%s|, out = |%s|\n", op, buf) );
+  xfree(op);
+  return (char *) tostring((char *) buf);
+}
+
+void overflo(const char *s)
+{
+  FATAL("regular expression too big: %.30s...", s);
+}
+
+void cfoll(fa *f, Node *v)  /* enter follow set of each leaf of vertex v into lfollow[leaf] */
+{
+  int i;
+  int *p;
+
+  switch (type(v)) {
+  ELEAF
+  LEAF
+    f->re[info(v)].ltype = type(v);
+    f->re[info(v)].lval.np = right(v);
+    while (f->accept >= maxsetvec) {  /* guessing here! */
+      maxsetvec *= 4;
+      setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+      tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+      if (setvec == 0 || tmpset == 0)
+        overflo("out of space in cfoll()");
+    }
+    for (i = 0; i <= f->accept; i++)
+      setvec[i] = 0;
+    setcnt = 0;
+    follow(v);  /* computes setvec and setcnt */
+    if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL)
+      overflo("out of space building follow set");
+    f->re[info(v)].lfollow = p;
+    *p = setcnt;
+    for (i = f->accept; i >= 0; i--)
+      if (setvec[i] == 1)  *++p = i;
+    break;
+  UNARY
+    cfoll(f,left(v));
+    break;
+  case CAT: /*FALL_THROUGH*/
+  case OR:
+    cfoll(f,left(v));
+    cfoll(f,right(v));
+    break;
+  default:  /* can't happen */
+    FATAL("can't happen: unknown type %d in cfoll", type(v));
+  }
+}
+
+int first(Node *p)  /* collects initially active leaves of p into setvec */
+      /* returns 0 if p matches empty string */
+{
+  int b, lp;
+
+  switch (type(p)) {
+  ELEAF
+  LEAF
+    lp = info(p);  /* look for high-water mark of subscripts */
+    while (setcnt >= maxsetvec || lp >= maxsetvec) {  /* guessing here! */
+      maxsetvec *= 4;
+      setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+      tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+      if (setvec == 0 || tmpset == 0)
+        overflo("out of space in first()");
+    }
+    if (type(p) == EMPTYRE) {
+      setvec[lp] = 0;
+      return(0);
+    }
+    if (setvec[lp] != 1) {
+      setvec[lp] = 1;
+      setcnt++;
+    }
+    if (type(p) == CCL && (*(char *) right(p)) == '\0')
+      return(0);    /* empty CCL */
+    else return(1);
+  case PLUS:
+    if (first(left(p)) == 0) return(0);
+    return(1);
+  case STAR: /*FALL_THROUGH*/
+  case QUEST:
+    first(left(p));
+    return(0);
+  case CAT:
+    if (first(left(p)) == 0 && first(right(p)) == 0) return(0);
+    return(1);
+  case OR:
+    b = first(right(p));
+    if (first(left(p)) == 0 || b == 0) return(0);
+    return(1);
+  default:
+    FATAL("can't happen: unknown type %d in first", type(p));  /* can't happen */
+    return(-1);
+  }
+}
+
+void follow(Node *v)  /* collects leaves that can follow v into setvec */
+{
+  Node *p;
+
+  if (type(v) == FINAL) return;
+  p = parent(v);
+  switch (type(p)) {
+  case STAR: /*FALL_THROUGH*/
+  case PLUS:
+    first(v);
+    follow(p);
+    return;
+
+  case OR: /*FALL_THROUGH*/
+  case QUEST:
+    follow(p);
+    return;
+
+  case CAT:
+    if (v == left(p)) {  /* v is left child of p */
+      if (first(right(p)) == 0) {
+        follow(p);
+        return;
+      }
+    } else follow(p);  /* v is right child */
+    return;
+  default: break;
+  }
+}
+
+int member(int c, const char *sarg)  /* is c in s? */
+{
+  uschar *s = (uschar *) sarg;
+
+  while (*s)
+    if (c == *s++) return(1);
+  return(0);
+}
+
+int match(fa *f, const char *p0)  /* shortest match ? */
+{
+  int s, ns;
+  uschar *p = (uschar *) p0;
+
+  s = f->reset ? makeinit(f,0) : f->initstat;
+  if (f->out[s]) return(1);
+  do {
+    /* assert(*p < NCHARS); */
+    if ((ns = f->gototab[s][*p]) != 0) s = ns;
+    else s = cgoto(f, s, *p);
+    if (f->out[s]) return(1);
+  } while (*p++ != 0);
+  return(0);
+}
+
+int pmatch(fa *f, const char *p0)  /* longest match, for sub */
+{
+  int s, ns;
+  uschar *p = (uschar *) p0;
+  uschar *q;
+  int i, k;
+
+  /* s = f->reset ? makeinit(f,1) : f->initstat; */
+  if (f->reset) f->initstat = s = makeinit(f,1);
+  else s = f->initstat;
+
+  patbeg = (char *) p;
+  patlen = -1;
+  do {
+    q = p;
+    do {
+      if (f->out[s]) patlen = q-p;  /* final state */
+      /* assert(*q < NCHARS); */
+      if ((ns = f->gototab[s][*q]) != 0) s = ns;
+      else s = cgoto(f, s, *q);
+      if (s == 1) {  /* no transition */
+        if (patlen >= 0) {
+          patbeg = (char *) p;
+          return(1);
+        }
+        else goto nextin;  /* no match */
+      }
+    } while (*q++ != 0);
+    if (f->out[s]) patlen = q-p-1;  /* don't count $ */
+    if (patlen >= 0) {
+      patbeg = (char *) p;
+      return(1);
+    }
+nextin:
+    s = 2;
+    if (f->reset) {
+      for (i = 2; i <= f->curstat; i++)
+        xfree(f->posns[i]);
+      k = *f->posns[0];
+      if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
+        overflo("out of space in pmatch");
+      for (i = 0; i <= k; i++)
+        (f->posns[2])[i] = (f->posns[0])[i];
+      f->initstat = f->curstat = 2;
+      f->out[2] = f->out[0];
+      for (i = 0; i < NCHARS; i++)
+        f->gototab[2][i] = 0;
+    }
+  } while (*p++ != 0);
+  return (0);
+}
+
+int nematch(fa *f, const char *p0)  /* non-empty match, for sub */
+{
+  int s, ns;
+  uschar *p = (uschar *) p0;
+  uschar *q;
+  int i, k;
+
+  /* s = f->reset ? makeinit(f,1) : f->initstat; */
+  if (f->reset) f->initstat = s = makeinit(f,1);
+  else s = f->initstat;
+
+  patlen = -1;
+  while (*p) {
+    q = p;
+    do {
+      if (f->out[s]) patlen = q-p;  /* final state */
+      /* assert(*q < NCHARS); */
+      if ((ns = f->gototab[s][*q]) != 0)
+        s = ns;
+      else s = cgoto(f, s, *q);
+      if (s == 1) {  /* no transition */
+        if (patlen > 0) {
+          patbeg = (char *) p;
+          return(1);
+        } else goto nnextin;  /* no nonempty match */
+      }
+    } while (*q++ != 0);
+    if (f->out[s])
+      patlen = q-p-1;  /* don't count $ */
+    if (patlen > 0 ) {
+      patbeg = (char *) p;
+      return(1);
+    }
+nnextin:
+    s = 2;
+    if (f->reset) {
+      for (i = 2; i <= f->curstat; i++)
+        xfree(f->posns[i]);
+      k = *f->posns[0];
+      if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
+        overflo("out of state space");
+      for (i = 0; i <= k; i++)
+        (f->posns[2])[i] = (f->posns[0])[i];
+      f->initstat = f->curstat = 2;
+      f->out[2] = f->out[0];
+      for (i = 0; i < NCHARS; i++)
+        f->gototab[2][i] = 0;
+    }
+    p++;
+  }
+  return (0);
+}
+
+Node *reparse(const char *p)  /* parses regular expression pointed to by p */
+{      /* uses relex() to scan regular expression */
+  Node *np;
+
+  dprintf( ("reparse <%s>\n", p) );
+  lastre = prestr = (uschar *) p;  /* prestr points to string to be parsed */
+  rtok = relex();
+  /* GNU compatibility: an empty regexp matches anything */
+  if (rtok == '\0') {
+    /* FATAL("empty regular expression"); previous */
+    return(op2(EMPTYRE, NIL, NIL));
+  }
+  np = regexp();
+  if (rtok != '\0')
+    FATAL("syntax error in regular expression %s at %s", lastre, prestr);
+  return(np);
+}
+
+Node *regexp(void)  /* top-level parse of reg expr */
+{
+  return (alt(concat(primary())));
+}
+
+Node *primary(void)
+{
+  Node *np;
+
+  switch (rtok) {
+  case CHAR:
+    np = op2(CHAR, NIL, itonp(rlxval));
+    rtok = relex();
+    return (unary(np));
+  case ALL:
+    rtok = relex();
+    return (unary(op2(ALL, NIL, NIL)));
+  case EMPTYRE:
+    rtok = relex();
+    return (unary(op2(ALL, NIL, NIL)));
+  case DOT:
+    rtok = relex();
+    return (unary(op2(DOT, NIL, NIL)));
+  case CCL:
+    np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr));
+    rtok = relex();
+    return (unary(np));
+  case NCCL:
+    np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr));
+    rtok = relex();
+    return (unary(np));
+  case '^':
+    rtok = relex();
+    return (unary(op2(CHAR, NIL, itonp(HAT))));
+  case '$':
+    rtok = relex();
+    return (unary(op2(CHAR, NIL, NIL)));
+  case '(':
+    rtok = relex();
+    if (rtok == ')') {  /* special pleading for () */
+      rtok = relex();
+      return unary(op2(CCL, NIL, (Node *) tostring("")));
+    }
+    np = regexp();
+    if (rtok == ')') {
+      rtok = relex();
+      return (unary(np));
+    }
+    else
+      FATAL("syntax error in regular expression %s at %s", lastre, prestr);
+  default:
+    FATAL("illegal primary in regular expression %s at %s", lastre, prestr);
+  }
+  return 0;  /*NOTREACHED*/
+}
+
+Node *concat(Node *np)
+{
+  switch (rtok) {
+  case CHAR: /*FALL_THROUGH*/
+  case DOT: /*FALL_THROUGH*/
+  case ALL: /*FALL_THROUGH*/
+  case EMPTYRE: /*FALL_THROUGH*/
+  case CCL: /*FALL_THROUGH*/
+  case NCCL: /*FALL_THROUGH*/
+  case '$': /*FALL_THROUGH*/
+  case '(':
+    return (concat(op2(CAT, np, primary())));
+    break; //NOT REACHABLE
+  default: return (np);
+    break; //NOT REACHABLE
+  }
+}
+
+Node *alt(Node *np) {
+  if (rtok == OR) {
+    rtok = relex();
+    return (alt(op2(OR, np, concat(primary()))));
+  }
+  return (np);
+}
+
+Node *unary(Node *np)
+{
+  switch (rtok) {
+  case STAR:
+    rtok = relex();
+    return (unary(op2(STAR, np, NIL)));
+  case PLUS:
+    rtok = relex();
+    return (unary(op2(PLUS, np, NIL)));
+  case QUEST:
+    rtok = relex();
+    return (unary(op2(QUEST, np, NIL)));
+  default:
+    return (np);
+  }
+}
+
+/*
+ * Character class definitions conformant to the POSIX locale as
+ * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source
+ * and operating character sets are both ASCII (ISO646) or supersets
+ * thereof.
+ *
+ * Note that to avoid overflowing the temporary buffer used in
+ * relex(), the expanded character class (prior to range expansion)
+ * must be less than twice the size of their full name.
+ */
+
+/* Because isblank doesn't show up in any of the header files on any
+ * system i use, it's defined here.  if some other locale has a richer
+ * definition of "blank", define HAS_ISBLANK and provide your own
+ * version.
+ * the parentheses here are an attempt to find a path through the maze
+ * of macro definition and/or function and/or version provided.  thanks
+ * to nelson beebe for the suggestion; let's see if it works everywhere.
+ */
+int (xisblank)(int c)
+{
+  return c==' ' || c=='\t';
+}
+
+int relex(void)    /* lexical analyzer for reparse */
+{
+  int c, n;
+  int cflag;
+  static uschar *buf = 0;
+  static int bufsz = 100;
+  uschar *bp;
+  struct charclass *cc;
+  int i;
+
+  switch (c = *prestr++) {
+    case '|': return OR;
+    case '*': return STAR;
+    case '+': return PLUS;
+    case '?': return QUEST;
+    case '.': return DOT;
+    case '\0': prestr--; return '\0';
+    case '^': /*FALL_THROUGH*/
+    case '$': /*FALL_THROUGH*/
+    case '(': /*FALL_THROUGH*/
+    case ')':
+           return c;
+    case '\\':
+           rlxval = quoted(&prestr);
+           return CHAR;
+    default:
+           rlxval = c;
+           return CHAR;
+    case '[':
+           if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL)
+             FATAL("out of space in reg expr %.10s..", lastre);
+           bp = buf;
+           if (*prestr == '^') {
+             cflag = 1;
+             prestr++;
+           }
+           else cflag = 0;
+           n = 2 * strlen((const char *) prestr)+1;
+           if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, "relex1"))
+             FATAL("out of space for reg expr %.10s...", lastre);
+           for (; ; ) {
+             if ((c = *prestr++) == '\\') {
+               *bp++ = '\\';
+               if ((c = *prestr++) == '\0')
+                 FATAL("nonterminated character class %.20s...", lastre);
+               *bp++ = c;
+               /* } else if (c == '\n') { */
+               /*   FATAL("newline in character class %.20s...", lastre); */
+           } else if (c == '[' && *prestr == ':') {
+             /* POSIX char class names, Dag-Erling Smorgrav, des@ofug.org */
+             for (cc = charclasses; cc->cc_name; cc++)
+               if (strncmp((const char *) prestr + 1, (const char *) cc->cc_name, cc->cc_namelen) == 0)
+                 break;
+             if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':' &&
+                 prestr[2 + cc->cc_namelen] == ']') {
+               prestr += cc->cc_namelen + 3;
+               for (i = 0; i < NCHARS; i++) {
+                 if (!adjbuf((char **) &buf, &bufsz, bp-buf+1, 100, (char **) &bp, "relex2"))
+                   FATAL("out of space for reg expr %.10s...", lastre);
+                 if (cc->cc_func(i)) {
+                   *bp++ = i;
+                   n++;
+                 }
+               }
+             } else *bp++ = c;
+           } else if (c == '\0') {
+             FATAL("nonterminated character class %.20s", lastre);
+           } else if (bp == buf) {  /* 1st char is special */
+             *bp++ = c;
+           } else if (c == ']') {
+             *bp++ = 0;
+             rlxstr = (uschar *) tostring((char *) buf);
+             if (cflag == 0)  return CCL;
+             else return NCCL;
+           } else *bp++ = c;
+           }
+  }
+}
+
+int cgoto(fa *f, int s, int c)
+{
+  int i, j, k;
+  int *p, *q;
+
+  assert(c == HAT || c < NCHARS);
+  while (f->accept >= maxsetvec) {  /* guessing here! */
+    maxsetvec *= 4;
+    setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+    tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+    if (setvec == 0 || tmpset == 0)
+      overflo("out of space in cgoto()");
+  }
+  for (i = 0; i <= f->accept; i++)
+    setvec[i] = 0;
+  setcnt = 0;
+  /* compute positions of gototab[s,c] into setvec */
+  p = f->posns[s];
+  for (i = 1; i <= *p; i++) {
+    if ((k = f->re[p[i]].ltype) != FINAL) {
+      if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np))
+       || (k == DOT && c != 0 && c != HAT)
+       || (k == ALL && c != 0)
+       || (k == EMPTYRE && c != 0)
+       || (k == CCL && member(c, (char *) f->re[p[i]].lval.up))
+       || (k == NCCL && !member(c, (char *) f->re[p[i]].lval.up) && c != 0 && c != HAT)) {
+        q = f->re[p[i]].lfollow;
+        for (j = 1; j <= *q; j++) {
+          if (q[j] >= maxsetvec) {
+            maxsetvec *= 4;
+            setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
+            tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
+            if (setvec == 0 || tmpset == 0)
+              overflo("cgoto overflow");
+          }
+          if (setvec[q[j]] == 0) {
+            setcnt++;
+            setvec[q[j]] = 1;
+          }
+        }
+      }
+    }
+  }
+  /* determine if setvec is a previous state */
+  tmpset[0] = setcnt;
+  j = 1;
+  for (i = f->accept; i >= 0; i--)
+    if (setvec[i]) {
+      tmpset[j++] = i;
+    }
+  /* tmpset == previous state? */
+  for (i = 1; i <= f->curstat; i++) {
+    p = f->posns[i];
+    if ((k = tmpset[0]) != p[0])
+      goto different;
+    for (j = 1; j <= k; j++)
+      if (tmpset[j] != p[j])
+        goto different;
+    /* setvec is state i */
+    f->gototab[s][c] = i;
+    return i;
+    different:;
+  }
+
+  /* add tmpset to current set of states */
+  if (f->curstat >= NSTATES-1) {
+    f->curstat = 2;
+    f->reset = 1;
+    for (i = 2; i < NSTATES; i++)
+      xfree(f->posns[i]);
+  } else ++(f->curstat);
+  for (i = 0; i < NCHARS; i++)
+    f->gototab[f->curstat][i] = 0;
+  xfree(f->posns[f->curstat]);
+  if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL)
+    overflo("out of space in cgoto");
+
+  f->posns[f->curstat] = p;/*
+   * end main.c
+   */
+
+
+  f->gototab[s][c] = f->curstat;
+  for (i = 0; i <= setcnt; i++)
+    p[i] = tmpset[i];
+  if (setvec[f->accept]) f->out[f->curstat] = 1;
+  else f->out[f->curstat] = 0;
+  return f->curstat;
+}
+
+
+void freefa(fa *f)  /* free a finite automaton */
+{
+  int i;
+
+  if (f == NULL) return;
+  for (i = 0; i <= f->curstat; i++)
+    xfree(f->posns[i]);
+  for (i = 0; i <= f->accept; i++) {
+    xfree(f->re[i].lfollow);
+    if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
+      xfree((f->re[i].lval.np));
+  }
+  xfree(f->restr);
+  xfree(f);
+}
+
+void awk_main(void)
+{
+  const char *fs = NULL;
+  int i;
+
+  if (!(toys.optflags & FLAG_f) && (toys.optc == 0)) {
+    toys.exithelp = 1;
+    error_exit("Provide -f or program");
+  }
+
+  setlocale(LC_CTYPE, "");
+  setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
+
+  if (toys.optflags & FLAG_d)  dbg = 1;
+
+  if (toys.optflags & FLAG_s)  safe = 1;
+
+  cmdname = toys.argv[0];
+  signal(SIGFPE, fpecatch);
+  srand_seed = 1;
+  srand(srand_seed);
+
+  yyin = NULL;
+  symtab = makesymtab(NSYMTAB/NSYMTAB);
+
+  if (toys.optflags & FLAG_F) {  /* -F FS set field separator */
+    if (TT.sep[0] == 't' && TT.sep[1] == 0) fs = "\t";
+    else fs = TT.sep;
+    if (fs == NULL || *fs == '\0') WARNING("field separator FS is empty");
+    else dprintf(("seprator : %s", fs));
+
+  }
+
+  if (toys.optflags & FLAG_v) { /* -v a=1 to be done NOW.  one -v for each */
+    while(TT.vars) {
+      if (isclvar(TT.vars->arg)) setclvar(TT.vars->arg);
+      else FATAL("invalid -v option argument: %s", TT.vars->arg);
+      TT.vars = TT.vars->next;
+    }
+  }
+
+  struct arg_list *backprogfiles = TT.prog_files;
+
+  if (toys.optflags & FLAG_f) { /* -f program filename */
+    while (TT.prog_files) {
+      if (npfile >= MAX_PFILE - 1) FATAL("too many -f options");
+      pfile[npfile++] = TT.prog_files->arg;
+      TT.prog_files = TT.prog_files->next;
+    }
+  } else {  /* no -f; first argument is program */
+    dprintf( ("program = |%s|\n", toys.optargs[0]) );
+    lexprog = toys.optargs[0];
+    toys.optargs++;
+    toys.optc--;
+  }
+
+  TT.prog_files = backprogfiles;
+  recinit(recsize);
+  syminit();
+  compile_time = 1;
+  for(i = 1;i <= toys.optc;i++)
+    toys.argv[i] = toys.optargs[i -1];
+
+  toys.argv[i] = NULL;
+  dprintf( ("argc=%d, argv[0]=%s\n", toys.optc, toys.optargs[0]) );
+  arginit(toys.optc +1, toys.argv);
+  if (!safe) envinit(environ);
+  yyparse();
+  setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
+  if (fs)  *FS = qstring(fs, '\0');
+  dprintf( ("errorflag=%d\n", errorflag) );
+  if (errorflag == 0) {
+    compile_time = 0;
+    run(winner);
+  } else bracecheck();
+}
+
+int pgetc(void)    /* get 1 character from awk program */
+{
+  int c;
+
+  for (;;) {
+    if (yyin == NULL) {
+      if (curpfile >= npfile)  return EOF;
+      if (strcmp(pfile[curpfile], "-") == 0) yyin = stdin;
+      else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
+        FATAL("can't open file %s", pfile[curpfile]);
+      lineno = 1;
+    }
+    if ((c = getc(yyin)) != EOF) return c;
+    if (yyin != stdin) fclose(yyin);
+    yyin = NULL;
+    curpfile++;
+  }
+}
+
+char *cursource(void)  /* current source file name */
+{
+  if (npfile > 0)  return pfile[curpfile];
+  else return NULL;
+}
+
diff --git a/toys/posix/basename.c b/toys/posix/basename.c
new file mode 100644 (file)
index 0000000..c49a5f3
--- /dev/null
@@ -0,0 +1,32 @@
+/* basename.c - Return non-directory portion of a pathname
+ *
+ * Copyright 2012 Tryn Mirell <tryn@mirell.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/basename.html
+
+
+USE_BASENAME(NEWTOY(basename, "<1>2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config BASENAME
+  bool "basename"
+  default y
+  help
+    usage: basename string [suffix]
+
+    Return non-directory portion of a pathname removing suffix
+*/
+
+#include "toys.h"
+
+void basename_main(void)
+{
+  char *base = basename(*toys.optargs), *suffix = toys.optargs[1];
+
+  // chop off the suffix if provided
+  if (suffix) {
+    char *s = base + strlen(base) - strlen(suffix);
+    if (s > base && !strcmp(s, suffix)) *s = 0;
+  }
+
+  puts(base);
+}
diff --git a/toys/posix/cal.c b/toys/posix/cal.c
new file mode 100644 (file)
index 0000000..e4301a6
--- /dev/null
@@ -0,0 +1,132 @@
+/* cal.c - show calendar.
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/cal.html
+
+USE_CAL(NEWTOY(cal, ">2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CAL
+  bool "cal"
+  default y
+  help
+    usage: cal [[month] year]
+    Print a calendar.
+
+    With one argument, prints all months of the specified year.
+    With two arguments, prints calendar for month and year.
+*/
+
+#include "toys.h"
+
+// Write calendar into buffer: each line is 20 chars wide, end indicated
+// by empty string.
+
+static char *calstrings(char *buf, struct tm *tm)
+{
+  char temp[21];
+  int wday, mday, start, len, line;
+
+  // header
+  len = strftime(temp, 21, "%B %Y", tm);
+  len += (20-len)/2;
+  buf += sprintf(buf, "%*s%*s ", len, temp, 20-len, "");
+  buf++;
+  buf += sprintf(buf, "Su Mo Tu We Th Fr Sa ");
+  buf++;
+
+  // What day of the week does this month start on?
+  if (tm->tm_mday>1)
+    start = (36+tm->tm_wday-tm->tm_mday)%7;
+  else start = tm->tm_wday;
+
+  // What day does this month end on?  Alas, libc doesn't tell us...
+  len = 31;
+  if (tm->tm_mon == 1) {
+    int year = tm->tm_year;
+    len = 28;
+    if (!(year & 3) && !((year&100) && !(year&400))) len++;
+  } else if ((tm->tm_mon+(tm->tm_mon>6 ? 1 : 0)) & 1) len = 30;
+
+  for (mday=line=0;line<6;line++) {
+    for (wday=0; wday<7; wday++) {
+      char *pat = "   ";
+      if (!mday ? wday==start : mday<len) {
+        pat = "%2d ";
+        mday++;
+      }
+      buf += sprintf(buf, pat, mday);
+    }
+    buf++;
+  }
+
+  return buf;
+}
+
+void xcheckrange(long val, long low, long high)
+{
+  char *err = "%ld %s than %ld";
+
+  if (val < low) error_exit(err, val, "less", low);
+  if (val > high) error_exit(err, val, "greater", high);
+}
+
+// Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
+// plus 8 lines/month plus 12 months, comes to a bit over 2k of our 4k buffer.
+
+void cal_main(void)
+{
+  struct tm *tm;
+  char *buf = toybuf;
+
+  if (toys.optc) {
+    // Conveniently starts zeroed
+    tm = (struct tm *)toybuf;
+    buf += sizeof(struct tm);
+
+    // Last argument is year, one before that (if any) is month.
+    xcheckrange(tm->tm_year = atol(toys.optargs[--toys.optc]),1,9999);
+    tm->tm_year -= 1900;
+    tm->tm_mday = 1;
+    tm->tm_hour = 12;  // noon to avoid timezone weirdness
+    if (toys.optc) {
+      xcheckrange(tm->tm_mon = atol(toys.optargs[--toys.optc]),1,12);
+      tm->tm_mon--;
+
+    // Print 12 months of the year
+
+    } else {
+      char *bufs[12];
+      int i, j, k;
+
+      for (i=0; i<12; i++) {
+        tm->tm_mon=i;
+        mktime(tm);
+        buf = calstrings(bufs[i]=buf, tm);
+      }
+
+      // 4 rows, 6 lines each, 3 columns
+      for (i=0; i<4; i++) {
+        for (j=0; j<8; j++) {
+          for(k=0; k<3; k++) {
+            char **b = bufs+(k+i*3);
+            *b += printf("%s ", *b);
+          }
+          puts("");
+        }
+      }
+      return;
+    }
+
+    // What day of the week does that start on?
+    mktime(tm);
+
+  } else {
+    time_t now;
+    time(&now);
+    tm = localtime(&now);
+  }
+
+  calstrings(buf, tm);
+  while (*buf) buf += printf("%s\n", buf);
+}
diff --git a/toys/posix/cat.c b/toys/posix/cat.c
new file mode 100644 (file)
index 0000000..78881c4
--- /dev/null
@@ -0,0 +1,37 @@
+/* cat.c - copy inputs to stdout.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/cat.html
+
+USE_CAT(NEWTOY(cat, "u", TOYFLAG_BIN))
+
+config CAT
+  bool "cat"
+  default y
+  help
+    usage: cat [-u] [file...]
+    Copy (concatenate) files to stdout.  If no files listed, copy from stdin.
+    Filename "-" is a synonym for stdin.
+
+    -u Copy one byte at a time (slow).
+*/
+
+#include "toys.h"
+
+static void do_cat(int fd, char *name)
+{
+  int len, size=toys.optflags ? 1 : sizeof(toybuf);
+
+  for (;;) {
+    len = read(fd, toybuf, size);
+    if (len<0) perror_msg("%s",name);
+    if (len<1) break;
+    xwrite(1, toybuf, len);
+  }
+}
+
+void cat_main(void)
+{
+  loopfiles(toys.optargs, do_cat);
+}
diff --git a/toys/posix/chgrp.c b/toys/posix/chgrp.c
new file mode 100644 (file)
index 0000000..0a314c2
--- /dev/null
@@ -0,0 +1,110 @@
+/* chgrp.c - Change user and group ownership
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/chown.html
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/chgrp.html
+ *
+ * TODO: group only one of [HLP]
+
+USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv", TOYFLAG_BIN))
+USE_CHGRP(OLDTOY(chown, chgrp, "<2hPLHRfv", TOYFLAG_BIN))
+
+config CHGRP
+  bool "chgrp/chown"
+  default y
+  help
+    usage: chown [-RHLP] [-fvh] [owner][:group] file...
+    usage: chgrp [-RHLP] [-fvh] group file...
+
+    Change ownership of one or more files.
+
+    -f suppress most error messages.
+    -h change symlinks instead of what they point to
+    -R recurse into subdirectories (implies -h).
+    -H with -R change target of symlink, follow command line symlinks
+    -L with -R change target of symlink, follow all symlinks
+    -P with -R change symlink, do not follow symlinks (default)
+    -v verbose output.
+*/
+
+#define FOR_chgrp
+#include "toys.h"
+
+GLOBALS(
+  uid_t owner;
+  gid_t group;
+  char *owner_name, *group_name;
+  int symfollow;
+)
+
+static int do_chgrp(struct dirtree *node)
+{
+  int fd, ret, flags = toys.optflags;
+
+  // Depth first search
+  if (!dirtree_notdotdot(node)) return 0;
+  if ((flags & FLAG_R) && node->data != -1 && S_ISDIR(node->st.st_mode))
+    return DIRTREE_COMEAGAIN|((flags&FLAG_L) ? DIRTREE_SYMFOLLOW : 0);
+
+  fd = dirtree_parentfd(node);
+  ret = fchownat(fd, node->name, TT.owner, TT.group,
+    (flags&(FLAG_L|FLAG_H)) || !(flags&(FLAG_h|FLAG_R))
+      ? 0 : AT_SYMLINK_NOFOLLOW);
+
+  if (ret || (flags & FLAG_v)) {
+    char *path = dirtree_path(node, 0);
+    if (flags & FLAG_v)
+      xprintf("%s %s%s%s %s\n", toys.which->name,
+        TT.owner_name ? TT.owner_name : "",
+        toys.which->name[2]=='o' && TT.group_name ? ":" : "",
+        TT.group_name ? TT.group_name : "", path);
+    if (ret == -1 && !(toys.optflags & FLAG_f))
+      perror_msg("'%s' to '%s:%s'", path, TT.owner_name, TT.group_name);
+    free(path);
+  }
+  toys.exitval |= ret;
+
+  return 0;
+}
+
+void chgrp_main(void)
+{
+  int ischown = toys.which->name[2] == 'o', hl = toys.optflags&(FLAG_H|FLAG_L);
+  char **s, *own;
+
+  // Distinguish chown from chgrp
+  if (ischown) {
+    char *grp;
+    struct passwd *p;
+
+    own = xstrdup(*toys.optargs);
+    if ((grp = strchr(own, ':')) || (grp = strchr(own, '.'))) {
+      *(grp++) = 0;
+      TT.group_name = grp;
+    }
+    if (*own) {
+      TT.owner_name = own;
+      p = getpwnam(own);
+      // TODO: trailing garbage?
+      if (!p && isdigit(*own)) p=getpwuid(atoi(own));
+      if (!p) error_exit("no user '%s'", own);
+      TT.owner = p->pw_uid;
+      if (CFG_TOYBOX_FREE) free(own);
+    }
+  } else TT.group_name = *toys.optargs;
+
+  if (TT.group_name) {
+    struct group *g;
+    g = getgrnam(TT.group_name);
+    if (!g) g=getgrgid(atoi(TT.group_name));
+    if (!g) error_exit("no group '%s'", TT.group_name);
+    TT.group = g->gr_gid;
+  }
+
+  for (s=toys.optargs+1; *s; s++) {
+    struct dirtree *new = dirtree_add_node(0, *s, hl);
+    if (new) dirtree_handle_callback(new, do_chgrp);
+    else toys.exitval = 1;
+  }
+}
diff --git a/toys/posix/chmod.c b/toys/posix/chmod.c
new file mode 100644 (file)
index 0000000..64369b6
--- /dev/null
@@ -0,0 +1,65 @@
+/* chmod.c - Change file mode bits
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/chmod.html
+
+USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN))
+
+config CHMOD
+  bool "chmod"
+  default y
+  help
+    usage: chmod [-R] MODE FILE...
+
+    Change mode of listed file[s] (recursively with -R).
+
+    MODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]
+
+    Stanzas are applied in order: For each category (u = user,
+    g = group, o = other, a = all three, if none specified default is a),
+    set (+), clear (-), or copy (=), r = read, w = write, x = execute.
+    s = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).
+    suid/sgid: execute as the user/group who owns the file.
+    sticky: can't delete files you don't own out of this directory
+    X = x for directories or if any category already has x set.
+
+    Or MODE can be an octal value up to 7777   ug uuugggooo    top +
+    bit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1   sstrwxrwxrwx    bottom
+
+    Examples:
+    chmod u+w file - allow owner of "file" to write to it.
+    chmod 744 file - user can read/write/execute, everyone else read only
+*/
+
+#define FOR_chmod
+#include "toys.h"
+
+GLOBALS(
+  char *mode;
+)
+
+int do_chmod(struct dirtree *try)
+{
+  mode_t mode;
+
+  if (!dirtree_notdotdot(try)) return 0;
+
+  mode = string_to_mode(TT.mode, try->st.st_mode);
+  if (toys.optflags & FLAG_v) {
+    char *s = dirtree_path(try, 0);
+    printf("chmod '%s' to %04o\n", s, mode);
+    free(s);
+  }
+  wfchmodat(dirtree_parentfd(try), try->name, mode);
+
+  return (toys.optflags & FLAG_R) ? DIRTREE_RECURSE : 0;
+}
+
+void chmod_main(void)
+{
+  TT.mode = *toys.optargs;
+  char **file;
+
+  for (file = toys.optargs+1; *file; file++) dirtree_read(*file, do_chmod);
+}
diff --git a/toys/posix/cksum.c b/toys/posix/cksum.c
new file mode 100644 (file)
index 0000000..9f6aa42
--- /dev/null
@@ -0,0 +1,80 @@
+/* cksum.c - produce crc32 checksum value for each input
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/cksum.html
+
+USE_CKSUM(NEWTOY(cksum, "IPLN", TOYFLAG_BIN))
+
+config CKSUM
+  bool "cksum"
+  default y
+  help
+    usage: cksum [-IPLN] [file...]
+
+    For each file, output crc32 checksum value, length and name of file.
+    If no files listed, copy from stdin.  Filename "-" is a synonym for stdin.
+
+    -L Little endian (defaults to big endian)
+    -P Pre-inversion
+    -I Skip post-inversion
+    -N Do not include length in CRC calculation
+*/
+
+#define FOR_cksum
+#include "toys.h"
+
+GLOBALS(
+  unsigned crc_table[256];
+)
+
+static unsigned cksum_be(unsigned crc, unsigned char c)
+{
+  return (crc<<8)^TT.crc_table[(crc>>24)^c];
+}
+
+static unsigned cksum_le(unsigned crc, unsigned char c)
+{
+  return TT.crc_table[(crc^c)&0xff] ^ (crc>>8);
+}
+
+static void do_cksum(int fd, char *name)
+{
+  unsigned crc = (toys.optflags&4) ? 0xffffffff : 0;
+  uint64_t llen = 0, llen2;
+  unsigned (*cksum)(unsigned crc, unsigned char c);
+
+  cksum = (toys.optflags&2) ? cksum_le : cksum_be;
+  // CRC the data
+
+  for (;;) {
+    int len, i;
+
+    len = read(fd, toybuf, sizeof(toybuf));
+    if (len<0) perror_msg("%s", name);
+    if (len<1) break;
+
+    llen += len;
+    for (i=0; i<len; i++) crc=cksum(crc, toybuf[i]);
+  }
+
+  // CRC the length
+
+  llen2 = llen;
+  if (!(toys.optflags&1)) {
+    while (llen) {
+      crc = cksum(crc, llen);
+      llen >>= 8;
+    }
+  }
+
+  printf("%u %"PRIu64, (toys.optflags&8) ? crc : ~crc, llen2);
+  if (strcmp("-", name)) printf(" %s", name);
+  xputc('\n');
+}
+
+void cksum_main(void)
+{
+  crc_init(TT.crc_table, toys.optflags&2);
+  loopfiles(toys.optargs, do_cksum);
+}
diff --git a/toys/posix/cmp.c b/toys/posix/cmp.c
new file mode 100644 (file)
index 0000000..13990d4
--- /dev/null
@@ -0,0 +1,84 @@
+/* cmp.c - Compare two files.
+ *
+ * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/cmp.html
+
+USE_CMP(NEWTOY(cmp, "<2>2ls", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CMP
+  bool "cmp"
+  default y
+  help
+    usage: cmp [-l] [-s] FILE1 FILE2
+
+    Compare the contents of two files.
+
+    -l show all differing bytes
+    -s silent
+*/
+
+#define FOR_cmp
+#include "toys.h"
+
+GLOBALS(
+  int fd;
+  char *name;
+)
+
+// This handles opening the file and
+
+void do_cmp(int fd, char *name)
+{
+  int i, len1, len2, min_len, size = sizeof(toybuf)/2;
+  long byte_no = 1, line_no = 1;
+  char *buf2 = toybuf+size;
+
+  // First time through, cache the data and return.
+  if (!TT.fd) {
+    TT.name = name;
+    // On return the old filehandle is closed, and this assures that even
+    // if we were called with stdin closed, the new filehandle != 0.
+    TT.fd = dup(fd);
+    return;
+  }
+
+  for (;;) {
+    len1 = readall(TT.fd, toybuf, size);
+    len2 = readall(fd, buf2, size);
+
+    min_len = len1 < len2 ? len1 : len2;
+    for (i=0; i<min_len; i++) {
+      if (toybuf[i] != buf2[i]) {
+        toys.exitval = 1;
+        if (toys.optflags & FLAG_l)
+          printf("%ld %o %o\n", byte_no, toybuf[i], buf2[i]);
+        else {
+          if (!(toys.optflags & FLAG_s)) {
+            printf("%s %s differ: char %ld, line %ld\n",
+              TT.name, name, byte_no, line_no);
+            toys.exitval++;
+          }
+          goto out;
+        }
+      }
+      byte_no++;
+      if (toybuf[i] == '\n') line_no++;
+    }
+    if (len1 != len2) {
+      if (!(toys.optflags & FLAG_s))
+        fprintf(stderr, "cmp: EOF on %s\n", len1 < len2 ? TT.name : name);
+      toys.exitval = 1;
+      break;
+    }
+    if (len1 < 1) break;
+  }
+out:
+  if (CFG_TOYBOX_FREE) close(TT.fd);
+}
+
+void cmp_main(void)
+{
+  loopfiles_rw(toys.optargs, O_RDONLY, 0, toys.optflags&FLAG_s, do_cmp);
+}
+
diff --git a/toys/posix/comm.c b/toys/posix/comm.c
new file mode 100644 (file)
index 0000000..bbdccce
--- /dev/null
@@ -0,0 +1,81 @@
+/* comm.c - select or reject lines common to two files
+ *
+ * Copyright 2012 Ilya Kuzmich <ikv@safe-mail.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/comm.html
+
+// <# and ># take single digit, so 321 define flags
+USE_COMM(NEWTOY(comm, "<2>2321", TOYFLAG_USR|TOYFLAG_BIN))
+
+config COMM
+  bool "comm"
+  default y
+  help
+    usage: comm [-123] FILE1 FILE2
+
+    Reads FILE1 and FILE2, which should be ordered, and produces three text
+    columns as output: lines only in FILE1; lines only in FILE2; and lines
+    in both files. Filename "-" is a synonym for stdin.
+
+    -1 suppress the output column of lines unique to FILE1
+    -2 suppress the output column of lines unique to FILE2
+    -3 suppress the output column of lines duplicated in FILE1 and FILE2
+*/
+
+#define FOR_comm
+#include "toys.h"
+
+static void writeline(const char *line, int col)
+{
+  if (col == 0 && toys.optflags & FLAG_1) return;
+  else if (col == 1) {
+    if (toys.optflags & FLAG_2) return;
+    if (!(toys.optflags & FLAG_1)) putchar('\t');
+  } else if (col == 2) {
+    if (toys.optflags & FLAG_3) return;
+    if (!(toys.optflags & FLAG_1)) putchar('\t');
+    if (!(toys.optflags & FLAG_2)) putchar('\t');
+  }
+  puts(line);
+}
+
+void comm_main(void)
+{
+  int file[2];
+  char *line[2];
+  int i;
+
+  if (toys.optflags == 7) return;
+
+  for (i = 0; i < 2; i++) {
+    file[i] = strcmp("-", toys.optargs[i])
+      ? xopen(toys.optargs[i], O_RDONLY) : 0;
+    line[i] = get_line(file[i]);
+  }
+
+  while (line[0] && line[1]) {
+    int order = strcmp(line[0], line[1]);
+
+    if (order == 0) {
+      writeline(line[0], 2);
+      for (i = 0; i < 2; i++) {
+        free(line[i]);
+        line[i] = get_line(file[i]);
+      }
+    } else {
+      i = order < 0 ? 0 : 1;
+      writeline(line[i], i);
+      free(line[i]);
+      line[i] = get_line(file[i]);
+    }
+  }
+
+  /* print rest of the longer file */
+  for (i = line[0] ? 0 : 1; line[i];) {
+    writeline(line[i], i);
+    free(line[i]);
+    line[i] = get_line(file[i]);
+  }
+
+  if (CFG_TOYBOX_FREE) for (i = 0; i < 2; i--) xclose(file[i]);
+}
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
new file mode 100644 (file)
index 0000000..8f6ab0e
--- /dev/null
@@ -0,0 +1,259 @@
+/* Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/cp.html
+ *
+ * TODO: sHLP
+
+USE_CP(NEWTOY(cp, "<2"USE_CP_MORE("rdavsln")"RHLPfip[-ni]", TOYFLAG_BIN))
+
+config CP
+  bool "cp"
+  default y
+  help
+    usage: cp [-fipRHLP] SOURCE... DEST
+
+    Copy files from SOURCE to DEST.  If more than one SOURCE, DEST must
+    be a directory.
+
+    -f force copy by deleting destination file
+    -i interactive, prompt before overwriting existing DEST
+    -p preserve timestamps, ownership, and permissions
+    -R recurse into subdirectories (DEST must be a directory)
+    -H Follow symlinks listed on command line
+    -L Follow all symlinks
+    -P Do not follow symlinks [default]
+
+config CP_MORE
+  bool "cp -rdavsl options"
+  default y
+  depends on CP
+  help
+    usage: cp [-rdavsl]
+
+    -a same as -dpr
+    -d don't dereference symlinks
+    -l hard link instead of copy
+    -n no clobber (don't overwrite DEST)
+    -r synonym for -R
+    -s symlink instead of copy
+    -v verbose
+*/
+
+#define FOR_cp
+#include "toys.h"
+
+// TODO: PLHlsd
+
+GLOBALS(
+  char *destname;
+  struct stat top;
+)
+
+// Callback from dirtree_read() for each file/directory under a source dir.
+
+int cp_node(struct dirtree *try)
+{
+  int fdout = -1, cfd = try->parent ? try->parent->extra : AT_FDCWD,
+      tfd = dirtree_parentfd(try);
+  unsigned flags = toys.optflags;
+  char *catch = try->parent ? try->name : TT.destname, *err = "%s";
+  struct stat cst;
+
+  if (!dirtree_notdotdot(try)) return 0;
+
+  // If returning from COMEAGAIN, jump straight to -p logic at end.
+  if (S_ISDIR(try->st.st_mode) && try->data == -1) {
+    fdout = try->extra;
+    err = 0;
+  } else {
+
+    // -d is only the same as -r for symlinks, not for directories
+    if (S_ISLNK(try->st.st_mode) & (flags & FLAG_d)) flags |= FLAG_r;
+
+    // Detect recursive copies via repeated top node (cp -R .. .) or
+    // identical source/target (fun with hardlinks).
+    if ((TT.top.st_dev == try->st.st_dev && TT.top.st_ino == try->st.st_ino
+         && (catch = TT.destname))
+        || (!fstatat(cfd, catch, &cst, 0) && cst.st_dev == try->st.st_dev
+         && cst.st_ino == try->st.st_ino))
+    {
+      error_msg("'%s' is '%s'", catch, err = dirtree_path(try, 0));
+      free(err);
+
+      return 0;
+    }
+
+    // Handle -inv
+
+    if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) {
+      char *s;
+
+      if (S_ISDIR(try->st.st_dev)) {
+        error_msg("dir at '%s'", s = dirtree_path(try, 0));
+        free(s);
+        return 0;
+      } else if (flags & FLAG_n) return 0;
+      else if (flags & FLAG_i) {
+        fprintf(stderr, "cp: overwrite '%s'", s = dirtree_path(try, 0));
+        free(s);
+        if (!yesno("", 1)) return 0;
+      }
+    }
+
+    if (flags & FLAG_v) {
+      char *s = dirtree_path(try, 0);
+      printf("cp '%s'\n", s);
+      free(s);
+    }
+
+    // Loop for -f retry after unlink
+    do {
+
+      // directory, hardlink, symlink, mknod (char, block, fifo, socket), file
+
+      // Copy directory
+
+      if (S_ISDIR(try->st.st_mode)) {
+        struct stat st2;
+
+        if (!(flags & (FLAG_a|FLAG_r|FLAG_R))) {
+          err = "Skipped dir '%s'";
+          catch = try->name;
+          break;
+        }
+
+        // Always make directory writeable to us, so we can create files in it.
+        //
+        // Yes, there's a race window between mkdir() and open() so it's
+        // possible that -p can be made to chown a directory other than the one
+        // we created. The closest we can do to closing this is make sure
+        // that what we open _is_ a directory rather than something else.
+
+        if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST)
+          if (-1 != (try->extra = openat(cfd, catch, O_NOFOLLOW)))
+            if (!fstat(try->extra, &st2))
+              if (S_ISDIR(st2.st_mode)) return DIRTREE_COMEAGAIN;
+
+      // Hardlink
+
+      } else if (flags & FLAG_l) {
+        if (!linkat(tfd, try->name, cfd, catch, 0)) err = 0;
+
+      // Copy tree as symlinks. For non-absolute paths this involves
+      // appending the right number of .. entries as you go down the tree.
+
+      } else if (flags & FLAG_s) {
+        char *s;
+        struct dirtree *or;
+        int dotdots = 0;
+
+        s = dirtree_path(try, 0);
+        for (or = try; or->parent; or = or->parent) dotdots++;
+
+        if (*or->name == '/') dotdots = 0;
+        if (dotdots) {
+          char *s2 = xmsprintf("% *c%s", 3*dotdots, ' ', s);
+          free(s);
+          s = s2;
+          while(dotdots--) {
+            memcpy(s2, "../", 3);
+            s2 += 3;
+          }
+        }
+        if (!symlinkat(s, cfd, catch)) {
+          err = 0;
+          fdout = AT_FDCWD;
+        }
+        free(s);
+
+      // Do something _other_ than copy contents of a file?
+      } else if (!S_ISREG(try->st.st_mode)
+                 && (try->parent || (flags & (FLAG_a|FLAG_r))))
+      {
+        int i;
+
+        // make symlink, or make block/char/fifo/socket
+        if (S_ISLNK(try->st.st_mode)
+            ? (0 < (i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf))) &&
+               sizeof(toybuf) > i && !symlinkat(toybuf, cfd, catch))
+            : !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev))
+        {
+          err = 0;
+          fdout = AT_FDCWD;
+        }
+
+      // Copy contents of file.
+      } else {
+        int fdin;
+
+        fdin = openat(tfd, try->name, O_RDONLY);
+        if (fdin < 0) {
+          catch = try->name;
+          break;
+        } else {
+          fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode);
+          if (fdout >= 0) {
+            xsendfile(fdin, fdout);
+            err = 0;
+          }
+          close(fdin);
+        }
+      }
+    } while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0));
+  }
+
+  if (fdout != -1) {
+    if (flags & (FLAG_a|FLAG_p)) {
+      struct timespec times[2];
+
+      // Inability to set these isn't fatal, some require root access.
+
+      times[0] = try->st.st_atim;
+      times[1] = try->st.st_mtim;
+
+      // If we can't get a filehandle to the actual object, use racy functions
+      if (fdout == AT_FDCWD) {
+        fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
+                 AT_SYMLINK_NOFOLLOW);
+        utimensat(cfd, catch, times, AT_SYMLINK_NOFOLLOW);
+        // permission bits already correct for mknod, don't apply to symlink
+      } else {
+        fchown(fdout, try->st.st_uid, try->st.st_gid);
+        futimens(fdout, times);
+        fchmod(fdout, try->st.st_mode);
+      }
+    }
+
+    if (fdout != AT_FDCWD) xclose(fdout);
+  }
+
+  if (err) perror_msg(err, catch);
+  return 0;
+}
+
+void cp_main(void)
+{
+  char *destname = toys.optargs[--toys.optc];
+  int i, destdir = !stat(destname, &TT.top) && S_ISDIR(TT.top.st_mode);
+
+  if (toys.optc>1 && !destdir) error_exit("'%s' not directory", destname);
+
+  if (toys.optflags & (FLAG_a|FLAG_p)) umask(0);
+
+  // Loop through sources
+
+  for (i=0; i<toys.optc; i++) {
+    struct dirtree *new;
+    char *src = toys.optargs[i];
+
+    // Skip nonexistent sources
+    if (!(new = dirtree_add_node(0, src, !(toys.optflags & (FLAG_d|FLAG_a)))))
+      perror_msg("bad '%s'", src);
+    else {
+      if (destdir) TT.destname = xmsprintf("%s/%s", destname, basename(src));
+      else TT.destname = destname;
+      dirtree_handle_callback(new, cp_node);
+      if (destdir) free(TT.destname);
+    }
+  }
+}
diff --git a/toys/posix/cut.c b/toys/posix/cut.c
new file mode 100644 (file)
index 0000000..6f81310
--- /dev/null
@@ -0,0 +1,327 @@
+/* cut.c - Cut from a file.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>, Kyungwan Han <asura321@gamil.com>
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html 
+ *
+USE_CUT(NEWTOY(cut, "b:c:f:d:sn", TOYFLAG_BIN))
+
+config CUT
+  bool "cut"
+  default y
+  help
+    usage: cut OPTION... [FILE]...
+    Print selected parts of lines from each FILE to standard output.
+      -b LIST    select only these bytes from LIST.
+      -c LIST    select only these characters from LIST.
+      -f LIST    select only these fields.
+      -d DELIM  use DELIM instead of TAB for field delimiter.
+      -s    do not print lines not containing delimiters.
+      -n    don't split multibyte characters (Ignored).
+*/
+#define FOR_cut
+#include "toys.h"
+
+typedef struct _slist {
+  int start_position;
+  int end_position;
+  struct _slist *next;
+}SLIST;
+
+GLOBALS(
+  char *delim;
+  char *flist;
+  char *clist;
+  char *blist;
+  struct _slist *slist_head;
+  unsigned nelem;
+)
+
+#define BEGINOFLINE  0
+#define ENDOFLINE INT_MAX
+#define NORANGE -1
+
+static int get_user_options(void);
+void (*do_cut)(int);
+static void do_fcut(int fd);
+static void do_bccut(int fd);
+static void free_list(void);
+
+/*
+ * add items in the slist.
+ */
+static void add_to_list(int start, int end)
+{
+  SLIST *current, *head_ref, *temp1_node;
+
+  head_ref = TT.slist_head;
+  temp1_node = (SLIST *)xzalloc(sizeof(SLIST));
+  temp1_node->start_position = start;
+  temp1_node->end_position = end;
+  temp1_node->next = NULL;
+
+  /* Special case for the head end */
+  if (head_ref == NULL || (head_ref)->start_position >= start) { 
+      temp1_node->next = head_ref; 
+      head_ref = temp1_node;
+  }  
+  else { 
+    /* Locate the node before the point of insertion */   
+    current = head_ref;   
+    while (current->next!=NULL && current->next->start_position < temp1_node->start_position)
+        current = current->next;
+    temp1_node->next = current->next;   
+    current->next = temp1_node;
+  }
+  TT.slist_head = head_ref;
+  return;
+}
+
+/*
+ * parse list and add to slist.
+ */
+static void parse_list(char *list)
+{
+  char *ctoken, *dtoken;
+  int  start = 0, end = 0;
+  while((ctoken = strsep(&list, ",")) != NULL) {
+    if(!ctoken[0]) continue;
+
+    //Get start position.
+    dtoken = strsep(&ctoken, "-");
+    if(!dtoken[0]) start = BEGINOFLINE;
+    else {
+      start = get_int_value(dtoken, 0, INT_MAX);
+      start = (start?(start-1):start);
+    }
+    //Get end position.
+    if(ctoken == NULL) end = NORANGE; //case e.g. 1,2,3
+    else if(!ctoken[0]) end = ENDOFLINE; //case e.g. N-
+    else {//case e.g. N-M
+      end = get_int_value(ctoken, 0, INT_MAX);
+      end = (end?end:ENDOFLINE);
+            end--;
+      if(end == start) end = NORANGE;
+    }
+    add_to_list(start, end);
+    TT.nelem++;
+  }
+  //if list is missing in command line.
+  if(TT.nelem == 0) error_exit("missing positions list:");
+  return;
+}
+
+/*
+ * retrive data from the file/s.
+ */
+static void get_data(void)
+{
+  char **argv = toys.optargs; //file name.
+  toys.exitval = EXIT_SUCCESS;
+
+  if(!*argv) do_cut(0); //for stdin
+  else {
+    for(; *argv; ++argv) {
+      if(strcmp(*argv, "-") == 0) do_cut(0); //for stdin
+      else {
+        int fd = open(*argv, O_RDONLY, 0);
+        if(fd < 0) {//if file not present then continue with other files.
+          perror_msg(*argv);
+          continue;
+        }
+        do_cut(fd);
+        xclose(fd);
+      }
+    }
+  }
+  return;
+}
+
+/*
+ * cut main function.
+*/
+void cut_main(void)
+{
+  char delimiter = '\t'; //default delimiter.
+  int  num_of_options = 0;
+  char *list;
+
+  TT.nelem = 0;
+  TT.slist_head = NULL;
+  //verify the number of options provided by user.
+  num_of_options = get_user_options();
+  if(num_of_options == 0) error_exit("specify a list of bytes, characters, or fields");
+  else if(num_of_options > 1) error_exit("only one type of list may be specified");
+
+  //Get list and assign the function.
+  if(toys.optflags & FLAG_f) {
+    list = TT.flist;
+    do_cut = do_fcut;
+  }
+  else if(toys.optflags & FLAG_c) {
+    list = TT.clist;
+    do_cut = do_bccut;
+  }
+  else {
+    list = TT.blist;
+    do_cut = do_bccut;
+  }
+
+  if(toys.optflags & FLAG_d) {
+    //delimiter must be 1 char.
+    if(TT.delim[0] && TT.delim[1]) perror_exit("the delimiter must be a single character");
+    delimiter = TT.delim[0];
+  }
+
+  if(!(toys.optflags & FLAG_d) && (toys.optflags & FLAG_f)) {
+    TT.delim = (char *)xzalloc(2);
+    TT.delim[0] = delimiter;
+  }
+  
+  //when field is not specified, cutting has some special handling.
+  if(!(toys.optflags & FLAG_f)) {
+    if(toys.optflags & FLAG_s) perror_exit("suppressing non-delimited lines operating on fields");
+    if(delimiter != '\t') perror_exit("an input delimiter may be specified only when operating on fields");
+  }
+
+  parse_list(list);
+  get_data();
+  if(!(toys.optflags & FLAG_d) && (toys.optflags & FLAG_f)) {
+    free(TT.delim);
+    TT.delim = NULL;
+  }
+  free_list();
+  return;
+}
+
+/*
+ * perform cut operation on the given delimiter.
+ */
+static void do_fcut(int fd)
+{
+  char *buff;
+  char *delimiter = TT.delim;
+  while((buff = get_line(fd)) != NULL) {
+    //does line have any delimiter?.
+    if(strrchr(buff, (int)delimiter[0]) == NULL) {
+      //if not then print whole line and move to next line.
+      if(!(toys.optflags & FLAG_s)) xputs(buff);
+      continue;
+    }
+
+    unsigned cpos = 0;
+    int start, ndelimiters = -1;
+    int  nprinted_fields = 0;
+    char *pfield = xzalloc(strlen(buff) + 1);
+    SLIST *temp_node = TT.slist_head;
+
+    if(temp_node != NULL) {
+      //process list on each line.
+      while(cpos < TT.nelem && buff) {
+        if(!temp_node) break;
+        start = temp_node->start_position;
+        do {
+          char *field = NULL;
+          //count number of delimeters per line.
+          while(buff) {
+            if(ndelimiters < start) {
+              ndelimiters++;
+              field = strsep(&buff, delimiter);
+            }
+            else break;
+          }
+          //print field (if not yet printed).
+          if(!pfield[ndelimiters]) {
+            if(ndelimiters == start) {
+              //put delimiter.
+              if(nprinted_fields++ > 0) xputc(delimiter[0]);
+              if(field) fputs(field, stdout);
+              //make sure this field won't print again.
+              pfield[ndelimiters] = (char) 0x23; //put some char at this position.
+            }
+          }
+          start++;
+          if((temp_node->end_position == NORANGE) || (!buff)) break;          
+        }while(start <= temp_node->end_position);
+        temp_node = temp_node->next;
+        cpos++;
+      }
+    }
+    xputc('\n');
+    free(pfield);
+    pfield = NULL;
+  }//End of while loop.
+  return;
+}
+
+/*
+ * perform cut operation char or byte.
+ */
+static void do_bccut(int fd)
+{
+  char *buff;
+  while((buff = get_line(fd)) != NULL) {
+    unsigned cpos = 0;
+    int buffln = strlen(buff);
+    char *pfield = xzalloc(buffln + 1);
+    SLIST *temp_node = TT.slist_head;
+    if(temp_node != NULL) {
+      while(cpos < TT.nelem) {
+        int start;
+        if(!temp_node) break;
+        start = temp_node->start_position;
+        while(start < buffln) {
+          //to avoid duplicate field printing.
+          if(pfield[start]) {
+              if(++start <= temp_node->end_position) continue;
+              temp_node = temp_node->next;
+              break;
+          }
+          else {
+            //make sure this field won't print again.
+            pfield[start] = (char) 0x23; //put some char at this position.
+            xputc(buff[start]);
+          }
+          if(++start > temp_node->end_position) {
+            temp_node = temp_node->next;
+            break;
+          }
+        }
+        cpos++;
+      }
+      xputc('\n');
+    }
+    free(pfield);
+    pfield = NULL;
+  }
+  return;
+}
+
+/* 
+ * used to verify "c", "b" and "f" options are present in command line.
+ * As cut command can only except any of them at a time.
+*/
+static int get_user_options(void)
+{
+  int i = 3, flag = 0;
+  for(; i<6; i++) {//verify 3rd, 4th and 5th bit is set.
+    int mask = 1;
+    mask = mask << i;
+    if(toys.optflags & mask) flag++;
+  }
+  return flag;
+}
+
+/*
+ * free the slist.
+*/
+static void free_list(void)
+{
+  SLIST *temp;
+  while(TT.slist_head != NULL) {
+    temp = TT.slist_head->next;
+    free(TT.slist_head);
+    TT.slist_head = temp;
+  }
+  return;
+}
diff --git a/toys/posix/date.c b/toys/posix/date.c
new file mode 100644 (file)
index 0000000..6875104
--- /dev/null
@@ -0,0 +1,89 @@
+/* date.c - set/get the date
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/date.html
+
+USE_DATE(NEWTOY(date, "r:u", TOYFLAG_BIN))
+
+config DATE
+  bool "date"
+  default y
+  help
+    usage: date [-u] [-r file] [+format] | mmddhhmm[[cc]yy]
+
+    Set/get the current date/time
+*/
+
+#define FOR_date
+#include "toys.h"
+
+GLOBALS(
+  char *file;
+)
+
+void date_main(void)
+{
+  const char *format_string = "%a %b %e %H:%M:%S %Z %Y";
+  time_t now = time(NULL);
+  struct tm tm;
+
+  if (TT.file) {
+    struct stat st;
+
+    xstat(TT.file, &st);
+    now = st.st_mtim.tv_sec;
+  }
+  ((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&now, &tm);
+
+  // Display the date?
+  if (!toys.optargs[0] || toys.optargs[0][0] == '+') {
+    if (toys.optargs[0]) format_string = toys.optargs[0]+1;
+    if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
+      perror_msg("bad format `%s'", format_string);
+
+    puts(toybuf);
+
+  // Set the date
+  } else {
+    struct timeval tv;
+    char *s = *toys.optargs;
+    int len = strlen(s);
+
+    if (len < 8 || len > 12 || (len & 1)) error_msg("bad date `%s'", s);
+
+    // Date format: mmddhhmm[[cc]yy]
+    memset(&tm, 0, sizeof(tm));
+    len = sscanf(s, "%2u%2u%2u%2u", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
+      &tm.tm_min);
+    tm.tm_mon--;
+
+    // If year specified, overwrite one we fetched earlier
+    if (len > 8) {
+      sscanf(s, "%u", &tm.tm_year);
+      if (len == 12) tm.tm_year -= 1900;
+      /* 69-99 = 1969-1999, 0 - 68 = 2000-2068 */
+      else if (tm.tm_year < 69) tm.tm_year += 100;
+    }
+
+    if (toys.optflags & FLAG_u) {
+      // Get the UTC version of a struct tm
+      char *tz = CFG_TOYBOX_FREE ? getenv("TZ") : 0;
+      setenv("TZ", "UTC", 1);
+      tzset();
+      tv.tv_sec = mktime(&tm);
+      if (CFG_TOYBOX_FREE) {
+        if (tz) setenv("TZ", tz, 1);
+        else unsetenv("TZ");
+        tzset();
+      }
+    } else tv.tv_sec = mktime(&tm);
+
+    if (tv.tv_sec == (time_t)-1) error_msg("bad `%s'", toys.optargs[0]);
+    tv.tv_usec = 0;
+    if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
+      perror_msg("bad format `%s'", format_string);
+    puts(toybuf);
+    if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
+  }
+}
diff --git a/toys/posix/dd.c b/toys/posix/dd.c
new file mode 100644 (file)
index 0000000..322f6b6
--- /dev/null
@@ -0,0 +1,875 @@
+/* dd.c - A program to convert and copy a file.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See  http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
+
+USE_DD(NEWTOY(dd, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config DD
+  bool "dd"
+  default y
+  help
+    usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
+        [seek=N] [conv=notrunc|noerror|sync|fsync]
+
+    Options:
+    if=FILE   Read from FILE instead of stdin
+    of=FILE   Write to FILE instead of stdout
+    bs=N    Read and write N bytes at a time
+    ibs=N     Read N bytes at a time
+    obs=N     Write N bytes at a time
+    count=N   Copy only N input blocks
+    skip=N    Skip N input blocks
+    seek=N    Skip N output blocks
+    conv=notrunc  Don't truncate output file
+    conv=noerror  Continue after read errors
+    conv=sync   Pad blocks with zeros
+    conv=fsync  Physically write data out before finishing
+
+    Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),
+    MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)
+    Copy a file, converting and formatting according to the operands.
+*/
+
+/*  $NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $  */                                                            
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define FOR_dd
+#include "toys.h"
+
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define ISCHR     0x01    /* character device (warn on short) */
+#define ISPIPE    0x02    /* pipe (not truncatable) */
+#define ISTAPE    0x04    /* tape (not seekable) */
+#define NOREAD    0x08    /* not readable */
+
+#define SFX_LIST  10
+/* Flags (in ddflags). */
+#define C_ASCII     0x00001
+#define C_BLOCK     0x00002
+#define C_BS        0x00004
+#define C_CBS       0x00008
+#define C_COUNT     0x00010
+#define C_EBCDIC    0x00020
+#define C_FILES     0x00040
+#define C_IBS       0x00080
+#define C_IF        0x00100
+#define C_LCASE     0x00200
+#define C_NOERROR   0x00400
+#define C_NOTRUNC   0x00800
+#define C_OBS       0x01000
+#define C_OF        0x02000
+#define C_SEEK      0x04000
+#define C_SKIP      0x08000
+#define C_SWAB      0x10000
+#define C_SYNC      0x20000
+#define C_UCASE     0x40000
+#define C_UNBLOCK   0x80000
+#define C_OSYNC     0x100000
+#define C_SPARSE    0x200000
+#define C_FSYNC     0x400000
+
+#define OFLAGS    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
+
+/* Input/output stream state. */
+typedef struct {
+  u_char    *db;    /* buffer address */
+  u_char    *dbp;    /* current buffer I/O address */
+  uint64_t  dbcnt;    /* current buffer byte count */
+  int64_t    dbrcnt;    /* last read byte count */
+  uint64_t  dbsz;    /* buffer size */
+  u_int    flags;
+  const char    *name;    /* name */
+  int    fd;    /* file descriptor */
+  uint64_t  offset;    /* # of blocks to skip */
+} IO;
+
+typedef struct {
+  uint64_t  in_full;  /* # of full input blocks */
+  uint64_t  in_part;  /* # of partial input blocks */
+  uint64_t  out_full;  /* # of full output blocks */
+  uint64_t  out_part;  /* # of partial output blocks */
+  uint64_t  trunc;    /* # of truncated records */
+  uint64_t  swab;    /* # of odd-length swab blocks */
+  uint64_t  sparse;    /* # of sparse output blocks */
+  uint64_t  bytes;    /* # of bytes written */
+  struct timeval  start;    /* start time of dd */
+} STAT;
+
+IO in, out;                 /* input/output state */
+STAT st;                    /* statistics */
+void (*cfunc)(void);        /* conversion function */
+uint64_t cpy_cnt ;          /* # of blocks to copy */
+static off_t pending = 0;   /* pending seek if sparse */
+u_int ddflags;              /* conversion options */
+uint64_t cbsz;              /* conversion block size */
+u_int files_cnt = 1;        /* # of files to copy */
+const u_char *ctab;         /* conversion table */
+sigset_t infoset;           /* a set blocking SIGINFO */
+
+struct suffix {                                                                              
+  char name[4];
+  unsigned mult;
+};
+
+static const struct suffix suffixes[] = {
+  { "c", 1 },
+  { "w", 2 },
+  { "b", 512 },    
+  { "kD", 1000 },  
+  { "k", 1024 },   
+  { "K", 1024 },  /* compat with coreutils dd */
+  { "MD", 1000000 }, 
+  { "M", 1048576 },  
+  { "GD", 1000000000 },
+  { "G", 1073741824 }
+};
+
+static const struct conv {
+  const char *name;
+  u_int set, noset;
+  const u_char *ctab;
+} clist[] = {
+  { "fsync",    C_FSYNC,    0, NULL },
+  { "noerror",  C_NOERROR,  0, NULL },
+  { "notrunc",  C_NOTRUNC,  0, NULL },
+  { "sync",     C_SYNC,     0, NULL },
+  /* If you add items to this table, be sure to add the
+   * conversions to the C_BS check in the jcl routine above.
+   */
+};
+
+static int find_suffix(char *arg)
+{
+  int i;
+  for(i = 0; i < SFX_LIST; i++)
+    if(strcmp(arg, suffixes[i].name) == 0) return i;
+  return -1;
+}
+
+static int c_conv(const void *a, const void *b)
+{
+  return (strcmp(((const struct conv *)a)->name,
+    ((const struct conv *)b)->name));
+}
+
+static long long strsuftoll(const char* name, const char* arg, int def, unsigned long long max)
+{
+  long long result;
+  char *endp;
+  int idx = -1;
+  errno = 0;
+
+  if(*arg == '-') error_exit("invalid number: '%s'", arg);
+  result = strtoll(arg, &endp, 10);
+  if(errno == ERANGE || result > max || result < def) perror_exit("invalid number '%s'",arg);
+  if(*endp != '\0') {
+    idx = find_suffix(endp);
+    if(idx == -1) error_exit("dd: invalid number '%s'",arg);
+    result = result* suffixes[idx].mult;
+  }
+  return result;
+}
+
+static void f_bs(char *arg)
+{
+  in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, INT_MAX);
+}
+
+static void f_count(char *arg)
+{
+  cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
+}
+
+static void f_ibs(char *arg)
+{
+  if (!(ddflags & C_BS)) in.dbsz = strsuftoll("input block size", arg, 1, INT_MAX);
+}
+
+static void f_if(char *arg)
+{
+  in.name = arg;
+}
+
+static void f_obs(char *arg)
+{
+  if (!(ddflags & C_BS)) out.dbsz = strsuftoll("output block size", arg, 1, INT_MAX);
+}
+
+static void f_of(char *arg)
+{
+  out.name = arg;
+}
+
+static void f_seek(char *arg)
+{
+  out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
+}
+
+static void f_skip(char *arg)
+{
+  in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
+}
+
+static void f_conv(char *arg)
+{
+  struct conv *cp, tmp;
+
+  while (arg != NULL) {
+    tmp.name = strsep(&arg, ",");
+    if (!(cp = (struct conv *)bsearch(&tmp, clist,
+      sizeof(clist)/sizeof(struct conv), sizeof(struct conv), c_conv)))
+      error_exit("unknown conversion %s", tmp.name);
+    if (ddflags & cp->noset) error_exit("%s: illegal conversion combination", tmp.name);
+
+    ddflags |= cp->set;
+    if (cp->ctab) ctab = cp->ctab;
+  }
+}
+
+static const struct arg {
+  const char *name;
+  void (*f)(char *);
+  u_int set, noset;
+} args[] = {
+   /* the array needs to be sorted by the first column so
+  bsearch() can be used to find commands quickly */
+  { "bs",     f_bs,    C_BS,    C_BS|C_OSYNC  },
+  { "conv",   f_conv,  0,       0             },
+  { "count",  f_count, C_COUNT, C_COUNT       },
+  { "ibs",    f_ibs,   C_IBS,   C_IBS         },
+  { "if",     f_if,    C_IF,    C_IF          },
+  { "obs",    f_obs,   C_OBS,   C_OBS         },
+  { "of",     f_of,    C_OF,    C_OF          },
+  { "seek",   f_seek,  C_SEEK,  C_SEEK        },
+  { "skip",   f_skip,  C_SKIP,  C_SKIP        },
+};
+
+void summary(void)
+{
+  char buf[100];
+  int64_t mS;
+  struct timeval tv;
+
+  (void)gettimeofday(&tv, NULL);
+  mS = tv2mS(tv) - tv2mS(st.start);
+  if (mS == 0) mS = 1;
+  /* Use snprintf(3) so that we don't reenter stdio(3). */
+  (void)snprintf(buf, sizeof(buf),
+    "%llu+%llu records in\n%llu+%llu records out\n",
+    (unsigned long long)st.in_full,  (unsigned long long)st.in_part,
+    (unsigned long long)st.out_full, (unsigned long long)st.out_part);
+  (void)write(STDERR_FILENO, buf, strlen(buf));
+  if (st.swab) {
+    (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
+      (unsigned long long)st.swab,
+      (st.swab == 1) ? "block" : "blocks");
+    (void)write(STDERR_FILENO, buf, strlen(buf));
+  }
+  if (st.trunc) {
+    (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
+      (unsigned long long)st.trunc,
+      (st.trunc == 1) ? "block" : "blocks");
+    (void)write(STDERR_FILENO, buf, strlen(buf));
+  }
+  if (st.sparse) {
+    (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
+      (unsigned long long)st.sparse,
+      (st.sparse == 1) ? "block" : "blocks");
+    (void)write(STDERR_FILENO, buf, strlen(buf));
+  }
+  (void)snprintf(buf, sizeof(buf),
+    "%llu bytes (%sB) transferred in %lu.%03d secs (%sB/sec)\n",
+    (unsigned long long) st.bytes, make_human_readable((unsigned long long)(st.bytes), 0),
+    (long) (mS / 1000),
+    (int) (mS % 1000),
+    (make_human_readable((unsigned long long)(st.bytes * 1000LL/ mS), 0)) );
+  (void)write(STDERR_FILENO, buf, strlen(buf));
+
+  chmod(out.name, 0664);
+}
+
+void terminate(int notused)
+{
+  if(notused == SIGINT) exit(1);
+  exit(0);
+}
+
+/* signal handler for SIGUSR1 */
+void summaryx(int notused)
+{
+  summary();
+}
+
+static void getfdtype(IO *io)
+{
+  struct stat sb;
+
+  if (fstat(io->fd, &sb)) perror_exit("cannot fstat: %s", io->name);
+  if (S_ISCHR(sb.st_mode)) io->flags |= ISCHR;
+  else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+    io->flags |= ISPIPE;    /* XXX fixed in 4.4BSD */
+}
+
+/*
+ * Move the parameter file descriptor to a descriptor that is outside the
+ * stdio descriptor range, if necessary.  This is required to avoid
+ * accidentally outputting completion or error messages into the
+ * output file that were intended for the tty.
+ */
+static int redup_clean_fd(int fd)
+{
+  int newfd;
+
+  if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
+    fd != STDERR_FILENO)
+    /* File descriptor is ok, return immediately. */
+    return fd;
+
+  /*
+   * 3 is the first descriptor greater than STD*_FILENO.  Any
+   * free descriptor valued 3 or above is acceptable...
+   */
+  newfd = fcntl(fd, F_DUPFD, 3);
+  if (newfd < 0) perror_exit("dupfd IO");
+
+  close(fd);
+  return newfd;
+}
+
+/*
+ * A protected against SIGINFO write
+ */
+ssize_t bwrite(int fd, const void *buf, size_t len)
+{
+  sigset_t oset;
+  ssize_t rv;
+  int oerrno;
+
+  (void)sigprocmask(SIG_BLOCK, &infoset, &oset);
+  rv = write(fd, buf, len);
+  oerrno = errno;
+  (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+  errno = oerrno;
+  return (rv);
+}
+
+/*
+ * Position input/output data streams before starting the copy.  Device type
+ * dependent.  Seekable devices use lseek, and the rest position by reading.
+ * Seeking past the end of file can cause null blocks to be written to the
+ * output.
+ */
+void pos_in(void)
+{
+  int bcnt, cnt, nr, warned;
+  /* If not a pipe or tape device, try to seek on it. */
+  if (!(in.flags & (ISPIPE|ISTAPE))) {
+    if (lseek(in.fd,
+      (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+      perror_exit("%s: seek error", in.name);
+    }
+    return;
+    /* NOTREACHED */
+  }
+  /*
+   * Read the data.  If a pipe, read until satisfy the number of bytes
+   * being skipped.  No differentiation for reading complete and partial
+   * blocks for other devices.
+   */
+  for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
+    if ((nr = read(in.fd, in.db, bcnt)) > 0) {
+      if (in.flags & ISPIPE) {
+        if (!(bcnt -= nr)) {
+          bcnt = in.dbsz;
+          --cnt;
+        }
+      } else --cnt;
+      continue;
+    }
+    if (nr == 0) {
+      if (files_cnt > 1) {
+        --files_cnt;
+        continue;
+      }
+      error_exit("skip reached end of input");
+    }
+    /*
+     * Input error -- either EOF with no more files, or I/O error.
+     * If noerror not set die.  POSIX requires that the warning
+     * message be followed by an I/O display.
+     */
+    if (ddflags & C_NOERROR) {
+      if (!warned) {
+        error_msg("%s: error occurred", in.name);
+        warned = 1;
+        summary();
+      }
+      continue;
+    }
+    perror_exit("%s: read error", in.name);
+  }
+}
+
+void pos_out(void)
+{
+  int cnt, n;
+  /*
+   * If not a tape, try seeking on the file.  Seeking on a pipe is
+   * going to fail, but don't protect the user -- they shouldn't
+   * have specified the seek operand.
+   */
+  if (!(out.flags & ISTAPE)) {
+    if (lseek(out.fd,
+      (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) {
+      perror_exit("%s: seek error", out.name);
+    }
+    return;
+  }
+  /* Read it. */
+  for (cnt = 0; cnt < out.offset; ++cnt) {
+    if ((n = read(out.fd, out.db, out.dbsz)) > 0)
+      continue;
+
+    if (n < 0) perror_exit("%s: cannot position by reading", out.name);
+    /*
+     * If reach EOF, fill with NUL characters; first, back up over
+     * the EOF mark.  Note, cnt has not yet been incremented, so
+     * the EOF read does not count as a seek'd block.
+     */
+    while (cnt++ < out.offset)
+      if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz)
+        perror_exit("%s: cannot position by writing", out.name);
+    break;
+  }
+}
+
+static void setup(void)
+{
+  if (in.name == NULL) {
+    in.name = "stdin";
+    in.fd = STDIN_FILENO;
+  } else {
+    in.fd = open(in.name, O_RDONLY, 0);
+    if (in.fd < 0) perror_exit("%s: cannot open for read", in.name);
+    /* Ensure in.fd is outside the stdio descriptor range */
+    in.fd = redup_clean_fd(in.fd);
+  }
+  getfdtype(&in);
+
+  if (files_cnt > 1 && !(in.flags & ISTAPE))
+    error_exit("files is not supported for non-tape devices");
+
+  if (out.name == NULL) {
+    /* No way to check for read access here. */
+    out.fd = STDOUT_FILENO;
+    out.name = "stdout";
+  } else {
+    out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */);
+    /*
+     * May not have read access, so try again with write only.
+     * Without read we may have a problem if output also does
+     * not support seeks.
+     */
+    if (out.fd < 0) {
+      out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */);
+      out.flags |= NOREAD;
+    }
+    if (out.fd < 0) perror_exit("%s: cannot open for write", out.name);
+
+    /* Ensure out.fd is outside the stdio descriptor range */
+    out.fd = redup_clean_fd(out.fd);
+  }
+  getfdtype(&out);
+  /*
+   * Allocate space for the input and output buffers.  If not doing
+   * record oriented I/O, only need a single buffer.
+   */
+  if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
+    in.db = xmalloc(MAX(out.dbsz, in.dbsz));
+    out.db = in.db;
+  } else {
+    in.db = xmalloc((u_int)(MAX(in.dbsz, cbsz) + cbsz));
+    out.db = xmalloc((u_int)(out.dbsz + cbsz));
+  }
+  in.dbp = in.db;
+  out.dbp = out.db;
+  /* Position the input/output streams. */
+  if (in.offset) pos_in();
+  if (out.offset) pos_out();
+  /*
+   * Truncate the output file; ignore errors because it fails on some
+   * kinds of output files, tapes, for example.
+   */
+  if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
+    (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
+  /*
+   * If converting case at the same time as another conversion, build a
+   * table that does both at once.  If just converting case, use the
+   * built-in tables.
+   */
+  (void)gettimeofday(&st.start, NULL);  /* Statistics timestamp. */
+}
+
+void dd_out(int force)
+{
+  static int warned;
+  int64_t cnt, n, nw = 0;
+  u_char *outp;
+  /*
+   * Write one or more blocks out.  The common case is writing a full
+   * output block in a single write; increment the full block stats.
+   * Otherwise, we're into partial block writes.  If a partial write,
+   * and it's a character device, just warn.  If a tape device, quit.
+   *
+   * The partial writes represent two cases.  1: Where the input block
+   * was less than expected so the output block was less than expected.
+   * 2: Where the input block was the right size but we were forced to
+   * write the block in multiple chunks.  The original versions of dd(1)
+   * never wrote a block in more than a single write, so the latter case
+   * never happened.
+   *
+   * One special case is if we're forced to do the write -- in that case
+   * we play games with the buffer size, and it's usually a partial write.
+   */
+  outp = out.db;
+  for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+    for (cnt = n;; cnt -= nw) {
+      if (!force && ddflags & C_SPARSE) {
+        int sparse, i;
+        sparse = 1;  /* Is buffer sparse? */
+        for (i = 0; i < cnt; i++)
+          if (outp[i] != 0) {
+            sparse = 0;
+            break;
+          }
+        if (sparse) {
+          pending += cnt;
+          outp += cnt;
+          nw = 0;
+          break;
+        }
+      }
+      if (pending != 0)
+        if (lseek(out.fd, pending, SEEK_CUR) == -1)
+          perror_exit("%s: seek error creating sparse file", out.name);
+      nw = bwrite(out.fd, outp, cnt);
+      if (nw <= 0) {
+        if (nw == 0) error_exit("%s: end of device", out.name);
+        if (errno != EINTR) perror_exit("%s: write error", out.name);
+        nw = 0;
+      }
+      if (pending) {
+        st.bytes += pending;
+        st.sparse += pending/out.dbsz;
+        st.out_full += pending/out.dbsz;
+        pending = 0;
+      }
+      outp += nw;
+      st.bytes += nw;
+      if (nw == n) {
+        if (n != out.dbsz) ++st.out_part;
+        else ++st.out_full;
+        break;
+      }
+      ++st.out_part;
+      if (nw == cnt) break;
+      if (out.flags & ISCHR && !warned) {
+        warned = 1;
+        error_msg("%s: short write on character device", out.name);
+      }
+      if (out.flags & ISTAPE)
+        error_exit("%s: short write on tape device", out.name);
+    }
+    if ((out.dbcnt -= n) < out.dbsz) break;
+  }
+  /* Reassemble the output block. */
+  if (out.dbcnt) (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+  out.dbp = out.db + out.dbcnt;
+}
+
+static void dd_in(void)
+{
+  int flags;
+  int64_t n;
+
+  for (flags = ddflags;;) {
+    if ((flags & C_COUNT) && (st.in_full + st.in_part) >= cpy_cnt)
+      return;
+    /*
+     * Clear the buffer first if doing "sync" on input.
+     * If doing block operations use spaces.  This will
+     * affect not only the C_NOERROR case, but also the
+     * last partial input block which should be padded
+     * with zero and not garbage.
+     */
+    if (flags & C_SYNC) {
+      if (flags & (C_BLOCK|C_UNBLOCK)) memset(in.dbp, ' ', in.dbsz);
+      else memset(in.dbp, 0, in.dbsz);
+    }
+    n = read(in.fd, in.dbp, in.dbsz);
+    if (n == 0) {
+      in.dbrcnt = 0;
+      return;
+    }
+    /* Read error. */
+    if (n < 0) {
+      /*
+       * If noerror not specified, die.  POSIX requires that
+       * the warning message be followed by an I/O display.
+       */
+      perror_msg("%s: read error", in.name);
+      if (!(flags & C_NOERROR)) exit(1);
+
+      summary();
+      /*
+       * If it's not a tape drive or a pipe, seek past the
+       * error.  If your OS doesn't do the right thing for
+       * raw disks this section should be modified to re-read
+       * in sector size chunks.
+       */
+      if (!(in.flags & (ISPIPE|ISTAPE)) &&
+        lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
+        fprintf(stderr, "%s: seek error: %s\n", in.name, strerror(errno));
+
+      /* If sync not specified, omit block and continue. */
+      if (!(ddflags & C_SYNC)) continue;
+
+      /* Read errors count as full blocks. */
+      in.dbcnt += in.dbrcnt = in.dbsz;
+      ++st.in_full;
+
+    /* Handle full input blocks. */
+    } else if (n == in.dbsz) {
+      in.dbcnt += in.dbrcnt = n;
+      ++st.in_full;
+
+    /* Handle partial input blocks. */
+    } else {
+      /* If sync, use the entire block. */
+      if (ddflags & C_SYNC) in.dbcnt += in.dbrcnt = in.dbsz;
+      else in.dbcnt += in.dbrcnt = n;
+      ++st.in_part;
+    }
+    /*
+     * POSIX states that if bs is set and no other conversions
+     * than noerror, notrunc or sync are specified, the block
+     * is output without buffering as it is read.
+     */
+    if (ddflags & C_BS) {
+      out.dbcnt = in.dbcnt;
+      dd_out(1);
+      in.dbcnt = 0;
+      continue;
+    }
+    in.dbp += in.dbrcnt;
+    (*cfunc)();
+  }
+}
+
+void def_close(void)
+{
+  /* Just update the count, everything is already in the buffer. */
+  if (in.dbcnt) out.dbcnt = in.dbcnt;
+}
+
+/*
+ * def --
+ * Copy input to output.  Input is buffered until reaches obs, and then
+ * output until less than obs remains.  Only a single buffer is used.
+ * Worst case buffer calculation is (ibs + obs - 1).
+ */
+void def(void)
+{
+  uint64_t cnt;
+  u_char *inp;
+  const u_char *t;
+
+  if ((t = ctab) != NULL)
+    for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
+      *inp = t[*inp];
+
+  /* Make the output buffer look right. */
+  out.dbp = in.dbp;
+  out.dbcnt = in.dbcnt;
+
+  if (in.dbcnt >= out.dbsz) {
+    /* If the output buffer is full, write it. */
+    dd_out(0);
+    /*
+     * Ddout copies the leftover output to the beginning of
+     * the buffer and resets the output buffer.  Reset the
+     * input buffer to match it.
+      */
+    in.dbp = out.dbp;
+    in.dbcnt = out.dbcnt;
+  }
+}
+
+/*
+ * Cleanup any remaining I/O and flush output.  If necesssary, output file
+ * is truncated.
+ */
+static void dd_close(void)
+{
+  if (cfunc == def) def_close();
+  /* If there are pending sparse blocks, make sure
+   * to write out the final block un-sparse
+   */
+  if ((out.dbcnt == 0) && pending) {
+    memset(out.db, 0, out.dbsz);
+    out.dbcnt = out.dbsz;
+    out.dbp = out.db + out.dbcnt;
+    pending -= out.dbsz;
+  }
+  if (out.dbcnt) dd_out(1);
+  /*
+   * Reporting nfs write error may be defered until next
+   * write(2) or close(2) system call.  So, we need to do an
+   * extra check.  If an output is stdout, the file structure
+   * may be shared among with other processes and close(2) just
+   * decreases the reference count.
+   */
+  if (out.fd == STDOUT_FILENO && fsync(out.fd) == -1 && errno != EINVAL) {
+    perror_exit("fsync stdout");
+    /* NOTREACHED */
+  }
+  if(ddflags & C_FSYNC)
+    if(fsync(out.fd) < 0) perror_exit("fsync failed for %s", out.name);
+
+  if (close(out.fd) == -1) perror_exit("close");
+}
+
+static int c_arg(const void *a, const void *b)
+{
+  return (strcmp(((const struct arg *)a)->name,
+    ((const struct arg *)b)->name));
+}
+
+/*
+ * args -- parse JCL syntax of dd.
+ */
+void jcl(char **argv)
+{
+  struct arg *ap, tmp;
+  char *oper, *arg;
+
+  in.dbsz = out.dbsz = 512;
+
+  while ((oper = *++argv) != NULL) {
+    if ((arg = strchr(oper, '=')) == NULL) 
+      error_exit("unknown operand %s", oper);
+
+    *arg++ = '\0';
+    if (!*arg) {
+      toys.exithelp = 1;
+      error_exit("");
+    }
+    tmp.name = oper;
+    if (!(ap = (struct arg *)bsearch(&tmp, args,
+            sizeof(args)/sizeof(struct arg), sizeof(struct arg), c_arg)))
+      error_exit("unknown operand %s", tmp.name);
+    if (ddflags & ap->noset)
+      error_exit("%s: illegal argument combination or already set", tmp.name);
+
+    ddflags |= ap->set;
+    ap->f(arg);
+  }
+  /* Final sanity checks. */
+  if (ddflags & C_BS) {
+    /*
+     * Bs is turned off by any conversion -- we assume the user
+     * just wanted to set both the input and output block sizes
+     * and didn't want the bs semantics, so we don't warn.
+     */
+    if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
+          C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
+      ddflags &= ~C_BS;
+      ddflags |= C_IBS|C_OBS;
+    }
+    /* Bs supersedes ibs and obs. */
+    if ((ddflags & C_BS) && (ddflags & (C_IBS|C_OBS)))
+      xprintf("bs supersedes ibs and obs\n");
+  }
+  cfunc = def;
+}
+
+int do_dd(int argc, char *argv[])
+{
+  int ch;
+
+  while ((ch = getopt(argc, argv, "")) != -1) {
+    switch (ch) {
+    default:
+      error_exit("usage: dd [operand ...]\n");
+      /* NOTREACHED */
+    }
+  }
+  argc -= (optind - 1);
+  argv += (optind - 1);
+
+  jcl(argv);
+  setup();
+
+  (void)signal(SIGUSR1, summaryx);
+  (void)signal(SIGINT, terminate);
+  (void)sigemptyset(&infoset);
+  (void)sigaddset(&infoset, SIGUSR1);
+  (void)atexit(summary);
+
+  while (files_cnt--) dd_in();
+
+  dd_close();
+  return 0;
+}
+
+void dd_main(void)
+{
+  int count = 0;
+  while(toys.argv[count]) count++;
+
+  /*Initialize global vars to 0 */
+  memset((void *)&in, 0, sizeof(IO));
+  memset((void *)&out, 0, sizeof(IO));
+  memset((void *)&st, 0, sizeof(STAT));
+
+  toys.exitval = do_dd(count, toys.argv);
+}
diff --git a/toys/posix/df.c b/toys/posix/df.c
new file mode 100644 (file)
index 0000000..9e8570f
--- /dev/null
@@ -0,0 +1,160 @@
+/* df.c - report free disk space.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/df.html
+
+USE_DF(NEWTOY(df, "Pkt*a", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config DF
+  bool "df (disk free)"
+  default y
+  help
+    usage: df [-t type] [FILESYSTEM ...]
+
+    The "disk free" command, df shows total/used/available disk space for
+    each filesystem listed on the command line, or all currently mounted
+    filesystems.
+
+    -t type    Display only filesystems of this type.
+
+config DF_PEDANTIC
+  bool "options -P and -k"
+  default y
+  depends on DF
+  help
+    usage: df [-Pk]
+
+    -P The SUSv3 "Pedantic" option
+
+    Provides a slightly less useful output format dictated by
+    the Single Unix Specification version 3, and sets the
+    units to 512 bytes instead of the default 1024 bytes.
+
+    -k Sets units back to 1024 bytes (the default without -P)
+*/
+
+#define FOR_df
+#include "toys.h"
+
+GLOBALS(
+  struct arg_list *fstype;
+
+  long units;
+)
+
+static void show_mt(struct mtab_list *mt)
+{
+  int len;
+  long long size, used, avail, percent, block;
+  char *device;
+
+  // Return if it wasn't found (should never happen, but with /etc/mtab...)
+  if (!mt) return;
+
+  // If we have -t, skip other filesystem types
+  if (TT.fstype) {
+    struct arg_list *al;
+
+    for (al = TT.fstype; al; al = al->next) 
+      if (!strcmp(mt->type, al->arg)) break;
+
+    if (!al) return;
+  }
+
+  // If we don't have -a, skip synthetic filesystems
+  if (!(toys.optflags & FLAG_a) && !mt->statvfs.f_blocks) return;
+
+  // Figure out how much total/used/free space this filesystem has,
+  // forcing 64-bit math because filesystems are big now.
+  block = mt->statvfs.f_bsize ? mt->statvfs.f_bsize : 1;
+  size = (block * mt->statvfs.f_blocks) / TT.units;
+  used = (block * (mt->statvfs.f_blocks-mt->statvfs.f_bfree)) / TT.units;
+  avail = (block * (getuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree))
+      / TT.units;
+  if (!(used+avail)) percent = 0;
+  else {
+    percent = (used*100)/(used+avail);
+    if (used*100 != percent*(used+avail)) percent++;
+  }
+
+  device = *mt->device == '/' ? realpath(mt->device, NULL) : NULL;
+  if (!device) device = mt->device;
+
+  // Figure out appropriate spacing
+  len = 25 - strlen(device);
+  if (len < 1) len = 1;
+  if (CFG_DF_PEDANTIC && (toys.optflags & FLAG_P)) {
+    xprintf("%s %lld %lld %lld %lld%% %s\n", device, size, used, avail,
+      percent, mt->dir);
+  } else {
+    xprintf("%s% *lld % 10lld % 9lld % 3lld%% %s\n", device, len,
+      size, used, avail, percent, mt->dir);
+  }
+
+  if (device != mt->device) free(device);
+}
+
+void df_main(void)
+{
+  struct mtab_list *mt, *mt2, *mtlist;
+
+  // Handle -P and -k
+  TT.units = 1024;
+  if (CFG_DF_PEDANTIC && (toys.optflags & FLAG_P)) {
+    // Units are 512 bytes if you select "pedantic" without "kilobytes".
+    if ((toys.optflags&(FLAG_P|FLAG_k)) == FLAG_P) TT.units = 512;
+    printf("Filesystem %ld-blocks Used Available Capacity Mounted on\n",
+      TT.units);
+  } else puts("Filesystem\t1K-blocks\tUsed Available Use% Mounted on");
+
+  mtlist = xgetmountlist();
+
+  // If we have a list of filesystems on the command line, loop through them.
+  if (*toys.optargs) {
+    char **next;
+
+    for(next = toys.optargs; *next; next++) {
+      struct stat st;
+
+      // Stat it (complain if we can't).
+      if(stat(*next, &st)) {
+        perror_msg("`%s'", *next);
+        continue;
+      }
+
+      // Find and display this filesystem.  Use _last_ hit in case of
+      // -- bind mounts.
+      mt2 = NULL;
+      for (mt = mtlist; mt; mt = mt->next) {
+        if (st.st_dev == mt->stat.st_dev) {
+          mt2 = mt;
+          break;
+        }
+      }
+      show_mt(mt2);
+    }
+  } else {
+    // Get and loop through mount list.
+
+    for (mt = mtlist; mt; mt = mt->next) {
+      struct mtab_list *mt2, *mt3;
+
+      if (!mt->stat.st_dev) continue;
+
+      // Filter out overmounts.
+      mt3 = mt;
+      for (mt2 = mt->next; mt2; mt2 = mt2->next) {
+        if (mt->stat.st_dev == mt2->stat.st_dev) {
+          // For --bind mounts, take last match
+          if (!strcmp(mt->device, mt2->device)) mt3 = mt2;
+          // Filter out overmounts
+          mt2->stat.st_dev = 0;
+        }
+      }
+      show_mt(mt3);
+    }
+  }
+
+  if (CFG_TOYBOX_FREE) llist_traverse(mtlist, free);
+}
diff --git a/toys/posix/dirname.c b/toys/posix/dirname.c
new file mode 100644 (file)
index 0000000..06470ad
--- /dev/null
@@ -0,0 +1,23 @@
+/* dirname.c - show directory portion of path
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/dirname.html
+
+USE_DIRNAME(NEWTOY(dirname, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config DIRNAME
+  bool "dirname"
+  default y
+  help
+    usage: dirname PATH
+
+    Show directory portion of path.
+*/
+
+#include "toys.h"
+
+void dirname_main(void)
+{
+  puts(dirname(*toys.optargs));
+}
diff --git a/toys/posix/du.c b/toys/posix/du.c
new file mode 100644 (file)
index 0000000..5dc3ee5
--- /dev/null
@@ -0,0 +1,215 @@
+/* du.c - disk usage program.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html
+
+USE_DU(NEWTOY(du, "d#<0hmlcaHkLsx", TOYFLAG_USR|TOYFLAG_BIN))
+
+config DU
+  bool "du"
+  default y
+  help
+    usage: du [-d N] [-askxHLlmc] [file...]
+
+    Estimate file space usage (default in unit of 512 blocks).
+    -a    Show all file sizes
+    -H    Follow symlinks on cmdline
+    -L    Follow all symlinks
+    -k    Show size in units of 1024.
+    -s    Show only the total Size for each file specified
+    -x    Estimate size only on the same device
+    -c    Print total size of all arguments
+    -d N  Limit output to directories (and files with -a) of depth < N
+    -l    Count sizes many times if hard linked
+    -h    Sizes in human readable format (e.g., 1K 243M 2G )
+    -m    Sizes in megabytes
+*/
+
+#define FOR_du
+#include "toys.h"
+
+GLOBALS(
+  long maxdepth;
+  long depth;
+  long *dirsum;
+  long total;
+  dev_t st_dev;
+  struct arg_list *inodes;
+)
+
+typedef struct node_size {
+  struct dirtree *node;
+  long size;
+}node_size;
+
+typedef struct inode_ent {
+  ino_t ino;
+  dev_t dev;
+}inode_ent_t;
+
+/*
+ * Adding '/' to the name if name is '.' or '..'
+ */
+
+char *make_pathproper(char *str)
+{
+  char *path = str;
+  switch(strlen(str)) {
+    case 1:
+      if(str[0] == '.') path = xstrdup("./");
+      break;
+    case 2:
+      if(str[0] == '.' && str[1] == '.') path = xstrdup("../");
+      break;
+    default:
+      break;
+  }
+  return path;
+}
+
+/*
+ * Print the size of the given entry in specified format, default in blocks of 512 bytes
+ */
+void print(long size, char* name)
+{
+  unsigned long long tempsize = (unsigned long long)size * 512;
+  unsigned long unit = 512;
+  char *sizestr = NULL;
+  if(TT.depth > TT.maxdepth) return;
+  if(toys.optflags & FLAG_h) unit = 0;
+  if(toys.optflags & FLAG_k) unit = 1024;
+  if(toys.optflags & FLAG_m) unit = 1024*1024;
+  sizestr =  make_human_readable(tempsize, unit); //make human readable string, depending upon unit size.
+  xprintf("%s\t%s\n",sizestr, name);
+  free(sizestr);
+}
+
+/*
+ * free the inodes which are stored for hard link reference
+ */
+void free_inodes(void *data)
+{
+  void *arg = ((struct arg_list*)data)->arg;
+  if(arg) free(arg);
+  free(data);
+}
+
+/*
+ * allocate and add a node to the list
+ */
+static void llist_add_inode(struct arg_list **old, void *data)
+{
+  struct arg_list *new = xmalloc(sizeof(struct arg_list));
+
+  new->arg = (char*)data;
+  new->next = *old;
+  *old = new;
+}
+
+/*
+ * check if the given stat entry is already there in list or not
+ */
+int is_inode_present(struct stat *st)
+{
+  struct arg_list *temparg = NULL;
+  inode_ent_t *ent = NULL;
+  if(!TT.inodes) return 0;
+  for(temparg = TT.inodes; temparg; temparg = (struct arg_list *)temparg->next) {
+    ent = (inode_ent_t*)temparg->arg;
+    if(ent && ent->ino == st->st_ino && ent->dev == st->st_dev) return 1;
+  }
+  return 0;
+}
+
+/*
+ * Compute the size of the node
+ */
+int do_du(struct dirtree *node)
+{
+  inode_ent_t *ino_details = NULL;
+  node_size *nd = NULL;
+  if(!dirtree_notdotdot(node)) return 0;
+  if((toys.optflags & FLAG_x) && (TT.st_dev != node->st.st_dev)) //if file not on same device, don't count size
+    return DIRTREE_RECURSE;
+
+  if(!(toys.optflags & FLAG_l) && node->st.st_nlink > 1 && !node->extra) { //keeping reference for hard links
+    if(is_inode_present(&node->st)) return DIRTREE_RECURSE;
+    ino_details = xzalloc(sizeof(inode_ent_t));
+    ino_details->ino = node->st.st_ino;
+    ino_details->dev = node->st.st_dev;
+    llist_add_inode(&TT.inodes, (void*)ino_details);
+  }
+
+  if(S_ISDIR(node->st.st_mode)) {
+    if(!(node->extra && (long)((node_size*)(node->extra))->node == (long)node)) {
+      nd = xzalloc(sizeof(node_size));
+      nd->node = node;
+      nd->size = 0;
+      TT.dirsum = (long*)&(nd->size);
+      node->extra = (long)nd;
+      *TT.dirsum = 0;
+      TT.depth++;
+      return (DIRTREE_RECURSE|DIRTREE_COMEAGAIN | ((toys.optflags & FLAG_L) ? DIRTREE_SYMFOLLOW : 0)); //DIRTREE_COMEAGAIN to comeback and print the entry.
+    }
+    else if(node->extra) { //extra is set for a returning DIR entry.
+      long offset = 0;
+      nd = (node_size*)node->extra;
+      offset = nd->size;
+      nd->size += node->st.st_blocks;
+      TT.depth--;
+      if(!(toys.optflags & FLAG_s))
+        print(*TT.dirsum, dirtree_path(node, NULL));
+      if((node->parent) && (node->parent->extra)) {
+        /* when returning from internal directory, get the saved size of the parent and continue from there */
+        nd = (node_size*)node->parent->extra;
+        TT.dirsum = (long*)&(nd->size);
+        *TT.dirsum += offset;
+        *TT.dirsum += node->st.st_blocks;
+        return DIRTREE_RECURSE;
+      }
+      else if(!node->parent) {
+        /*if node has no parent, it means it is the top in the tree, stop recursing here */
+        TT.total += *TT.dirsum;
+        if((toys.optflags & FLAG_s))
+          print(*TT.dirsum, dirtree_path(node, NULL));
+        return 0;
+      }
+    }
+  }
+  else if(!(node->parent)) {
+    /* this is the file specified on cmdline */
+    TT.total += node->st.st_blocks;
+    print(node->st.st_blocks, dirtree_path(node, NULL));
+    return 0;
+  }
+  if(TT.dirsum) *TT.dirsum += node->st.st_blocks;
+  if(toys.optflags & FLAG_a && !(toys.optflags & FLAG_s))
+    print(node->st.st_blocks, dirtree_path(node, NULL));
+  return DIRTREE_RECURSE;
+}
+
+/*
+ * DU utility main function
+ */
+void du_main(void)
+{
+  int symfollow = toys.optflags & (FLAG_H | FLAG_L);
+  TT.total = 0;
+  TT.inodes = NULL;
+
+  if(!(toys.optflags & FLAG_d)) TT.maxdepth = INT_MAX;
+  if(toys.optc == 0) toys.optargs[0] = "./";
+  while(*toys.optargs) {
+    TT.depth = 0;
+    char *path = make_pathproper(*toys.optargs);
+    struct dirtree *root = dirtree_add_node(0, path, symfollow);
+    if(root) {
+      TT.st_dev = root->st.st_dev;
+      dirtree_handle_callback(root, do_du); // recurse thru the DIR children.
+    }
+    toys.optargs++;
+  }
+  if(TT.inodes) llist_traverse(TT.inodes, free_inodes); //free the stored nodes
+  if(toys.optflags & FLAG_c) print(TT.total, "total");
+}
diff --git a/toys/posix/echo.c b/toys/posix/echo.c
new file mode 100644 (file)
index 0000000..28284bf
--- /dev/null
@@ -0,0 +1,90 @@
+/* echo.c - echo supporting -n and -e.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/echo.html
+
+USE_ECHO(NEWTOY(echo, "^?en", TOYFLAG_BIN))
+
+config ECHO
+  bool "echo"
+  default y
+  help
+    usage: echo [-ne] [args...]
+
+    Write each argument to stdout, with one space between each, followed
+    by a newline.
+
+    -n No trailing newline.
+    -e Process the following escape sequences:
+       \\      backslash
+       \0NNN   octal values (1 to 3 digits)
+       \a      alert (beep/flash)
+       \b      backspace
+       \c      stop output here (avoids trailing newline)
+       \f      form feed
+       \n      newline
+       \r      carriage return
+       \t      horizontal tab
+       \v      vertical tab
+       \xHH    hexadecimal values (1 to 2 digits)
+*/
+
+#define FOR_echo
+#include "toys.h"
+
+void echo_main(void)
+{
+  int i = 0, out;
+  char *arg, *from = "\\abfnrtv", *to = "\\\a\b\f\n\r\t\v", *c;
+
+  for (;;) {
+    arg = toys.optargs[i];
+    if (!arg) break;
+    if (i++) xputc(' ');
+
+    // Should we output arg verbatim?
+
+    if (!(toys.optflags & FLAG_e)) {
+      xprintf("%s", arg);
+      continue;
+    }
+
+    // Handle -e
+
+    for (c=arg;;) {
+      if (!(out = *(c++))) break;
+
+      // handle \escapes
+      if (out == '\\' && *c) {
+        int n = 0, slash = *(c++);
+        char *found = strchr(from, slash);
+        if (found) out = to[found-from];
+        else if (slash == 'c') goto done;
+        else if (slash == '0') {
+          out = 0;
+          while (*c>='0' && *c<='7' && n++<3) out = (out*8)+*(c++)-'0';
+        } else if (slash == 'x') {
+          out = 0;
+          while (n++<2) {
+            if (*c>='0' && *c<='9') out = (out*16)+*(c++)-'0';
+            else {
+              int temp = tolower(*c);
+              if (temp>='a' && temp<='f') {
+                out = (out*16)+temp-'a'+10;
+                c++;
+              } else break;
+            }
+          }
+        // Slash in front of unknown character, print literal.
+        } else c--;
+      }
+      xputc(out);
+    }
+  }
+
+  // Output "\n" if no -n
+  if (!(toys.optflags&FLAG_n)) xputc('\n');
+done:
+  xflush();
+}
diff --git a/toys/posix/egrep.c b/toys/posix/egrep.c
new file mode 100644 (file)
index 0000000..506aefa
--- /dev/null
@@ -0,0 +1,46 @@
+/* egrep.c - egrep command
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/grep.html
+
+USE_EGREP(NEWTOY(egrep, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config EGREP
+  bool "egrep"
+  default y
+  depends GREP
+  help
+    usage: egrep [-H] [-h] [-n] [-l] [-L] [-c] [-o] [-q] [-v] [-s] [-r] [-i] [-w] [-m NUMBER] [-A NUMBER] [-B NUMBER] [-C NUMBER] [-e PTRN] [-f FILE]
+
+    A grep program to find extended PATTERN - egrep.
+
+    Mostly used for finding in files.
+*/
+
+#define FOR_egrep
+#include "toys.h"
+
+void egrep_main(void)
+{
+  int count = 0, i;
+  char **myargv = NULL;
+
+  while(toys.argv[count++])
+  {
+    /*Do Nothing */
+  }
+
+  myargv = xzalloc(sizeof(char *) * (count+2)); //2 -- 1 for grep, 1 for -F
+  myargv[0] = xstrdup("grep");
+  myargv[1] = xstrdup("-E");
+
+  i = 1;
+  while(toys.argv[i]) //at 0th index, it is fgrep .. which is omitted
+  {
+    myargv[i + 1] = toys.argv[i];
+    i++;
+  }
+
+  toy_exec(myargv); //execute fgre as grep -F
+}
diff --git a/toys/posix/env.c b/toys/posix/env.c
new file mode 100644 (file)
index 0000000..8f7ccf1
--- /dev/null
@@ -0,0 +1,51 @@
+/* env.c - Set the environment for command invocation.
+ *
+ * Copyright 2012 Tryn Mirell <tryn@mirell.org>
+ *
+ * http://opengroup.org/onlinepubs/9699919799/utilities/env.html
+
+USE_ENV(NEWTOY(env, "^i", TOYFLAG_USR|TOYFLAG_BIN))
+
+config ENV
+  bool "env"
+  default y
+  help
+    usage: env [-i] [NAME=VALUE...] [command [option...]]
+
+    Set the environment for command invocation.
+
+    -i Clear existing environment.
+*/
+
+#include "toys.h"
+
+extern char **environ;
+
+void env_main(void)
+{
+  char **ev;
+  char **command = NULL;
+  char *del = "=";
+
+  if (toys.optflags) clearenv();
+
+  for (ev = toys.optargs; *ev != NULL; ev++) {
+    char *env, *val = NULL;
+
+    env = strtok(*ev, del);
+
+    if (env) val = strtok(NULL, del);
+
+    if (val) setenv(env, val, 1);
+    else {
+      command = ev;
+      break;
+    }
+  }
+
+  if (!command) {
+    char **ep;
+    for (ep = environ; *ep; ep++) xputs(*ep);
+    return;
+  } else xexec(command);
+}
diff --git a/toys/posix/expand.c b/toys/posix/expand.c
new file mode 100644 (file)
index 0000000..7bc3716
--- /dev/null
@@ -0,0 +1,128 @@
+/* expand.c - expands tabs to space
+ *
+ * Copyright 2012 Jonathan Clairembault <jonathan at clairembault dot fr>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expand.html
+
+USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN))
+
+config EXPAND
+  bool "expand"
+  default y
+  help
+    usage: expand [-t tablist] [file...]
+
+    Expand tabs to spaces according to tabstops.
+
+    -t tablist
+
+      Specify tab stops, either a single number instead of the default 8,
+      or a comma separated list of increasing numbers representing tabstop
+      positions (absolute, not increments) with each additional tab beyound
+      that becoming one space.
+*/
+
+#define FOR_expand
+#include "toys.h"
+
+GLOBALS(
+  struct arg_list *tabs;
+
+  unsigned tabcount, *tab;
+)
+
+static void expand_file(int fd, char *name)
+{
+  int i, len, x=0, stop = 0;
+
+  for (;;) {
+    len = read(fd, toybuf, sizeof(toybuf));
+    if (len<0) {
+      perror_msg("%s", name);
+      return;
+    }
+    if (!len) break;
+    for (i=0; i<len; i++) {
+      int width = 1;
+      char c;
+
+      if (CFG_TOYBOX_I18N) {
+        wchar_t blah;
+
+        width = mbrtowc(&blah, toybuf+i, len-i, 0);
+        if (width > 1) {
+          if (width != fwrite(toybuf+i, width, 1, stdout))
+            perror_exit("stdout");
+          i += width-1;
+          x++;
+          continue;
+        } else if (width == -2) break;
+        else if (width == -1) continue;
+      }
+      c = toybuf[i];
+
+      if (c != '\t') {
+        if (EOF == putc(c, stdout)) perror_exit(0);
+
+        if (c == '\b' && x) width = -1;
+        if (c == '\n') {
+          x = stop = 0;
+          continue;
+        }
+      } else {
+        if (TT.tabcount < 2) {
+          width = TT.tabcount ? *TT.tab : 8;
+          width -= x%width;
+        } else while (stop < TT.tabcount) {
+          if (TT.tab[stop] > x) {
+            width = TT.tab[stop] - x;
+            break;
+          } else stop++;
+        }
+        xprintf("%*c", width, ' ');
+      }
+      x += width;
+    }
+  }
+}
+
+// Parse -t options to fill out unsigned array in tablist (if not NULL)
+// return number of entries in tablist
+static int parse_tablist(unsigned *tablist)
+{
+  struct arg_list *tabs;
+  int tabcount = 0;
+
+  for (tabs = TT.tabs; tabs; tabs = tabs->next) {
+    char *s = tabs->arg;
+
+    while (*s) {
+      int count;
+      unsigned x, *t = tablist ? tablist+tabcount : &x;
+
+      if (tabcount >= sizeof(toybuf)/sizeof(unsigned)) break;
+      if (sscanf(s, "%u%n", t, &count) != 1) break;
+      if (tabcount++ && tablist && *(t-1) >= *t) break;
+      s += count;
+      if (*s==' ' || *s==',') s++;
+      else break;
+    }
+    if (*s) error_exit("bad tablist");
+  }
+
+  return tabcount;
+}
+
+void expand_main(void)
+{
+  TT.tabcount = parse_tablist(NULL);
+
+  // Determine size of tablist, allocate memory, fill out tablist
+  if (TT.tabcount) {
+    TT.tab = xmalloc(sizeof(unsigned)*TT.tabcount);
+    parse_tablist(TT.tab);
+  }
+
+  loopfiles(toys.optargs, expand_file);
+  if (CFG_TOYBOX_FREE) free(TT.tab);
+}
diff --git a/toys/posix/false.c b/toys/posix/false.c
new file mode 100644 (file)
index 0000000..73458be
--- /dev/null
@@ -0,0 +1,21 @@
+/* false.c - Return nonzero.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/false.html
+
+USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN))
+
+config FALSE
+  bool "false"
+  default y
+  help
+    Return nonzero.
+*/
+
+#include "toys.h"
+
+void false_main(void)
+{
+  toys.exitval = 1;
+}
diff --git a/toys/posix/fgrep.c b/toys/posix/fgrep.c
new file mode 100644 (file)
index 0000000..2d909f8
--- /dev/null
@@ -0,0 +1,46 @@
+/* fgrep.c - fgrep command
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/grep.html
+
+USE_FGREP(NEWTOY(fgrep, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config FGREP
+  bool "fgrep"
+  default y
+  depends GREP
+  help
+    usage: fgrep [-H] [-h] [-n] [-l] [-L] [-c] [-o] [-q] [-v] [-s] [-r] [-i] [-w] [-m NUMBER] [-A NUMBER] [-B NUMBER] [-C NUMBER] [-e PTRN] [-f FILE]
+
+    A grep program to find fix PATTERN - fgrep.
+
+    Mostly used for finding in files.
+*/
+
+#define FOR_fgrep
+#include "toys.h"
+
+void fgrep_main(void)
+{
+  int count = 0, i;
+  char **myargv = NULL;
+
+  while(toys.argv[count++])
+  {
+    /*Do Nothing */
+  }
+
+  myargv = xzalloc(sizeof(char *) * (count+2)); //2 -- 1 for grep, 1 for -F
+  myargv[0] = xstrdup("grep");
+  myargv[1] = xstrdup("-F");
+
+  i = 1;
+  while(toys.argv[i]) //at 0th index, it is fgrep .. which is omitted
+  {
+    myargv[i + 1] = toys.argv[i];
+    i++;
+  }
+
+  toy_exec(myargv); //execute fgre as grep -F
+}
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
new file mode 100644 (file)
index 0000000..fda3555
--- /dev/null
@@ -0,0 +1,1476 @@
+/* grep.c - A grep world program.
+ *
+ * Copyright 2012 Harvind Singh <harvindsingh1981@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/grep.html
+
+USE_GREP(NEWTOY(grep, "<1aHhnlLcoqvsxriwFEm#A#B#C#e:f:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config GREP
+  bool "grep"
+  default y
+  help
+    usage: grep [-aHhnlLcoqvsxriwFE] [-m NUMBER] [-A NUMBER] [-B NUMBER] [-C NUMBER] PATTERN/ [-e PTRN] [-f FILE] [FILE]...
+
+    Search for PATTERN in FILEs (or stdin)
+
+    -a  Process a binary file as text file.
+    -H  Add 'filename:' prefix
+    -h  Do not add 'filename:' prefix
+    -n  Add 'line_no:' prefix
+    -l  Show only names of files that match
+    -L  Show only names of files that don't match
+    -c  Show only count of matching lines
+    -o  Show only the matching part of line
+    -q  Quiet. Return 0 if PATTERN is found, 1 otherwise
+    -v  Select non-matching lines
+    -s  Suppress open and read errors
+    -r  Recurse
+    -i  Ignore case
+    -w  Match whole words only
+    -x  Match whole lines only
+    -F  PATTERN is a literal (not regexp)
+    -E  PATTERN is an extended regexp
+    -m N  Match up to N times per file
+    -A N  Print N lines of trailing context
+    -B N  Print N lines of leading context
+    -C N  Same as '-A N -B N'
+    -e PTRN Pattern to match
+    -f FILE Read pattern from file
+*/
+/*  $NetBSD: grep.c,v 1.3 2006/05/15 21:12:21 rillig Exp $   */                                                        
+/*
+ * Copyright (c) 1999 James Howard and Dag-Erling CoyAna Smgrav
+ * All rights reserved.
+ * Copyright (c) 1980, 1989, 1993, 1994
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "toys.h"
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <fnmatch.h>
+#undef __USE_FILE_OFFSET64
+#include <fts.h>
+/***********************************HEADER******************************************************************************/
+#include <regex.h>
+#include <stdbool.h>
+#ifdef WITHOUT_NLS
+#define getstr(n)   errstr[n]
+#else
+#include <nl_types.h>
+nl_catd catalog;
+#define getstr(n)   catgets(catalog, 1, n, errstr[n])
+#endif
+
+#define GREP_FIXED    0
+#define GREP_BASIC    1
+#define GREP_EXTENDED 2
+#define BINFILE_BIN   0
+#define BINFILE_SKIP  1
+#define BINFILE_TEXT  2
+#define DIR_READ      0
+#define DIR_SKIP      1
+#define DIR_RECURSE   2
+#define DEV_READ      0
+#define DEV_SKIP      1
+#define LINK_READ     0
+#define LINK_EXPLICIT 1
+#define LINK_SKIP     2
+#define EXCL_PAT      0
+#define INCL_PAT      1
+#define MAX_LINE_MATCHES  32
+
+struct file {
+  int  fd;
+  bool binary;
+};
+
+struct str {
+  off_t   off;
+  size_t  len;
+  char    *dat;
+  char    *file;
+  int     line_no;
+};
+
+struct epat {
+  char *pat;
+  int  mode;
+};
+
+typedef struct {
+  size_t         len;
+  unsigned char  *pattern;
+  int            qsBc[UCHAR_MAX + 1];
+  /* flags */
+  bool bol, eol, reversed, word;
+} fastgrep_t;
+
+/* Flags passed to regcomp() and regexec() */
+int cflags = 0;
+int eflags = REG_STARTEND;
+
+/*
+ * Command-line flags.
+ * Aflag -> -A x: print x lines trailing each match.
+ * Bflag -> -B x: print x lines leading each match.
+ * mcount -> count for -m.
+ * Hflag -> -H: always print file name.
+ * Lflag -> -L: only show names of files with no matches.
+ * bflag -> -b: show block numbers for each match.
+ * cflag -> -c: only show a count of matching lines.
+ * hflag -> -h: don't print filename headers.
+ * iflag -> -i: ignore case.
+ * lflag -> -l: only show names of files with matches.
+ * mflag -> -m x: stop reading the files after x matches.
+ * nflag -> -n: show line numbers in front of matching lines.
+ * oflag -> -o: print only matching part.
+ * qflag -> -q: quiet mode (don't output anything).
+ * sflag -> -s: silent mode (ignore errors).
+ * vflag -> -v: only show non-matching lines.
+ * wflag -> -w: pattern must start and end on word boundaries.
+ * xflag -> -x: pattern must match entire line.
+ * lbflag -> --line-buffered.
+ * nullflag -> --null.
+ * nulldataflag -> --null-data.
+ * dexclude -> --exclude-dir.
+ * dinclude -> --include-dir.
+ * fexclude -> --exclude.
+ * finclude -> --include.
+ * matchall -> Shortcut for matching all cases like empty regex.
+ * notfound -> file not found.
+ */
+unsigned long long Aflag, Bflag, mcount;
+bool Hflag, Lflag, bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
+     qflag, sflag, vflag, wflag, xflag, lbflag, nullflag, nulldataflag,
+     dexclude, dinclude, fexclude, finclude, matchall, notfound;
+
+unsigned char line_sep = '\n';  // 0 for --null-data.
+char *label;// --label.
+const char *color; // --color.
+
+/*
+ * grepbehave -> -EFGP: type of the regex.
+ * binbehave -> -aIU: handling of binary files.
+ * filebehave -> -JZ: normal, gzip or bzip2 file.
+ * devbehave -> -D: handling of devices.
+ * dirbehave -> -dRr: handling of directories.
+ * linkbehave -> -OpS: handling of symlinks.
+ * tail -> lines left to print.
+ */
+int grepbehave = GREP_BASIC;
+int binbehave = BINFILE_BIN;
+int filebehave = 0;
+int devbehave = DEV_READ;
+int dirbehave = DIR_READ;
+int linkbehave = LINK_READ;
+int tail;
+
+/*
+ * Filename exclusion/inclusion patterns.
+ * Searching patterns.
+ */
+unsigned int fpatterns, fpattern_sz, dpatterns, dpattern_sz, patterns, pattern_sz;
+char **pattern;
+struct epat *dpattern, *fpattern;
+regex_t *r_pattern;
+fastgrep_t *fg_pattern;
+
+/* For regex errors  */
+#define RE_ERROR_BUF  512
+char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
+
+/*
+ * Default messags to use when NLS is disabled or no catalogue
+ * is found.
+ */
+const char *errstr[] = {
+  "",
+  /*1*/  "(standard input)",
+  /*2*/  "cannot read bzip2 compressed file",
+  /*3*/  "unknown %s option",
+  /*4*/  "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
+  /*5*/  "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
+  /*6*/  "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
+  /*7*/  "\t[pattern] [file ...]\n",
+  /*8*/  "Binary file %s matches\n",
+  /*9*/  "%s (BSD grep) %s\n",
+};
+
+enum {
+  BIN_OPT = CHAR_MAX + 1,
+  COLOR_OPT,
+  DECOMPRESS_OPT,
+  HELP_OPT,
+  MMAP_OPT,
+  LINEBUF_OPT,
+  LABEL_OPT,
+  R_EXCLUDE_OPT,
+  R_INCLUDE_OPT,
+  R_DEXCLUDE_OPT,
+  R_DINCLUDE_OPT
+};
+
+static const char optstr[] =
+"0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
+
+struct option long_options[] =
+{
+  {"binary-files",        required_argument, NULL, BIN_OPT},
+  {"decompress",          no_argument,       NULL, DECOMPRESS_OPT},
+  {"help",                no_argument,       NULL, HELP_OPT},
+  {"mmap",                no_argument,       NULL, MMAP_OPT},
+  {"line-buffered",       no_argument,       NULL, LINEBUF_OPT},
+  {"label",               required_argument, NULL, LABEL_OPT},
+  {"color",               optional_argument, NULL, COLOR_OPT},
+  {"colour",              optional_argument, NULL, COLOR_OPT},
+  {"exclude",             required_argument, NULL, R_EXCLUDE_OPT},
+  {"include",             required_argument, NULL, R_INCLUDE_OPT},
+  {"exclude-dir",         required_argument, NULL, R_DEXCLUDE_OPT},
+  {"include-dir",         required_argument, NULL, R_DINCLUDE_OPT},
+  {"after-context",       required_argument, NULL, 'A'},
+  {"text",                no_argument,       NULL, 'a'},
+  {"before-context",      required_argument, NULL, 'B'},
+  {"byte-offset",         no_argument,       NULL, 'b'},
+  {"context",             optional_argument, NULL, 'C'},
+  {"count",               no_argument,       NULL, 'c'},
+  {"devices",             required_argument, NULL, 'D'},
+  {"directories",         required_argument, NULL, 'd'},
+  {"extended-regexp",     no_argument,       NULL, 'E'},
+  {"regexp",              required_argument, NULL, 'e'},
+  {"fixed-strings",       no_argument,       NULL, 'F'},
+  {"file",                required_argument, NULL, 'f'},
+  {"basic-regexp",        no_argument,       NULL, 'G'},
+  {"no-filename",         no_argument,       NULL, 'h'},
+  {"with-filename",       no_argument,       NULL, 'H'},
+  {"ignore-case",         no_argument,       NULL, 'i'},
+  {"bz2decompress",       no_argument,       NULL, 'J'},
+  {"files-with-matches",  no_argument,       NULL, 'l'},
+  {"files-without-match", no_argument,       NULL, 'L'},
+  {"max-count",           required_argument, NULL, 'm'},
+  {"line-number",         no_argument,       NULL, 'n'},
+  {"only-matching",       no_argument,       NULL, 'o'},
+  {"quiet",               no_argument,       NULL, 'q'},
+  {"silent",              no_argument,       NULL, 'q'},
+  {"recursive",           no_argument,       NULL, 'r'},
+  {"no-messages",         no_argument,       NULL, 's'},
+  {"binary",              no_argument,       NULL, 'U'},
+  {"unix-byte-offsets",   no_argument,       NULL, 'u'},
+  {"invert-match",        no_argument,       NULL, 'v'},
+  {"word-regexp",         no_argument,       NULL, 'w'},
+  {"line-regexp",         no_argument,       NULL, 'x'},
+  {"null",                no_argument,       NULL, 'Z'},
+  {"null-data",           no_argument,       NULL, 'z'},
+  {NULL,                  no_argument,       NULL, 0}
+};
+
+/*******************************UTIL GLOBAL*********************************/
+static bool first, first_global = true;
+static unsigned long long since_printed;
+
+/*******************************File global*********************************/
+#define MAXBUFSIZ (32 * 1024)
+#define LNBUFBUMP 80
+
+static unsigned char buffer[MAXBUFSIZ], *bufpos, *lnbuf;
+static size_t bufrem, lnbuflen;
+
+/****************queue global***********************************************/
+struct qentry {
+  STAILQ_ENTRY(qentry) list;
+  struct str data;
+};
+static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
+static unsigned long long  count;
+
+/*******************************************************************************************************************/
+static int validate_for_negative(char *arg)
+{
+  while (*arg == ' ') arg++;
+  if (*arg == '-') return -1;
+  return 0;
+}
+
+static inline const char *init_color(char *d)
+{
+  char *c = getenv("GREP_COLOR");
+  return (c ? c : d);
+}
+
+static int get_argc(char **argv)
+{
+  int i = 0;
+  while (argv[i]) i++;
+  return i;
+}
+/*
+ * Safe calloc() for internal use.
+ */
+static void *grep_calloc(size_t nmemb, size_t size)
+{
+  void *ptr = calloc(nmemb, size);
+  if (!ptr) perror_exit("calloc");
+  return (ptr);
+}
+/****************************************************************************************************/
+static inline int grep_refill(struct file *f)
+{
+  bufpos = buffer;
+  bufrem = 0;
+  ssize_t nr = read(f->fd, buffer, MAXBUFSIZ);
+  if (nr < 0) return -1;
+  bufrem = nr;
+  return 0;
+}
+
+static inline int grep_lnbufgrow(size_t newlen)
+{
+  if (lnbuflen < newlen) {
+    lnbuf = xrealloc(lnbuf, newlen);
+    lnbuflen = newlen;
+  }
+  return 0;
+}
+
+static char *grep_fgetln(struct file *f, size_t *lenp)
+{
+  unsigned char *p;
+  char *ret;
+  size_t len, off;
+  ptrdiff_t diff;
+
+  /* Fill the buffer, if necessary */
+  if (!bufrem && grep_refill(f)) goto error;
+  if (!bufrem) { /* Return zero length to indicate EOF */
+    *lenp = 0;
+    return ((char *)bufpos);
+  }
+
+  /* Look for a newline in the remaining part of the buffer */
+  if ((p = memchr(bufpos, line_sep, bufrem))) {
+    ++p; /* advance over newline */
+    ret = (char *)bufpos;
+    len = p - bufpos;
+    bufrem -= len;
+    bufpos = p;
+    *lenp = len;
+    return ret;
+  }
+
+  /* We have to copy the current buffered data to the line buffer */
+  for (len = bufrem, off = 0; ; len += bufrem) {
+    /* Make sure there is room for more data */
+    if (grep_lnbufgrow(len + LNBUFBUMP)) goto error;
+    memcpy(lnbuf + off, bufpos, len - off);
+    off = len;
+    if (grep_refill(f)) goto error;
+    if (!bufrem) break; /* EOF: return partial line */
+    if (!(p = memchr(bufpos, line_sep, bufrem))) continue;
+    /* got it: finish up the line (like code above) */
+    ++p;
+    diff = p - bufpos;
+    len += diff;
+    if (grep_lnbufgrow(len)) goto error;
+    memcpy(lnbuf + off, bufpos, diff);
+    bufrem -= diff;
+    bufpos = p;
+    break;
+  }
+  *lenp = len;
+  return (char *)lnbuf;
+
+error:
+  *lenp = 0;
+  return NULL;
+}
+
+static inline struct file *grep_file_init(struct file *f)
+{
+  /* Fill read buffer, also catches errors early */
+  if (grep_refill(f)) goto error;
+
+  /* Check for binary stuff, if necessary */
+  if (!nulldataflag && binbehave != BINFILE_TEXT && memchr(bufpos, '\0', bufrem))
+    f->binary = true;
+  return f;
+error:
+  xclose(f->fd);
+  free(f);
+  f = NULL;
+  return NULL;
+}
+/*
+ * Opens a file for processing.
+ */
+static struct file *grep_open(char *path)
+{
+  int ret;
+  struct file *f = xzalloc(sizeof *f);
+  if (!path) {/* Processing stdin implies --line-buffered. */
+    lbflag = true;
+    f->fd = STDIN_FILENO;
+  } else {
+    ret = f->fd = open(path, O_RDONLY);
+    if ( ret < 0) {
+      free(f);
+      return NULL;
+    }
+  }
+  return (grep_file_init(f));
+}
+/*
+ * Close the file descriptor and Reset read buffer and line buffer.
+ */
+static void grep_close(struct file *f)
+{
+  xclose(f->fd);
+  bufpos = buffer;
+  bufrem = 0;
+
+  free(lnbuf);
+  lnbuf = NULL;
+  lnbuflen = 0;
+}
+/*
+ * Prints a matching line according to the command line options.
+ */
+static void printline(struct str *line, int sep, regmatch_t *matches, int m)
+{
+  size_t a = 0;
+  int i, n = 0;
+
+  if (!hflag) {
+    if (!nullflag) fputs(line->file, stdout);
+    else {
+      printf("%s", line->file);
+      putchar(0);
+    }
+    ++n;
+  }
+  if (nflag) {
+    if (n > 0) putchar(sep);
+    printf("%d", line->line_no);
+    ++n;
+  }
+  if (bflag) {
+    if (n > 0) putchar(sep);
+    printf("%lld", (long long)line->off);
+    ++n;
+  }
+  if (n) putchar(sep);
+  /* --color and -o */
+  if ((oflag || color) && m > 0) {
+    for (i = 0; i < m; i++) {
+      if (!oflag) fwrite(line->dat + a, matches[i].rm_so - a, 1, stdout);
+      if (color) fprintf(stdout, "\33[%sm\33[K", color);
+      if (nflag && i && oflag) {
+        printf("%d", line->line_no);
+        putchar(sep);
+      }
+      fwrite(line->dat + matches[i].rm_so,
+          matches[i].rm_eo - matches[i].rm_so, 1, stdout);
+      if (color) fprintf(stdout, "\33[m\33[K");
+      a = matches[i].rm_eo;
+      if (oflag) putchar('\n');
+    }
+    if (!oflag) {
+      if (line->len - a > 0) fwrite(line->dat + a, line->len - a, 1, stdout);
+      putchar(line_sep);
+    }
+  } else {
+    fwrite(line->dat, line->len, 1, stdout);
+    putchar(line_sep);
+  }
+}
+/****************************************************************************************************/
+static struct qentry *dequeue(void)
+{
+  struct qentry *item = STAILQ_FIRST(&queue);
+  if (!item) return NULL;
+  STAILQ_REMOVE_HEAD(&queue, list);
+  --count;
+  return item;
+}
+
+static void enqueue(struct str *x)
+{
+  struct qentry *item = xzalloc(sizeof(struct qentry));
+  item->data.dat = xzalloc(sizeof(char) * x->len);
+  item->data.len = x->len;
+  item->data.line_no = x->line_no;
+  item->data.off = x->off;
+  memcpy(item->data.dat, x->dat, x->len);
+  item->data.file = x->file;
+
+  STAILQ_INSERT_TAIL(&queue, item, list);
+
+  if (++count > Bflag) {
+    item = dequeue();
+    if (item && item->data.dat) free(item->data.dat);
+    if (item) free(item);
+  }
+}
+
+static void printqueue(void)
+{
+  struct qentry *item;
+  while ((item = dequeue())) {
+    printline(&item->data, '-', NULL, 0);
+    if (item->data.dat) free(item->data.dat);
+    free(item);
+  }
+}
+
+static void clearqueue(void)
+{
+  struct qentry *item;
+  while ((item = dequeue())) {
+    if (item->data.dat) free(item->data.dat);
+    free(item);
+  }
+}
+/****************************************************************************************************/
+/*
+ * Returns:  i >= 0 on failure (position that it failed)
+ *           -1 on success
+ */
+static inline int grep_cmp(unsigned char *pat, unsigned char *data, size_t len)
+{
+  size_t size;
+  wchar_t *wdata, *wpat;
+  unsigned int i;
+
+  if (iflag) {
+    if ((size = mbstowcs(NULL, (const char *)data, 0)) == ((size_t) - 1))
+      return -1;
+
+    wdata = xmalloc(size * sizeof(wint_t));
+    if (mbstowcs(wdata, (const char *)data, size) == ((size_t) - 1)) {
+      free(wdata);
+      return -1;
+    }
+    if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
+        ((size_t) - 1))
+      return (-1);
+
+    wpat = xmalloc(size * sizeof(wint_t));
+    if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1)) {
+      free(wdata);
+      free(wpat);
+      return -1;
+    }
+    for (i = 0; i < len; i++) {
+      if ((towlower(wpat[i]) == towlower(wdata[i])) ||
+          ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
+        continue;
+      free(wpat);
+      free(wdata);
+      return i;
+    }
+  } else {
+    for (i = 0; i < len; i++) {
+      if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) && pat[i] == '.'))
+        continue;
+      return i;
+    }
+  }
+  return -1;
+}
+
+static int grep_search(fastgrep_t *fg, unsigned char *data, size_t len, regmatch_t *pmatch)
+{
+  unsigned int j;
+  int ret = REG_NOMATCH;
+
+  if (pmatch->rm_so == (ssize_t)len) return ret;
+  if (fg->bol && pmatch->rm_so != 0) {
+    pmatch->rm_so = len;
+    pmatch->rm_eo = len;
+    return ret;
+  }
+  /* No point in going farther if we do not have enough data. */
+  if (len < fg->len) return ret;
+
+  /* Only try once at the beginning or ending of the line. */
+  if (fg->bol || fg->eol) {
+    /* Verify data is >= pattern length before searching on it. */
+    if (len >= fg->len) {
+      /* Determine where in data to start search at. */
+      j = fg->eol ? len - fg->len : 0;
+      if (!((fg->bol && fg->eol) && (len != fg->len)))
+        if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
+          pmatch->rm_so = j;
+          pmatch->rm_eo = j + fg->len;
+          ret = 0;
+        }
+    }
+  } else if (fg->reversed) {
+    /* Quick Search algorithm. */
+    j = len;
+    do {
+      if (grep_cmp(fg->pattern, data + j - fg->len, fg->len) == -1) {
+        pmatch->rm_so = j - fg->len;
+        pmatch->rm_eo = j;
+        ret = 0;
+        break;
+      }
+      /* Shift if within bounds, otherwise, we are done. */
+      if (j == fg->len) break;
+      j -= fg->qsBc[data[j - fg->len - 1]];
+    } while (j >= fg->len);
+  } else {
+    /* Quick Search algorithm. */
+    j = pmatch->rm_so;
+    do {
+      if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
+        pmatch->rm_so = j;
+        pmatch->rm_eo = j + fg->len;
+        ret = 0;
+        break;
+      }
+      /* Shift if within bounds, otherwise, we are done. */
+      if (j + fg->len == len) break;
+      else j += fg->qsBc[data[j + fg->len]];
+    } while (j <= (len - fg->len));
+  }
+  return ret;
+}
+
+static inline void grep_revstr(unsigned char *str, int len)
+{
+  int i, counter = len/2;
+  char c;
+  for (i = 0; i < counter; i++) {
+    c = str[i];
+    str[i] = str[len - i - 1];
+    str[len - i - 1] = c;
+  }
+}
+/****************************************************************************************************/
+/*
+ * Adds a searching pattern to the internal array.
+ */
+static void add_pattern(char *pat, size_t len)
+{
+  /* Check if we can do a shortcut */
+  if (!len || matchall) {
+    matchall = true;
+    return;
+  }
+  /* Increase size if necessary */
+  if (patterns == pattern_sz) {
+    pattern_sz *= 2;
+    pattern = xrealloc(pattern, ++pattern_sz * sizeof(*pattern));
+  }
+  if (len > 0 && pat[len - 1] == '\n') --len;
+  /* pat may not be NULL-terminated */
+  pattern[patterns] = xmalloc(len + 1);
+  memcpy(pattern[patterns], pat, len);
+  pattern[patterns][len] = '\0';
+  ++patterns;
+}
+/*
+ * Adds a file include/exclude pattern to the internal array.
+ */
+static void add_fpattern(char *pat, int mode)
+{
+  /* Increase size if necessary */
+  if (fpatterns == fpattern_sz) {
+    fpattern_sz *= 2;
+    fpattern = xrealloc(fpattern, ++fpattern_sz * sizeof(struct epat));
+  }
+  fpattern[fpatterns].pat = xstrdup(pat);
+  fpattern[fpatterns].mode = mode;
+  ++fpatterns;
+}
+/*
+ * Adds a directory include/exclude pattern to the internal array.
+ */
+static void add_dpattern(char *pat, int mode)
+{
+  /* Increase size if necessary */
+  if (dpatterns == dpattern_sz) {
+    dpattern_sz *= 2;
+    dpattern = xrealloc(dpattern, ++dpattern_sz * sizeof(struct epat));
+  }
+  dpattern[dpatterns].pat = xstrdup(pat);
+  dpattern[dpatterns].mode = mode;
+  ++dpatterns;
+}
+/*
+ * Reads searching patterns from a file and adds them with add_pattern().
+ */
+static void read_patterns(char *fn)
+{
+  char *line = NULL;
+  size_t len = 0;
+  ssize_t rlen;
+  FILE *f = fopen(fn, "r");
+
+  if (!f) perror_exit("%s", fn);
+  while ((rlen = getline(&line, &len, f)) != -1)
+    add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
+  free(line);
+
+  if (ferror(f)) perror_exit("%s", fn);
+  fclose(f);
+}
+
+/***************************************************************************************/
+static bool file_matching(char *fname)
+{
+  char *fname_base, *fname_copy;
+  unsigned int i;
+  bool ret = finclude ? false : true;
+
+  fname_copy = xstrdup(fname);
+  fname_base = basename(fname_copy);
+
+  for (i = 0; i < fpatterns; ++i) {
+    if (!fnmatch(fpattern[i].pat, fname, 0) ||
+        !fnmatch(fpattern[i].pat, fname_base, 0)) {
+      if (fpattern[i].mode == EXCL_PAT) return false;
+      else ret = true;
+    }
+  }
+  free(fname_copy);
+  return ret;
+}
+
+static inline bool dir_matching(char *dname)
+{
+  unsigned int i;
+  bool ret = dinclude ? false : true;
+
+  for (i = 0; i < dpatterns; ++i) {
+    if (dname && !fnmatch(dname, dpattern[i].pat, 0)) {
+      if (dpattern[i].mode == EXCL_PAT) return false;
+      else ret = true;
+    }
+  }
+  return ret;
+}
+/*
+ * Processes a line comparing it with the specified patterns.  Each pattern
+ * is looped to be compared along with the full string, saving each and every
+ * match, which is necessary to colorize the output and to count the
+ * matches.  The matching lines are passed to printline() to display the
+ * appropriate output.
+ */
+static int procline(struct str *l, int nottext)
+{
+#define iswword(x)  (iswalnum((x)) || (x) == L'_')
+  regmatch_t matches[MAX_LINE_MATCHES], pmatch;
+  size_t st = 0;
+  unsigned int i;
+  int c = 0, m = 0, r = 0;
+
+  if (!matchall) {
+    while (st <= l->len) {/* Loop to process the whole line */
+      pmatch.rm_so = st;
+      pmatch.rm_eo = l->len;
+      for (i = 0; i < patterns; i++) {/* Loop to compare with all the patterns */
+        if (fg_pattern[i].pattern) {
+          r = grep_search(&fg_pattern[i], (unsigned char *)l->dat, l->len, &pmatch);
+          r = (r == 0) ? 0 : REG_NOMATCH;
+          st = pmatch.rm_eo;
+        } else {
+          r = regexec(&r_pattern[i], l->dat, 1, &pmatch, eflags);
+          r = (r == 0) ? 0 : REG_NOMATCH;
+          st = pmatch.rm_eo;
+        }
+        if (r == REG_NOMATCH) continue;
+
+        /* Check for full match */
+        if (!r && xflag)
+          if (pmatch.rm_so || (size_t)pmatch.rm_eo != l->len) r = REG_NOMATCH;
+
+        /* Check for whole word match */
+        if (!r && fg_pattern[i].word ) {
+          wint_t wbegin, wend;
+
+          wbegin = wend = L' ';
+          if (pmatch.rm_so) wbegin = l->dat[pmatch.rm_so - 1];
+          if ((size_t)pmatch.rm_eo != l->len) wend = l->dat[pmatch.rm_eo];
+          if (iswword(wbegin) || iswword(wend)) r = REG_NOMATCH;
+        }
+        if (!r) {
+          if (!m) c++;
+          if (m < MAX_LINE_MATCHES) matches[m++] = pmatch;
+          /* matches - skip further patterns */
+          if ((color && !oflag) || qflag || lflag) break;
+        }
+      }
+
+      if (vflag) {
+        c = !c;
+        break;
+      }
+      /* One pass if we are not recording matches */
+      if ((color && !oflag) || qflag || lflag) break;
+      if (st == (size_t)pmatch.rm_so) break; /* No matches */
+    }
+  } else c = !vflag;
+
+  /* Binary file */
+  if (c && binbehave == BINFILE_BIN && nottext) return c;
+
+  /* Dealing with the context */
+  if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
+    if (c) {
+      if ((Aflag || Bflag) && !first_global && (first || since_printed > Bflag))
+        printf("--\n");
+      tail = Aflag;
+      if (Bflag > 0) printqueue();
+      printline(l, ':', matches, m);
+    } else {
+      printline(l, '-', matches, m);
+      tail--;
+    }
+    first = false;
+    first_global = false;
+    since_printed = 0;
+  } else {
+    if (Bflag) enqueue(l);
+    since_printed++;
+  }
+  return c;
+#undef iswword
+}
+/*
+ * Opens a file and processes it.  Each file is processed line-by-line
+ * passing the lines to procline().
+ */
+static int procfile(char *fn)
+{
+  struct file *f;
+  struct stat sb;
+  struct str ln;
+  mode_t s;
+  int c, t;
+
+  if (mflag && (mcount <= 0)) return 0;
+  if (!strcmp(fn, "-")) {
+    fn = (label ? label : getstr(1));
+    f = grep_open(NULL);
+  } else {
+    if (!stat(fn, &sb)) {
+      /* Check if we need to process the file */
+      s = sb.st_mode & S_IFMT;
+      if (s == S_IFDIR || dirbehave == DIR_SKIP ) {
+        if (Lflag) printf("%s%c", fn, line_sep);
+        return 0;
+      }
+      if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK || s == S_IFSOCK)
+          && devbehave == DEV_SKIP)
+        return 0;
+    }
+    f = grep_open(fn);
+  }
+  if (!f) {
+    if (!sflag) perror_msg("%s", fn);
+    if (errno == ENOENT) notfound = true;
+    return 0;
+  }
+  ln.file = xmalloc(strlen(fn) + 1);
+  strcpy(ln.file, fn);
+  ln.line_no = ln.len = tail = 0;
+  ln.off = -1;
+
+  for (first = true, c = 0;  !c || !(lflag || qflag ); ) {
+    ln.off += ln.len + 1;
+    if (!(ln.dat = grep_fgetln(f, &ln.len)) || !(ln.len)) {
+      if (!(ln.line_no) && matchall) exit(0);
+      else break;
+    }
+    if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep) --ln.len;
+    ln.line_no++;
+
+    /* Return if we need to skip a binary file */
+    if (f->binary && binbehave == BINFILE_SKIP) {
+      grep_close(f);
+      free(ln.file);
+      free(f);
+      ln.file = NULL;
+      f = NULL;
+      return 0;
+    }
+    /* Process the file line-by-line */
+    t = procline(&ln, f->binary);
+    c += t;
+
+    /* Count the matches if we have a match limit */
+    if (mflag) {
+      if ((mcount -c) <= 0) break;
+    }
+  }
+  if (Bflag > 0) clearqueue();
+  grep_close(f);
+
+  if (cflag) {
+    if (!hflag) printf("%s:", ln.file);
+    printf("%u%c", c, line_sep);
+  }
+  if (lflag && !qflag && c) printf("%s%c", fn, line_sep);
+  if (Lflag && !qflag && !c) printf("%s%c", fn, line_sep);
+  if (c && !cflag && !lflag && !Lflag && binbehave == BINFILE_BIN
+      && f->binary && !qflag)
+    printf(getstr(8), fn);
+
+  free(ln.file);
+  free(f);
+  return c;
+}
+/*
+ * Processes a directory when a recursive search is performed with
+ * the -R option.  Each appropriate file is passed to procfile().
+ */
+static int grep_tree(char **argv)
+{
+  FTS *fts;
+  FTSENT *p;
+  char *d, *dir = NULL;
+  int c = 0, fts_flags = 0;
+  bool ok;
+
+  switch(linkbehave) {
+    case LINK_EXPLICIT:
+      fts_flags = FTS_COMFOLLOW;
+      break;
+    case LINK_SKIP:
+      fts_flags = FTS_PHYSICAL;
+      break;
+    default:
+      fts_flags = FTS_LOGICAL;
+      break;
+  }
+
+  fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
+
+  if (!(fts = fts_open(argv, fts_flags, NULL))) perror_exit("fts_open");
+  while ((p = fts_read(fts))) {
+    switch (p->fts_info) {
+      case FTS_DNR:/* FALLTHROUGH */
+      case FTS_ERR:
+        error_exit("%s: %s", p->fts_path, strerror(p->fts_errno));
+        break;
+      case FTS_D: /* FALLTHROUGH */
+      case FTS_DP:
+        break;
+      case FTS_DC: /* Print a warning for recursive directory loop */
+        error_msg("warning: %s: recursive directory loop", p->fts_path);
+        break;
+      default: /* Check for file exclusion/inclusion */
+        ok = true;
+        if (dexclude || dinclude) {
+          if ((d = strrchr(p->fts_path, '/'))) {
+            dir = xmalloc(sizeof(char) * (d - p->fts_path + 1));
+            memcpy(dir, p->fts_path, d - p->fts_path);
+            dir[d - p->fts_path] = '\0';
+          }
+          ok = dir_matching(dir);
+          if(dir) free(dir);
+        }
+        if (fexclude || finclude) ok &= file_matching(p->fts_path);
+        if (ok) c += procfile(p->fts_path);
+        break;
+    }
+  }
+  fts_close(fts);
+  return c;
+}
+
+/**************************************************************************************************/
+static void fgrepcomp(fastgrep_t *fg, char *pat)
+{
+  unsigned int i;
+  /* Initialize. */
+  fg->len = strlen(pat);
+  fg->bol = fg->eol = fg->reversed = false;
+  fg->pattern = (unsigned char *)xstrdup(pat);
+
+  /* Preprocess pattern. */
+  for (i = 0; i <= UCHAR_MAX; i++) fg->qsBc[i] = fg->len;
+
+  wchar_t *wpat;
+  size_t size;
+
+  if(iflag) {
+    if ((size = mbstowcs(NULL, (const char *)pat, 0)) == ((size_t) - 1))
+      goto out;
+
+    wpat = xmalloc(size * sizeof(wint_t));
+    if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1)) {
+      free(wpat);
+      goto out; 
+    }            
+    for (i = 0; i < fg->len; i++) {
+      wchar_t ch = towlower(wpat[i]);
+      fg->qsBc[ch] = fg->len - i;
+      ch = towupper(wpat[i]);
+      fg->qsBc[ch] = fg->len - i;
+    }
+  }
+  else {
+out:
+    for (i = 1; i < fg->len; i++) fg->qsBc[fg->pattern[i]] = fg->len - i;
+  }
+}
+/*
+ * Returns: -1 on failure, 0 on success
+ */
+static int fastcomp(fastgrep_t *fg, char *pat)
+{
+  unsigned int i;
+  int firstHalfDot, firstLastHalfDot, hasDot, lastHalfDot, shiftPatternLen;
+
+  /* Initialize. */
+  firstHalfDot = firstLastHalfDot = -1;
+  hasDot = lastHalfDot = 0;
+
+  fg->len = strlen(pat);
+  fg->bol = fg->eol = fg->reversed = false;
+  fg->word = wflag;
+
+  /* Remove end-of-line character ('$'). */
+  if (fg->len > 0 && pat[fg->len - 1] == '$') {
+    fg->eol = true;
+    fg->len--;
+  }
+
+  /* Remove beginning-of-line character ('^'). */
+  if (pat[0] == '^') {
+    fg->bol = true;
+    fg->len--;
+    pat++;
+  }
+  if (fg->eol && fg->bol && !fg->len) return -1;
+
+  if (fg->len >= 14 &&
+      !memcmp(pat, "[[:<:]]", 7) &&
+      !memcmp(pat + fg->len - 7, "[[:>:]]", 7)) {
+    fg->len -= 14;
+    pat += 7;
+    /* Word boundary is handled separately in util.c */
+    fg->word = true;
+  }
+
+  /*
+   * pat has been adjusted earlier to not include '^', '$' or
+   * the word match character classes at the beginning and ending
+   * of the string respectively.
+   */
+  fg->pattern = xmalloc(fg->len + 1);
+  memcpy(fg->pattern, pat, fg->len);
+  fg->pattern[fg->len] = '\0';
+
+  /* Look for ways to cheat...er...avoid the full regex engine. */
+  for (i = 0; i < fg->len; i++) {
+    /* Can still cheat? */
+    if (fg->pattern[i] == '.') {
+      hasDot = i;
+      if (i < fg->len / 2) {
+        /* Closest dot to the beginning */
+        if (firstHalfDot < 0) firstHalfDot = i;
+      } else {
+        /* Closest dot to the end of the pattern. */
+        lastHalfDot = i;
+        if (firstLastHalfDot < 0) firstLastHalfDot = i;
+      }
+    } else {
+      /* Free memory and let others know this is empty. */
+      free(fg->pattern);
+      fg->pattern = NULL;
+      return -1;
+    }
+  }
+
+  // Determine if a reverse search would be faster based on the placement of the dots.
+  if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
+        ((lastHalfDot) && ((firstHalfDot < 0) ||
+          ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
+      !oflag && !color) {
+    fg->reversed = true;
+    hasDot = fg->len - (firstHalfDot < 0 ? firstLastHalfDot : firstHalfDot) - 1;
+    grep_revstr(fg->pattern, fg->len);
+  }
+
+  /*
+   * Normal Quick Search would require a shift based on the position the
+   * next character after the comparison is within the pattern.  With
+   * wildcards, the position of the last dot effects the maximum shift
+   * distance.
+   * The closer to the end the wild card is the slower the search.  A
+   * reverse version of this algorithm would be useful for wildcards near
+   * the end of the string.
+   *
+   * Examples:
+   * Pattern  Max shift
+   * -------  ---------
+   * this    5
+   * .his    4
+   * t.is    3
+   * th.s    2
+   * thi.    1
+   */
+
+  /* Adjust the shift based on location of the last dot ('.'). */
+  shiftPatternLen = fg->len - hasDot;
+
+  /* Preprocess pattern. */
+  for (i = 0; i <= (signed)UCHAR_MAX; i++) fg->qsBc[i] = shiftPatternLen;
+  for (i = hasDot + 1; i < fg->len; i++) fg->qsBc[fg->pattern[i]] = fg->len - i;
+
+  // Put pattern back to normal after pre-processing to allow for easy comparisons later.
+  if (fg->reversed) grep_revstr(fg->pattern, fg->len);
+  return 0;
+}
+
+/********************************************************************************************************************************/
+void grep_main(void)
+{
+  char **aargv, **eargv, *eopts, *ep;
+  unsigned long long l;
+  unsigned int aargc, eargc, i, j;
+  int c, lastc, needpattern, newarg, prevoptind, argc;
+
+  toys.exitval = 2;
+  setlocale(LC_ALL,"");
+#ifndef WITHOUT_NLS
+  catalog = catopen("grep", NL_CAT_LOCALE);
+#endif
+  argc = get_argc(toys.argv);
+
+  lastc = '\0';
+  newarg = prevoptind = needpattern = 1;
+
+  eopts = getenv("GREP_OPTIONS");
+  /* support for extra arguments in GREP_OPTIONS */
+  eargc = 0;
+  if (eopts) {
+    char *str;
+    /* make an estimation of how many extra arguments we have */
+    for (j = 0; j < strlen(eopts); j++)
+      if (eopts[j] == ' ') eargc++;
+    eargv = (char **)xmalloc(sizeof(char *) * (eargc + 1));
+    eargc = 0;
+    /* parse extra arguments */
+    while ((str = strsep(&eopts, " "))) eargv[eargc++] = xstrdup(str);
+    aargv = (char **)grep_calloc(eargc + argc + 1, sizeof(char *));
+
+    aargv[0] = toys.argv[0];
+    for (i = 0; i < eargc; i++) aargv[i + 1] = eargv[i];
+    for (j = 1; j < (unsigned int)argc; j++, i++) aargv[i+1] = toys.argv[j];
+    aargc = eargc + argc;
+  } else {
+    aargv = toys.argv;
+    aargc = argc;
+  }
+
+  while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != -1)) {
+    switch (c) {
+      case '0': /*FALL_THROUGH*/
+      case '1': /*FALL_THROUGH*/
+      case '2': /*FALL_THROUGH*/
+      case '3': /*FALL_THROUGH*/
+      case '4': /*FALL_THROUGH*/
+      case '5': /*FALL_THROUGH*/
+      case '6': /*FALL_THROUGH*/
+      case '7': /*FALL_THROUGH*/
+      case '8': /*FALL_THROUGH*/
+      case '9':
+        if (newarg || !isdigit(lastc)) Aflag = 0;
+        else if (Aflag > LLONG_MAX / 10) {
+          errno = ERANGE;
+          error_exit(NULL);
+        }
+        Aflag = Bflag = (Aflag * 10) + (c - '0');
+        break;
+      case 'C': /* FALLTHROUGH */
+        if (!optarg) {
+          Aflag = Bflag = 2;
+          break;
+        }
+      case 'A': /* FALLTHROUGH */
+      case 'B':
+        if (validate_for_negative(optarg)) error_exit("Invalid argument %s", optarg);
+        errno = 0;
+        l = strtoull(optarg, &ep, 10);
+        if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
+            ((errno == EINVAL) && (l == 0))) error_exit("Invalid argument '%s'", optarg);
+        else if (ep[0] != '\0') {
+          errno = EINVAL;
+          error_exit(NULL);
+        }
+        if (c == 'A') Aflag = l;
+        else if (c == 'B') Bflag = l;
+        else Aflag = Bflag = l;
+        break;
+      case 'a':
+        binbehave = BINFILE_TEXT;
+        break;
+      case 'b':
+        bflag = true;
+        break;
+      case 'c':
+        cflag = true;
+        break;
+      case 'D':
+        if (!strcasecmp(optarg, "skip")) devbehave = DEV_SKIP;
+        else if (!strcasecmp(optarg, "read")) devbehave = DEV_READ;
+        else error_exit("%s %s", getstr(3), "--devices");
+        break;
+      case 'd':
+        if (!strcasecmp("recurse", optarg)) {
+          Hflag = true;
+          dirbehave = DIR_RECURSE;
+        } else if (!strcasecmp("skip", optarg)) dirbehave = DIR_SKIP;
+        else if (!strcasecmp("read", optarg)) dirbehave = DIR_READ;
+        else error_exit("%s %s", getstr(3), "--directories");
+        break;
+      case 'E':
+        grepbehave = GREP_EXTENDED;
+        cflags |= REG_EXTENDED;
+        break;
+      case 'e':
+        add_pattern(optarg, strlen(optarg));
+        needpattern = 0;
+        break;
+      case 'F':
+        grepbehave = GREP_FIXED;
+        break;
+      case 'f':
+        read_patterns(optarg);
+        needpattern = 0;
+        break;
+      case 'G':
+        grepbehave = GREP_BASIC;
+        break;
+      case 'H':
+        Hflag = true;
+        hflag = false;
+        break;
+      case 'h':
+        Hflag = false;
+        hflag = true;
+        break;
+      case 'I':
+        binbehave = BINFILE_SKIP;
+        break;
+      case 'i': /*FALL_THROUGH*/
+      case 'y':
+        iflag =  true;
+        cflags |= REG_ICASE;
+        break;
+      case 'L':
+        lflag = false;
+        Lflag = true;
+        break;
+      case 'l':
+        cflag = Lflag = false;
+        lflag = true;
+        break;
+      case 'm':
+        mflag = true;
+        errno = 0;
+        if (validate_for_negative(optarg)) error_exit("Invalid argument %s", optarg);
+        mcount = strtoull(optarg, &ep, 10);
+        if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
+            ((errno == EINVAL) && (mcount == 0)))
+          error_exit("Invalid argument '%s'", optarg);
+        else if (ep[0] != '\0') {
+          errno = EINVAL;
+          error_exit(NULL);
+        }
+        break;
+      case 'n':
+        nflag = true;
+        break;
+      case 'O':
+        linkbehave = LINK_EXPLICIT;
+        break;
+      case 'o':
+        oflag = true;
+        break;
+      case 'p':
+        linkbehave = LINK_SKIP;
+        break;
+      case 'q':
+        qflag = true;
+        break;
+      case 'S':
+        linkbehave = LINK_READ;
+        break;
+      case 'R': /*FALL_THROUGH*/
+      case 'r':
+        dirbehave = DIR_RECURSE;
+        break;
+      case 's':
+        sflag = true;
+        break;
+      case 'U':
+        binbehave = BINFILE_BIN;
+        break;
+      case 'u': /*FALL_THROUGH*/
+      case MMAP_OPT:
+        /* noop, compatibility */
+        break;
+      case 'v':
+        vflag = true;
+        break;
+      case 'w':
+        wflag = true;
+        break;
+      case 'x':
+        xflag = true;
+        break;
+      case 'Z':
+        nullflag = true;
+        break;
+      case 'z':
+        nulldataflag = true;
+        line_sep = '\0';
+        break;
+      case BIN_OPT:
+        if (!strcasecmp("binary", optarg)) binbehave = BINFILE_BIN;
+        else if (!strcasecmp("without-match", optarg)) binbehave = BINFILE_SKIP;
+        else if (!strcasecmp("text", optarg)) binbehave = BINFILE_TEXT;
+        else error_exit("%s %s", getstr(3), "--binary-files");
+        break;
+      case COLOR_OPT:
+        color = NULL;
+        if (!optarg || !strcasecmp("auto", optarg) ||
+            !strcasecmp("tty", optarg) || !strcasecmp("if-tty", optarg)) {
+          char *term = getenv("TERM");
+          if (isatty(STDOUT_FILENO) && term && strcasecmp(term, "dumb"))
+            color = init_color("01;31");
+        } else if (!strcasecmp("always", optarg) ||
+            !strcasecmp("yes", optarg) || !strcasecmp("force", optarg)) {
+          color = init_color("01;31");
+        } else if (strcasecmp("never", optarg) &&
+            strcasecmp("none", optarg) && strcasecmp("no", optarg))
+          error_exit("%s %s", getstr(3), "--color");
+        break;
+      case LABEL_OPT:
+        label = optarg;
+        break;
+      case LINEBUF_OPT:
+        lbflag = true;
+        break;
+      case R_INCLUDE_OPT:
+        finclude = true;
+        add_fpattern(optarg, INCL_PAT);
+        break;
+      case R_EXCLUDE_OPT:
+        fexclude = true;
+        add_fpattern(optarg, EXCL_PAT);
+        break;
+      case R_DINCLUDE_OPT:
+        dinclude = true;
+        add_dpattern(optarg, INCL_PAT);
+        break;
+      case R_DEXCLUDE_OPT:
+        dexclude = true;
+        add_dpattern(optarg, EXCL_PAT);
+        break;
+      case HELP_OPT:
+      default:
+        toys.exitval = 2;
+        return;
+    }
+    lastc = c;
+    newarg = optind != prevoptind;
+    prevoptind = optind;
+  }
+  aargc -= optind;
+  aargv += optind;
+
+  /* Fail if we don't have any pattern */
+  if (!aargc && needpattern) {
+    toys.exitval = 2;
+    return;
+  }
+
+  /* Process patterns from command line */
+  if (aargc && needpattern) {
+    add_pattern(*aargv, strlen(*aargv));
+    --aargc;
+    ++aargv;
+  }
+
+  fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
+  r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
+
+  if (dirbehave == DIR_RECURSE) {
+    struct stat sb;
+    if (!stat(*aargv, &sb) && S_ISDIR(sb.st_mode))
+      Hflag = true;
+  }
+  /*
+   * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
+   * Optimizations should be done there.
+   */
+  /* Check if cheating is allowed (always is for fgrep). */
+  if (grepbehave == GREP_FIXED)
+    for (i = 0; i < patterns; ++i) fgrepcomp(&fg_pattern[i], pattern[i]);
+  else {
+    for (i = 0; i < patterns; ++i) {
+      if (fastcomp(&fg_pattern[i], pattern[i])) {
+        /* Fall back to full regex library */
+        if ((c = regcomp(&r_pattern[i], pattern[i], cflags))) {
+          regerror(c, &r_pattern[i], re_error, RE_ERROR_BUF);
+          error_exit("%s", re_error);
+        }
+      }
+    }
+  }
+
+  if (lbflag) setlinebuf(stdout);
+  if ((aargc == 0 || aargc == 1) && !Hflag) hflag = true;
+  if (!aargc) exit(!procfile("-"));
+  if (dirbehave == DIR_RECURSE) c = grep_tree(aargv);
+  else {
+    for (c = 0; aargc--; ++aargv) {
+      if ((finclude || fexclude) && !file_matching(*aargv)) continue;
+      c+= procfile(*aargv);
+    }
+  }
+
+#ifndef WITHOUT_NLS
+  catclose(catalog);
+#endif
+
+  /* Find out the correct return value according to the results and the command line option. */
+  toys.exitval = (c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
+}
+#define __USE_FILE_OFFSET64
diff --git a/toys/posix/head.c b/toys/posix/head.c
new file mode 100644 (file)
index 0000000..e8517d4
--- /dev/null
@@ -0,0 +1,54 @@
+/* head.c - copy first lines from input to stdout.
+ *
+ * Copyright 2006 Timothy Elliott <tle@holymonkey.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/head.html
+
+USE_HEAD(NEWTOY(head, "n#<0=10", TOYFLAG_BIN))
+
+config HEAD
+  bool "head"
+  default y
+  help
+    usage: head [-n number] [file...]
+
+    Copy first lines from files to stdout. If no files listed, copy from
+    stdin. Filename "-" is a synonym for stdin.
+
+    -n Number of lines to copy.
+*/
+
+#define FOR_head
+#include "toys.h"
+
+GLOBALS(
+  long lines;
+  int file_no;
+)
+
+static void do_head(int fd, char *name)
+{
+  int i, len, lines=TT.lines, size=sizeof(toybuf);
+
+  if (toys.optc > 1) {
+    // Print an extra newline for all but the first file
+    if (TT.file_no++) xprintf("\n");
+    xprintf("==> %s <==\n", name);
+    xflush();
+  }
+
+  while (lines) {
+    len = read(fd, toybuf, size);
+    if (len<0) perror_msg("%s",name);
+    if (len<1) break;
+
+    for(i=0; i<len;) if (toybuf[i++] == '\n' && !--lines) break;
+
+    xwrite(1, toybuf, i);
+  }
+}
+
+void head_main(void)
+{
+  loopfiles(toys.optargs, do_head);
+}
diff --git a/toys/posix/id.c b/toys/posix/id.c
new file mode 100644 (file)
index 0000000..dbcf9a1
--- /dev/null
@@ -0,0 +1,137 @@
+/* id.c - print real and effective user and group IDs
+ *
+ * Copyright 2012 Sony Network Entertainment, Inc.
+ *
+ * by Tim Bird <tim.bird@am.sony.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/id.html
+
+USE_ID(NEWTOY(id, ">1nGgru[!Ggu]", TOYFLAG_BIN))
+USE_ID_GROUPS(OLDTOY(groups, id, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config ID
+  bool "id"
+  default y
+  help
+    usage: id [-nGgru]
+
+    Print user and group ID.
+
+    -n print names instead of numeric IDs (to be used with -Ggu)
+    -G Show only the group IDs
+    -g Show only the effective group ID
+    -r Show real ID instead of effective ID
+    -u Show only the effective user ID
+
+config ID_GROUPS
+  bool "groups"
+  default y
+  depends on ID
+  help
+    usage: groups [user]
+
+    Print the groups a user is in.
+
+*/
+
+#define FOR_id
+#include "toys.h"
+
+static void s_or_u(char *s, unsigned u, int done)
+{
+  if (toys.optflags & FLAG_n) printf("%s", s);
+  else printf("%u", u);
+  if (done) {
+    xputc('\n');
+    exit(0);
+  }
+}
+
+static void showid(char *header, unsigned u, char *s)
+{
+  printf("%s%u(%s)", header, u, s);
+}
+
+struct passwd *xgetpwuid(uid_t uid)
+{
+  struct passwd *pwd = getpwuid(uid);
+  if (!pwd) error_exit(NULL);
+  return pwd;
+}
+
+struct group *xgetgrgid(gid_t gid)
+{
+  struct group *group = getgrgid(gid);
+  if (!group) error_exit(NULL);
+  return group;
+}
+
+void do_id(char *username)
+{
+  int flags, i, ngroups, cmd_groups = toys.which->name[0] == 'g';
+  struct passwd *pw;
+  struct group *grp;
+  uid_t uid = getuid(), euid = geteuid();
+  gid_t gid = getgid(), egid = getegid(), *groups;
+
+  if (cmd_groups)
+      toys.optflags |= FLAG_G | FLAG_n;
+
+  flags = toys.optflags;
+
+  // check if a username is given
+  if (username) {
+    if (!(pw = getpwnam(username)))
+      error_exit("no such user '%s'", username);
+    uid = euid = pw->pw_uid;
+    gid = egid = pw->pw_gid;
+    if (cmd_groups)
+      printf("%s : ", pw->pw_name);
+  }
+
+  i = flags & FLAG_r;
+  pw = xgetpwuid(i ? uid : euid);
+  if (flags & FLAG_u) s_or_u(pw->pw_name, pw->pw_uid, 1);
+
+  grp = xgetgrgid(i ? gid : egid);
+  if (flags & FLAG_g) s_or_u(grp->gr_name, grp->gr_gid, 1);
+
+  if (!(flags & FLAG_G)) {
+    showid("uid=", pw->pw_uid, pw->pw_name);
+    showid(" gid=", grp->gr_gid, grp->gr_name);
+
+    if (!i) {
+      if (uid != euid) {
+        pw = xgetpwuid(euid);
+        showid(" euid=", pw->pw_uid, pw->pw_name);
+      }
+      if (gid != egid) {
+        grp = xgetgrgid(egid);
+        showid(" egid=", grp->gr_gid, grp->gr_name);
+      }
+    }
+
+    showid(" groups=", grp->gr_gid, grp->gr_name);
+  }
+
+  groups = (gid_t *)toybuf;
+  i = sizeof(toybuf)/sizeof(gid_t);
+  ngroups = username ? getgrouplist(username, gid, groups, &i)
+    : getgroups(i, groups);
+  if (0 >= ngroups) perror_exit(0);
+
+  for (i = 0;;) {
+    if (!(grp = getgrgid(groups[i]))) perror_msg(0);
+    else if (flags & FLAG_G) s_or_u(grp->gr_name, grp->gr_gid, 0);
+    else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name);
+    if (++i >= ngroups) break;
+    xputc(' ');
+  }
+  xputc('\n');
+}
+
+void id_main(void)
+{
+  if (toys.optc) while(*toys.optargs) do_id(*toys.optargs++);
+  else do_id(NULL);
+}
diff --git a/toys/posix/kill.c b/toys/posix/kill.c
new file mode 100644 (file)
index 0000000..1e5e1d9
--- /dev/null
@@ -0,0 +1,67 @@
+/* kill.c - a program to send signals to processes
+ *
+ * Copyright 2012 Daniel Walter <d.walter@0x90.at>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/kill.html
+
+USE_KILL(NEWTOY(kill, "?s: l", TOYFLAG_BIN))
+
+config KILL
+  bool "kill"
+  default y
+  help
+    usage: kill [-l [SIGNAL] | -s SIGNAL | -SIGNAL] pid...
+
+    Send a signal to a process
+*/
+
+#define FOR_kill
+#include "toys.h"
+
+GLOBALS(
+  char *signame;
+)
+
+void kill_main(void)
+{
+  int signum;
+  char *tmp, **args = toys.optargs;
+  pid_t pid;
+
+  // list signal(s)
+  if (toys.optflags & FLAG_l) {
+    if (*args) {
+      int signum = sig_to_num(*args);
+      char *s = NULL;
+
+      if (signum>=0) s = num_to_sig(signum&127);
+      puts(s ? s : "UNKNOWN");
+    } else sig_to_num(NULL);
+    return;
+  }
+
+  // signal must come before pids, so "kill -9 -1" isn't confusing.
+
+  if (!TT.signame && *args && **args=='-') TT.signame=*(args++)+1;
+  if (TT.signame) {
+    char *arg;
+    int i = strtol(TT.signame, &arg, 10);
+    if (!*arg) arg = num_to_sig(i);
+    else arg = TT.signame;
+
+    if (!arg || -1 == (signum = sig_to_num(arg)))
+      error_exit("Unknown signal '%s'", arg);
+  } else signum = SIGTERM;
+
+  if (!*args) {
+    toys.exithelp++;
+    error_exit("missing argument");
+  }
+
+  while (*args) {
+    char *arg = *(args++);
+
+    pid = strtol(arg, &tmp, 10);
+    if (*tmp || kill(pid, signum) < 0) error_msg("unknown pid '%s'", arg);
+  }
+}
diff --git a/toys/posix/link.c b/toys/posix/link.c
new file mode 100644 (file)
index 0000000..38d2cf0
--- /dev/null
@@ -0,0 +1,25 @@
+/* link.c - hardlink a file
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/link.html
+
+USE_LINK(NEWTOY(link, "<2>2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config LINK
+  bool "link"
+  default y
+  help
+    usage: link FILE NEWLINK
+
+    Create hardlink to a file.
+*/
+
+#include "toys.h"
+
+void link_main(void)
+{
+  if (link(toys.optargs[0], toys.optargs[1]))
+    perror_exit("couldn't link '%s' to '%s'", toys.optargs[1],
+      toys.optargs[0]);
+}
diff --git a/toys/posix/ln.c b/toys/posix/ln.c
new file mode 100644 (file)
index 0000000..d69aabf
--- /dev/null
@@ -0,0 +1,62 @@
+/* ln.c - Create filesystem links
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/ln.html
+
+USE_LN(NEWTOY(ln, "<1nfs", TOYFLAG_BIN))
+
+config LN
+  bool "ln"
+  default y
+  help
+    usage: ln [-sfn] [FROM...] TO
+
+    Create a link between FROM and TO.
+    With only one argument, create link in current directory.
+
+    -s Create a symbolic link
+    -f Force the creation of the link, even if TO already exists
+    -n Symlink at destination treated as file
+*/
+
+#define FOR_ln
+#include "toys.h"
+
+void ln_main(void)
+{
+  char *dest = toys.optargs[--toys.optc], *new;
+  struct stat buf;
+  int i;
+
+  // With one argument, create link in current directory.
+  if (!toys.optc) {
+    toys.optc++;
+    dest=".";
+  }
+
+  // Is destination a directory?
+  if (((toys.optflags&FLAG_n) ? lstat : stat)(dest, &buf)
+    || !S_ISDIR(buf.st_mode))
+  {
+    if (toys.optc>1) error_exit("'%s' not a directory");
+    buf.st_mode = 0;
+  }
+
+  for (i=0; i<toys.optc; i++) {
+    int rc;
+    char *try = toys.optargs[i];
+
+    if (S_ISDIR(buf.st_mode)) new = xmsprintf("%s/%s", dest, basename(try));
+    else new = dest;
+    /* Silently unlink the existing target. If it doesn't exist,
+     * then we just move on */
+    if (toys.optflags & FLAG_f) unlink(new);
+
+    rc = (toys.optflags & FLAG_s) ? symlink(try, new) : link(try, new);
+    if (rc)
+      perror_exit("cannot create %s link from '%s' to '%s'",
+        (toys.optflags & FLAG_s) ? "symbolic" : "hard", try, new);
+    if (new != dest) free(new);
+  }
+}
diff --git a/toys/posix/logger.c b/toys/posix/logger.c
new file mode 100644 (file)
index 0000000..c0f4c6d
--- /dev/null
@@ -0,0 +1,197 @@
+/* logger.c - log messages.
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/logger.html
+
+USE_LOGGER(NEWTOY(logger, "p:t:si", TOYFLAG_USR|TOYFLAG_BIN))
+
+config LOGGER
+  bool "logger"
+  default y
+  help
+    usage: logger [-s] [-t TAG] [-p PRIORITY] [message]
+
+    -i    Log the process id of the logger process with each line.
+    -s    Log to stderr as well as the system log
+    -t TAG  Log using the specified tag (defaults to user name)
+    -p PRIO Priority (numeric or facility.level pair)
+*/
+
+#define FOR_logger
+#include "toys.h"
+
+GLOBALS(
+  char *tag;
+  char *priority;
+)
+
+#ifndef SYSLOG_NAMES
+#define INTERNAL_NOPRI  0x10  /* the "no priority" priority */
+/* mark "facility" */
+#define INTERNAL_MARK   LOG_MAKEPRI(LOG_NFACILITIES, 0)
+typedef struct _code {
+  char  *c_name;
+  int c_val;
+} CODE;
+
+static CODE prioritynames[] =
+{  
+  { "alert", LOG_ALERT },
+  { "crit", LOG_CRIT },
+  { "debug", LOG_DEBUG },
+  { "emerg", LOG_EMERG },
+  { "err", LOG_ERR },
+  { "error", LOG_ERR },     /* DEPRECATED */
+  { "info", LOG_INFO },                                                                             
+  { "none", INTERNAL_NOPRI },   /* INTERNAL */
+  { "notice", LOG_NOTICE },
+  { "panic", LOG_EMERG },   /* DEPRECATED */
+  { "warn", LOG_WARNING },    /* DEPRECATED */
+  { "warning", LOG_WARNING },
+  { NULL, -1 }
+};
+
+static CODE facilitynames[] =
+{
+  { "auth", LOG_AUTH },
+  { "authpriv", LOG_AUTHPRIV },
+  { "cron", LOG_CRON },
+  { "daemon", LOG_DAEMON },
+  { "ftp", LOG_FTP },
+  { "kern", LOG_KERN },
+  { "lpr", LOG_LPR },
+  { "mail", LOG_MAIL },
+  { "mark", INTERNAL_MARK },    /* INTERNAL */
+  { "news", LOG_NEWS },
+  { "security", LOG_AUTH },     /* DEPRECATED */
+  { "syslog", LOG_SYSLOG },
+  { "user", LOG_USER },
+  { "uucp", LOG_UUCP },
+  { "local0", LOG_LOCAL0 },
+  { "local1", LOG_LOCAL1 },
+  { "local2", LOG_LOCAL2 },
+  { "local3", LOG_LOCAL3 },
+  { "local4", LOG_LOCAL4 },
+  { "local5", LOG_LOCAL5 },
+  { "local6", LOG_LOCAL6 },
+  { "local7", LOG_LOCAL7 },
+  { NULL, -1 }
+};
+#endif
+
+/*
+ * get effective username, for setting as tag for syslog
+ */
+char* get_username()
+{
+  struct passwd* pw = NULL;
+  pw = getpwuid(geteuid());
+  return (pw?pw->pw_name : NULL);
+}
+
+/*
+ * search the given name and return its value
+ */
+static int dec(char* name, CODE *clist)
+{
+  const CODE *c;
+
+  if (isdigit(*name)) return atoi(name); //if integer then just return the value 
+  for (c = clist; c->c_name; c++) { //find the given parameter in list and return the value.
+    if (strcasecmp(name, c->c_name) == 0) return c->c_val;
+  }
+  return -1;
+}
+
+/*
+ * Compute priority from "facility.level" pair 
+ */
+int get_priority(char* prio)
+{
+  int fac = 0;
+  int lev = 0;
+  char* dot = NULL;
+
+  dot = strchr(prio, '.');
+  if(dot) {
+    *dot = '\0';
+    fac = dec(prio, facilitynames);
+    if(fac < 0) error_exit("Unknown facilityname '%s'",prio);
+    *dot++ = '.';
+  }
+  else dot = prio;
+  lev = dec(dot, prioritynames);
+  if(lev < 0) error_exit("Unknown priority '%s'",dot);
+
+  return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+/* 
+ * logger utility main function
+ */
+void logger_main(void)
+{
+  int prio = 0, msglen = 0, flags = 0;
+  char *message = NULL;
+  if(!(toys.optflags & FLAG_t)) TT.tag = get_username();
+
+  if(toys.optflags & FLAG_p) prio = get_priority(TT.priority);
+  else prio = LOG_USER | LOG_INFO;
+
+  if(toys.optflags & FLAG_s) flags |= LOG_PERROR;
+  if(toys.optflags & FLAG_i) flags |= LOG_PID;
+
+  openlog(TT.tag, flags, 0);
+  if(!toys.optc) { // read messages from STDIN and log them using syslog
+    while(fgets(toybuf, sizeof(toybuf), stdin)) { 
+      if(toybuf[0] && !(toybuf[0] == '\n' && !toybuf[1])) syslog(prio,"%s",toybuf);
+    }
+  } else {
+    while(*toys.optargs) {
+      if(message != NULL) msglen = strlen(message);
+      message = xrealloc(message, msglen + strlen(*toys.optargs) + 2); // 2 byte: 1 for ' ' and 1 for '\0';
+      sprintf(message + msglen, " %s", *toys.optargs++);
+    }
+    syslog(prio, "%s", message+1); //ignore leading ' '
+    free(message);
+    message = NULL;
+  }
+  closelog();
+  toys.exitval = 0;
+}
+
+/*-
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * This is the original license statement for the decode and pencode functions.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *
+ * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *  ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/toys/posix/logname.c b/toys/posix/logname.c
new file mode 100644 (file)
index 0000000..b638ea2
--- /dev/null
@@ -0,0 +1,25 @@
+/* logname.c - Print user's login name.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/logname.html
+
+USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_BIN))
+
+config LOGNAME
+  bool "logname"
+  default y
+  help
+    usage: logname
+
+    Prints the calling user's name or an error when this cannot be
+    determined.
+*/
+
+#include "toys.h"
+
+void logname_main(void)
+{
+  if (getlogin_r(toybuf, sizeof(toybuf))) error_exit("no login name");
+  xputs(toybuf);
+}
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
new file mode 100644 (file)
index 0000000..caabe7d
--- /dev/null
@@ -0,0 +1,440 @@
+/* ls.c - list files
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html
+
+USE_LS(NEWTOY(ls, "goACFHLRSacdfiklmnpqrstux1[-1Cglmnox][-cu][-ftS][-HL]", TOYFLAG_BIN))
+
+config LS
+  bool "ls"
+  default y
+  help
+    usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...]
+    list files
+
+    what to show:
+    -a all files including .hidden
+    -c use ctime for timestamps
+    -d directory, not contents
+    -i inode number
+    -k block sizes in kilobytes
+    -p put a '/' after directory names
+    -q unprintable chars as '?'
+    -s size (in blocks)
+    -u use access time for timestamps
+    -A list all files except . and ..
+    -H follow command line symlinks
+    -L follow symlinks
+    -R recursively list files in subdirectories
+    -F append file type indicator (/=dir, *=exe, @=symlink, |=FIFO)
+
+    output formats:
+    -1 list one file per line
+    -C columns (sorted vertically)
+    -g like -l but no owner
+    -l long (show full details for each file)
+    -m comma separated
+    -n like -l but numeric uid/gid
+    -o like -l but no group
+    -x columns (sorted horizontally)
+
+    sorting (default is alphabetical):
+    -f unsorted
+    -r reverse
+    -t timestamp
+    -S size
+*/
+
+#define FOR_ls
+#include "toys.h"
+
+// test sst output (suid/sticky in ls flaglist)
+
+// ls -lR starts .: then ./subdir:
+
+GLOBALS(
+  struct dirtree *files;
+
+  unsigned screen_width;
+  int nl_title;
+
+  // group and user can make overlapping use of the utoa() buf, so move it
+  char uid_buf[12];
+)
+
+void dlist_to_dirtree(struct dirtree *parent)
+{
+  // Turn double_list into dirtree
+  struct dirtree *dt = parent->child;
+  if (dt) {
+    dt->parent->next = NULL;
+    while (dt) {
+      dt->parent = parent;
+      dt = dt->next;
+    }
+  }
+}
+
+static char endtype(struct stat *st)
+{
+  mode_t mode = st->st_mode;
+  if ((toys.optflags&(FLAG_F|FLAG_p)) && S_ISDIR(mode)) return '/';
+  if (toys.optflags & FLAG_F) {
+    if (S_ISLNK(mode)) return '@';
+    if (S_ISREG(mode) && (mode&0111)) return '*';
+    if (S_ISFIFO(mode)) return '|';
+    if (S_ISSOCK(mode)) return '=';
+  }
+  return 0;
+}
+
+static char *getusername(uid_t uid)
+{
+  struct passwd *pw = getpwuid(uid);
+  utoa_to_buf(uid, TT.uid_buf, 12);
+  return pw ? pw->pw_name : TT.uid_buf;
+}
+
+static char *getgroupname(gid_t gid)
+{
+  struct group *gr = getgrgid(gid);
+  return gr ? gr->gr_name : utoa(gid);
+}
+
+// Figure out size of printable entry fields for display indent/wrap
+
+static void entrylen(struct dirtree *dt, unsigned *len)
+{
+  struct stat *st = &(dt->st);
+  unsigned flags = toys.optflags;
+
+  *len = strlen(dt->name);
+  if (endtype(st)) ++*len;
+  if (flags & FLAG_m) ++*len;
+
+  if (flags & FLAG_i) *len += (len[1] = numlen(st->st_ino));
+  if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
+    unsigned fn = flags & FLAG_n;
+    len[2] = numlen(st->st_nlink);
+    len[3] = strlen(fn ? utoa(st->st_uid) : getusername(st->st_uid));
+    len[4] = strlen(fn ? utoa(st->st_gid) : getgroupname(st->st_gid));
+    len[5] = numlen(st->st_size);
+  }
+  if (flags & FLAG_s) *len += (len[6] = numlen(st->st_blocks));
+}
+
+static int compare(void *a, void *b)
+{
+  struct dirtree *dta = *(struct dirtree **)a;
+  struct dirtree *dtb = *(struct dirtree **)b;
+  int ret = 0, reverse = (toys.optflags & FLAG_r) ? -1 : 1;
+
+  if (toys.optflags & FLAG_S) {
+    if (dta->st.st_size > dtb->st.st_size) ret = -1;
+    else if (dta->st.st_size < dtb->st.st_size) ret = 1;
+  }
+  if (toys.optflags & FLAG_t) {
+    if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1;
+    else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1;
+  }
+  if (!ret) ret = strcmp(dta->name, dtb->name);
+  return ret * reverse;
+}
+
+// callback from dirtree_recurse() determining how to handle this entry.
+
+static int filter(struct dirtree *new)
+{
+  int flags = toys.optflags;
+
+  // Special case to handle enormous dirs without running out of memory.
+  if (flags == (FLAG_1|FLAG_f)) {
+    xprintf("%s\n", new->name);
+    return 0;
+  }
+
+  if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime;
+  if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime;
+  if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2;
+
+  if (flags & (FLAG_a|FLAG_f)) return DIRTREE_SAVE;
+  if (!(flags & FLAG_A) && new->name[0]=='.') return 0;
+
+  return dirtree_notdotdot(new) & DIRTREE_SAVE;
+}
+
+// For column view, calculate horizontal position (for padding) and return
+// index of next entry to display.
+
+static unsigned long next_column(unsigned long ul, unsigned long dtlen,
+  unsigned columns, unsigned *xpos)
+{
+  unsigned long transition;
+  unsigned height, widecols;
+
+  // Horizontal sort is easy
+  if (!(toys.optflags & FLAG_C)) {
+    *xpos = ul % columns;
+    return ul;
+  }
+
+  // vertical sort
+
+  // For -x, calculate height of display, rounded up
+  height = (dtlen+columns-1)/columns;
+
+  // Sanity check: does wrapping render this column count impossible
+  // due to the right edge wrapping eating a whole row?
+  if (height*columns - dtlen >= height) {
+    *xpos = columns;
+    return 0;
+  }
+
+  // Uneven rounding goes along right edge
+  widecols = dtlen % height;
+  if (!widecols) widecols = height;
+  transition = widecols * columns;
+  if (ul < transition) {
+    *xpos =  ul % columns;
+    return (*xpos*height) + (ul/columns);
+  }
+
+  ul -= transition;
+  *xpos = ul % (columns-1);
+
+  return (*xpos*height) + widecols + (ul/(columns-1));
+}
+
+// Display a list of dirtree entries, according to current format
+// Output types -1, -l, -C, or stream
+
+static void listfiles(int dirfd, struct dirtree *indir)
+{
+  struct dirtree *dt, **sort = 0;
+  unsigned long dtlen = 0, ul = 0;
+  unsigned width, flags = toys.optflags, totals[7], len[7],
+    *colsizes = (unsigned *)(toybuf+260), columns = (sizeof(toybuf)-260)/4;
+
+  memset(totals, 0, sizeof(totals));
+
+  // Silently descend into single directory listed by itself on command line.
+  // In this case only show dirname/total header when given -R.
+  if (!indir->parent) {
+    if (!(dt = indir->child)) return;
+    if (S_ISDIR(dt->st.st_mode) && !dt->next && !(flags & FLAG_d)) {
+      dt->extra = 1;
+      listfiles(open(dt->name, 0), dt);
+      return;
+    }
+  } else {
+    // Read directory contents. We dup() the fd because this will close it.
+    indir->data = dup(dirfd);
+    dirtree_recurse(indir, filter, (flags&FLAG_L));
+  }
+
+  // Copy linked list to array and sort it. Directories go in array because
+  // we visit them in sorted order.
+
+  for (;;) {
+    for (dt = indir->child; dt; dt = dt->next) {
+      if (sort) sort[dtlen] = dt;
+      dtlen++;
+    }
+    if (sort) break;
+    sort = xmalloc(dtlen * sizeof(void *));
+    dtlen = 0;
+    continue;
+  }
+
+  // Label directory if not top of tree, or if -R
+  if (indir->parent && (!indir->extra || (flags & FLAG_R)))
+  {
+    char *path = dirtree_path(indir, 0);
+
+    if (TT.nl_title++) xputc('\n');
+    xprintf("%s:\n", path);
+    free(path);
+  }
+
+  if (!(flags & FLAG_f)) qsort(sort, dtlen, sizeof(void *), (void *)compare);
+
+  // Find largest entry in each field for display alignment
+  if (flags & (FLAG_C|FLAG_x)) {
+
+    // columns can't be more than toybuf can hold, or more than files,
+    // or > 1/2 screen width (one char filename, one space).
+    if (columns > TT.screen_width/2) columns = TT.screen_width/2;
+    if (columns > dtlen) columns = dtlen;
+
+    // Try to fit as many columns as we can, dropping down by one each time
+    for (;columns > 1; columns--) {
+      unsigned c, totlen = columns;
+
+      memset(colsizes, 0, columns*sizeof(unsigned));
+      for (ul=0; ul<dtlen; ul++) {
+        entrylen(sort[next_column(ul, dtlen, columns, &c)], len);
+        if (c == columns) break;
+        // Does this put us over budget?
+        if (*len > colsizes[c]) {
+          totlen += *len-colsizes[c];
+          colsizes[c] = *len;
+          if (totlen > TT.screen_width) break;
+        }
+      }
+      // If it fit, stop here
+      if (ul == dtlen) break;
+    }
+  } else if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) {
+    unsigned long blocks = 0;
+
+    for (ul = 0; ul<dtlen; ul++)
+    {
+      entrylen(sort[ul], len);
+      for (width=0; width<6; width++)
+        if (len[width] > totals[width]) totals[width] = len[width];
+      blocks += sort[ul]->st.st_blocks;
+    }
+
+    if (indir->parent) xprintf("total %lu\n", blocks);
+  }
+
+  // Loop through again to produce output.
+  memset(toybuf, ' ', 256);
+  width = 0;
+  for (ul = 0; ul<dtlen; ul++) {
+    unsigned curcol;
+    unsigned long next = next_column(ul, dtlen, columns, &curcol);
+    struct stat *st = &(sort[next]->st);
+    mode_t mode = st->st_mode;
+    char et = endtype(st);
+
+    // Skip directories at the top of the tree when -d isn't set
+    if (S_ISDIR(mode) && !indir->parent && !(flags & FLAG_d)) continue;
+    TT.nl_title=1;
+
+    // Handle padding and wrapping for display purposes
+    entrylen(sort[next], len);
+    if (ul) {
+      if (flags & FLAG_m) xputc(',');
+      if (flags & (FLAG_C|FLAG_x)) {
+        if (!curcol) xputc('\n');
+      } else if ((flags & FLAG_1) || width+1+*len > TT.screen_width) {
+        xputc('\n');
+        width = 0;
+      } else {
+        xputc(' ');
+        width++;
+      }
+    }
+    width += *len;
+
+    if (flags & FLAG_i) xprintf("% *lu ", len[1], (unsigned long)st->st_ino);
+    if (flags & FLAG_s) xprintf("% *lu ", len[6], (unsigned long)st->st_blocks);
+
+    if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
+      struct tm *tm;
+      char perm[11], thyme[64], *usr, *upad, *grp, *grpad;
+
+      mode_to_string(mode, perm);
+
+      tm = localtime(&(st->st_mtime));
+      strftime(thyme, sizeof(thyme), "%F %H:%M", tm);
+
+      if (flags&FLAG_o) grp = grpad = toybuf+256;
+      else {
+        grp = (flags&FLAG_n) ? utoa(st->st_gid) : getgroupname(st->st_gid);
+        grpad = toybuf+256-(totals[4]-len[4]);
+      }
+
+      if (flags&FLAG_g) usr = upad = toybuf+256;
+      else {
+        upad = toybuf+255-(totals[3]-len[3]);
+        if (flags&FLAG_n) {
+          usr = TT.uid_buf;
+          utoa_to_buf(st->st_uid, TT.uid_buf, 12);
+        } else usr = getusername(st->st_uid);
+      }
+
+      // Coerce the st types into something we know we can print.
+      xprintf("%s% *ld %s%s%s%s% *"PRId64" %s ", perm, totals[2]+1,
+        (long)st->st_nlink, usr, upad, grp, grpad, totals[5]+1,
+        (int64_t)st->st_size, thyme);
+    }
+
+    if (flags & FLAG_q) {
+      char *p;
+      for (p=sort[next]->name; *p; p++) xputc(isprint(*p) ? *p : '?');
+    } else xprintf("%s", sort[next]->name);
+    if ((flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) && S_ISLNK(mode))
+      xprintf(" -> %s", sort[next]->symlink);
+
+    if (et) xputc(et);
+
+    // Pad columns
+    if (flags & (FLAG_C|FLAG_x)) {
+      curcol = colsizes[curcol] - *len;
+      if (curcol >= 0) xprintf("%s", toybuf+255-curcol);
+    }
+  }
+
+  if (width) xputc('\n');
+
+  // Free directory entries, recursing first if necessary.
+
+  for (ul = 0; ul<dtlen; free(sort[ul++])) {
+    if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)
+      || !dirtree_notdotdot(sort[ul])) continue;
+
+    // Recurse into dirs if at top of the tree or given -R
+    if (!indir->parent || (flags & FLAG_R))
+      listfiles(openat(dirfd, sort[ul]->name, 0), sort[ul]);
+  }
+  free(sort);
+  if (dirfd != AT_FDCWD) close(indir->data);
+}
+
+void ls_main(void)
+{
+  char **s, *noargs[] = {".", 0};
+  struct dirtree *dt;
+
+  // Do we have an implied -1
+  if (!isatty(1) || (toys.optflags&(FLAG_l|FLAG_o|FLAG_n|FLAG_g)))
+    toys.optflags |= FLAG_1;
+  else {
+    TT.screen_width = 80;
+    terminal_size(&TT.screen_width, NULL);
+    if (TT.screen_width<2) TT.screen_width = 2;
+    if (!(toys.optflags&(FLAG_1|FLAG_x|FLAG_m))) toys.optflags |= FLAG_C;
+  }
+  // The optflags parsing infrastructure should really do this for us,
+  // but currently it has "switch off when this is set", so "-dR" and "-Rd"
+  // behave differently
+  if (toys.optflags & FLAG_d) toys.optflags &= ~FLAG_R;
+
+  // Iterate through command line arguments, collecting directories and files.
+  // Non-absolute paths are relative to current directory.
+  TT.files = dirtree_add_node(0, 0, 0);
+  for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) {
+    dt = dirtree_add_node(0, *s,
+      (toys.optflags & (FLAG_L|FLAG_H|FLAG_l))^FLAG_l);
+
+    if (!dt) {
+      toys.exitval = 1;
+      continue;
+    }
+
+    // Typecast means double_list->prev temporarirly goes in dirtree->parent
+    dlist_add_nomalloc((void *)&TT.files->child, (struct double_list *)dt);
+  }
+
+  // Turn double_list into dirtree
+  dlist_to_dirtree(TT.files);
+
+  // Display the files we collected
+  listfiles(AT_FDCWD, TT.files);
+
+  if (CFG_TOYBOX_FREE) free(TT.files);
+}
diff --git a/toys/posix/mkdir.c b/toys/posix/mkdir.c
new file mode 100644 (file)
index 0000000..24930f4
--- /dev/null
@@ -0,0 +1,73 @@
+/* mkdir.c - Make directories
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/mkdir.html
+
+USE_MKDIR(NEWTOY(mkdir, "<1pm:", TOYFLAG_BIN))
+
+config MKDIR
+  bool "mkdir"
+  default y
+  help
+    usage: mkdir [-p] [-m mode] [dirname...]
+    Create one or more directories.
+
+    -p make parent directories as needed.
+    -m  set permissions of directory to mode.
+*/
+
+#define FOR_mkdir
+#include "toys.h"
+
+GLOBALS(
+  char *arg_mode;
+
+  mode_t mode;
+)
+
+static int do_mkdir(char *dir)
+{
+  struct stat buf;
+  char *s;
+  mode_t mode = 0777;
+
+  // mkdir -p one/two/three is not an error if the path already exists,
+  // but is if "three" is a file.  The others we dereference and catch
+  // not-a-directory along the way, but the last one we must explicitly
+  // test for. Might as well do it up front.
+
+  if (!stat(dir, &buf) && !S_ISDIR(buf.st_mode)) {
+    errno = EEXIST;
+    return 1;
+  }
+
+  for (s=dir; ; s++) {
+    char save=0;
+
+    // Skip leading / of absolute paths.
+    if (s!=dir && *s == '/' && (toys.optflags&FLAG_p)) {
+      save = *s;
+      *s = 0;
+    } else if (*s) continue;
+
+    // Use the mode from the -m option only for the last directory.
+    if ((toys.optflags&FLAG_m) && save != '/') mode = TT.mode;
+
+    if (mkdir(dir, mode)<0 && ((toys.optflags&~FLAG_p) || errno != EEXIST))
+      return 1;
+
+    if (!(*s = save)) break;
+  }
+
+  return 0;
+}
+
+void mkdir_main(void)
+{
+  char **s;
+
+  if(toys.optflags&FLAG_m) TT.mode = string_to_mode(TT.arg_mode, 0777);
+
+  for (s=toys.optargs; *s; s++) if (do_mkdir(*s)) perror_msg("'%s'", *s);
+}
diff --git a/toys/posix/mkfifo.c b/toys/posix/mkfifo.c
new file mode 100644 (file)
index 0000000..15fab70
--- /dev/null
@@ -0,0 +1,35 @@
+/* mkfifo.c - Create FIFOs (named pipes)
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/mkfifo.html
+
+USE_MKFIFO(NEWTOY(mkfifo, "<1m:", TOYFLAG_BIN))
+
+config MKFIFO
+  bool "mkfifo"
+  default y
+  help
+    usage: mkfifo [fifo_name...]
+
+    Create FIFOs (named pipes).
+*/
+
+#define FOR_mkfifo
+#include "toys.h"
+
+GLOBALS(
+  char *m_string;
+  mode_t mode;
+)
+
+void mkfifo_main(void)
+{
+  char **s;
+
+  TT.mode = 0666;
+  if (toys.optflags & FLAG_m) TT.mode = string_to_mode(TT.m_string, 0);
+
+  for (s = toys.optargs; *s; s++)
+    if (mknod(*s, S_IFIFO | TT.mode, 0) < 0) perror_msg("%s", *s);
+}
diff --git a/toys/posix/mv.c b/toys/posix/mv.c
new file mode 100644 (file)
index 0000000..ad7d66f
--- /dev/null
@@ -0,0 +1,289 @@
+/* mv.c - Move file / Directory.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html
+ *
+USE_MV(NEWTOY(mv, "<2fin[-fin]", TOYFLAG_BIN))
+
+config MV
+  bool "mv"
+  default y
+  help
+    usage: mv [-fin] SOURCE DEST
+       mv [-fin] SOURCE... DIRECTORY
+
+    Rename SOURCE to DEST or Move SOURCES(s) to DIRECTORY.
+
+    -f  Don't Prompt before overwrite
+    -i  interactive, prompt before overwrite
+    -n  Don't overwrite an existing file
+*/
+
+#define FOR_mv
+#include "toys.h"
+
+GLOBALS(
+  char *destname;
+  char *srcname;
+  int destisdir;
+  int keep_symlinks;
+  struct arg_list *link_nodes;
+)
+
+typedef struct link_node_ent {
+  ino_t ino;
+  dev_t dev;
+  char *name;
+} link_node_ent_t;
+
+/*
+ * free the inodes which are stored for hard link reference
+ */
+static void free_link_nodes(void *data)
+{ 
+  void *arg = ((struct arg_list*)data)->arg;
+  if (arg) free(arg);
+  free(data);
+} 
+
+/*
+ * allocate and add a node to the list
+ */
+static void llist_add_link_node(struct arg_list **old, void *data)
+{ 
+  struct arg_list *new = xmalloc(sizeof(struct arg_list));
+  new->arg = (char*)data;
+  new->next = *old;
+  *old = new;
+}
+
+/* 
+ * check if the given stat entry is already there in list or not
+ */
+static char *is_link_node_present(struct stat *st)
+{  
+  struct arg_list *temparg = NULL;
+  link_node_ent_t *ent = NULL;
+  if (!TT.link_nodes) return NULL;
+  for (temparg = TT.link_nodes; temparg; temparg = (struct arg_list *)temparg->next) {
+    ent = (link_node_ent_t *)temparg->arg;
+    if (ent && ent->ino == st->st_ino && ent->dev == st->st_dev) return ent->name;
+  }
+  return NULL;
+}
+
+/*
+ * Copy an individual file or directory to target.
+ */
+static void mv_file(char *src, char *dst, struct stat *srcst)
+{
+  int fdout = -1;
+  struct stat std;
+
+  if (!stat(TT.destname, &std) &&
+      (srcst->st_dev == std.st_dev && srcst->st_ino == std.st_ino))
+    error_exit("recursion bad source '%s'", src);
+
+  if (S_ISDIR(srcst->st_mode)) {//Copy directory or file to destination.
+    struct stat st2;
+    /* Always make directory writeable to us, so we can create files in it.
+     * Yes, there's a race window between mkdir() and open(). So make sure
+     * that what we open _is_ a directory rather than something else.
+     * Can't do fchmod() etc. here?
+     */
+    if ((mkdir(dst, srcst->st_mode | 0200) && errno != EEXIST)
+        || 0>(fdout = open(dst, 0)) || fstat(fdout, &st2) || !S_ISDIR(st2.st_mode))
+      perror_exit("mkdir '%s'", dst);
+
+  } else if (TT.keep_symlinks && S_ISLNK(srcst->st_mode)) {
+    char *link = xreadlink(src);
+    //How do we get a filehandle to them?  O_NOFOLLOW causes the open to fail.
+    if (!link || symlink(link, dst)) perror_msg("link '%s'", dst);
+    free(link);
+    return;
+  } else {
+    int fdin, i;
+    link_node_ent_t *ino_details = NULL;
+    //if present then link and return
+    char *link_name = is_link_node_present(srcst);
+    if (link_name) {
+      if ((i = link(link_name, dst))) {
+        if (errno == EEXIST && (toys.optflags & FLAG_f)) unlink(dst);
+        if (i) perror_msg("link '%s'", dst);
+      }
+      return;
+    }
+
+    ino_details = xzalloc(sizeof(link_node_ent_t));
+    ino_details->ino = srcst->st_ino;
+    ino_details->dev = srcst->st_dev;
+    ino_details->name = xstrdup(dst);
+    llist_add_link_node(&TT.link_nodes, (void*)ino_details);
+
+    fdin = xopen(src, O_RDONLY);
+    for (i = 2 ; i; i--) {
+      fdout = open(dst, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
+      if (fdout >= 0 || !(toys.optflags & FLAG_f)) break;
+      unlink(dst);
+    }
+    if (fdout < 0) perror_exit("%s", dst);
+    xsendfile(fdin, fdout);
+    xclose(fdin);
+    unlink(src);
+  }
+  xclose(fdout);
+}
+
+/*
+ * Callback from dirtree_read() for each file/directory under a source dir.
+ */
+static int mv_node(struct dirtree *node)
+{
+  int len = 0;
+  char *s = NULL, *path = dirtree_path(node, 0);
+
+  if (!dirtree_notdotdot(node)) return 0;
+
+  s = path + strlen(TT.srcname); //get the actual file/dir to be copied
+  len = strlen(TT.destname);
+  if (len && TT.destname[len - 1] != '/')
+    s = xmsprintf("%s/%s", TT.destname, ((*s == '/') ? s+1 : s));
+  else s = xmsprintf("%s%s", TT.destname, ((*s == '/') ? s+1 : s));
+  if (node->data == -1) rmdir(path);
+  else mv_file(path, s, &(node->st));
+  free(s);
+  free(path); 
+  return (DIRTREE_RECURSE | ((node->data == -1) ? 0 : DIRTREE_COMEAGAIN));
+}
+
+/*
+ * Move source file/dir from source to destination.
+ */
+static int do_move(char *src, char *dst) 
+{
+  struct stat sb;
+  /* Conditions (1):
+   * When dest path exists and '-f' option is not set:
+   *   (a) The permissions of the dest path do not permit writing & the stdin is a terminal.
+   *   (b) The '-i' option is set.
+   * The mv utility shall write a prompt to stderr & read a line from stdin.
+   * If the response is not affirmative, mv shall do nothing more with the current src file.
+   */
+  if ( !(toys.optflags & FLAG_f)  && !access(dst, F_OK) ) {
+    int ask = 1;
+    if (toys.optflags & FLAG_n) return 0;
+    if (toys.optflags & FLAG_i) {
+      if (access(src, F_OK)) {
+        perror_msg("rename '%s'", src);
+        return 1;
+      }
+      (void)fprintf(stderr, "overwrite %s? ", dst);
+    } else if (access(dst, W_OK) && (!stat(dst, &sb)) ) {
+      if (access(src, F_OK)) {
+        perror_msg("rename '%s'", src);
+        return 1;
+      }
+      (void)fprintf(stderr, "override %ud/%ud for %s? ", sb.st_uid, sb.st_gid, dst);
+    } else ask = 0;
+    if (ask) {
+      if (!yesno("", 0)) return 0;
+    }
+  }
+  /* Conditions (2 & 3):
+   * When rename() succeeds, mv shall do nothing more with the current src file.
+   *   If it fails for any other reason than EXDEV, mv shall write a diagnostic
+   *   message to the stderr and do nothing more with the current src file.
+   * When dest path exists and it is a directory; where as src file is not a directory,
+   *    or src file is a file of type directory, mv shall write a diagnostic message
+   *    to stderr and do nothing more with the current src file.
+   */
+  if (!rename(src, dst)) return 0;
+  else if (errno != EXDEV) {
+    perror_msg("'%s' to '%s failed'",src, dst);
+    return 1;
+  }
+
+  /* Conditions (4):
+   * When dest path exists, mv shall attempt to remove it. If it fails for any
+   *   reason, mv shall write a diagnostic message to the stderr and do nothing
+   *   more with the current src file.
+   */
+  if (!lstat(dst, &sb) && unlink(dst)) {
+    perror_msg("cannot remove '%s'", dst);
+    return 1;
+  }
+
+  /* Conditions (5):
+   * The file hierarchy rooted in src file shall be duplicated as a file
+   *   hierarchy rooted in the dest path.
+   */
+  if (lstat(src, &sb)) {
+    perror_msg("'%s'", src);
+    return 1;
+  }
+
+  if (S_ISDIR(sb.st_mode)) {
+    mv_file(src, dst, &sb);
+    struct dirtree *root = dirtree_add_node(0, src, 0);
+    if (root) dirtree_handle_callback(root, mv_node);
+  } else mv_file(src, dst, &sb);
+  return 0;
+}
+
+/*
+ * Move main function.
+ */
+void mv_main(void)
+{
+  struct stat stc, std;
+  int i, len;
+
+  TT.destname = toys.optargs[--toys.optc];
+
+  if (!stat(TT.destname, &std) && S_ISDIR(std.st_mode)) TT.destisdir++;
+  else if (toys.optc > 1) error_exit("'%s' not directory", TT.destname);
+
+  //Loop through each src file.
+  for (i = 0; i < toys.optc; i++) {
+    char *dst = NULL, *src = toys.optargs[i];
+    //Skip nonexistent sources.
+    TT.keep_symlinks = 1;
+
+    //Skip when src=dest
+    if (!strcmp(src, TT.destname)) {
+      error_msg("'%s' and '%s' are same", src, TT.destname);
+      continue;
+    }
+
+    if (TT.keep_symlinks ? lstat(src, &stc) : stat(src, &stc)
+        || (stc.st_dev == std.st_dev && stc.st_ino == std.st_ino)) {
+      perror_msg("bad source '%s'", src);
+      continue;
+    }
+
+    len = strlen(src);
+    while (len && src[--len] == '/') src[len] = '\0';
+
+    dst = strrchr(src, '/');
+    if (dst && TT.destisdir) TT.srcname = xstrndup(src, (dst - src + 1));
+    else if (TT.destisdir)  TT.srcname = "";
+    else TT.srcname = xstrdup(src);
+
+    //Copy directory or file.
+    if (TT.destisdir) {
+      dst = src + strlen(TT.srcname);
+      len = strlen(TT.destname);
+      if (len && TT.destname[len - 1] != '/')
+        dst = xmsprintf("%s/%s", TT.destname, dst);
+      else dst = xmsprintf("%s%s", TT.destname, dst);
+    } else dst = TT.destname;
+
+    toys.exitval = do_move(src, dst);
+    if (TT.destisdir) free(dst);
+  }
+
+  if (CFG_TOYBOX_FREE) {
+    if (TT.link_nodes) llist_traverse(TT.link_nodes, free_link_nodes);
+  }
+}
diff --git a/toys/posix/nice.c b/toys/posix/nice.c
new file mode 100644 (file)
index 0000000..4b587ee
--- /dev/null
@@ -0,0 +1,38 @@
+/* nice.c - Run a program at a different niceness level.
+ *
+ * Copyright 2010 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/nice.html
+
+USE_NICE(NEWTOY(nice, "^<1n#", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NICE
+  bool "nice"
+  default y
+  help
+    usage: nice [-n PRIORITY] command [args...]
+
+    Run a command line at an increased or decreased scheduling priority.
+
+    Higher numbers make a program yield more CPU time, from -20 (highest
+    priority) to 19 (lowest).  By default processes inherit their parent's
+    niceness (usually 0).  By default this command adds 10 to the parent's
+    priority.  Only root can set a negative niceness level.
+*/
+
+#define FOR_nice
+#include "toys.h"
+
+GLOBALS(
+  long priority;
+)
+
+void nice_main(void)
+{
+  if (!toys.optflags) TT.priority = 10;
+
+  errno = 0;
+  if (nice(TT.priority)==-1 && errno) perror_exit("Can't set priority");
+
+  xexec(toys.optargs);
+}
diff --git a/toys/posix/nohup.c b/toys/posix/nohup.c
new file mode 100644 (file)
index 0000000..b936a09
--- /dev/null
@@ -0,0 +1,40 @@
+/* nohup.c - run commandline with SIGHUP blocked.
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/nohup.html
+
+USE_NOHUP(NEWTOY(nohup, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NOHUP
+  bool "nohup"
+  default y
+  help
+    usage: nohup COMMAND [ARGS...]
+
+    Run a command that survives the end of its terminal.
+    If stdin is a tty, redirect from /dev/null
+    If stdout is a tty, redirect to file "nohup.out"
+*/
+
+#include "toys.h"
+
+void nohup_main(void)
+{
+  signal(SIGHUP, SIG_IGN);
+  if (isatty(1)) {
+    close(1);
+    if (-1 == open("nohup.out", O_CREAT|O_APPEND|O_WRONLY,
+        S_IRUSR|S_IWUSR ))
+    {
+      char *temp = getenv("HOME");
+      temp = xmsprintf("%s/%s", temp ? temp : "", "nohup.out");
+      xcreate(temp, O_CREAT|O_APPEND|O_WRONLY, S_IRUSR|S_IWUSR);
+    }
+  }
+  if (isatty(0)) {
+    close(0);
+    open("/dev/null", O_RDONLY);
+  }
+  xexec(toys.optargs);
+}
diff --git a/toys/posix/od.c b/toys/posix/od.c
new file mode 100644 (file)
index 0000000..948a99e
--- /dev/null
@@ -0,0 +1,260 @@
+/* od.c - Provide octal/hex dumps of data
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/od.html
+
+USE_OD(NEWTOY(od, "j#vN#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
+
+config OD
+  bool "od"
+  default y
+  help
+    usage: od [-bdosxv] [-j #] [-N #] [-A doxn] [-t arg]
+
+    -A Address base (decimal, octal, hexdecimal, none)
+    -t output type(s) a (ascii) c (char) d (decimal) foux
+*/
+
+#define FOR_od
+#include "toys.h"
+
+GLOBALS(
+  struct arg_list *output_base;
+  char *address_base;
+  long max_count;
+  long jump_bytes;
+
+  unsigned types, leftover, star, address_idx;
+  char *buf;
+  uint64_t bufs[4]; // force 64-bit alignment
+  off_t pos;
+)
+
+static char *ascii = "nulsohstxetxeotenqackbel bs ht nl vt ff cr so si"
+  "dledc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp";
+
+struct odtype {
+  int type;
+  int size;
+};
+
+static void od_outline(void)
+{
+  unsigned flags = toys.optflags;
+  char *abases[] = {"", "%07d", "%07o", "%06x"};
+  struct odtype *types = (struct odtype *)toybuf, *t;
+  int i, len;
+
+  if (TT.leftover<16) memset(TT.buf+TT.leftover, 0, 16-TT.leftover);
+
+  // Handle duplciate lines as *
+  if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover
+    && !memcmp(TT.bufs, TT.bufs + 2, 16))
+  {
+    if (!TT.star) {
+      xputs("*");
+      TT.star++;
+    }
+
+  // Print line position
+  } else {
+    TT.star = 0;
+
+    xprintf(abases[TT.address_idx], TT.pos);
+    if (!TT.leftover) {
+      if (TT.address_idx) xputc('\n');
+      return;
+    }
+  }
+
+  TT.pos += len = TT.leftover;
+  TT.leftover = 0;
+  if (TT.star) return;
+
+  // For each output type, print one line
+
+  for (i=0; i<TT.types; i++) {
+    int j = 0, pad = i ? 8 : 0;
+    char buf[128];
+
+    t = types+i;
+    while (j<len) {
+      unsigned k;
+      int throw = 0;
+
+      // Handle ascii
+      if (t->type < 2) {
+        char c = TT.buf[j++];
+        pad += 4;
+
+        if (!t->type) {
+          c &= 127;
+          if (c<=32) sprintf(buf, "%.3s", ascii+(3*c));
+          else if (c==127) strcpy(buf, "del");
+          else sprintf(buf, "%c", c);
+        } else {
+          char *bfnrtav = "\b\f\n\r\t\a\v", *s = strchr(bfnrtav, c);
+          if (s) sprintf(buf, "\\%c", "bfnrtav0"[s-bfnrtav]);
+          else if (c < 32 || c >= 127) sprintf(buf, "%03o", c);
+          else {
+            // TODO: this should be UTF8 aware.
+            sprintf(buf, "%c", c);
+          }
+        }
+      } else if (CFG_TOYBOX_FLOAT && t->type == 6) {
+        long double ld;
+        union {float f; double d; long double ld;} fdl;
+
+        memcpy(&fdl, TT.buf+j, t->size);
+        j += t->size;
+        if (sizeof(float) == t->size) {
+          ld = fdl.f;
+          pad += (throw = 8)+7;
+        } else if (sizeof(double) == t->size) {
+          ld = fdl.d;
+          pad += (throw = 17)+8;
+        } else if (sizeof(long double) == t->size) {
+          ld = fdl.ld;
+          pad += (throw = 21)+9;
+        } else error_exit("bad -tf '%d'", t->size);
+
+        sprintf(buf, "%.*Le", throw, ld);
+      // Integer types
+      } else {
+        unsigned long long ll = 0, or;
+        char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"},
+          *class = c[t->type-2];
+
+        // Work out width of field
+        if (t->size == 8) {
+          or = -1LL;
+          if (t->type == 2) or >>= 1;
+        } else or = (1LL<<(8*t->size))-1;
+        throw = sprintf(buf, class, 0, or);
+
+        // Accumulate integer based on size argument
+        for (k=0; k < t->size; k++) {
+          or = TT.buf[j++];
+          ll |= or << (8*(IS_BIG_ENDIAN ? t->size-k-1 : k));
+        }
+
+        // Handle negative values
+        if (t->type == 2) {
+          or = sizeof(or) - t->size;
+          throw++;
+          if (or && (ll & (1l<<((8*t->size)-1))))
+            ll |= ((or<<(8*or))-1) << (8*t->size);
+        }
+
+        sprintf(buf, class, throw, ll);
+        pad += throw+1;
+      }
+      xprintf("%*s", pad, buf);
+      pad = 0;
+    }
+    xputc('\n');
+  }
+
+  // buffer toggle for "same as last time" check.
+  TT.buf = (char *)((TT.buf == (char *)TT.bufs) ? TT.bufs+2 : TT.bufs);
+}
+
+static void do_od(int fd, char *name)
+{
+  // Skip input, possibly more than one entire file.
+  if (TT.jump_bytes < TT.pos) {
+    off_t off = lskip(fd, TT.jump_bytes);
+    if (off > 0) TT.pos += off;
+    if (TT.jump_bytes < TT.pos) return;
+  }
+
+  for(;;) {
+    char *buf = TT.buf + TT.leftover;
+    int len = 16 - TT.leftover;
+
+    if (toys.optflags & FLAG_N) {
+      if (!TT.max_count) break;
+      if (TT.max_count < len) len = TT.max_count;
+    }
+
+    len = readall(fd, buf, len);
+    if (len < 0) {
+      perror_msg("%s", name);
+      break;
+    }
+    if (TT.max_count) TT.max_count -= len;
+    TT.leftover += len;
+    if (TT.leftover < 16) break;
+
+    od_outline();
+  }
+}
+
+static void append_base(char *base)
+{
+  char *s = base;
+  struct odtype *types = (struct odtype *)toybuf;
+  int type;
+
+  for (;;) {
+    int size = 1;
+
+    if (!*s) return;
+    if (TT.types >= sizeof(toybuf)/sizeof(struct odtype)) break;
+    if (-1 == (type = stridx("acduox"USE_TOYBOX_FLOAT("f"), *(s++)))) break;
+
+    if (isdigit(*s)) {
+      size = strtol(s, &s, 10);
+      if (type < 2 && size != 1) break;
+      if (CFG_TOYBOX_FLOAT && type == 6 && size == sizeof(long double));
+      else if (size < 0 || size > 8) break;
+    } else if (CFG_TOYBOX_FLOAT && type == 6) {
+      int sizes[] = {sizeof(float), sizeof(double), sizeof(long double)};
+      if (-1 == (size = stridx("FDL", *s))) size = sizeof(double);
+      else {
+        s++;
+        size = sizes[size];
+      }
+    } else if (type > 1) {
+      if (-1 == (size = stridx("CSIL", *s))) size = 4;
+      else {
+        s++;
+        size = 1 << size;
+      }
+    }
+
+    types[TT.types].type = type;
+    types[TT.types].size = size;
+    TT.types++;
+  }
+
+  error_exit("bad -t %s", base);
+}
+
+void od_main(void)
+{
+  struct arg_list *arg;
+
+  TT.buf = (char *)TT.bufs;
+
+  if (!TT.address_base) TT.address_idx = 2;
+  else if (0>(TT.address_idx = stridx("ndox", *TT.address_base)))
+    error_exit("bad -A '%c'", *TT.address_base);
+
+  // Collect -t entries
+
+  for (arg = TT.output_base; arg; arg = arg->next) append_base(arg->arg);
+  if (toys.optflags & FLAG_b) append_base("o1");
+  if (toys.optflags & FLAG_d) append_base("u2");
+  if (toys.optflags & FLAG_o) append_base("o2");
+  if (toys.optflags & FLAG_s) append_base("d2");
+  if (toys.optflags & FLAG_x) append_base("x2");
+  if (!TT.output_base) append_base("o2");
+
+  loopfiles(toys.optargs, do_od);
+
+  if (TT.leftover) od_outline();
+  od_outline();
+}
diff --git a/toys/posix/patch.c b/toys/posix/patch.c
new file mode 100644 (file)
index 0000000..bb44348
--- /dev/null
@@ -0,0 +1,421 @@
+/* patch.c - Apply a "universal" diff.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * see http://opengroup.org/onlinepubs/9699919799/utilities/patch.html
+ * (But only does -u, because who still cares about "ed"?)
+ *
+ * TODO:
+ * -b backup
+ * -l treat all whitespace as a single space
+ * -N ignore already applied
+ * -d chdir first
+ * -D define wrap #ifdef and #ifndef around changes
+ * -o outfile output here instead of in place
+ * -r rejectfile write rejected hunks to this file
+ *
+ * -E remove empty files --remove-empty-files
+ * -f force (no questions asked)
+ * -F fuzz (number, default 2)
+ * [file] which file to patch
+
+USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PATCH
+  bool "patch"
+  default y
+  help
+    usage: patch [-i file] [-p depth] [-Ru]
+
+    Apply a unified diff to one or more files.
+
+    -i Input file (defaults=stdin)
+    -l Loose match (ignore whitespace)
+    -p Number of '/' to strip from start of file paths (default=all)
+    -R Reverse patch.
+    -u Ignored (only handles "unified" diffs)
+
+    This version of patch only handles unified diffs, and only modifies
+    a file when all all hunks to that file apply.  Patch prints failed
+    hunks to stderr, and exits with nonzero status if any hunks fail.
+
+    A file compared against /dev/null (or with a date <= the epoch) is
+    created/deleted as appropriate.
+*/
+
+#define FOR_patch
+#include "toys.h"
+
+GLOBALS(
+  char *infile;
+  long prefix;
+
+  struct double_list *current_hunk;
+  long oldline, oldlen, newline, newlen;
+  long linenum;
+  int context, state, filein, fileout, filepatch, hunknum;
+  char *tempname;
+)
+
+// Dispose of a line of input, either by writing it out or discarding it.
+
+// state < 2: just free
+// state = 2: write whole line to stderr
+// state = 3: write whole line to fileout
+// state > 3: write line+1 to fileout when *line != state
+
+#define PATCH_DEBUG (CFG_TOYBOX_DEBUG && (toys.optflags & 32))
+
+static void do_line(void *data)
+{
+  struct double_list *dlist = (struct double_list *)data;
+
+  if (TT.state>1 && *dlist->data != TT.state) {
+    char *s = dlist->data+(TT.state>3 ? 1 : 0);
+    int i = TT.state == 2 ? 2 : TT.fileout;
+
+    xwrite(i, s, strlen(s));
+    xwrite(i, "\n", 1);
+  }
+
+  if (PATCH_DEBUG) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
+
+  free(dlist->data);
+  free(data);
+}
+
+static void finish_oldfile(void)
+{
+  if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
+  TT.fileout = TT.filein = -1;
+}
+
+static void fail_hunk(void)
+{
+  if (!TT.current_hunk) return;
+  TT.current_hunk->prev->next = 0;
+
+  fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n",
+      TT.hunknum, TT.oldline, TT.newline);
+  toys.exitval = 1;
+
+  // If we got to this point, we've seeked to the end.  Discard changes to
+  // this file and advance to next file.
+
+  TT.state = 2;
+  llist_traverse(TT.current_hunk, do_line);
+  TT.current_hunk = NULL;
+  delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
+  TT.state = 0;
+}
+
+// Compare ignoring whitespace. Just returns
+static int loosecmp(char *aa, char *bb)
+{
+  int a = 0, b = 0;
+
+  for (;;) {
+    while (isspace(aa[a])) a++;
+    while (isspace(bb[b])) b++;
+    if (aa[a] != bb[b]) return 1;
+    if (!aa[a]) return 0;
+    a++, b++;
+  }
+}
+
+// Given a hunk of a unified diff, make the appropriate change to the file.
+// This does not use the location information, but instead treats a hunk
+// as a sort of regex.  Copies data from input to output until it finds
+// the change to be made, then outputs the changed data and returns.
+// (Finding EOF first is an error.)  This is a single pass operation, so
+// multiple hunks must occur in order in the file.
+
+static int apply_one_hunk(void)
+{
+  struct double_list *plist, *buf = NULL, *check;
+  int matcheof = 0, reverse = toys.optflags & FLAG_R, backwarn = 0;
+  int (*lcmp)(char *aa, char *bb);
+
+  lcmp = (toys.optflags & FLAG_l) ? (void *)loosecmp : (void *)strcmp;
+
+  // Break doubly linked list so we can use singly linked traversal function.
+  TT.current_hunk->prev->next = NULL;
+
+  // Match EOF if there aren't as many ending context lines as beginning
+  for (plist = TT.current_hunk; plist; plist = plist->next) {
+    if (plist->data[0]==' ') matcheof++;
+    else matcheof = 0;
+    if (PATCH_DEBUG) fprintf(stderr, "HUNK:%s\n", plist->data);
+  }
+  matcheof = matcheof < TT.context;
+
+  if (PATCH_DEBUG) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
+
+  // Loop through input data searching for this hunk.  Match all context
+  // lines and all lines to be removed until we've found the end of a
+  // complete hunk.
+  plist = TT.current_hunk;
+  buf = NULL;
+  if (TT.context) for (;;) {
+    char *data = get_line(TT.filein);
+
+    TT.linenum++;
+
+    // Figure out which line of hunk to compare with next.  (Skip lines
+    // of the hunk we'd be adding.)
+    while (plist && *plist->data == "+-"[reverse]) {
+      if (data && !lcmp(data, plist->data+1)) {
+        if (!backwarn) backwarn = TT.linenum;
+      }
+      plist = plist->next;
+    }
+
+    // Is this EOF?
+    if (!data) {
+      if (PATCH_DEBUG) fprintf(stderr, "INEOF\n");
+
+      // Does this hunk need to match EOF?
+      if (!plist && matcheof) break;
+
+      if (backwarn)
+        fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
+            TT.hunknum, TT.linenum);
+
+      // File ended before we found a place for this hunk.
+      fail_hunk();
+      goto done;
+    } else if (PATCH_DEBUG) fprintf(stderr, "IN: %s\n", data);
+    check = dlist_add(&buf, data);
+
+    // Compare this line with next expected line of hunk.
+
+    // A match can fail because the next line doesn't match, or because
+    // we hit the end of a hunk that needed EOF, and this isn't EOF.
+
+    // If match failed, flush first line of buffered data and
+    // recheck buffered data for a new match until we find one or run
+    // out of buffer.
+
+    for (;;) {
+      if (!plist || lcmp(check->data, plist->data+1)) {
+        // Match failed.  Write out first line of buffered data and
+        // recheck remaining buffered data for a new match.
+
+        if (PATCH_DEBUG) fprintf(stderr, "NOT: %s\n", plist->data);
+
+        TT.state = 3;
+        check = llist_pop(&buf);
+        check->prev->next = buf;
+        buf->prev = check->prev;
+        do_line(check);
+        plist = TT.current_hunk;
+
+        // If we've reached the end of the buffer without confirming a
+        // match, read more lines.
+        if (check==buf) {
+          buf = 0;
+          break;
+        }
+        check = buf;
+      } else {
+        if (PATCH_DEBUG) fprintf(stderr, "MAYBE: %s\n", plist->data);
+        // This line matches.  Advance plist, detect successful match.
+        plist = plist->next;
+        if (!plist && !matcheof) goto out;
+        check = check->next;
+        if (check == buf) break;
+      }
+    }
+  }
+out:
+  // We have a match.  Emit changed data.
+  TT.state = "-+"[reverse];
+  llist_traverse(TT.current_hunk, do_line);
+  TT.current_hunk = NULL;
+  TT.state = 1;
+done:
+  if (buf) {
+    buf->prev->next = NULL;
+    llist_traverse(buf, do_line);
+  }
+
+  return TT.state;
+}
+
+// Read a patch file and find hunks, opening/creating/deleting files.
+// Call apply_one_hunk() on each hunk.
+
+// state 0: Not in a hunk, look for +++.
+// state 1: Found +++ file indicator, look for @@
+// state 2: In hunk: counting initial context lines
+// state 3: In hunk: getting body
+
+void patch_main(void)
+{
+  int reverse = toys.optflags&FLAG_R, state = 0, patchlinenum = 0,
+    strip = 0;
+  char *oldname = NULL, *newname = NULL;
+
+  if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY);
+  TT.filein = TT.fileout = -1;
+
+  // Loop through the lines in the patch
+  for (;;) {
+    char *patchline;
+
+    patchline = get_line(TT.filepatch);
+    if (!patchline) break;
+
+    // Other versions of patch accept damaged patches,
+    // so we need to also.
+    if (strip || !patchlinenum++) {
+      int len = strlen(patchline);
+      if (patchline[len-1] == '\r') {
+        if (!strip) fprintf(stderr, "Removing DOS newlines\n");
+        strip = 1;
+        patchline[len-1]=0;
+      }
+    }
+    if (!*patchline) {
+      free(patchline);
+      patchline = xstrdup(" ");
+    }
+
+    // Are we assembling a hunk?
+    if (state >= 2) {
+      if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
+        dlist_add(&TT.current_hunk, patchline);
+
+        if (*patchline != '+') TT.oldlen--;
+        if (*patchline != '-') TT.newlen--;
+
+        // Context line?
+        if (*patchline==' ' && state==2) TT.context++;
+        else state=3;
+
+        // If we've consumed all expected hunk lines, apply the hunk.
+
+        if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
+        continue;
+      }
+      fail_hunk();
+      state = 0;
+      continue;
+    }
+
+    // Open a new file?
+    if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
+      char *s, **name = &oldname;
+      int i;
+
+      if (*patchline == '+') {
+        name = &newname;
+        state = 1;
+      }
+
+      free(*name);
+      finish_oldfile();
+
+      // Trim date from end of filename (if any).  We don't care.
+      for (s = patchline+4; *s && *s!='\t'; s++)
+        if (*s=='\\' && s[1]) s++;
+      i = atoi(s);
+      if (i>1900 && i<=1970) *name = xstrdup("/dev/null");
+      else {
+        *s = 0;
+        *name = xstrdup(patchline+4);
+      }
+
+      // We defer actually opening the file because svn produces broken
+      // patches that don't signal they want to create a new file the
+      // way the patch man page says, so you have to read the first hunk
+      // and _guess_.
+
+    // Start a new hunk?  Usually @@ -oldline,oldlen +newline,newlen @@
+    // but a missing ,value means the value is 1.
+    } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
+      int i;
+      char *s = patchline+4;
+
+      // Read oldline[,oldlen] +newline[,newlen]
+
+      TT.oldlen = TT.newlen = 1;
+      TT.oldline = strtol(s, &s, 10);
+      if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
+      TT.newline = strtol(s+2, &s, 10);
+      if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
+
+      TT.context = 0;
+      state = 2;
+
+      // If this is the first hunk, open the file.
+      if (TT.filein == -1) {
+        int oldsum, newsum, del = 0;
+        char *name;
+
+        oldsum = TT.oldline + TT.oldlen;
+        newsum = TT.newline + TT.newlen;
+
+        name = reverse ? oldname : newname;
+
+        // We're deleting oldname if new file is /dev/null (before -p)
+        // or if new hunk is empty (zero context) after patching
+        if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
+        {
+          name = reverse ? newname : oldname;
+          del++;
+        }
+
+        // handle -p path truncation.
+        for (i = 0, s = name; *s;) {
+          if ((toys.optflags & FLAG_p) && TT.prefix == i) break;
+          if (*s++ != '/') continue;
+          while (*s == '/') s++;
+          name = s;
+          i++;
+        }
+
+        if (del) {
+          printf("removing %s\n", name);
+          xunlink(name);
+          state = 0;
+        // If we've got a file to open, do so.
+        } else if (!(toys.optflags & FLAG_p) || i <= TT.prefix) {
+          // If the old file was null, we're creating a new one.
+          if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK))
+          {
+            printf("creating %s\n", name);
+            s = strrchr(name, '/');
+            if (s) {
+              *s = 0;
+              xmkpath(name, -1);
+              *s = '/';
+            }
+            TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
+          } else {
+            printf("patching %s\n", name);
+            TT.filein = xopen(name, O_RDONLY);
+          }
+          TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
+          TT.linenum = 0;
+          TT.hunknum = 0;
+        }
+      }
+
+      TT.hunknum++;
+
+      continue;
+    }
+
+    // If we didn't continue above, discard this line.
+    free(patchline);
+  }
+
+  finish_oldfile();
+
+  if (CFG_TOYBOX_FREE) {
+    close(TT.filepatch);
+    free(oldname);
+    free(newname);
+  }
+}
diff --git a/toys/posix/printf.c b/toys/posix/printf.c
new file mode 100644 (file)
index 0000000..ec6cc6c
--- /dev/null
@@ -0,0 +1,390 @@
+/* printf.c - Format and Print the data.
+ *
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
+
+USE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PRINTF 
+    bool "printf"
+    default y
+    help
+    usage: printf Format [Arguments..]
+    
+    Format and print ARGUMENT(s) according to FORMAT.
+    Format is 'C' control output as 'C' printf.
+*/
+
+#define FOR_printf
+#include "toys.h"
+
+GLOBALS(
+  char *hv_w;
+  char *hv_p;
+  int encountered;
+)
+
+/*
+ * Calculate width and precision from format string,
+ */
+static int find_w_p()                      //Handle width and prec.
+{
+  char *ptr, *str;
+  errno = 0;
+  str = *toys.optargs;
+  if(*str == '-') str++;
+  long value = strtol(str, &ptr, 10);
+  if(errno) perror_msg("Not a valid num %s, %d", str, errno);
+  if(ptr && (*ptr != '\0' || ptr == (str))) perror_msg("Not a valid num %s", *toys.optargs);
+  if(*--str == '-') return (int)(-1 * value);
+  else return value;
+}
+/*
+ * Set have_width and have_prec global variables.
+ */
+static void set_w_p(char *start)              
+{
+  TT.hv_p = NULL;
+  TT.hv_w = NULL;
+  TT.hv_p = strstr(start, ".*");        
+  TT.hv_w = strchr(start, '*');        
+  if((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL;       //pitfall: handle diff b/w * and .*
+}
+/*
+ * Add ll to Interger formats and L to floating point
+ * as we have to handle max. possible given value.
+ */
+static char* get_format(char *f)                
+{
+  int len = strlen(f);
+  char last = f[len - 1];
+  f[len - 1] = '\0';
+  char *post = "";
+  if(strchr("diouxX", last)) post = "ll";  // add ll to integer modifier.
+  else if(strchr("feEgG", last)) post = "L"; // add L to float modifier.
+  return xmsprintf("%s%s%c", f, post, last);
+}
+/*
+ * Print the long values with width and prec taken 
+ * care of.
+ */
+static void print_long(char *format, long long llong_value, int p, int w)
+{
+  if(!TT.hv_w) {
+    if(!TT.hv_p) printf(format,llong_value);
+    else printf(format,p,llong_value);
+  }
+  else {
+    if(!TT.hv_p) printf(format,w,llong_value);
+    else printf(format,w,p,llong_value);
+  }
+}
+/*
+ * Print the arguments with corresponding conversion and 
+ * width and precision.
+ */
+static void print(char *fmt, int w, int p, int l)
+{
+  char *endptr = NULL;
+  char *ptr = (fmt+l-1);
+  long long llong_value;
+  long double double_value;
+  char *format = NULL;
+  errno = 0;
+  switch(*ptr) {
+    case'd': /*FALL_THROUGH*/
+    case'i':
+      if(*toys.optargs != NULL) {  
+        if(**toys.optargs == '\'' || **toys.optargs == '"') 
+          llong_value = *((*toys.optargs) + 1);
+        else {
+          llong_value = strtoll(*toys.optargs, &endptr, 0);
+          if(errno) { 
+            perror_msg("Invalid num %s", *toys.optargs);
+            llong_value = 0;
+          }
+          else {
+            if(endptr && (*endptr != '\0' || endptr == (*toys.optargs))) {
+              perror_msg("Not a valid num %s",*toys.optargs);
+              llong_value = 0;
+            }
+          }
+        }
+      }
+      else llong_value = 0;
+
+      format = get_format(fmt);
+      print_long(format, llong_value, p, w);
+      break;
+    case'o': /*FALL_THROUGH*/
+    case'u': /*FALL_THROUGH*/
+    case'x': /*FALL_THROUGH*/
+    case'X':
+      if(*toys.optargs != NULL) {
+        if(**toys.optargs == '\'' || **toys.optargs == '"')
+          llong_value = *((*toys.optargs) + 1);
+        else {
+          llong_value = strtoll(*toys.optargs, &endptr, 0);
+          if(errno) {
+            perror_msg("Invalid num %s", *toys.optargs);
+            llong_value = 0;
+          }
+          else {
+            if(endptr && (*endptr != '\0' || endptr == (*toys.optargs))) {
+              perror_msg("Not a valid num %s", *toys.optargs);
+              llong_value = 0;
+            }
+          }
+        }
+      }
+      else llong_value = 0;
+
+      format = get_format(fmt);
+      print_long(format, llong_value, p, w);
+      break;
+    case'g': /*FALL_THROUGH*/
+    case'e': /*FALL_THROUGH*/
+    case'E': /*FALL_THROUGH*/
+    case'G': /*FALL_THROUGH*/
+    case'f':
+      if(*toys.optargs != NULL) {
+        double_value = strtold(*toys.optargs, &endptr);
+        if(*endptr != '\0' || endptr == *toys.optargs) {
+          perror_msg("Not a valid num %s", *toys.optargs);
+          double_value = 0;
+        }
+      }
+      else double_value = 0;
+
+      format = get_format(fmt);
+      if(!TT.hv_w) {
+        if(!TT.hv_p) printf(format, double_value);
+        else printf(format, p, double_value);
+      }
+      else {
+        if(!TT.hv_p) printf(format, w, double_value);
+        else printf(format, w, p, double_value);
+      }
+      break;
+    case's':
+      if(!TT.hv_w) {
+        if(!TT.hv_p) printf(fmt, (*toys.optargs ? *toys.optargs : ""));
+        else printf(fmt, p, (*toys.optargs ? *toys.optargs : ""));
+      }
+      else {
+        if(!TT.hv_p) printf(fmt, w, (*toys.optargs ? *toys.optargs : ""));
+        else printf(fmt,w,p,(*toys.optargs ? *toys.optargs : ""));
+      }
+      break;
+    case'c':
+      printf(fmt, (*toys.optargs ? **toys.optargs : '\0'));
+      break;
+  }
+  free(format);
+  format = NULL;
+}
+/*
+ * Handle the escape sequences. 
+ */
+static int handle_slash(char **esc_val)
+{
+  char *ptr = *esc_val;
+  unsigned  base = 0;
+  unsigned num = 0;
+  int esc_length = 0;
+  unsigned result = 0, count = 0;
+  if(*ptr == 'x') {
+    ptr++;
+    esc_length++; // Hexadecimal escape sequence have only 1 or 2 digits, xHH.
+    base = 16;
+  }
+  else if(isdigit(*ptr)) base = 8;  // Octal escape sequence have 1,2 or 3 digits, xHHH. Leading "0" (\0HHH) we are ignoring.
+
+  while(esc_length < 3 && base != 0) {
+    num = tolower(*ptr) - '0';
+    if(num > 10) num += ('0' - 'a' + 10);
+    if(num >= base) { 
+      if(base == 16) {
+        esc_length--;
+        if(esc_length == 0) { // Invalid hex values eg. /xvd, print it as it is /xvd
+          result = '\\';
+          ptr--;
+        }
+      }
+      break;
+    }
+    esc_length++;
+    result = count * base + num;
+    count = result;
+    ptr++;
+  }
+  if(base != 0) {
+    ptr--;
+    *esc_val = ptr;
+    return (char)result;
+  }
+  else {
+    switch(*ptr) {
+      case 'n': 
+        result = '\n';
+        break;
+      case 't': 
+        result = '\t';
+        break;
+      case 'e': 
+        result = (char)27;
+        break;
+      case 'b': 
+        result = '\b';
+        break;
+      case 'a': 
+        result = '\a';
+        break;
+      case 'f': 
+        result = '\f';
+        break;
+      case 'v': 
+        result = '\v';
+        break;
+      case 'r': 
+        result = '\r';
+        break;
+      case '\\': 
+        result = '\\';
+        break;
+      default :
+        result = '\\';
+        ptr--; // Let pointer pointing to / we will increment after returning.
+        break;
+    }
+  }
+  *esc_val = ptr;
+  return (char)result;
+}
+/*
+ * Handle "%b" option with '\' interpreted.
+ */
+static void print_esc_str(char *str)              
+{
+  while(*str != '\0') {
+    if(*str == '\\') {
+      str++; // Go to escape char     
+      xputc(handle_slash(&str)); //print corresponding char
+      str++;          
+    }
+    else xputc(*str++);
+  }
+}
+/*
+ * Prase the format string and print.
+ */
+static void parse_print(char *f)
+{
+  char *start, *p;
+  char format_specifiers[] = "diouxXfeEgGcs";
+  int len = 0;
+  int width = 0;
+  int prec = 0;
+
+  while(*f) {
+    switch(*f) {
+      case '%':
+        start = f++;
+        len++;
+        if(*f == '%') {
+          xputc('%');
+          break;
+        }
+        if(*f == 'b') {
+          if(*toys.optargs) {
+            print_esc_str(*toys.optargs);
+            TT.encountered = 1;
+          }
+          else print_esc_str("");
+          if(*toys.optargs) toys.optargs++;
+          break;
+        }
+        if(strchr("-+# ", *f)) {            //Just consume -+# printf will take care.
+          f++;
+          len++;
+        }
+        if(*f == '*') {  
+          f++;
+          len++;
+          if(*toys.optargs != NULL) {
+            width = find_w_p();
+            toys.optargs++;
+          }
+        }
+        else { 
+          while(isdigit(*f)) {
+            f++;
+            len++;
+          }
+        }
+        if(*f == '.') {
+          f++;
+          len++;
+          if(*f == '*') {
+            f++;
+            len++;
+            if(*toys.optargs != NULL) {
+              prec = find_w_p();
+              toys.optargs++;
+            }
+          }
+          else {
+            while(isdigit(*f)) {
+              f++;
+              len++;
+            }
+          }
+        }
+        p = strchr(format_specifiers, *f);
+        if(p == NULL) {
+          perror_exit("Missing OR Invalid format specifier");
+          return;
+        }
+        else {
+          len++;
+          set_w_p(start);
+          p = xmalloc(len+1);
+          memcpy(p, start, len);
+          p[len] = '\0';
+          print(p, width, prec, len);
+          if(*toys.optargs) toys.optargs++;
+          free(p);
+          p = NULL;
+        } 
+        TT.encountered = 1;
+        break;
+      case'\\':
+        if(f[1]) {
+          if(*++f == 'c') exit(0); //Got '\c', so no further output  
+          xputc(handle_slash(&f));
+        }
+        else xputc(*f);
+        break;
+      default:
+        xputc(*f);
+        break;
+    }
+    f++;
+    len = 0;
+  }
+  return;
+}
+/*
+ * Printf main function.
+ */
+void printf_main(void)
+{
+  char *format = NULL;
+  TT.encountered = 0;
+  format = *toys.optargs;
+  toys.optargs++;
+  parse_print(format); //printf acc. to format..
+  while((*toys.optargs != NULL) && TT.encountered) parse_print(format); //Re-use FORMAT arg as necessary to convert all given ARGS.
+  xflush();
+}
diff --git a/toys/posix/pwd.c b/toys/posix/pwd.c
new file mode 100644 (file)
index 0000000..3203592
--- /dev/null
@@ -0,0 +1,54 @@
+/* pwd.c - Print working directory.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/pwd.html
+
+USE_PWD(NEWTOY(pwd, ">0LP[-LP]", TOYFLAG_BIN))
+
+config PWD
+  bool "pwd"
+  default y
+  help
+    usage: pwd [-L|-P]
+
+    Print working (current) directory.
+
+    -L  Use shell's path from $PWD (when applicable)
+    -P  Print cannonical absolute path
+*/
+
+#define FOR_pwd
+#include "toys.h"
+
+void pwd_main(void)
+{
+  char *s, *pwd = getcwd(0, 0), *PWD;
+
+  // Only use $PWD if it's an absolute path alias for cwd with no "." or ".."
+  if (!(toys.optflags & FLAG_P) && (s = PWD = getenv("PWD"))) {
+    struct stat st1, st2;
+
+    while (*s == '/') {
+      if (*(++s) == '.') {
+        if (s[1] == '/' || !s[1]) break;
+        if (s[1] == '.' && (s[2] == '/' || !s[2])) break;
+      }
+      while (*s && *s != '/') s++;
+    }
+    if (!*s && s != PWD) s = PWD;
+    else s = NULL;
+
+    // If current directory exists, make sure it matches.
+    if (s && pwd)
+        if (stat(pwd, &st1) || stat(PWD, &st2) || st1.st_ino != st2.st_ino ||
+            st1.st_dev != st2.st_dev) s = NULL;
+  } else s = NULL;
+
+  // If -L didn't give us a valid path, use cwd.
+  if (!s && !(s = pwd)) perror_exit("xgetcwd");
+
+  xprintf("%s\n", s);
+
+  if (CFG_TOYBOX_FREE) free(pwd);
+}
diff --git a/toys/posix/rm.c b/toys/posix/rm.c
new file mode 100644 (file)
index 0000000..7954dae
--- /dev/null
@@ -0,0 +1,94 @@
+/* rm.c - remove files
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html
+
+USE_RM(NEWTOY(rm, "fiRr[-fi]", TOYFLAG_BIN))
+
+config RM
+  bool "rm"
+  default y
+  help
+    usage: rm [-fiRr] FILE...
+
+    Remove each argument from the filesystem.
+
+    -f force: remove without confirmation, no error if it doesn't exist
+    -i interactive: prompt for confirmation
+    -rR        recursive: remove directory contents
+*/
+
+#define FOR_rm
+#include "toys.h"
+
+static int do_rm(struct dirtree *try)
+{
+  int fd = dirtree_parentfd(try), flags = toys.optflags;
+  int dir = S_ISDIR(try->st.st_mode), or = 0, using = 0;
+
+  // Skip . and .. (yes, even explicitly on the command line: posix says to)
+  if (!dirtree_notdotdot(try)) return 0;
+
+  // Intentionally fail non-recursive attempts to remove even an empty dir
+  // (via wrong flags to unlinkat) because POSIX says to.
+  if (dir && !(flags & (FLAG_r|FLAG_R))) goto skip;
+
+  // This is either the posix section 2(b) prompt or the section 3 prompt.
+  if (!(flags & FLAG_f)
+    && (!S_ISLNK(try->st.st_mode) && faccessat(fd, try->name, W_OK, 0))) or++;
+  if (!(dir && try->data == -1) && ((or && isatty(0)) || (flags & FLAG_i))) {
+    char *s = dirtree_path(try, 0);
+    fprintf(stderr, "rm %s%s", or ? "ro " : "", dir ? "dir " : "");
+    or = yesno(s, 0);
+    free(s);
+    if (!or) goto nodelete;
+  }
+
+  // handle directory recursion
+  if (dir) {
+
+    if (try->data != -1) return DIRTREE_COMEAGAIN;
+    using = AT_REMOVEDIR;
+    if (try->symlink) goto nodelete;
+    if (flags & FLAG_i) {
+      char *s = dirtree_path(try, 0);
+      // This is the section 2(d) prompt. (Yes, posix says to prompt twice.)
+      fprintf(stderr, "rmdir ");
+      or = yesno(s, 0);
+      free(s);
+      if (!or) goto nodelete;
+    }
+  }
+
+skip:
+  if (unlinkat(fd, try->name, using)) {
+    perror_msg("%s", try->name);
+nodelete:
+    if (try->parent) try->parent->symlink = (char *)1;
+  }
+
+  return 0;
+}
+
+void rm_main(void)
+{
+  char **s;
+
+  // Can't use <1 in optstring because zero arguments with -f isn't an error
+  if (!toys.optc && !(toys.optflags & FLAG_f)) error_exit("Needs 1 argument");
+
+  for (s = toys.optargs; *s; s++) {
+    if (!strcmp(*s, "/")) {
+      error_msg("rm /. if you mean it");
+      continue;
+    }
+
+    // There's a race here where a file removed between this access and
+    // dirtree's stat would report the nonexistence as an error, but that's
+    // not a normal "it didn't exist" so I'm ok with it.
+    if ((toys.optflags & FLAG_f) && (access(*s, F_OK) && errno == ENOENT))
+      continue;
+    dirtree_read(*s, do_rm);
+  }
+}
diff --git a/toys/posix/rmdir.c b/toys/posix/rmdir.c
new file mode 100644 (file)
index 0000000..fec3ce9
--- /dev/null
@@ -0,0 +1,46 @@
+/* rmdir.c - remove directory/path
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/rmdir.html
+
+USE_RMDIR(NEWTOY(rmdir, "<1p", TOYFLAG_BIN))
+
+config RMDIR
+  bool "rmdir"
+  default y
+  help
+    usage: rmdir [-p] [dirname...]
+    Remove one or more directories.
+
+    -p Remove path.
+*/
+
+#include "toys.h"
+
+static void do_rmdir(char *name)
+{
+  char *temp;
+
+  for (;;) {
+    if (rmdir(name)) {
+      perror_msg("%s",name);
+      return;
+    }
+
+    // Each -p cycle back up one slash, ignoring trailing and repeated /.
+
+    if (!toys.optflags) return;
+    do {
+      if (!(temp = strrchr(name, '/'))) return;
+      *temp = 0;
+    } while (!temp[1]);
+  }
+}
+
+void rmdir_main(void)
+{
+  char **s;
+
+  for (s=toys.optargs; *s; s++) do_rmdir(*s);
+}
diff --git a/toys/posix/sleep.c b/toys/posix/sleep.c
new file mode 100644 (file)
index 0000000..a83f1e5
--- /dev/null
@@ -0,0 +1,49 @@
+/* sleep.c - Wait for a number of seconds.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/sleep.html
+
+USE_SLEEP(NEWTOY(sleep, "<1", TOYFLAG_BIN))
+
+config SLEEP
+  bool "sleep"
+  default y
+  help
+    usage: sleep SECONDS
+
+    Wait before exiting.
+
+config SLEEP_FLOAT
+  bool
+  default y
+  depends on SLEEP && TOYBOX_FLOAT
+  help
+    The delay can be a decimal fraction. An optional suffix can be "m"
+    (minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
+*/
+
+#include "toys.h"
+
+void sleep_main(void)
+{
+
+  if (!CFG_TOYBOX_FLOAT) toys.exitval = sleep(atol(*toys.optargs));
+  else {
+    char *arg;
+    double d = strtod(*toys.optargs, &arg);
+    struct timespec tv;
+
+    // Parse suffix
+    if (*arg) {
+      int ismhd[]={1,60,3600,86400};
+      char *smhd = "smhd", *c = strchr(smhd, *arg);
+      if (!c) error_exit("Unknown suffix '%c'", *arg);
+      d *= ismhd[c-smhd];
+    }
+
+    tv.tv_nsec=1000000000*(d-(tv.tv_sec = (unsigned long)d));
+    toys.exitval = !!nanosleep(&tv, NULL);
+  }
+}
diff --git a/toys/posix/sort.c b/toys/posix/sort.c
new file mode 100644 (file)
index 0000000..41b020b
--- /dev/null
@@ -0,0 +1,400 @@
+/* sort.c - put input lines into order
+ *
+ * Copyright 2004, 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/007904975/utilities/sort.html
+
+USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")USE_SORT_BIG("S:T:m" "o:k*t:xbMcszdfi") "run", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SORT
+  bool "sort"
+  default y
+  help
+    usage: sort [-run] [FILE...]
+
+    Sort all lines of text from input files (or stdin) to stdout.
+
+    -r    reverse
+    -u    unique lines only
+    -n    numeric order (instead of alphabetical)
+
+config SORT_BIG
+  bool "SuSv3 options (Support -ktcsbdfiozM)"
+  default y
+  depends on SORT
+  help
+    usage: sort [-bcdfiMsz] [-k#[,#[x]] [-t X]] [-o FILE]
+
+    -b    ignore leading blanks (or trailing blanks in second part of key)
+    -c    check whether input is sorted
+    -d    dictionary order (use alphanumeric and whitespace chars only)
+    -f    force uppercase (case insensitive sort)
+    -i    ignore nonprinting characters
+    -M    month sort (jan, feb, etc).
+    -x    Hexadecimal numerical sort
+    -s    skip fallback sort (only sort with keys)
+    -z    zero (null) terminated input
+    -k    sort by "key" (see below)
+    -t    use a key separator other than whitespace
+    -o    output to FILE instead of stdout
+
+    Sorting by key looks at a subset of the words on each line.  -k2
+    uses the second word to the end of the line, -k2,2 looks at only
+    the second word, -k2,4 looks from the start of the second to the end
+    of the fourth word.  Specifying multiple keys uses the later keys as
+    tie breakers, in order.  A type specifier appended to a sort key
+    (such as -2,2n) applies only to sorting that key.
+
+config SORT_FLOAT
+  bool "Floating point (-g)"
+  default y
+  depends on SORT_BIG
+  help
+    usage: sort [-g]
+
+    This version of sort requires floating point.
+
+    -g general numeric sort (double precision with nan and inf)
+
+*/
+
+#define FOR_sort
+#include "toys.h"
+
+GLOBALS(
+  char *key_separator;
+  struct arg_list *raw_keys;
+  char *outfile;
+  char *ignore1, ignore2;   // GNU compatability NOPs for -S and -T.
+
+  void *key_list;
+  int linecount;
+  char **lines;
+)
+
+// The sort types are n, g, and M.
+// u, c, s, and z apply to top level only, not to keys.
+// b at top level implies bb.
+// The remaining options can be applied to search keys.
+
+#define FLAG_bb (1<<31)  // Ignore trailing blanks
+
+struct sort_key
+{
+  struct sort_key *next_key;  // linked list
+  unsigned range[4];          // start word, start char, end word, end char
+  int flags;
+};
+
+// Copy of the part of this string corresponding to a key/flags.
+
+static char *get_key_data(char *str, struct sort_key *key, int flags)
+{
+  int start=0, end, len, i, j;
+
+  // Special case whole string, so we don't have to make a copy
+
+  if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3]
+    && !(flags&(FLAG_b&FLAG_d&FLAG_f&FLAG_i&FLAG_bb))) return str;
+
+  // Find start of key on first pass, end on second pass
+
+  len = strlen(str);
+  for (j=0; j<2; j++) {
+    if (!key->range[2*j]) end=len;
+
+    // Loop through fields
+    else {
+      end=0;
+      for (i=1; i < key->range[2*j]+j; i++) {
+
+        // Skip leading blanks
+        if (str[end] && !TT.key_separator)
+          while (isspace(str[end])) end++;
+
+        // Skip body of key
+        for (; str[end]; end++) {
+          if (TT.key_separator) {
+            if (str[end]==*TT.key_separator) break;
+          } else if (isspace(str[end])) break;
+        }
+      }
+    }
+    if (!j) start=end;
+  }
+
+  // Key with explicit separator starts after the separator
+  if (TT.key_separator && str[start]==*TT.key_separator) start++;
+
+  // Strip leading and trailing whitespace if necessary
+  if (flags&FLAG_b) while (isspace(str[start])) start++;
+  if (flags&FLAG_bb) while (end>start && isspace(str[end-1])) end--;
+
+  // Handle offsets on start and end
+  if (key->range[3]) {
+    end += key->range[3]-1;
+    if (end>len) end=len;
+  }
+  if (key->range[1]) {
+    start += key->range[1]-1;
+    if (start>len) start=len;
+  }
+
+  // Make the copy
+  if (end<start) end=start;
+  str = xstrndup(str+start, end-start);
+
+  // Handle -d
+  if (flags&FLAG_d) {
+    for (start = end = 0; str[end]; end++)
+      if (isspace(str[end]) || isalnum(str[end])) str[start++] = str[end];
+    str[start] = 0;
+  }
+
+  // Handle -i
+  if (flags&FLAG_i) {
+    for (start = end = 0; str[end]; end++)
+      if (isprint(str[end])) str[start++] = str[end];
+    str[start] = 0;
+  }
+
+  // Handle -f
+  if (flags*FLAG_f) for(i=0; str[i]; i++) str[i] = toupper(str[i]);
+
+  return str;
+}
+
+// append a sort_key to key_list.
+
+static struct sort_key *add_key(void)
+{
+  void **stupid_compiler = &TT.key_list;
+  struct sort_key **pkey = (struct sort_key **)stupid_compiler;
+
+  while (*pkey) pkey = &((*pkey)->next_key);
+  return *pkey = xzalloc(sizeof(struct sort_key));
+}
+
+// Perform actual comparison
+static int compare_values(int flags, char *x, char *y)
+{
+  int ff = flags & (FLAG_n|FLAG_g|FLAG_M|FLAG_x);
+
+  // Ascii sort
+  if (!ff) return strcmp(x, y);
+
+  if (CFG_SORT_FLOAT && ff == FLAG_g) {
+    char *xx,*yy;
+    double dx = strtod(x,&xx), dy = strtod(y,&yy);
+    int xinf, yinf;
+
+    // not numbers < NaN < -infinity < numbers < +infinity
+
+    if (x==xx) return y==yy ? 0 : -1;
+    if (y==yy) return 1;
+
+    // Check for isnan
+    if (dx!=dx) return (dy!=dy) ? 0 : -1;
+    if (dy!=dy) return 1;
+
+    // Check for infinity.  (Could underflow, but avoids needing libm.)
+    xinf = (1.0/dx == 0.0);
+    yinf = (1.0/dy == 0.0);
+    if (xinf) {
+      if(dx<0) return (yinf && dy<0) ? 0 : -1;
+      return (yinf && dy>0) ? 0 : 1;
+    }
+    if (yinf) return dy<0 ? 1 : -1;
+
+    return dx>dy ? 1 : (dx<dy ? -1 : 0);
+  } else if (CFG_SORT_BIG && ff == FLAG_M) {
+    struct tm thyme;
+    int dx;
+    char *xx,*yy;
+
+    xx = strptime(x,"%b",&thyme);
+    dx = thyme.tm_mon;
+    yy = strptime(y,"%b",&thyme);
+    if (!xx) return !yy ? 0 : -1;
+    else if (!yy) return 1;
+    else return dx==thyme.tm_mon ? 0 : dx-thyme.tm_mon;
+
+  } else if (CFG_SORT_BIG && ff == FLAG_x) {
+    return strtol(x, NULL, 16)-strtol(y, NULL, 16);
+  // This has to be ff == FLAG_n
+  } else {
+    // Full floating point version of -n
+    if (CFG_SORT_FLOAT) {
+      double dx = atof(x), dy = atof(y);
+
+      return dx>dy ? 1 : (dx<dy ? -1 : 0);
+    // Integer version of -n for tiny systems
+    } else return atoi(x)-atoi(y);
+  }
+}
+
+// Callback from qsort(): Iterate through key_list and perform comparisons.
+static int compare_keys(const void *xarg, const void *yarg)
+{
+  int flags = toys.optflags, retval = 0;
+  char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg;
+  struct sort_key *key;
+
+  if (CFG_SORT_BIG) {
+    for (key=(struct sort_key *)TT.key_list; !retval && key;
+       key = key->next_key)
+    {
+      flags = key->flags ? key->flags : toys.optflags;
+
+      // Chop out and modify key chunks, handling -dfib
+
+      x = get_key_data(xx, key, flags);
+      y = get_key_data(yy, key, flags);
+
+      retval = compare_values(flags, x, y);
+
+      // Free the copies get_key_data() made.
+
+      if (x != xx) free(x);
+      if (y != yy) free(y);
+
+      if (retval) break;
+    }
+  } else retval = compare_values(flags, xx, yy);
+
+  // Perform fallback sort if necessary
+  if (!retval && !(CFG_SORT_BIG && (toys.optflags&FLAG_s))) {
+    retval = strcmp(xx, yy);
+    flags = toys.optflags;
+  }
+
+  return retval * ((flags&FLAG_r) ? -1 : 1);
+}
+
+// Callback from loopfiles to handle input files.
+static void sort_read(int fd, char *name)
+{
+  // Read each line from file, appending to a big array.
+
+  for (;;) {
+    char * line = (CFG_SORT_BIG && (toys.optflags&FLAG_z))
+             ? get_rawline(fd, NULL, 0) : get_line(fd);
+
+    if (!line) break;
+
+    // handle -c here so we don't allocate more memory than necessary.
+    if (CFG_SORT_BIG && (toys.optflags&FLAG_c)) {
+      int j = (toys.optflags&FLAG_u) ? -1 : 0;
+
+      if (TT.lines && compare_keys((void *)&TT.lines, &line)>j)
+        error_exit("%s: Check line %d\n", name, TT.linecount);
+      free(TT.lines);
+      TT.lines = (char **)line;
+    } else {
+      if (!(TT.linecount&63))
+        TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64));
+      TT.lines[TT.linecount] = line;
+    }
+    TT.linecount++;
+  }
+}
+
+void sort_main(void)
+{
+  int idx, fd = 1;
+
+  // Open output file if necessary.
+  if (CFG_SORT_BIG && TT.outfile)
+    fd = xcreate(TT.outfile, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+
+  // Parse -k sort keys.
+  if (CFG_SORT_BIG && TT.raw_keys) {
+    struct arg_list *arg;
+
+    for (arg = TT.raw_keys; arg; arg = arg->next) {
+      struct sort_key *key = add_key();
+      char *temp;
+      int flag;
+
+      idx = 0;
+      temp = arg->arg;
+      while (*temp) {
+        // Start of range
+        key->range[2*idx] = (unsigned)strtol(temp, &temp, 10);
+        if (*temp=='.')
+          key->range[(2*idx)+1] = (unsigned)strtol(temp+1, &temp, 10);
+
+        // Handle flags appended to a key type.
+        for (;*temp;temp++) {
+          char *temp2, *optlist;
+
+          // Note that a second comma becomes an "Unknown key" error.
+
+          if (*temp==',' && !idx++) {
+            temp++;
+            break;
+          }
+
+          // Which flag is this?
+
+          optlist = toys.which->options;
+          temp2 = strchr(optlist, *temp);
+          flag = (1<<(optlist-temp2+strlen(optlist)-1));
+
+          // Was it a flag that can apply to a key?
+
+          if (!temp2 || flag>FLAG_b
+            || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z)))
+          {
+            error_exit("Unknown key option.");
+          }
+          // b after , means strip _trailing_ space, not leading.
+          if (idx && flag==FLAG_b) flag = FLAG_bb;
+          key->flags |= flag;
+        }
+      }
+    }
+  }
+
+  // global b flag strips both leading and trailing spaces
+  if (toys.optflags&FLAG_b) toys.optflags |= FLAG_bb;
+
+  // If no keys, perform alphabetic sort over the whole line.
+  if (CFG_SORT_BIG && !TT.key_list) add_key()->range[0] = 1;
+
+  // Open input files and read data, populating TT.lines[TT.linecount]
+  loopfiles(toys.optargs, sort_read);
+
+  // The compare (-c) logic was handled in sort_read(),
+  // so if we got here, we're done.
+  if (CFG_SORT_BIG && (toys.optflags&FLAG_c)) goto exit_now;
+
+  // Perform the actual sort
+  qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys);
+
+  // handle unique (-u)
+  if (toys.optflags&FLAG_u) {
+    int jdx;
+
+    for (jdx=0, idx=1; idx<TT.linecount; idx++) {
+      if (!compare_keys(&TT.lines[jdx], &TT.lines[idx]))
+        free(TT.lines[idx]);
+      else TT.lines[++jdx] = TT.lines[idx];
+    }
+    if (TT.linecount) TT.linecount = jdx+1;
+  }
+
+  // Output result
+  for (idx = 0; idx<TT.linecount; idx++) {
+    char *s = TT.lines[idx];
+    xwrite(fd, s, strlen(s));
+    if (CFG_TOYBOX_FREE) free(s);
+    xwrite(fd, "\n", 1);
+  }
+
+exit_now:
+  if (CFG_TOYBOX_FREE) {
+    if (fd != 1) close(fd);
+    free(TT.lines);
+  }
+}
diff --git a/toys/posix/split.c b/toys/posix/split.c
new file mode 100644 (file)
index 0000000..f80527d
--- /dev/null
@@ -0,0 +1,108 @@
+/* split.c - split a file into smaller files
+ *
+ * Copyright 2013 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/split.html
+ *
+ * Standard does not cover:
+ * - should splitting an empty file produce an empty outfile? (Went with "no".)
+ * - permissions on output file
+
+USE_SPLIT(NEWTOY(split, ">2a#<1=2>9b#<1l#<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SPLIT
+  bool "split"
+  default y
+  help
+    usage: split [-a SUFFIX_LEN] [-b BYTES] [-l LINES] [INPUT [OUTPUT]]
+
+    Copy INPUT (or stdin) data to a series of OUTPUT (or "x") files with
+    alphabetically increasing suffix (aa, ab, ac... az, ba, bb...).
+
+    -a Suffix length (default 2)
+    -b BYTES/file (10, 10k, 10m, 10g...)
+    -l LINES/file (default 1000)
+*/
+
+#define FOR_split
+#include "toys.h"
+
+GLOBALS(
+  long lines;
+  long bytes;
+  long suflen;
+
+  char *outfile;
+)
+
+void do_split(int infd, char *in)
+{
+  unsigned long bytesleft, linesleft, filenum, len, pos;
+  int outfd = -1;
+  struct stat st;
+
+  // posix doesn't cover permissions on output file, so copy input (or 0777)
+  st.st_mode = 0777;
+  fstat(infd, &st);
+
+  len = pos = filenum = bytesleft = linesleft = 0;
+  for (;;) {
+    int i, j;
+
+    // Refill toybuf?
+    if (len == pos) {
+      if (!(len = xread(infd, toybuf, sizeof(toybuf)))) break;
+      pos = 0;
+    }
+
+    // Start new output file?
+    if ((TT.bytes && !bytesleft) || (TT.lines && !linesleft)) {
+      char *s = TT.outfile + strlen(TT.outfile);
+
+      j = filenum++;
+      for (i = 0; i<TT.suflen; i++) {
+        *(--s) = 'a'+(j%26);
+        j /= 26;
+      }
+      if (j) error_exit("bad suffix");
+      bytesleft = TT.bytes;
+      linesleft = TT.lines;
+      if (outfd != -1) close(outfd);
+      outfd = xcreate(TT.outfile, O_RDWR|O_CREAT|O_TRUNC, st.st_mode & 0777);
+    }
+
+    // Write next chunk of output.
+    if (TT.lines) {
+      for (i = pos; i < len; ) {
+        if (toybuf[i++] == '\n' && !--linesleft) break;
+        if (!--bytesleft) break;
+      }
+      j = i - pos;
+    } else {
+      j = len - pos;
+      if (j > bytesleft) j = bytesleft;
+      bytesleft -= j;
+    }
+    xwrite(outfd, toybuf+pos, j);
+    pos += j;
+  }
+
+  if (CFG_TOYBOX_FREE) {
+    if (outfd != -1) close(outfd);
+    if (infd) close(infd);
+    free(TT.outfile);
+  }
+  xexit();
+}
+
+void split_main(void)
+{
+  if (!TT.bytes && !TT.lines) TT.lines = 1000;
+
+  // Allocate template for output filenames
+  TT.outfile = xmsprintf("%s% *c", (toys.optc == 2) ? toys.optargs[1] : "x",
+    (int)TT.suflen, ' ');
+
+  // We only ever use one input, but this handles '-' or no input for us.
+  loopfiles(toys.optargs, do_split);
+}
diff --git a/toys/posix/stty.c b/toys/posix/stty.c
new file mode 100644 (file)
index 0000000..1aac1ba
--- /dev/null
@@ -0,0 +1,1126 @@
+/* stty.c - change and print terminal line settings
+ *
+ * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/stty.html
+
+USE_STTY(NEWTOY(stty, "?F:ag", TOYFLAG_BIN))
+
+config STTY
+  bool "stty"
+  default y
+  help
+    usage: stty [-F device] [-a] [-g] [...]
+
+    Without arguments, prints baud rate, line discipline,
+    and deviations from stty sane
+
+    -F DEVICE   Open device instead of stdin
+    -a    Print all current settings in human-readable form
+    -g    Print in stty-readable form
+*/
+
+/* $NetBSD: stty.c,v 1.19 2003/08/07 09:05:42 agc Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *  may be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define FOR_stty
+#include "toys.h"
+
+GLOBALS(
+  char *device;
+  int col;
+  const char *label;
+)
+
+#include <sys/cdefs.h>
+#include <limits.h>
+#include <termios.h>
+
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE '\0'
+#endif
+
+#ifndef _PATH_URANDOM
+#define _PATH_URANDOM   "/dev/urandom"
+#endif
+
+#define  LINELENGTH  80
+struct info {
+  int fd;          /* file descriptor */
+  int ldisc;        /* line discipline */
+  int off;        /* turn off */
+  int set;        /* need set */
+  int wset;        /* need window set */
+  char *arg;        /* argument */
+  struct termios t;      /* terminal info */
+  struct winsize win;      /* window info */
+};
+
+struct cchar {
+  const char *name;
+  int sub;
+  u_char def;
+};
+
+enum FMT { STTY_NOTSET, STTY_GFLAG, STTY_BSD, STTY_POSIX };
+
+
+struct modes {
+  const char *name;
+  tcflag_t set;
+  tcflag_t unset;
+};
+
+/*
+ * The code in optlist() depends on minus options following regular
+ * options, i.e. "foo" must immediately precede "-foo".
+ */
+const struct modes cmodes[] = {
+  { "cs5",  CS5, CSIZE },
+  { "cs6",  CS6, CSIZE },
+  { "cs7",  CS7, CSIZE },
+  { "cs8",  CS8, CSIZE },
+  { "cstopb",  CSTOPB, 0 },
+  { "-cstopb",  0, CSTOPB },
+  { "cread",  CREAD, 0 },
+  { "-cread",  0, CREAD },
+  { "parenb",  PARENB, 0 },
+  { "-parenb",  0, PARENB },
+  { "parodd",  PARODD, 0 },
+  { "-parodd",  0, PARODD },
+  { "parity",  PARENB | CS7, PARODD | CSIZE },
+  { "-parity",  CS8, PARODD | PARENB | CSIZE },
+  { "evenp",  PARENB | CS7, PARODD | CSIZE },
+  { "-evenp",  CS8, PARODD | PARENB | CSIZE },
+  { "oddp",  PARENB | CS7 | PARODD, CSIZE },
+  { "-oddp",  CS8, PARODD | PARENB | CSIZE },
+  { "pass8",  CS8, PARODD | PARENB | CSIZE },
+  { "-pass8",  PARENB | CS7, PARODD | CSIZE },
+  { "hupcl",  HUPCL, 0 },
+  { "-hupcl",  0, HUPCL },
+  { "hup",  HUPCL, 0 },
+  { "-hup",  0, HUPCL },
+  { "clocal",  CLOCAL, 0 },
+  { "-clocal",  0, CLOCAL },
+  { "crtscts",  CRTSCTS, 0 },
+  { "-crtscts",  0, CRTSCTS },
+  { .name = NULL },
+};
+
+const struct modes imodes[] = {
+  { "ignbrk",  IGNBRK, 0 },
+  { "-ignbrk",  0, IGNBRK },
+  { "brkint",  BRKINT, 0 },
+  { "-brkint",  0, BRKINT },
+  { "ignpar",  IGNPAR, 0 },
+  { "-ignpar",  0, IGNPAR },
+  { "parmrk",  PARMRK, 0 },
+  { "-parmrk",  0, PARMRK },
+  { "inpck",  INPCK, 0 },
+  { "-inpck",  0, INPCK },
+  { "istrip",  ISTRIP, 0 },
+  { "-istrip",  0, ISTRIP },
+  { "iutf8",  IUTF8, 0 },
+  { "-iutf8",  0, IUTF8 },
+  { "inlcr",  INLCR, 0 },
+  { "-inlcr",  0, INLCR },
+  { "igncr",  IGNCR, 0 },
+  { "-igncr",  0, IGNCR },
+  { "icrnl",  ICRNL, 0 },
+  { "-icrnl",  0, ICRNL },
+  { "ixon",  IXON, 0 },
+  { "-ixon",  0, IXON },
+  { "flow",  IXON, 0 },
+  { "-flow",  0, IXON },
+  { "ixoff",  IXOFF, 0 },
+  { "-ixoff",  0, IXOFF },
+  { "tandem",  IXOFF, 0 },
+  { "-tandem",  0, IXOFF },
+  { "iuclc",  IUCLC, 0 },
+  { "-iuclc",  0, IUCLC },
+  { "ixany",  IXANY, 0 },
+  { "-ixany",  0, IXANY },
+  { "decctlq",  0, IXANY },
+  { "-decctlq",  IXANY, 0 },
+  { "imaxbel",  IMAXBEL, 0 },
+  { "-imaxbel",  0, IMAXBEL },
+  { .name = NULL },
+};
+
+const struct modes lmodes[] = {
+  { "echo",  ECHO, 0 },
+  { "-echo",  0, ECHO },
+  { "echoe",  ECHOE, 0 },
+  { "-echoe",  0, ECHOE },
+  { "crterase",  ECHOE, 0 },
+  { "-crterase",  0, ECHOE },
+  { "crtbs",  ECHOE, 0 },  /* crtbs not supported, close enough */
+  { "-crtbs",  0, ECHOE },
+  { "echok",  ECHOK, 0 },
+  { "-echok",  0, ECHOK },
+  { "echoke",  ECHOKE, 0 },
+  { "-echoke",  0, ECHOKE },
+  { "crtkill",  ECHOKE, 0 },
+  { "-crtkill",  0, ECHOKE },
+  { "iexten",  IEXTEN, 0 },
+  { "-iexten",  0, IEXTEN },
+  { "echonl",  ECHONL, 0 },
+  { "-echonl",  0, ECHONL },
+  { "echoctl",  ECHOCTL, 0 },
+  { "-echoctl",  0, ECHOCTL },
+  { "ctlecho",  ECHOCTL, 0 },
+  { "-ctlecho",  0, ECHOCTL },
+  { "echoprt",  ECHOPRT, 0 },
+  { "-echoprt",  0, ECHOPRT },
+  { "prterase",  ECHOPRT, 0 },
+  { "-prterase",  0, ECHOPRT },
+  { "isig",  ISIG, 0 },
+  { "-isig",  0, ISIG },
+  { "icanon",  ICANON, 0 },
+  { "-icanon",  0, ICANON },
+  { "noflsh",  NOFLSH, 0 },
+  { "-noflsh",  0, NOFLSH },
+  { "tostop",  TOSTOP, 0 },
+  { "-tostop",  0, TOSTOP },
+  { "flusho",  FLUSHO, 0 },
+  { "-flusho",  0, FLUSHO },
+  { "xcase",  XCASE, 0 },
+  { "-xcase",  0, XCASE },
+  { "pendin",  PENDIN, 0 },
+  { "-pendin",  0, PENDIN },
+  { "crt",  ECHOE|ECHOKE|ECHOCTL,  0},
+  { "newcrt",  ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+  { "-newcrt",  ECHOK, ECHOE|ECHOKE|ECHOCTL },
+  { .name = NULL },
+};
+
+const struct modes omodes[] = {
+  { "opost",  OPOST, 0 },
+  { "-opost",  0, OPOST },
+  { "olcuc",  OLCUC, 0 },
+  { "-olcuc",  0, OLCUC },
+  { "litout",  0, OPOST },
+  { "-litout",  OPOST, 0 },
+  { "onlcr",  ONLCR, 0 },
+  { "-onlcr",  0, ONLCR },
+  { "ocrnl",  OCRNL, 0 },
+  { "-ocrnl",  0, OCRNL },
+  { "tabs",  0, TABDLY },
+  { "-tabs",  TABDLY, 0 },
+  { "onocr",  ONOCR, 0 },
+  { "-onocr",  0, ONOCR },
+  { "onlret",  ONLRET, 0 },
+  { "-onlret",  0, ONLRET },
+  { "onfill",  OFILL, 0 },
+  { "-onfill",  0, OFILL },
+  { "ofdel",  OFDEL, 0 },
+  { "-ofdel",  0, OFDEL },
+  { "nl1",  NL1, 0 },
+  { "nl0",  NL0, 0 },
+  { "cr3",  CR3, 0 },
+  { "cr2",  CR2, 0 },
+  { "cr1",  CR1, 0 },
+  { "cr0",  CR0, 0 },
+  { "tab3",  TAB3, 0 },
+  { "tab2",  TAB2, 0 },
+  { "tab1",  TAB1, 0 },
+  { "tab0",  TAB0, 0 },
+  { "bs1",  BS1, 0 },
+  { "bs0",  BS0, 0 },
+  { "vt1",  VT1, 0 },
+  { "vt0",  VT0, 0 },
+  { "ff1",  FF1, 0 },
+  { "ff0",  FF0, 0 },
+  { .name = NULL },
+};
+
+/*
+ * Special control characters.
+ *
+ * Cchars1 are the standard names, cchars2 are the old aliases.
+ * The first are displayed, but both are recognized on the
+ * command line.
+ */
+const struct cchar cchars1[] = {
+  { "discard",  VDISCARD,   CDISCARD },
+#ifdef VDSUSP
+  { "dsusp",   VDSUSP,    CDSUSP },
+#endif
+  { "eof",  VEOF,    CEOF },
+  { "eol",  VEOL,    CEOL },
+  { "eol2",  VEOL2,    CEOL },
+  { "erase",  VERASE,    CERASE },
+  { "intr",  VINTR,    CINTR },
+  { "kill",  VKILL,    CKILL },
+  { "lnext",  VLNEXT,    CLNEXT },
+  { "min",  VMIN,    CMIN },
+  { "quit",  VQUIT,    CQUIT },
+  { "reprint",  VREPRINT,   CREPRINT },
+  { "start",  VSTART,    CSTART },
+#ifdef VSTATUS
+  { "status",  VSTATUS,   CSTATUS },
+#endif
+  { "stop",  VSTOP,    CSTOP },
+  { "susp",  VSUSP,    CSUSP },
+  { "time",  VTIME,    CTIME },
+  { "werase",  VWERASE,  CWERASE },
+  { .name = NULL },
+};
+
+const struct cchar cchars2[] = {
+  { "brk",  VEOL,    CEOL },
+  { "flush",  VDISCARD,   CDISCARD },
+  { "rprnt",  VREPRINT,   CREPRINT },
+  { .name = NULL },
+};
+
+static int c_cchar(const void *, const void *);
+void  f_all(struct info *);
+void  f_cbreak(struct info *);
+void  f_columns(struct info *);
+void  f_dec(struct info *);
+void  f_ek(struct info *);
+void  f_lcase(struct info *);
+void  f_insane(struct info *);
+void  f_ispeed(struct info *);
+void  f_nl(struct info *);
+void  f_ospeed(struct info *);
+void  f_raw(struct info *);
+void  f_rows(struct info *);
+void  f_sane(struct info *);
+void  f_cooked(struct info *);
+void  stty_f_size(struct info *);
+void  f_speed(struct info *);
+void  f_ostart(struct info *);
+void  f_ostop(struct info *);
+
+static const struct key {
+  const char *name;      /* name */
+  void (*f)(struct info *);    /* function */
+#define  F_NEEDARG  0x01      /* needs an argument */
+#define  F_OFFOK    0x02      /* can turn off */
+  int flags;
+} keys[] = {
+  { "all",  f_all,    0 },
+  { "cbreak",  f_cbreak,  F_OFFOK },
+  { "cols",  f_columns,  F_NEEDARG },
+  { "columns",  f_columns,  F_NEEDARG },
+  { "cooked",   f_cooked,    F_OFFOK },
+  { "dec",  f_dec,    0 },
+  { "ek",  f_ek,  0 },
+  { "lcase",  f_lcase,  F_OFFOK },
+  { "insane",  f_insane,  0 },
+  { "ispeed",  f_ispeed,  F_NEEDARG },
+  { "nl",    f_nl,    F_OFFOK },
+  { "ospeed",  f_ospeed,  F_NEEDARG },
+  { "raw",  f_raw,    F_OFFOK },
+  { "rows",  f_rows,    F_NEEDARG },
+  { "sane",  f_sane,    0 },
+  { "size",  stty_f_size,    0 },
+  { "speed",  f_speed,  0 },
+};
+
+static int c_key(const void *, const void *);
+
+static void binit(const char *);
+static void bput(const char *);
+static const char *ccval(const struct cchar *, int);
+
+static void usage(void)
+{
+
+  (void)fprintf(stderr, "usage: %s [-a|-g] [-F file] [options]\n", "stty");
+  exit(1);
+  /* NOTREACHED */
+}
+  
+#define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
+struct baud_value_map {
+  unsigned short baud;
+  unsigned int value;
+};  
+
+static const struct baud_value_map bauds[] = {
+  {B0, 0},
+  {B50, 50},
+  {B75, 75},
+  {B110, 110},
+  {B134, 134},
+  {B150, 150},
+  {B200, 200},
+  {B300, 300},
+  {B600, 600},
+  {B1200, 1200},
+  {B1800, 1800},
+  {B2400, 2400},
+  {B4800, 4800},
+  {B9600, 9600},
+  {B19200, 19200},
+  {B38400, 38400},
+  {B57600, 57600},
+  {B115200, 115200},
+  {B230400, 230400},
+  {B460800, 460800},
+  {B921600, 921600},
+};   
+
+enum { NUM_OF_BAUDS = ARRAY_SIZE(bauds) };
+
+/*
+ * baud_to_value() converts baud specified in enum values like B600, B38400 etc.. to 
+ * actual baud value.
+ */
+unsigned int baud_to_value(speed_t baud)
+{  
+  int i = 0;
+
+  for(i = 0; i < NUM_OF_BAUDS; i++) {
+    if (baud == bauds[i].baud) return bauds[i].value;
+  }
+
+  return 0;
+}
+
+/*
+ * Converts speed value to baud for terminal settings
+ */
+speed_t value_to_baud(unsigned int value)                                                             
+{
+  int i = 0;
+
+  for(i = 0; i < NUM_OF_BAUDS; i++) {
+    if (value == baud_to_value(bauds[i].baud)) return bauds[i].baud;
+  }
+
+  return (speed_t) - 1;
+}
+
+/*Print the terminal settings to stdout, depending upon the argument,
+ * if '-a' is specified then all the values are printed.
+ * if no arguments are specified then only deviation from SANE settings is printed.
+ */
+void stty_print(struct termios *tp, struct winsize *wp, int ldisc, enum FMT fmt)
+{
+  const struct cchar *p;
+  long tmp;
+  u_char *cc;
+  int cnt, ispeed, ospeed;
+  char buf1[100];
+
+  cnt = 0;
+
+  /* Line speed and Descipline. */
+  ispeed = cfgetispeed(tp);
+  ospeed = cfgetospeed(tp);
+  if (ispeed != ospeed) cnt += printf("ispeed %d baud; ospeed %d baud;", baud_to_value(ispeed), baud_to_value(ospeed));
+  else cnt += printf("speed %d baud;", baud_to_value(ispeed));
+  if (fmt >= STTY_BSD) cnt += printf(" %d rows; %d columns;", wp->ws_row, wp->ws_col);
+  cnt += printf(" line = %u;",tp->c_line);
+  if (cnt) xprintf("\n");
+
+#define SANE_SET  1
+#define SANE_UNSET  0
+#define  on(f)  ((tmp&f) != 0)
+// print all settings if '-a' is specified, else only deviation from 'sane'
+#define put(n, f, d) \
+  if (fmt >= STTY_BSD || on(f) != d) \
+    bput(n + on(f)); 
+
+  /* "local" flags */
+  tmp = tp->c_lflag;
+  binit("lflags");
+  put("-isig", ISIG, SANE_SET);
+  put("-icanon", ICANON, SANE_SET);
+  put("-iexten", IEXTEN, SANE_SET);
+  put("-echo", ECHO, SANE_SET);
+  put("-echoe", ECHOE, SANE_SET);
+  put("-echok", ECHOK, SANE_SET);
+  put("-echonl", ECHONL, SANE_UNSET);
+  put("-noflsh", NOFLSH, SANE_UNSET);
+  put("-xcase", XCASE, SANE_UNSET);
+  put("-tostop", TOSTOP, SANE_UNSET);
+  put("-echoprt", ECHOPRT, SANE_UNSET);
+  put("-echoctl", ECHOCTL, SANE_SET);
+  put("-echoke", ECHOKE, SANE_SET);
+
+  /* input flags */
+  tmp = tp->c_iflag;
+  binit("iflags");
+  put("-ignbrk", IGNBRK, SANE_UNSET);
+  put("-brkint", BRKINT, SANE_SET);
+  put("-ignpar", IGNPAR, SANE_UNSET);
+  put("-parmrk", PARMRK, SANE_UNSET);
+  put("-inpck", INPCK, SANE_UNSET);
+  put("-istrip", ISTRIP, SANE_UNSET);
+  put("-inlcr", INLCR, SANE_UNSET);
+  put("-igncr", IGNCR, SANE_UNSET);
+  put("-icrnl", ICRNL, SANE_SET);
+  put("-iutf8", IUTF8, SANE_UNSET);
+  put("-ixon", IXON, SANE_SET);
+  put("-ixoff", IXOFF, SANE_UNSET);
+  put("-iuclc", IUCLC, SANE_UNSET);
+  put("-ixany", IXANY, SANE_UNSET);
+  put("-imaxbel", IMAXBEL, SANE_SET);
+
+  /* output flags */
+  tmp = tp->c_oflag;
+  binit("oflags");
+  put("-opost", OPOST, SANE_SET);
+  put("-olcuc", OLCUC, SANE_UNSET);
+  put("-ocrnl", OCRNL, SANE_UNSET);
+  put("-onlcr", ONLCR, SANE_SET);
+  put("-onocr", ONOCR, SANE_UNSET);
+  put("-onlret", ONLRET, SANE_UNSET);
+  put("-ofill", OFILL, SANE_UNSET);
+  put("-ofdel", OFDEL, SANE_UNSET);
+  switch(tmp & NLDLY) {
+    case NL1:
+      bput("nl1");
+      break;   
+    case NL0:
+      if (fmt >= STTY_BSD) bput("nl0"); //this is sane setting
+      break;
+  }
+   switch(tmp & CRDLY) {
+  case CR3:
+    bput("cr3");
+    break;
+  case CR2:
+    bput("cr2");
+    break;
+  case CR1:
+    bput("cr1");
+    break;
+  case CR0:
+    if (fmt >= STTY_BSD) bput("cr0"); //this is sane setting.
+    break;
+  }
+
+  switch(tmp & TABDLY) {
+  case TAB3:
+    bput("tab3");
+    break;
+  case TAB2:
+    bput("tab2");
+    break;
+  case TAB1:
+    bput("tab1");
+    break;
+  case TAB0:
+    if (fmt >= STTY_BSD) bput("tab0"); //this is sane setting
+    break;
+  }
+   
+  switch(tmp & BSDLY) {
+    case BS1:
+      bput("bs1");
+      break;   
+    case BS0:
+      if (fmt >= STTY_BSD) bput("bs0"); //this is sane setting
+      break;
+  }
+  switch(tmp & VTDLY) {
+    case VT1:
+      bput("vt1");
+      break;   
+    case VT0:
+      if (fmt >= STTY_BSD) bput("vt0"); //this is sane setting
+      break;
+  }
+  switch(tmp & FFDLY) {
+    case FF1:
+      bput("ff1");
+      break;   
+    case FF0:
+      if (fmt >= STTY_BSD) bput("ff0"); //this is sane setting
+      break;
+  }
+
+  /* control flags (hardware state) */
+  tmp = tp->c_cflag;
+  binit("cflags");
+  put("-parenb", PARENB, SANE_UNSET);
+  put("-parodd", PARODD, SANE_UNSET);
+  switch(tmp&CSIZE) {
+  case CS5:
+    bput("cs5");
+    break;
+  case CS6:
+    bput("cs6");
+    break;
+  case CS7:
+    bput("cs7");
+    break;
+  case CS8:
+    if (fmt >= STTY_BSD) bput("cs8"); //this is sane setting
+    break;
+  }
+  put("-hupcl", HUPCL, SANE_SET);
+  put("-cstopb", CSTOPB, SANE_UNSET);
+  put("-cread", CREAD, SANE_SET);
+  put("-clocal", CLOCAL, SANE_UNSET);
+  put("-crtscts", CRTSCTS, SANE_UNSET);
+
+  /* special control characters */
+  cc = tp->c_cc;
+  binit("cchars");
+  for (p = cchars1; p->name; ++p) {
+    if((fmt < STTY_BSD) && (cc[p->sub] == p->def)) continue;
+    (void)snprintf(buf1, sizeof(buf1), "%s = %s;",
+        p->name, ccval(p, cc[p->sub]));
+    bput(buf1);
+  }
+  for (p = cchars2; p->name; ++p) {
+    if((fmt < STTY_BSD) && (cc[p->sub] == p->def)) continue;
+    (void)snprintf(buf1, sizeof(buf1), "%s = %s;",
+        p->name, ccval(p, cc[p->sub]));
+    bput(buf1);
+  }
+
+  binit(NULL);
+}
+
+/* 
+ * set the printing cursor on a new label 
+ */
+static void binit(const char *lb)
+{
+
+  if (TT.col) {
+    xprintf("\n");
+    TT.col = 0;
+  }
+  TT.label = lb;
+}
+
+/*
+ * print the string, wrapping the line at LINELENGTH, i.e 80
+ */
+static void bput(const char *s)
+{
+
+  if (TT.col == 0) {
+    TT.col = printf("%s", s);
+    return;
+  }
+  
+  if ((TT.col + strlen(s)) > LINELENGTH) {
+    xprintf("\n");
+    TT.col = printf("%s", s);
+    return;
+  }
+  TT.col += printf(" %s", s);
+}
+
+/*
+ * this function gets the control character values.
+ */
+static const char* ccval(const struct cchar *p, int c)
+{
+  static char buf[5];
+  char *bp;
+
+  if (p->sub == VMIN || p->sub == VTIME) {
+    (void)snprintf(buf, sizeof(buf), "%d", c); //VMIN and VTIME values are specified in integer values.
+    return (buf);
+  }
+
+  if (c == _POSIX_VDISABLE) return ("<undef>");
+  bp = buf;
+  if (c & 0200) {
+    *bp++ = 'M';
+    *bp++ = '-';
+    c &= 0177;
+  }
+  if (c == 0177) {
+    *bp++ = '^';
+    *bp++ = '?';
+  }
+  else if (c < 040) {
+    *bp++ = '^';
+    *bp++ = c + '@';
+  }
+  else *bp++ = c;
+
+  *bp = '\0';
+  return (buf);
+}
+
+
+
+
+#undef CHK
+#define  CHK(s)  (!strcmp(name, s))
+
+/* 
+ * search the given keyword in modes and apply the changes accordingly
+ */
+int msearch(char ***argvp, struct info *ip)
+{
+  const struct modes *mp;
+  char *name;
+
+  name = **argvp;
+
+  for (mp = cmodes; mp->name; ++mp)
+    if (CHK(mp->name)) {
+      ip->t.c_cflag &= ~mp->unset;
+      ip->t.c_cflag |= mp->set;
+      ip->set = 1;
+      return (1);
+    }
+  for (mp = imodes; mp->name; ++mp)
+    if (CHK(mp->name)) {
+      ip->t.c_iflag &= ~mp->unset;
+      ip->t.c_iflag |= mp->set;
+      ip->set = 1;
+      return (1);
+    }
+  for (mp = lmodes; mp->name; ++mp)
+    if (CHK(mp->name)) {
+      ip->t.c_lflag &= ~mp->unset;
+      ip->t.c_lflag |= mp->set;
+      ip->set = 1;
+      return (1);
+    }
+  for (mp = omodes; mp->name; ++mp)
+    if (CHK(mp->name)) {
+      ip->t.c_oflag &= ~mp->unset;
+      ip->t.c_oflag |= mp->set;
+      ip->set = 1;
+      return (1);
+    }
+  return (0);
+}
+
+
+
+
+static int c_cchar(const void *a, const void *b)
+{
+    return (strcmp(((const struct cchar *)a)->name,
+    ((const struct cchar *)b)->name));
+}
+
+/*
+ * Search and apply the values for special characters
+ */
+int csearch(char ***argvp, struct info *ip)
+{
+  struct cchar *cp, tmp;
+  long val;
+  char *arg, *ep, *name;
+    
+  name = **argvp;
+
+  tmp.name = name;
+  if (!(cp = (struct cchar *)bsearch(&tmp, cchars1,
+    sizeof(cchars1)/sizeof(cchars1[0]) - 1, sizeof(cchars1[0]),
+    c_cchar)) &&
+    !(cp = (struct cchar *)bsearch(&tmp, cchars2,
+    sizeof(cchars2)/sizeof(cchars2[0]) - 1, sizeof(cchars2[0]),
+    c_cchar)))
+    return (0);
+
+  arg = *++*argvp;
+  if (!arg) {
+    error_msg("option requires an argument -- %s", name);
+    usage();
+  }
+#undef CHK
+#define CHK(s)  (*arg == s[0] && !strcmp(arg, s))
+  if (CHK("undef") || CHK("<undef>"))  ip->t.c_cc[cp->sub] = _POSIX_VDISABLE;
+  else if (cp->sub == VMIN || cp->sub == VTIME) {
+    val = strtol(arg, &ep, 10);
+    if (val == _POSIX_VDISABLE) {
+      error_msg("value of %ld would disable the option -- %s",
+        val, name);
+      usage();
+    }
+    if (val > UCHAR_MAX) {
+      error_msg("maximum option value is %d -- %s",
+        UCHAR_MAX, name);
+      usage();
+    }
+    if (*ep != '\0') {
+      error_msg("option requires a numeric argument -- %s", name);
+      usage();
+    }
+    ip->t.c_cc[cp->sub] = (cc_t)val;
+  } else if (arg[0] == '^')
+    ip->t.c_cc[cp->sub] = (arg[1] == '?') ? 0177 :
+      (arg[1] == '-') ? _POSIX_VDISABLE : arg[1] & 037;
+  else
+    ip->t.c_cc[cp->sub] = arg[0];
+  ip->set = 1;
+  return (1);
+}
+
+/*
+ * print terminal settings in STTY readable format (colon ':' separated)
+ */
+void gprint(struct termios *tp)
+{
+  int i;
+  xprintf("%x:%x:%x:%x",tp->c_iflag, tp->c_oflag,tp->c_cflag,tp->c_lflag);
+  for( i = 0; i < NCCS; i++)
+    xprintf(":%x",tp->c_cc[i]);
+  xprintf("\n");
+}
+
+/*
+ * Apply the STTY readable format values to terminal
+ */
+int gread(struct termios *tp, char *s) 
+{
+  unsigned long input_flag, outflag, controlflag, localflag;
+  unsigned int schar;
+  int count, i;
+  if(sscanf(s, "%lx:%lx:%lx:%lx%n", &input_flag, &outflag, &controlflag, &localflag, &count) != 4) return 0;
+
+  s += count;
+  for(i = 0; i < NCCS; i++) {
+    if(sscanf(s, ":%x%n", &schar, &count) != 1) return 0;
+    tp->c_cc[i] = schar;
+    s += count;
+  }
+
+  if(*s != '\0') return 0;
+
+  tp->c_iflag = input_flag;
+  tp->c_oflag = outflag;
+  tp->c_cflag = controlflag;
+  tp->c_lflag = localflag;
+
+  return 1;
+}
+
+/*
+ * compare key names
+ */
+static int c_key(const void *a, const void *b)
+{
+
+    return (strcmp(((const struct key *)a)->name,
+    ((const struct key *)b)->name));
+}
+
+/*
+ * Search for key mode inputs like, ispeed, ospeed, sane, etc...
+ * and call the callback function for the same.
+ */
+int ksearch(char ***argvp, struct info *ip)
+{
+  char *name;
+  struct key *kp, tmp;
+
+  name = **argvp;
+  if (*name == '-') {
+    ip->off = 1;
+    ++name;
+  } else ip->off = 0;
+
+  tmp.name = name;
+  if (!(kp = (struct key *)bsearch(&tmp, keys,
+    sizeof(keys)/sizeof(struct key), sizeof(struct key), c_key)))
+    return (0);
+  if (!(kp->flags & F_OFFOK) && ip->off) {
+    error_msg("illegal option -- %s", name);
+    usage();
+  }
+  if (kp->flags & F_NEEDARG && !(ip->arg = *++*argvp)) {
+    error_msg("option requires an argument -- %s", name);
+    usage();
+  }
+  kp->f(ip); //callback function call with arguments
+  return (1);
+}
+
+void f_all(struct info *ip)
+{
+  stty_print(&ip->t, &ip->win, ip->ldisc, STTY_BSD);
+}
+
+void f_cbreak(struct info *ip)
+{
+  if (ip->off) ip->t.c_lflag |= ICANON;
+  else ip->t.c_lflag &= ~ICANON;
+  ip->set = 1;
+}
+
+void f_columns(struct info *ip)
+{
+  ip->win.ws_col = atoi(ip->arg);
+  ip->wset = 1;
+}
+
+void f_dec(struct info *ip)
+{
+  ip->t.c_cc[VERASE] = (u_char)0177;
+  ip->t.c_cc[VKILL] = CTRL('u');
+  ip->t.c_cc[VINTR] = CTRL('c');
+  ip->t.c_lflag &= ~ECHOPRT;
+  ip->t.c_lflag |= ECHOE|ECHOKE|ECHOCTL;
+  ip->t.c_iflag &= ~IXANY;
+  ip->set = 1;
+}
+
+void f_ek(struct info *ip)
+{
+  ip->t.c_cc[VERASE] = CERASE;
+  ip->t.c_cc[VKILL] = CKILL;
+  ip->set = 1;
+}
+
+void f_lcase(struct info *ip)
+{
+  if(ip->off) {
+    ip->t.c_lflag &= ~XCASE;
+    ip->t.c_iflag &= ~IUCLC;
+    ip->t.c_oflag &= ~OLCUC;
+  }
+  else {
+    ip->t.c_lflag |= XCASE;
+    ip->t.c_iflag |= IUCLC;
+    ip->t.c_oflag |= OLCUC;
+  }
+  ip->set = 1;
+}
+
+void f_insane(struct info *ip)
+{
+  int f, r;
+  
+  r = f = open(_PATH_URANDOM, O_RDONLY, 0);
+  if (f >= 0) {
+    r = read(f, &(ip->t), sizeof(struct termios));
+    close(f);
+  }
+  if (r < 0) {
+    /* XXX not cryptographically secure! */
+    
+      srandom(time(NULL));
+    ip->t.c_iflag = random();
+    ip->t.c_oflag = random();
+    ip->t.c_cflag = random();
+    ip->t.c_lflag = random();
+    for (f = 0; f < NCCS; f++) {
+      ip->t.c_cc[f] = random() & 0xFF;
+    }
+    ip->t.c_ispeed = random();
+    ip->t.c_ospeed = random();
+  }
+  
+  ip->set = 1;
+}
+
+void f_ispeed(struct info *ip)
+{
+  cfsetispeed(&ip->t, value_to_baud((unsigned int)atolx(ip->arg)));
+  ip->set = 1;
+}
+
+void f_nl(struct info *ip)
+{
+  if (ip->off) {
+    ip->t.c_iflag |= ICRNL;
+    ip->t.c_iflag &= ~(INLCR | IGNCR);
+    ip->t.c_oflag |= ONLCR;
+    ip->t.c_oflag &= ~(OCRNL | ONLRET);
+  } else {
+    ip->t.c_iflag &= ~ICRNL;
+    ip->t.c_oflag &= ~ONLCR;
+  }
+  ip->set = 1;
+}
+
+void f_ospeed(struct info *ip)
+{
+  cfsetospeed(&ip->t, value_to_baud((unsigned int)atolx(ip->arg)));
+  ip->set = 1;
+}
+
+void f_raw(struct info *ip)
+{
+  if (ip->off) {
+    ip->off = 0;
+    f_cooked(ip);
+  }
+  else {
+    char *raw_val[] = {"-ignbrk", "-brkint", "-ignpar", "-parmrk", "-inpck", "-istrip",
+      "-inlcr", "-igncr", "-icrnl", "-ixon", "-ixoff", "-iuclc", "-ixany",
+      "-imaxbel", "-opost", "-isig", "-icanon", "-xcase", NULL}; //raw mode settings
+    char **ap;
+    for(ap = raw_val; *ap; ap++)                           
+      msearch(&ap, ip);
+    ip->t.c_cflag &= ~(CSIZE|PARENB);
+    ip->t.c_cflag |= CS8;
+    ip->t.c_cc[VMIN] = 1;
+    ip->t.c_cc[VTIME] = 0;
+    ip->set = 1;
+  }
+}
+
+void f_rows(struct info *ip)
+{
+  ip->win.ws_row = atoi(ip->arg);
+  ip->wset = 1;
+}
+
+void f_cooked(struct info *ip)
+{  
+  char *cooked_val[] = {"brkint", "ignpar", "istrip", 
+    "icrnl", "ixon", "opost", "isig", "icanon", NULL }; //cooked mode settings
+  char **ap;
+  if (ip->off) {
+    ip->off = 0;
+    f_raw(ip);
+  }
+  else {
+    for(ap = cooked_val; *ap; ap++)
+      msearch(&ap, ip);
+    if(VEOF == VMIN) ip->t.c_cc[VEOF] = CEOF;
+    if(VEOL == VTIME) ip->t.c_cc[VEOL] = CEOL;
+  }
+  ip->set = 1;
+
+}
+
+
+void f_sane(struct info *ip)
+{
+  const struct cchar *p;
+  char *sane_val[] = {"cread", "-ignbrk", "brkint", "-inlcr", "-igncr", "icrnl", "-ixoff", 
+    "-iuclc", "-ixany", "imaxbel", "opost", "-olcuc", "-ocrnl", "onlcr",
+    "-onocr", "-onlret", "-ofill", "-ofdel", "nl0", "cr0", "tab0", "bs0", "vt0",
+    "ff0", "isig", "icanon", "iexten", "echo", "echoe", "echok", "-echonl",
+    "-noflsh", "-xcase", "-tostop", "-echoprt", "echoctl", "echoke", NULL}; //sane settings
+  char **ap;
+  memset(ip->t.c_cc, 0, NCCS);
+  for (p = cchars1; p->name; ++p) {
+    if(p->sub == VMIN || p->sub == VTIME) continue;
+    ip->t.c_cc[p->sub] = p->def;
+  }
+  ip->t.c_cc[VMIN] = 1;
+  ip->t.c_cc[VTIME] = 0;
+
+  for(ap = sane_val; *ap; ap++)
+    msearch(&ap, ip);
+
+  ip->set = 1;
+}
+
+void stty_f_size(struct info *ip)
+{
+  xprintf("%d %d\n", ip->win.ws_row, ip->win.ws_col);
+}
+
+void f_speed(struct info *ip)
+{
+  xprintf("%d\n", baud_to_value(cfgetospeed(&ip->t)));
+}
+
+/*
+ * stty main entry point: this function will parse the command line arguments
+ * and get/set the properties accordingly.
+ */
+void stty_main() 
+{
+  struct info i;
+  struct termios mode;
+  enum FMT fmt;
+
+  memset(&i, 0, sizeof(struct info));
+  fmt = STTY_NOTSET;
+  i.fd = STDIN_FILENO;
+
+  if(toys.optflags & FLAG_a) fmt = STTY_POSIX;
+  if(toys.optflags & FLAG_g) fmt = STTY_GFLAG;
+  if(toys.optflags & FLAG_F) {
+    if ((i.fd = open(TT.device, O_RDONLY | O_NONBLOCK)) < 0) // Open the device file specified on cmdline
+      perror_exit("%s", TT.device);
+  }
+
+  if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) perror_exit("%s: TIOCGETD", TT.device); //get the line descipline of terminal
+  if (tcgetattr(i.fd, &i.t) < 0) perror_exit("tcgetattr"); //get terminal attributes
+  if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0) perror_msg("TIOCGWINSZ"); //get terminal window size
+
+  switch(fmt) {
+  case STTY_NOTSET:
+    if (*toys.optargs)
+      break;
+    /* FALLTHROUGH */
+  case STTY_BSD:
+  case STTY_POSIX:
+    stty_print(&i.t, &i.win, i.ldisc, fmt); //print terminal settings in human-readable format
+    break;
+  case STTY_GFLAG:
+    gprint(&i.t); //print terminal settings in STTY-readable (: separated) format
+    break;
+  }
+  
+  for (i.set = i.wset = 0; *toys.optargs; ++toys.optargs) {
+    if (ksearch(&toys.optargs, &i))  continue;
+
+    if (csearch(&toys.optargs, &i))  continue;
+
+    if (msearch(&toys.optargs, &i))  continue;
+
+    if(gread(&i.t, *toys.optargs)){ //read and parse the STTY-readable format input.
+      i.set = 1;
+      continue;
+    }
+
+    if (isdigit((unsigned char)**toys.optargs)) { // input in digits is assumed to be speed setting
+      unsigned int speed;
+
+      speed = (unsigned int)atolx(*toys.optargs);
+      if((speed = value_to_baud(speed)) != (speed_t) -1) {
+        cfsetospeed(&i.t, speed);
+        cfsetispeed(&i.t, speed);
+        i.set = 1;
+        continue;
+      }
+    }
+
+    error_msg("illegal option -- %s", *toys.optargs);
+    usage();
+  }
+
+  if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) // set terminal attributes
+    perror_exit("tcsetattr");
+  if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0) // set terminal window size.
+    perror_msg("TIOCSWINSZ");
+  memset(&mode, 0, sizeof(struct termios));
+  tcgetattr(i.fd, &mode); //get terminal attributes, for checking the proper settings.
+  if(memcmp(&mode, &i.t, sizeof(struct termios)))
+    error_exit("unable to perform all requested operations");
+}
diff --git a/toys/posix/tail.c b/toys/posix/tail.c
new file mode 100644 (file)
index 0000000..8f63902
--- /dev/null
@@ -0,0 +1,224 @@
+/* tail.c - copy last lines from input to stdout.
+ *
+ * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/tail.html
+
+USE_TAIL(NEWTOY(tail, "fc-n-", TOYFLAG_BIN))
+
+config TAIL
+  bool "tail"
+  default y
+  help
+    usage: tail [-n|c number] [-f] [file...]
+
+    Copy last lines from files to stdout. If no files listed, copy from
+    stdin. Filename "-" is a synonym for stdin.
+
+    -n output the last X lines (default 10), +X counts from start.
+    -c output the last X bytes, +X counts from start
+    -f follow file, waiting for more data to be appended
+
+config TAIL_SEEK
+  bool "tail seek support"
+  default y
+  depends on TAIL
+  help
+    This version uses lseek, which is faster on large files.
+*/
+
+#define FOR_tail
+#include "toys.h"
+
+GLOBALS(
+  long lines;
+  long bytes;
+
+  int file_no;
+)
+
+struct line_list {
+  struct line_list *next, *prev;
+  char *data;
+  int len;
+};
+
+static struct line_list *get_chunk(int fd, int len)
+{
+  struct line_list *line = xmalloc(sizeof(struct line_list)+len);
+
+  line->data = ((char *)line) + sizeof(struct line_list);
+  line->len = readall(fd, line->data, len);
+
+  if (line->len < 1) {
+    free(line);
+    return 0;
+  }
+
+  return line;
+}
+
+static void dump_chunk(void *ptr)
+{
+  struct line_list *list = ptr;
+  xwrite(1, list->data, list->len);
+  free(list);
+}
+
+// Reading through very large files is slow.  Using lseek can speed things
+// up a lot, but isn't applicable to all input (cat | tail).
+// Note: bytes and lines are negative here.
+static int try_lseek(int fd, long bytes, long lines)
+{
+  struct line_list *list = 0, *temp;
+  int flag = 0, chunk = sizeof(toybuf);
+  ssize_t pos = lseek(fd, 0, SEEK_END);
+
+  // If lseek() doesn't work on this stream, return now.
+  if (pos<0) return 0;
+
+  // Seek to the right spot, output data from there.
+  if (bytes) {
+    if (lseek(fd, bytes, SEEK_END)<0) lseek(fd, 0, SEEK_SET);
+    xsendfile(fd, 1);
+    return 1;
+  }
+
+  // Read from end to find enough lines, then output them.
+
+  bytes = pos;
+  while (lines && pos) {
+    int offset;
+
+    // Read in next chunk from end of file
+    if (chunk>pos) chunk = pos;
+    pos -= chunk;
+    if (pos != lseek(fd, pos, SEEK_SET)) {
+      perror_msg("seek failed");
+      break;
+    }
+    if (!(temp = get_chunk(fd, chunk))) break;
+    if (list) list->next = temp;
+    list = temp;
+
+    // Count newlines in this chunk.
+    offset = list->len;
+    while (offset--) {
+      // If the last line ends with a newline, that one doesn't count.
+      if (!flag) {
+        flag++;
+
+        continue;
+      }
+
+      // Start outputting data right after newline
+      if (list->data[offset] == '\n' && !++lines) {
+        offset++;
+        list->data += offset;
+        list->len -= offset;
+
+        break;
+      }
+    }
+  }
+
+  // Output stored data
+  llist_traverse(list, dump_chunk);
+
+  // In case of -f
+  lseek(fd, bytes, SEEK_SET);
+  return 1;
+}
+
+// Called for each file listed on command line, and/or stdin
+static void do_tail(int fd, char *name)
+{
+  long bytes = TT.bytes, lines = TT.lines;
+
+  if (toys.optc > 1) {
+    if (TT.file_no++) xputc('\n');
+    xprintf("==> %s <==\n", name);
+  }
+
+  // Are we measuring from the end of the file?
+
+  if (bytes<0 || lines<0) {
+    struct line_list *list = 0, *new;
+
+    // The slow codepath is always needed, and can handle all input,
+    // so make lseek support optional.
+    if (CFG_TAIL_SEEK && try_lseek(fd, bytes, lines));
+
+    // Read data until we run out, keep a trailing buffer
+    else for (;;) {
+      int len, count;
+      char *try;
+
+      if (!(new = get_chunk(fd, sizeof(toybuf)))) break;
+      // append in order
+      dlist_add_nomalloc((void *)&list, (struct double_list *)new);
+
+      // Measure new chunk, discarding extra data from buffer
+      len = new->len;
+      try = new->data;
+      for (count=0; count<len; count++) {
+        if ((toys.optflags & FLAG_c) && bytes) {
+          bytes++;
+          continue;
+        }
+
+        if (lines) {
+          if(try[count] != '\n' && count != len-1) continue;
+          if (lines<0) {
+            if (!++lines) ++lines;
+            continue;
+          }
+        }
+
+        // Time to discard data; given that bytes and lines were
+        // nonzero coming in, we can't discard too much if we're
+        // measuring right.
+        do {
+          char c = *(list->data++);
+          if (!(--list->len)) {
+            struct line_list *next = list->next;
+            list->prev->next = next;
+            list->next->prev = list->prev;
+            free(list);
+            list = next;
+          }
+          if (c == '\n') break;
+        } while (lines);
+      }
+    }
+
+    // Output/free the buffer.
+    llist_traverse(list, dump_chunk);
+
+  // Measuring from the beginning of the file.
+  } else for (;;) {
+    int len, offset = 0;
+
+    // Error while reading does not exit.  Error writing does.
+    len = read(fd, toybuf, sizeof(toybuf));
+    if (len<1) break;
+    while (bytes > 1 || lines > 1) {
+      bytes--;
+      if (toybuf[offset++] == '\n') lines--;
+      if (offset >= len) break;
+    }
+    if (offset<len) xwrite(1, toybuf+offset, len-offset);
+  }
+
+  // -f support: cache name/descriptor
+}
+
+void tail_main(void)
+{
+  // if nothing specified, default -n to -10
+  if (!(toys.optflags&(FLAG_n|FLAG_c))) TT.lines = -10;
+
+  loopfiles(toys.optargs, do_tail);
+
+  // do -f stuff
+}
diff --git a/toys/posix/tee.c b/toys/posix/tee.c
new file mode 100644 (file)
index 0000000..0388510
--- /dev/null
@@ -0,0 +1,71 @@
+/* tee.c - cat to multiple outputs.
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/tee.html
+
+USE_TEE(NEWTOY(tee, "ia", TOYFLAG_BIN))
+
+config TEE
+  bool "tee"
+  default y
+  help
+    usage: tee [-ai] [file...]
+
+    Copy stdin to each listed file, and also to stdout.
+    Filename "-" is a synonym for stdout.
+
+    -a append to files.
+    -i ignore SIGINT.
+*/
+
+#define FOR_tee
+#include "toys.h"
+
+GLOBALS(
+  void *outputs;
+)
+
+struct fd_list {
+  struct fd_list *next;
+  int fd;
+};
+
+// Open each output file, saving filehandles to a linked list.
+
+static void do_tee_open(int fd, char *name)
+{
+  struct fd_list *temp;
+
+  temp = xmalloc(sizeof(struct fd_list));
+  temp->next = TT.outputs;
+  temp->fd = fd;
+  TT.outputs = temp;
+}
+
+void tee_main(void)
+{
+  if (toys.optflags & FLAG_i) signal(SIGINT, SIG_IGN);
+
+  // Open output files
+  loopfiles_rw(toys.optargs,
+    O_RDWR|O_CREAT|((toys.optflags & FLAG_a)?O_APPEND:O_TRUNC),
+    0666, 0, do_tee_open);
+
+  for (;;) {
+    struct fd_list *fdl;
+    int len;
+
+    // Read data from stdin
+    len = xread(0, toybuf, sizeof(toybuf));
+    if (len<1) break;
+
+    // Write data to each output file, plus stdout.
+    fdl = TT.outputs;
+    for (;;) {
+      if(len != writeall(fdl ? fdl->fd : 1, toybuf, len)) toys.exitval=1;
+      if (!fdl) break;
+      fdl = fdl->next;
+    }
+  }
+}
diff --git a/toys/posix/test.c b/toys/posix/test.c
new file mode 100644 (file)
index 0000000..58b31b6
--- /dev/null
@@ -0,0 +1,421 @@
+/* test.c - check file types and compare values (Evaluate Expression).
+ *
+ * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
+ *
+USE_TEST(NEWTOY(test, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_TEST(OLDTOY([, test, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_TEST(OLDTOY([[, test, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+
+config TEST
+  bool "test"
+  default y
+  help
+    usage: test [EXPRESSION] [EXPRESSION] ...
+
+    check file types and compare values (Evaluate Expression).
+*/
+
+/* $NetBSD: test.c,v 1.30 2006/09/24 13:24:08 hubertf Exp $ */
+
+/*
+ * test(1); version 7-like  --  author Erik Baalbergen
+ * modified by Eric Gisin to be used as built-in.
+ * modified by Arnold Robbins to add SVR3 compatibility
+ * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
+ * modified by J.T. Conklin for NetBSD.
+ *
+ * This program is in the Public Domain.
+ */
+
+#define FOR_test
+#include "toys.h"
+#include <sys/cdefs.h>
+#include <err.h>
+
+/* test(1) accepts the following grammar:
+ *     oexpr  ::= aexpr | aexpr "-o" oexpr ;
+ *     aexpr  ::= nexpr | nexpr "-a" aexpr ;
+ *     nexpr  ::= primary | "!" primary
+ *     primary  ::= unary-operator operand
+ *         | operand binary-operator operand
+ *         | operand
+ *         | "(" oexpr ")"
+ *         ;
+ *     unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
+ *               "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
+ *    binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+ *               "-nt"|"-ot"|"-ef";
+ *     operand ::= <any legal UNIX file name>
+*/
+
+enum token {
+  EOI, 
+  //file access...
+  FILRD, FILWR, FILEX, FILEXIST,
+  //file type...
+  FILREG, FILDIR, FILCDEV, FILBDEV, FILFIFO, FILSOCK, FILSYM, FILGZ, FILTT,
+  //file bit...
+  FILSUID, FILSGID, FILSTCK,
+  //file options...
+  FILNT, FILOT, FILEQ, FILUID, FILGID,
+  //str options...
+  STREZ, STRNZ, STREQ, STRNE, STRLT, STRGT,
+  //int options...
+  INTEQ, INTNE, INTGE, INTGT, INTLE, INTLT, UNOT, BAND, BOR, LPAREN, RPAREN, OPERAND
+};
+
+enum token_types {
+  UNOP,
+  BINOP,
+  BUNOP,
+  BBINOP,
+  PAREN
+};
+
+static struct t_op {
+  const char *op_text;
+  short op_num, op_type;
+} const ops [] = {
+  {"-r",  FILRD,  UNOP},  //FILE exists and read permission is granted
+  {"-w",  FILWR,  UNOP},  //FILE exists and write permission is granted
+  {"-x",  FILEX,  UNOP},  //FILE exists and execute (or search) permission is granted
+  {"-e",  FILEXIST,UNOP},  //FILE exists
+  {"-f",  FILREG,  UNOP},  //FILE exists and is a regular file
+  {"-d",  FILDIR,  UNOP},  //FILE exists and is a directory
+  {"-c",  FILCDEV,UNOP},  //FILE exists and is character special
+  {"-b",  FILBDEV,UNOP},  //FILE exists and is block special
+  {"-p",  FILFIFO,UNOP},  //FILE exists and is a named pipe
+  {"-u",  FILSUID,UNOP},  //FILE exists and its set-user-ID bit is set
+  {"-g",  FILSGID,UNOP},  //FILE exists and is set-group-ID
+  {"-k",  FILSTCK,UNOP},  //FILE exists and has its sticky bit set
+  {"-s",  FILGZ,  UNOP},  //FILE exists and has a size greater than zero
+  {"-t",  FILTT,  UNOP},  //file descriptor FD is opened on a terminal
+  {"-z",  STREZ,  UNOP},  //the length of STRING is zero
+  {"-n",  STRNZ,  UNOP},  //the length of STRING is nonzero
+  {"-h",  FILSYM,  UNOP},  //FILE exists and is a symbolic link (same as -L)
+  {"-O",  FILUID,  UNOP},  //FILE exists and is owned by the effective user ID
+  {"-G",  FILGID,  UNOP},  //FILE exists and is owned by the effective group ID
+  {"-L",  FILSYM,  UNOP},  //FILE exists and is a symbolic link (same as -h)
+  {"-S",  FILSOCK,UNOP},  //FILE exists and is a socket
+  {"=",  STREQ,  BINOP},  //STRING1 = STRING2 (the strings are equal)
+  {"==", STREQ,  BINOP},
+  {"!=",  STRNE,  BINOP}, //STRING1 != STRING2 (the strings are not equal)
+  {"<",  STRLT,  BINOP},  //STRING1 < STRING2
+  {">",  STRGT,  BINOP},  //STRING1 > STRING2
+  {"-eq",  INTEQ,  BINOP},  //INTEGER1 -eq INTEGER2 (INTEGER1 is equal to INTEGER2)
+  {"-ne",  INTNE,  BINOP}, //INTEGER1 -ne INTEGER2 (INTEGER1 is not equal to INTEGER2)
+  {"-ge",  INTGE,  BINOP}, //INTEGER1 -ge INTEGER2 (INTEGER1 is greater than or equal to INTEGER2)
+  {"-gt",  INTGT,  BINOP}, //INTEGER1 -gt INTEGER2 (INTEGER1 is greater than INTEGER2)
+  {"-le",  INTLE,  BINOP}, //INTEGER1 -le INTEGER2 (INTEGER1 is less than or equal to INTEGER2)
+  {"-lt",  INTLT,  BINOP}, //INTEGER1 -lt INTEGER2 (INTEGER1 is less than INTEGER2)
+  {"-nt",  FILNT,  BINOP}, //FILE1 -nt FILE2 (FILE1 is newer (modification date) than FILE2)
+  {"-ot",  FILOT,  BINOP}, //FILE1 -ot FILE2 (FILE1 is older than FILE2)
+  {"-ef",  FILEQ,  BINOP}, //FILE1 -ef FILE2 (FILE1 and FILE2 have the same device and inode numbers)
+  {"!",  UNOT,  BUNOP}, //EXPRESSION is false (! EXPRESSION)
+  {"-a",  BAND,  BBINOP}, //EXPRESSION1 -a EXPRESSION2 (both EXPRESSION1 and EXPRESSION2 are true)
+  {"-o",  BOR,  BBINOP}, //EXPRESSION1 -o EXPRESSION2 (either EXPRESSION1 or EXPRESSION2 is true)
+  {"(",  LPAREN,  PAREN}, //( EXPRESSION ) EXPRESSION is true
+  {")",  RPAREN,  PAREN}, //( EXPRESSION ) EXPRESSION is true
+  {0,  0,  0}
+};
+
+static char **t_wp;
+static struct t_op const *t_wp_op;
+
+static int primary(enum token n);
+
+//For error message printing...
+static void syntax(const char *op, const char *msg)
+{
+  toys.exitval = 2;
+  if (op && *op) error_exit("%s: %s", op, msg);
+  else error_exit("%s", msg);
+  return;
+}
+
+static int isoperand(void) //verify the operand (if there search again in the "ops" list)...
+{
+  struct t_op const *op;
+  char *s, *t;
+
+  op = ops;
+  if((s  = *(t_wp+1)) == 0)
+    return 1;
+  if((t = *(t_wp+2)) == 0)
+    return 0;
+  while(op->op_text) {
+    if (strcmp(s, op->op_text) == 0)
+      return op->op_type == BINOP &&
+          (t[0] != ')' || t[1] != '\0'); 
+    op++;
+  }
+  return 0;
+}
+
+//Find the option in "ops" list (if found return the option number from the list)...
+static enum token t_lex(char *option)
+{
+  struct t_op const *op;
+  op = ops;
+  if(option == 0) {//if there is no option...
+    t_wp_op = NULL;
+    return EOI;
+  }
+  while(op->op_text) {//Search option...
+    if (strcmp(option, op->op_text) == 0) {
+      if ((op->op_type == UNOP && isoperand()) ||
+        (op->op_num == LPAREN && *(t_wp+1) == 0))
+        break;
+      t_wp_op = op;
+      return op->op_num;
+    }
+    op++;
+  }
+  t_wp_op = NULL;
+  return OPERAND;
+}
+//Verify the "!" oprator (if there evaluate next expression)...
+static int nexpr(enum token num)
+{
+  if(num == UNOT)
+    return !nexpr(t_lex(*++t_wp));
+  return primary(num);
+}
+
+static int aexpr(enum token num)
+{
+  int res = nexpr(num);
+  if(t_lex(*++t_wp) == BAND)
+    return aexpr(t_lex(*++t_wp)) && res;
+  t_wp--;
+  return res;
+}
+
+static int oexpr(enum token num)
+{
+  int res = aexpr(num);
+  if(t_lex(*++t_wp) == BOR)
+    return oexpr(t_lex(*++t_wp)) || res;
+  t_wp--;
+  return res;
+}
+
+//atoi with error detection...
+static int getn(const char *s)
+{
+  char *p;
+  long r;
+
+  errno = 0;
+  r = strtol(s, &p, 10);
+  if (errno != 0)
+         syntax(s, "out of range");
+  while (isspace((unsigned char)*p)) p++;
+
+  if (*p)
+         syntax(s, "bad number");
+  return (int) r;
+}
+
+static int newerf(const char *f1, const char *f2)
+{
+  struct stat b1, b2;
+
+  return (stat(f1, &b1) == 0 &&
+    stat(f2, &b2) == 0 &&
+    b1.st_mtime > b2.st_mtime);
+}
+
+static int olderf(const char *f1, const char *f2)
+{
+  struct stat b1, b2;
+
+  return (stat(f1, &b1) == 0 &&
+    stat(f2, &b2) == 0 &&
+    b1.st_mtime < b2.st_mtime);
+}
+
+static int equalf(const char *f1, const char *f2)
+{
+  struct stat b1, b2;
+
+  return (stat(f1, &b1) == 0 &&
+    stat(f2, &b2) == 0 &&
+    b1.st_dev == b2.st_dev &&
+    b1.st_ino == b2.st_ino);
+}
+
+static int binop(void)
+{
+  const char *opnd1, *opnd2;
+  struct t_op const *op;
+
+  opnd1 = *t_wp;
+  (void) t_lex(*++t_wp);
+  op = t_wp_op;
+
+  if ((opnd2 = *++t_wp) == NULL)
+    syntax(op->op_text, "argument expected");
+
+  switch (op->op_num) {
+  case STREQ:
+    return strcmp(opnd1, opnd2) == 0;
+  case STRNE:
+    return strcmp(opnd1, opnd2) != 0;
+  case STRLT:
+    return strcmp(opnd1, opnd2) < 0;
+  case STRGT:
+    return strcmp(opnd1, opnd2) > 0;
+  case INTEQ:
+    return getn(opnd1) == getn(opnd2);
+  case INTNE:
+    return getn(opnd1) != getn(opnd2);
+  case INTGE:
+    return getn(opnd1) >= getn(opnd2);
+  case INTGT:
+    return getn(opnd1) > getn(opnd2);
+  case INTLE:
+    return getn(opnd1) <= getn(opnd2);
+  case INTLT:
+    return getn(opnd1) < getn(opnd2);
+  case FILNT: //Time of last modification...
+    return newerf(opnd1, opnd2);
+  case FILOT:
+    return olderf(opnd1, opnd2);
+  case FILEQ:
+    return equalf(opnd1, opnd2);
+  default:
+    abort(); break;
+    /* NOTREACHED */
+  }
+  return 0; //verify...
+}
+//For the file related expressions...
+static int filestat(char *nm, enum token mode)
+{
+  struct stat s;
+
+  if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
+    return 0;
+
+  switch (mode) {
+  case FILRD:
+    return access(nm, R_OK) == 0;
+  case FILWR:
+    return access(nm, W_OK) == 0;
+  case FILEX:
+    return access(nm, X_OK) == 0;
+  case FILEXIST:
+    return access(nm, F_OK) == 0;
+  case FILREG:
+    return S_ISREG(s.st_mode);
+  case FILDIR:
+    return S_ISDIR(s.st_mode);
+  case FILCDEV:
+    return S_ISCHR(s.st_mode);
+  case FILBDEV:
+    return S_ISBLK(s.st_mode);
+  case FILFIFO:
+    return S_ISFIFO(s.st_mode);
+  case FILSOCK:
+    return S_ISSOCK(s.st_mode);
+  case FILSYM:
+    return S_ISLNK(s.st_mode);
+  case FILSUID:
+    return (s.st_mode & S_ISUID) != 0;
+  case FILSGID:
+    return (s.st_mode & S_ISGID) != 0;
+  case FILSTCK:
+    return (s.st_mode & S_ISVTX) != 0;
+  case FILGZ:
+    return s.st_size > (off_t)0;
+  case FILUID:
+    return s.st_uid == geteuid();
+  case FILGID:
+    return s.st_gid == getegid();
+  default:
+    return 1;
+  }
+}
+
+static int primary(enum token num)
+{
+  enum token nn;
+  int res;
+
+  if (num == EOI)
+    return 0;//missing expression..
+  if (num == LPAREN) {
+    if ((nn = t_lex(*++t_wp)) == RPAREN)
+      return 0; //missing expression...
+    res = oexpr(nn);
+    if (t_lex(*++t_wp) != RPAREN)
+      syntax(NULL, "closing paren expected");
+    return res;
+  }
+  if (t_wp_op && t_wp_op->op_type == UNOP) { //For the unary operator...
+    //unary expression...
+    if (*++t_wp == NULL)
+      syntax(t_wp_op->op_text, "argument expected");
+    switch (num) {
+    case STREZ:
+      return strlen(*t_wp) == 0;
+    case STRNZ:
+      return strlen(*t_wp) != 0;
+    case FILTT:
+      return isatty(getn(*t_wp));
+    default:
+      return filestat(*t_wp, num);
+    }
+  }
+
+  if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
+    return binop();
+  }
+  return strlen(*t_wp) > 0;
+}
+
+void test_main(void)
+{
+  char **argv = toys.argv;
+  int res;
+  const char *argv0;
+  int argc = 0;
+
+  argv0 = argv[0];
+  while(*argv) {argc++; argv++;}
+  argv = toys.argv;
+
+  if(argv0[0] == '[') {
+    --argc;
+    if(!argv0[1]) { /* "[" ? */
+      char *arg = argv[argc];
+      if(arg[0] != ']' || arg[1]) {
+       syntax(NULL, "missing ]");
+        return;
+      }
+    } else { /* assuming "[[" */
+        if (strcmp(argv[argc], "]]") != 0) {
+          syntax(NULL, "missing ]]");
+          return;
+        }
+    }
+    argv[argc] = NULL;
+  }
+  
+  if(!argv[1]) {
+         toys.exitval = 1;
+         return;
+  }
+  t_wp = &argv[1];
+  res = !oexpr(t_lex(*t_wp));
+
+  if (*t_wp != NULL && *++t_wp != NULL)
+    syntax(*t_wp, "unexpected operator");
+
+  toys.exitval = res;
+  return;
+}
diff --git a/toys/posix/time.c b/toys/posix/time.c
new file mode 100644 (file)
index 0000000..0ccba19
--- /dev/null
@@ -0,0 +1,48 @@
+/* time.c - time a simple command
+ *
+ * Copyright 2013 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/time.html
+
+USE_TIME(NEWTOY(time, "<1^p", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TIME
+  bool "time"
+  default y
+  help
+    usage: time [-p] COMMAND [ARGS...]
+
+    Run command line and report real, user, and system time elapsed in seconds.
+    (real = clock on the wall, user = cpu used by command's code,
+    system = cpu used by OS on behalf of command.)
+
+    -p posix mode (ignored)
+*/
+
+#include "toys.h"
+
+void time_main(void)
+{
+  pid_t pid;
+  struct timeval tv, tv2;
+
+  gettimeofday(&tv, NULL);
+  if (!(pid = fork())) xexec(toys.optargs);
+  else {
+    int stat;
+    struct rusage ru;
+    float r, u, s;
+
+    wait4(pid, &stat, 0, &ru);
+    gettimeofday(&tv2, NULL);
+    if (tv.tv_usec > tv2.tv_usec) {
+      tv2.tv_usec += 1000000;
+      tv2.tv_sec--;
+    }
+    r = (tv2.tv_sec-tv.tv_sec)+((tv2.tv_usec-tv.tv_usec)/1000000.0);
+    u = ru.ru_utime.tv_sec+(ru.ru_utime.tv_usec/1000000.0);
+    s = ru.ru_stime.tv_sec+(ru.ru_stime.tv_usec/1000000.0);
+    fprintf(stderr, "real %f\nuser %f\nsys %f\n", r, u, s);
+    toys.exitval = WIFEXITED(stat) ? WEXITSTATUS(stat) : WTERMSIG(stat);
+  }
+}
diff --git a/toys/posix/touch.c b/toys/posix/touch.c
new file mode 100644 (file)
index 0000000..76884be
--- /dev/null
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4:
+ *
+ * touch.c : change timestamp of a file
+ * Copyright 2012 Choubey Ji <warior.linux@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html 
+ * acmrtd
+
+USE_TOUCH(NEWTOY(touch, "acd:mr:t:[!dtr]", TOYFLAG_BIN))
+
+config TOUCH
+  bool "touch"
+  default y
+  help
+    Usage: Usage: touch [OPTION]... FILE...
+
+    Update the access and modification times of each FILE to the current time.
+
+    -a change access time
+    -m change modification time
+    -c don't create file
+    -d DATE    use YYYY-MM-DDThh:mm:SS[.frac][tz] as time
+    -t TIME    use [[CC]YY]MMDDhhmm[.ss][frac] as time
+    -r FILE    use reference file's time
+*/
+
+#define FOR_touch
+#include "toys.h"
+
+GLOBALS(
+  char *time;
+  char *file;
+  char *date;
+)
+
+int fetch(char *file, struct timeval *tv, unsigned flags)
+{
+  struct stat st;
+
+  if (stat(TT.file, &st)) return 1;
+
+  if (flags & FLAG_a) {
+    tv[0].tv_sec = st.st_atime;
+    tv[0].tv_usec = st.st_atim.tv_nsec/1000;
+  }
+  if (flags & FLAG_m) {
+    tv[1].tv_sec = st.st_mtime;
+    tv[1].tv_usec = st.st_mtim.tv_nsec/1000;
+  }
+
+  return 0;
+}
+
+void touch_main(void)
+{
+  struct timeval tv[2];
+  struct tm tm;
+  char **ss;
+  int flag;
+
+  gettimeofday(tv, NULL);
+  localtime_r(&(tv->tv_sec), &tm);
+
+  if (toys.optflags & (FLAG_t|FLAG_d)) {
+    char *date, *s;
+    int i, len;
+
+    if (toys.optflags & FLAG_d) {
+      date = TT.date;
+      i = strlen(date)-1;
+      if (*date && toupper(date[i])=='Z') {
+        putenv("TZ=UTC");
+        strncpy(toybuf, date, sizeof(toybuf)-1);
+        date = toybuf;
+        date[i]=0;
+        gmtime_r(&(tv->tv_sec), &tm);
+      }
+      s = strptime(date, "%Y-%m-%dT%T", &tm);
+      if (s && *s=='.') {
+        sscanf(s, ".%d%n", &i, &len);
+        s += len;
+        tv->tv_usec = i;
+      }
+    } else {
+      strcpy(toybuf, "%Y%m%d%H%M");
+      date = TT.time;
+      for (i=0;i<3;i++) {
+        s = strptime(date, toybuf+(i&2), &tm);
+        if (s) break;
+        toybuf[1]='y';
+      }
+      if (s && *s=='.') {
+        int count = sscanf(s, ".%2d%u%n", &(tm.tm_sec), &i, &len);
+        if (count==2) tv->tv_usec = i;
+        s += len;
+      }
+    }
+
+    errno = 0;
+    tv->tv_sec = mktime(&tm);
+    if (!s || *s || errno == EOVERFLOW) {
+      // Warn Indiana Jones the monkey died.
+      perror_exit("bad '%s'", date);
+    }
+  }
+  tv[1]=tv[0];
+
+  if (TT.file && fetch(TT.file, tv, FLAG_a|FLAG_m))
+    perror_exit("-r '%s'", TT.file);
+
+  flag = (~toys.optflags) & (FLAG_m|FLAG_a);
+  if (flag == (FLAG_m|FLAG_a)) flag = 0;
+  for (ss=toys.optargs; *ss;) {
+    int fd;
+
+    if ((!flag || !fetch(*ss, tv, flag)) && !utimes(*ss, tv)) ss++;
+    else if (toys.optflags & FLAG_c) ss++;
+    else if (-1 != (fd = open(*ss, O_CREAT, 0666))) close(fd);
+    else perror_msg("'%s'", *ss++);
+  }
+}
diff --git a/toys/posix/true.c b/toys/posix/true.c
new file mode 100644 (file)
index 0000000..09e551c
--- /dev/null
@@ -0,0 +1,21 @@
+/* true.c - Return zero.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/true.html
+
+USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN))
+
+config TRUE
+  bool "true"
+  default y
+  help
+    Return zero.
+*/
+
+#include "toys.h"
+
+void true_main(void)
+{
+  return;
+}
diff --git a/toys/posix/tty.c b/toys/posix/tty.c
new file mode 100644 (file)
index 0000000..578c9af
--- /dev/null
@@ -0,0 +1,30 @@
+/* tty.c - Show stdin's terminal name
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/tty.html
+
+USE_TTY(NEWTOY(tty, "s", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TTY
+  bool "tty"
+  default y
+  help
+    Show filename of terminal connected to stdin.
+
+    Prints "not a tty" and exits with nonzero status if no terminal
+    is connected to stdin.
+
+    -s silent mode
+*/
+
+#include "toys.h"
+
+void tty_main(void)
+{
+  char *tty = ttyname(0);
+
+  if (!toys.optflags) puts(tty ? tty : "not a tty");
+
+  toys.exitval = !tty;
+}
diff --git a/toys/posix/uname.c b/toys/posix/uname.c
new file mode 100644 (file)
index 0000000..2c1a050
--- /dev/null
@@ -0,0 +1,73 @@
+/* uname.c - return system name
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/uname.html
+
+USE_UNAME(NEWTOY(uname, "amvrns", TOYFLAG_BIN))
+
+config UNAME
+  bool "uname"
+  default y
+  help
+    usage: uname [-asnrvmpio]
+
+    Print system information.
+
+    -s System name
+    -n Network (domain) name
+    -r Release number
+    -v Version (build date)
+    -m Machine (hardware) name
+    -a All of the above
+*/
+
+#define FOR_uname
+#include "toys.h"
+
+// If a 32 bit x86 build environment working in a chroot under an x86-64
+// kernel returns x86_64 for -m it confuses ./configure.  Special case it.
+
+#if defined(__i686__)
+#define GROSS "i686"
+#elif defined(__i586__)
+#define GROSS "i586"
+#elif defined(__i486__)
+#define GROSS "i486"
+#elif defined(__i386__)
+#define GROSS "i386"
+#endif
+
+void uname_main(void)
+{
+  int i, flags = toys.optflags, needspace=0;
+
+  uname((void *)toybuf);
+
+  if (!flags) flags = FLAG_s;
+  for (i=0; i<5; i++) {
+    char *c = toybuf+(65*i);
+
+    if (flags & ((1<<i)|FLAG_a)) {
+      int len = strlen(c);
+
+      // This problem originates in autoconf, so of course the solution
+      // is horribly ugly.
+#ifdef GROSS
+      if (i==4 && !strcmp(c,"x86_64")) {
+        printf(GROSS);
+        continue;
+      }
+#endif
+
+      if (needspace++) {
+        // We can't decrement on the first entry, because
+        // needspace would be 0
+        *(--c)=' ';
+        len++;
+      }
+      xwrite(1, c, len);
+    }
+  }
+  putchar('\n');
+}
diff --git a/toys/posix/uniq.c b/toys/posix/uniq.c
new file mode 100644 (file)
index 0000000..bd41d4a
--- /dev/null
@@ -0,0 +1,119 @@
+/* uniq.c - report or filter out repeated lines in a file
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/uniq.html
+
+USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_BIN))
+
+config UNIQ
+  bool "uniq"
+  default y
+  help
+    usage: uniq [-cduiz] [-w maxchars] [-f fields] [-s char] [input_file [output_file]]
+
+    Report or filter out repeated lines in a file
+
+    -c show counts before each line
+    -d show only lines that are repeated
+    -u show only lines that are unique
+    -i ignore case when comparing lines
+    -z lines end with \0 not \n
+    -w compare maximum X chars per line
+    -f ignore first X fields
+    -s ignore first X chars
+*/
+
+#define FOR_uniq
+#include "toys.h"
+
+GLOBALS(
+  long maxchars;
+  long nchars;
+  long nfields;
+  long repeats;
+)
+
+static char *skip(char *str)
+{
+  long nchars = TT.nchars, nfields;
+
+  // Skip fields first
+  for (nfields = TT.nfields; nfields; str++) {
+    while (*str && isspace(*str)) str++;
+    while (*str && !isspace(*str)) str++;
+    nfields--;
+  }
+  // Skip chars
+  while (*str && nchars--) str++;
+
+  return str;
+}
+
+static void print_line(FILE *f, char *line)
+{
+  if (toys.optflags & (TT.repeats ? FLAG_u : FLAG_d)) return;
+  if (toys.optflags & FLAG_c) fprintf(f, "%7lu ", TT.repeats + 1);
+  fputs(line, f);
+  if (toys.optflags & FLAG_z) fputc(0, f);
+}
+
+void uniq_main(void)
+{
+  FILE *infile = stdin, *outfile = stdout;
+  char *thisline = NULL, *prevline = NULL, *tmpline, eol = '\n';
+  size_t thissize, prevsize = 0, tmpsize;
+
+  if (toys.optc >= 1) infile = xfopen(toys.optargs[0], "r");
+  if (toys.optc >= 2) outfile = xfopen(toys.optargs[1], "w");
+
+  if (toys.optflags & FLAG_z) eol = 0;
+
+  // If first line can't be read
+  if (getdelim(&prevline, &prevsize, eol, infile) < 0)
+    return;
+
+  while (getdelim(&thisline, &thissize, eol, infile) > 0) {
+    int diff;
+    char *t1, *t2;
+
+    // If requested get the chosen fields + character offsets.
+    if (TT.nfields || TT.nchars) {
+      t1 = skip(thisline);
+      t2 = skip(prevline);
+    } else {
+      t1 = thisline;
+      t2 = prevline;
+    }
+
+    if (TT.maxchars == 0) {
+      diff = !(toys.optflags & FLAG_i) ? strcmp(t1, t2) : strcasecmp(t1, t2);
+    } else {
+      diff = !(toys.optflags & FLAG_i) ? strncmp(t1, t2, TT.maxchars)
+              : strncasecmp(t1, t2, TT.maxchars);
+    }
+
+    if (diff == 0) { // same
+      TT.repeats++;
+    } else {
+      print_line(outfile, prevline);
+
+      TT.repeats = 0;
+
+      tmpline = prevline;
+      prevline = thisline;
+      thisline = tmpline;
+
+      tmpsize = prevsize;
+      prevsize = thissize;
+      thissize = tmpsize;
+    }
+  }
+
+  print_line(outfile, prevline);
+
+  if (CFG_TOYBOX_FREE) {
+    free(prevline);
+    free(thisline);
+  }
+}
diff --git a/toys/posix/unlink.c b/toys/posix/unlink.c
new file mode 100644 (file)
index 0000000..4faef9d
--- /dev/null
@@ -0,0 +1,24 @@
+/* unlink.c - delete one file
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/unlink.html
+
+USE_UNLINK(NEWTOY(unlink, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNLINK
+  bool "unlink"
+  default y
+  help
+    usage: unlink FILE
+
+    Deletes one file.
+*/
+
+#include "toys.h"
+
+void unlink_main(void)
+{
+  if (unlink(*toys.optargs))
+    perror_exit("Couldn't unlink `%s'", *toys.optargs);
+}
diff --git a/toys/posix/uudecode.c b/toys/posix/uudecode.c
new file mode 100644 (file)
index 0000000..fd557ec
--- /dev/null
@@ -0,0 +1,107 @@
+/* uudecode.c - uudecode / base64 decode
+ *
+ * Copyright 2013 Erich Plondke <toybox@erich.wreck.org>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/uudecode.html
+
+USE_UUDECODE(NEWTOY(uudecode, ">1o:", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
+
+config UUDECODE
+  bool "uudecode"
+  default y
+  help
+    usage: uudecode [-o OUTFILE] [INFILE]
+
+    Decode file from stdin (or INFILE).
+
+    -o write to OUTFILE instead of filename in header
+*/
+
+#define FOR_uudecode
+#include "toys.h"
+
+GLOBALS(
+  char *o;
+)
+
+void uudecode_main(void)
+{
+  int ifd = 0, ofd, idx = 0, m = m;
+  char *line = 0, mode[16],
+       *class[] = {"begin%*[ ]%15s%*[ ]%n", "begin-base64%*[ ]%15s%*[ ]%n"};
+
+  if (toys.optc) ifd = xopen(*toys.optargs, O_RDONLY);
+
+  while (!idx) {
+    free(line);
+    if (!(line = get_line(ifd))) error_exit("bad EOF");
+    for (m=0; m < 2; m++) {
+      sscanf(line, class[m], mode, &idx);
+      if (idx) break;
+    }
+  }
+
+  ofd = xcreate(TT.o ? TT.o : line+idx, O_WRONLY|O_CREAT|O_TRUNC,
+    string_to_mode(mode, 0777^toys.old_umask));
+
+  for(;;) {
+    char *in, *out;
+    int olen;
+
+    free(line);
+    if (m == 2 || !(line = get_line(ifd))) break;
+    if (!strcmp(line, m ? "====" : "end")) {
+      m = 2;
+      continue;
+    }
+
+    olen = 0;
+    in = out = line;
+    if (!m) olen = (*(in++) - 32) & 0x3f;
+
+    for (;;) {
+      int i = 0, x = 0, len = 4;
+      char c = 0;
+
+      if (!m) {
+        if (olen < 1) break;
+        if (olen < 3) len = olen + 1;
+      }
+
+      while (i < len) {
+        if (!(c = *(in++))) goto line_done;
+
+        if (m) {
+          if (c == '=') {
+            len--;
+            continue;
+          } else if (len != 4) break;
+
+          if (c >= 'A' && c <= 'Z') c -= 'A';
+          else if (c >= 'a' && c <= 'z') c += 26 - 'a';
+          else if (c >= '0' && c <= '9') c += 52 - '0';
+          else if (c == '+') c = 62;
+          else if (c == '/') c = 63;
+          else continue;
+        } else c = (c - 32) & 0x3f;
+
+        x |= c << (6*(3-i));
+
+        if (i && i < len) {
+          *(out++) = (x>>(8*(3-i))) & 0xff;
+          olen--;
+        }
+        i++;
+      }
+
+      if (i && i!=len) error_exit("bad %s", line);
+    }
+line_done:
+    xwrite(ofd, line, out-line);
+  }
+
+  if (CFG_TOYBOX_FREE) {
+    if (ifd) close(ifd);
+    close(ofd);
+  }
+}
diff --git a/toys/posix/uuencode.c b/toys/posix/uuencode.c
new file mode 100644 (file)
index 0000000..2323c98
--- /dev/null
@@ -0,0 +1,67 @@
+/* uuencode.c - uuencode / base64 encode
+ *
+ * Copyright 2013 Erich Plondke <toybox@erich.wreck.org>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/uuencode.html
+
+USE_UUENCODE(NEWTOY(uuencode, "<1>2m", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UUENCODE
+  bool "uuencode"
+  default y 
+  help
+    usage: uuencode [-m] [file] encode-filename
+
+    Uuencode stdin (or file) to stdout, with encode-filename in the output.
+
+    -m base64-encode
+*/
+
+#define FOR_uuencode
+#include "toys.h"
+
+void uuencode_main(void)
+{
+  char *p, *name = toys.optargs[toys.optc-1], buf[(76/4)*3];
+
+  int i, m = toys.optflags & FLAG_m, fd = 0;
+
+  if (toys.optc > 1) fd = xopen(toys.optargs[0], O_RDONLY);
+
+  // base64 table
+
+  p = toybuf;
+  for (i = 'A'; i != ':'; i++) {
+    if (i == 'Z'+1) i = 'a';
+    if (i == 'z'+1) i = '0';
+    *(p++) = i;
+  }
+  *(p++) = '+';
+  *(p++) = '/';
+
+  xprintf("begin%s 744 %s\n", m ? "-base64" : "", name);
+  for (;;) {
+    char *in;
+
+    if (!(i = xread(fd, buf, m ? sizeof(buf) : 45))) break;
+
+    if (!m) xputc(i+32);
+    in = buf;
+
+    for (in = buf; in-buf < i; ) {
+      int j, x, bytes = i - (in-buf);
+
+      if (bytes > 3) bytes = 3;
+
+      for (j = x = 0; j<4; j++) {
+        int out;
+
+        if (j < bytes) x |= (*(in++) & 0x0ff) << (8*(2-j));
+        out = (x>>((3-j)*6)) & 0x3f;
+        xputc(m ? (j > bytes ? '=' : toybuf[out]) : (out ? out + 0x20 : 0x60));
+      } 
+    }
+    xputc('\n');
+  }
+  xputs(m ? "====" : "end");
+}
diff --git a/toys/posix/wc.c b/toys/posix/wc.c
new file mode 100644 (file)
index 0000000..3a6540b
--- /dev/null
@@ -0,0 +1,89 @@
+/* wc.c - Word count
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/wc.html
+
+USE_WC(NEWTOY(wc, USE_TOYBOX_I18N("m")"cwl", TOYFLAG_USR|TOYFLAG_BIN))
+
+config WC
+  bool "wc"
+  default y
+  help
+    usage: wc -lwcm [FILE...]
+
+    Count lines, words, and characters in input.
+
+    -l show lines
+    -w show words
+    -c show bytes
+    -m show characters
+
+    By default outputs lines, words, bytes, and filename for each
+    argument (or from stdin if none). Displays only either bytes
+    or characters.
+*/
+
+#define FOR_wc
+#include "toys.h"
+
+GLOBALS(
+  unsigned long totals[3];
+)
+
+static void show_lengths(unsigned long *lengths, char *name)
+{
+  int i, nospace = 1;
+  for (i=0; i<3; i++) {
+    if (!toys.optflags || (toys.optflags&(1<<i))) {
+      xprintf(" %ld"+nospace, lengths[i]);
+      nospace = 0;
+    }
+    TT.totals[i] += lengths[i];
+  }
+  if (*toys.optargs) xprintf(" %s", name);
+  xputc('\n');
+}
+
+static void do_wc(int fd, char *name)
+{
+  int i, len, clen=1, space;
+  unsigned long word=0, lengths[]={0,0,0};
+
+  for (;;) {
+    len = read(fd, toybuf, sizeof(toybuf));
+    if (len<0) perror_msg("%s", name);
+    if (len<1) break;
+    for (i=0; i<len; i+=clen) {
+      wchar_t wchar;
+
+      if (CFG_TOYBOX_I18N && (toys.optflags&FLAG_m)) {
+        clen = mbrtowc(&wchar, toybuf+i, len-i, 0);
+        if (clen == -1) {
+          clen = 1;
+          continue;
+        }
+        if (clen == -2) break;
+        if (clen == 0) clen=1;
+        space = iswspace(wchar);
+      } else space = isspace(toybuf[i]);
+
+      if (toybuf[i]==10) lengths[0]++;
+      if (space) word=0;
+      else {
+        if (!word) lengths[1]++;
+        word=1;
+      }
+      lengths[2]++;
+    }
+  }
+
+  show_lengths(lengths, name);
+}
+
+void wc_main(void)
+{
+  toys.optflags |= (toys.optflags&8)>>1;
+  loopfiles(toys.optargs, do_wc);
+  if (toys.optc>1) show_lengths(TT.totals, "total");
+}
diff --git a/toys/posix/who.c b/toys/posix/who.c
new file mode 100644 (file)
index 0000000..2c8a2e6
--- /dev/null
@@ -0,0 +1,47 @@
+/* who.c - display who is on the system
+ *
+ * Copyright 2012 ProFUSION Embedded Systems
+ *
+ * by Luis Felipe Strano Moraes <lfelipe@profusion.mobi>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/who.html
+ *
+ * Posix says to support many options (-abdHlmpqrstTu) but this
+ * isn't aimed at minicomputers with modem pools.
+
+USE_WHO(NEWTOY(who, "a", TOYFLAG_BIN))
+
+config WHO
+  bool "who"
+  default y
+  help
+    usage: who
+
+    Print logged user information on system
+*/
+
+#define FOR_who
+#include "toys.h"
+
+void who_main(void)
+{
+  struct utmpx *entry;
+
+  setutxent();
+
+  while ((entry = getutxent())) {
+    if ((toys.optflags & FLAG_a) || entry->ut_type == USER_PROCESS) {
+      time_t time;
+      int time_size;
+      char *times;
+
+      time = entry->ut_tv.tv_sec;
+      times = ctime(&time);
+      time_size = strlen(times) - 2;
+      printf("%s\t%s\t%*.*s\t(%s)\n", entry->ut_user, entry->ut_line,
+        time_size, time_size, ctime(&time), entry->ut_host);
+    }
+  }
+
+  endutxent();
+}
diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c
new file mode 100644 (file)
index 0000000..18b70f2
--- /dev/null
@@ -0,0 +1,185 @@
+/* xargs.c - Run command with arguments taken from stdin.
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html
+
+USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config XARGS
+  bool "xargs"
+  default y
+  help
+    usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND...
+
+    Run command line one or more times, appending arguments from stdin.
+
+    If command exits with 255, don't launch another even if arguments remain.
+
+    -s Size in bytes per command line
+    -n Max number of arguments per command
+    -0 Each argument is NULL terminated, no whitespace or quote processing
+    #-p        Prompt for y/n from tty before running each command
+    #-t        Trace, print command line to stderr
+    #-x        Exit if can't fit everything in one command
+    #-r        Don't run command with empty input
+    #-L        Max number of lines of input per command
+    -E stop at line matching string
+*/
+
+#define FOR_xargs
+#include "toys.h"
+
+GLOBALS(
+  long max_bytes;
+  long max_entries;
+  long L;
+  char *eofstr;
+  char *I;
+
+  long entries, bytes;
+  char delim;
+)
+
+// If out==NULL count TT.bytes and TT.entries, stopping at max.
+// Otherwise, fill out out[]
+
+// Returning NULL means need more data.
+// Returning char * means hit data limits, start of data left over
+// Returning 1 means hit data limits, but consumed all data
+// Returning 2 means hit -E eofstr
+
+static char *handle_entries(char *data, char **entry)
+{
+  if (TT.delim) {
+    char *s = data;
+
+    // Chop up whitespace delimited string into args
+    while (*s) {
+      char *save;
+
+      while (isspace(*s)) {
+        if (entry) *s = 0;
+        s++;
+      }
+
+      if (TT.max_entries && TT.entries >= TT.max_entries)
+        return *s ? s : (char *)1;
+
+      if (!*s) break;
+      save = s;
+
+      for (;;) {
+        if (++TT.bytes >= TT.max_bytes && TT.max_bytes) return save;
+        if (!*s || isspace(*s)) break;
+        s++;
+      }
+      if (TT.eofstr) {
+        int len = s-save;
+        if (len == strlen(TT.eofstr) && !strncmp(save, TT.eofstr, len))
+          return (char *)2;
+      }
+      if (entry) entry[TT.entries] = save;
+      ++TT.entries;
+    }
+
+  // -0 support
+  } else {
+    TT.bytes += strlen(data)+1;
+    if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data;
+    if (TT.max_entries && TT.entries >= TT.max_entries)
+      return (char *)1;
+    if (entry) entry[TT.entries] = data;
+    TT.entries++;
+  }
+
+  return NULL;
+}
+
+void xargs_main(void)
+{
+  struct double_list *dlist = NULL;
+  int entries, bytes, done = 0, status;
+  char *data = NULL;
+
+  if (!(toys.optflags & FLAG_0)) TT.delim = '\n';
+
+  // If no optargs, call echo.
+  if (!toys.optc) {
+    free(toys.optargs);
+    *(toys.optargs = xzalloc(2*sizeof(char *)))="echo";
+    toys.optc = 1;
+  }
+
+  for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++)
+    bytes += strlen(toys.optargs[entries]);
+
+  // Loop through exec chunks.
+  while (data || !done) {
+    char **out;
+
+    TT.entries = 0;
+    TT.bytes = bytes;
+
+    // Loop reading input
+    for (;;) {
+
+      // Read line
+      if (!data) {
+        ssize_t l = 0;
+        l = getdelim(&data, (size_t *)&l, TT.delim, stdin);
+
+        if (l<0) {
+          data = 0;
+          done++;
+          break;
+        }
+      }
+      dlist_add(&dlist, data);
+
+      // Count data used
+      data = handle_entries(data, NULL);
+      if (!data) continue;
+      if (data == (char *)2) done++;
+      if ((long)data <= 2) data = 0;
+      else data = xstrdup(data);
+
+      break;
+    }
+
+    // Accumulate cally thing
+
+    if (data && !TT.entries) error_exit("argument too long");
+    out = xzalloc((entries+TT.entries+1)*sizeof(char *));
+
+    if (dlist) {
+      struct double_list *dtemp;
+
+      // Fill out command line to exec
+      memcpy(out, toys.optargs, entries*sizeof(char *));
+      TT.entries = 0;
+      TT.bytes = bytes;
+      dlist->prev->next = 0;
+      for (dtemp = dlist; dtemp; dtemp = dtemp->next)
+        handle_entries(dtemp->data, out+entries);
+    }
+    pid_t pid=fork();
+    if (!pid) {
+      xclose(0);
+      open("/dev/null", O_RDONLY);
+      xexec(out);
+    }
+    waitpid(pid, &status, 0);
+    status = WEXITSTATUS(status);
+
+    // Abritrary number of execs, can't just leak memory each time...
+    while (dlist) {
+      struct double_list *dtemp = dlist->next;
+
+      free(dlist->data);
+      free(dlist);
+      dlist = dtemp;
+    }
+    free(out);
+  }
+}
diff --git a/www/about.html b/www/about.html
new file mode 100644 (file)
index 0000000..ace499f
--- /dev/null
@@ -0,0 +1,217 @@
+<!--#include file="header.html" -->
+
+<h2><a name="what" />What is ToyBox?</h2>
+
+<p>Toybox combines the most common Linux command line utilities together into
+a single <a href=license.html>BSD-licensed</a> executable. It's simple, small, fast, and reasonably
+standards-compliant (<a href=http://opengroup.org/onlinepubs/9699919799>POSIX-2008</a> and <a href=http://refspecs.linuxfoundation.org/LSB_4.1.0>LSB 4.1</a>).</p>
+
+<p>Toybox's 1.0 release goal is to turn generic Android into a
+development environment capable of compiling <a href=http://www.linuxfromscratch.org>Linux From Scratch</a>.
+A tiny system <a href=/aboriginal>built from</a> just toybox, linux, <a href=http://musl-libc.org>a C library</a>, and a C compiler (such as LLVM or
+gcc 4.2.1+binutils 2.17) should be
+able to rebuild itself from source code without needing any other packages.</p>
+
+<b><h2><a name="status" />What commands are implemented in Toybox?</h2></b>
+
+<p>The current list of commands implemented by toybox is on the
+<a href=status.html>status page</a>, which is updated each release.
+There is also <a href=roadmap.html>roadmap</a> listing all planned commands for the
+1.0 release.</p>
+
+<p>In general, configuring toybox for "defconfig" enables all the commands
+compete enough to be useful.  Configuring "allyesconfig" enables partially
+implemented commands as well, along with debugging features.</p>
+
+<p>Several toybox commands can do things other vesions can't.  For example
+the toybox "df" isn't confused by initramfs the way other df implementations
+are. (If initramfs is visible, df shows it like any other mount point.)</p>
+
+<b><h3>Command Shell</h3></b>
+<p>The Toybox Shell (toysh) aims to be a reasonable bash replacement.  It
+implements the "sh" and "toysh" commands, plus the built-in commands "cd" and
+"exit".  This is the largest single sub-project in toybox.</p>
+
+<p>The following additional commands may be built into the shell (but not as
+separate executables): cd, exit, if, while, for, function, fg, bg, jobs, source,
+<a href="http://opengroup.org/onlinepubs/9699919799/utilities/alias.html">alias</a>,
+export, set, unset, read, trap, and exec.  (Note: not done yet.)</p>
+
+</ul>
+
+<h2><a name="commands" />Which commands are planned?</h2>
+
+<p>The toybox <a href=todo.txt>todo list</a> mentions many potential commands
+which may be added to this project.  (Whether that file is readable by anybody
+but the project's maintainer is open to debate.)  The roadmap wiki in the
+nav bar has a more human readable version.</p>
+
+<p>The criteria for a toybox 1.0 release is that a system built from just the
+Linux kernel, toybox, C library (such as uClibc), and a compiler (such as
+tinycc) can rebuild itself from source code.</p>
+
+<b><h3>Relevant Standards</h3></b>
+
+<p>Most commands are implemented according to
+<a href=http://opengroup.org/onlinepubs/9699919799/idx/utilities.html>The
+Single Unix Specification version 4</a> where applicable. This does not mean
+that Toybox is implementing every SUSv4 utility: some such as SCCS and ed are
+obsolete, while others such as c99 are outside the scope of the project.
+Toybox also isn't implementing full internationalization support: it should be
+8-bit clean and handle UTF-8, but otherwise we leave this to X11 and higher
+layers. And some things (like $CDPATH support in "cd") await a good
+explanation of why to bother with them. (The standard provides an important
+frame of reference, but is not infallable set of commandments to be blindly
+obeyed.)</p>
+
+<p>The other major sources of commands are the Linux man pages, and testing
+the behavior of existing commands (although not generally looking at their
+source code), including the commands in Android's toolbox. SUSv4 does not
+include many basic commands such as "mount", "init", and "mke2fs", which are
+kind of nice to have.</p>
+
+<b><h2><a name="downloads" />Download</h2></b>
+
+<p>This project is maintained as a mercurial archive.  To get a copy of the
+current development version, either use mercurial (hg clone
+http://landley.net/hg/toybox) or click on one of the zip/gz/bz2 links
+at the top of the <a href=/hg/toybox>mercurial archive browser</a> page to get
+an archive of the appropriate version.  Click
+<a href="/hg/toybox?cmd=tags">tags</a> to see all the tagged release
+versions ("tip" is the current development version).</p>
+
+<p>The maintainer's <a href=/notes.html>development log</a> and the project's
+<a href=http://lists.landley.net/listinfo.cgi/toybox-landley.net>mailing
+list</a> are also good ways to track what's going on with the project.</p>
+
+<!--
+<b><h2><a name="why">Why do toybox?</h2></b>
+
+<p>Because smart phones are replacing the PC, and Android must become
+self-hosting to beat the iPhone in establishing the new standard.</p>
+
+<p>This is the third such major transition in computer history:
+(mainframe-&gt;minicomputer-&gt;microcomputer-&gt;smartphone).
+The mainframe was replaced by the minicomputer, which was replaced by
+the microcomputer (renamed the "personal" computer to make clear you could
+access porn through it), which is being replaced by the smartphone. Nobody
+needed to wait for printouts from a big computer in another building when they
+could use a little one down the hall.  Then nobody needed the big computer
+down the hall when they had a little one on their desk.  Now nobody needs the
+big computer on their desk when they have a little one in their pocket.</p>
+
+<p>The new platform displaces the old when it becomes natively self hosting.
+Often they leverage existing technology: just as early microcmputers used
+teletypes and televisions for output, phones can use
+<a href=http://us.toshiba.com/accessory/PA3575U-1PRP>USB docking stations</a>
+to access a bigger screen, mouse, keyboard, speakers, etc. Plugging a phone into
+USB even charges the battery. But to use the phone as a development
+workstation, it needs more software, such as a Posix command line, a native
+compiler, and drivers for the USB peripherals.</p>
+
+<p>The new platform also eventually weans itself off of its dominant language.
+Dalvik is to Android what ROM Basic was to the PC: something it must
+eventually outgrow. Thus toybox is native C code, not Java.</p>
+
+<b><h3>So why aren't self-hosting smartphones attracting more attention?</h3></b>
+
+<p>Because most people are focusing on the legacy platforms, not on the new
+stuff. Existing multi-billion dollar industries are getting evicted from their
+decades-old established niche, and are trying to spin the transition as an
+opportunity instead of a forced march onto reservations. When elephants run
+from mice, it's easier to notice the elephants.</p>
+
+<p>History's our guide here: the previous technology always gets kicked up into
+the "server space", moving from "the thing you stood in front of waiting for
+your printout" to "that thing you sometimes accessed remotely via the new
+computer". This time around they're calling it "the cloud" and pretending it's
+a big deal; it's really just a beowulf cluster with a layer of
+virtualization/containerization software implementing hotplug hardware and
+live migration to provide cheap
+commodity processing power that dominant players (like amazon) literally
+give away for free. These old machines become secondary, only
+accessed through the new machines users now directly interact with.</p>
+
+<p>Since there's only one server space, the mainframe ate the minicomputer in
+the 1980's (when DEC went under), and this time around "the cloud" seems to be
+eating the mainframe (IBM ain't happy). The inevitable consolidation leads
+to drama, but doesn't mean much in the long run.</p>
+
+<p><a href=http://landley.net/notes-2012.html#12-07-2012>For more
+on this topic...</a></p>
+
+<b><h3><a name="why_android">Why is Android important?</h2></b>
+
+<p>Major hardware transitions introduce
+<a href=http://landley.net/notes-2011.html#26-06-2011>new software
+standards</a> which are extremely sticky once
+established, due to network effects.</p>
+
+<p>Last time around, the PC was stuck with
+a proprietary operating system (DOS/Windows) which is still dominant on that
+hardware platform's descendants 30 years later. This time around, the choice
+is between Android (a Linux derivative) and iPhone (a closed BSD fork ala
+SunOS, put out by a company already engaged in multiple aggressive IP lawsuits).
+The main difference between Apple and Microsoft is that Apple is competent.</p>
+
+<p>And yes, it has to be Android, it won't be vanilla Linux any time soon,
+for three reasons. 1) <a href=http://landley.net/notes-2010.html#13-08-2010>Open
+Source can't do user interfaces</a> for about the same reason wikipedia can't
+write a novel, 2) it's too late to the
+party (a 5 year headstart is forever in computers), 3) preinstalls matter
+(GPLv3 spooked all the hardware vendors, Android has a "no GPL in
+userspace" policy which is rigidly enforced).</p>
+
+<p>And "any time soon" is important: attempting to displace an existing
+entrenched de-facto standard is what linux has spent the last 20
+years trying (and failing) to do on the desktop. Spending another 20
+years fighting for less than 1% of the phone market would just be sad.</p>
+
+<b><h3><a name="how_google">How is Google less evil than Apple?</h3></b>
+
+<p>Because Android isn't Google's core business, attaching advertising to large
+scale data searches is. Android and Chrome and such are Google's way of
+"commoditizing their co-factors" to drive down the price of ingredients
+to their core business.</p>
+
+<p>Thus Google is pursuing a commodity market and encouring as many vendors as
+possible to participate, not to control the new space but to hold it open,
+so that its search products are widely available without requiring the
+permission of some other monopoly gatekeeper. Apple is attemping to corner the
+smartphone market and extract monopoly rents, excluding all
+vendors except itself.</p>
+
+<p>So if Google wins we get a commodity market in smartphone/tablet software,
+and may be able to open it further in future. If Apple wins we get a proprietary
+smartphone/tablet OS with a single monopoly vendor, which is likely to close it
+further.</p>
+
+<b><h3>Why not just use BusyBox?</h3></b>
+
+<p>Android can't. Busybox predates android
+by many years; if they were ever going to ship it they'd have done so by
+now. Android has had a "No GPL in Userspace" policy ever since GPLv3
+came out (before the first Android phone shipped), and they mean it.</p>
+
+<p>Toybox also has a better design and simpler code. I did both
+and this is the one I enjoy banging on; I tried to contribute a few things
+to busybox and it was like crawling through a thornbush of #ifdefs. Busybox
+development is just no fun anymore.</p>
+
+-->
+
+<b><h2><a name="toycans" />What's the toybox logo image?</h2></b>
+
+<p>It's <a href=toycans-big.jpg>carefully stacked soda cans</a>.  Specifically,
+it's a bunch of the original "Coke Zero" and "Pepsi One" cans, circa 2006,
+stacked to spell out the binary values of the ascii string "Toybox", with
+null terminator at the bottom.  (The big picture's on it's side because
+the camera was held sideways to get a better shot.)</p>
+
+<p>No, it's not photoshopped, I actually had these cans until a coworker
+who Totally Did Not Get It <sup><font size=-3><a href=http://www.timesys.com>tm</a></font></sup> threw them out one day after I'd gone home,
+thinking they were recycling.  (I still have two of each kind, but
+Pepsi One seems discontinued and Coke Zero switched its can color
+from black to grey, presumably in celebration.  It was fun while it lasted...)</p>
+
+<!--#include file="footer.html" -->
diff --git a/www/code.html b/www/code.html
new file mode 100644 (file)
index 0000000..7548356
--- /dev/null
@@ -0,0 +1,1026 @@
+<!--#include file="header.html" -->
+
+<p><h1>Code style</h1></p>
+
+<p>The primary goal of toybox is _simple_ code. Keeping the code small is
+second, with speed and lots of features coming in somewhere after that.
+(For more on that, see the <a href=design.html>design</a> page.)</p>
+
+<p>A simple implementation usually takes up fewer lines of source code,
+meaning more code can fit on the screen at once, meaning the programmer can
+see more of it on the screen and thus keep more if in their head at once.
+This helps code auditing and thus reduces bugs. That said, sometimes being
+more explicit is preferable to being clever enough to outsmart yourself:
+don't be so terse your code is unreadable.</p>
+
+<p>Toybox source uses two spaces per indentation level, and wraps at 80
+columns.</p>
+
+<p>Gotos are allowed for error handling, and for breaking out of
+nested loops.  In general, a goto should only jump forward (not back), and
+should either jump to the end of an outer loop, or to error handling code
+at the end of the function.  Goto labels are never indented: they override the
+block structure of the file.  Putting them at the left edge makes them easy
+to spot as overrides to the normal flow of control, which they are.</p>
+
+<p><h1>Building Toybox:</h1></p>
+
+<p>Toybox is configured using the Kconfig language pioneered by the Linux
+kernel, and adopted by many other projects (uClibc, OpenEmbedded, etc).
+This generates a ".config" file containing the selected options, which
+controls which features are included when compiling toybox.</p>
+
+<p>Each configuration option has a default value. The defaults indicate the
+"maximum sane configuration", I.E. if the feature defaults to "n" then it
+either isn't complete or is a special-purpose option (such as debugging
+code) that isn't intended for general purpose use.</p>
+
+<p>The standard build invocation is:</p>
+
+<ul>
+<li>make defconfig #(or menuconfig)</li>
+<li>make</li>
+<li>make install</li>
+</ul>
+
+<p>Type "make help" to see all available build options.</p>
+
+<p>The file "configure" contains a number of environment variable definitions
+which influence the build, such as specifying which compiler to use or where
+to install the resulting binaries. This file is included by the build, but
+accepts existing definitions of the environment variables, so it may be sourced
+or modified by the developer before building and the definitions exported
+to the environment will take precedence.</p>
+
+<p>(To clarify: "configure" describes the build and installation environment,
+".config" lists the features selected by defconfig/menuconfig.)</p>
+
+<p><h1>Infrastructure:</h1></p>
+
+<p>The toybox source code is in following directories:</p>
+<ul>
+<li>The <a href="#top">top level directory</a> contains the file main.c (were
+execution starts), the header file toys.h (included by every command), and
+other global infrastructure.</li>
+<li>The <a href="#lib">lib directory</a> contains common functions shared by
+multiple commands:</li>
+<ul>
+<li><a href="#lib_lib">lib/lib.c</a></li>
+<li><a href="#lib_llist">lib/llist.c</a></li>
+<li><a href="#lib_args">lib/args.c</a></li>
+<li><a href="#lib_dirtree">lib/dirtree.c</a></li>
+</ul>
+<li>The <a href="#toys">toys directory</a> contains the C files implementating
+each command. Currently it contains three subdirectories:
+posix, lsb, and other.</li>
+<li>The <a href="#scripts">scripts directory</a> contains the build and
+test infrastructure.</li>
+<li>The <a href="#kconfig">kconfig directory</a> contains the configuration
+infrastructure implementing menuconfig (copied from the Linux kernel).</li>
+<li>The <a href="#generated">generated directory</a> contains intermediate
+files generated from other parts of the source code.</li>
+</ul>
+
+<a name="adding" />
+<p><h1>Adding a new command</h1></p>
+<p>To add a new command to toybox, add a C file implementing that command under
+the toys directory.  No other files need to be modified; the build extracts
+all the information it needs (such as command line arguments) from specially
+formatted comments and macros in the C file.  (See the description of the
+<a href="#generated">"generated" directory</a> for details.)</p>
+
+<p>Currently there are three subdirectories under "toys", one for commands
+defined by the POSIX standard, one for commands defined by the Linux Standard
+Base, and one for all other commands. (This is just for developer convenience
+sorting them, the directories are otherwise functionally identical.)</p>
+
+<p>An easy way to start a new command is copy the file "toys/other/hello.c" to
+the name of the new command, and modify this copy to implement the new command.
+This file is an example command meant to be used as a "skeleton" for
+new commands (more or less by turning every instance of "hello" into the
+name of your command, updating the command line arguments, globals, and
+help data,  and then filling out its "main" function with code that does
+something interesting).  It provides examples of all the build infrastructure
+(including optional elements like command line argument parsing and global
+variables that a "hello world" program doesn't strictly need).</p>
+
+<p>Here's a checklist of steps to turn hello.c into another command:</p>
+
+<ul>
+<li><p>First "cd toys/other" and "cp hello.c yourcommand.c".  Note that the name
+of this file is significant, it's the name of the new command you're adding
+to toybox.  Open your new file in your favorite editor.</p></li>
+
+<li><p>Change the one line comment at the top of the file (currently
+"hello.c - A hello world program") to describe your new file.</p></li>
+
+<li><p>Change the copyright notice to your name, email, and the current
+year.</p></li>
+
+<li><p>Give a URL to the relevant standards document, where applicable.
+(Sample links to SUSv4 and LSB are provided, feel free to link to other
+documentation or standards as appropriate.)</p></li>
+
+<li><p>Update the USE_YOURCOMMAND(NEWTOY(yourcommand,"blah",0)) line.
+The NEWTOY macro fills out this command's <a href="#toy_list">toy_list</a>
+structure.  The arguments to the NEWTOY macro are:</p>
+
+<ol>
+<li><p>the name used to run your command</p></li>
+<li><p>the command line argument <a href="#lib_args">option parsing string</a> (NULL if none)</p></li>
+<li><p>a bitfield of TOYFLAG values
+(defined in toys.h) providing additional information such as where your
+command should be installed on a running system, whether to blank umask
+before running, whether or not the command must run as root (and thus should
+retain root access if installed SUID), and so on.</p></li>
+</ol>
+</li>
+
+<li><p>Change the kconfig data (from "config YOURCOMMAND" to the end of the
+comment block) to supply your command's configuration and help
+information.  The uppper case config symbols are used by menuconfig, and are
+also what the CFG_ and USE_() macros are generated from (see [TODO]).  The
+help information here is used by menuconfig, and also by the "help" command to
+describe your new command.  (See [TODO] for details.)  By convention,
+unfinished commands default to "n" and finished commands default to "y",
+so "make defconfig" selects all finished commands.  (Note, "finished" means
+"ready to be used", not that it'll never change again.)<p>
+
+<p>Each help block should start with a "usage: yourcommand" line explaining
+any command line arguments added by this config option.  The "help" command
+outputs this text, and scripts/config2help.c in the build infrastructure
+collates these usage lines for commands with multiple configuration
+options when producing generated/help.h.</p>
+</li>
+
+<li><p>Change the "#define FOR_hello" line to "#define FOR_yourcommand" right
+before the "#include <toys.h>". (This selects the appropriate FLAG_ macros and
+does a "#define TT this.yourcommand" so you can access the global variables
+out of the space-saving union of structures. If you aren't using any command
+flag bits and aren't defining a GLOBAL block, you can delete this line.)</p></li>
+
+<li><p>Update the GLOBALS() macro to contain your command's global
+variables. If your command has no global variables, delete this macro.</p>
+
+<p>Variables in the GLOBALS() block are are stored in a space saving
+<a href="#toy_union">union of structures</a> format, which may be accessed
+using the TT macro as if TT were a global structure (so TT.membername).
+If you specified two-character command line arguments in
+NEWTOY(), the first few global variables will be initialized by the automatic
+argument parsing logic, and the type and order of these variables must
+correspond to the arguments specified in NEWTOY().
+(See <a href="#lib_args">lib/args.c</a> for details.)</p></li>
+
+<li><p>Rename hello_main() to yourcommand_main().  This is the main() function
+where execution of your command starts. Your command line options are
+already sorted into this.optflags, this.optargs, this.optc, and the GLOBALS()
+as appropriate by the time this function is called. (See
+<a href="#lib_args">get_optflags()</a> for details.</p></li>
+</ul>
+
+<p><a name="top" /><h2>Top level directory.</h2></p>
+
+<p>This directory contains global infrastructure.</p>
+
+<h3>toys.h</h3>
+<p>Each command #includes "toys.h" as part of its standard prolog. It
+may "#define FOR_commandname" before doing so to get some extra entries
+specific to this command.</p>
+
+<p>This file sucks in most of the commonly used standard #includes, so
+individual files can just #include "toys.h" and not have to worry about
+stdargs.h and so on.  Individual commands still need to #include
+special-purpose headers that may not be present on all systems (and thus would
+prevent toybox from building that command on such a system with that command
+enabled).  Examples include regex support, any "linux/" or "asm/" headers, mtab
+support (mntent.h and sys/mount.h), and so on.</p>
+
+<p>The toys.h header also defines structures for most of the global variables
+provided to each command by toybox_main().  These are described in
+detail in the description for main.c, where they are initialized.</p>
+
+<p>The global variables are grouped into structures (and a union) for space
+savings, to more easily track the amount of memory consumed by them,
+so that they may be automatically cleared/initialized as needed, and so
+that access to global variables is more easily distinguished from access to
+local variables.</p>
+
+<h3>main.c</h3>
+<p>Contains the main() function where execution starts, plus
+common infrastructure to initialize global variables and select which command
+to run.  The "toybox" multiplexer command also lives here.  (This is the
+only command defined outside of the toys directory.)</p>
+
+<p>Execution starts in main() which trims any path off of the first command
+name and calls toybox_main(), which calls toy_exec(), which calls toy_find()
+and toy_init() before calling the appropriate command's function from
+toy_list[] (via toys.which->toy_main()).
+If the command is "toybox", execution recurses into toybox_main(), otherwise
+the call goes to the appropriate commandname_main() from a C file in the toys
+directory.</p>
+
+<p>The following global variables are defined in main.c:</p>
+<ul>
+<a name="toy_list" />
+<li><p><b>struct toy_list toy_list[]</b> - array describing all the
+commands currently configured into toybox.  The first entry (toy_list[0]) is
+for the "toybox" multiplexer command, which runs all the other built-in commands
+without symlinks by using its first argument as the name of the command to
+run and the rest as that command's argument list (ala "./toybox echo hello").
+The remaining entries are the commands in alphabetical order (for efficient
+binary search).</p>
+
+<p>This is a read-only array initialized at compile time by
+defining macros and #including generated/newtoys.h.</p>
+
+<p>Members of struct toy_list (defined in "toys.h") include:</p>
+<ul>
+<li><p>char *<b>name</b> - the name of this command.</p></li>
+<li><p>void (*<b>toy_main</b>)(void) - function pointer to run this
+command.</p></li>
+<li><p>char *<b>options</b> - command line option string (used by
+get_optflags() in lib/args.c to intialize toys.optflags, toys.optargs, and
+entries in the toy's GLOBALS struct).  When this is NULL, no option
+parsing is done before calling toy_main().</p></li>
+<li><p>int <b>flags</b> - Behavior flags for this command.  The following flags are currently understood:</p>
+
+<ul>
+<li><b>TOYFLAG_USR</b> - Install this command under /usr</li>
+<li><b>TOYFLAG_BIN</b> - Install this command under /bin</li>
+<li><b>TOYFLAG_SBIN</b> - Install this command under /sbin</li>
+<li><b>TOYFLAG_NOFORK</b> - This command can be used as a shell builtin.</li>
+<li><b>TOYFLAG_UMASK</b> - Call umask(0) before running this command.</li>
+<li><b>TOYFLAG_STAYROOT</b> - Don't drop permissions for this command if toybox is installed SUID root.</li>
+<li><b>TOYFLAG_NEEDROOT</b> - This command cannot function unless run with root access.</li>
+</ul>
+<br>
+
+<p>These flags are combined with | (or).  For example, to install a command
+in /usr/bin, or together TOYFLAG_USR|TOYFLAG_BIN.</p>
+</ul>
+</li>
+
+<li><p><b>struct toy_context toys</b> - global structure containing information
+common to all commands, initializd by toy_init() and defined in "toys.h".
+Members of this structure include:</p>
+<ul>
+<li><p>struct toy_list *<b>which</b> - a pointer to this command's toy_list
+structure.  Mostly used to grab the name of the running command
+(toys->which.name).</p>
+</li>
+<li><p>int <b>exitval</b> - Exit value of this command.  Defaults to zero.  The
+error_exit() functions will return 1 if this is zero, otherwise they'll
+return this value.</p></li>
+<li><p>char **<b>argv</b> - "raw" command line options, I.E. the original
+unmodified string array passed in to main().  Note that modifying this changes
+"ps" output, and is not recommended.  This array is null terminated; a NULL
+entry indicates the end of the array.</p>
+<p>Most commands don't use this field, instead the use optargs, optflags,
+and the fields in the GLOBALS struct initialized by get_optflags().</p>
+</li>
+<li><p>unsigned <b>optflags</b> - Command line option flags, set by
+<a href="#lib_args">get_optflags()</a>.  Indicates which of the command line options listed in
+toys->which.options occurred this time.</p>
+
+<p>The rightmost command line argument listed in toys->which.options sets bit
+1, the next one sets bit 2, and so on.  This means the bits are set in the same
+order the binary digits would be listed if typed out as a string.  For example,
+the option string "abcd" would parse the command line "-c" to set optflags to 2,
+"-a" would set optflags to 8, and "-bd" would set optflags to 6 (4|2).</p>
+
+<p>Only letters are relevant to optflags.  In the string "a*b:c#d", d=1, c=2,
+b=4, a=8.  Punctuation after a letter initializes global variables at the
+start of the GLOBALS() block (see <a href="#toy_union">union toy_union this</a>
+for details).</p>
+
+<p>The build infrastructure creates FLAG_ macros for each option letter,
+corresponding to the bit position, so you can check (toys.optflags & FLAG_x)
+to see if a flag was specified. (The correct set of FLAG_ macros is selected
+by defining FOR_mycommand before #including toys.h. The macros live in
+toys/globals.h which is generated by scripts/make.sh.)</p>
+
+<p>For more information on option parsing, see <a href="#lib_args">get_optflags()</a>.</p>
+
+</li>
+<li><p>char **<b>optargs</b> - Null terminated array of arguments left over
+after get_optflags() removed all the ones it understood.  Note: optarg[0] is
+the first argument, not the command name.  Use toys.which->name for the command
+name.</p></li>
+<li><p>int <b>optc</b> - Optarg count, equivalent to argc but for
+optargs[].<p></li>
+<li><p>int <b>exithelp</b> - Whether error_exit() should print a usage message
+via help_main() before exiting.  (True during option parsing, defaults to
+false afterwards.)</p></li>
+</ul>
+
+<a name="toy_union" />
+<li><p><b>union toy_union this</b> - Union of structures containing each
+command's global variables.</p>
+
+<p>Global variables are useful: they reduce the overhead of passing extra
+command line arguments between functions, they conveniently start prezeroed to
+save initialization costs, and the command line argument parsing infrastructure
+can also initialize global variables with its results.</p>
+
+<p>But since each toybox process can only run one command at a time, allocating
+space for global variables belonging to other commands you aren't currently
+running would be wasteful.</p>
+
+<p>Toybox handles this by encapsulating each command's global variables in
+a structure, and declaring a union of those structures with a single global
+instance (called "this").  The GLOBALS() macro contains the global
+variables that should go in the current command's global structure.  Each
+variable can then be accessed as "this.commandname.varname".
+If you #defined FOR_commandname before including toys.h, the macro TT is
+#defined to this.commandname so the variable can then be accessed as
+"TT.variable".  See toys/hello.c for an example.</p>
+
+<p>A command that needs global variables should declare a structure to
+contain them all, and add that structure to this union.  A command should never
+declare global variables outside of this, because such global variables would
+allocate memory when running other commands that don't use those global
+variables.</p>
+
+<p>The first few fields of this structure can be intialized by <a href="#lib_args">get_optargs()</a>,
+as specified by the options field off this command's toy_list entry.  See
+the get_optargs() description in lib/args.c for details.</p>
+</li>
+
+<li><b>char toybuf[4096]</b> - a common scratch space buffer so
+commands don't need to allocate their own.  Any command is free to use this,
+and it should never be directly referenced by functions in lib/ (although
+commands are free to pass toybuf in to a library function as an argument).</li>
+</ul>
+
+<p>The following functions are defined in main.c:</p>
+<ul>
+<li><p>struct toy_list *<b>toy_find</b>(char *name) - Return the toy_list
+structure for this command name, or NULL if not found.</p></li>
+<li><p>void <b>toy_init</b>(struct toy_list *which, char *argv[]) - fill out
+the global toys structure, calling get_optargs() if necessary.</p></li>
+<li><p>void <b>toy_exec</b>(char *argv[]) - Run a built-in command with
+arguments.</p>
+<p>Calls toy_find() on argv[0] (which must be just a command name
+without path).  Returns if it can't find this command, otherwise calls
+toy_init(), toys->which.toy_main(), and exit() instead of returning.</p>
+
+<p>Use the library function xexec() to fall back to external executables
+in $PATH if toy_exec() can't find a built-in command.  Note that toy_exec()
+does not strip paths before searching for a command, so "./command" will
+never match an internal command.</li>
+
+<li><p>void <b>toybox_main</b>(void) - the main function for the multiplexer
+command (I.E. "toybox").  Given a command name as its first argument, calls
+toy_exec() on its arguments.  With no arguments, it lists available commands.
+If the first argument starts with "-" it lists each command with its default
+install path prepended.</p></li>
+
+</ul>
+
+<h3>Config.in</h3>
+
+<p>Top level configuration file in a stylized variant of
+<a href=http://kernel.org/doc/Documentation/kbuild/kconfig-language.txt>kconfig</a> format.  Includes generated/Config.in.</p>
+
+<p>These files are directly used by "make menuconfig" to select which commands
+to build into toybox (thus generating a .config file), and by
+scripts/config2help.py to create generated/help.h.</p>
+
+<h3>Temporary files:</h3>
+
+<p>There is one temporary file in the top level source directory:</p>
+<ul>
+<li><p><b>.config</b> - Configuration file generated by kconfig, indicating
+which commands (and options to commands) are currently enabled.  Used
+to make generated/config.h and determine which toys/*.c files to build.</p>
+
+<p>You can create a human readable "miniconfig" version of this file using
+<a href=http://landley.net/aboriginal/new_platform.html#miniconfig>these
+instructions</a>.</p>
+</li>
+</ul>
+
+<a name="generated" />
+<p>The "generated/" directory contains files generated from other source code
+in toybox.  All of these files can be recreated by the build system, although
+some (such as generated/help.h) are shipped in release versions to reduce
+environmental dependencies (I.E. so you don't need python on your build
+system).</p>
+
+<ul>
+<li><p><b>generated/config.h</b> - list of CFG_SYMBOL and USE_SYMBOL() macros,
+generated from .config by a sed invocation in the top level Makefile.</p>
+
+<p>CFG_SYMBOL is a comple time constant set to 1 for enabled symbols and 0 for
+disabled symbols.  This allows the use of normal if() statements to remove
+code at compile time via the optimizer's dead code elimination (which removes
+from the binary any code that cannot be reached).  This saves space without
+cluttering the code with #ifdefs or leading to configuration dependent build
+breaks.  (See the 1992 Usenix paper
+<a href=http://doc.cat-v.org/henry_spencer/ifdef_considered_harmful.pdf>#ifdef
+Considered Harmful</a> for more information.)</p>
+
+<p>USE_SYMBOL(code) evaluates to the code in parentheses when the symbol
+is enabled, and nothing when the symbol is disabled.  This can be used
+for things like varargs or variable declarations which can't always be
+eliminated by a simple test on CFG_SYMBOL.  Note that
+(unlike CFG_SYMBOL) this is really just a variant of #ifdef, and can
+still result in configuration dependent build breaks.  Use with caution.</p>
+</li>
+</ul>
+
+<p><h2>Directory toys/</h2></p>
+
+<h3>toys/Config.in</h3>
+
+<p>Included from the top level Config.in, contains one or more
+configuration entries for each command.</p>
+
+<p>Each command has a configuration entry matching the command name (although
+configuration symbols are uppercase and command names are lower case).
+Options to commands start with the command name followed by an underscore and
+the option name.  Global options are attached to the "toybox" command,
+and thus use the prefix "TOYBOX_".  This organization is used by
+scripts/cfg2files to select which toys/*.c files to compile for a given
+.config.</p>
+
+<p>A command with multiple names (or multiple similar commands implemented in
+the same .c file) should have config symbols prefixed with the name of their
+C file.  I.E. config symbol prefixes are NEWTOY() names.  If OLDTOY() names
+have config symbols they're options (symbols with an underscore and suffix)
+to the NEWTOY() name.  (See toys/toylist.h)</p>
+
+<h3>toys/toylist.h</h3>
+<p>The first half of this file prototypes all the structures to hold
+global variables for each command, and puts them in toy_union.  These
+prototypes are only included if the macro NEWTOY isn't defined (in which
+case NEWTOY is defined to a default value that produces function
+prototypes).</p>
+
+<p>The second half of this file lists all the commands in alphabetical
+order, along with their command line arguments and install location.
+Each command has an appropriate configuration guard so only the commands that
+are enabled wind up in the list.</p>
+
+<p>The first time this header is #included, it defines structures and
+produces function prototypes for the commands in the toys directory.</p>
+
+
+<p>The first time it's included, it defines structures and produces function
+prototypes.
+  This
+is used to initialize toy_list in main.c, and later in that file to initialize
+NEED_OPTIONS (to figure out whether the command like parsing logic is needed),
+and to put the help entries in the right order in toys/help.c.</p>
+
+<h3>toys/help.h</h3>
+
+<p>#defines two help text strings for each command: a single line
+command_help and an additinal command_help_long.  This is used by help_main()
+in toys/help.c to display help for commands.</p>
+
+<p>Although this file is generated from Config.in help entries by
+scripts/config2help.py, it's shipped in release tarballs so you don't need
+python on the build system.  (If you check code out of source control, or
+modify Config.in, then you'll need python installed to rebuild it.)</p>
+
+<p>This file contains help for all commands, regardless of current
+configuration, but only the currently enabled ones are entered into help_data[]
+in toys/help.c.</p>
+
+<a name="lib">
+<h2>Directory lib/</h2>
+
+<p>TODO: document lots more here.</p>
+
+<p>lib: getmountlist(), error_msg/error_exit, xmalloc(),
+strlcpy(), xexec(), xopen()/xread(), xgetcwd(), xabspath(), find_in_path(),
+itoa().</p>
+
+<h3>lib/portability.h</h3>
+
+<p>This file is automatically included from the top of toys.h, and smooths
+over differences between platforms (hardware targets, compilers, C libraries,
+operating systems, etc).</p>
+
+<p>This file provides SWAP macros (SWAP_BE16(x) and SWAP_LE32(x) and so on).</p>
+
+<p>A macro like SWAP_LE32(x) means "The value in x is stored as a little
+endian 32 bit value, so perform the translation to/from whatever the native
+32-bit format is".  You do the swap once on the way in, and once on the way
+out. If your target is already little endian, the macro is a NOP.</p>
+
+<p>The SWAP macros come in BE and LE each with 16, 32, and 64 bit versions.
+In each case, the name of the macro refers to the _external_ representation,
+and converts to/from whatever your native representation happens to be (which
+can vary depending on what you're currently compiling for).</p>
+
+<a name="lib_llist"><h3>lib/llist.c</h3>
+
+<p>Some generic single and doubly linked list functions, which take
+advantage of a couple properties of C:</p>
+
+<ul>
+<li><p>Structure elements are laid out in memory in the order listed, and
+the first element has no padding. This means you can always treat (typecast)
+a pointer to a structure as a pointer to the first element of the structure,
+even if you don't know anything about the data following it.</p></li>
+
+<li><p>An array of length zero at the end of a structure adds no space
+to the sizeof() the structure, but if you calculate how much extra space
+you want when you malloc() the structure it will be available at the end.
+Since C has no bounds checking, this means each struct can have one variable
+length array.</p></li>
+</ul>
+
+<p>Toybox's list structures always have their <b>next</b> pointer as
+the first entry of each struct, and singly linked lists end with a NULL pointer.
+This allows generic code to traverse such lists without knowing anything
+else about the specific structs composing them: if your pointer isn't NULL
+typecast it to void ** and dereference once to get the next entry.</p>
+
+<p><b>lib/lib.h</b> defines three structure types:</p>
+<ul>
+<li><p><b>struct string_list</b> - stores a single string (<b>char str[0]</b>),
+memory for which is allocated as part of the node. (I.E. llist_traverse(list,
+free); can clean up after this type of list.)</p></li>
+
+<li><p><b>struct arg_list</b> - stores a pointer to a single string
+(<b>char *arg</b>) which is stored in a separate chunk of memory.</p></li>
+
+<li><p><b>struct double_list</b> - has a second pointer (<b>struct double_list
+*prev</b> along with a <b>char *data</b> for payload.</p></li>
+</ul>
+
+<b>List Functions</b>
+
+<ul>
+<li><p>void *<b>llist_pop</b>(void **list) - advances through a list ala
+<b>node = llist_pop(&list);</b>  This doesn't modify the list contents,
+but does advance the pointer you feed it (which is why you pass the _address_
+of that pointer, not the pointer itself).</p></li>
+
+<li><p>void <b>llist_traverse</b>(void *list, void (*using)(void *data)) -
+iterate through a list calling a function on each node.</p></li>
+
+<li><p>struct double_list *<b>dlist_add</b>(struct double_list **llist, char *data)
+- append an entry to a circular linked list.
+This function allocates a new struct double_list wrapper and returns the
+pointer to the new entry (which you can usually ignore since it's llist->prev,
+but if llist was NULL you need it). The argument is the ->data field for the
+new node.</p></li>
+<ul><li><p>void <b>dlist_add_nomalloc</b>(struct double_list **llist,
+struct double_list *new) - append existing struct double_list to
+list, does not allocate anything.</p></li></ul>
+</ul>
+
+<b>Trivia questions:</b>
+
+<ul>
+<li><p><b>Why do arg_list and double_list contain a char * payload instead of
+a void *?</b> - Because you always have to typecast a void * to use it, and
+typecasting a char * does no harm. Thus having it default to the most common
+pointer type saves a few typecasts (strings are the most common payload),
+and doesn't hurt anything otherwise.</p>
+</li>
+
+<li><p><b>Why do the names ->str, ->arg, and ->data differ?</b> - To force
+you to keep track of which one you're using, calling free(node->str) would
+be bad, and _failing_ to free(node->arg) leaks memory.</p></li>
+
+<li><p><b>Why does llist_pop() take a void * instead of void **?</b> -
+because the stupid compiler complains about "type punned pointers" when
+you typecast and dereference ont he same line,
+due to insane FSF developers hardwiring limitations of their optimizer
+into gcc's warning system. Since C automatically typecasts any other
+pointer _down_ to a void *, the current code works fine. It's sad that it
+won't warn you if you forget the &, but the code crashes pretty quickly in
+that case.</p></li>
+
+<li><p><b>How do I assemble a singly-linked-list in order?</b> - use
+a double_list, dlist_add() your entries, and then break the circle with
+<b>list->prev->next = NULL;</b> when done.</li>
+</ul>
+
+<a name="lib_args"><h3>lib/args.c</h3>
+
+<p>Toybox's main.c automatically parses command line options before calling the
+command's main function.  Option parsing starts in get_optflags(), which stores
+results in the global structures "toys" (optflags and optargs) and "this".</p>
+
+<p>The option parsing infrastructure stores a bitfield in toys.optflags to
+indicate which options the current command line contained.  Arguments
+attached to those options are saved into the command's global structure
+("this").  Any remaining command line arguments are collected together into
+the null-terminated array toys.optargs, with the length in toys.optc.  (Note
+that toys.optargs does not contain the current command name at position zero,
+use "toys.which->name" for that.)  The raw command line arguments get_optflags()
+parsed are retained unmodified in toys.argv[].</p>
+
+<p>Toybox's option parsing logic is controlled by an "optflags" string, using
+a format reminiscent of getopt's optargs but has several important differences.
+Toybox does not use the getopt()
+function out of the C library, get_optflags() is an independent implementation
+which doesn't permute the original arguments (and thus doesn't change how the
+command is displayed in ps and top), and has many features not present in
+libc optargs() (such as the ability to describe long options in the same string
+as normal options).</p>
+
+<p>Each command's NEWTOY() macro has an optflags string as its middle argument,
+which sets toy_list.options for that command to tell get_optflags() what
+command line arguments to look for, and what to do with them.
+If a command has no option
+definition string (I.E. the argument is NULL), option parsing is skipped
+for that command, which must look at the raw data in toys.argv to parse its
+own arguments.  (If no currently enabled command uses option parsing,
+get_optflags() is optimized out of the resulting binary by the compiler's
+--gc-sections option.)</p>
+
+<p>You don't have to free the option strings, which point into the environment
+space (I.E. the string data is not copied).  A TOYFLAG_NOFORK command
+that uses the linked list type "*" should free the list objects but not
+the data they point to, via "llist_free(TT.mylist, NULL);".  (If it's not
+NOFORK, exit() will free all the malloced data anyway unless you want
+to implement a CONFIG_TOYBOX_FREE cleanup for it.)</p>
+
+<h4>Optflags format string</h4>
+
+<p>Note: the optflags option description string format is much more
+concisely described by a large comment at the top of lib/args.c.</p>
+
+<p>The general theory is that letters set optflags, and punctuation describes
+other actions the option parsing logic should take.</p>
+
+<p>For example, suppose the command line <b>command -b fruit -d walrus -a 42</b>
+is parsed using the optflags string "<b>a#b:c:d</b>".  (I.E.
+toys.which->options="a#b:c:d" and argv = ["command", "-b", "fruit", "-d",
+"walrus", "-a", "42"]).  When get_optflags() returns, the following data is
+available to command_main():
+
+<ul>
+<li><p>In <b>struct toys</b>:
+<ul>
+<li>toys.optflags = 13; // -a = 8 | -b = 4 | -d = 1</li>
+<li>toys.optargs[0] = "walrus"; // leftover argument</li>
+<li>toys.optargs[1] = NULL; // end of list</li>
+<li>toys.optc=1; // there was 1 leftover argument</li>
+<li>toys.argv[] = {"-b", "fruit", "-d", "walrus", "-a", "42"}; // The original command line arguments
+</ul>
+<p></li>
+
+<li><p>In <b>union this</b> (treated as <b>long this[]</b>):
+<ul>
+<li>this[0] = NULL; // -c didn't get an argument this time, so get_optflags() didn't change it and toys_init() zeroed "this" during setup.)</li>
+<li>this[1] = (long)"fruit"; // argument to -b</li>
+<li>this[2] = 42; // argument to -a</li>
+</ul>
+</p></li>
+</ul>
+
+<p>If the command's globals are:</p>
+
+<blockquote><pre>
+GLOBALS(
+       char *c;
+       char *b;
+       long a;
+)
+</pre></blockquote>
+<p>That would mean TT.c == NULL, TT.b == "fruit", and TT.a == 42.  (Remember,
+each entry that receives an argument must be a long or pointer, to line up
+with the array position.  Right to left in the optflags string corresponds to
+top to bottom in GLOBALS().</p>
+
+<p><b>long toys.optflags</b></p>
+
+<p>Each option in the optflags string corresponds to a bit position in
+toys.optflags, with the same value as a corresponding binary digit.  The
+rightmost argument is (1<<0), the next to last is (1<<1) and so on.  If
+the option isn't encountered while parsing argv[], its bit remains 0.</p>
+
+<p>For example,
+the optflags string "abcd" would parse the command line argument "-c" to set
+optflags to 2, "-a" would set optflags to 8, "-bd" would set optflags to
+6 (I.E. 4|2), and "-a -c" would set optflags to 10 (2|8).</p>
+
+<p>Only letters are relevant to optflags, punctuation is skipped: in the
+string "a*b:c#d", d=1, c=2, b=4, a=8.  The punctuation after a letter
+usually indicate that the option takes an argument.</p>
+
+<p>Since toys.optflags is an unsigned int, it only stores 32 bits.  (Which is
+the amount a long would have on 32-bit platforms anyway; 64 bit code on
+32 bit platforms is too expensive to require in common code used by almost
+all commands.)  Bit positions beyond the 1<<31 aren't recorded, but
+parsing higher options can still set global variables.</p>
+
+<p><b>Automatically setting global variables from arguments (union this)</b></p>
+
+<p>The following punctuation characters may be appended to an optflags
+argument letter, indicating the option takes an additional argument:</p>
+
+<ul>
+<li><b>:</b> - plus a string argument, keep most recent if more than one.</li>
+<li><b>*</b> - plus a string argument, appended to a linked list.</li>
+<li><b>@</b> - plus an occurrence counter (stored in a long)</li>
+<li><b>#</b> - plus a signed long argument.
+<li><b>.</b> - plus a floating point argument (if CFG_TOYBOX_FLOAT).</li>
+<ul>The following can be appended to a float or double:
+<li><b>&lt;123</b> - error if argument is less than this</li>
+<li><b>&gt;123</b> - error if argument is greater than this</li>
+<li><b>=123</b> - default value if argument not supplied</li>
+</ul>
+<ul><li>Option parsing only understands <>= after . when CFG_TOYBOX_FLOAT
+is enabled. (Otherwise the code to determine where floating point constants
+end drops out.  When disabled, it can reserve a global data slot for the
+argument so offsets won't change, but will never fill it out.). You can handle
+this by using the USE_BLAH() macros with C string concatenation, ala:
+"abc." USE_TOYBOX_FLOAT("<1.23>4.56=7.89") "def"</li></ul>
+</ul>
+
+<p>Arguments may occur with or without a space (I.E. "-a 42" or "-a42").
+The command line argument "-abc" may be interepreted many different ways:
+the optflags string "cba" sets toys.optflags = 7, "c:ba" sets toys.optflags=4
+and saves "ba" as the argument to -c, and "cb:a" sets optflags to 6 and saves
+"c" as the argument to -b.</p>
+
+<p>Options which have an argument fill in the corresponding slot in the global
+union "this" (see generated/globals.h), treating it as an array of longs
+with the rightmost saved in this[0].  Again using "a*b:c#d", "-c 42" would set
+this[0]=42; and "-b 42" would set this[1]="42"; each slot is left NULL if
+the corresponding argument is not encountered.</p>
+
+<p>This behavior is useful because the LP64 standard ensures long and pointer
+are the same size. C99 guarantees structure members will occur in memory
+in the same order they're declared, and that padding won't be inserted between
+consecutive variables of register size.  Thus the first few entries can
+be longs or pointers corresponding to the saved arguments.</p>
+
+<p><b>char *toys.optargs[]</b></p>
+
+<p>Command line arguments in argv[] which are not consumed by option parsing
+(I.E. not recognized either as -flags or arguments to -flags) will be copied
+to toys.optargs[], with the length of that array in toys.optc.
+(When toys.optc is 0, no unrecognized command line arguments remain.)
+The order of entries is preserved, and as with argv[] this new array is also
+terminated by a NULL entry.</p>
+
+<p>Option parsing can require a minimum or maximum number of optargs left
+over, by adding "<1" (read "at least one") or ">9" ("at most nine") to the
+start of the optflags string.</p>
+
+<p>The special argument "--" terminates option parsing, storing all remaining
+arguments in optargs.  The "--" itself is consumed.</p>
+
+<p><b>Other optflags control characters</b></p>
+
+<p>The following characters may occur at the start of each command's
+optflags string, before any options that would set a bit in toys.optflags:</p>
+
+<ul>
+<li><b>^</b> - stop at first nonoption argument (for nice, xargs...)</li>
+<li><b>?</b> - allow unknown arguments (pass non-option arguments starting
+with - through to optargs instead of erroring out).</li>
+<li><b>&amp;</b> - the first argument has imaginary dash (ala tar/ps.  If given twice, all arguments have imaginary dash.)</li>
+<li><b>&lt;</b> - must be followed by a decimal digit indicating at least this many leftover arguments are needed in optargs (default 0)</li>
+<li><b>&gt;</b> - must be followed by a decimal digit indicating at most this many leftover arguments allowed (default MAX_INT)</li>
+</ul>
+
+<p>The following characters may be appended to an option character, but do
+not by themselves indicate an extra argument should be saved in this[].
+(Technically any character not recognized as a control character sets an
+optflag, but letters are never control characters.)</p>
+
+<ul>
+<li><b>^</b> - stop parsing options after encountering this option, everything else goes into optargs.</li>
+<li><b>|</b> - this option is required.  If more than one marked, only one is required.</li>
+<li><b>+X</b> enabling this option also enables option X (switch bit on).</li>
+<li><b>~X</b> enabling this option disables option X (switch bit off).</li>
+<li><b>!X</b> this option cannot be used in combination with X (die with error).</li>
+<li><b>[yz]</b> this option requires at least one of y or z to also be enabled.</li>
+</ul>
+
+<p>The following may be appended to a float or double:</p>
+
+<ul>
+<li><b>&lt;123</b> - error if argument is less than this</li>
+<li><b>&gt;123</b> - error if argument is greater than this</li>
+<li><b>=123</b> - default value if argument not supplied</li>
+</ul>
+
+<p>Option parsing only understands <>= after . when CFG_TOYBOX_FLOAT
+is enabled. (Otherwise the code to determine where floating point constants
+end drops out.  When disabled, it can reserve a global data slot for the
+argument so offsets won't change, but will never fill it out.). You can handle
+this by using the USE_BLAH() macros with C string concatenation, ala:</p>
+
+<blockquote>"abc." USE_TOYBOX_FLOAT("<1.23>4.56=7.89") "def"</blockquote>
+
+<p><b>--longopts</b></p>
+
+<p>The optflags string can contain long options, which are enclosed in
+parentheses.  They may be appended to an existing option character, in
+which case the --longopt is a synonym for that option, ala "a:(--fred)"
+which understands "-a blah" or "--fred blah" as synonyms.</p>
+
+<p>Longopts may also appear before any other options in the optflags string,
+in which case they have no corresponding short argument, but instead set
+their own bit based on position.  So for "(walrus)#(blah)xy:z" "command
+--walrus 42" would set toys.optflags = 16 (-z = 1, -y = 2, -x = 4, --blah = 8)
+and would assign this[1] = 42;</p>
+
+<p>A short option may have multiple longopt synonyms, "a(one)(two)", but
+each "bare longopt" (ala "(one)(two)abc" before any option characters)
+always sets its own bit (although you can group them with +X).</p>
+
+<a name="lib_dirtree"><h3>lib/dirtree.c</h3>
+
+<p>The directory tree traversal code should be sufficiently generic
+that commands never need to use readdir(), scandir(), or the fts.h family
+of functions.</p>
+
+<p>These functions do not call chdir() or rely on PATH_MAX. Instead they
+use openat() and friends, using one filehandle per directory level to
+recurseinto subdirectories. (I.E. they can descend 1000 directories deep
+if setrlimit(RLIMIT_NOFILE) allows enough open filehandles, and the default
+in /proc/self/limits is generally 1024.)</p>
+
+<p>The basic dirtree functions are:</p>
+
+<ul>
+<li><p><b>dirtree_read(char *path, int (*callback)(struct dirtree node))</b> -
+recursively read directories, either applying callback() or returning
+a tree of struct dirtree if callback is NULL.</p></li>
+
+<li><p><b>dirtree_path(struct dirtree *node, int *plen)</b> - malloc() a
+string containing the path from the root of this tree to this node. If
+plen isn't NULL then *plen is how many extra bytes to malloc at the end
+of string.</p></li>
+
+<li><p><b>dirtree_parentfd(struct dirtree *node)</b> - return fd of
+containing directory, for use with openat() and such.</p></li>
+</ul>
+
+<p>The <b>dirtree_read()</b> function takes two arguments, a starting path for
+the root of the tree, and a callback function. The callback takes a
+<b>struct dirtree *</b> (from lib/lib.h) as its argument. If the callback is
+NULL, the traversal uses a default callback (dirtree_notdotdot()) which
+recursively assembles a tree of struct dirtree nodes for all files under
+this directory and subdirectories (filtering out "." and ".." entries),
+after which dirtree_read() returns the pointer to the root node of this
+snapshot tree.</p>
+
+<p>Otherwise the callback() is called on each entry in the directory,
+with struct dirtree * as its argument. This includes the initial
+node created by dirtree_read() at the top of the tree.</p>
+
+<p><b>struct dirtree</b></p>
+
+<p>Each struct dirtree node contains <b>char name[]</b> and <b>struct stat
+st</b> entries describing a file, plus a <b>char *symlink</b>
+which is NULL for non-symlinks.</p>
+
+<p>During a callback function, the <b>int data</b> field of directory nodes
+contains a dirfd (for use with the openat() family of functions). This is
+generally used by calling dirtree_parentfd() on the callback's node argument.
+For symlinks, data contains the length of the symlink string. On the second
+callback from DIRTREE_COMEAGAIN (depth-first traversal) data = -1 for
+all nodes (that's how you can tell it's the second callback).</p>
+
+<p>Users of this code may put anything they like into the <b>long extra</b>
+field. For example, "cp" and "mv" use this to store a dirfd for the destination
+directory (and use DIRTREE_COMEAGAIN to get the second callback so they can
+close(node->extra) to avoid running out of filehandles).
+This field is not directly used by the dirtree code, and
+thanks to LP64 it's large enough to store a typecast pointer to an
+arbitrary struct.</p>
+
+<p>The return value of the callback combines flags (with boolean or) to tell
+the traversal infrastructure how to behave:</p>
+
+<ul>
+<li><p><b>DIRTREE_SAVE</b> - Save this node, assembling a tree. (Without
+this the struct dirtree is freed after the callback returns. Filtering out
+siblings is fine, but discarding a parent while keeping its child leaks
+memory.)</p></li>
+<li><p><b>DIRTREE_ABORT</b> - Do not examine any more entries in this
+directory. (Does not propagate up tree: to abort entire traversal,
+return DIRTREE_ABORT from parent callbacks too.)</p></li>
+<li><p><b>DIRTREE_RECURSE</b> - Examine directory contents. Ignored for
+non-directory entries. The remaining flags only take effect when
+recursing into the children of a directory.</p></li>
+<li><p><b>DIRTREE_COMEAGAIN</b> - Call the callback a second time after
+examining all directory contents, allowing depth-first traversal.
+On the second call, dirtree->data = -1.</p></li>
+<li><p><b>DIRTREE_SYMFOLLOW</b> - follow symlinks when populating children's
+<b>struct stat st</b> (by feeding a nonzero value to the symfollow argument of
+dirtree_add_node()), which means DIRTREE_RECURSE treats symlinks to
+directories as directories. (Avoiding infinite recursion is the callback's
+problem: the non-NULL dirtree->symlink can still distinguish between
+them.)</p></li>
+</ul>
+
+<p>Each struct dirtree contains three pointers (next, parent, and child)
+to other struct dirtree.</p>
+
+<p>The <b>parent</b> pointer indicates the directory
+containing this entry; even when not assembling a persistent tree of
+nodes the parent entries remain live up to the root of the tree while
+child nodes are active. At the top of the tree the parent pointer is
+NULL, meaning the node's name[] is either an absolute path or relative
+to cwd. The function dirtree_parentfd() gets the directory file descriptor
+for use with openat() and friends, returning AT_FDCWD at the top of tree.</p>
+
+<p>The <b>child</b> pointer points to the first node of the list of contents of
+this directory. If the directory contains no files, or the entry isn't
+a directory, child is NULL.</p>
+
+<p>The <b>next</b> pointer indicates sibling nodes in the same directory as this
+node, and since it's the first entry in the struct the llist.c traversal
+mechanisms work to iterate over sibling nodes. Each dirtree node is a
+single malloc() (even char *symlink points to memory at the end of the node),
+so llist_free() works but its callback must descend into child nodes (freeing
+a tree, not just a linked list), plus whatever the user stored in extra.</p>
+
+<p>The <b>dirtree_read</b>() function is a simple wrapper, calling <b>dirtree_add_node</b>()
+to create a root node relative to the current directory, then calling
+<b>handle_callback</b>() on that node (which recurses as instructed by the callback
+return flags). Some commands (such as chgrp) bypass this wrapper, for example
+to control whether or not to follow symlinks to the root node; symlinks
+listed on the command line are often treated differently than symlinks
+encountered during recursive directory traversal).
+
+<p>The ls command not only bypasses the wrapper, but never returns
+<b>DIRTREE_RECURSE</b> from the callback, instead calling <b>dirtree_recurse</b>() manually
+from elsewhere in the program. This gives ls -lR manual control
+of traversal order, which is neither depth first nor breadth first but
+instead a sort of FIFO order requried by the ls standard.</p>
+
+<a name="#toys">
+<h2>Directory toys/</h2>
+
+<p>This directory contains command implementations. Each command is a single
+self-contained file. Adding a new command involves adding a single
+file, and removing a command involves removing that file. Commands use
+shared infrastructure from the lib/ and generated/ directories.</p>
+
+<p>Currently there are three subdirectories under "toys/" containing commands
+described in POSIX-2008, the Linux Standard Base 4.1, or "other". The only
+difference this makes is which menu the command shows up in during "make
+menuconfig", the directories are otherwise identical. Note that they commands
+exist within a single namespace at runtime, so you can't have the same
+command in multiple subdirectories.</p>
+
+<p>(There are actually four sub-menus in "make menuconfig", the fourth
+contains global configuration options for toybox, and lives in Config.in at
+the top level.)</p>
+
+<p>See <a href="#adding">adding a new command</a> for details on the
+layout of a command file.</p>
+
+<h2>Directory scripts/</h2>
+
+<p>Build infrastructure. The makefile calls scripts/make.sh for "make"
+and scripts/install.sh for "make install".</p>
+
+<p>There's also a test suite, "make test" calls make/test.sh, which runs all
+the tests in make/test/*. You can run individual tests via
+"scripts/test.sh command", or "TEST_HOST=1 scripts/test.sh command" to run
+that test against the host implementation instead of the toybox one.</p>
+
+<h3>scripts/cfg2files.sh</h3>
+
+<p>Run .config through this filter to get a list of enabled commands, which
+is turned into a list of files in toys via a sed invocation in the top level
+Makefile.
+</p>
+
+<h2>Directory kconfig/</h2>
+
+<p>Menuconfig infrastructure copied from the Linux kernel.  See the
+Linux kernel's Documentation/kbuild/kconfig-language.txt</p>
+
+<a name="generated">
+<h2>Directory generated/</h2>
+
+<p>All the files in this directory except the README are generated by the
+build.  (See scripts/make.sh)</p>
+
+<ul>
+<li><p><b>config.h</b> - CFG_COMMAND and USE_COMMAND() macros set by menuconfig via .config.</p></li>
+
+<li><p><b>Config.in</b> - Kconfig entries for each command.  Included by top level Config.in.  The help text in here is used to generated help.h</p></li>
+
+<li><p><b>help.h</b> - Help text strings for use by "help" command.  Building
+this file requires python on the host system, so the prebuilt file is shipped
+in the build tarball to avoid requiring python to build toybox.</p></li>
+
+<li><p><b>newtoys.h</b> - List of NEWTOY() or OLDTOY() macros for all available
+commands.  Associates command_main() functions with command names, provides
+option string for command line parsing (<a href="#lib_args">see lib/args.c</a>),
+specifies where to install each command and whether toysh should fork before
+calling it.</p></li>
+</ul>
+
+<p>Everything in this directory is a derivative file produced from something
+else.  The entire directory is deleted by "make distclean".</p>
+<!--#include file="footer.html" -->
diff --git a/www/design.html b/www/design.html
new file mode 100644 (file)
index 0000000..ce5fc55
--- /dev/null
@@ -0,0 +1,286 @@
+<!--#include file="header.html" -->
+
+<b><h2>Design goals</h2></b>
+
+<p>Toybox should be simple, small, fast, and full featured.  Often, these
+things need to be balanced off against each other.  In general, keeping the
+code simple the most important (and hardest) goal, and small is slightly more
+important than fast. Features are the reason we write code in the first
+place but this has all been implemented before so if we can't do a better
+job why bother?  It should be possible to get 80% of the way to each goal
+before they really start to fight.</p>
+
+<p>Here they are in reverse order of importance:</p>
+
+<b><h3>Features</h3></b>
+
+<p>The <a href=roadmap.html>roadmap</a> has the list of features we're
+trying to implement, and the reasons for them. After the 1.0 release
+some of that material may get moved here.</p>
+
+<p>Some things are simply outside the scope of the project: even though
+posix defines commands for compiling and linking, we're not going to include
+a compiler or linker (and support for a potentially infinite number of hardware
+targets). And until somebody comes up with a ~30k ssh implementation, we're
+going to point you at dropbear or polarssl.</p>
+
+<p>Environmental dependencies are a type of complexity, so needing other
+packages to build or run is a big downside.  For example, we don't use curses
+when we can simply output ansi escape sequences and trust all terminal
+programs written in the past 30 years to be able to support them. (A common
+use case is to download a statically linked toybox binary to an arbitrary
+Linux system, and use it in an otherwise unknown environment; being
+self-contained helps support this.)</p>
+
+<b><h3>Fast</h3></b>
+
+<p>It's easy to say lots about optimizing for speed (which is why this section
+is so long), but at the same time it's the optimization we care the least about.
+The essence of speed is being as efficient as possible, which means doing as
+little work as possible.  A design that's small and simple gets you 90% of the
+way there, and most of the rest is either fine-tuning or more trouble than
+it's worth (and often actually counterproductive).  Still, here's some
+advice:</p>
+
+<p>First, understand the darn problem you're trying to solve.  You'd think
+I wouldn't have to say this, but I do.  Trying to find a faster sorting
+algorithm is no substitute for figuring out a way to skip the sorting step
+entirely.  The fastest way to do anything is not to have to do it at all,
+and _all_ optimization boils down to avoiding unnecessary work.</p>
+
+<p>Speed is easy to measure; there are dozens of profiling tools for Linux
+(although personally I find the "time" command a good starting place).
+Don't waste too much time trying to optimize something you can't measure,
+and there's no much point speeding up things you don't spend much time doing
+anyway.</p>
+
+<p>Understand the difference between throughput and latency.  Faster
+processors improve throughput, but don't always do much for latency.
+After 30 years of Moore's Law, most of the remaining problems are latency,
+not throughput.  (There are of course a few exceptions, like data compression
+code, encryption, rsync...)  Worry about throughput inside long-running
+loops, and worry about latency everywhere else.  (And don't worry too much
+about avoiding system calls or function calls or anything else in the name
+of speed unless you are in the middle of a tight loop that's you've already
+proven isn't running fast enough.)</p>
+
+<p>"Locality of reference" is generally nice, in all sorts of contexts.
+It's obvious that waiting for disk access is 1000x slower than doing stuff in
+RAM (and making the disk seek is 10x slower than sequential reads/writes),
+but it's just as true that a loop which stays in L1 cache is many times faster
+than a loop that has to wait for a DRAM fetch on each iteration.  Don't worry
+about whether "&" is faster than "%" until your executable loop stays in L1
+cache and the data access is fetching cache lines intelligently.  (To
+understand DRAM, L1, and L2 cache, read Hannibal's marvelous ram guide at Ars
+Technica:
+<a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part1-2.html>part one</a>,
+<a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part2-1.html>part two</a>,
+<a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part3-1.html>part three</a>,
+plus this
+<a href=http://arstechnica.com/articles/paedia/cpu/caching.ars/1>article on
+cacheing</a>, and this one on
+<a href=http://arstechnica.com/articles/paedia/cpu/bandwidth-latency.ars>bandwidth
+and latency</a>.
+And there's <a href=http://arstechnica.com/paedia/index.html>more where that came from</a>.)
+Running out of L1 cache can execute one instruction per clock cycle, going
+to L2 cache costs a dozen or so clock cycles, and waiting for a worst case dram
+fetch (round trip latency with a bank switch) can cost thousands of
+clock cycles.  (Historically, this disparity has gotten worse with time,
+just like the speed hit for swapping to disk.  These days, a _big_ L1 cache
+is 128k and a big L2 cache is a couple of megabytes.  A cheap low-power
+embedded processor may have 8k of L1 cache and no L2.)</p>
+
+<p>Learn how virtual memory and memory managment units work.  Don't touch
+memory you don't have to.  Even just reading memory evicts stuff from L1 and L2
+cache, which may have to be read back in later.  Writing memory can force the
+operating system to break copy-on-write, which allocates more memory.  (The
+memory returned by malloc() is only a virtual allocation, filled with lots of
+copy-on-write mappings of the zero page.  Actual physical pages get allocated
+when the copy-on-write gets broken by writing to the virtual page.  This
+is why checking the return value of malloc() isn't very useful anymore, it
+only detects running out of virtual memory, not physical memory.  Unless
+you're using a NOMMU system, where all bets are off.)</p>
+
+<p>Don't think that just because you don't have a swap file the system can't
+start swap thrashing: any file backed page (ala mmap) can be evicted, and
+there's a reason all running programs require an executable file (they're
+mmaped, and can be flushed back to disk when memory is short).  And long
+before that, disk cache gets reclaimed and has to be read back in.  When the
+operating system really can't free up any more pages it triggers the out of
+memory killer to free up pages by killing processes (the alternative is the
+entire OS freezing solid).  Modern operating systems seldom run out of
+memory gracefully.</p>
+
+<p>Also, it's better to be simple than clever.  Many people think that mmap()
+is faster than read() because it avoids a copy, but twiddling with the memory
+management is itself slow, and can cause unnecessary CPU cache flushes.  And
+if a read faults in dozens of pages sequentially, but your mmap iterates
+backwards through a file (causing lots of seeks, each of which your program
+blocks waiting for), the read can be many times faster.  On the other hand, the
+mmap can sometimes use less memory, since the memory provided by mmap
+comes from the page cache (allocated anyway), and it can be faster if you're
+doing a lot of different updates to the same area.  The moral?  Measure, then
+try to speed things up, and measure again to confirm it actually _did_ speed
+things up rather than made them worse.  (And understanding what's really going
+on underneath is a big help to making it happen faster.)</p>
+
+<p>In general, being simple is better than being clever.  Optimization
+strategies change with time.  For example, decades ago precalculating a table
+of results (for things like isdigit() or cosine(int degrees)) was clearly
+faster because processors were so slow.  Then processors got faster and grew
+math coprocessors, and calculating the value each time became faster than
+the table lookup (because the calculation fit in L1 cache but the lookup
+had to go out to DRAM).  Then cache sizes got bigger (the Pentium M has
+2 megabytes of L2 cache) and the table fit in cache, so the table became
+fast again...  Predicting how changes in hardware will affect your algorithm
+is difficult, and using ten year old optimization advice and produce
+laughably bad results.  But being simple and efficient is always going to
+give at least a reasonable result.</p>
+
+<p>The famous quote from Ken Thompson, "When in doubt, use brute force",
+applies to toybox.  Do the simple thing first, do as little of it as possible,
+and make sure it's right.  You can always speed it up later.</p>
+
+<b><h3>Small</h3></b>
+<p>Again, simple gives you most of this.  An algorithm that does less work
+is generally smaller.  Understand the problem, treat size as a cost, and
+get a good bang for the byte.</p>
+
+<p>Understand the difference between binary size, heap size, and stack size.
+Your binary is the executable file on disk, your heap is where malloc() memory
+lives, and your stack is where local variables (and function call return
+addresses) live.  Optimizing for binary size is generally good: executing
+fewer instructions makes your program run faster (and fits more of it in
+cache).  On embedded systems, binary size is especially precious because
+flash is expensive (and its successor, MRAM, even more so).  Small stack size
+is important for nommu systems because they have to preallocate their stack
+and can't make it bigger via page fault.  And everybody likes a small heap.</p>
+
+<p>Measure the right things.  Especially with modern optimizers, expecting
+something to be smaller is no guarantee it will be after the compiler's done
+with it.  Binary size isn't the most accurate indicator of the impact of a
+given change, because lots of things get combined and rounded during
+compilation and linking.  Matt Mackall's bloat-o-meter is a python script
+which compares two versions of a program, and shows size changes in each
+symbol (using the "nm" command behind the scenes).  To use this, run
+"make baseline" to build a baseline version to compare against, and
+then "make bloatometer" to compare that baseline version against the current
+code.</p>
+
+<p>Avoid special cases.  Whenever you see similar chunks of code in more than
+one place, it might be possible to combine them and have the users call shared
+code. (This is the most commonly cited trick, which doesn't make it easy. If
+seeing two lines of code do the same thing makes you slightly uncomfortable,
+you've got the right mindset.)</p>
+
+<p>Some specific advice: Using a char in place of an int when doing math
+produces significantly larger code on some platforms (notably arm),
+because each time the compiler has to emit code to convert it to int, do the
+math, and convert it back.  Bitfields have this problem on most platforms.
+Because of this, using char to index a for() loop is probably not a net win,
+although using char (or a bitfield) to store a value in a structure that's
+repeated hundreds of times can be a good tradeoff of binary size for heap
+space.</p>
+
+<b><h3>Simple</h3></b>
+
+<p>Complexity is a cost, just like code size or runtime speed. Treat it as
+a cost, and spend your complexity budget wisely. (Sometimes this means you
+can't afford a feature because it complicates the code too much to be
+worth it.)</p>
+
+<p>Simplicity has lots of benefits.  Simple code is easy to maintain, easy to
+port to new processors, easy to audit for security holes, and easy to
+understand.  (Comments help, but they're no substitute for simple code.)</p>
+
+<p>Prioritizing simplicity tends to serve our other goals: simplifying code
+generally reduces its size (both in terms of binary size and runtime memory
+usage), and avoiding unnecessary work makes code run faster. Smaller code
+also tends to run faster on modern hardware due to CPU cacheing: fitting your
+code into L1 cache is great, and staying in L2 cache is still pretty good.</p>
+
+
+<p><a href=http://www.joelonsoftware.com/articles/fog0000000069.html>Joel
+Spolsky argues against throwing code out and starting over</a>, and he has
+good points: an existing debugged codebase contains a huge amount of baked
+in knowledge about strange real-world use cases that the designers didn't
+know about until users hit the bugs, and most of this knowledge is never
+explicitly stated anywhere except in the source code.</p>
+
+<p>That said, the Mythical Man-Month's "build one to throw away" advice points
+out that until you've solved the problem you don't properly understand it, and
+about the time you finish your first version is when you've finally figured
+out what you _should_ have done.  (The corrolary is that if you build one
+expecting to throw it away, you'll actually wind up throwing away two.  You
+don't understand the problem until you _have_ solved it.)</p>
+
+<p>Joel is talking about what closed source software can afford to do: Code
+that works and has been paid for is a corporate asset not lightly abandoned.
+Open source software can afford to re-implement code that works, over and
+over from scratch, for incremental gains.  Before toybox, the unix command line
+has already been reimplemented from scratch several times in a row (the
+original AT&amp;T Unix command line in assembly and then in C, the BSD
+versions, the GNU tools, BusyBox...) but maybe toybox can do a better job. :)</p>
+
+<p>P.S.  How could I resist linking to an article about
+<a href=http://blog.outer-court.com/archive/2005-08-24-n14.html>why
+programmers should strive to be lazy and dumb</a>?</p>
+
+<b><h2>Portability issues</h2></b>
+
+<b><h3>Platforms</h3></b>
+<p>Toybox should run on Android (alas, with bionic), and every other hardware
+platform Linux runs on.  Other posix/susv4 environments (perhaps MacOS X or
+newlib+libgloss) are vaguely interesting but only if they're easy to support;
+I'm not going to spend much effort on them.</p>
+
+<p>I don't do windows.</p>
+
+<b><h3>32/64 bit</h3></b>
+<p>Toybox should work on both 32 bit and 64 bit systems.  By the end of 2008
+64 bit hardware will be the new desktop standard, but 32 bit hardware will
+continue to be important in embedded devices for years to come.</p>
+
+<p>Toybox relies on the fact that on any Unix-like platform, pointer and long
+are always the same size (on both 32 and 64 bit).  Pointer and int are _not_
+the same size on 64 bit systems, but pointer and long are.</p>
+
+<p>This is guaranteed by the LP64 memory model, a Unix standard (which Linux
+and MacOS X both implement).  See
+<a href=http://www.unix.org/whitepapers/64bit.html>the LP64 standard</a> and
+<a href=http://www.unix.org/version2/whatsnew/lp64_wp.html>the LP64
+rationale</a> for details.</p>
+
+<p>Note that Windows doesn't work like this, and I don't care.
+<a href=http://blogs.msdn.com/oldnewthing/archive/2005/01/31/363790.aspx>The
+insane legacy reasons why this is broken on Windows are explained here.</a></p>
+
+<b><h3>Signedness of char</h3></b>
+<p>On platforms like x86, variables of type char default to unsigned.  On
+platforms like arm, char defaults to signed.  This difference can lead to
+subtle portability bugs, and to avoid them we specify which one we want by
+feeding the compiler -funsigned-char.</p>
+
+<p>The reason to pick "unsigned" is that way we're 8-bit clean by default.</p>
+
+<p><h3>Error messages and internationalization:</h3></p>
+<p>Error messages are extremely terse not just to save bytes, but because we
+don't use any sort of _("string") translation infrastructure.</p>
+
+<p>Thus "bad -A '%c'" is
+preferable to "Unrecognized address base '%c'", because a non-english speaker
+can see that -A was the problem, and with a ~20 word english vocabulary is
+more likely to know (or guess) "bad" than the longer message.</p>
+
+<p>The help text might someday have translated versions, and strerror()
+messages produced by perror_exit() and friends can be expected to be
+localized by libc. Our error functions also prepend the command name,
+which non-english speakers can presumably recognize already.</p>
+
+<p>An enventual goal is <a href=http://yarchive.net/comp/linux/utf8.html>UTF-8</a> support, although it isn't a priority for the
+first pass of each command. (All commands should at least be 8-bit clean.)</p>
+
+<p>Locale support isn't currently a goal; that's a presentation layer issue,
+X11 or Dalvik's problem.</p>
+
+<!--#include file="footer.html" -->
diff --git a/www/ext2.html b/www/ext2.html
new file mode 100644 (file)
index 0000000..28bd6cd
--- /dev/null
@@ -0,0 +1,108 @@
+<title>Rob's ext2 documentation</title>
+
+<p>This page focuses on the ext2 on-disk format.  The Linux kernel's filesystem
+implementation (the code to read and write it) is documented in the kernel
+source, Documentation/filesystems/ext2.txt.</p>
+
+<p>Note: for our purposes, ext3 and ext4 are just ext2 with some extra data
+fields.</p>
+
+<h2>Overview</h2>
+
+<h2>Blocks and Block Groups</h2>
+
+<p>Every ext2 filesystem consists of blocks, which are divided into block
+groups.  Blocks can be 1k, 2k, or 4k in length.<super><a href="#1">[1]</a></super>
+All ext2 disk layout is done in terms of these logical blocks, never in
+terms of 512-byte logical blocks.</p>
+
+<p>Each block group contains as many blocks as one block can hold a
+bitmap for, so at a 1k block size a block group contains 8192 blocks (1024
+bytes * 8 bits), and at 4k block size a block group contains 32768 blocks.
+Groups are numbered starting at 0, and occur one after another on disk,
+in order, with no gaps between them.</p>
+
+<p>Block groups contain the following structures, in order:</p>
+
+<ul>
+<li>Superblock (sometimes)</li>
+<li>Group table (sometimes)</li>
+<li>Block bitmap</li>
+<li>Inode bitmap</li>
+<li>Inode table</li>
+<li>Data blocks</li>
+</ul>
+
+<p>Not all block groups contain all structures.  Specifically, the first two
+(superblock and group table) only occur in some groups, and other block
+groups start with the block bitmap and go from there.  This frees up more
+data blocks to hold actual file and directory data, see the superblock
+description for details.</p>
+
+<p>Each structure in this list is stored in its' own block (or blocks in the
+case of the group and inode tables), and doesn't share blocks with any other
+structure.  This can involve padding the end of the block with zeroes, or
+extending tables with extra entries to fill up the rest of the block.</p>
+
+<p>The linux/ext2_fs.h #include file defines struct ext2_super_block,
+struct ext2_group_desc, struct ext2_inode, struct ext2_dir_entry_2, and a lot
+of constants.  Toybox doesn't use this file directly, instead it has an e2fs.h
+include of its own containting cleaned-up versions of the data it needs.</p>
+
+<h2>Superblock</h2>
+
+<p>The superblock contains a 1024 byte structure, which toybox calls
+"struct ext2_superblock".  Where exactly this structure is to be found is
+a bit complicated for historical reasons.</p>
+
+<p>For copies of the superblock stored in block groups after the first,
+the superblock structure starts at the beginning of the first block of the
+group, with zero padding afterwards if necessary (I.E. if the block size is
+larger than 1k).  In modern "sparse superblock" filesystems (everything
+anyone still cares about), the superblock occurs in group 0 and in later groups
+that are powers of 3, 5, and 7.  (So groups 0, 1, 3, 5, 7, 9, 25, 27, 49, 81,
+125, 243, 343...)  Any block group starting with a superblock will also
+have a group descriptor table, and ones that don't won't.</p>
+
+<p>The very first superblock is weird.  This is because if you format an entire
+block device (rather than a partition), you stomp the very start of the disk
+which contains the boot sector and the partition table.  Back when ext2 on
+floppies was common, this was a big deal.</p>
+
+<p>So the very first 1024 bytes of the very first block are always left alone.
+When the block size is 1024 bytes, then that block is left alone and the
+superblock is stored in the second block instead<super><a href="#2">[2]</a>.
+When the block size is larger than 1024 bytes, the first superblock starts
+1024 bytes into the block, with the original data preserved by mke2fs and
+appropriate zero padding added to the end of the block (if necessary).</p>
+
+<h2>Group descriptor table</h2>
+<h2>Block bitmap</h2>
+<h2>Inode bitmap</h2>
+<h2>Inode table</h2>
+<h2>Data blocks</h2>
+
+<h2>Directories</h2>
+
+<p>For performance reasons, directory entries are 4-byte aligned (rec_len is
+a multiple of 4), so up to 3 bytes of padding (zeroes) can be added at the end
+of each name.  (This affects rec_len but not the name_len.)</p>
+
+<p>The last directory entry in each block is padded up to block size.  If there
+isn't enough space for another struct ext2_dentry the last </p>
+
+<p>Question: is the length stored in the inode also padded up to block size?</p>
+
+<hr />
+<p><a name="1" />Footnote 1: On some systems blocks can be larger than 4k, but
+for implementation reasons not larger than PAGE_SIZE.  So the Alpha can have
+8k blocks but most other systems couldn't mount them, thus you don't see this
+out in the wild much anymore.</p>
+
+<p><a name="2" />Footnote 2: In this case, the first_data_block field in the
+superblock structure will be set to 1.  Otherwise it's always 0.  How this
+could POSSIBLY be useful information is an open question, since A) you have to
+read the superblock before you can get this information, so you know where
+it came from, B) the first copy of the superblock always starts at offset 1024
+no matter what, and if your block size is 1024 you already know you skipped the
+first block.</p>
diff --git a/www/footer.html b/www/footer.html
new file mode 100644 (file)
index 0000000..5647c9f
--- /dev/null
@@ -0,0 +1,7 @@
+</td></tr></table>
+<hr />
+<table width="100%">
+<tr><td>Copyright 2007 Rob Landley &lt;rob@landley.net&gt;</td></tr>
+</table>
+</body>
+</html>
diff --git a/www/header.html b/www/header.html
new file mode 100644 (file)
index 0000000..465bef9
--- /dev/null
@@ -0,0 +1,42 @@
+<html>
+<head>
+<title>toybox</title>
+</head>
+<!-- body style="background: url(toycans.png) repeat;" -->
+<table border=0 cellpadding=0 cellspacing=10>
+
+<tr><td valign=top>
+  <h1>toybox</h1>
+
+  <b>About</b>
+  <ul>
+    <li><a href="index.html">News</a></li>
+    <li>What is it?<br>
+      <a href="about.html">About</a><br>
+      <a href="roadmap.html">Roadmap</a><br>
+      <a href="status.html">Status</a><br>
+    </li>
+
+    <li>Why is it?<br>
+      <a href="http://youtu.be/SGmtP5Lg_t0">video</a>/<a href=http://landley.net/talks/celf-2013.txt>outline</a><br>
+      <a href="http://www.h-online.com/open/features/Inside-the-ToyBox-An-interview-with-Rob-Landley-1447494.html">Interview</a><br>
+    </li>
+  </ul>
+  <b>Download</b>
+  <ul>
+    <li><a href="/hg/toybox">Mercurial Repository</a></li>
+    <li><a href="downloads">Releases</a></li>
+    <li><a href="bin">Binaries</a></li>
+  </ul>
+  <b>Development</b>
+  <ul>
+    <li><a href="design.html">Design</a></li>
+    <li><a href="code.html">Source walkthrough</a></li>
+    <li><a href="http://lists.landley.net/listinfo.cgi/toybox-landley.net">Mailing List</a></li>
+    <li>IRC #toybox on freenode.net</li>
+    <li><a href="/notes.html">Maintainer's Bog</a></li>
+  </ul>
+</td>
+
+<td valign=top>
+
diff --git a/www/index.html b/www/index.html
new file mode 120000 (symlink)
index 0000000..0def2f1
--- /dev/null
@@ -0,0 +1 @@
+news.html
\ No newline at end of file
diff --git a/www/license.html b/www/license.html
new file mode 100644 (file)
index 0000000..f53ddc5
--- /dev/null
@@ -0,0 +1,22 @@
+<!--#include file="header.html" -->
+
+<h2>Toybox is licensed under a simple 2-clause MIT/BSD style license:</h2>
+
+<blockquote>
+<p>Copyright (C) 2006 by Rob Landley &lt;rob@landley.net&gt;
+
+<p>Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.</p>
+
+<p>THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
+</blockquote>
+
+<P>The text of this license is included in the file LICENSE in the source.</p>
+
+<!--#include file="footer.html" -->
diff --git a/www/news.html b/www/news.html
new file mode 100644 (file)
index 0000000..f1c8594
--- /dev/null
@@ -0,0 +1,396 @@
+<!--#include file="header.html" -->
+
+<p><b>Toybox combines the most common Linux command line utilities together
+into a single BSD-licensed executable that's simple, small, fast,
+reasonably standards-compliant, and powerful enough to turn Android into
+a development environment.</b> See the links on the left for details.</p>
+
+<h2>News</h2>
+<hr><b>March 21, 2013</b>
+<p>Video of my ELC talk
+"<a href=http://youtu.be/SGmtP5Lg_t0>Why is Toybox?</a>"
+is up on youtube. Related materials include the
+<a href=http://landley.net/talks/celf-2013.txt>talk outline</a> and an
+<a href=/aboriginal/about.html#selfhost>android self-hosting writeup</a>.</p>
+
+<p>[Updated June 4] The following links jump to specific topics in the video. (Sorry about
+the ads, it's The Linux Foundation.)</p>
+
+<ul>
+<li>0m29s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=0m29s>The smartphone is replacing the PC</a></li>
+  <ul>
+  <li>4m22s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=4m22s>Software needed to become self-hosting</a></li>
+  <li>6m20s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=6m20s>Do we care if android or iphone wins?</a></li>
+  </ul>
+<li>9m45s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=9m45s>Android not vanilla: oppose or accept?</a></li>
+  <ul>
+  <li>11m30s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=11m30s>Open source can't do User Interfaces</a></li>
+  </ul>
+<li>15m09s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=15m09s>Android is not copyleft: oppose or accept?</a></li>
+<li>18m23s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=18m23s>Security issues</a></li>
+<li>21m15s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=21m15s>Solutions to the software problems</a></li>
+  <ul>
+  <li>22m55s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=22m55s>What toybox needs to be/do</a></li>
+  <li>28m17s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=28m17s>What is toybox?</a></li>
+    <ul>
+    <li>28m58s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=28m58s>Why toybox started...</a></li>
+    <li>37m50s <a href=http://www.youtube.com/watch?v=SGmtP5Lg_t0#t=37m50s>What does toybox actually implement?</a></li>
+    </ul>
+  </ul>
+</ul>
+</span>
+
+
+<hr><b>March 14, 2013</b>
+<blockquote><p>"Ford, you're turning into a penguin. Stop it." -
+The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p><a href=downloads/toybox-0.4.4.tar.bz2>Toybox 0.4.4</a> is based on
+<a href=http://landley.net/hg/toybox/shortlog/813>commit 813</a>, adding
+the "time" and "readahead" commands, plus some bugfixes.</p>
+
+<p>The "cp" command now implements the -s symlink option, plus bugfixes
+getting various corner cases right as used in actual package builds.
+"id -Gn root" should now print root's groups
+instead of the current user's. Several build fixes so toybox builds under
+Ubuntu 8.04 again (which is about as old a build environment as you
+can expect to find posix-2008 features in).</p>
+
+<p>Unfinished commands have generally been moved to "toys/pending".
+Everything else should "default y" to participate in make defconfig.
+Several of those pending commands got some basic cleanup so allyesconfig
+should at least compile (although defconfig is still what's useful).</p>
+
+<p>Significant roadmap updates, checking several other multicall binaries
+(klibc, sash, sbase, s6...) to see what commands they include.</p>
+
+<hr><b>January 18, 2013</b>
+<blockquote><p>This must be Thursday. I never could get the hang of Thursdays. - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p><a href=downloads/toybox-0.4.3.tar.bz2>Toybox 0.4.3</a> is based on
+<a href=http://landley.net/hg/toybox/shortlog/793>commit 793</a>. There
+are now exactly 100 commands in defconfig (of a little over 220 on the
+<a href=roadmap.html>todo list</a>).</p>
+
+<p>Elie De Brauwer added the rev command, cleaned up tac, implemented the -s
+and -f flags for seq, added -v and -i to killall (and fixed killall not to
+kill itself before finishing its pid list), and added to the test suite.
+Felix Janda added -m to mkdir, pwd -L and -P, and more test suite entries.</p>
+
+<p>Rob Landley added the losetup command, and fixed the existing ls, cp, and
+readlink commands. The segfault in ls
+happened when it couldn't determine the screen size (last release changed the
+default to -C and a screen size of 0 made column view unhappy), and cp got an
+extensive rewrite bringing it up to date with the dirtree changes and fixing
+a number of things it never did right in the first place. The xabspath()
+code in the library now handles a symlink after ".." properly (and the test
+suite checks for it).</p>
+
+<p>Infrastructure-wise the code is better about automatically setting the
+error return code properly. Now error_msg() sets the exit code to 1 if it's
+still defaulting to 0, and the global exit path does a fflush(NULL) with error
+bit check rather than trying to be quite so granular about flushing. (That
+means if we use printf() instead of xprintf() it still exits with the right
+error code, it just doesn't end the program early on an output error.)
+Minor bugfix so TOYBOX_DEBUG
+doesn't always warn about the lack of suid bit when toybox is built with
+at least one STAYROOT command. Bugfix for the option [grouping] logic
+(and then further fixes to the error reporting pointed out by Ashwini Sharma).
+dirtree_handle_callback() now has a prefix like the rest of the dirtree
+functions. A lot of stuff doing manual path handling was switched to using
+libc basename() (including, embarassingly, the basename command), which means
+it now correctly detects "/trailing/slash/" which the previous code didn't.</p>
+
+<p>Also, last release included some accidentally checked in debug code that
+disabled compiler optimization, so the binary size bloated a bit. It's back
+to -Os by default now.</p>
+
+<hr><b>December 15, 2012</b>
+<blockquote><p>"The major difference between a thing that might go wrong and a
+thing that cannot possibly go wrong is that when a thing that cannot possibly
+go wrong goes wrong it usually turns out to be impossible to get at or repair."
+</p><p>- The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p><a href=downloads/toybox-0.4.2.tar.bz2>Toybox 0.4.2</a> is based on
+<a href=http://landley.net/hg/toybox/shortlog/749>commit 749</a> and is
+just a resync. Linux 3.7 came out, meaning it's time to do an Aboriginal
+Linux release, and that should use a stable version of toybox. So here's
+a new stable version.</p>
+
+<p>The new commands are cut (from Jason Kyungwan Han), touch
+(from Choubey Ji), expand (from Jonathan Clairembault, and he fixed a
+bug in login), and rm (from Rob Landley). Felix Janda added UTF-8
+support infrastructure (for non-ascii character sets) with a config option.
+Elie De Brauwer added tests for cat and sha1sum, and -so options to pidof.
+The "ls" command defaults to -C (column view) now, and "readlink" now supports
+-fenq.</p>
+
+<p>Portability work: toybox should now build against the musl C library,
+and against older glibc versions (circa 2008, much before that and kernel
+features we depend on start to drop out).</p>
+
+<p>The whole codebase got reindented from "one tab" to "two spaces" per
+level. The option parsing logic now understands [groups] of commands (when more
+than one in a group is selected it can switch the others off, or error out,
+or other things). The error_exit() infrastructure can now longjmp back to an
+earlier point instead of exiting. Each toys/* directory now has a README,
+the first line of which is the fancy name menuconfig uses for the directory
+(so no more hardwired directory list in scripts/genconfig.sh).</p>
+
+<p>Fixed a filehandle leak in getmountlist().
+Pass parent pointer to dirtree_add_node() so it can give error messages with
+full path. The yesno() function now always reads from stdin and writes to
+stderr (we can retry tty checking complexity once we've got commands needing
+it).</p>
+
+<p>The open group broke their website so the
+<a href=http://opengroup.org/onlinepubs/9699919799>old links</a> to POSIX 2008
+now <a href=http://pubs.opengroup.org/onlinepubs/9699919799>need to start with
+pubs</a>. Some of the links in the tree have been updated, others haven't while
+I wait to see if their webmaster notices and fixes it.</p>
+
+<p>(I note that the current rm implementation is not technically posix compliant
+because the standard requires infinite recursion depth and the current
+implementation uses one filehandle per level. I can add a config option
+to do it Posix's way, which is more brittle and needs extra security checks,
+but am waiting for somebody to complain first. The default "ulimit -n" is 1024
+filehandles, so drilling down over 1000 nested subdirectories).</p>
+
+<hr><b>November 13, 2012</b>
+<blockquote><p>"Rule Six: The winning team shall be the first team that wins."
+- The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p><a href=downloads/toybox-0.4.1.tar.bz2>Toybox 0.4.1</a> is based on
+<a href=http://landley.net/hg/toybox/shortlog/691>commit 691</a>.</p>
+
+<p>Elie De Brauwer contributed usleep, Ashwini Kumar contributed du, and
+Kyungwan Han contributed vconfig. Other new commands include switch_root and
+md5sum, and the remaining shell wrappers are now proper commands (dos2unix,
+unix2dos).</p>
+
+<p>The patch command now supports -l, and gethostname is now enabled by
+default. The df command follows symlinks to get the actual device name.
+Felix Janda added -m support to wc (for utf8).</p>
+
+<p>On the infrastructure side, the commands have now been grouped into
+"posix", "lsb", and "other" subdirectories (for things required by Posix-2008,
+the Linux Standard Base 4.1, and commands in neither). This affects menuconfig
+and the actual source layout (toys/cp.c is now toys/posix/cp.c, and so on).
+An android directory is planned (see the updated
+<a href=roadmap.html#android>android roadmap analysis</a>).</p>
+
+<p>The FLAG_ macros for command option parsing and TT alias for the command's
+global block are now automatically generated, commands should
+#define FOR_commandname before #including <toys.h> to get the macros for that
+command.</p>
+
+<p>An upgrade to the build infrastructure now allows commands with _ and -
+in them, such as switch_root.</p>
+
+<p>Bugfixes: Avery Pennarun spotted a case where ls showed uid twice instead of
+uid and gid, and that nice was using the wrong range of numbers.
+The ls command also recursed inappropriately last time (not quite
+properly converted for the dirtree changes last release), and now it's
+fixed. Roy Tam pointed out a glitch in sh, and fixed df's percentage
+calculation to match the POSIX spec. The kernel build didn't like our mktemp
+and it does now. The wc command wasn't quite posix compliant (trailing spaces
+break stuff). The ls command recursed inappropriately last time (not quite
+properly converted for the dirtree changes last release), and now it's
+fixed. The catv command wasn't displaying byte 255 correctly. Some lib
+fixes (thinko in xpidfile). Fixed uname -m when running a 32 bit x86 binary
+on an x86-64 host (it lies and says the system is i686, i586, or i486 depending
+on what the toolchain that built the binary supported. This makes builds in
+a 32 bit chroot on a 64 bit kernel break less.) The df command was checking
+partitions in the wrong order (displaying undermounts instead of overmounts:
+this used to work but some library code changed out from under it and it
+wasn't updated to match until now). Felix Janda filled out the test suite
+some more. The patch file creation logic got tweaked several times to
+successfully apply more patches. Support for older (pre 2.10) glibc
+versions was added to portability.h.</p>
+
+<p>Miscelaneous cleanups all around (mknod, sha1sum, logname), including a
+rewrite of taskset to be less dependent on libc getting the headers right. All
+the command headers should now point to the current relevant standards
+document, where applicable.</p>
+
+<p>This news page had old news entries from before the relaunch moved into
+a separate <a href=oldnews.html>oldnews</a> page.</p>
+
+<p>I forgot to create <a href=bin>static binaries</a> last time, but they're
+back now.</p>
+</span>
+
+<hr><b>July 23, 2012</b>
+<blockquote><p>"Ford", Arthur said. "There's an infinite number of monkeys
+out here who want to talk to us about this script for Hamlet they've worked
+out." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p><a href=downloads/toybox-0.4.0.tar.bz2>Toybox 0.4.0</a> is based on
+<a href=http://landley.net/hg/toybox/shortlog/640>commit 640</a>.</p>
+
+<p>The new <a href=status.html>status page</a> is calculated from
+the roadmap info, and should be easier to keep up to date in future.</p>
+
+<p>Andre Renaud contributed od and modinfo. Elie De Brauwer contributed
+taskset, bugfixes to cmp and tail, and tests for sort and tail. Kyungwan Han
+contributed passwd. Gaurang Shastri contributed w. Ashwini Sharma spotted a
+case where dirtree was adding extra slashes to a path.</p>
+
+<p>I rewrote od, cleaned up comm, documented the
+<a href=code.html#lib_llist>llist</a> and
+<a href=code.html#lib_dirtree>dirtree</a> infrastructure, added an -r option
+to date (and fixed a bug where -u wouldn't override /etc/localtime),
+fixed bugs in chmod +stw, fixed ls to show suid bits properly when the
+corresponding executable bit wasn't set, and worked around a longstanding
+glibc bug where static linking prevents stdout from automatically flushing
+pending output on exit.</p>
+
+<hr><b>June 25, 2012</b>
+<blockquote><p>"For a moment, nothing happened. Then, after a second or so, nothing continued to happen." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p><a href=downloads/toybox-0.3.1.tar.bz2>Toybox 0.3.1</a> is based on commit
+<a href=http://landley.net/hg/toybox/shortlog/607>commit 607</a>. It's
+mostly a bugfix release for ls -l (which was unhappy on targets other than
+x86-64), plus a new "date" from Andre Renaud and rewritten chgrp/chown which
+now support the full set of posix flags, plus a little work on the test
+suite and some more header tweaks towards eventual compatability with the
+musl libc.</p>
+
+<p>The todo list runneth over, but "release early, release often", so here
+it is. The roadmap and documentation are a bit behind, and I've got ~40
+pending submissions to review. I need to catch up...</p>
+</span>
+
+<hr><b>June 12, 2012</b>
+<blockquote><p>"For instance, on the planet Earth, man had always assumed that
+he was more intelligent than dolphins because he had achieved so much - the
+wheel, New York, wars and so on - whilst all the dolphins had ever done was
+muck about in the water having a good time. But conversely, the dolphins had
+always believed that they were far more intelligent than man - for precisely
+the same reasons." - The Hitchhiker's Guide to the Galaxy.</p></blockquote>
+
+<p>It's well past time for <a href=downloads/toybox-0.3.0.tar.bz2>toybox 0.3.0</a>,
+so here it is, based
+on <a href=http://landley.net/hg/toybox/shortlog/595>commit 595</a>, and the
+statically linked <a href=downloads/binaries>prebuilt binaries</a> should
+actually be statically linked this time (thanks Ashwini Sharma for spotting
+that).</p>
+
+<p>It's hard to figure out where to cut a release, because development
+doesn't stop. "Long before now" is the obviuos answer, of course.
+The project's maintainer also moved house during this development cycle, which
+threw things off for a bit (so many boxes). Releases should hopefully be a bit
+more frequent from here on.</p>
+
+<p>The big things Rob worked on this time were the new dirtree (directory
+tree traversal) infrastructure, and a complete rewrite of ls using that
+which should now implement all 26 posix options.</p>
+
+<p>Georgi Chorbadzhiyski added printenv, whoami, mkdir, mkfifo, chmod, chown,
+chgrp, and uniq. He also added fraction and extension support to sleep (so if
+you need a quarter-second sleep, it can do that now), and fixed a build bug
+on slackware.</p>
+
+<p>Daniel Walter contributed a string to mode_t parser (in use by chmod and
+mkdir -m).  Ilya Kuzmich contributed comm. Elie De Brauwer added mountpoint,
+vmstat, logname, login, and mktemp. Kevin Chase did some portability cleanups.
+Pere Orga fixed some documentation.</p>
+
+<p>The "tac" and "clear" commands are now normal commands instead of shell
+wrappers, and the header #includes have been cleaned up a bit to remove
+deprecated functions and attempt to increase compatability with the bionic and
+musl C libraries, "tail" should now use lseek() for large files, and "id" got
+some cleanups and bugfixes.</p>
+
+<p>The new TOYBOX_FLOAT configuration option selects whether or not
+to include floating point support (for embedded targets where that's
+problematic).</p>
+
+<p>Several random bugfixes: unshare() might actually build portably now,
+yes 'n' | cp -i should no longer bypass stdin and prompt via the tty, the
+SUID support no longer drops permissions going through the toybox
+multiplexer command, and a bugfix to xargs -0 means it should no longer
+segfault. (I have a pending bug report about xargs not doing the full
+posix whitespace handling that -0 obsoleted, but I'll deal with that next
+release.)</p>
+
+<p>The build infrastructure is now automatically generating FLAG_ macros
+for the options, but currently with the wrong names. Some more macro glue
+is necessary, which I haven't quite figured out how to do yet.</p>
+
+<p>A defconfig toybox at the start of the $PATH has successfully built
+Linux From Scratch (in my Aboriginal Linux project). The commands that
+'default n' in the config are often still broken, cleanup is ongoing.
+(The new dirtree stuff broke several of them that haven't been converted
+yet, but if I wait until everything works we won't have a release before
+1.0, so here's a checkpoint.)</p>
+
+
+<hr><b>March 3, 2012</b>
+
+<blockquote><p>"They went unnoticed at Goonhilly, passed over Cape Canaveral
+without a blip, and Woomera and Jodrell Bank looked straight through them.
+Which was a pity, because it was exactly the sort of thing they'd been looking
+for all these years."</p></p>- The Hitchhiker's Guide to the Galaxy.</p>
+</p></blockquote>
+
+<p>Here's <a href=downloads/toybox-0.2.1.tar.bz2>toybox 0.2.1</a> based
+on <a href=http://landley.net/hg/toybox/shortlog/512>commit 512</a>.  This
+time around, there are statically linked <a href=downloads/binaries>prebuilt
+binaries</a> for various embedded targets.</p>
+
+<p>It's been a busy few weeks, almost entirely due to new contributors. (I
+have not quite been keeping up.)</p>
+
+<p>Elie De Brauwer contributed free, uptime, swapon, swapoff, lsmod, mknod,
+insmod, rmmod, and fixed a bug in basename.  Andre Renaud contributed ls, ln,
+realpath, and hostname. Andres Heck contributed pidof and killall.  Daniel
+Walter wrote kill and extended id. Timothy Elliott contributed tail and tests
+for cmp. Frank Bergmann sent a warning fix. Bryce Fricke added -i to cp.
+Nathan McSween pointed out an optimization. Georgi Chorbadzhiyski fixed
+cross compiling to work more reliably.</p>
+
+<p>(My own contribution this time around was just tightening up other people's
+code, a build fix to unshare, some random bugfixes, and so on. My only new
+code this time around was writing a bash replacement for the existing python
+bloat-o-meter.)</p>
+
+<p>Last time (the 0.2.0 release) included the first pass at an id command from
+Tim Bird, env and basename from Tryn Mirell, cmp and head from Timothy Elliott,
+more bugfixes from Nathan McSween and Elie De Brauwer, and Luis Felipe Strano
+Moraes did a first pass at the who command plus other bugfixes and
+optimizations.</p>
+
+<p>(For that release I did xargs, cal, truncate, unlink, nohup, tty, wc, link,
+dirname, unshare, and various infrastructure tweaks, but it took me 3 months
+and those guys did their stuff in a week or so.)</p>
+
+
+<hr><b>February 12, 2012</b>
+<blockquote><p>
+"for though it has many omissions and contains much that is apocryphal, or at
+least wildly inaccurate, it scores over the older, more pedestrian work in two
+important respects..."</p>
+<p> - The Hitchhiker's Guide to the Galaxy</p></blockquote>
+
+<p>Here's the first BSD licensed release,
+<a href=downloads/toybox-0.2.0.tar.bz2>toybox-0.2.0</a>, more a synchronization
+point than anything particularly useful.  47 commands in a reasonably
+ready-to-use state (what "make defconfig" builds), another ten or so partially
+finished stubs ("make allyesconfig"), and several
+patches pending on the mailing list I need to review and merge.</p>
+
+<p>More to come...</p>
+
+<hr>
+<p><b>November 15, 2011</b> - Back from the dead, Toybox is now under a 2
+clause BSD license, and aiming to become the default command line
+implementation of Android systems everywhere.</p>
+
+<p>More to come...</p>
+
+<hr>
+
+<p><a href=oldnews.html>Old news</a> from before the relaunch.</p>
+
+<!--#include file="footer.html" -->
diff --git a/www/oldnews.html b/www/oldnews.html
new file mode 100644 (file)
index 0000000..116d4c1
--- /dev/null
@@ -0,0 +1,138 @@
+<!--#include file="header.html" -->
+
+<h2>Old News</h2>
+
+<p>This project <a href=http://lists.busybox.net/pipermail/busybox/2006-September/024794.html>started in 2006</a>, got mothballed in 2009
+<a href=http://landley.net/notes-2009.html#18-06-2009>for</a>
+<a href=http://landley.net/notes-2011.html#31-03-2011>various</a>
+<a href=http://landley.net/notes-2011.html#08-06-2011>reasons</a>
+(during which time I
+<a href=http://lists.busybox.net/pipermail/busybox/2010-March/071783.html>contributed</a>
+some toybox code and design ideas to busybox</a> but retained the copyrights to
+my work), then <a href=http://landley.net/notes-2011.html#13-11-2011>relaunched</a>
+under a 2-clause BSD license in 2011.
+The following news entries predate that relaunch:</p>
+
+<hr>
+<p><b>December 1, 2009</b> - <a href=downloads/toybox-0.1.0.tar.bz2>toybox-0.1.0</a> is out.</p>
+
+<p>This release is a couple build fixes and another bugfix to patch.</p>
+
+<hr>
+<p><b>April 17, 2009</b> - Another bugfix release,
+<a href=downloads/toybox-0.0.9.2.tar.bz2>toybox-0.0.9.2</a>, off by one allocation error in patch.</p>
+
+<p>(Darn fiddly command, innit?)</p>
+
+<hr>
+<p><b>March 29, 2009</b> - Released
+<a href=downloads/toybox-0.0.9.1.tar.bz2>toybox 0.0.9.1</a> which is a bugfix
+release for issues with the patch command.</p>
+
+<p>The project is currently on hold while the developers learn Lua and
+decide whether or not to port the whole thing to that language.
+(Also note: the mailing list moved.  See the links on the left.  You'll
+have to resubscribe.)</p>
+
+<hr>
+<p><b>January 29, 2009</b> - Released
+<a href=downloads/toybox-0.0.9.tar.bz2>toybox 0.0.9.tar.bz2</a> which is a minor packaging
+fix for 0.0.8.  (The previous release tarball contained a prebuilt x86-64
+kconfig/conf file, because the release script ran defconfig to
+pregenerate help.h, and didn't run make clean afterwards.)  The actual source
+code is identical to the previous release.</p>
+
+<hr>
+<p><b>January 20, 2009</b> - <a href=downloads/toybox-0.0.8.tar.bz2>toybox 0.0.8</a>
+adds the uname, cksum, and mkswapfs commands.</p>
+
+<p>This uname implementation is cross compile friendly: when built as a 32 bit
+binary on an x86_64 host, it reports "i686" to confuse autoconf less.</p>
+
+<p>This cksum has several extra command line options which can be used to
+produce different cksum variants based on the same crc32 algorithm.  For
+example, the broadcom "trx" image packaging uses a little endian crc,
+pre-inverted instead of post-inverted, and does not include the length.
+(Without these arguments, it produces the normal SUSv4 cksum output.)</p>
+
+<p>It also upgrades netcat with a server mode (-l option) and fixes several
+netcat bugs.  It also fixed multiple bugs in "patch", works around a
+reiserfs bug in cp, and oneit can reboot on exit more reliably.</p>
+
+<p><b>November 12, 2008</b> -
+<a href=downloads/toybox-0.0.7.tar.bz2>toybox 0.0.7</a>
+adds sort and tee commands, upgrades the internal option parsing logic and the
+test suite, and numerous bugfixes (bunzip, chroot, cat, patch).</p>
+
+<p><b>May 26, 2008</b> - <a href=downloads/toybox-0.0.6.tar.bz2>toybox 0.0.6</a>
+adds cat, rmdir, and seq.  Bugfixes to cp and a new -v option.  Updates mdev to
+work with the 2.6.25 kernel.  Updates patch to knows that a file dated
+1969-12-31 means it doesn't exist, and to fail if a file it needs to create
+already exists.  Command line option parsing can now handle things like "echo
+-nex" vs "echo -ne".  Several updates to the test suite (run scripts/test.sh),
+and some build fixes.</p>
+
+<p>
+<hr>
+<p><b>March 29, 2008</b> -
+Time to release <a href=downloads/toybox-0.0.5.tar.bz2>toybox 0.0.5</a>, with
+new commands cp and chvt and several bugfixes.</p>
+
+<p>More makefile targets: "make test" runs the test suite (which needs more test
+scripts), and make install/install_flat/uninstall/uninstall_flat calls
+make/install.sh (with options --long --symlink --force --uninstall depending
+on the context).</p>
+
+<p>Most of the work has been behind the scenes, namely a significant rewrite of
+the build logic so adding each new command consists of adding a single C file to
+the "toys" directory, eliminating the need to touch any other files.
+There are specially formatted comments at the top of the C file to generate
+the other files, see toys/hello.c for an example.  (See generated/README.txt
+and <a href=code.html>code.html</a> for details.)</p>
+
+<p><b>January 2, 2008</b> - And <a href=downloads/toybox-0.0.4.tar.bz2>toybox-0.0.4.tar.bz2</a> is out.
+The new applets this time around include basename, chroot, dirname, dmesg,
+help, mkfifo, netcat, patch, sha1sum, touch, and tty.</p>
+
+<p>Note that this "touch" includes -l to set the length, which can truncate
+a file or create a commpletely sparse file, good for filesystem images.</p>
+
+<p>Expect the next release in about 6 months.</p>
+
+<p><b>December 12, 2007</b> - Updated the list of implemented applications,
+put up a <a href=todo.txt>todo list</a> and <a href=code.html>infrastructure
+documentation</a>.  Expect another release towards the end of the month.</p>
+
+<p><b>June 18, 2007</b> - Put out
+<a href=downloads/toybox-0.0.3.tar.bz2>toybox-0.0.3.tar.bz2</a> since it's
+been too long since I did something like that.  This one implements
+catv, count, df, echo, false, oneit, pwd, sleep, sync, toysh, true, which,
+and yes (which is what "make defconfig" enables).  There are several other
+commands not enabled by defconfig, because they don't really work yet.</p>
+
+<p>Most of the general infrastructure's there now, although lots of tweaking
+and optimizing is still needed.  The test suite is skeletal and not entirely
+checked in yet, but I'm working on that.</p>
+
+<p>I don't have nearly as much time to work on this as I'd like, but I'm making
+a little progress.</p>
+
+<p><b>January 31, 2007</b> -
+Toybox <a href=downloads/toybox-0.0.2.tar.bz2>0.0.2 release</a>.
+Implements count, yes, pwd, echo, bzcat, catv, oneit, and an unfinished
+skeleton of mke2fs.  Adds argument parsing logic and bunzip code to library.
+Now configured with menuconfig.  Adds "make baseline" and "make bloatcheck"
+using Matt Mackall's bloat-o-meter, and scripts/showasm.</p>
+
+<p>Screwing up the web page a bit, adding an index bar along the side
+which doesn't properly connect up to anything yet.  (Busy implementing
+mke2fs and gene2fs.)</p>
+
+<p><b>October 30, 2006</b> -
+Toybox <a href=downloads/toybox-0.0.1.tar.bz2>0.0.1 release</a>.  Implements
+df, a skeletal toysh, and some library functions.  Proof of concept, really.</p>
+
+<p><b>September 7, 2006</b> -
+Project launched, first commit to mercurial archive.</p>
+
+<!--#include file="footer.html" -->
diff --git a/www/roadmap.html b/www/roadmap.html
new file mode 100644 (file)
index 0000000..94df830
--- /dev/null
@@ -0,0 +1,676 @@
+<!--#include file="header.html" -->
+<title>Toybox Roadmap</title>
+
+<h2>Goals and use cases</h2>
+
+<p>We have several potential use cases for a new set of command line
+utilities, and are using those to determine which commands to implement
+for Toybox's 1.0 release.</p>
+
+<p>The most interesting standards are POSIX-2008 (also known as the Single
+Unix Specification version 4) and the Linux Standard Base (version 4.1).
+The main test harness including toybox in Aboriginal Linux and if that can
+build itself using the result to build Linux From Scratch (version 6.8).
+We also aim to replace Android's Toolbox.</p>
+
+<p>At a secondary level we'd like to meet other use cases. We've analyzed
+the commands provided by similar projects (klibc, sash, sbase, s6, embutils,
+nash, and beastiebox), along with various vendor configurations of busybox,
+and some end user requests.</p>
+
+<p>Finally, we'd like to provide a good replacement for the Bash shell,
+which was the first program Linux ever ran and remains the standard shell
+of Linux no matter what Ubuntu says. This doesn't mean including the full
+set of Bash 4.x functionality, but does involve {various,features} beyond
+posix.</p>
+
+<p>See the <a href=status.html>status page</a> for the combined list
+and progress towards implementing it.</p>
+
+<ul>
+<li><a href=#susv4>POSIX-2008/SUSv4</a></li>
+<li><a href=#sigh>Linux "Standard" Base</a></li>
+<li><a href=#dev_env>Development Environment</a></li>
+<li><a href=#android>Android Toolbox</a></li>
+<li>Miscelaneous: <a href=#klibc>klibc</a>, <a href=#sash>sash</a>,
+<a href=#sbase>sbase</a>, <a href=#s6>s6</a>...</li>
+</ul>
+
+<hr />
+<a name="standards">
+<h2>Use case: standards compliance.</h2>
+
+<h3><a name=susv4 /><a href="#susv4">POSIX-2008/SUSv4</a></h3>
+<p>The best standards are the kind that describe reality, rather than
+attempting to impose a new one.  (I.E. a good standard should document, not
+legislate.)</p>
+
+<p>The kind of standards which describe existing reality tend to be approved by
+more than one standards body, such ANSI and ISO both approving C.  That's why
+the IEEE POSIX committee's 2008 standard, the Single Unix Specification version
+4, and the Open Group Base Specification edition 7 are all the same standard
+from three sources.</p>
+
+<p>The <a href="http://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html">"utilities"
+section</a>
+of these standards is devoted to the unix command line, and are the best such
+standard for our purposes.  (My earlier work on BusyBox was implemented with
+regard to SUSv3, an earlier version of this standard.)</p>
+
+<h3>Problems with the standard</h3>
+
+<p>Unfortunately, these standards describe a subset of reality, lacking any
+mention of commands such as init, login, or mount required to actually boot a
+system. It provides ipcrm and ipcs, but not ipcmk, so you can use System V IPC
+resources but not create them.</p>
+
+<p>These standards also contain a large number of commands that are
+inappropriate for toybox to implement in its 1.0 release.  (Perhaps some of
+these could be reintroduced in later releases, but not now.)</p>
+
+<p>Starting with the full "utilities" list, we first remove generally obsolete
+commands (compess ed ex pr uncompress uccp uustat uux), commands for the
+pre-CVS "SCCS" source control system (admin delta get prs rmdel sact sccs unget
+val what), fortran support (asa fort77), and batch processing support (batch
+qalter qdel qhold qmove qmsg qrerun qrls qselect qsig qstat qsub).</p>
+
+<p>Some commands are for a compiler toolchain (ar c99 cflow ctags cxref gencat
+iconv lex m4 make nm strings strip tsort yacc), which is outside of toybox's
+mandate and should be supplied externally.  (Again, some of these may be
+revisited later, but not for toybox 1.0.)</p>
+
+<p>Some commands are part of a command shell, and cannot be implemented as
+separate executables (alias bg cd command fc fg getopts hash jobs kill read
+type ulimit umask unalias wait).  These may be revisited as part of a built-in
+toybox shell, but are not exported into $PATH via symlinks.  (If you fork a
+child process and have it "cd" then exit, you've accomplished nothing.)</p>
+
+<p>A few other commands are judgement calls, providing command-line
+internationalization support (iconv locale localedef), System V inter-process
+communication (ipcrm ipcs), and cross-tty communication from the minicomputer
+days (talk mesg write).  The "pax" utility was supplanted by tar, "mailx" is
+a command line email client, and "lp" submits files for printing to... what
+exactly?  (cups?)  The standard defines crontab but not crond.</p>
+
+<p>Removing all of that leaves the following commands, which toybox should
+implement:</p>
+
+<blockquote><b>
+<span id=posix>
+at awk basename bc cal cat chgrp chmod chown cksum cmp comm cp
+csplit cut date dd df diff dirname du echo env expand expr false file find
+fold fuser getconf grep head id join kill link ln logger logname ls man
+mkdir mkfifo more mv newgrp nice nl nohup od paste patch pathchk printf ps
+pwd renice rm rmdir sed sh sleep sort split stty tabs tail tee test time
+touch tput tr true tty uname unexpand uniq unlink uudecode uuencode vi wc
+who xargs zcat
+</span>
+</b></blockquote>
+
+<h3><a name=sigh /><a href="#sigh">Linux Standard Base</a></h3>
+
+<p>One attempt to supplement POSIX towards an actual usable system was the
+Linux Standard Base. Unfortunately, the quality of this "standard" is
+fairly low.</p>
+
+<p>POSIX allowed its standards process to be compromised
+by leaving things out, thus allowing IBM mainframes and Windows NT to drive
+a truck through the holes and declare themselves compilant. But it means what
+they DID standardize tends to be respected.</p>
+
+<p>The Linux Standard Base's failure mode is different, they respond to
+pressure by including special-case crap, such as allowing Red Hat to shoehorn
+RPM on the standard even though all sorts of distros (Debian, Slackware, Arch,
+Gentoo) don't use it and probably never will. This means anything in the LSB is
+at best a suggestion: arbitrary portions of this standard are widely
+ignored.</p>
+
+<p>The LSB does specify a <a href=http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cmdbehav.html>list of command line
+utilities</a>:</p>
+
+<blockquote><b>
+ar at awk batch bc chfn chsh col cpio crontab df dmesg du echo egrep 
+fgrep file fuser gettext grep groupadd groupdel groupmod groups 
+gunzip gzip hostname install install_initd ipcrm ipcs killall lpr ls 
+lsb_release m4 md5sum mknod mktemp more mount msgfmt newgrp od passwd 
+patch pidof remove_initd renice sed sendmail seq sh shutdown su sync 
+tar umount useradd userdel usermod xargs zcat
+</b></blockquote>
+
+<p>Where posix specifies one of those commands, LSB's deltas tend to be
+accomodations for broken tool versions which aren't up to date with the
+standard yet. (See <a href=http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/more.html>more</a> and <a href=http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/xargs.html>xargs</a>
+for examples.)</p>
+
+<p>Since we've already committed to using our own judgement to skip bits of
+POSIX, and LSB's "judgement" in this regard is purely bug workarounds to declare
+various legacy tool implementations "compliant", this means we're mostly
+interested in the set of tools that aren't specified in posix at all.</p>
+
+<p>Of these, gettext and msgfmt are internationalization, install_initd and
+remove_initd aren't present on ubuntu 10.04, lpr is out of scope, and
+lsb_release is a distro issue (it's a nice command, but the output of
+lsb_release -a is the name and version number of the linux distro you're
+running, which toybox doesn't know).</p>
+
+<p>This leaves:</p>
+
+<blockquote><b>
+<span id=lsb>
+chfn chsh dmesg egrep fgrep groupadd groupdel groupmod groups
+gunzip gzip hostname install killall md5sum
+mknod mktemp mount passwd pidof sendmail seq shutdown
+su sync tar umount useradd userdel usermod zcat
+</span>
+</b></blockquote>
+
+<hr />
+<a name="dev_env">
+<h2><a href="#dev_env">Use case: provide a self-hosting development environment</a></h2>
+
+<p>The following commands are enough to build the Aboriginal Linux development
+environment, boot it to a shell prompt, and build Linux From Scratch 6.8 under
+it. (Aboriginal Linux currently uses BusyBox for this, thus provides a
+drop-in test environment for toybox. We install both implementations side
+by side, redirecting the symlinks a command at a time until the older
+package is no longer used, and can be removed.)</p>
+
+<p>This use case includes running init scripts and other shell scripts, running
+configure, make, and install in each package, and providing basic command line
+facilities such as a text editor. (It does not include a compiler toolchain or
+C library, those are outside the scope of this project.)</p>
+
+<blockquote><b>
+<span id=development>
+bzcat cat cp dirname echo env patch rmdir sha1sum sleep sort sync
+true uname wc which yes zcat
+awk basename bzip2 chmod chown cmp cut date dd diff
+egrep expr find grep gzip head hostname id install ln ls
+mkdir mktemp mv od readlink rm sed sh tail tar touch tr uniq
+wget whoami xargs chgrp comm gunzip less logname man split
+tee test time bunzip2 chgrp chroot comm cpio dmesg
+dnsdomainname ftpd ftpget ftpput gunzip ifconfig init less
+logname losetup man mdev mount mountpoint nc pgrep pkill 
+pwd route split stat switch_root tac umount vi
+</span>
+</b></blockquote>
+
+<p>Note: Aboriginal Linux installs bash 2.05b as #!/bin/sh and its scripts
+require bash extensions not present in shells such as busybox ash.
+This means that toysh needs to supply several bash extensions _and_ work
+when called under the name "bash".</p>
+
+<hr />
+<h2><a name=android /><a href="#android">Use case: Replacing Android Toolbox</a></h2>
+
+<p>Android has a policy against GPL in userspace, so even though BusyBox
+predates Android by many years, they couldn't use it. Instead they grabbed
+an old version of ash and implemented their own command line utility set
+called "toolbox".</p>
+
+<p>Toolbox doesn't have its own repository, instead it's part of Android's
+<a href=https://android.googlesource.com/platform/system/core>system/core
+git repository</a> (this analysis looked at commit 51ccef27cab58).</p>
+
+<h3>Toolbox commands:</h3>
+
+<p>According to core/toolbox/Android.mk the toolbox directory builds the
+following commands:</p>
+
+<blockquote><b>
+ls mount cat ps kill ln insmod rmmod lsmod ifconfig setconsole
+rm mkdir rmdir reboot getevent sendevent date wipe sync umount
+start stop notify cmp dmesg route hd dd df getprop setprop watchprops
+log sleep renice printenv smd chmod chown newfs_msdos netstat ioctl 
+mv schedtop top iftop id uptime vmstat nandread ionice touch lsof md5 r
+cp du grep watchdogd
+</b></blockquote>
+
+<p>If selinux is enabled, you also get:</p>
+<blockquote><b>
+getenforce setenforce chcon restorecon runcon getsebool setsebool load_policy
+</b></blockquote>
+
+<p>The Android.mk file also refers to dynarray.c and toolbox.c as library
+code. This leaves the following apparently unused C files in toolbox/*.c, each
+of which has a command_main() function and seems to implement a standalone
+command:</p>
+
+<blockquote><b>
+alarm exists lsusb readtty rotatefb setkey syren
+</b></blockquote>
+
+<h3>Command shell (ash)</h3>
+
+<p>The core/sh subdirectory contains a fork of ash 1.17, and sucks in
+liblinenoise to provide command line history/editing.</p>
+
+<h3>Other Android core commands</h3>
+
+<p>Other than the toolbox and sh directories, the currently interesting
+subdirectories in the core repository are fs_mgr, gpttool, init,
+logcat, logwrapper, mkbootimg, netcfg, run-as, and sdcard.</p>
+
+<ul>
+<li><b>fs_mgr</b> - subset of mount</li>
+<li><b>gpttool</b> - subset of fdisk</li>
+<li><b>init</b> - Android's PID 1</li>
+<li><b>logcat</b> - read android log format</li>
+<li><b>logwrapper</b> - redirect stdio to android log</li>
+<li><b>mkbootimg</b> - create signed boot image</li>
+<li><b>netcfg</b> - network configuration (sucks in libnetutils)</li>
+<li><b>run-as</b> - subset of sudo</li>
+<li><b>sdcard</b> - FUSE wrapper to squash UID/GID/permissions to what FAT supports.</li>
+</ul>
+
+<p>Almost all of these reinvent an existing wheel with less functionality and a
+different user interface. We may want to provide that interface, but
+implementing the full commands (mount, fdisk, init, ifconfig with dhcp,
+and sudo) come first.</p>
+
+<p>Although logcat/logwrapper also reinvent a wheel, Android did so in the
+kernel and these provide an interface to that.</p>
+
+<p>Also, gpttool and mkbootimg are install tools, and sdcard looks like a
+testing tool. These aren't a priority if android wants to use its own
+bespoke code to install itself.</p>
+
+<h3>Analysis</h3>
+
+<p>For reference, combining everything listed above, we get:</p>
+
+<blockquote><b>
+alarm ash cat chcon chmod chown cmp cp date dd df dmesg du exists fs_mgr
+getenforce
+getevent getprop getsebool gpttool grep hd id ifconfig iftop init insmod ioctl
+ionice kill ln load_policy log logcat logwrapper ls lsmod lsof lsusb md5
+mkbootimg mkdir mount mv nandread netcfg netstat newfs_msdos notify printenv
+ps r readtty reboot renice restorecon rm rmdir rmmod rotatefb route run-as
+runcon schedtop sdcard sendevent setconsole setenforce setkey setprop setsebool
+sleep smd start stop sync syren top touch umount uptime vmstat watchdogd
+watchprops wipe
+</b></blockquote>
+
+<p>We may eventually implement all of that, but for toybox 1.0 we need to
+focus a bit. For our first pass, let's ignore selinux, strip out the "unlisted"
+commands except lsusb, and grab just logcat and logwrapper from the "core"
+commands (since the rest have some full/standard version providing that
+functionality, which we can implement a shim interface for later).</p>
+
+<p>This means toybox should implement:</p>
+<blockquote><b>
+<span id=toolbox>
+cat chmod chown cmp cp date dd df dmesg du getevent getprop grep hd id ifconfig
+iftop insmod ioctl ionice kill ln log logcat logwrapper ls lsmod lsof lsusb md5
+mkdir mount mv nandread
+netstat newfs_msdos notify printenv ps r reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top touch
+umount uptime vmstat watchprops watchdogd wipe
+</span>
+</b></blockquote>
+
+<p>The following Toolbox commands are already covered in previous
+sections of this analysis:</p>
+
+<blockquote><b>
+cat chmod chown cmp cp date dd df dmesg du grep id ifconfig insmod kill ln ls
+lsmod mkdir mount mv ps renice rm rmdir rmmod route sleep sync top touch umount
+</b></blockquote>
+
+<p>Which leaves the following commands as new from Toolbox:</p>
+
+<blockquote><b>
+getevent getprop hd iftop ioctl ionice log lsof nandread netstat
+newfs_msdos notify printenv r reboot schedtop sendevent setconsole
+setprop smd start stop top uptime vmstat watchprops watchdogd wipe
+</b></blockquote>
+
+<hr /><a name=klibc />
+<h2>klibc:</h2>
+
+<p>Long ago some kernel developers came up with a project called
+<a href=http://en.wikipedia.org/wiki/Klibc>klibc</a>.
+After a decade of development it still has no web page or HOWTO,
+and nobody's quite sure if the license is BSD or GPL. It inexplicably
+<a href=http://www.infoworld.com/d/data-center/perl-isnt-going-anywhere-better-or-worse-211580>requires perl to build</a>, and seems like an ideal candidate for
+replacement.</p>
+
+<p>In addition to a C library even less capable than bionic (obsoleted by
+musl), klibc builds a random assortment of executables to run init scripts
+with. There's no multiplexer command, these are individual executables:</p>
+
+<blockquote><p>
+cat chroot cpio dd dmesg false fixdep fstype gunzip gzip halt ipconfig kill
+kinit ln losetup ls minips mkdir mkfifo mknodes
+mksyntax mount mv nfsmount nuke pivot_root poweroff readlink reboot resume
+run-init sh sha1hash sleep sync true umount uname zcat
+</p></blockquote>
+
+<p>To get that list, build klibc according to the instructions (I
+<a href=http://landley.net/notes-2013.html#23-01-2013>looked at</a> version
+2.0.2 and did cd klibc-*; ln -s /output/of/kernel/make/headers_install
+linux; make) then <b>echo $(for i in $(find . -type f); do file $i | grep -q
+executable && basename $i; done | grep -v '[.]g$' | sort -u)</b> to find
+executables, then eliminated the *.so files and *.shared duplicates.</p>
+
+<p>Some of those binaries are build-time tools that don't get installed,
+which removes mknodes, mksyntax, sha1hash, and fixdep from the list.
+(And sha1hash is just an unpolished sha1sum anyway.)</p>
+
+<p>The run-init command is more commonly called switch_root, nuke is just
+"rm -rf -- $@", and minips is more commonly called "ps". I'm not doing aliases
+for the oddball names.</p>
+
+<p>Yet more stale forks of dash and gzip sucked in here (see "dubious
+license terms" above), adding nothing to the other projects we've looked at.
+But we still need sh, gunzip, gzip, and zcat to replace this package.</p>
+
+<p>By the time I did the analysis toybox already had cat, chroot, dmesg, false,
+kill, ln, losetup, ls, mkdir, mkfifo, readlink, rm, switch_root, sleep, sync,
+true, and uname.</p>
+
+<p>The low hanging fruit is cpio, dd, ps, mv, and pivot_root.</p>
+
+<p>The "kinit" command is another gratuitous rename, it's init running as PID 1.
+The halt, poweroff, and reboot commands work with it.</p>
+
+<p>I've got mount and umount queued up already, fstype and nfsmount go with
+those. (And probably smbmount and p9mount, but this hasn't got one. Those
+are all about querying for login credentials, probably workable into the
+base mount command.)</p>
+
+<p>The ipconfig command here has a built in dhcp client, so it's ifconfig
+and dhcpcd and maybe some other stuff.</p>
+
+<p>The resume command is... weird. It finds a swap partition and reads data
+from it into a /proc file, something the kernel is capable of doing itself.
+(Even though the klibc author
+<a href=http://www.zytor.com/pipermail/klibc/2006-June/001748.html>attempted
+to remove</a> that capability from the kernel, current kernel/power/hibernate.c
+still parses "resume=" on the command line). And yet various distros seem to
+make use of klibc for this>
+Given the history of swsusp/hibernate (and 
+<a href=http://lwn.net/Articles/333007>TuxOnIce</a>
+and <a href=http://lwn.net/Articles/242107>kexec jump</a>) I've lost track
+of the current state of the art here. Ah, Documentation/power/userland-swsusp.txt
+has the API docs, and <a href=http://suspend.sf.net>here's a better
+tool</a>...</p>
+
+<p>So the list of things actually in klibc are:</p>
+
+<blockquote><b>
+<span id=klibc_cmd />
+cat chroot dmesg false kill ln losetup ls mkdir mkfifo readlink rm switch_root
+sleep sync true uname
+
+cpio dd ps mv pivot_root
+mount nfsmount fstype umount
+sh gunzip gzip zcat
+kinit halt poweroff reboot
+ipconfig
+resume
+</span>
+</b></blockquote>
+
+<hr />
+<a name=sash />
+<h2>Stand-Alone Shell</h2>
+
+<p>Wikipedia has <a href=http://en.wikipedia.org/wiki/Stand-alone_shell>a good
+summary of sash</a>, with links. The original Stand-Alone Shell project reached
+a stopping point, and then <a href=http://www.baiti.net/sash>"sash plus
+patches"</a> extended it a bit further. The result is a megabyte executable
+that provides 40 commands.</p>
+
+<p>Sash is a shell with built-in commands. It doesn't have a multiplexer
+command, meaning "sash ls -l" doesn't work (you have to go "sash -c 'ls -l'").
+</p>
+
+<p>The list of commands can be obtained via building it and doing
+"echo help | ./sash | awk '{print $1}' | sed 's/^-//' | xargs echo", which
+gives us:</p>
+
+<blockquote><b>
+alias aliasall ar cd chattr chgrp chmod chown cmp cp chroot dd echo ed exec
+exit file find grep gunzip gzip help kill losetup losetup ln ls lsattr mkdir
+mknod more mount mv pivot_root printenv prompt pwd quit rm rmdir setenv source
+sum sync tar touch umask umount unalias where
+</b></blockquote>
+
+<p>Plus sh because it's a shell. A dozen or so commands can only sanely be
+implemented as shell builtins (alias aliasall cd exec exit prompt quit setenv
+source umask unalias), where is an alias for which, and at triage time toybox
+already has chgrp, chmod, chown, cmp, cp, chroot, echo, help, kill, losetup,
+ln, ls, mkdir, mknod, printenv, pwd, rm, rmdir, sync, and touch.</p>
+
+<p>This leaves:</p>
+
+<blockquote><b>
+<span id=sash_cmd>
+ar chattr dd ed file find grep gunzip gzip lsattr more mount mv pivot_root
+sh sum tar umount
+</span>
+</b></blockquote>
+
+<p>(For once, this project doesn't include a fork of gzip, instead
+it sucks in -lz from the host.)</p>
+
+<hr />
+<a name=sbase />
+<h2>sbase:</h2>
+
+<p>It's <a href=http://git.suckless.org/sbase>on suckless</a>. So far it's
+implemented:</p>
+
+<blockquote><p>
+<span id=sbase_cmd />
+basename cat chmod chown cksum cmp cp date dirname echo false fold grep head
+kill ln ls mc mkdir mkfifo mv nl nohup pwd rm seq sleep sort tail tee test
+touch true tty uname uniq wc yes
+</span>
+</p></blockquote>
+
+<p>And has a TODO list:</p>
+
+<blockquote><p>
+<span id=sbase_cmd />
+cal chgrp chvt comm cut df diff du env expand expr id md5sum nice paste
+printenv printf readlink rmdir seq sha1sum split sync test tr unexpand unlink
+who
+</span>
+</p></blockquote>
+
+<p>At triage time, of the first list I still need to do: fold grep mc mv nl. Of
+the second list: diff expr paste printf split test tr unexpand who.</p>
+
+<hr />
+<a name=s6 />
+<h2>s6</h2>
+
+<p>The website <a href=http://skarnet.org/software/>skarnet</a> has a bunch
+of small utilities as part of something called "s6". This includes the
+<a href=http://skarnet.org/software/s6-portable-utils>s6-portabile-utils</a>
+and the <a href=http://skarnet.org/software/s6-linux-utils>s6-linux-utils</a>.
+</p>
+
+<p>Both packages rely on multiple bespoke external libraries without which
+they can't compile. The source is completely uncommented and doesn't wrap at
+80 characters. Doing a find for *.c files brings up the following commands:</p>
+
+<blockquote><b>
+<span id=s6>
+basename cat chmod chown chroot clock cut devd dirname echo env expr false
+format-filter freeramdisk grep halt head hiercopy hostname linkname ln
+logwatch ls maximumtime memoryhog mkdir mkfifo mount nice nuke pause
+pivotchroot poweroff printenv quote quote-filter reboot rename rmrf sleep
+sort swapoff swapon sync tail test touch true umount uniquename unquote
+unquote-filter update-symlinks
+</span>
+</b></blockquote>
+
+<p>Triage: memoryhog isn't even listed on the website nor does it have
+a documentation file, clock seems like a subset
+of date, devd is some sort of netlink wrapper that spawns its command line
+every time it gets a message (maybe this is meant to implement part of
+udev/mdev?), format-filter is sort of awk's '{print $2}' function split out
+into its own command, hiercopy a subset of "cp -r", maximumtime is something
+I implemented as a shell script (more/timeout.sh in Aboriginal Linux),
+nuke isn't the same as klibc (this one's "kill SIG -1" only with hardwared
+SIG options), pause is a program that literally waits to be killed (I
+generally sleep 999999999 which is a little over 30 years),
+pivotchroot is a subset of switch_root, rmrf is rm -rf...</p>
+
+<p>I see "nuke" resurface, and if "rmrf" wasn't also here I might think
+klibc had a point.</b>
+
+<blockquote>
+basename cat chmod chown chroot cut dirname echo env expr false
+freeramdisk grep halt head hostname linkname ln
+logwatch ls mkdir mkfifo mount nice
+pivotchroot poweroff printenv quote quote-filter reboot rename sleep
+sort swapoff swapon sync tail test touch true umount uniquename unquote
+unquote-filter update-symlinks
+</blockquote>
+
+
+<hr />
+<a name=nash />
+<h2>nash:</h2>
+
+<p>Red Hat's nash was part of its "mkinitrd" package, replacement for a shell
+and utilities on the boot floppy back in the 1990's (the same general idea
+as BusyBox, developed independently). Red Hat discontinued nash development
+in 2010, replacing it with dracut (which collects together existing packages,
+including busybox).</p>
+
+<p>I couldn't figure out how to beat source code out of
+<a href=http://pkgs.fedoraproject.org/git/mkinitrd>Fedora's current git</a>
+repository. The last release version that used it was Fedora Core 12
+which has <a href=http://archive.fedoraproject.org/pub/archive/fedora/linux/releases/12/Fedora/source/SRPMS/mkinitrd-6.0.93-1.fc12.src.rpm>a source rpm</a>
+that can be unwound with "rpm2cpio mkinitrd.src.rpm | cpio -i -d -H newc
+--no-absolute-filenames" and in there is a mkinitrd-6.0.93.tar.bz2 which
+has the source.</p>
+
+<p>In addition to being a bit like a command shell, the nash man page lists the
+following commands:</p>
+
+<blockquote><p>
+access echo find losetup mkdevices mkdir mknod mkdmnod mkrootdev mount
+pivot_root readlink raidautorun setquiet showlabels sleep switchroot umount
+</p></blockquote>
+
+<p>Oddly, the only occurrence of the string pivot_root in the nash source code
+is in the man page, the command isn't there. (It seems to have been removed
+when the underscoreless switchroot went in.)</p>
+
+<p>A more complete list seems to be the handlers[] array in nash.c:</p>
+
+<blockquote><p>
+access buildEnv cat cond cp daemonize dm echo exec exit find kernelopt
+loadDrivers loadpolicy mkchardevs mkblktab mkblkdevs mkdir mkdmnod mknod
+mkrootdev mount netname network null plymouth hotplug killplug losetup
+ln ls raidautorun readlink resume resolveDevice rmparts setDeviceEnv
+setquiet setuproot showelfinterp showlabels sleep stabilized status switchroot
+umount waitdev
+</p></blockquote>
+
+<p>This list is nuts: "plymouth" is an alias for "null" which is basically
+"true" (which thie above list doesn't have). Things like buildEnv and
+loadDrivers are bespoke Red Hat behavior that might as well be hardwired in
+to nash's main() without being called.</p>
+
+<p>Instead of eliminating items
+from the list with an explanation for each, I'm just going to cherry pick
+a few: the device mapper (dm, raidautorun) is probably interesting,
+hotplug (may be obsolete due to kernel changes that now load firmware
+directly), and another "resume" ala klibc.</p>
+
+<p>But mostly: I don't care about this one. And neither does Red Hat anymore.</p>
+
+<p>Verdict: ignore</p>
+
+<hr />
+<a name=beastiebox />
+<h2>Beastiebox</h2>
+
+<p>Back in 2008, the BSD guys vented some busybox-envy
+<a href=http://beastiebox.sourceforge.net>on sourceforge</a>. Then stopped.
+Their repository is still in CVS, hasn't been touched in years, it's a giant
+hairball of existing code sucked together. (The web page says the author
+is aware of crunchgen, but decided to do this by hand anyway. This is not
+a collection of new code, it's a katamari of existing code rolled up in a
+ball.)</p>
+
+<p>Combining the set of commands listed on the web page with the set of
+man pages in the source gives us:</P>
+
+<blockquote><p>
+[ cat chmod cp csh date df disklabel dmesg echo ex fdisk fsck fsck_ffs getty
+halt hostname ifconfig init kill less lesskey ln login ls lv mksh more mount
+mount_ffs mv pfctl ping poweroff ps reboot rm route sed sh stty sysctl tar test
+traceroute umount vi wiconfig
+</p></blockquote>
+
+<p>Apparently lv is the missing link ed and vi, copyright 1982-1997 (do not
+want), ex is another obsolete vi mode, lesskey is "used to
+specify a set of key bindings to be used with less", and csh is a shell they
+sucked in, [ is an alias for test. Several more bsd-isms that don't have Linux
+equivalents (even in the ubuntu "install this package" search) are
+disklabel, fsck_ffs, mount_ffs, and pfctl. And wiconfig is a wavelan interface
+network card driver utility. Subtracting all that and the commands toybox
+already implements at triage time, we get:</p>
+
+<blockquote><p>
+<span id=beastiebox_cmd>
+fdisk fsck getty halt ifconfig init kill less mksh more mount mv ping poweroff
+ps reboot route sed sh stty sysctl tar test traceroute umount vi
+</span>
+</p></blockquote>
+
+<p>Not a hugely interesting list, but eh.</p>
+
+<p>Verdict: ignore</p>
+
+<hr />
+<a name=BsdBox />
+<h2>BsdBox</h2>
+
+<p>Somebody decided to do a <a href=https://wiki.freebsd.org/AdrianChadd/BsdBox>multicall binary for freebsd</a>.</p>
+
+<p>They based it on crunchgen, a tool that glues existing programs together
+into an archive and uses the name to execute the right one. It has no
+simplification or code sharing benefits whatsoever, it's basically an
+archiver that produces executables.</p>
+
+<p>That's about where I stopped reading.</p>
+
+<p>Verdict: ignore.</p>
+
+<hr />
+<a name=slowaris />
+<h2>OpenSolaris Busybox</h2>
+
+<p>Somebody <a href=http://hub.opensolaris.org/bin/view/Project+busybox/>wrote
+a wiki page</a> saying that Busybox for OpenSolaris would be a good idea.</p>
+
+<p>The corresponding "files" tab is an auto-generated stub. The project never
+even got as far as suggesting commands to include before Oracle discontinued
+OpenSolaris.</p>
+
+<p>Verdict: ignore.</p>
+
+<hr />
+<h2>Requests:</h2>
+
+<p>The following additional commands have been requested by various users:</p>
+<blockquote><b>
+<span id=request>
+freeramdisk getty halt hexdump hwclock klogd modprobe ping ping6 pivot_root
+poweroff readahead rev sfdisk sudo syslogd taskset telnet telnetd tracepath
+traceroute unzip usleep vconfig zip free login modinfo unshare netcat help w
+ntpd
+</span>
+</b></blockquote>
+
+<!-- #include "footer.html" -->
+
diff --git a/www/status.html b/www/status.html
new file mode 100644 (file)
index 0000000..b3fcec1
--- /dev/null
@@ -0,0 +1,42 @@
+<!--#include file="header.html" -->
+<title>Toybox Status</title>
+
+<h1>How are we doing on implementing stuff so far?</h1>
+
+<p>Legend: [posix] &lt;lsb&gt; (development) {android} =klibc= #sash# @sbase@
+*beastiebox* +request+ other <strike>implemented</strike></p>
+
+<!--#include file="status.gen" -->
+
+<h1>The current status of toybox (as of 0.4.1 release):</h1>
+
+<h3><u>These commands are reasonably finished</u>:</h3>
+<blockquote><b>
+<span id=ready>
+basename cal cat catv chgrp chmod chown chvt cksum clear cmp comm count cp
+df dirname dmesg dos2unix echo env false gethostname killall link logname
+losetup ls lsmod md5sum mkdir
+mkfifo mkswap mktemp nice nohup od oneit pwd realpath rev rm seq setsid
+sha1sum sleep sort split swapoff swapon sync tac taskset tee true truncate tty
+uniq unix2dos unlink usleep wc which whoami yes
+</span>
+</b></blockquote>
+
+<h3><u>These commands are implemented but have pending todo items remaining:</u></h3>
+<blockquote><b>
+<span id=pending>
+bzcat bunzip2 chroot cut date du expand free head help hostname id ifconfig
+insmod kill ln login lsusb mdev
+mknod modinfo mountpoint mv nc netcat passwd patch pidof printenv readlink rmdir
+rmmod switch_root tail touch uname unshare uptime vconfig vmstat w who xargs
+</span>
+</b></blockquote>
+
+<h3><u>Work on these is underway, but not usable yet:</u></h3>
+<blockquote><b>
+bzip2 mke2fs more mount umount sed tar sh grep/egrep/fgrep
+</blockquote></b>
+
+<p>See <a href="todo.txt">the todo list</a> for details.</p>
+
+<!-- #include "footer.html" -->
diff --git a/www/toycans.png b/www/toycans.png
new file mode 100644 (file)
index 0000000..1a133da
Binary files /dev/null and b/www/toycans.png differ