This commit was manufactured by cvs2svn to create tag 'busybox_1_00'.
authornobody <nobody@localhost>
Wed, 13 Oct 2004 09:42:10 +0000 (09:42 -0000)
committernobody <nobody@localhost>
Wed, 13 Oct 2004 09:42:10 +0000 (09:42 -0000)
785 files changed:
busybox/.cvsignore [new file with mode: 0644]
busybox/.indent.pro [new file with mode: 0644]
busybox/AUTHORS [new file with mode: 0644]
busybox/Changelog [new file with mode: 0644]
busybox/INSTALL [new file with mode: 0644]
busybox/LICENSE [new file with mode: 0644]
busybox/Makefile [new file with mode: 0644]
busybox/README [new file with mode: 0644]
busybox/Rules.mak [new file with mode: 0644]
busybox/TODO [new file with mode: 0644]
busybox/applets/Makefile [new file with mode: 0644]
busybox/applets/Makefile.in [new file with mode: 0644]
busybox/applets/applets.c [new file with mode: 0644]
busybox/applets/busybox.c [new file with mode: 0644]
busybox/applets/busybox.mkll [new file with mode: 0755]
busybox/applets/install.sh [new file with mode: 0755]
busybox/archival/Config.in [new file with mode: 0644]
busybox/archival/Makefile [new file with mode: 0644]
busybox/archival/Makefile.in [new file with mode: 0644]
busybox/archival/ar.c [new file with mode: 0644]
busybox/archival/bunzip2.c [new file with mode: 0644]
busybox/archival/cpio.c [new file with mode: 0644]
busybox/archival/dpkg.c [new file with mode: 0644]
busybox/archival/dpkg_deb.c [new file with mode: 0644]
busybox/archival/gunzip.c [new file with mode: 0644]
busybox/archival/gzip.c [new file with mode: 0644]
busybox/archival/libunarchive/Makefile [new file with mode: 0644]
busybox/archival/libunarchive/Makefile.in [new file with mode: 0644]
busybox/archival/libunarchive/archive_xread_all.c [new file with mode: 0644]
busybox/archival/libunarchive/archive_xread_all_eof.c [new file with mode: 0644]
busybox/archival/libunarchive/check_header_gzip.c [new file with mode: 0644]
busybox/archival/libunarchive/data_align.c [new file with mode: 0644]
busybox/archival/libunarchive/data_extract_all.c [new file with mode: 0644]
busybox/archival/libunarchive/data_extract_to_buffer.c [new file with mode: 0644]
busybox/archival/libunarchive/data_extract_to_stdout.c [new file with mode: 0644]
busybox/archival/libunarchive/data_skip.c [new file with mode: 0644]
busybox/archival/libunarchive/decompress_bunzip2.c [new file with mode: 0644]
busybox/archival/libunarchive/decompress_uncompress.c [new file with mode: 0644]
busybox/archival/libunarchive/decompress_unzip.c [new file with mode: 0644]
busybox/archival/libunarchive/filter_accept_all.c [new file with mode: 0644]
busybox/archival/libunarchive/filter_accept_list.c [new file with mode: 0644]
busybox/archival/libunarchive/filter_accept_list_reassign.c [new file with mode: 0644]
busybox/archival/libunarchive/filter_accept_reject_list.c [new file with mode: 0644]
busybox/archival/libunarchive/find_list_entry.c [new file with mode: 0644]
busybox/archival/libunarchive/get_header_ar.c [new file with mode: 0644]
busybox/archival/libunarchive/get_header_cpio.c [new file with mode: 0644]
busybox/archival/libunarchive/get_header_tar.c [new file with mode: 0644]
busybox/archival/libunarchive/get_header_tar_bz2.c [new file with mode: 0644]
busybox/archival/libunarchive/get_header_tar_gz.c [new file with mode: 0644]
busybox/archival/libunarchive/header_list.c [new file with mode: 0644]
busybox/archival/libunarchive/header_skip.c [new file with mode: 0644]
busybox/archival/libunarchive/header_verbose_list.c [new file with mode: 0644]
busybox/archival/libunarchive/init_handle.c [new file with mode: 0644]
busybox/archival/libunarchive/open_transformer.c [new file with mode: 0644]
busybox/archival/libunarchive/seek_by_char.c [new file with mode: 0644]
busybox/archival/libunarchive/seek_by_jump.c [new file with mode: 0644]
busybox/archival/libunarchive/unpack_ar_archive.c [new file with mode: 0644]
busybox/archival/rpm.c [new file with mode: 0644]
busybox/archival/rpm2cpio.c [new file with mode: 0644]
busybox/archival/tar.c [new file with mode: 0644]
busybox/archival/uncompress.c [new file with mode: 0644]
busybox/archival/unzip.c [new file with mode: 0644]
busybox/console-tools/Config.in [new file with mode: 0644]
busybox/console-tools/Makefile [new file with mode: 0644]
busybox/console-tools/Makefile.in [new file with mode: 0644]
busybox/console-tools/chvt.c [new file with mode: 0644]
busybox/console-tools/clear.c [new file with mode: 0644]
busybox/console-tools/deallocvt.c [new file with mode: 0644]
busybox/console-tools/dumpkmap.c [new file with mode: 0644]
busybox/console-tools/loadfont.c [new file with mode: 0644]
busybox/console-tools/loadkmap.c [new file with mode: 0644]
busybox/console-tools/openvt.c [new file with mode: 0644]
busybox/console-tools/reset.c [new file with mode: 0644]
busybox/console-tools/setkeycodes.c [new file with mode: 0644]
busybox/coreutils/Config.in [new file with mode: 0644]
busybox/coreutils/Makefile [new file with mode: 0644]
busybox/coreutils/Makefile.in [new file with mode: 0644]
busybox/coreutils/basename.c [new file with mode: 0644]
busybox/coreutils/cal.c [new file with mode: 0644]
busybox/coreutils/cat.c [new file with mode: 0644]
busybox/coreutils/chgrp.c [new file with mode: 0644]
busybox/coreutils/chmod.c [new file with mode: 0644]
busybox/coreutils/chown.c [new file with mode: 0644]
busybox/coreutils/chroot.c [new file with mode: 0644]
busybox/coreutils/cmp.c [new file with mode: 0644]
busybox/coreutils/cp.c [new file with mode: 0644]
busybox/coreutils/cut.c [new file with mode: 0644]
busybox/coreutils/date.c [new file with mode: 0644]
busybox/coreutils/dd.c [new file with mode: 0644]
busybox/coreutils/df.c [new file with mode: 0644]
busybox/coreutils/dirname.c [new file with mode: 0644]
busybox/coreutils/dos2unix.c [new file with mode: 0644]
busybox/coreutils/du.c [new file with mode: 0644]
busybox/coreutils/echo.c [new file with mode: 0644]
busybox/coreutils/env.c [new file with mode: 0644]
busybox/coreutils/expr.c [new file with mode: 0644]
busybox/coreutils/false.c [new file with mode: 0644]
busybox/coreutils/fold.c [new file with mode: 0644]
busybox/coreutils/head.c [new file with mode: 0644]
busybox/coreutils/hostid.c [new file with mode: 0644]
busybox/coreutils/id.c [new file with mode: 0644]
busybox/coreutils/install.c [new file with mode: 0644]
busybox/coreutils/length.c [new file with mode: 0644]
busybox/coreutils/libcoreutils/Makefile [new file with mode: 0644]
busybox/coreutils/libcoreutils/Makefile.in [new file with mode: 0644]
busybox/coreutils/libcoreutils/coreutils.h [new file with mode: 0644]
busybox/coreutils/libcoreutils/cp_mv_stat.c [new file with mode: 0644]
busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c [new file with mode: 0644]
busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c [new file with mode: 0644]
busybox/coreutils/ln.c [new file with mode: 0644]
busybox/coreutils/logname.c [new file with mode: 0644]
busybox/coreutils/ls.c [new file with mode: 0644]
busybox/coreutils/md5_sha1_sum.c [new file with mode: 0644]
busybox/coreutils/mkdir.c [new file with mode: 0644]
busybox/coreutils/mkfifo.c [new file with mode: 0644]
busybox/coreutils/mknod.c [new file with mode: 0644]
busybox/coreutils/mv.c [new file with mode: 0644]
busybox/coreutils/od.c [new file with mode: 0644]
busybox/coreutils/printf.c [new file with mode: 0644]
busybox/coreutils/pwd.c [new file with mode: 0644]
busybox/coreutils/realpath.c [new file with mode: 0644]
busybox/coreutils/rm.c [new file with mode: 0644]
busybox/coreutils/rmdir.c [new file with mode: 0644]
busybox/coreutils/seq.c [new file with mode: 0644]
busybox/coreutils/sleep.c [new file with mode: 0644]
busybox/coreutils/sort.c [new file with mode: 0644]
busybox/coreutils/stty.c [new file with mode: 0644]
busybox/coreutils/sync.c [new file with mode: 0644]
busybox/coreutils/tail.c [new file with mode: 0644]
busybox/coreutils/tee.c [new file with mode: 0644]
busybox/coreutils/test.c [new file with mode: 0644]
busybox/coreutils/touch.c [new file with mode: 0644]
busybox/coreutils/tr.c [new file with mode: 0644]
busybox/coreutils/true.c [new file with mode: 0644]
busybox/coreutils/tty.c [new file with mode: 0644]
busybox/coreutils/uname.c [new file with mode: 0644]
busybox/coreutils/uniq.c [new file with mode: 0644]
busybox/coreutils/usleep.c [new file with mode: 0644]
busybox/coreutils/uudecode.c [new file with mode: 0644]
busybox/coreutils/uuencode.c [new file with mode: 0644]
busybox/coreutils/watch.c [new file with mode: 0644]
busybox/coreutils/wc.c [new file with mode: 0644]
busybox/coreutils/who.c [new file with mode: 0644]
busybox/coreutils/whoami.c [new file with mode: 0644]
busybox/coreutils/yes.c [new file with mode: 0644]
busybox/debian/busybox-cvs-doc.docs [new file with mode: 0644]
busybox/debian/busybox-cvs-static.dirs [new file with mode: 0644]
busybox/debian/busybox-cvs-static.manpages [new file with mode: 0644]
busybox/debian/busybox-cvs-static.override [new file with mode: 0644]
busybox/debian/busybox-cvs.dirs [new file with mode: 0644]
busybox/debian/busybox-cvs.manpages [new file with mode: 0644]
busybox/debian/changelog [new file with mode: 0644]
busybox/debian/compat [new file with mode: 0644]
busybox/debian/config-deb [new file with mode: 0644]
busybox/debian/config-floppy-udeb-linux [new file with mode: 0644]
busybox/debian/config-static [new file with mode: 0644]
busybox/debian/config-udeb [new file with mode: 0644]
busybox/debian/config-udeb-linux [new file with mode: 0644]
busybox/debian/control [new file with mode: 0644]
busybox/debian/control-extract [new file with mode: 0644]
busybox/debian/copyright [new file with mode: 0644]
busybox/debian/rules [new file with mode: 0755]
busybox/debianutils/Config.in [new file with mode: 0644]
busybox/debianutils/Makefile [new file with mode: 0644]
busybox/debianutils/Makefile.in [new file with mode: 0644]
busybox/debianutils/mktemp.c [new file with mode: 0644]
busybox/debianutils/pipe_progress.c [new file with mode: 0644]
busybox/debianutils/readlink.c [new file with mode: 0644]
busybox/debianutils/run_parts.c [new file with mode: 0644]
busybox/debianutils/start_stop_daemon.c [new file with mode: 0644]
busybox/debianutils/which.c [new file with mode: 0644]
busybox/docs/.cvsignore [new file with mode: 0644]
busybox/docs/autodocifier.pl [new file with mode: 0755]
busybox/docs/busybox.net/.cvsignore [new file with mode: 0644]
busybox/docs/busybox.net/FAQ.html [new file with mode: 0644]
busybox/docs/busybox.net/about.html [new file with mode: 0644]
busybox/docs/busybox.net/busybox-growth.ps [new file with mode: 0644]
busybox/docs/busybox.net/copyright.txt [new file with mode: 0644]
busybox/docs/busybox.net/cvs_anon.html [new file with mode: 0644]
busybox/docs/busybox.net/cvs_howto.html [new file with mode: 0644]
busybox/docs/busybox.net/cvs_write.html [new file with mode: 0644]
busybox/docs/busybox.net/docs.html [new file with mode: 0644]
busybox/docs/busybox.net/download.html [new file with mode: 0644]
busybox/docs/busybox.net/footer.html [new file with mode: 0644]
busybox/docs/busybox.net/header.html [new file with mode: 0644]
busybox/docs/busybox.net/images/back.png [new file with mode: 0644]
busybox/docs/busybox.net/images/busybox.jpeg [new file with mode: 0644]
busybox/docs/busybox.net/images/busybox.png [new file with mode: 0644]
busybox/docs/busybox.net/images/busybox1.png [new file with mode: 0644]
busybox/docs/busybox.net/images/busybox2.jpg [new file with mode: 0644]
busybox/docs/busybox.net/images/busybox3.jpg [new file with mode: 0644]
busybox/docs/busybox.net/images/dir.png [new file with mode: 0644]
busybox/docs/busybox.net/images/donate.png [new file with mode: 0644]
busybox/docs/busybox.net/images/fm.mini.png [new file with mode: 0644]
busybox/docs/busybox.net/images/gfx_by_gimp.png [new file with mode: 0644]
busybox/docs/busybox.net/images/ltbutton2.png [new file with mode: 0644]
busybox/docs/busybox.net/images/sdsmall.png [new file with mode: 0644]
busybox/docs/busybox.net/images/text.png [new file with mode: 0644]
busybox/docs/busybox.net/images/vh40.gif [new file with mode: 0644]
busybox/docs/busybox.net/images/written.in.vi.png [new file with mode: 0644]
busybox/docs/busybox.net/index.html [new file with mode: 0644]
busybox/docs/busybox.net/license.html [new file with mode: 0644]
busybox/docs/busybox.net/lists.html [new file with mode: 0644]
busybox/docs/busybox.net/news.html [new file with mode: 0644]
busybox/docs/busybox.net/oldnews.html [new file with mode: 0644]
busybox/docs/busybox.net/products.html [new file with mode: 0644]
busybox/docs/busybox.net/screenshot.html [new file with mode: 0644]
busybox/docs/busybox.net/shame.html [new file with mode: 0644]
busybox/docs/busybox_footer.pod [new file with mode: 0644]
busybox/docs/busybox_header.pod [new file with mode: 0644]
busybox/docs/contributing.txt [new file with mode: 0644]
busybox/docs/new-applet-HOWTO.txt [new file with mode: 0644]
busybox/docs/style-guide.txt [new file with mode: 0644]
busybox/editors/Config.in [new file with mode: 0644]
busybox/editors/Makefile [new file with mode: 0644]
busybox/editors/Makefile.in [new file with mode: 0644]
busybox/editors/awk.c [new file with mode: 0644]
busybox/editors/patch.c [new file with mode: 0644]
busybox/editors/sed.c [new file with mode: 0644]
busybox/editors/vi.c [new file with mode: 0644]
busybox/examples/bootfloppy/bootfloppy.txt [new file with mode: 0644]
busybox/examples/bootfloppy/display.txt [new file with mode: 0644]
busybox/examples/bootfloppy/etc/fstab [new file with mode: 0644]
busybox/examples/bootfloppy/etc/init.d/rcS [new file with mode: 0755]
busybox/examples/bootfloppy/etc/inittab [new file with mode: 0644]
busybox/examples/bootfloppy/etc/profile [new file with mode: 0644]
busybox/examples/bootfloppy/mkdevs.sh [new file with mode: 0755]
busybox/examples/bootfloppy/mkrootfs.sh [new file with mode: 0755]
busybox/examples/bootfloppy/mksyslinux.sh [new file with mode: 0755]
busybox/examples/bootfloppy/quickstart.txt [new file with mode: 0644]
busybox/examples/bootfloppy/syslinux.cfg [new file with mode: 0644]
busybox/examples/busybox.spec [new file with mode: 0644]
busybox/examples/depmod.pl [new file with mode: 0755]
busybox/examples/devfsd.conf [new file with mode: 0644]
busybox/examples/inetd.conf [new file with mode: 0644]
busybox/examples/inittab [new file with mode: 0644]
busybox/examples/mk2knr.pl [new file with mode: 0755]
busybox/examples/udhcp/sample.bound [new file with mode: 0755]
busybox/examples/udhcp/sample.deconfig [new file with mode: 0755]
busybox/examples/udhcp/sample.nak [new file with mode: 0755]
busybox/examples/udhcp/sample.renew [new file with mode: 0755]
busybox/examples/udhcp/sample.script [new file with mode: 0644]
busybox/examples/udhcp/simple.script [new file with mode: 0644]
busybox/examples/udhcp/udhcpd.conf [new file with mode: 0644]
busybox/examples/undeb [new file with mode: 0644]
busybox/examples/unrpm [new file with mode: 0644]
busybox/findutils/Config.in [new file with mode: 0644]
busybox/findutils/Makefile [new file with mode: 0644]
busybox/findutils/Makefile.in [new file with mode: 0644]
busybox/findutils/find.c [new file with mode: 0644]
busybox/findutils/grep.c [new file with mode: 0644]
busybox/findutils/xargs.c [new file with mode: 0644]
busybox/include/.cvsignore [new file with mode: 0644]
busybox/include/applets.h [new file with mode: 0644]
busybox/include/busybox.h [new file with mode: 0644]
busybox/include/dump.h [new file with mode: 0644]
busybox/include/grp_.h [new file with mode: 0644]
busybox/include/inet_common.h [new file with mode: 0644]
busybox/include/libbb.h [new file with mode: 0644]
busybox/include/pwd_.h [new file with mode: 0644]
busybox/include/shadow_.h [new file with mode: 0644]
busybox/include/unarchive.h [new file with mode: 0644]
busybox/include/usage.h [new file with mode: 0644]
busybox/init/Config.in [new file with mode: 0644]
busybox/init/Makefile [new file with mode: 0644]
busybox/init/Makefile.in [new file with mode: 0644]
busybox/init/halt.c [new file with mode: 0644]
busybox/init/init.c [new file with mode: 0644]
busybox/init/init_shared.c [new file with mode: 0644]
busybox/init/init_shared.h [new file with mode: 0644]
busybox/init/mesg.c [new file with mode: 0644]
busybox/init/poweroff.c [new file with mode: 0644]
busybox/init/reboot.c [new file with mode: 0644]
busybox/libbb/.cvsignore [new file with mode: 0644]
busybox/libbb/Makefile [new file with mode: 0644]
busybox/libbb/Makefile.in [new file with mode: 0644]
busybox/libbb/README [new file with mode: 0644]
busybox/libbb/ask_confirmation.c [new file with mode: 0644]
busybox/libbb/bb_askpass.c [new file with mode: 0644]
busybox/libbb/bb_asprintf.c [new file with mode: 0644]
busybox/libbb/change_identity.c [new file with mode: 0644]
busybox/libbb/chomp.c [new file with mode: 0644]
busybox/libbb/compare_string_array.c [new file with mode: 0644]
busybox/libbb/concat_path_file.c [new file with mode: 0644]
busybox/libbb/concat_subpath_file.c [new file with mode: 0644]
busybox/libbb/copy_file.c [new file with mode: 0644]
busybox/libbb/copyfd.c [new file with mode: 0644]
busybox/libbb/correct_password.c [new file with mode: 0644]
busybox/libbb/create_icmp6_socket.c [new file with mode: 0644]
busybox/libbb/create_icmp_socket.c [new file with mode: 0644]
busybox/libbb/default_error_retval.c [new file with mode: 0644]
busybox/libbb/device_open.c [new file with mode: 0644]
busybox/libbb/dump.c [new file with mode: 0644]
busybox/libbb/error_msg.c [new file with mode: 0644]
busybox/libbb/error_msg_and_die.c [new file with mode: 0644]
busybox/libbb/fclose_nonstdin.c [new file with mode: 0644]
busybox/libbb/fflush_stdout_and_exit.c [new file with mode: 0644]
busybox/libbb/fgets_str.c [new file with mode: 0644]
busybox/libbb/find_mount_point.c [new file with mode: 0644]
busybox/libbb/find_pid_by_name.c [new file with mode: 0644]
busybox/libbb/find_root_device.c [new file with mode: 0644]
busybox/libbb/full_read.c [new file with mode: 0644]
busybox/libbb/full_write.c [new file with mode: 0644]
busybox/libbb/get_console.c [new file with mode: 0644]
busybox/libbb/get_last_path_component.c [new file with mode: 0644]
busybox/libbb/get_line_from_file.c [new file with mode: 0644]
busybox/libbb/get_terminal_width_height.c [new file with mode: 0644]
busybox/libbb/get_ug_id.c [new file with mode: 0644]
busybox/libbb/getopt_ulflags.c [new file with mode: 0644]
busybox/libbb/hash_fd.c [new file with mode: 0644]
busybox/libbb/herror_msg.c [new file with mode: 0644]
busybox/libbb/herror_msg_and_die.c [new file with mode: 0644]
busybox/libbb/human_readable.c [new file with mode: 0644]
busybox/libbb/inet_common.c [new file with mode: 0644]
busybox/libbb/inode_hash.c [new file with mode: 0644]
busybox/libbb/interface.c [new file with mode: 0644]
busybox/libbb/isdirectory.c [new file with mode: 0644]
busybox/libbb/kernel_version.c [new file with mode: 0644]
busybox/libbb/last_char_is.c [new file with mode: 0644]
busybox/libbb/llist_add_to.c [new file with mode: 0644]
busybox/libbb/login.c [new file with mode: 0644]
busybox/libbb/loop.c [new file with mode: 0644]
busybox/libbb/make_directory.c [new file with mode: 0644]
busybox/libbb/messages.c [new file with mode: 0644]
busybox/libbb/mode_string.c [new file with mode: 0644]
busybox/libbb/module_syscalls.c [new file with mode: 0644]
busybox/libbb/mtab.c [new file with mode: 0644]
busybox/libbb/mtab_file.c [new file with mode: 0644]
busybox/libbb/my_getgrgid.c [new file with mode: 0644]
busybox/libbb/my_getgrnam.c [new file with mode: 0644]
busybox/libbb/my_getpwnam.c [new file with mode: 0644]
busybox/libbb/my_getpwuid.c [new file with mode: 0644]
busybox/libbb/my_getug.c [new file with mode: 0644]
busybox/libbb/obscure.c [new file with mode: 0644]
busybox/libbb/parse_mode.c [new file with mode: 0644]
busybox/libbb/parse_number.c [new file with mode: 0644]
busybox/libbb/perror_msg.c [new file with mode: 0644]
busybox/libbb/perror_msg_and_die.c [new file with mode: 0644]
busybox/libbb/perror_nomsg.c [new file with mode: 0644]
busybox/libbb/perror_nomsg_and_die.c [new file with mode: 0644]
busybox/libbb/print_file.c [new file with mode: 0644]
busybox/libbb/printf.c [new file with mode: 0644]
busybox/libbb/process_escape_sequence.c [new file with mode: 0644]
busybox/libbb/procps.c [new file with mode: 0644]
busybox/libbb/pw_encrypt.c [new file with mode: 0644]
busybox/libbb/pwd2spwd.c [new file with mode: 0644]
busybox/libbb/qmodule.c [new file with mode: 0644]
busybox/libbb/read_package_field.c [new file with mode: 0644]
busybox/libbb/recursive_action.c [new file with mode: 0644]
busybox/libbb/remove_file.c [new file with mode: 0644]
busybox/libbb/restricted_shell.c [new file with mode: 0644]
busybox/libbb/run_parts.c [new file with mode: 0644]
busybox/libbb/run_shell.c [new file with mode: 0644]
busybox/libbb/safe_read.c [new file with mode: 0644]
busybox/libbb/safe_strncpy.c [new file with mode: 0644]
busybox/libbb/safe_strtol.c [new file with mode: 0644]
busybox/libbb/safe_write.c [new file with mode: 0644]
busybox/libbb/setup_environment.c [new file with mode: 0644]
busybox/libbb/simplify_path.c [new file with mode: 0644]
busybox/libbb/skip_whitespace.c [new file with mode: 0644]
busybox/libbb/speed_table.c [new file with mode: 0644]
busybox/libbb/syscalls.c [new file with mode: 0644]
busybox/libbb/syslog_msg_with_name.c [new file with mode: 0644]
busybox/libbb/trim.c [new file with mode: 0644]
busybox/libbb/u_signal_names.c [new file with mode: 0644]
busybox/libbb/vdprintf.c [new file with mode: 0644]
busybox/libbb/verror_msg.c [new file with mode: 0644]
busybox/libbb/vfork_daemon_rexec.c [new file with mode: 0644]
busybox/libbb/vherror_msg.c [new file with mode: 0644]
busybox/libbb/vperror_msg.c [new file with mode: 0644]
busybox/libbb/warn_ignoring_args.c [new file with mode: 0644]
busybox/libbb/wfopen.c [new file with mode: 0644]
busybox/libbb/wfopen_input.c [new file with mode: 0644]
busybox/libbb/xconnect.c [new file with mode: 0644]
busybox/libbb/xfuncs.c [new file with mode: 0644]
busybox/libbb/xgetcwd.c [new file with mode: 0644]
busybox/libbb/xgethostbyname.c [new file with mode: 0644]
busybox/libbb/xgethostbyname2.c [new file with mode: 0644]
busybox/libbb/xgetlarg.c [new file with mode: 0644]
busybox/libbb/xgetularg.c [new file with mode: 0644]
busybox/libbb/xreadlink.c [new file with mode: 0644]
busybox/libbb/xregcomp.c [new file with mode: 0644]
busybox/libpwdgrp/Makefile [new file with mode: 0644]
busybox/libpwdgrp/Makefile.in [new file with mode: 0644]
busybox/libpwdgrp/pwd_grp.c [new file with mode: 0644]
busybox/loginutils/Config.in [new file with mode: 0644]
busybox/loginutils/Makefile [new file with mode: 0644]
busybox/loginutils/Makefile.in [new file with mode: 0644]
busybox/loginutils/addgroup.c [new file with mode: 0644]
busybox/loginutils/adduser.c [new file with mode: 0644]
busybox/loginutils/delgroup.c [new file with mode: 0644]
busybox/loginutils/delline.c [new file with mode: 0644]
busybox/loginutils/deluser.c [new file with mode: 0644]
busybox/loginutils/getty.c [new file with mode: 0644]
busybox/loginutils/login.c [new file with mode: 0644]
busybox/loginutils/passwd.c [new file with mode: 0644]
busybox/loginutils/su.c [new file with mode: 0644]
busybox/loginutils/sulogin.c [new file with mode: 0644]
busybox/loginutils/vlock.c [new file with mode: 0644]
busybox/miscutils/Config.in [new file with mode: 0644]
busybox/miscutils/Makefile [new file with mode: 0644]
busybox/miscutils/Makefile.in [new file with mode: 0644]
busybox/miscutils/adjtimex.c [new file with mode: 0644]
busybox/miscutils/crond.c [new file with mode: 0644]
busybox/miscutils/crontab.c [new file with mode: 0644]
busybox/miscutils/dc.c [new file with mode: 0644]
busybox/miscutils/devfsd.c [new file with mode: 0644]
busybox/miscutils/hdparm.c [new file with mode: 0644]
busybox/miscutils/last.c [new file with mode: 0644]
busybox/miscutils/makedevs.c [new file with mode: 0644]
busybox/miscutils/mt.c [new file with mode: 0644]
busybox/miscutils/rx.c [new file with mode: 0644]
busybox/miscutils/strings.c [new file with mode: 0644]
busybox/miscutils/time.c [new file with mode: 0644]
busybox/miscutils/watchdog.c [new file with mode: 0644]
busybox/modutils/Config.in [new file with mode: 0644]
busybox/modutils/Makefile [new file with mode: 0644]
busybox/modutils/Makefile.in [new file with mode: 0644]
busybox/modutils/insmod.c [new file with mode: 0644]
busybox/modutils/lsmod.c [new file with mode: 0644]
busybox/modutils/modprobe.c [new file with mode: 0644]
busybox/modutils/rmmod.c [new file with mode: 0644]
busybox/networking/Config.in [new file with mode: 0644]
busybox/networking/Makefile [new file with mode: 0644]
busybox/networking/Makefile.in [new file with mode: 0644]
busybox/networking/arping.c [new file with mode: 0644]
busybox/networking/ftpgetput.c [new file with mode: 0644]
busybox/networking/hostname.c [new file with mode: 0644]
busybox/networking/httpd.c [new file with mode: 0644]
busybox/networking/ifconfig.c [new file with mode: 0644]
busybox/networking/ifupdown.c [new file with mode: 0644]
busybox/networking/inetd.c [new file with mode: 0644]
busybox/networking/ip.c [new file with mode: 0644]
busybox/networking/ipaddr.c [new file with mode: 0644]
busybox/networking/ipcalc.c [new file with mode: 0644]
busybox/networking/iplink.c [new file with mode: 0644]
busybox/networking/iproute.c [new file with mode: 0644]
busybox/networking/iptunnel.c [new file with mode: 0644]
busybox/networking/libiproute/Makefile [new file with mode: 0644]
busybox/networking/libiproute/Makefile.in [new file with mode: 0644]
busybox/networking/libiproute/ip_common.h [new file with mode: 0644]
busybox/networking/libiproute/ip_parse_common_args.c [new file with mode: 0644]
busybox/networking/libiproute/ipaddress.c [new file with mode: 0644]
busybox/networking/libiproute/iplink.c [new file with mode: 0644]
busybox/networking/libiproute/iproute.c [new file with mode: 0644]
busybox/networking/libiproute/iptunnel.c [new file with mode: 0644]
busybox/networking/libiproute/libnetlink.c [new file with mode: 0644]
busybox/networking/libiproute/libnetlink.h [new file with mode: 0644]
busybox/networking/libiproute/linux/pkt_sched.h [new file with mode: 0644]
busybox/networking/libiproute/ll_addr.c [new file with mode: 0644]
busybox/networking/libiproute/ll_map.c [new file with mode: 0644]
busybox/networking/libiproute/ll_map.h [new file with mode: 0644]
busybox/networking/libiproute/ll_proto.c [new file with mode: 0644]
busybox/networking/libiproute/ll_types.c [new file with mode: 0644]
busybox/networking/libiproute/rt_names.c [new file with mode: 0644]
busybox/networking/libiproute/rt_names.h [new file with mode: 0644]
busybox/networking/libiproute/rtm_map.c [new file with mode: 0644]
busybox/networking/libiproute/rtm_map.h [new file with mode: 0644]
busybox/networking/libiproute/utils.c [new file with mode: 0644]
busybox/networking/libiproute/utils.h [new file with mode: 0644]
busybox/networking/nameif.c [new file with mode: 0644]
busybox/networking/nc.c [new file with mode: 0644]
busybox/networking/netstat.c [new file with mode: 0644]
busybox/networking/nslookup.c [new file with mode: 0644]
busybox/networking/ping.c [new file with mode: 0644]
busybox/networking/ping6.c [new file with mode: 0644]
busybox/networking/route.c [new file with mode: 0644]
busybox/networking/telnet.c [new file with mode: 0644]
busybox/networking/telnetd.c [new file with mode: 0644]
busybox/networking/tftp.c [new file with mode: 0644]
busybox/networking/traceroute.c [new file with mode: 0644]
busybox/networking/udhcp/AUTHORS [new file with mode: 0644]
busybox/networking/udhcp/COPYING [new file with mode: 0644]
busybox/networking/udhcp/ChangeLog [new file with mode: 0644]
busybox/networking/udhcp/Config.in [new file with mode: 0644]
busybox/networking/udhcp/Makefile [new file with mode: 0644]
busybox/networking/udhcp/Makefile.in [new file with mode: 0644]
busybox/networking/udhcp/README [new file with mode: 0644]
busybox/networking/udhcp/README.dumpleases [new file with mode: 0644]
busybox/networking/udhcp/README.udhcpc [new file with mode: 0644]
busybox/networking/udhcp/README.udhcpd [new file with mode: 0644]
busybox/networking/udhcp/TODO [new file with mode: 0644]
busybox/networking/udhcp/arpping.c [new file with mode: 0644]
busybox/networking/udhcp/arpping.h [new file with mode: 0644]
busybox/networking/udhcp/clientpacket.c [new file with mode: 0644]
busybox/networking/udhcp/clientpacket.h [new file with mode: 0644]
busybox/networking/udhcp/clientsocket.c [new file with mode: 0644]
busybox/networking/udhcp/clientsocket.h [new file with mode: 0644]
busybox/networking/udhcp/common.c [new file with mode: 0644]
busybox/networking/udhcp/common.h [new file with mode: 0644]
busybox/networking/udhcp/dhcpc.c [new file with mode: 0644]
busybox/networking/udhcp/dhcpc.h [new file with mode: 0644]
busybox/networking/udhcp/dhcpd.c [new file with mode: 0644]
busybox/networking/udhcp/dhcpd.h [new file with mode: 0644]
busybox/networking/udhcp/dumpleases.c [new file with mode: 0644]
busybox/networking/udhcp/files.c [new file with mode: 0644]
busybox/networking/udhcp/files.h [new file with mode: 0644]
busybox/networking/udhcp/frontend.c [new file with mode: 0644]
busybox/networking/udhcp/leases.c [new file with mode: 0644]
busybox/networking/udhcp/leases.h [new file with mode: 0644]
busybox/networking/udhcp/libbb_udhcp.h [new file with mode: 0644]
busybox/networking/udhcp/options.c [new file with mode: 0644]
busybox/networking/udhcp/options.h [new file with mode: 0644]
busybox/networking/udhcp/packet.c [new file with mode: 0644]
busybox/networking/udhcp/packet.h [new file with mode: 0644]
busybox/networking/udhcp/pidfile.c [new file with mode: 0644]
busybox/networking/udhcp/pidfile.h [new file with mode: 0644]
busybox/networking/udhcp/script.c [new file with mode: 0644]
busybox/networking/udhcp/script.h [new file with mode: 0644]
busybox/networking/udhcp/serverpacket.c [new file with mode: 0644]
busybox/networking/udhcp/serverpacket.h [new file with mode: 0644]
busybox/networking/udhcp/signalpipe.c [new file with mode: 0644]
busybox/networking/udhcp/signalpipe.h [new file with mode: 0644]
busybox/networking/udhcp/socket.c [new file with mode: 0644]
busybox/networking/udhcp/socket.h [new file with mode: 0644]
busybox/networking/udhcp/static_leases.c [new file with mode: 0644]
busybox/networking/udhcp/static_leases.h [new file with mode: 0644]
busybox/networking/udhcp/version.h [new file with mode: 0644]
busybox/networking/vconfig.c [new file with mode: 0644]
busybox/networking/wget.c [new file with mode: 0644]
busybox/patches/cmp_n.diff [new file with mode: 0644]
busybox/patches/dd_ibs_and_obs.diff [new file with mode: 0644]
busybox/patches/eject.diff [new file with mode: 0644]
busybox/patches/makdevs_table.diff [new file with mode: 0644]
busybox/patches/rpm2cpio_bzip2.patch [new file with mode: 0644]
busybox/patches/tftp_timeout_multicast.diff [new file with mode: 0644]
busybox/patches/top_system_cpu.diff [new file with mode: 0644]
busybox/patches/udhcp_additional_items.diff [new file with mode: 0644]
busybox/patches/udhcp_config_paths.diff [new file with mode: 0644]
busybox/patches/udhcpd_foreground.diff [new file with mode: 0644]
busybox/procps/Config.in [new file with mode: 0644]
busybox/procps/Makefile [new file with mode: 0644]
busybox/procps/Makefile.in [new file with mode: 0644]
busybox/procps/free.c [new file with mode: 0644]
busybox/procps/kill.c [new file with mode: 0644]
busybox/procps/pidof.c [new file with mode: 0644]
busybox/procps/ps.c [new file with mode: 0644]
busybox/procps/renice.c [new file with mode: 0644]
busybox/procps/sysctl.c [new file with mode: 0644]
busybox/procps/top.c [new file with mode: 0644]
busybox/procps/uptime.c [new file with mode: 0644]
busybox/scripts/.cvsignore [new file with mode: 0644]
busybox/scripts/config/.cvsignore [new file with mode: 0644]
busybox/scripts/config/Kconfig-language.txt [new file with mode: 0644]
busybox/scripts/config/Makefile [new file with mode: 0644]
busybox/scripts/config/checklist.c [new file with mode: 0644]
busybox/scripts/config/colors.h [new file with mode: 0644]
busybox/scripts/config/conf.c [new file with mode: 0644]
busybox/scripts/config/confdata.c [new file with mode: 0644]
busybox/scripts/config/dialog.h [new file with mode: 0644]
busybox/scripts/config/expr.c [new file with mode: 0644]
busybox/scripts/config/expr.h [new file with mode: 0644]
busybox/scripts/config/inputbox.c [new file with mode: 0644]
busybox/scripts/config/lex.zconf.c_shipped [new file with mode: 0644]
busybox/scripts/config/lkc.h [new file with mode: 0644]
busybox/scripts/config/lkc_proto.h [new file with mode: 0644]
busybox/scripts/config/mconf.c [new file with mode: 0644]
busybox/scripts/config/menu.c [new file with mode: 0644]
busybox/scripts/config/menubox.c [new file with mode: 0644]
busybox/scripts/config/msgbox.c [new file with mode: 0644]
busybox/scripts/config/symbol.c [new file with mode: 0644]
busybox/scripts/config/textbox.c [new file with mode: 0644]
busybox/scripts/config/util.c [new file with mode: 0644]
busybox/scripts/config/yesno.c [new file with mode: 0644]
busybox/scripts/config/zconf.l [new file with mode: 0644]
busybox/scripts/config/zconf.tab.c_shipped [new file with mode: 0644]
busybox/scripts/config/zconf.tab.h_shipped [new file with mode: 0644]
busybox/scripts/config/zconf.y [new file with mode: 0644]
busybox/scripts/mkdep.c [new file with mode: 0644]
busybox/scripts/split-include.c [new file with mode: 0644]
busybox/shell/Config.in [new file with mode: 0644]
busybox/shell/Makefile [new file with mode: 0644]
busybox/shell/Makefile.in [new file with mode: 0644]
busybox/shell/ash.c [new file with mode: 0644]
busybox/shell/cmdedit.c [new file with mode: 0644]
busybox/shell/cmdedit.h [new file with mode: 0644]
busybox/shell/hush.c [new file with mode: 0644]
busybox/shell/lash.c [new file with mode: 0644]
busybox/shell/msh.c [new file with mode: 0644]
busybox/sysdeps/linux/Config.in [new file with mode: 0644]
busybox/sysdeps/linux/defconfig [new file with mode: 0644]
busybox/sysklogd/Config.in [new file with mode: 0644]
busybox/sysklogd/Makefile [new file with mode: 0644]
busybox/sysklogd/Makefile.in [new file with mode: 0644]
busybox/sysklogd/klogd.c [new file with mode: 0644]
busybox/sysklogd/logger.c [new file with mode: 0644]
busybox/sysklogd/logread.c [new file with mode: 0644]
busybox/sysklogd/syslogd.c [new file with mode: 0644]
busybox/testsuite/README [new file with mode: 0644]
busybox/testsuite/TODO [new file with mode: 0644]
busybox/testsuite/basename/basename-does-not-remove-identical-extension [new file with mode: 0644]
busybox/testsuite/basename/basename-works [new file with mode: 0644]
busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input [new file with mode: 0644]
busybox/testsuite/bunzip2/bunzip2-removes-compressed-file [new file with mode: 0644]
busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file [new file with mode: 0644]
busybox/testsuite/cat/cat-prints-a-file [new file with mode: 0644]
busybox/testsuite/cat/cat-prints-a-file-and-standard-input [new file with mode: 0644]
busybox/testsuite/cmp/cmp-detects-difference [new file with mode: 0644]
busybox/testsuite/cp/cp-a-files-to-dir [new file with mode: 0644]
busybox/testsuite/cp/cp-a-preserves-links [new file with mode: 0644]
busybox/testsuite/cp/cp-copies-empty-file [new file with mode: 0644]
busybox/testsuite/cp/cp-copies-large-file [new file with mode: 0644]
busybox/testsuite/cp/cp-copies-small-file [new file with mode: 0644]
busybox/testsuite/cp/cp-d-files-to-dir [new file with mode: 0644]
busybox/testsuite/cp/cp-dir-create-dir [new file with mode: 0644]
busybox/testsuite/cp/cp-dir-existing-dir [new file with mode: 0644]
busybox/testsuite/cp/cp-does-not-copy-unreadable-file [new file with mode: 0644]
busybox/testsuite/cp/cp-files-to-dir [new file with mode: 0644]
busybox/testsuite/cp/cp-follows-links [new file with mode: 0644]
busybox/testsuite/cp/cp-preserves-hard-links [new file with mode: 0644]
busybox/testsuite/cp/cp-preserves-links [new file with mode: 0644]
busybox/testsuite/cp/cp-preserves-source-file [new file with mode: 0644]
busybox/testsuite/cut/cut-cuts-a-character [new file with mode: 0644]
busybox/testsuite/cut/cut-cuts-a-closed-range [new file with mode: 0644]
busybox/testsuite/cut/cut-cuts-a-field [new file with mode: 0644]
busybox/testsuite/cut/cut-cuts-an-open-range [new file with mode: 0644]
busybox/testsuite/cut/cut-cuts-an-unclosed-range [new file with mode: 0644]
busybox/testsuite/date/date-R-works [new file with mode: 0644]
busybox/testsuite/date/date-format-works [new file with mode: 0644]
busybox/testsuite/date/date-u-works [new file with mode: 0644]
busybox/testsuite/date/date-works [new file with mode: 0644]
busybox/testsuite/dd/dd-accepts-if [new file with mode: 0644]
busybox/testsuite/dd/dd-accepts-of [new file with mode: 0644]
busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output [new file with mode: 0644]
busybox/testsuite/dd/dd-prints-count-to-standard-error [new file with mode: 0644]
busybox/testsuite/dirname/dirname-handles-absolute-path [new file with mode: 0644]
busybox/testsuite/dirname/dirname-handles-empty-path [new file with mode: 0644]
busybox/testsuite/dirname/dirname-handles-multiple-slashes [new file with mode: 0644]
busybox/testsuite/dirname/dirname-handles-relative-path [new file with mode: 0644]
busybox/testsuite/dirname/dirname-handles-root [new file with mode: 0644]
busybox/testsuite/dirname/dirname-handles-single-component [new file with mode: 0644]
busybox/testsuite/dirname/dirname-works [new file with mode: 0644]
busybox/testsuite/du/du-h-works [new file with mode: 0644]
busybox/testsuite/du/du-k-works [new file with mode: 0644]
busybox/testsuite/du/du-l-works [new file with mode: 0644]
busybox/testsuite/du/du-m-works [new file with mode: 0644]
busybox/testsuite/du/du-s-works [new file with mode: 0644]
busybox/testsuite/du/du-works [new file with mode: 0644]
busybox/testsuite/echo/echo-does-not-print-newline [new file with mode: 0644]
busybox/testsuite/echo/echo-prints-argument [new file with mode: 0644]
busybox/testsuite/echo/echo-prints-arguments [new file with mode: 0644]
busybox/testsuite/echo/echo-prints-newline [new file with mode: 0644]
busybox/testsuite/expr/expr-works [new file with mode: 0644]
busybox/testsuite/false/false-is-silent [new file with mode: 0644]
busybox/testsuite/false/false-returns-failure [new file with mode: 0644]
busybox/testsuite/find/find-supports-minus-xdev [new file with mode: 0644]
busybox/testsuite/grep/egrep-is-not-case-insensitive [new file with mode: 0644]
busybox/testsuite/grep/egrep-supports-extended-regexps [new file with mode: 0644]
busybox/testsuite/grep/grep-handles-binary-files [new file with mode: 0644]
busybox/testsuite/grep/grep-handles-multiple-regexps [new file with mode: 0644]
busybox/testsuite/grep/grep-is-also-egrep [new file with mode: 0644]
busybox/testsuite/grep/grep-matches-NUL [new file with mode: 0644]
busybox/testsuite/gunzip/gunzip-reads-from-standard-input [new file with mode: 0644]
busybox/testsuite/gzip/gzip-accepts-multiple-files [new file with mode: 0644]
busybox/testsuite/gzip/gzip-accepts-single-minus [new file with mode: 0644]
busybox/testsuite/gzip/gzip-removes-original-file [new file with mode: 0644]
busybox/testsuite/head/head-n-works [new file with mode: 0644]
busybox/testsuite/head/head-works [new file with mode: 0644]
busybox/testsuite/hostid/hostid-works [new file with mode: 0644]
busybox/testsuite/hostname/hostname-d-works [new file with mode: 0644]
busybox/testsuite/hostname/hostname-i-works [new file with mode: 0644]
busybox/testsuite/hostname/hostname-s-works [new file with mode: 0644]
busybox/testsuite/hostname/hostname-works [new file with mode: 0644]
busybox/testsuite/id/id-g-works [new file with mode: 0644]
busybox/testsuite/id/id-u-works [new file with mode: 0644]
busybox/testsuite/id/id-un-works [new file with mode: 0644]
busybox/testsuite/id/id-ur-works [new file with mode: 0644]
busybox/testsuite/ln/ln-creates-hard-links [new file with mode: 0644]
busybox/testsuite/ln/ln-creates-soft-links [new file with mode: 0644]
busybox/testsuite/ln/ln-force-creates-hard-links [new file with mode: 0644]
busybox/testsuite/ln/ln-force-creates-soft-links [new file with mode: 0644]
busybox/testsuite/ln/ln-preserves-hard-links [new file with mode: 0644]
busybox/testsuite/ln/ln-preserves-soft-links [new file with mode: 0644]
busybox/testsuite/ls/ls-1-works [new file with mode: 0644]
busybox/testsuite/ls/ls-h-works [new file with mode: 0644]
busybox/testsuite/ls/ls-l-works [new file with mode: 0644]
busybox/testsuite/ls/ls-s-works [new file with mode: 0644]
busybox/testsuite/md5sum/md5sum-verifies-non-binary-file [new file with mode: 0644]
busybox/testsuite/mkdir/mkdir-makes-a-directory [new file with mode: 0644]
busybox/testsuite/mkdir/mkdir-makes-parent-directories [new file with mode: 0644]
busybox/testsuite/msh/msh-supports-underscores-in-variable-names [new file with mode: 0644]
busybox/testsuite/mv/mv-files-to-dir [new file with mode: 0644]
busybox/testsuite/mv/mv-follows-links [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-empty-file [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-file [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-hardlinks [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-large-file [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-small-file [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-symlinks [new file with mode: 0644]
busybox/testsuite/mv/mv-moves-unreadable-files [new file with mode: 0644]
busybox/testsuite/mv/mv-preserves-hard-links [new file with mode: 0644]
busybox/testsuite/mv/mv-preserves-links [new file with mode: 0644]
busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir [new file with mode: 0644]
busybox/testsuite/mv/mv-removes-source-file [new file with mode: 0644]
busybox/testsuite/pwd/pwd-prints-working-directory [new file with mode: 0644]
busybox/testsuite/rm/rm-removes-file [new file with mode: 0644]
busybox/testsuite/rmdir/rmdir-removes-parent-directories [new file with mode: 0644]
busybox/testsuite/runtest [new file with mode: 0755]
busybox/testsuite/sed/sed-accepts-blanks-before-command [new file with mode: 0644]
busybox/testsuite/sed/sed-aic-commands [new file with mode: 0644]
busybox/testsuite/sed/sed-append-hold-space-to-pattern-space [new file with mode: 0644]
busybox/testsuite/sed/sed-append-next-line [new file with mode: 0644]
busybox/testsuite/sed/sed-branch [new file with mode: 0644]
busybox/testsuite/sed/sed-branch-conditional [new file with mode: 0644]
busybox/testsuite/sed/sed-branch-conditional2 [new file with mode: 0644]
busybox/testsuite/sed/sed-branch-no-label [new file with mode: 0644]
busybox/testsuite/sed/sed-chains-substs [new file with mode: 0644]
busybox/testsuite/sed/sed-chains-substs2 [new file with mode: 0644]
busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line [new file with mode: 0644]
busybox/testsuite/sed/sed-handles-embedded-slashes [new file with mode: 0644]
busybox/testsuite/sed/sed-handles-empty-lines [new file with mode: 0644]
busybox/testsuite/sed/sed-handles-unsatisfied-backrefs [new file with mode: 0644]
busybox/testsuite/sed/sed-next-line [new file with mode: 0644]
busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs [new file with mode: 0644]
busybox/testsuite/sed/sed-recurses-properly [new file with mode: 0644]
busybox/testsuite/sed/sed-regex-match-newline [new file with mode: 0644]
busybox/testsuite/sed/sed-splits-edit-commands-on-command-line [new file with mode: 0644]
busybox/testsuite/sed/sed-subst-subprint [new file with mode: 0644]
busybox/testsuite/sed/sed-write-to-stdout [new file with mode: 0644]
busybox/testsuite/sort/sort-n-works [new file with mode: 0644]
busybox/testsuite/sort/sort-r-works [new file with mode: 0644]
busybox/testsuite/sort/sort-works [new file with mode: 0644]
busybox/testsuite/strings/strings-works-like-GNU [new file with mode: 0644]
busybox/testsuite/tail/tail-n-works [new file with mode: 0644]
busybox/testsuite/tail/tail-works [new file with mode: 0644]
busybox/testsuite/tar/tar-archives-multiple-files [new file with mode: 0644]
busybox/testsuite/tar/tar-complains-about-missing-file [new file with mode: 0644]
busybox/testsuite/tar/tar-demands-at-least-one-ctx [new file with mode: 0644]
busybox/testsuite/tar/tar-demands-at-most-one-ctx [new file with mode: 0644]
busybox/testsuite/tar/tar-extracts-file [new file with mode: 0644]
busybox/testsuite/tar/tar-extracts-from-standard-input [new file with mode: 0644]
busybox/testsuite/tar/tar-extracts-multiple-files [new file with mode: 0644]
busybox/testsuite/tar/tar-extracts-to-standard-output [new file with mode: 0644]
busybox/testsuite/tar/tar-handles-cz-options [new file with mode: 0644]
busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list [new file with mode: 0644]
busybox/testsuite/tar/tar-handles-exclude-and-extract-lists [new file with mode: 0644]
busybox/testsuite/tar/tar-handles-multiple-X-options [new file with mode: 0644]
busybox/testsuite/tar/tar-handles-nested-exclude [new file with mode: 0644]
busybox/testsuite/tee/tee-appends-input [new file with mode: 0644]
busybox/testsuite/tee/tee-tees-input [new file with mode: 0644]
busybox/testsuite/touch/touch-creates-file [new file with mode: 0644]
busybox/testsuite/touch/touch-does-not-create-file [new file with mode: 0644]
busybox/testsuite/touch/touch-touches-files-after-non-existent-file [new file with mode: 0644]
busybox/testsuite/tr/tr-d-works [new file with mode: 0644]
busybox/testsuite/tr/tr-non-gnu [new file with mode: 0644]
busybox/testsuite/tr/tr-works [new file with mode: 0644]
busybox/testsuite/true/true-is-silent [new file with mode: 0644]
busybox/testsuite/true/true-returns-success [new file with mode: 0644]
busybox/testsuite/uptime/uptime-works [new file with mode: 0644]
busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly [new file with mode: 0644]
busybox/testsuite/wc/wc-counts-all [new file with mode: 0644]
busybox/testsuite/wc/wc-counts-characters [new file with mode: 0644]
busybox/testsuite/wc/wc-counts-lines [new file with mode: 0644]
busybox/testsuite/wc/wc-counts-words [new file with mode: 0644]
busybox/testsuite/wc/wc-prints-longest-line-length [new file with mode: 0644]
busybox/testsuite/wget/wget--O-overrides--P [new file with mode: 0644]
busybox/testsuite/wget/wget-handles-empty-path [new file with mode: 0644]
busybox/testsuite/wget/wget-retrieves-google-index [new file with mode: 0644]
busybox/testsuite/wget/wget-supports--P [new file with mode: 0644]
busybox/testsuite/which/which-uses-default-path [new file with mode: 0644]
busybox/testsuite/xargs/xargs-works [new file with mode: 0644]
busybox/util-linux/Config.in [new file with mode: 0644]
busybox/util-linux/Makefile [new file with mode: 0644]
busybox/util-linux/Makefile.in [new file with mode: 0644]
busybox/util-linux/dmesg.c [new file with mode: 0644]
busybox/util-linux/fbset.c [new file with mode: 0644]
busybox/util-linux/fdflush.c [new file with mode: 0644]
busybox/util-linux/fdformat.c [new file with mode: 0644]
busybox/util-linux/fdisk.c [new file with mode: 0644]
busybox/util-linux/freeramdisk.c [new file with mode: 0644]
busybox/util-linux/fsck_minix.c [new file with mode: 0644]
busybox/util-linux/getopt.c [new file with mode: 0644]
busybox/util-linux/hexdump.c [new file with mode: 0644]
busybox/util-linux/hwclock.c [new file with mode: 0644]
busybox/util-linux/losetup.c [new file with mode: 0644]
busybox/util-linux/mkfs_minix.c [new file with mode: 0644]
busybox/util-linux/mkswap.c [new file with mode: 0644]
busybox/util-linux/more.c [new file with mode: 0644]
busybox/util-linux/mount.c [new file with mode: 0644]
busybox/util-linux/nfsmount.c [new file with mode: 0644]
busybox/util-linux/nfsmount.h [new file with mode: 0644]
busybox/util-linux/pivot_root.c [new file with mode: 0644]
busybox/util-linux/rdate.c [new file with mode: 0644]
busybox/util-linux/swaponoff.c [new file with mode: 0644]
busybox/util-linux/umount.c [new file with mode: 0644]

diff --git a/busybox/.cvsignore b/busybox/.cvsignore
new file mode 100644 (file)
index 0000000..4e4f586
--- /dev/null
@@ -0,0 +1,7 @@
+busybox
+busybox.links
+_install
+.config
+.menuconfig.log
+.config.cmd
+.config.old
diff --git a/busybox/.indent.pro b/busybox/.indent.pro
new file mode 100644 (file)
index 0000000..492ecf1
--- /dev/null
@@ -0,0 +1,33 @@
+--blank-lines-after-declarations
+--blank-lines-after-procedures
+--break-before-boolean-operator
+--no-blank-lines-after-commas
+--braces-on-if-line
+--braces-on-struct-decl-line
+--comment-indentation25
+--declaration-comment-column25
+--no-comment-delimiters-on-blank-lines
+--cuddle-else
+--continuation-indentation4
+--case-indentation0
+--else-endif-column33
+--space-after-cast
+--line-comments-indentation0
+--declaration-indentation1
+--dont-format-first-column-comments
+--dont-format-comments
+--honour-newlines
+--indent-level4
+/* changed from 0 to 4 */
+--parameter-indentation4
+--line-length78 /* changed from 75 */
+--continue-at-parentheses
+--no-space-after-function-call-names
+--dont-break-procedure-type
+--dont-star-comments
+--leave-optional-blank-lines
+--dont-space-special-semicolon
+--tab-size4
+/* additions by Mark */
+--case-brace-indentation0
+--leave-preprocessor-space
diff --git a/busybox/AUTHORS b/busybox/AUTHORS
new file mode 100644 (file)
index 0000000..87df22d
--- /dev/null
@@ -0,0 +1,133 @@
+List of the authors of code contained in BusyBox.
+
+If you have code in BusyBox, you should be listed here.  If you should be
+listed, or the description of what you have done needs more detail, or is
+incorect, _please_ let me know.
+
+ -Erik
+
+-----------
+
+Emanuele Aina <emanuele.aina@tiscali.it>
+    run-parts
+
+Erik Andersen <andersen@codepoet.org>
+    Tons of new stuff, major rewrite of most of the
+    core apps, tons of new apps as noted in header files.
+    Lots of tedious effort writing these boring docs that
+    nobody is going to actually read.
+
+Laurence Anderson <l.d.anderson@warwick.ac.uk>
+    rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
+
+Jeff Angielski <jeff@theptrgroup.com>
+    ftpput, ftpget
+
+Edward Betts <edward@debian.org>
+    expr, hostid, logname, whoami
+
+John Beppu <beppu@codepoet.org>
+    du, nslookup, sort
+
+Brian Candler <B.Candler@pobox.com>
+    tiny-ls(ls)
+
+Randolph Chung <tausq@debian.org>
+    fbset, ping, hostname
+
+Dave Cinege <dcinege@psychosis.com>
+    more(v2), makedevs, dutmp, modularization, auto links file,
+    various fixes, Linux Router Project maintenance
+
+Jordan Crouse <jordan@cosmicpenguin.net>
+    ipcalc
+
+Magnus Damm <damm@opensource.se>
+    tftp client
+    insmod powerpc support
+
+Larry Doolittle <ldoolitt@recycle.lbl.gov>
+    pristine source directory compilation, lots of patches and fixes.
+
+Glenn Engel <glenne@engel.org>
+    httpd
+
+Gennady Feldman <gfeldman@gena01.com>
+    Sysklogd (single threaded syslogd, IPC Circular buffer support,
+    logread), various fixes.
+
+Robert Griebl <sandman@handhelds.org>
+    modprobe, hwclock, suid/sgid handling, tinylogin integration
+    many bugfixes and enhancements
+
+Karl M. Hegbloom <karlheg@debian.org>
+    cp_mv.c, the test suite, various fixes to utility.c, &c.
+
+Daniel Jacobowitz <dan@debian.org>
+    mktemp.c
+
+Matt Kraai <kraai@alumni.cmu.edu>
+    documentation, bugfixes, test suite
+
+Stephan Linz <linz@li-pro.net>
+    ipcalc, Red Hat equivalence
+
+John Lombardo <john@deltanet.com>
+    tr
+
+Glenn McGrath <bug1@iinet.net.au>
+    Common unarchving code and unarchiving applets, ifupdown, ftpgetput,
+    nameif, sed, patch, fold, install, uudecode. 
+    Various bugfixes, review and apply numerous patches. 
+
+Manuel Novoa III <mjn3@codepoet.org>
+    cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+    mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
+    get_last_path_component, simplify_path, and a number trivial libbb routines
+
+    also bug fixes, partial rewrites, and size optimizations in
+    ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
+    mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
+    interface, dutmp, ifconfig, route
+
+Vladimir Oleynik <dzo@simtreas.ru>
+    cmdedit; xargs(current), httpd(current);
+    ports: ash, crond, fdisk, inetd, stty, traceroute, top;
+    locale, various fixes
+    and irreconcilable critic of everything not perfect.
+
+Bruce Perens <bruce@pixar.com>
+    Original author of BusyBox in 1995, 1996. Some of his code can
+    still be found hiding here and there...
+
+Tim Riker <Tim@Rikers.org>
+    bug fixes, member of fan club
+
+Kent Robotti <robotti@metconnect.com>
+    reset, tons and tons of bug reports and patches.
+
+Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
+    wget - Contributed by permission of Covad Communications
+
+Pavel Roskin <proski@gnu.org>
+    Lots of bugs fixes and patches.
+
+Gyepi Sam <gyepi@praxis-sw.com>
+    Remote logging feature for syslogd
+
+Linus Torvalds <torvalds@transmeta.com>
+    mkswap, fsck.minix, mkfs.minix
+
+Mark Whitley <markw@codepoet.org>
+    grep, sed, cut, xargs(previous),
+    style-guide, new-applet-HOWTO, bug fixes, etc.
+
+Charles P. Wright <cpwright@villagenet.com>
+    gzip, mini-netcat(nc)
+
+Enrique Zanardi <ezanardi@ull.es>
+    tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+Tito Ragusa <farmatito@tiscali.it>
+    devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm and fdformat.
+
diff --git a/busybox/Changelog b/busybox/Changelog
new file mode 100644 (file)
index 0000000..721fc82
--- /dev/null
@@ -0,0 +1,1381 @@
+---------------------
+PatchSet 4347 
+Date: 2004/08/16 10:29:28
+Author: andersen
+Branch: HEAD
+Tag: busybox_1_00_rc3 
+Log:
+Prepare for release
+
+Members: 
+       Changelog:1.294->1.295 
+       docs/busybox_header.pod:1.17->1.18 
+       docs/busybox.net/news.html:1.21->1.22 
+       docs/busybox.net/screenshot.html:1.11->1.12 
+
+---------------------
+PatchSet 4348 
+Date: 2004/08/18 17:57:16
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Fixup 'dc' usage
+
+Members: 
+       include/usage.h:1.218->1.219 
+
+---------------------
+PatchSet 4349 
+Date: 2004/08/19 18:22:13
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Vladimir N. Oleynik:
+
+On Wed Aug 18, 2004 at 06:52:57PM +0800, Matt Johnston wrote:
+> I've come across some strange-seeming behaviour when running programs
+> under Busybox (1.0.0-rc3) ash. If the child process sets stdin to be
+> non-blocking and then exits, the parent ash will also exit. A quick strace
+> shows that a subsequent read() from stdin returns EAGAIN (as would be
+> expected):
+
+Thanks!
+Patch attached.
+
+
+--w
+vodz
+
+Members: 
+       shell/ash.c:1.104->1.105 
+       shell/cmdedit.c:1.92->1.93 
+
+---------------------
+PatchSet 4350 
+Date: 2004/08/19 18:25:02
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Tito documenting the '-q' option
+
+Members: 
+       include/usage.h:1.219->1.220 
+
+---------------------
+PatchSet 4351 
+Date: 2004/08/19 18:26:26
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Tito adding support for '-q'
+
+Members: 
+       procps/kill.c:1.52->1.53 
+
+---------------------
+PatchSet 4352 
+Date: 2004/08/19 18:30:31
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Mike Castle to cleanup some modutils issues, in
+particular making alias support work better.
+
+Members: 
+       modutils/modprobe.c:1.39->1.40 
+
+---------------------
+PatchSet 4353 
+Date: 2004/08/19 19:15:06
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+regularly update the status line display
+ -Erik
+
+Members: 
+       editors/vi.c:1.37->1.38 
+
+---------------------
+PatchSet 4354 
+Date: 2004/08/19 19:17:30
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Rodney Radford adding x86_64 support.
+
+Members: 
+       modutils/insmod.c:1.122->1.123 
+
+---------------------
+PatchSet 4355 
+Date: 2004/08/25 02:02:19
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Manousaridis Angelos to cleanup stale file descriptors, it was preventing unmounting an initial filesystem.
+
+Members: 
+       loginutils/getty.c:1.13->1.14 
+       loginutils/login.c:1.19->1.20 
+
+---------------------
+PatchSet 4356 
+Date: 2004/08/26 21:45:21
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Felipe Kellermann writes:
+
+Unfortunatelly I've not followed the last two or three weeks commits (new
+semester started and so now I rarely have time to fix my personal bridge)
+but tonight I synched my tree and immediately noticed a rather nasty bug!
+
+[Using libbb/interface.c:1.24]
+# grep eth0 /proc/net/dev | xargs
+eth0:311708397 237346 1670 0 1789 1670 0 0 22580308 120297 0 0 0 102 0 0
+
+# ifconfig eth0
+eth0      Link encap:Ethernet  HWaddr 00:20:AF:7C:EA:B7
+          inet addr:10.0.0.1  Bcast:10.0.0.127  Mask:255.255.255.128
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
+          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:1000
+          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
+          Interrupt:5 Base address:0x320
+
+
+All values `ifconfig' is showing are `zeroed' -- I quickly looked at the
+last commits I missed and noticed that there were a commit relating to
+ifconfig, libbb/interface.c:1.23->1.24 (PatchSet 4338).
+
+I've reversed the patch and now everything is working again.  I compared
+the get_name's return values from the 1.23 and 1.24 and quickly noticed
+that the new revision is leaving `p' right on the sep while the rev 1.23
+was leaving it right on the starting of the values...
+
+1-line, 1/3-minute patch attached  :-)
+
+Members: 
+       libbb/interface.c:1.24->1.25 
+
+---------------------
+PatchSet 4357 
+Date: 2004/08/26 22:18:56
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Tito writes:
+
+Hi,
+I've spent the half night staring at the devilish  my_getpwuid and my_getgrgid functions
+trying to find out a way to avoid actual and future potential buffer overflow problems
+without breaking existing code.
+Finally I've  found a not intrusive way to do this that surely doesn't break existing code
+and fixes a couple of problems too.
+The attached patch:
+1) changes the behaviour of my_getpwuid and my_getgrgid to avoid potetntial buffer overflows
+2) fixes all occurences of this function calls in tar.c , id.c , ls.c, whoami.c, logger.c, libbb.h.
+3) The behaviour of tar, ls and  logger is unchanged.
+4) The behavior of ps with somewhat longer usernames messing up output is fixed.
+5) The only bigger change was the increasing of size of the buffers in id.c to avoid
+     false negatives (unknown user: xxxxxx) with usernames longer than 8 chars.
+     The value i used ( 32 chars ) was taken from the tar header ( see gname and uname).
+     Maybe this buffers can be reduced a bit  ( to 16 or whatever ), this is up to you.
+6) The increase of size of the binary is not so dramatic:
+     size busybox
+       text    data     bss     dec     hex filename
+     239568    2300   36816  278684   4409c busybox
+    size busybox_fixed
+       text    data     bss     dec     hex filename
+     239616    2300   36816  278732   440cc busybox
+7) The behaviour of whoami changed:
+    actually it  prints out an username cut down to the size of the buffer.
+    This could be fixed by increasing the size of the buffer as in id.c or
+    avoid the use of my_getpwuid and use getpwuid directly instead.
+    Maybe this colud be also remain unchanged......
+
+Please apply if you think it is ok to do so.
+The diff applies on today's cvs tarball (2004-08-25).
+Thanks in advance,
+Ciao,
+Tito
+
+Members: 
+       archival/tar.c:1.194->1.195 
+       coreutils/id.c:1.24->1.25 
+       coreutils/ls.c:1.110->1.111 
+       coreutils/whoami.c:1.21->1.22 
+       include/libbb.h:1.133->1.134 
+       libbb/my_getgrgid.c:1.7->1.8 
+       libbb/my_getpwuid.c:1.7->1.8 
+       libbb/procps.c:1.13->1.14 
+       sysklogd/logger.c:1.39->1.40 
+
+---------------------
+PatchSet 4358 
+Date: 2004/08/26 22:22:50
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Vladimir N. Oleynik writes:
+
+Ming-Ching,
+
+>>No. Here there are no mistakes.
+>>You using POST metod.
+>>For get data you should read from stdin CONTENT_LENGTH bytes.
+
+>Hower as I posted a little while ago, there is indeed a bug
+>in POST method if the CONTENT_LENGTH is bigger
+>than sizeof(wbuf[128]). So if your CGI script is expecting to
+>read the full CONTENT_LENGTH, it might block forever,
+>because it will only transfer sizeof(wbuf) to the CGI.
+
+Ok, Ok. I should find time to understand with a problem.
+Try attached patch.
+
+
+--w
+vodz
+
+Members: 
+       networking/httpd.c:1.26->1.27 
+
+---------------------
+PatchSet 4359 
+Date: 2004/08/26 22:26:26
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Save a line or two
+
+Members: 
+       loginutils/getty.c:1.14->1.15 
+       loginutils/login.c:1.20->1.21 
+
+---------------------
+PatchSet 4360 
+Date: 2004/08/26 22:36:02
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Tito writes:
+
+Hi,
+I've fixed also the issue of whoami cutting down usernames.
+This time I cannot send a diff because i don't know if my previous patches will be applied
+or not, so I send in the whole file.
+The changes I've made don't affect size but ensure that usernames of whatever lenght
+are correctly displayed.
+root@localhost:/dev/pts/3:/root/Desktop/busybox/coreutils# size whoami_orig.o
+   text    data     bss     dec     hex filename
+    102       0       0     102      66 whoami_orig.o
+root@localhost:/dev/pts/3:/root/Desktop/busybox/coreutils# size whoami.o
+   text    data     bss     dec     hex filename
+     93       0       0      93      5d whoami.o
+
+This should be applied even if the other patches aren't as this matches the behaviour of the  GNU whoami.
+
+Thanks in advance,
+Ciao,
+Tito
+
+Members: 
+       coreutils/whoami.c:1.22->1.23 
+
+---------------------
+PatchSet 4361 
+Date: 2004/08/26 23:01:34
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+The login applet should always be setuid root
+
+Members: 
+       include/applets.h:1.114->1.115 
+
+---------------------
+PatchSet 4362 
+Date: 2004/08/26 23:13:00
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Improve the setuid situation a bit, and make it more apparent
+when people really ought to make busybox setuid root.
+ -Erik
+
+Members: 
+       Makefile:1.296->1.297 
+       loginutils/Config.in:1.8->1.9 
+       miscutils/Config.in:1.18->1.19 
+
+---------------------
+PatchSet 4363 
+Date: 2004/08/26 23:15:29
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Togg writes:
+
+Syslogd wont start if remote-logging is enabled and the connection to the
+remote-log server is not possible on syslogd startup.
+
+I found a patch somewhere which works like a charm. It uses sendto() which
+seems more reliable for this issue.
+
+Please see attached patch. Many people will be more happy with this included
+I think.
+
+Regards,
+Togg
+
+Members: 
+       sysklogd/syslogd.c:1.113->1.114 
+
+---------------------
+PatchSet 4364 
+Date: 2004/08/27 19:55:28
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Quiet a few warnings
+
+Members: 
+       init/mesg.c:1.2->1.3 
+       shell/msh.c:1.20->1.21 
+
+---------------------
+PatchSet 4365 
+Date: 2004/08/28 00:43:05
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Fixup some warnings
+
+Members: 
+       archival/bunzip2.c:1.19->1.20 
+       archival/libunarchive/decompress_bunzip2.c:1.13->1.14 
+       coreutils/uniq.c:1.21->1.22 
+       modutils/insmod.c:1.123->1.124 
+       networking/ipcalc.c:1.10->1.11 
+       util-linux/mkfs_minix.c:1.42->1.43 
+
+---------------------
+PatchSet 4366 
+Date: 2004/09/02 22:21:39
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Tito writes:
+
+Hi Erik,
+Hi to all,
+This is part five of the my_get*id story.
+I've tweaked a bit this two functions to make them more flexible,
+but this changes will not affect existing code.
+Now they work so:
+1) my_getpwuid( char *user, uid_t uid, int bufsize)
+
+   if bufsize is > 0 char *user cannot be set to NULL
+                     on success username is written on static allocated buffer
+                     on failure uid as string is written to buffer and NULL is returned
+   if bufsize is = 0 char *user can be set to NULL
+                     on success username is returned
+                     on failure NULL is returned
+   if bufsize is < 0 char *user can be set to NULL
+                     on success username is returned
+                     on failure an error message is printed and the program exits
+
+  2) 1) my_getgrgid( char *group, uid_t uid, int bufsize)
+
+   if bufsize is > 0 char *group cannot be set to NULL
+                     on success groupname is written on static allocated buffer
+                     on failure gid as string is written to buffer and NULL is returned
+   if bufsize is = 0 char *group can be set to NULL
+                     on success groupname is returned
+                     on failure NULL is returned
+   if bufsize is < 0 char *group can be set to nULL
+                     on success groupname is returned
+                     on failure an error message is printed and the program exits
+
+This changes were needed mainly for my new id applet.
+It is somewhat bigger then the previous but matches the behaviour of GNU id
+and is capable to handle usernames of whatever length.
+BTW: at a first look it seems to me that it will integrate well (with just a few changes)
+with the pending patch  in patches/id_groups_alias.patch.
+The increase in size is balanced by the removal of my_getpwnamegid.c
+from libbb as this was used only in previous id applet and by size optimizations
+made possible in whoami.c and in passwd.c.
+I know that we are in feature freeze but I think that i've tested it enough
+(at least I hope so.......).
+
+Members: 
+       coreutils/id.c:1.25->1.26 
+       coreutils/whoami.c:1.23->1.24 
+       include/libbb.h:1.134->1.135 
+       libbb/Makefile.in:1.36->1.37 
+       libbb/my_getgrgid.c:1.8->1.9 
+       libbb/my_getpwuid.c:1.8->1.9 
+       loginutils/passwd.c:1.7->1.8 
+
+---------------------
+PatchSet 4367 
+Date: 2004/09/02 22:22:16
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Tito writes:
+
+The second patch contains:
+1) a size optimization for adduser.c
+2) removes a warning about an unused variable in syslogd.c if CONFIG_FEATURE_REMOTE_LOG is not set
+3)cosmetic fixes for addgroup_full_usage and adduser_full_usage
+
+Ciao,
+Tito
+
+Members: 
+       include/usage.h:1.220->1.221 
+       loginutils/adduser.c:1.10->1.11 
+       sysklogd/syslogd.c:1.114->1.115 
+
+---------------------
+PatchSet 4368 
+Date: 2004/09/02 23:03:24
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Based on patches from Mike Frysinger, add insmod support for
+sparc and ia64 (itanium).
+
+Also, reorganize the insmod architecture support code to be
+alphasorted and less messy.
+
+Update the readme to list current insmod arch support.
+
+Members: 
+       README:1.35->1.36 
+       modutils/insmod.c:1.124->1.125 
+
+---------------------
+PatchSet 4369 
+Date: 2004/09/02 23:11:52
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+No longer needed
+
+Members: 
+       libbb/my_getpwnamegid.c:1.7->1.8(DEAD) 
+       patches/id_groups_alias.patch:1.1->1.2(DEAD) 
+
+---------------------
+PatchSet 4370 
+Date: 2004/09/02 23:13:10
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Jonas Holmberg from axis dot com writes:
+
+This patch makes msh handle variable expansion within backticks more
+correctly.
+
+Current behaviour (wrong):
+--------------------------
+
+BusyBox v1.00-rc3 (2004.08.26-11:51+0000) Built-in shell (msh)
+Enter 'help' for a list of built-in commands.
+
+$ A='`echo hello`'
+$ echo $A
+`echo hello`
+$ echo `echo $A`
+hello
+$
+
+
+New behaviour (correct):
+------------------------
+
+BusyBox v1.00-rc3 (2004.08.26-11:51+0000) Built-in shell (msh)
+Enter 'help' for a list of built-in commands.
+
+$ A='`echo hello`'
+$ echo $A
+`echo hello`
+$ echo `echo $A`
+`echo hello`
+$
+
+The current behaviour (wrong according to standards) was actually my
+fault. msh handles backticks by executing a subshell (which makes it
+work on MMU-less systems). Executing a subshell makes it hard to only
+expand variables once in the parent. Therefore I export all variables
+that will be expanded within the backticks and let the subshell handle
+the expansion instead.
+
+The bug was found while searching for security leaks in CGI-scripts.
+Current behaviour of msh makes it easy to expand backticks by mistake
+in $QUERY_STRING. I recommend appling the patch before release of bb
+1.00.
+
+/Jonas
+
+Members: 
+       shell/msh.c:1.21->1.22 
+
+---------------------
+PatchSet 4371 
+Date: 2004/09/08 10:01:07
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patrick Huesmann noticed BusyBox would not link when
+CONFIG_FEATURE_COMMAND_EDITING was defined *and*
+CONFIG_FEATURE_COMMAND_TAB_COMPLETION was undefined.
+
+Vladimir N. Oleynik writes:
+
+Its declare always, also if CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+undefined.
+Patch to CVS version attached.
+
+--w
+vodz
+
+Members: 
+       shell/ash.c:1.105->1.106 
+
+---------------------
+PatchSet 4372 
+Date: 2004/09/08 10:56:06
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Felipe Kellermann writes:
+
+The Togg's sysklogd patch to use sendto() on remote logging is formatting
+strangely (using `<' and '>' surrounding the `msg' string message).  This
+is OK, but this is not the standard way of formatting this message.
+
+So this patch does the following:
+
+o Fix the formatting to the standard way.
+o Uses `MAXLINE' when needed;
+o Don't loop sending messages without a "sleeping time",
+  I'm now doing `now = 1', `now <<= 1';
+o Don't die on `init_RemoteLog' when starting up (feature!)
+  We're now trying to connect every time we have an invalid fd;
+o Removes one static uneeded variable.
+o Removes two automatic uneeded variables.
+
+Members: 
+       sysklogd/syslogd.c:1.115->1.116 
+
+---------------------
+PatchSet 4373 
+Date: 2004/09/08 20:13:05
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Fixup URL
+
+Members: 
+       docs/busybox.net/cvs_write.html:1.9->1.10 
+
+---------------------
+PatchSet 4374 
+Date: 2004/09/14 13:59:44
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+I have to assume both Avaks and LSILogic are deliberatly ignoring me.
+
+Members: 
+       docs/busybox.net/shame.html:1.18->1.19 
+
+---------------------
+PatchSet 4375 
+Date: 2004/09/14 16:08:02
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from tito to add argument checking.
+
+Members: 
+       loginutils/addgroup.c:1.12->1.13 
+
+---------------------
+PatchSet 4376 
+Date: 2004/09/14 16:23:56
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Felipe Kellermann, adds missing applet usage options, removes usage
+for options that are currently not implemented and fixes typos.
+
+Members: 
+       include/usage.h:1.221->1.222 
+
+---------------------
+PatchSet 4377 
+Date: 2004/09/14 17:24:58
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Felipe Kellermann, remove some unnecessary dups, i declared a few extra const's also.
+
+Members: 
+       networking/ifupdown.c:1.50->1.51 
+       networking/telnet.c:1.43->1.44 
+       networking/telnetd.c:1.12->1.13 
+       networking/tftp.c:1.28->1.29 
+       util-linux/getopt.c:1.13->1.14 
+
+---------------------
+PatchSet 4378 
+Date: 2004/09/14 18:12:13
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch by Felipe Kellermann, fix a bug introduced in the last patch by adding a condition around the remote logging, also adds some comments.
+
+Members: 
+       sysklogd/syslogd.c:1.116->1.117 
+
+---------------------
+PatchSet 4379 
+Date: 2004/09/14 18:56:52
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Apply patch from Felipe Kellermann to simlify logic of sort functions.
+I reversed the result of the sort functions to make the big numbers go to the top.
+
+Members: 
+       procps/top.c:1.12->1.13 
+
+---------------------
+PatchSet 4380 
+Date: 2004/09/14 19:14:00
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+remove a cut/paste mistake, i better get some sleep.
+
+Members: 
+       procps/top.c:1.13->1.14 
+
+---------------------
+PatchSet 4381 
+Date: 2004/09/15 02:05:23
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch by Felipe Kellermann, use the common escape handling function and remove some unused code.
+
+Members: 
+       coreutils/printf.c:1.22->1.23 
+
+---------------------
+PatchSet 4382 
+Date: 2004/09/15 02:39:09
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Only write to shadow file is shadow passwords are enabled. Patch by magicfox modified by myself to retain check for shadow file access.
+
+Members: 
+       loginutils/passwd.c:1.8->1.9 
+
+---------------------
+PatchSet 4383 
+Date: 2004/09/15 03:04:07
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Tito writes,
+"This patch fixes all the bugs in id previously spotted by vodz and me.
+The binary size increased a bit,  but now it should work as expected."
+
+Members: 
+       coreutils/id.c:1.26->1.27 
+       include/libbb.h:1.135->1.136 
+       libbb/Makefile.in:1.37->1.38 
+       libbb/my_getgrgid.c:1.9->1.10 
+       libbb/my_getpwuid.c:1.9->1.10 
+       libbb/my_getug.c:INITIAL->1.1 
+
+---------------------
+PatchSet 4384 
+Date: 2004/09/15 03:24:32
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Add a missing brace, patch by Hideki IWAMOTO
+
+Members: 
+       coreutils/stty.c:1.9->1.10 
+
+---------------------
+PatchSet 4385 
+Date: 2004/09/23 20:08:46
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Add some notes on how to make telnetd actually work
+
+Members: 
+       networking/Config.in:1.28->1.29 
+
+---------------------
+PatchSet 4386 
+Date: 2004/09/24 01:25:39
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+A bit of extra explanation regarding STANDALONE
+
+Members: 
+       shell/Config.in:1.16->1.17 
+
+---------------------
+PatchSet 4387 
+Date: 2004/09/24 02:04:13
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from David Daney to make the -i option work with -l.
+
+Members: 
+       coreutils/ls.c:1.111->1.112 
+
+---------------------
+PatchSet 4388 
+Date: 2004/09/24 02:36:44
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Remove this error message at Vodz request, it was misleading.
+
+Members: 
+       libbb/correct_password.c:1.4->1.5 
+
+---------------------
+PatchSet 4389 
+Date: 2004/09/24 09:09:44
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Fix a typo
+
+Members: 
+       shell/Config.in:1.17->1.18 
+
+---------------------
+PatchSet 4390 
+Date: 2004/09/24 09:18:55
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Egor Duda
+Attached patch prevents modprobe from trying to call 'insmod (null)'
+whenever nonexistent module is either passed to modprobe via command
+line or mentioned in modules.dep
+
+this replaces cryptic error
+sh: Syntax error: word unexpected (expecting ")")
+with
+modprobe: module some-module not found.
+
+egor.
+
+Members: 
+       modutils/modprobe.c:1.40->1.41 
+
+---------------------
+PatchSet 4391 
+Date: 2004/09/24 09:24:27
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Dmitry Zakharov to fix a bug triggered by freeswan's scripts.
+
+Members: 
+       editors/awk.c:1.10->1.11 
+
+---------------------
+PatchSet 4392 
+Date: 2004/09/30 00:24:21
+Author: bug1
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from William Barsse to fix a segfault when multiple files are specified.
+
+Members: 
+       coreutils/tail.c:1.47->1.48 
+
+---------------------
+PatchSet 4393 
+Date: 2004/10/07 00:35:59
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Make it more apparent that archive creation is not supported
+
+Members: 
+       archival/ar.c:1.49->1.50 
+
+---------------------
+PatchSet 4394 
+Date: 2004/10/08 07:21:58
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Michael Tokarev:
+
+Scenario:
+
+  touch x -- creates plain file name `x'
+  mkdir x -- exits successefully
+
+libbb/make_directory.c, bb_make_directory(), contains
+the following code:
+
+        if (mkdir(path, 0777) < 0) {
+            /* If we failed for any other reason than the directory
+             * already exists, output a diagnostic and return -1.*/
+            if (errno != EEXIST) {
+                fail_msg = "create";
+                umask(mask);
+                break;
+            }
+            /* Since the directory exists, don't attempt to change
+             * permissions if it was the full target.  Note that
+             * this is not an error conditon. */
+            if (!c) {
+                umask(mask);
+                return 0;
+            }
+        }
+
+The assumption that EEXIST error is due to that the *directory*
+already exists is wrong: any file type with that name will cause
+this error to be returned.  Proper way IMHO will be is to stat()
+the path and check whenever this is really a directory.  Below
+(attached) is a patch to fix this issue.
+
+Members: 
+       libbb/make_directory.c:1.15->1.16 
+
+---------------------
+PatchSet 4395 
+Date: 2004/10/08 07:45:08
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+egor duda writes:
+
+Hi!
+
+I've created a patch to busybox' build system to allow building it in
+separate tree in a manner similar to kbuild from kernel version 2.6.
+
+That is, one runs command like
+'make O=/build/some/where/for/specific/target/and/options'
+and everything is built in this exact directory, provided that it exists.
+
+I understand that applyingc such invasive changes during 'release
+candidates' stage of development is at best unwise. So, i'm currently
+asking for comments about this patch, starting from whether such thing
+is needed at all to whether it coded properly.
+
+'make check' should work now, and one make creates Makefile in build
+directory, so one can run 'make' in build directory after that.
+
+One possible caveat is that if we build in some directory other than
+source one, the source directory should be 'distclean'ed first.
+
+egor
+
+Members: 
+       Makefile:1.297->1.298 
+       Rules.mak:1.37->1.38 
+       applets/Makefile:1.5->1.6 
+       applets/Makefile.in:1.5->1.6 
+       archival/Makefile:1.7->1.8 
+       archival/Makefile.in:1.5->1.6 
+       archival/libunarchive/Makefile:1.5->1.6 
+       archival/libunarchive/Makefile.in:1.23->1.24 
+       console-tools/Makefile:1.4->1.5 
+       console-tools/Makefile.in:1.5->1.6 
+       coreutils/Makefile:1.3->1.4 
+       coreutils/Makefile.in:1.9->1.10 
+       coreutils/libcoreutils/Makefile:1.3->1.4 
+       coreutils/libcoreutils/Makefile.in:1.3->1.4 
+       debianutils/Makefile:1.3->1.4 
+       debianutils/Makefile.in:1.5->1.6 
+       editors/Makefile:1.4->1.5 
+       editors/Makefile.in:1.5->1.6 
+       findutils/Makefile:1.4->1.5 
+       findutils/Makefile.in:1.4->1.5 
+       init/Makefile:1.5->1.6 
+       init/Makefile.in:1.9->1.10 
+       libbb/Makefile:1.10->1.11 
+       libbb/Makefile.in:1.38->1.39 
+       libpwdgrp/Makefile:1.3->1.4 
+       libpwdgrp/Makefile.in:1.4->1.5 
+       loginutils/Makefile:1.3->1.4 
+       loginutils/Makefile.in:1.8->1.9 
+       miscutils/Makefile:1.7->1.8 
+       miscutils/Makefile.in:1.12->1.13 
+       modutils/Makefile:1.4->1.5 
+       modutils/Makefile.in:1.3->1.4 
+       networking/Makefile:1.7->1.8 
+       networking/Makefile.in:1.19->1.20 
+       networking/libiproute/Makefile:1.3->1.4 
+       networking/libiproute/Makefile.in:1.6->1.7 
+       networking/udhcp/Makefile:1.3->1.4 
+       networking/udhcp/Makefile.in:1.10->1.11 
+       procps/Makefile:1.4->1.5 
+       procps/Makefile.in:1.6->1.7 
+       scripts/config/Makefile:1.4->1.5 
+       shell/Makefile:1.4->1.5 
+       shell/Makefile.in:1.3->1.4 
+       sysklogd/Makefile:1.5->1.6 
+       sysklogd/Makefile.in:1.3->1.4 
+       testsuite/runtest:1.8->1.9 
+       testsuite/du/du-h-works:1.1->1.2 
+       testsuite/du/du-k-works:1.1->1.2 
+       testsuite/du/du-l-works:1.1->1.2 
+       testsuite/du/du-m-works:1.1->1.2 
+       testsuite/du/du-s-works:1.1->1.2 
+       testsuite/du/du-works:1.1->1.2 
+       testsuite/head/head-n-works:1.1->1.2 
+       testsuite/head/head-works:1.1->1.2 
+       testsuite/ls/ls-1-works:1.1->1.2 
+       testsuite/ls/ls-h-works:1.1->1.2 
+       testsuite/ls/ls-l-works:1.1->1.2 
+       testsuite/ls/ls-s-works:1.1->1.2 
+       testsuite/sort/sort-n-works:1.1->1.2 
+       testsuite/sort/sort-r-works:1.1->1.2 
+       testsuite/sort/sort-works:1.1->1.2 
+       testsuite/tail/tail-n-works:1.1->1.2 
+       testsuite/tail/tail-works:1.1->1.2 
+       testsuite/xargs/xargs-works:1.1->1.2 
+       util-linux/Makefile:1.6->1.7 
+       util-linux/Makefile.in:1.8->1.9 
+
+---------------------
+PatchSet 4396 
+Date: 2004/10/08 07:58:30
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+As noticed by egor duda, current_menu is declared as 'extern struct menu
+*current_menu;' in scripts/config/lkc.h line 63, and this conflicts with
+static definition in mconf.c.
+
+Members: 
+       scripts/config/mconf.c:1.5->1.6 
+
+---------------------
+PatchSet 4397 
+Date: 2004/10/08 08:03:29
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+last_patch139.gz from Vladimir N. Oleynik:
+
+>I also don't mean to disagree about leaving 30x status codes until after
+>1.0.  In fact, although redirecting http://host/dir to http://host/dir/
+>with a 301 is common practice (e.g. Apache, IIS), AFAIK it isn't
+>actually required (or mentioned) by the HTTP specs.
+
+Ok.
+Attached patch have 302 and 408 implemented features.
+
+
+--w
+vodz
+
+Members: 
+       networking/httpd.c:1.27->1.28 
+
+---------------------
+PatchSet 4398 
+Date: 2004/10/08 08:07:40
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Tito writes:
+
+Hi to all,
+This patch contains just some fixes for some misleading
+comments in my_getpwuid.c and my_getug.c.
+The code is untouched so this patch will not
+cause troubles.
+
+Please apply.
+
+Thanks in advance and Ciao,
+Tito
+
+Members: 
+       libbb/my_getpwuid.c:1.10->1.11 
+       libbb/my_getug.c:1.1->1.2 
+
+---------------------
+PatchSet 4399 
+Date: 2004/10/08 08:10:57
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Hiroshi Ito writes:
+
+    Hello
+
+    I'm using busy box on mipsel machine.
+
+    "grep -f file" will cause segmentation fault.
+
+Vladimir N. Oleynik writes:
+
+Hiroshi,
+
+Thank for bug report, but your patch is full broken.
+Worked patch attached.
+(really changes is zero initialize, and indent correcting).
+
+
+--w
+vodz
+
+Members: 
+       findutils/grep.c:1.85->1.86 
+
+---------------------
+PatchSet 4400 
+Date: 2004/10/08 08:14:58
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Hiroshi Ito writes:
+
+ash
+   "unset OLDPWD; cd -"  causes segmentation fault.
+    ( OLDPWD is not set when sh is invoked from getty. )
+
+patch against current CVS is attached.
+
+Members: 
+       shell/ash.c:1.106->1.107 
+
+---------------------
+PatchSet 4401 
+Date: 2004/10/08 08:17:39
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Hiroshi Ito writes:
+
+"kill -HUP 1" reloads inittab, and when I append one line to inittab
+and send HUP signal two times, It will starts 2 process.
+
+patch against current CVS is attached.
+
+Members: 
+       init/init.c:1.204->1.205 
+
+---------------------
+PatchSet 4402 
+Date: 2004/10/08 08:21:54
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Hiroshi Ito writes:
+
+Hello, all.
+
+Busybox init does not handle removed inittab entry correctly.
+
+# I'm sorry about my poor english, but you can find
+# what I would like to say from patch, isn't it?
+
+even if you apply this path,
+when yoy try to change a command line option in inittab,
+you have to do following steps.
+1. remove old line from initrd
+2. send HUP signal to init
+3. kill old proces which is invoked from init.
+4. append new line to inittab
+5. send HUP signal to init, again
+
+patch is against current CVS + last patch witch I send it last.
+
+Members: 
+       init/init.c:1.205->1.206 
+
+---------------------
+PatchSet 4403 
+Date: 2004/10/08 08:27:40
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Denis Vlasenko to fix a problem where
+wget http://1.2.3.4/abc/ loses last '/'
+
+Members: 
+       networking/wget.c:1.74->1.75 
+
+---------------------
+PatchSet 4404 
+Date: 2004/10/08 08:49:25
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Wade Berrier writes:
+
+Hello,
+
+Here's a patch for a first attempt at static leases for udhcpd.
+Included in the tarball are 2 files (static_leases.c, static_leases.h)
+and a patch against the latest cvs.
+
+In the config file you can configure static leases with the following
+format:
+
+static_lease 00:60:08:11:CE:4E 192.168.0.54
+static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+Comments/suggestions/improvements are welcome.
+
+
+Wade
+
+Members: 
+       examples/udhcp/udhcpd.conf:1.3->1.4 
+       networking/udhcp/Makefile.in:1.11->1.12 
+       networking/udhcp/dhcpd.c:1.6->1.7 
+       networking/udhcp/dhcpd.h:1.6->1.7 
+       networking/udhcp/files.c:1.14->1.15 
+       networking/udhcp/leases.c:1.6->1.7 
+       networking/udhcp/serverpacket.c:1.6->1.7 
+       networking/udhcp/static_leases.c:INITIAL->1.1 
+       networking/udhcp/static_leases.h:INITIAL->1.1 
+
+---------------------
+PatchSet 4405 
+Date: 2004/10/08 08:57:35
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from Claus Klein to increase, and make more apparent
+the hard coded limit on the number of mounts
+
+Members: 
+       libbb/mtab.c:1.5->1.6 
+
+---------------------
+PatchSet 4406 
+Date: 2004/10/08 09:43:34
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Fix CONFIG_ASH_MATH_SUPPORT_64 so it actually works
+
+Members: 
+       shell/ash.c:1.107->1.108 
+
+---------------------
+PatchSet 4407 
+Date: 2004/10/08 10:50:08
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Add an initial FAQ
+
+Members: 
+       docs/busybox.net/FAQ.html:INITIAL->1.1 
+       docs/busybox.net/header.html:1.8->1.9 
+
+---------------------
+PatchSet 4408 
+Date: 2004/10/08 10:52:08
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Fix the supported architectures section
+
+Members: 
+       README:1.36->1.37 
+
+---------------------
+PatchSet 4409 
+Date: 2004/10/08 10:52:33
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Bump version
+
+Members: 
+       Rules.mak:1.38->1.39 
+
+---------------------
+PatchSet 4410 
+Date: 2004/10/08 10:54:20
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+unmerged fix
+
+Members: 
+       docs/busybox.net/news.html:1.22->1.23 
+
+---------------------
+PatchSet 4411 
+Date: 2004/10/08 11:11:02
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+oops
+
+Members: 
+       docs/busybox.net/FAQ.html:1.1->1.2 
+
+---------------------
+PatchSet 4412 
+Date: 2004/10/11 20:52:16
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Patch from David Daney:
+
+It seems that date  -s MMDDHHMMYYYY.ss
+
+will ignore the .ss part.  This patch tries to fix the problem.
+
+David Daney.
+
+Members: 
+       coreutils/date.c:1.47->1.48 
+
+---------------------
+PatchSet 4413 
+Date: 2004/10/13 06:25:51
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Make certain clients of bb_make_directory default to honoring
+the user's umask
+
+Members: 
+       archival/libunarchive/data_extract_all.c:1.20->1.21 
+       libbb/make_directory.c:1.16->1.17 
+       miscutils/devfsd.c:1.9->1.10 
+
+---------------------
+PatchSet 4414 
+Date: 2004/10/13 07:18:05
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+Simon Poole writes:
+
+Erik,
+
+Attached is a patch for the udhcpc sample scripts, to correct the order in
+which routers are applied if the DHCP server provides more than one (as per
+section 3.5 of RFC2132).
+
+Apologies for not being on the mailing list and thanks for your continued
+efforts.
+
+Simon.
+
+Members: 
+       examples/udhcp/sample.bound:1.1->1.2 
+       examples/udhcp/sample.renew:1.1->1.2 
+       examples/udhcp/simple.script:1.1->1.2 
+
+---------------------
+PatchSet 4415 
+Date: 2004/10/13 07:25:01
+Author: andersen
+Branch: HEAD
+Tag: (none) 
+Log:
+return failure when nslookup fails
+
+Members: 
+       networking/nslookup.c:1.32->1.33 
+
diff --git a/busybox/INSTALL b/busybox/INSTALL
new file mode 100644 (file)
index 0000000..c9cdf8e
--- /dev/null
@@ -0,0 +1,20 @@
+1) Run 'make config' or 'make menuconfig' and select the
+   functionality that you wish to enable.
+
+2) Run 'make dep'
+
+3) Check the Makefile for any Makefile setting you wish
+    to adjust for your system (things like like setting
+    your cross compiler, adjusting optimizations, etc)
+
+4) Run 'make'
+
+5) Go get a drink of water, drink a soda, visit the bathroom,
+    or whatever while it compiles.  It doesn't take very
+    long to compile, so you don't really need to waste too
+    much time waiting...
+
+6) Run 'make install' or 'make PREFIX=/target install' to
+    install busybox and all the needed links.  Some people
+    will prefer to install using hardlinks and will instead
+    want to run 'make install-hardlinks'....
diff --git a/busybox/LICENSE b/busybox/LICENSE
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/busybox/Makefile b/busybox/Makefile
new file mode 100644 (file)
index 0000000..3e2b3ef
--- /dev/null
@@ -0,0 +1,315 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+#--------------------------------------------------------------
+# You shouldn't need to mess with anything beyond this point...
+#--------------------------------------------------------------
+noconfig_targets := menuconfig config oldconfig randconfig \
+       defconfig allyesconfig allnoconfig clean distclean \
+       release tags  
+
+ifndef TOPDIR
+TOPDIR=$(CURDIR)/
+endif
+ifndef top_srcdir
+top_srcdir=$(CURDIR)
+endif
+ifndef top_builddir
+top_builddir=$(CURDIR)
+endif
+
+srctree=$(top_srcdir)
+vpath %/Config.in $(srctree)
+
+include $(top_builddir)/Rules.mak
+
+DIRS:=applets archival archival/libunarchive coreutils console-tools \
+       debianutils editors findutils init miscutils modutils networking \
+       networking/libiproute networking/udhcp procps loginutils shell \
+       sysklogd util-linux libpwdgrp coreutils/libcoreutils libbb
+
+SRC_DIRS:=$(patsubst %,$(top_srcdir)/%,$(DIRS))
+
+ifeq ($(strip $(CONFIG_SELINUX)),y)
+CFLAGS += -I/usr/include/selinux
+LIBRARIES += -lsecure
+endif
+
+CONFIG_CONFIG_IN = $(top_srcdir)/sysdeps/$(TARGET_OS)/Config.in
+CONFIG_DEFCONFIG = $(top_srcdir)/sysdeps/$(TARGET_OS)/defconfig
+
+ALL_DIRS:= $(DIRS) scripts/config
+ALL_MAKEFILES:=$(patsubst %,%/Makefile,$(ALL_DIRS))
+
+ifeq ($(KBUILD_SRC),)
+
+ifdef O
+  ifeq ("$(origin O)", "command line")
+    KBUILD_OUTPUT := $(O)
+  endif
+endif
+
+# That's our default target when none is given on the command line
+.PHONY: _all
+_all:
+
+ifneq ($(KBUILD_OUTPUT),)
+# Invoke a second make in the output directory, passing relevant variables
+# check that the output directory actually exists
+saved-output := $(KBUILD_OUTPUT)
+KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)
+$(if $(wildcard $(KBUILD_OUTPUT)),, \
+     $(error output directory "$(saved-output)" does not exist))
+
+.PHONY: $(MAKECMDGOALS)
+
+$(filter-out _all,$(MAKECMDGOALS)) _all: $(KBUILD_OUTPUT)/Rules.mak $(KBUILD_OUTPUT)/Makefile
+       $(MAKE) -C $(KBUILD_OUTPUT) \
+       top_srcdir=$(CURDIR) \
+       top_builddir=$(KBUILD_OUTPUT) \
+       TOPDIR=$(KBUILD_OUTPUT) \
+       KBUILD_SRC=$(CURDIR) \
+       -f $(CURDIR)/Makefile $@
+
+$(KBUILD_OUTPUT)/Rules.mak:
+       @echo > $@
+       @echo top_srcdir=$(CURDIR) >> $@
+       @echo top_builddir=$(KBUILD_OUTPUT) >> $@
+       @echo include $(top_srcdir)/Rules.mak >> $@
+
+$(KBUILD_OUTPUT)/Makefile:
+       @echo > $@
+       @echo top_srcdir=$(CURDIR) >> $@
+       @echo top_builddir=$(KBUILD_OUTPUT) >> $@
+       @echo KBUILD_SRC='$$(top_srcdir)' >> $@
+       @echo include '$$(KBUILD_SRC)'/Makefile >> $@
+
+# Leave processing to above invocation of make
+skip-makefile := 1
+endif # ifneq ($(KBUILD_OUTPUT),)
+endif # ifeq ($(KBUILD_SRC),)
+
+ifeq ($(skip-makefile),)
+
+_all: all
+
+ifeq ($(strip $(HAVE_DOT_CONFIG)),y)
+
+all: busybox busybox.links doc
+
+all_tree:      $(ALL_MAKEFILES)
+
+$(ALL_MAKEFILES): %/Makefile: $(top_srcdir)/%/Makefile
+       d=`dirname $@`; [ -d "$$d" ] || mkdir -p "$$d"; cp $< $@
+
+# In this section, we need .config
+-include $(top_builddir)/.config.cmd
+include $(patsubst %,%/Makefile.in, $(SRC_DIRS))
+-include $(top_builddir)/.depend
+
+busybox: $(ALL_MAKEFILES) .depend include/config.h $(libraries-y)
+       $(CC) $(LDFLAGS) -o $@ -Wl,--start-group $(libraries-y) $(LIBRARIES) -Wl,--end-group
+       $(STRIPCMD) $@
+
+busybox.links: $(top_srcdir)/applets/busybox.mkll include/config.h $(top_srcdir)/include/applets.h
+       - $(SHELL) $^ >$@
+
+install: applets/install.sh busybox busybox.links
+       $(SHELL) $< $(PREFIX)
+ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
+       @echo
+       @echo
+       @echo --------------------------------------------------
+       @echo You will probably need to make your busybox binary
+       @echo setuid root to ensure all configured applets will
+       @echo work properly.
+       @echo --------------------------------------------------
+       @echo
+endif
+
+uninstall: busybox.links
+       rm -f $(PREFIX)/bin/busybox
+       for i in `cat busybox.links` ; do rm -f $(PREFIX)$$i; done
+
+install-hardlinks: applets/install.sh busybox busybox.links
+       $(SHELL) $< $(PREFIX) --hardlinks
+
+check: busybox
+       bindir=$(top_builddir) srcdir=$(top_srcdir)/testsuite \
+       $(top_srcdir)/testsuite/runtest
+
+# Documentation Targets
+doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
+
+docs/busybox.pod : $(top_srcdir)/docs/busybox_header.pod $(top_srcdir)/include/usage.h $(top_srcdir)/docs/busybox_footer.pod
+       -mkdir -p docs
+       - ( cat $(top_srcdir)/docs/busybox_header.pod; \
+           $(top_srcdir)/docs/autodocifier.pl $(top_srcdir)/include/usage.h; \
+           cat $(top_srcdir)/docs/busybox_footer.pod ) > docs/busybox.pod
+
+docs/BusyBox.txt: docs/busybox.pod
+       @echo
+       @echo BusyBox Documentation
+       @echo
+       -mkdir -p docs
+       -pod2text $< > $@
+
+docs/BusyBox.1: docs/busybox.pod
+       - mkdir -p docs
+       - pod2man --center=BusyBox --release="version $(VERSION)" \
+               $< > $@
+
+docs/BusyBox.html: docs/busybox.net/BusyBox.html
+       - mkdir -p docs
+       -@ rm -f docs/BusyBox.html
+       -@ cp docs/busybox.net/BusyBox.html docs/BusyBox.html
+
+docs/busybox.net/BusyBox.html: docs/busybox.pod
+       -@ mkdir -p docs/busybox.net
+       -  pod2html --noindex $< > \
+           docs/busybox.net/BusyBox.html
+       -@ rm -f pod2htm*
+
+# The nifty new buildsystem stuff
+scripts/mkdep: $(top_srcdir)/scripts/mkdep.c
+       $(HOSTCC) $(HOSTCFLAGS) -o $@ $<
+
+scripts/split-include: $(top_srcdir)/scripts/split-include.c
+       $(HOSTCC) $(HOSTCFLAGS) -o $@ $<
+
+.depend: scripts/mkdep
+       rm -f .depend .hdepend;
+       mkdir -p include/config;
+       scripts/mkdep -I include -- \
+         `find $(top_srcdir) -name \*.c -print | sed -e "s,^./,,"` >> .depend;
+       scripts/mkdep -I include -- \
+         `find $(top_srcdir) -name \*.h -print | sed -e "s,^./,,"` >> .hdepend;
+
+depend dep: include/config.h .depend
+
+include/config/MARKER: depend scripts/split-include
+       scripts/split-include include/config.h include/config
+       @ touch include/config/MARKER
+
+include/config.h: .config
+       @if [ ! -x $(top_builddir)/scripts/config/conf ] ; then \
+           $(MAKE) -C scripts/config conf; \
+       fi;
+       @$(top_builddir)/scripts/config/conf -o $(CONFIG_CONFIG_IN)
+
+finished2:
+       @echo
+       @echo Finished installing...
+       @echo
+
+else # ifeq ($(strip $(HAVE_DOT_CONFIG)),y)
+
+all: menuconfig
+
+# configuration
+# ---------------------------------------------------------------------------
+
+$(ALL_MAKEFILES): %/Makefile: $(top_srcdir)/%/Makefile
+       d=`dirname $@`; [ -d "$$d" ] || mkdir -p "$$d"; cp $< $@
+
+scripts/config/conf: scripts/config/Makefile Rules.mak
+       $(MAKE) -C scripts/config conf
+       -@if [ ! -f .config ] ; then \
+               cp $(CONFIG_DEFCONFIG) .config; \
+       fi
+
+scripts/config/mconf: scripts/config/Makefile Rules.mak
+       $(MAKE) -C scripts/config ncurses conf mconf
+       -@if [ ! -f .config ] ; then \
+               cp $(CONFIG_DEFCONFIG) .config; \
+       fi
+
+menuconfig: scripts/config/mconf
+       @./scripts/config/mconf $(CONFIG_CONFIG_IN)
+
+config: scripts/config/conf
+       @./scripts/config/conf $(CONFIG_CONFIG_IN)
+
+oldconfig: scripts/config/conf
+       @./scripts/config/conf -o $(CONFIG_CONFIG_IN)
+
+randconfig: scripts/config/conf
+       @./scripts/config/conf -r $(CONFIG_CONFIG_IN)
+
+allyesconfig: scripts/config/conf
+       @./scripts/config/conf -y $(CONFIG_CONFIG_IN)
+       sed -i -e "s/^CONFIG_DEBUG.*/# CONFIG_DEBUG is not set/" .config
+       sed -i -e "s/^USING_CROSS_COMPILER.*/# USING_CROSS_COMPILER is not set/" .config
+       sed -i -e "s/^CONFIG_STATIC.*/# CONFIG_STATIC is not set/" .config
+       sed -i -e "s/^CONFIG_SELINUX.*/# CONFIG_SELINUX is not set/" .config
+       @./scripts/config/conf -o $(CONFIG_CONFIG_IN)
+
+allnoconfig: scripts/config/conf
+       @./scripts/config/conf -n $(CONFIG_CONFIG_IN)
+
+defconfig: scripts/config/conf
+       @./scripts/config/conf -d $(CONFIG_CONFIG_IN)
+
+clean:
+       - rm -f docs/busybox.dvi docs/busybox.ps \
+           docs/busybox.pod docs/busybox.net/busybox.html \
+           docs/busybox pod2htm* *.gdb *.elf *~ core .*config.log \
+           docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html \
+           docs/busybox.net/BusyBox.html busybox.links libbb/loop.h \
+           .config.old .hdepend busybox
+       - rm -rf _install
+       - find . -name .\*.flags -exec rm -f {} \;
+       - find . -name \*.o -exec rm -f {} \;
+       - find . -name \*.a -exec rm -f {} \;
+
+distclean: clean
+       - rm -f scripts/split-include scripts/mkdep
+       - rm -rf include/config include/config.h
+       - find . -name .depend -exec rm -f {} \;
+       rm -f .config .config.old .config.cmd
+       - $(MAKE) -C scripts/config clean
+
+release: distclean #doc
+       cd ..;                                  \
+       rm -rf $(PROG)-$(VERSION);              \
+       cp -a busybox $(PROG)-$(VERSION);       \
+                                               \
+       find $(PROG)-$(VERSION)/ -type d        \
+                                -name CVS      \
+                                -print         \
+               -exec rm -rf {} \; ;            \
+                                               \
+       find $(PROG)-$(VERSION)/ -type f        \
+                                -name .\#*     \
+                                -print         \
+               -exec rm -f {} \;  ;            \
+                                               \
+       tar -cvzf $(PROG)-$(VERSION).tar.gz $(PROG)-$(VERSION)/;
+
+tags:
+       ctags -R .
+
+
+endif # ifeq ($(strip $(HAVE_DOT_CONFIG)),y)
+
+endif # ifeq ($(skip-makefile),)
+
+.PHONY: dummy subdirs release distclean clean config oldconfig \
+       menuconfig tags check test depend buildtree
+
diff --git a/busybox/README b/busybox/README
new file mode 100644 (file)
index 0000000..bf2ae6f
--- /dev/null
@@ -0,0 +1,127 @@
+Please see the LICENSE file for details on copying and usage.
+
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides minimalist replacements for most of the utilities
+you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox
+generally have fewer options than their full-featured GNU cousins; however, the
+options that are included provide the expected functionality and behave very
+much like their GNU counterparts.
+
+BusyBox has been written with size-optimization and limited resources in mind.
+It is also extremely modular so you can easily include or exclude commands (or
+features) at compile time. This makes it easy to customize your embedded
+systems. To create a working system, just add /dev, /etc, and a Linux kernel.
+BusyBox provides a fairly complete POSIX environment for any small or embedded
+system.
+
+BusyBox is extremely configurable.  This allows you to include only the
+components you need, thereby reducing binary size. Run 'make config' or
+'make menuconfig' to select the functionality that you wish to enable.
+
+After the build is complete, a busybox.links file is generated.  This is
+used by 'make install' to create symlinks to the BusyBox binary for all
+compiled in functions.  By default, 'make install' will place the symlink
+forest into `pwd`/_install unless you have defined the PREFIX environment
+variable (i.e., 'make PREFIX=/tmp/foo install')
+
+If you wish to install hard links, rather than symlinks, you can use
+'make PREFIX=/tmp/foo install-hardlinks' instead.
+
+----------------
+
+Supported architectures:
+
+   BusyBox in general will build on any architecture supported by gcc.
+   Kernel module loading for 2.2 and 2.4 Linux kernels is currently
+   limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC,
+   S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels.  For 2.6.x
+   kernels, kernel module loading support should work on all architectures.
+
+
+Supported C Libraries:
+
+   uClibc and glibc are supported.  People have been looking at newlib and
+   dietlibc, but they are currently considered unsupported, untested, or
+   worse.  Linux-libc5 is no longer supported -- you should probably use uClibc
+   instead if you want a small C library.
+
+Supported kernels:
+
+   Full functionality requires Linux 2.2.x or better.  A large fraction of the
+   code should run on just about anything.  While the current code is fairly
+   Linux specific, it should be fairly easy to port the majority of the code
+   to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you
+   are into that sort of thing).
+
+----------------
+
+Getting help:
+
+When you find you need help, you can check out the BusyBox mailing list
+archives at http://busybox.net/lists/busybox/ or even join
+the mailing list if you are interested.
+
+----------------
+
+Bugs:
+
+If you find bugs, please submit a detailed bug report to the BusyBox mailing
+list at busybox@mail.busybox.net.  A well-written bug report should include a
+transcript of a shell session that demonstrates the bad behavior and enables
+anyone else to duplicate the bug on their own machine. The following is such
+an example:
+
+    To: busybox@mail.busybox.net
+    From: diligent@testing.linux.org
+    Subject: /bin/date doesn't work
+
+    Package: BusyBox
+    Version: 1.00
+
+    When I execute BusyBox 'date' it produces unexpected results.
+    With GNU date I get the following output:
+
+       $ date
+       Fri Oct  8 14:19:41 MDT 2004
+
+    But when I use BusyBox date I get this instead:
+
+       $ date
+       illegal instruction
+
+    I am using Debian unstable, kernel version 2.4.25-vrs2 on a Netwinder,
+    and the latest uClibc from CVS.  Thanks for the wonderful program!
+
+       -Diligent
+
+Note the careful description and use of examples showing not only what BusyBox
+does, but also a counter example showing what an equivalent GNU app does.  Bug
+reports lacking such detail may never be fixed...  Thanks for understanding.
+
+----------------
+
+Downloads:
+
+Source for the latest released version, as well as daily snapshots, can always
+be downloaded from
+    http://busybox.net/downloads/
+
+----------------
+
+CVS:
+
+BusyBox now has its own publicly browsable CVS tree at:
+    http://busybox.net/cgi-bin/cvsweb/busybox/
+
+Anonymous CVS access is available.  For instructions, check out:
+    http://busybox.net/cvs_anon.html
+
+For those that are actively contributing there is even CVS write access:
+    http://busybox.net/cvs_write.html
+
+----------------
+
+Please feed suggestions, bug reports, insults, and bribes back to:
+       Erik Andersen
+       <andersen@codepoet.org>
+
diff --git a/busybox/Rules.mak b/busybox/Rules.mak
new file mode 100644 (file)
index 0000000..d04d4b9
--- /dev/null
@@ -0,0 +1,196 @@
+# Rules.make for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+#--------------------------------------------------------
+PROG      := busybox
+VERSION   := 1.00
+BUILDTIME := $(shell TZ=UTC date -u "+%Y.%m.%d-%H:%M%z")
+
+
+#--------------------------------------------------------
+# With a modern GNU make(1) (highly recommended, that's what all the
+# developers use), all of the following configuration values can be
+# overridden at the command line.  For example:
+#   make CROSS=powerpc-linux- BB_SRC_DIR=$HOME/busybox PREFIX=/mnt/app
+#--------------------------------------------------------
+
+# If you are running a cross compiler, you will want to set 'CROSS'
+# to something more interesting...  Target architecture is determined
+# by asking the CC compiler what arch it compiles things for, so unless
+# your compiler is broken, you should not need to specify TARGET_ARCH
+CROSS           =$(subst ",, $(strip $(CROSS_COMPILER_PREFIX)))
+CC             = $(CROSS)gcc
+AR             = $(CROSS)ar
+AS             = $(CROSS)as
+LD             = $(CROSS)ld
+NM             = $(CROSS)nm
+STRIP          = $(CROSS)strip
+CPP            = $(CC) -E
+# MAKEFILES      = $(top_builddir)/.config
+
+# What OS are you compiling busybox for?  This allows you to include
+# OS specific things, syscall overrides, etc.
+TARGET_OS=linux
+
+# Select the compiler needed to build binaries for your development system
+HOSTCC    = gcc
+HOSTCFLAGS= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
+
+# Ensure consistent sort order, 'gcc -print-search-dirs' behavior, etc.
+LC_ALL:= C
+
+# If you want to add some simple compiler switches (like -march=i686),
+# especially from the command line, use this instead of CFLAGS directly.
+# For optimization overrides, it's better still to set OPTIMIZATION.
+CFLAGS_EXTRA=$(subst ",, $(strip $(EXTRA_CFLAGS_OPTIONS)))
+
+# If you have a "pristine" source directory, point BB_SRC_DIR to it.
+# Experimental and incomplete; tell the mailing list
+# <busybox@busybox.net> if you do or don't like it so far.
+BB_SRC_DIR=
+
+# To compile vs some other alternative libc, you may need to use/adjust
+# the following lines to meet your needs...
+#
+# If you are using Red Hat 6.x with the compatible RPMs (for developing under
+# Red Hat 5.x and glibc 2.0) uncomment the following.  Be sure to read about
+# using the compatible RPMs (compat-*) at http://www.redhat.com !
+#LIBCDIR:=/usr/i386-glibc20-linux
+#
+# For other libraries, you are on your own.  But these may (or may not) help...
+#LDFLAGS+=-nostdlib
+#LIBRARIES:=$(LIBCDIR)/lib/libc.a -lgcc
+#CROSS_CFLAGS+=-nostdinc -I$(LIBCDIR)/include -I$(GCCINCDIR)
+#GCCINCDIR:=$(shell gcc -print-search-dirs | sed -ne "s/install: \(.*\)/\1include/gp")
+
+WARNINGS=-Wall -Wstrict-prototypes -Wshadow
+CFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include -I$(srcdir)
+ARFLAGS=-r
+
+#--------------------------------------------------------
+export VERSION BUILDTIME TOPDIR HOSTCC HOSTCFLAGS CROSS CC AR AS LD NM STRIP CPP
+ifeq ($(strip $(TARGET_ARCH)),)
+TARGET_ARCH=$(shell $(CC) -dumpmachine | sed -e s'/-.*//' \
+               -e 's/i.86/i386/' \
+               -e 's/sparc.*/sparc/' \
+               -e 's/arm.*/arm/g' \
+               -e 's/m68k.*/m68k/' \
+               -e 's/ppc/powerpc/g' \
+               -e 's/v850.*/v850/g' \
+               -e 's/sh[234]/sh/' \
+               -e 's/mips-.*/mips/' \
+               -e 's/mipsel-.*/mipsel/' \
+               -e 's/cris.*/cris/' \
+               )
+endif
+
+# Pull in the user's busybox configuration
+ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),)
+-include $(top_builddir)/.config
+endif
+
+# A nifty macro to make testing gcc features easier
+check_gcc=$(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \
+       then echo "$(1)"; else echo "$(2)"; fi)
+
+#--------------------------------------------------------
+# Arch specific compiler optimization stuff should go here.
+# Unless you want to override the defaults, do not set anything
+# for OPTIMIZATION...
+
+# use '-Os' optimization if available, else use -O2
+OPTIMIZATION=
+OPTIMIZATION=${call check_gcc,-Os,-O2}
+
+# Some nice architecture specific optimizations
+ifeq ($(strip $(TARGET_ARCH)),arm)
+       OPTIMIZATION+=-fstrict-aliasing
+endif
+ifeq ($(strip $(TARGET_ARCH)),i386)
+       OPTIMIZATION+=$(call check_gcc,-march=i386,)
+       OPTIMIZATION+=$(call check_gcc,-mpreferred-stack-boundary=2,)
+       OPTIMIZATION+=$(call check_gcc,-falign-functions=0 -falign-jumps=0 -falign-loops=0,\
+               -malign-functions=0 -malign-jumps=0 -malign-loops=0)
+endif
+OPTIMIZATIONS=$(OPTIMIZATION) -fomit-frame-pointer
+
+#
+#--------------------------------------------------------
+# If you're going to do a lot of builds with a non-vanilla configuration,
+# it makes sense to adjust parameters above, so you can type "make"
+# by itself, instead of following it by the same half-dozen overrides
+# every time.  The stuff below, on the other hand, is probably less
+# prone to casual user adjustment.
+#
+
+ifeq ($(strip $(CONFIG_LFS)),y)
+    # For large file summit support
+    CFLAGS+=-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+endif
+ifeq ($(strip $(CONFIG_DMALLOC)),y)
+    # For testing mem leaks with dmalloc
+    CFLAGS+=-DDMALLOC
+    LIBRARIES:=-ldmalloc
+else
+    ifeq ($(strip $(CONFIG_EFENCE)),y)
+       LIBRARIES:=-lefence
+    endif
+endif
+ifeq ($(strip $(CONFIG_DEBUG)),y)
+    CFLAGS  +=$(WARNINGS) -g -D_GNU_SOURCE
+    LDFLAGS +=-Wl,-warn-common
+    STRIPCMD:=/bin/true -Not_stripping_since_we_are_debugging
+else
+    CFLAGS+=$(WARNINGS) $(OPTIMIZATIONS) -D_GNU_SOURCE -DNDEBUG
+    LDFLAGS += -s -Wl,-warn-common
+    STRIPCMD:=$(STRIP) --remove-section=.note --remove-section=.comment
+endif
+ifeq ($(strip $(CONFIG_STATIC)),y)
+    LDFLAGS += --static
+endif
+
+ifeq ($(strip $(PREFIX)),)
+    PREFIX:=`pwd`/_install
+endif
+
+# Additional complications due to support for pristine source dir.
+# Include files in the build directory should take precedence over
+# the copy in BB_SRC_DIR, both during the compilation phase and the
+# shell script that finds the list of object files.
+# Work in progress by <ldoolitt@recycle.lbl.gov>.
+#
+ifneq ($(strip $(BB_SRC_DIR)),)
+    VPATH:=$(BB_SRC_DIR)
+endif
+
+OBJECTS:=$(APPLET_SOURCES:.c=.o) busybox.o usage.o applets.o
+CFLAGS    += $(CROSS_CFLAGS)
+ifdef BB_INIT_SCRIPT
+    CFLAGS += -DINIT_SCRIPT='"$(BB_INIT_SCRIPT)"'
+endif
+
+# Put user-supplied flags at the end, where they
+# have a chance of winning.
+CFLAGS += $(CFLAGS_EXTRA)
+
+.PHONY: dummy
+
+
+.EXPORT_ALL_VARIABLES:
+
diff --git a/busybox/TODO b/busybox/TODO
new file mode 100644 (file)
index 0000000..ffffd4f
--- /dev/null
@@ -0,0 +1,11 @@
+Busybox TODO
+
+Stuff that needs to be done
+
+----
+tr - missing SuS3 features in busybox 1.0pre10 
+
+tr doesnt support [:blank:], [:digit:] or other predefined classes, [=equiv=]
+support is also missing.
+----
diff --git a/busybox/applets/Makefile b/busybox/applets/Makefile
new file mode 100644 (file)
index 0000000..b566e4d
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/applets
+APPLETS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir).depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/applets/Makefile.in b/busybox/applets/Makefile.in
new file mode 100644 (file)
index 0000000..e31bb6f
--- /dev/null
@@ -0,0 +1,37 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+APPLETS_AR:=applets.a
+ifndef $(APPLETS_DIR)
+APPLETS_DIR:=$(top_builddir)/applets/
+endif
+srcdir=$(top_srcdir)/applets
+
+APPLET_SRC:=applets.c busybox.c
+APPLET_OBJ:= $(patsubst %.c,$(APPLETS_DIR)%.o, $(APPLET_SRC))
+
+libraries-y+=$(APPLETS_DIR)$(APPLETS_AR)
+
+$(APPLETS_DIR)$(APPLETS_AR): $(APPLET_OBJ)
+       $(AR) -ro $@ $(APPLET_OBJ)
+
+$(APPLET_OBJ): $(top_builddir)/.config
+$(APPLET_OBJ): $(APPLETS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/applets/applets.c b/busybox/applets/applets.c
new file mode 100644 (file)
index 0000000..9db16b4
--- /dev/null
@@ -0,0 +1,512 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) tons of folks.  Tracking down who wrote what
+ * isn't something I'm going to worry about...  If you wrote something
+ * here, please feel free to acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "busybox.h"
+
+const char usage_messages[] =
+
+#define MAKE_USAGE
+#include "usage.h"
+
+#include "applets.h"
+
+;
+
+#undef MAKE_USAGE
+#undef APPLET
+#undef APPLET_NOUSAGE
+#undef PROTOTYPES
+#include "applets.h"
+
+
+static struct BB_applet *applet_using;
+
+/* The -1 arises because of the {0,NULL,0,-1} entry above. */
+const size_t NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet) - 1);
+
+
+#ifdef CONFIG_FEATURE_SUID
+
+static void check_suid (struct BB_applet *app);
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include "pwd_.h"
+#include "grp_.h"
+
+static void parse_config_file (void);
+
+#define CONFIG_FILE "/etc/busybox.conf"
+
+/* applets [] is const, so we have to define this "override" structure */
+struct BB_suid_config
+{
+  struct BB_applet *m_applet;
+
+  uid_t m_uid;
+  gid_t m_gid;
+  mode_t m_mode;
+
+  struct BB_suid_config *m_next;
+};
+
+static struct BB_suid_config *suid_config;
+static int suid_cfg_readable;
+
+#endif /* CONFIG_FEATURE_SUID_CONFIG */
+
+#endif /* CONFIG_FEATURE_SUID */
+
+
+
+extern void
+bb_show_usage (void)
+{
+  const char *format_string;
+  const char *usage_string = usage_messages;
+  int i;
+
+  for (i = applet_using - applets; i > 0;) {
+       if (!*usage_string++) {
+         --i;
+       }
+  }
+
+  format_string = "%s\n\nUsage: %s %s\n\n";
+  if (*usage_string == '\b')
+       format_string = "%s\n\nNo help available.\n\n";
+  fprintf (stderr, format_string, bb_msg_full_version, applet_using->name,
+                  usage_string);
+
+  exit (EXIT_FAILURE);
+}
+
+static int
+applet_name_compare (const void *x, const void *y)
+{
+  const char *name = x;
+  const struct BB_applet *applet = y;
+
+  return strcmp (name, applet->name);
+}
+
+extern const size_t NUM_APPLETS;
+
+struct BB_applet *
+find_applet_by_name (const char *name)
+{
+  return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet),
+                                 applet_name_compare);
+}
+
+void
+run_applet_by_name (const char *name, int argc, char **argv)
+{
+  static int recurse_level = 0;
+  extern int been_there_done_that;      /* From busybox.c */
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+  if (recurse_level == 0)
+       parse_config_file ();
+#endif
+
+  recurse_level++;
+  /* Do a binary search to find the applet entry given the name. */
+  if ((applet_using = find_applet_by_name (name)) != NULL) {
+       bb_applet_name = applet_using->name;
+       if (argv[1] && strcmp (argv[1], "--help") == 0) {
+         if (strcmp (applet_using->name, "busybox") == 0) {
+               if (argv[2])
+                 applet_using = find_applet_by_name (argv[2]);
+               else
+                 applet_using = NULL;
+         }
+         if (applet_using)
+               bb_show_usage ();
+         been_there_done_that = 1;
+         busybox_main (0, NULL);
+       }
+#ifdef CONFIG_FEATURE_SUID
+       check_suid (applet_using);
+#endif
+
+       exit ((*(applet_using->main)) (argc, argv));
+  }
+  /* Just in case they have renamed busybox - Check argv[1] */
+  if (recurse_level == 1) {
+       run_applet_by_name ("busybox", argc, argv);
+  }
+  recurse_level--;
+}
+
+
+#ifdef CONFIG_FEATURE_SUID
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+
+/* check if u is member of group g */
+static int
+ingroup (uid_t u, gid_t g)
+{
+  struct group *grp = getgrgid (g);
+
+  if (grp) {
+       char **mem;
+
+       for (mem = grp->gr_mem; *mem; mem++) {
+         struct passwd *pwd = getpwnam (*mem);
+
+         if (pwd && (pwd->pw_uid == u))
+               return 1;
+       }
+  }
+  return 0;
+}
+
+#endif
+
+
+void
+check_suid (struct BB_applet *applet)
+{
+  uid_t ruid = getuid ();               /* real [ug]id */
+  uid_t rgid = getgid ();
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+  if (suid_cfg_readable) {
+       struct BB_suid_config *sct;
+
+       for (sct = suid_config; sct; sct = sct->m_next) {
+         if (sct->m_applet == applet)
+               break;
+       }
+       if (sct) {
+         mode_t m = sct->m_mode;
+
+         if (sct->m_uid == ruid)       /* same uid */
+               m >>= 6;
+         else if ((sct->m_gid == rgid) || ingroup (ruid, sct->m_gid))  /* same group / in group */
+               m >>= 3;
+
+         if (!(m & S_IXOTH))           /* is x bit not set ? */
+               bb_error_msg_and_die ("You have no permission to run this applet!");
+
+         if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {     /* *both* have to be set for sgid */
+               if (setegid (sct->m_gid))
+                 bb_error_msg_and_die
+                       ("BusyBox binary has insufficient rights to set proper GID for applet!");
+         } else
+               setgid (rgid);                  /* no sgid -> drop */
+
+         if (sct->m_mode & S_ISUID) {
+               if (seteuid (sct->m_uid))
+                 bb_error_msg_and_die
+                       ("BusyBox binary has insufficient rights to set proper UID for applet!");
+         } else
+               setuid (ruid);                  /* no suid -> drop */
+       } else {
+               /* default: drop all priviledges */
+         setgid (rgid);
+         setuid (ruid);
+       }
+       return;
+  } else {
+#ifndef CONFIG_FEATURE_SUID_CONFIG_QUIET
+       static int onetime = 0;
+
+       if (!onetime) {
+         onetime = 1;
+         fprintf (stderr, "Using fallback suid method\n");
+       }
+#endif
+  }
+#endif
+
+  if (applet->need_suid == _BB_SUID_ALWAYS) {
+       if (geteuid () != 0)
+         bb_error_msg_and_die ("This applet requires root priviledges!");
+  } else if (applet->need_suid == _BB_SUID_NEVER) {
+       setgid (rgid);                          /* drop all priviledges */
+       setuid (ruid);
+  }
+}
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+
+/* This should probably be a libbb routine.  In that case,
+ * I'd probably rename it to something like bb_trimmed_slice.
+ */
+static char *get_trimmed_slice(char *s, char *e)
+{
+       /* First, consider the value at e to be nul and back up until we
+        * reach a non-space char.  Set the char after that (possibly at
+        * the original e) to nul. */
+       while (e-- > s) {
+               if (!isspace(*e)) {
+                       break;
+               }
+       }
+       e[1] = 0;
+
+       /* Next, advance past all leading space and return a ptr to the
+        * first non-space char; possibly the terminating nul. */
+       return (char *) bb_skip_whitespace(s);
+}
+
+
+#define parse_error(x)  { err=x; goto pe_label; }
+
+/* Don't depend on the tools to combine strings. */
+static const char config_file[] = CONFIG_FILE;
+
+/* There are 4 chars + 1 nul for each of user/group/other. */
+static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-";
+
+/* We don't supply a value for the nul, so an index adjustment is
+ * necessary below.  Also, we use unsigned short here to save some
+ * space even though these are really mode_t values. */
+static const unsigned short mode_mask[] = {
+       /*  SST         sst                 xxx   --- */
+       S_ISUID,    S_ISUID|S_IXUSR,    S_IXUSR,    0,  /* user */
+       S_ISGID,    S_ISGID|S_IXGRP,    S_IXGRP,    0,  /* group */
+       0,          S_IXOTH,            S_IXOTH,    0   /* other */
+};
+
+static void parse_config_file(void)
+{
+       struct BB_suid_config *sct_head;
+       struct BB_suid_config *sct;
+       struct BB_applet *applet;
+       FILE *f;
+       char *err;
+       char *s;
+       char *e;
+       int i, lc, section;
+       char buffer[256];
+       struct stat st;
+
+       assert(!suid_config);           /* Should be set to NULL by bss init. */
+
+       if ((stat(config_file, &st) != 0)                       /* No config file? */
+               || !S_ISREG(st.st_mode)                                 /* Not a regular file? */
+               || (st.st_uid != 0)                                             /* Not owned by root? */
+               || (st.st_mode & (S_IWGRP | S_IWOTH))   /* Writable by non-root? */
+               || !(f = fopen(config_file, "r"))               /* Can not open? */
+               ) {
+               return;
+       }
+
+       suid_cfg_readable = 1;
+       sct_head = NULL;
+       section = lc = 0;
+
+       do {
+               s = buffer;
+
+               if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
+                       if (ferror(f)) {   /* Make sure it wasn't a read error. */
+                               parse_error("reading");
+                       }
+                       fclose(f);
+                       suid_config = sct_head; /* Success, so set the pointer. */
+                       return;
+               }
+
+               lc++;                                   /* Got a (partial) line. */
+
+               /* If a line is too long for our buffer, we consider it an error.
+                * The following test does mistreat one corner case though.
+                * If the final line of the file does not end with a newline and
+                * yet exactly fills the buffer, it will be treated as too long
+                * even though there isn't really a problem.  But it isn't really
+                * worth adding code to deal with such an unlikely situation, and
+                * we do err on the side of caution.  Besides, the line would be
+                * too long if it did end with a newline. */
+               if (!strchr(s, '\n') && !feof(f)) {
+                       parse_error("line too long");
+               }
+
+               /* Trim leading and trailing whitespace, ignoring comments, and
+                * check if the resulting string is empty. */
+               if (!*(s = get_trimmed_slice(s, strchrnul(s, '#')))) {
+                       continue;
+               }
+
+               /* Check for a section header. */
+
+               if (*s == '[') {
+                       /* Unlike the old code, we ignore leading and trailing
+                        * whitespace for the section name.  We also require that
+                        * there are no stray characters after the closing bracket. */
+                       if (!(e = strchr(s, ']'))       /* Missing right bracket? */
+                               || e[1]                                 /* Trailing characters? */
+                               || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
+                               ) {
+                               parse_error("section header");
+                       }
+                       /* Right now we only have one section so just check it.
+                        * If more sections are added in the future, please don't
+                        * resort to cascading ifs with multiple strcasecmp calls.
+                        * That kind of bloated code is all too common.  A loop
+                        * and a string table would be a better choice unless the
+                        * number of sections is very small. */
+                       if (strcasecmp(s, "SUID") == 0) {
+                               section = 1;
+                               continue;
+                       }
+                       section = -1;   /* Unknown section so set to skip. */
+                       continue;
+               }
+
+               /* Process sections. */
+
+               if (section == 1) {             /* SUID */
+                       /* Since we trimmed leading and trailing space above, we're
+                        * now looking for strings of the form
+                        *    <key>[::space::]*=[::space::]*<value>
+                        * where both key and value could contain inner whitespace. */
+
+                       /* First get the key (an applet name in our case). */
+                       if (!!(e = strchr(s, '='))) {
+                               s = get_trimmed_slice(s, e);
+                       }
+                       if (!e || !*s) {        /* Missing '=' or empty key. */
+                               parse_error("keyword");
+                       }
+
+                       /* Ok, we have an applet name.  Process the rhs if this
+                        * applet is currently built in and ignore it otherwise.
+                        * Note: This can hide config file bugs which only pop
+                        * up when the busybox configuration is changed. */
+                       if ((applet = find_applet_by_name(s))) {
+                               /* Note: We currently don't check for duplicates!
+                                * The last config line for each applet will be the
+                                * one used since we insert at the head of the list.
+                                * I suppose this could be considered a feature. */
+                               sct = xmalloc(sizeof(struct BB_suid_config));
+                               sct->m_applet = applet;
+                               sct->m_mode = 0;
+                               sct->m_next = sct_head;
+                               sct_head = sct;
+
+                               /* Get the specified mode. */
+
+                               e = (char *) bb_skip_whitespace(e+1);
+
+                               for (i=0 ; i < 3 ; i++) {
+                                       const char *q;
+                                       if (!*(q = strchrnul(mode_chars + 5*i, *e++))) {
+                                               parse_error("mode");
+                                       }
+                                       /* Adjust by -i to account for nul. */
+                                       sct->m_mode |= mode_mask[(q - mode_chars) - i];
+                               }
+
+                               /* Now get the the user/group info. */
+               
+                               s = (char *) bb_skip_whitespace(e);
+
+                               /* Note: We require whitespace between the mode and the
+                                * user/group info. */
+                               if ((s == e) || !(e = strchr(s, '.'))) {
+                                       parse_error("<uid>.<gid>");
+                               }
+                               *e++ = 0;
+
+                               /* We can't use get_ug_id here since it would exit()
+                                * if a uid or gid was not found.  Oh well... */
+                               {
+                                       char *e2;
+
+                                       sct->m_uid = strtoul(s, &e2, 10);
+                                       if (*e2 || (s == e2)) {
+                                               struct passwd *pwd;
+                                               if (!(pwd = getpwnam(s))) {
+                                                       parse_error("user");
+                                               }
+                                               sct->m_uid = pwd->pw_uid;
+                                       }
+
+                                       sct->m_gid = strtoul(e, &e2, 10);
+                                       if (*e2 || (e == e2)) {
+                                               struct group *grp;
+                                               if (!(grp = getgrnam(e))) {
+                                                       parse_error("group");
+                                               }
+                                               sct->m_gid = grp->gr_gid;
+                                       }
+                               }
+                       }
+                       continue;
+               }
+
+               /* Unknown sections are ignored. */
+
+               /* Encountering configuration lines prior to seeing a
+                * section header is treated as an error.  This is how
+                * the old code worked, but it may not be desirable.
+                * We may want to simply ignore such lines in case they
+                * are used in some future version of busybox. */
+               if (!section) {
+                       parse_error("keyword outside section");
+               }
+
+       } while (1);
+
+ pe_label:
+       fprintf(stderr, "Parse error in %s, line %d: %s\n",
+                       config_file, lc, err);
+
+       fclose(f);
+       /* Release any allocated memory before returning. */
+       while (sct_head) {
+               sct = sct_head->m_next;
+               free(sct_head);
+               sct_head = sct;
+       }
+       return;
+}
+
+#endif
+
+#endif
+
+/* END CODE */
+/*
+  Local Variables:
+  c-file-style: "linux"
+  c-basic-offset: 4
+  tab-width: 4
+End:
+*/
diff --git a/busybox/applets/busybox.c b/busybox/applets/busybox.c
new file mode 100644 (file)
index 0000000..dbb5e17
--- /dev/null
@@ -0,0 +1,193 @@
+/* vi: set sw=4 ts=4: */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "busybox.h"
+#ifdef CONFIG_LOCALE_SUPPORT
+#include <locale.h>
+#endif
+
+int been_there_done_that = 0; /* Also used in applets.c */
+const char *bb_applet_name;
+
+#ifdef CONFIG_FEATURE_INSTALLER
+/*
+ * directory table
+ *             this should be consistent w/ the enum, busybox.h::Location,
+ *             or else...
+ */
+static const char usr_bin [] ="/usr/bin";
+static const char usr_sbin[] ="/usr/sbin";
+
+static const char* const install_dir[] = {
+       &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
+       &usr_bin [4], /* "/bin" */
+       &usr_sbin[4], /* "/sbin" */
+       usr_bin,
+       usr_sbin
+};
+
+/* abstract link() */
+typedef int (*__link_f)(const char *, const char *);
+
+/*
+ * Where in the filesystem is this busybox?
+ * [return]
+ *             malloc'd string w/ full pathname of busybox's location
+ *             NULL on failure
+ */
+static inline char *busybox_fullpath(void)
+{
+       return xreadlink("/proc/self/exe");
+}
+
+/* create (sym)links for each applet */
+static void install_links(const char *busybox, int use_symbolic_links)
+{
+       __link_f Link = link;
+
+       char *fpc;
+       int i;
+       int rc;
+
+       if (use_symbolic_links)
+               Link = symlink;
+
+       for (i = 0; applets[i].name != NULL; i++) {
+               fpc = concat_path_file(
+                       install_dir[applets[i].location], applets[i].name);
+               rc = Link(busybox, fpc);
+               if (rc!=0 && errno!=EEXIST) {
+                       bb_perror_msg("%s", fpc);
+               }
+               free(fpc);
+       }
+}
+
+#endif /* CONFIG_FEATURE_INSTALLER */
+
+int main(int argc, char **argv)
+{
+       const char *s;
+
+       bb_applet_name = argv[0];
+
+       if (bb_applet_name[0] == '-')
+               bb_applet_name++;
+
+       for (s = bb_applet_name; *s != '\0';) {
+               if (*s++ == '/')
+                       bb_applet_name = s;
+       }
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#ifdef CONFIG_INIT
+       if(getpid()!=1) /* Do not set locale for `init' */
+#endif
+       {
+               setlocale(LC_ALL, "");
+       }
+#endif
+
+       run_applet_by_name(bb_applet_name, argc, argv);
+       bb_error_msg_and_die("applet not found");
+}
+
+
+int busybox_main(int argc, char **argv)
+{
+       int col = 0, len, i;
+
+#ifdef CONFIG_FEATURE_INSTALLER
+       /*
+        * This style of argument parsing doesn't scale well
+        * in the event that busybox starts wanting more --options.
+        * If someone has a cleaner approach, by all means implement it.
+        */
+       if (argc > 1 && (strcmp(argv[1], "--install") == 0)) {
+               int use_symbolic_links = 0;
+               int rc = 0;
+               char *busybox;
+
+               /* to use symlinks, or not to use symlinks... */
+               if (argc > 2) {
+                       if ((strcmp(argv[2], "-s") == 0)) {
+                               use_symbolic_links = 1;
+                       }
+               }
+
+               /* link */
+               busybox = busybox_fullpath();
+               if (busybox) {
+                       install_links(busybox, use_symbolic_links);
+                       free(busybox);
+               } else {
+                       rc = 1;
+               }
+               return rc;
+       }
+#endif /* CONFIG_FEATURE_INSTALLER */
+
+       argc--;
+
+       /* If we've already been here once, exit now */
+       if (been_there_done_that == 1 || argc < 1) {
+               const struct BB_applet *a = applets;
+               int output_width = 60;
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+               /* Obtain the terminal width.  */
+               get_terminal_width_height(0, &output_width, NULL);
+               /* leading tab and room to wrap */
+               output_width -= 20;
+#endif
+
+               fprintf(stderr, "%s\n\n"
+                               "Usage: busybox [function] [arguments]...\n"
+                               "   or: [function] [arguments]...\n\n"
+                               "\tBusyBox is a multi-call binary that combines many common Unix\n"
+                               "\tutilities into a single executable.  Most people will create a\n"
+                               "\tlink to busybox for each function they wish to use, and BusyBox\n"
+                               "\twill act like whatever it was invoked as.\n"
+                               "\nCurrently defined functions:\n", bb_msg_full_version);
+
+               while (a->name != 0) {
+                       col +=
+                               fprintf(stderr, "%s%s", ((col == 0) ? "\t" : ", "),
+                                               (a++)->name);
+                       if (col > output_width && a->name != 0) {
+                               fprintf(stderr, ",\n");
+                               col = 0;
+                       }
+               }
+               fprintf(stderr, "\n\n");
+               exit(0);
+       }
+
+       /* Flag that we've been here already */
+       been_there_done_that = 1;
+
+       /* Move the command line down a notch */
+       /* Preserve pointers so setproctitle() works consistently */
+       len = argv[argc] + strlen(argv[argc]) - argv[1];
+       memmove(argv[0], argv[1], len);
+       memset(argv[0] + len, 0, argv[1] - argv[0]);
+
+       /* Fix up the argv pointers */
+       len = argv[1] - argv[0];
+       memmove(argv, argv + 1, sizeof(char *) * (argc + 1));
+       for (i = 0; i < argc; i++)
+               argv[i] -= len;
+
+       return (main(argc, argv));
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/applets/busybox.mkll b/busybox/applets/busybox.mkll
new file mode 100755 (executable)
index 0000000..5b6677d
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Make busybox links list file.
+
+# input $1: full path to Config.h
+# input $2: full path to applets.h
+# output (stdout): list of pathnames that should be linked to busybox
+
+# Maintainer: Larry Doolittle <ldoolitt@recycle.lbl.gov>
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+CONFIG_H=${1:-include/config.h}
+APPLETS_H=${2:-include/applets.h}
+gcc -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H |
+  awk '/^[ \t]*LINK/{
+       dir=substr($2,8)
+       gsub("_","/",dir)
+       if(dir=="/ROOT") dir=""
+       file=$3
+       gsub("\"","",file)
+       if (file=="busybox") next
+       print tolower(dir) "/" file
+  }'
diff --git a/busybox/applets/install.sh b/busybox/applets/install.sh
new file mode 100755 (executable)
index 0000000..d163a2e
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+prefix=$1
+if [ "$prefix" = "" ]; then
+    echo "No installation directory, aborting."
+    exit 1;
+fi
+if [ "$2" = "--hardlinks" ]; then
+    linkopts="-f"
+else
+    linkopts="-fs"
+fi
+h=`sort busybox.links | uniq`
+
+
+rm -f $prefix/bin/busybox || exit 1
+mkdir -p $prefix/bin || exit 1
+install -m 755 busybox $prefix/bin/busybox || exit 1
+
+for i in $h ; do
+       appdir=`dirname $i`
+       mkdir -p $prefix/$appdir || exit 1
+       if [ "$2" = "--hardlinks" ]; then
+           bb_path="$prefix/bin/busybox"
+       else
+           case "$appdir" in
+               /)
+                   bb_path="bin/busybox"
+               ;;
+               /bin)
+                   bb_path="busybox"
+               ;;
+               /sbin)
+                   bb_path="../bin/busybox"
+               ;;
+               /usr/bin|/usr/sbin)
+                   bb_path="../../bin/busybox"
+               ;;
+               *)
+               echo "Unknown installation directory: $appdir"
+               exit 1
+               ;;
+           esac
+       fi
+       echo "  $prefix$i -> $bb_path"
+       ln $linkopts $bb_path $prefix$i || exit 1
+done
+
+exit 0
diff --git a/busybox/archival/Config.in b/busybox/archival/Config.in
new file mode 100644 (file)
index 0000000..db358db
--- /dev/null
@@ -0,0 +1,258 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Archival Utilities"
+
+config CONFIG_AR
+       bool "ar"
+       default n
+       help
+         ar is an archival utility program used to create, modify, and
+         extract contents from archives.  An archive is a single file holding
+         a collection of other files in a structure that makes it possible to
+         retrieve the original individual files (called archive members).
+         The original files' contents, mode (permissions), timestamp, owner,
+         and group are preserved in the archive, and can be restored on
+         extraction.
+
+         The stored filename is limited to 15 characters. (for more information
+         see long filename support).
+         ar has 60 bytes of overheads for every stored file.
+
+         This implementation of ar can extract archives, it cannot create or
+         modify them.
+         On an x86 system, the ar applet adds about 1K.
+
+         Unless you have a specific application which requires ar, you should
+         probably say N here.
+
+config CONFIG_FEATURE_AR_LONG_FILENAMES
+       bool "  Enable support for long filenames (not need for debs)"
+       default n
+       depends on CONFIG_AR
+       help
+         By default the ar format can only store the first 15 characters of the
+         filename, this option removes that limitation.
+         It supports the GNU ar long filename method which moves multiple long
+         filenames into a the data section of a new ar entry.
+
+config CONFIG_BUNZIP2
+       bool "bunzip2"
+       default n
+       help
+         bunzip2 is a compression utility using the Burrows-Wheeler block
+         sorting text compression algorithm, and Huffman coding.  Compression
+         is generally considerably better than that achieved by more
+         conventional LZ77/LZ78-based compressors, and approaches the
+         performance of the PPM family of statistical compressors.
+       
+         The BusyBox bunzip2 applet is limited to de-compression only.
+         On an x86 system, this applet adds about 11K.
+       
+         Unless you have a specific application which requires bunzip2, you
+         should probably say N here.
+
+config CONFIG_CPIO
+       bool "cpio"
+       default n
+       help
+         cpio is an archival utility program used to create, modify, and extract
+         contents from archives.
+         cpio has 110 bytes of overheads for every stored file.
+
+         This implementation of cpio can extract cpio archives created in the
+         "newc" or "crc" format, it cannot create or modify them.
+
+         Unless you have a specific application which requires cpio, you should
+         probably say N here.
+
+config CONFIG_DPKG
+       bool "dpkg"
+       default n
+       help
+         dpkg is a medium-level tool to install, build, remove and manage Debian packages.
+       
+         This implementation of dpkg has a number of limitations, you should use the
+         official dpkg if possible.
+
+config CONFIG_DPKG_DEB
+       bool "dpkg_deb"
+       default n
+       help
+         dpkg-deb packs, unpacks and provides information about Debian archives.
+       
+         This implementation of dpkg-deb cannot pack archives.
+       
+         Unless you have a specific application which requires dpkg-deb, you should
+         probably say N here.
+
+config CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
+       bool "  extract only (-x)"
+       default n
+       depends on CONFIG_DPKG_DEB
+       help
+         This reduces dpkg-deb to the equivalent of "ar -p <deb> data.tar.gz | tar -zx".
+         However it saves space as none of the extra dpkg-deb, ar or tar options are
+         needed, they are linked to internally.
+
+config CONFIG_GUNZIP
+       bool "gunzip"
+       default n
+       help
+         gunzip is used to decompress archives created by gzip.
+         You can use the `-t' option to test the integrity of
+         an archive, without decompressing it.
+
+config CONFIG_FEATURE_GUNZIP_UNCOMPRESS
+       bool "  Uncompress support"
+       default n
+       depends on CONFIG_GUNZIP
+       help
+         Enable if you want gunzip to have the ability to decompress
+         archives created by the program compress (not much
+         used anymore).
+
+config CONFIG_GZIP
+       bool "gzip"
+       default n
+       help
+         gzip is used to compress files.
+         It's probably the most widely used UNIX compression program.
+
+config CONFIG_RPM2CPIO
+       bool "rpm2cpio"
+       default n
+       help
+         Converts an RPM file into a CPIO archive.
+
+config CONFIG_RPM
+       bool "rpm"
+       default n
+       help
+         Mini RPM applet - queries and extracts
+
+config CONFIG_TAR
+       bool "tar"
+       default n
+       help
+         tar is an archiving program. It's commonly used with gzip to
+         create compressed archives. It's probably the most widely used
+         UNIX archive program.
+
+config CONFIG_FEATURE_TAR_CREATE
+       bool "  Enable archive creation"
+       default y
+       depends on CONFIG_TAR
+       help
+         If you enable this option you'll be able to create
+         tar archives using the `-c' option.
+
+config CONFIG_FEATURE_TAR_BZIP2
+       bool "  Enable -j option to handle .tar.bz2 files"
+       default n
+       depends on CONFIG_TAR
+       help
+         If you enable this option you'll be able to extract
+         archives compressed with bzip2.
+
+config CONFIG_FEATURE_TAR_FROM
+       bool "  Enable -X (exclude from) and -T (include from) options)"
+       default n
+       depends on CONFIG_TAR
+       help
+         If you enable this option you'll be able to specify
+         a list of files to include or exclude from an archive.
+
+config CONFIG_FEATURE_TAR_GZIP
+       bool "  Enable -z option"
+       default y
+       depends on CONFIG_TAR
+       help
+         If you enable this option tar will be able to call gzip,
+         when creating or extracting tar gziped archives.
+
+config CONFIG_FEATURE_TAR_COMPRESS
+       bool "  Enable -Z option"
+       default n
+       depends on CONFIG_TAR
+       help
+         If you enable this option tar will be able to call uncompress,
+         when extracting .tar.Z archives.
+
+config CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY
+       bool "  Enable support for old tar header format"
+       default N
+       depends on CONFIG_TAR
+       help
+         This option is required to unpack archives created in
+         the old GNU format; help to kill this old format by
+         repacking your ancient archives with the new format.
+
+config CONFIG_FEATURE_TAR_GNU_EXTENSIONS
+       bool "  Enable support for some GNU tar extensions"
+       default y
+       depends on CONFIG_TAR
+       help
+         With this option busybox supports GNU long filenames and
+         linknames.
+
+config CONFIG_FEATURE_TAR_LONG_OPTIONS
+       bool "  Enable long options"
+       default n
+       depends on CONFIG_TAR
+       help
+               Enable use of long options, increases size by about 400 Bytes
+
+config CONFIG_UNCOMPRESS
+       bool "uncompress"
+       default n
+       help
+         uncompress is used to decompress archives created by compress.
+         Not much used anymore, replaced by gzip/gunzip.
+
+config CONFIG_UNZIP
+       bool "unzip"
+       default n
+       help
+         unzip will list or extract files from a ZIP archive,
+         commonly found on DOS/WIN systems. The default behavior
+         (with no options) is to extract the archive into the
+         current directory. Use the `-d' option to extract to a
+         directory of your choice.
+
+comment "Common options for cpio and tar"
+       depends on CONFIG_CPIO || CONFIG_TAR
+
+config CONFIG_FEATURE_UNARCHIVE_TAPE
+       bool "  Enable tape drive support"
+       default n
+       depends on CONFIG_CPIO || CONFIG_TAR
+       help
+         I don't think this is needed anymore.
+
+comment "Common options for dpkg and dpkg_deb"
+       depends on CONFIG_DPKG || CONFIG_DPKG_DEB
+
+config CONFIG_FEATURE_DEB_TAR_GZ
+       bool "  gzip debian packages (normal)"
+       default y if CONFIG_DPKG || CONFIG_DPKG_DEB
+       depends on CONFIG_DPKG || CONFIG_DPKG_DEB
+       help
+         This is the default compression method inside the debian ar file.
+       
+         If you want compatibility with standard .deb's you should say yes here.
+
+config CONFIG_FEATURE_DEB_TAR_BZ2
+       bool "  bzip2 debian packages"
+       default n
+       depends on CONFIG_DPKG || CONFIG_DPKG_DEB
+       help
+         This allows dpkg and dpkg-deb to extract deb's that are compressed internally
+         with bzip2 instead of gzip.
+       
+         You only want this if you are creating your own custom debian packages that
+         use an internal control.tar.bz2 or data.tar.bz2.
+
+endmenu
diff --git a/busybox/archival/Makefile b/busybox/archival/Makefile
new file mode 100644 (file)
index 0000000..a96daa4
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+ARCHIVAL_DIR:=./
+srcdir=$(top_srcdir)/archival
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/archival/Makefile.in b/busybox/archival/Makefile.in
new file mode 100644 (file)
index 0000000..76ab6cd
--- /dev/null
@@ -0,0 +1,48 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+ARCHIVAL_AR:=archival.a
+ifndef $(ARCHIVAL_DIR)
+ARCHIVAL_DIR:=$(top_builddir)/archival/
+endif
+srcdir=$(top_srcdir)/archival
+
+ARCHIVAL-y:=
+ARCHIVAL-$(CONFIG_APT_GET)     +=
+ARCHIVAL-$(CONFIG_AR)          += ar.o
+ARCHIVAL-$(CONFIG_BUNZIP2)     += bunzip2.o
+ARCHIVAL-$(CONFIG_CPIO)                += cpio.o
+ARCHIVAL-$(CONFIG_DPKG)                += dpkg.o
+ARCHIVAL-$(CONFIG_DPKG_DEB)    += dpkg_deb.o
+ARCHIVAL-$(CONFIG_GUNZIP)      += gunzip.o
+ARCHIVAL-$(CONFIG_GZIP)                += gzip.o
+ARCHIVAL-$(CONFIG_RPM2CPIO)    += rpm2cpio.o
+ARCHIVAL-$(CONFIG_RPM)         += rpm.o
+ARCHIVAL-$(CONFIG_TAR)         += tar.o
+ARCHIVAL-$(CONFIG_UNCOMPRESS)  += uncompress.o
+ARCHIVAL-$(CONFIG_UNZIP)       += unzip.o
+
+libraries-y+=$(ARCHIVAL_DIR)$(ARCHIVAL_AR)
+
+$(ARCHIVAL_DIR)$(ARCHIVAL_AR): $(patsubst %,$(ARCHIVAL_DIR)%, $(ARCHIVAL-y))
+       $(AR) -ro $@ $(patsubst %,$(ARCHIVAL_DIR)%, $(ARCHIVAL-y))
+
+$(ARCHIVAL_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/archival/ar.c b/busybox/archival/ar.c
new file mode 100644 (file)
index 0000000..44c5db0
--- /dev/null
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ar implementation for busybox
+ *
+ * Copyright (C) 2000 by Glenn McGrath
+ * Written by Glenn McGrath <bug1@iinet.net.au> 1 June 2000
+ *
+ * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * There is no single standard to adhere to so ar may not portable
+ * between different systems
+ * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <utime.h>
+#include <unistd.h>
+
+#include "unarchive.h"
+#include "busybox.h"
+
+static void header_verbose_list_ar(const file_header_t *file_header)
+{
+       const char *mode = bb_mode_string(file_header->mode);
+       char *mtime;
+
+       mtime = ctime(&file_header->mtime);
+       mtime[16] = ' ';
+       memmove(&mtime[17], &mtime[20], 4);
+       mtime[21] = '\0';
+       printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid, (int) file_header->size, &mtime[4], file_header->name);
+}
+
+#define AR_CTX_PRINT                   0x01
+#define AR_CTX_LIST                            0x02
+#define AR_CTX_EXTRACT                 0x04
+#define AR_OPT_PRESERVE_DATE   0x08
+#define AR_OPT_VERBOSE                 0x10
+#define AR_OPT_CREATE                  0x20
+
+extern int ar_main(int argc, char **argv)
+{
+       archive_handle_t *archive_handle;
+       unsigned long opt;
+       char magic[8];
+
+       archive_handle = init_handle();
+
+       bb_opt_complementaly = "p~tx:t~px:x~pt";
+       opt = bb_getopt_ulflags(argc, argv, "ptxovc");
+
+       if ((opt & 0x80000000UL) || (optind == argc)) {
+               bb_show_usage();
+       }
+
+       if (opt & AR_CTX_PRINT) {
+               archive_handle->action_data = data_extract_to_stdout;
+       }
+       if (opt & AR_CTX_LIST) {
+               archive_handle->action_header = header_list;
+       }
+       if (opt & AR_CTX_EXTRACT) {
+               archive_handle->action_data = data_extract_all;
+       }
+       if (opt & AR_OPT_PRESERVE_DATE) {
+               archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
+       }
+       if (opt & AR_OPT_VERBOSE) {
+               archive_handle->action_header = header_verbose_list_ar;
+       }
+       if (opt & AR_OPT_CREATE) {
+               bb_error_msg_and_die("Archive creation not supported.  Install binutils 'ar'.");
+       }
+
+       archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY);
+
+       while (optind < argc) {
+               archive_handle->filter = filter_accept_list;
+               archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind++]);
+       }
+
+       archive_xread_all(archive_handle, magic, 7);
+       if (strncmp(magic, "!<arch>", 7) != 0) {
+               bb_error_msg_and_die("Invalid ar magic");
+       }
+       archive_handle->offset += 7;
+
+       while (get_header_ar(archive_handle) == EXIT_SUCCESS);
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/archival/bunzip2.c b/busybox/archival/bunzip2.c
new file mode 100644 (file)
index 0000000..bedd38c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  Modified for busybox by Glenn McGrath <bug1@iinet.net.au>
+ *  Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "busybox.h"
+#include "unarchive.h"
+
+#define BUNZIP2_OPT_STDOUT     1
+#define BUNZIP2_OPT_FORCE      2
+
+int bunzip2_main(int argc, char **argv)
+{
+       char *compressed_name;
+       /* Note: Ignore the warning about save_name being used uninitialized.
+        * That is not the case, but gcc has trouble working that out... */
+       char *save_name;
+       unsigned long opt;
+       int status;
+       int src_fd;
+       int dst_fd;
+
+       opt = bb_getopt_ulflags(argc, argv, "cf");
+
+       /* if called as bzcat force the stdout flag */
+       if (bb_applet_name[2] == 'c') {
+               opt |= BUNZIP2_OPT_STDOUT;
+       }
+
+       /* Set input filename and number */
+       compressed_name = argv[optind];
+       if ((compressed_name) && (compressed_name[0] != '-') && (compressed_name[1] != '\0')) {
+               /* Open input file */
+               src_fd = bb_xopen(compressed_name, O_RDONLY);
+       } else {
+               src_fd = STDIN_FILENO;
+               opt |= BUNZIP2_OPT_STDOUT;
+       }
+
+       /* Check that the input is sane.  */
+       if (isatty(src_fd) && (opt & BUNZIP2_OPT_FORCE) == 0) {
+               bb_error_msg_and_die("compressed data not read from terminal.  Use -f to force it.");
+       }
+
+       if (opt & BUNZIP2_OPT_STDOUT) {
+               dst_fd = STDOUT_FILENO;
+       } else {
+               int len = strlen(compressed_name) - 4;
+               if (strcmp(compressed_name + len, ".bz2") != 0) {
+                       bb_error_msg_and_die("Invalid extension");
+               }
+               save_name = bb_xstrndup(compressed_name, len);
+               dst_fd = bb_xopen(save_name, O_WRONLY | O_CREAT);
+       }
+
+       status = uncompressStream(src_fd, dst_fd);
+       if(!(opt & BUNZIP2_OPT_STDOUT)) {
+               char *delete_name;
+               if (status) {
+                       delete_name = save_name;
+               } else {
+                       delete_name = compressed_name;
+               }
+               if (unlink(delete_name) < 0) {
+                       bb_error_msg_and_die("Couldn't remove %s", delete_name);
+               }
+       }
+
+       return status;
+}
diff --git a/busybox/archival/cpio.c b/busybox/archival/cpio.c
new file mode 100644 (file)
index 0000000..0fbe7b8
--- /dev/null
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cpio implementation for busybox
+ *
+ * Copyright (C) 2001 by Glenn McGrath
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Limitations:
+ *             Doesn't check CRC's
+ *             Only supports new ASCII and CRC formats
+ *
+ */
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+#define CPIO_OPT_EXTRACT               0x01
+#define CPIO_OPT_TEST                  0x02
+#define CPIO_OPT_UNCONDITIONAL         0x04
+#define CPIO_OPT_VERBOSE               0x08
+#define CPIO_OPT_FILE                  0x10
+#define CPIO_OPT_CREATE_LEADING_DIR    0x20
+#define CPIO_OPT_PRESERVE_MTIME        0x40
+
+extern int cpio_main(int argc, char **argv)
+{
+       archive_handle_t *archive_handle;
+       char *cpio_filename = NULL;
+       unsigned long opt;
+
+       /* Initialise */
+       archive_handle = init_handle();
+       archive_handle->src_fd = STDIN_FILENO;
+       archive_handle->seek = seek_by_char;
+       archive_handle->flags = ARCHIVE_EXTRACT_NEWER | ARCHIVE_PRESERVE_DATE;
+
+       opt = bb_getopt_ulflags(argc, argv, "ituvF:dm", &cpio_filename);
+
+       /* One of either extract or test options must be given */
+       if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
+               bb_show_usage();
+       }
+
+       if (opt & CPIO_OPT_TEST) {
+               /* if both extract and test option are given, ignore extract option */
+               if (opt & CPIO_OPT_EXTRACT) {
+                       opt &= ~CPIO_OPT_EXTRACT;
+               }
+               archive_handle->action_header = header_list;
+       }
+       if (opt & CPIO_OPT_EXTRACT) {
+               archive_handle->action_data = data_extract_all;
+       }
+       if (opt & CPIO_OPT_UNCONDITIONAL) {
+               archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
+               archive_handle->flags &= ~ARCHIVE_EXTRACT_NEWER;
+       }
+       if (opt & CPIO_OPT_VERBOSE) {
+               if (archive_handle->action_header == header_list) {
+                       archive_handle->action_header = header_verbose_list;
+               } else {
+                       archive_handle->action_header = header_list;
+               }
+       }
+       if (cpio_filename) {    /* CPIO_OPT_FILE */
+               archive_handle->src_fd = bb_xopen(cpio_filename, O_RDONLY);
+               archive_handle->seek = seek_by_jump;
+       }
+       if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
+               archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
+       }
+
+       while (optind < argc) {
+               archive_handle->filter = filter_accept_list;
+               archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]);
+               optind++;
+       }
+
+       while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/archival/dpkg.c b/busybox/archival/dpkg.c
new file mode 100644 (file)
index 0000000..c096518
--- /dev/null
@@ -0,0 +1,1833 @@
+/*
+ *  Mini dpkg implementation for busybox.
+ *  This is not meant as a replacement for dpkg
+ *
+ *  Written By Glenn McGrath with the help of others
+ *  Copyright (C) 2001 by Glenn McGrath
+ *
+ *  Started life as a busybox implementation of udpkg
+ *
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Known difference between busybox dpkg and the official dpkg that i don't
+ * consider important, its worth keeping a note of differences anyway, just to
+ * make it easier to maintain.
+ *  - The first value for the Confflile: field isnt placed on a new line.
+ *  - When installing a package the Status: field is placed at the end of the
+ *      section, rather than just after the Package: field.
+ *
+ * Bugs that need to be fixed
+ *  - (unknown, please let me know when you find any)
+ *
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+/* NOTE: If you vary HASH_PRIME sizes be aware,
+ * 1) Tweaking these will have a big effect on how much memory this program uses.
+ * 2) For computational efficiency these hash tables should be at least 20%
+ *    larger than the maximum number of elements stored in it.
+ * 3) All _HASH_PRIME's must be a prime number or chaos is assured, if your looking
+ *    for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt
+ * 4) If you go bigger than 15 bits you may get into trouble (untested) as its
+ *    sometimes cast to an unsigned int, if you go to 16 bit you will overlap
+ *    int's and chaos is assured, 16381 is the max prime for 14 bit field
+ */
+
+/* NAME_HASH_PRIME, Stores package names and versions,
+ * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME,
+ * as there a lot of duplicate version numbers */
+#define NAME_HASH_PRIME 16381
+char *name_hashtable[NAME_HASH_PRIME + 1];
+
+/* PACKAGE_HASH_PRIME, Maximum number of unique packages,
+ * It must not be smaller than STATUS_HASH_PRIME,
+ * Currently only packages from status_hashtable are stored in here, but in
+ * future this may be used to store packages not only from a status file,
+ * but an available_hashtable, and even multiple packages files.
+ * Package can be stored more than once if they have different versions.
+ * e.g. The same package may have different versions in the status file
+ *      and available file */
+#define PACKAGE_HASH_PRIME 10007
+typedef struct edge_s {
+       unsigned int operator:3;
+       unsigned int type:4;
+       unsigned int name:14;
+       unsigned int version:14;
+} edge_t;
+
+typedef struct common_node_s {
+       unsigned int name:14;
+       unsigned int version:14;
+       unsigned int num_of_edges:14;
+       edge_t **edge;
+} common_node_t;
+common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
+
+/* Currently it doesnt store packages that have state-status of not-installed
+ * So it only really has to be the size of the maximum number of packages
+ * likely to be installed at any one time, so there is a bit of leeway here */
+#define STATUS_HASH_PRIME 8191
+typedef struct status_node_s {
+       unsigned int package:14;        /* has to fit PACKAGE_HASH_PRIME */
+       unsigned int status:14;         /* has to fit STATUS_HASH_PRIME */
+} status_node_t;
+status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
+
+/* Even numbers are for 'extras', like ored dependencies or null */
+enum edge_type_e {
+       EDGE_NULL = 0,
+       EDGE_PRE_DEPENDS = 1,
+       EDGE_OR_PRE_DEPENDS = 2,
+       EDGE_DEPENDS = 3,
+       EDGE_OR_DEPENDS = 4,
+       EDGE_REPLACES = 5,
+       EDGE_PROVIDES = 7,
+       EDGE_CONFLICTS = 9,
+       EDGE_SUGGESTS = 11,
+       EDGE_RECOMMENDS = 13,
+       EDGE_ENHANCES = 15
+};
+enum operator_e {
+       VER_NULL = 0,
+       VER_EQUAL = 1,
+       VER_LESS = 2,
+       VER_LESS_EQUAL = 3,
+       VER_MORE = 4,
+       VER_MORE_EQUAL = 5,
+       VER_ANY = 6
+};
+
+enum dpkg_opt_e {
+       dpkg_opt_purge = 1,
+       dpkg_opt_remove = 2,
+       dpkg_opt_unpack = 4,
+       dpkg_opt_configure = 8,
+       dpkg_opt_install = 16,
+       dpkg_opt_package_name = 32,
+       dpkg_opt_filename = 64,
+       dpkg_opt_list_installed = 128,
+       dpkg_opt_force_ignore_depends = 256
+};
+
+typedef struct deb_file_s {
+       char *control_file;
+       char *filename;
+       unsigned int package:14;
+} deb_file_t;
+
+
+void make_hash(const char *key, unsigned int *start, unsigned int *decrement, const int hash_prime)
+{
+       unsigned long int hash_num = key[0];
+       int len = strlen(key);
+       int i;
+
+       /* Maybe i should have uses a "proper" hashing algorithm here instead
+        * of making one up myself, seems to be working ok though. */
+       for(i = 1; i < len; i++) {
+               /* shifts the ascii based value and adds it to previous value
+                * shift amount is mod 24 because long int is 32 bit and data
+                * to be shifted is 8, don't want to shift data to where it has
+                * no effect*/
+               hash_num += ((key[i] + key[i-1]) << ((key[i] * i) % 24));
+       }
+       *start = (unsigned int) hash_num % hash_prime;
+       *decrement = (unsigned int) 1 + (hash_num % (hash_prime - 1));
+}
+
+/* this adds the key to the hash table */
+int search_name_hashtable(const char *key)
+{
+       unsigned int probe_address = 0;
+       unsigned int probe_decrement = 0;
+//     char *temp;
+
+       make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME);
+       while(name_hashtable[probe_address] != NULL) {
+               if (strcmp(name_hashtable[probe_address], key) == 0) {
+                       return(probe_address);
+               } else {
+                       probe_address -= probe_decrement;
+                       if ((int)probe_address < 0) {
+                               probe_address += NAME_HASH_PRIME;
+                       }
+               }
+       }
+       name_hashtable[probe_address] = bb_xstrdup(key);
+       return(probe_address);
+}
+
+/* this DOESNT add the key to the hashtable
+ * TODO make it consistent with search_name_hashtable
+ */
+unsigned int search_status_hashtable(const char *key)
+{
+       unsigned int probe_address = 0;
+       unsigned int probe_decrement = 0;
+
+       make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME);
+       while(status_hashtable[probe_address] != NULL) {
+               if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) {
+                       break;
+               } else {
+                       probe_address -= probe_decrement;
+                       if ((int)probe_address < 0) {
+                               probe_address += STATUS_HASH_PRIME;
+                       }
+               }
+       }
+       return(probe_address);
+}
+
+/* Need to rethink version comparison, maybe the official dpkg has something i can use ? */
+int version_compare_part(const char *version1, const char *version2)
+{
+       int upstream_len1 = 0;
+       int upstream_len2 = 0;
+       char *name1_char;
+       char *name2_char;
+       int len1 = 0;
+       int len2 = 0;
+       int tmp_int;
+       int ver_num1;
+       int ver_num2;
+       int ret;
+
+       if (version1 == NULL) {
+               version1 = bb_xstrdup("");
+       }
+       if (version2 == NULL) {
+               version2 = bb_xstrdup("");
+       }
+       upstream_len1 = strlen(version1);
+       upstream_len2 = strlen(version2);
+
+       while ((len1 < upstream_len1) || (len2 < upstream_len2)) {
+               /* Compare non-digit section */
+               tmp_int = strcspn(&version1[len1], "0123456789");
+               name1_char = bb_xstrndup(&version1[len1], tmp_int);
+               len1 += tmp_int;
+               tmp_int = strcspn(&version2[len2], "0123456789");
+               name2_char = bb_xstrndup(&version2[len2], tmp_int);
+               len2 += tmp_int;
+               tmp_int = strcmp(name1_char, name2_char);
+               free(name1_char);
+               free(name2_char);
+               if (tmp_int != 0) {
+                       ret = tmp_int;
+                       goto cleanup_version_compare_part;
+               }
+
+               /* Compare digits */
+               tmp_int = strspn(&version1[len1], "0123456789");
+               name1_char = bb_xstrndup(&version1[len1], tmp_int);
+               len1 += tmp_int;
+               tmp_int = strspn(&version2[len2], "0123456789");
+               name2_char = bb_xstrndup(&version2[len2], tmp_int);
+               len2 += tmp_int;
+               ver_num1 = atoi(name1_char);
+               ver_num2 = atoi(name2_char);
+               free(name1_char);
+               free(name2_char);
+               if (ver_num1 < ver_num2) {
+                       ret = -1;
+                       goto cleanup_version_compare_part;
+               }
+               else if (ver_num1 > ver_num2) {
+                       ret = 1;
+                       goto cleanup_version_compare_part;
+               }
+       }
+       ret = 0;
+cleanup_version_compare_part:
+       return(ret);
+}
+
+/* if ver1 < ver2 return -1,
+ * if ver1 = ver2 return 0,
+ * if ver1 > ver2 return 1,
+ */
+int version_compare(const unsigned int ver1, const unsigned int ver2)
+{
+       char *ch_ver1 = name_hashtable[ver1];
+       char *ch_ver2 = name_hashtable[ver2];
+
+       char epoch1, epoch2;
+       char *deb_ver1, *deb_ver2;
+       char *ver1_ptr, *ver2_ptr;
+       char *upstream_ver1;
+       char *upstream_ver2;
+       int result;
+
+       /* Compare epoch */
+       if (ch_ver1[1] == ':') {
+               epoch1 = ch_ver1[0];
+               ver1_ptr = strchr(ch_ver1, ':') + 1;
+       } else {
+               epoch1 = '0';
+               ver1_ptr = ch_ver1;
+       }
+       if (ch_ver2[1] == ':') {
+               epoch2 = ch_ver2[0];
+               ver2_ptr = strchr(ch_ver2, ':') + 1;
+       } else {
+               epoch2 = '0';
+               ver2_ptr = ch_ver2;
+       }
+       if (epoch1 < epoch2) {
+               return(-1);
+       }
+       else if (epoch1 > epoch2) {
+               return(1);
+       }
+
+       /* Compare upstream version */
+       upstream_ver1 = bb_xstrdup(ver1_ptr);
+       upstream_ver2 = bb_xstrdup(ver2_ptr);
+
+       /* Chop off debian version, and store for later use */
+       deb_ver1 = strrchr(upstream_ver1, '-');
+       deb_ver2 = strrchr(upstream_ver2, '-');
+       if (deb_ver1) {
+               deb_ver1[0] = '\0';
+               deb_ver1++;
+       }
+       if (deb_ver2) {
+               deb_ver2[0] = '\0';
+               deb_ver2++;
+       }
+       result = version_compare_part(upstream_ver1, upstream_ver2);
+
+       free(upstream_ver1);
+       free(upstream_ver2);
+
+       if (result != 0) {
+               return(result);
+       }
+
+       /* Compare debian versions */
+       return(version_compare_part(deb_ver1, deb_ver2));
+}
+
+int test_version(const unsigned int version1, const unsigned int version2, const unsigned int operator)
+{
+       const int version_result = version_compare(version1, version2);
+       switch(operator) {
+               case (VER_ANY):
+                       return(TRUE);
+               case (VER_EQUAL):
+                       if (version_result == 0) {
+                               return(TRUE);
+                       }
+                       break;
+               case (VER_LESS):
+                       if (version_result < 0) {
+                               return(TRUE);
+                       }
+                       break;
+               case (VER_LESS_EQUAL):
+                       if (version_result <= 0) {
+                               return(TRUE);
+                       }
+                       break;
+               case (VER_MORE):
+                       if (version_result > 0) {
+                               return(TRUE);
+                       }
+                       break;
+               case (VER_MORE_EQUAL):
+                       if (version_result >= 0) {
+                               return(TRUE);
+                       }
+                       break;
+       }
+       return(FALSE);
+}
+
+
+int search_package_hashtable(const unsigned int name, const unsigned int version, const unsigned int operator)
+{
+       unsigned int probe_address = 0;
+       unsigned int probe_decrement = 0;
+
+       make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME);
+       while(package_hashtable[probe_address] != NULL) {
+               if (package_hashtable[probe_address]->name == name) {
+                       if (operator == VER_ANY) {
+                               return(probe_address);
+                       }
+                       if (test_version(package_hashtable[probe_address]->version, version, operator)) {
+                               return(probe_address);
+                       }
+               }
+               probe_address -= probe_decrement;
+               if ((int)probe_address < 0) {
+                       probe_address += PACKAGE_HASH_PRIME;
+               }
+       }
+       return(probe_address);
+}
+
+/*
+ * This function searches through the entire package_hashtable looking
+ * for a package which provides "needle". It returns the index into
+ * the package_hashtable for the providing package.
+ *
+ * needle is the index into name_hashtable of the package we are
+ * looking for.
+ *
+ * start_at is the index in the package_hashtable to start looking
+ * at. If start_at is -1 then start at the beginning. This is to allow
+ * for repeated searches since more than one package might provide
+ * needle.
+ *
+ * FIXME: I don't think this is very efficient, but I thought I'd keep
+ * it simple for now until it proves to be a problem.
+ */
+int search_for_provides(int needle, int start_at) {
+       int i, j;
+       common_node_t *p;
+       for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) {
+               p = package_hashtable[i];
+               if ( p == NULL ) continue;
+               for(j = 0; j < p->num_of_edges; j++)
+                       if ( p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle )
+                               return i;
+       }
+       return -1;
+}
+
+/*
+ * Add an edge to a node
+ */
+void add_edge_to_node(common_node_t *node, edge_t *edge)
+{
+       node->num_of_edges++;
+       node->edge = xrealloc(node->edge, sizeof(edge_t) * (node->num_of_edges + 1));
+       node->edge[node->num_of_edges - 1] = edge;
+}
+
+/*
+ * Create one new node and one new edge for every dependency.
+ *
+ * Dependencies which contain multiple alternatives are represented as
+ * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a
+ * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of
+ * the OR edge contains the full dependency string while the version
+ * field contains the number of EDGE nodes which follow as part of
+ * this alternative.
+ */
+void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type)
+{
+       char *line = bb_xstrdup(whole_line);
+       char *line2;
+       char *line_ptr1 = NULL;
+       char *line_ptr2 = NULL;
+       char *field;
+       char *field2;
+       char *version;
+       edge_t *edge;
+       edge_t *or_edge;
+       int offset_ch;
+
+       field = strtok_r(line, ",", &line_ptr1);
+       do {
+               /* skip leading spaces */
+               field += strspn(field, " ");
+               line2 = bb_xstrdup(field);
+               field2 = strtok_r(line2, "|", &line_ptr2);
+               if ( (edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS) &&
+                    (strcmp(field, field2) != 0)) {
+                       or_edge = (edge_t *)xmalloc(sizeof(edge_t));
+                       or_edge->type = edge_type + 1;
+               } else {
+                       or_edge = NULL;
+               }
+
+               if ( or_edge ) {
+                       or_edge->name = search_name_hashtable(field);
+                       or_edge->version = 0; // tracks the number of altenatives
+
+                       add_edge_to_node(parent_node, or_edge);
+               }
+
+               do {
+                       edge = (edge_t *) xmalloc(sizeof(edge_t));
+                       edge->type = edge_type;
+
+                       /* Skip any extra leading spaces */
+                       field2 += strspn(field2, " ");
+
+                       /* Get dependency version info */
+                       version = strchr(field2, '(');
+                       if (version == NULL) {
+                               edge->operator = VER_ANY;
+                               /* Get the versions hash number, adding it if the number isnt already in there */
+                               edge->version = search_name_hashtable("ANY");
+                       } else {
+                               /* Skip leading ' ' or '(' */
+                               version += strspn(field2, " ");
+                               version += strspn(version, "(");
+                               /* Calculate length of any operator characters */
+                               offset_ch = strspn(version, "<=>");
+                               /* Determine operator */
+                               if (offset_ch > 0) {
+                                       if (strncmp(version, "=", offset_ch) == 0) {
+                                               edge->operator = VER_EQUAL;
+                                       }
+                                       else if (strncmp(version, "<<", offset_ch) == 0) {
+                                               edge->operator = VER_LESS;
+                                       }
+                                       else if (strncmp(version, "<=", offset_ch) == 0) {
+                                               edge->operator = VER_LESS_EQUAL;
+                                       }
+                                       else if (strncmp(version, ">>", offset_ch) == 0) {
+                                               edge->operator = VER_MORE;
+                                       }
+                                       else if (strncmp(version, ">=", offset_ch) == 0) {
+                                               edge->operator = VER_MORE_EQUAL;
+                                       } else {
+                                               bb_error_msg_and_die("Illegal operator\n");
+                                       }
+                               }
+                               /* skip to start of version numbers */
+                               version += offset_ch;
+                               version += strspn(version, " ");
+
+                               /* Truncate version at trailing ' ' or ')' */
+                               version[strcspn(version, " )")] = '\0';
+                               /* Get the versions hash number, adding it if the number isnt already in there */
+                               edge->version = search_name_hashtable(version);
+                       }
+
+                       /* Get the dependency name */
+                       field2[strcspn(field2, " (")] = '\0';
+                       edge->name = search_name_hashtable(field2);
+
+                       if ( or_edge )
+                               or_edge->version++;
+
+                       add_edge_to_node(parent_node, edge);
+               } while ((field2 = strtok_r(NULL, "|", &line_ptr2)) != NULL);
+               free(line2);
+       } while ((field = strtok_r(NULL, ",", &line_ptr1)) != NULL);
+       free(line);
+
+       return;
+}
+
+void free_package(common_node_t *node)
+{
+       unsigned short i;
+       if (node) {
+               for (i = 0; i < node->num_of_edges; i++) {
+                       free(node->edge[i]);
+               }
+               if ( node->edge )
+                       free(node->edge);
+               free(node);
+       }
+}
+
+unsigned int fill_package_struct(char *control_buffer)
+{
+       common_node_t *new_node = (common_node_t *) xcalloc(1, sizeof(common_node_t));
+       const char *field_names[] = { "Package", "Version", "Pre-Depends", "Depends",
+               "Replaces", "Provides", "Conflicts", "Suggests", "Recommends", "Enhances", 0};
+       char *field_name;
+       char *field_value;
+       int field_start = 0;
+       int num = -1;
+       int buffer_length = strlen(control_buffer);
+
+       new_node->version = search_name_hashtable("unknown");
+       while (field_start < buffer_length) {
+               unsigned short field_num;
+
+               field_start += read_package_field(&control_buffer[field_start],
+                               &field_name, &field_value);
+
+               if (field_name == NULL) {
+                       goto fill_package_struct_cleanup; /* Oh no, the dreaded goto statement ! */
+               }
+
+               field_num = compare_string_array(field_names, field_name);
+               switch(field_num) {
+                       case 0: /* Package */
+                               new_node->name = search_name_hashtable(field_value);
+                               break;
+                       case 1: /* Version */
+                               new_node->version = search_name_hashtable(field_value);
+                               break;
+                       case 2: /* Pre-Depends */
+                               add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS);
+                               break;
+                       case 3: /* Depends */
+                               add_split_dependencies(new_node, field_value, EDGE_DEPENDS);
+                               break;
+                       case 4: /* Replaces */
+                               add_split_dependencies(new_node, field_value, EDGE_REPLACES);
+                               break;
+                       case 5: /* Provides */
+                               add_split_dependencies(new_node, field_value, EDGE_PROVIDES);
+                               break;
+                       case 6: /* Conflicts */
+                               add_split_dependencies(new_node, field_value, EDGE_CONFLICTS);
+                               break;
+                       case 7: /* Suggests */
+                               add_split_dependencies(new_node, field_value, EDGE_SUGGESTS);
+                               break;
+                       case 8: /* Recommends */
+                               add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS);
+                               break;
+                       case 9: /* Enhances */
+                               add_split_dependencies(new_node, field_value, EDGE_ENHANCES);
+                               break;
+               }
+fill_package_struct_cleanup:
+               free(field_name);
+               free(field_value);
+       }
+
+       if (new_node->version == search_name_hashtable("unknown")) {
+               free_package(new_node);
+               return(-1);
+       }
+       num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL);
+       if (package_hashtable[num] == NULL) {
+               package_hashtable[num] = new_node;
+       } else {
+               free_package(new_node);
+       }
+       return(num);
+}
+
+/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
+unsigned int get_status(const unsigned int status_node, const int num)
+{
+       char *status_string = name_hashtable[status_hashtable[status_node]->status];
+       char *state_sub_string;
+       unsigned int state_sub_num;
+       int len;
+       int i;
+
+       /* set tmp_string to point to the start of the word number */
+       for (i = 1; i < num; i++) {
+               /* skip past a word */
+               status_string += strcspn(status_string, " ");
+               /* skip past the separating spaces */
+               status_string += strspn(status_string, " ");
+       }
+       len = strcspn(status_string, " \n\0");
+       state_sub_string = bb_xstrndup(status_string, len);
+       state_sub_num = search_name_hashtable(state_sub_string);
+       free(state_sub_string);
+       return(state_sub_num);
+}
+
+void set_status(const unsigned int status_node_num, const char *new_value, const int position)
+{
+       const unsigned int new_value_len = strlen(new_value);
+       const unsigned int new_value_num = search_name_hashtable(new_value);
+       unsigned int want = get_status(status_node_num, 1);
+       unsigned int flag = get_status(status_node_num, 2);
+       unsigned int status = get_status(status_node_num, 3);
+       int want_len = strlen(name_hashtable[want]);
+       int flag_len = strlen(name_hashtable[flag]);
+       int status_len = strlen(name_hashtable[status]);
+       char *new_status;
+
+       switch (position) {
+               case (1):
+                       want = new_value_num;
+                       want_len = new_value_len;
+                       break;
+               case (2):
+                       flag = new_value_num;
+                       flag_len = new_value_len;
+                       break;
+               case (3):
+                       status = new_value_num;
+                       status_len = new_value_len;
+                       break;
+               default:
+                       bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
+       }
+
+       new_status = (char *) xmalloc(want_len + flag_len + status_len + 3);
+       sprintf(new_status, "%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]);
+       status_hashtable[status_node_num]->status = search_name_hashtable(new_status);
+       free(new_status);
+       return;
+}
+
+const char *describe_status(int status_num) {
+       int status_want, status_state ;
+       if ( status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0 )
+               return "is not installed or flagged to be installed\n";
+
+       status_want = get_status(status_num, 1);
+       status_state = get_status(status_num, 3);
+
+       if ( status_state == search_name_hashtable("installed") ) {
+               if ( status_want == search_name_hashtable("install") )
+                       return "is installed";
+               if ( status_want == search_name_hashtable("deinstall") )
+                       return "is marked to be removed";
+               if ( status_want == search_name_hashtable("purge") )
+                       return "is marked to be purged";
+       }
+       if ( status_want ==  search_name_hashtable("unknown") )
+               return "is in an indeterminate state";
+       if ( status_want == search_name_hashtable("install") )
+               return "is marked to be installed";
+
+       return "is not installed or flagged to be installed";
+}
+
+
+void index_status_file(const char *filename)
+{
+       FILE *status_file;
+       char *control_buffer;
+       char *status_line;
+       status_node_t *status_node = NULL;
+       unsigned int status_num;
+
+       status_file = bb_xfopen(filename, "r");
+       while ((control_buffer = fgets_str(status_file, "\n\n")) != NULL) {
+               const unsigned int package_num = fill_package_struct(control_buffer);
+               if (package_num != -1) {
+                       status_node = xmalloc(sizeof(status_node_t));
+                       /* fill_package_struct doesnt handle the status field */
+                       status_line = strstr(control_buffer, "Status:");
+                       if (status_line != NULL) {
+                               status_line += 7;
+                               status_line += strspn(status_line, " \n\t");
+                               status_line = bb_xstrndup(status_line, strcspn(status_line, "\n\0"));
+                               status_node->status = search_name_hashtable(status_line);
+                               free(status_line);
+                       }
+                       status_node->package = package_num;
+                       status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]);
+                       status_hashtable[status_num] = status_node;
+               }
+               free(control_buffer);
+       }
+       fclose(status_file);
+       return;
+}
+
+#if 0 /* this code is no longer used */
+char *get_depends_field(common_node_t *package, const int depends_type)
+{
+       char *depends = NULL;
+       char *old_sep = (char *)xcalloc(1, 3);
+       char *new_sep = (char *)xcalloc(1, 3);
+       int line_size = 0;
+       int depends_size;
+
+       int i;
+
+       for (i = 0; i < package->num_of_edges; i++) {
+               if ((package->edge[i]->type == EDGE_OR_PRE_DEPENDS) ||
+                       (package->edge[i]->type == EDGE_OR_DEPENDS)) {
+               }
+
+               if ((package->edge[i]->type == depends_type) ||
+                       (package->edge[i]->type == depends_type + 1)) {
+                       /* Check if its the first time through */
+
+                       depends_size = 8 + strlen(name_hashtable[package->edge[i]->name])
+                               + strlen(name_hashtable[package->edge[i]->version]);
+                       line_size += depends_size;
+                       depends = (char *) xrealloc(depends, line_size + 1);
+
+                       /* Check to see if this dependency is the type we are looking for
+                        * +1 to check for 'extra' types, e.g. ored dependecies */
+                       strcpy(old_sep, new_sep);
+                       if (package->edge[i]->type == depends_type) {
+                               strcpy(new_sep, ", ");
+                       }
+                       else if (package->edge[i]->type == depends_type + 1) {
+                               strcpy(new_sep, "| ");
+                       }
+
+                       if (depends_size == line_size) {
+                               strcpy(depends, "");
+                       } else {
+                               if ((strcmp(old_sep, "| ") == 0) && (strcmp(new_sep, "| ") == 0)) {
+                                       strcat(depends, " | ");
+                               } else {
+                                       strcat(depends, ", ");
+                               }
+                       }
+
+                       strcat(depends, name_hashtable[package->edge[i]->name]);
+                       if (strcmp(name_hashtable[package->edge[i]->version], "NULL") != 0) {
+                               if (package->edge[i]->operator == VER_EQUAL) {
+                                       strcat(depends, " (= ");
+                               }
+                               else if (package->edge[i]->operator == VER_LESS) {
+                                       strcat(depends, " (<< ");
+                               }
+                               else if (package->edge[i]->operator == VER_LESS_EQUAL) {
+                                       strcat(depends, " (<= ");
+                               }
+                               else if (package->edge[i]->operator == VER_MORE) {
+                                       strcat(depends, " (>> ");
+                               }
+                               else if (package->edge[i]->operator == VER_MORE_EQUAL) {
+                                       strcat(depends, " (>= ");
+                               } else {
+                                       strcat(depends, " (");
+                               }
+                               strcat(depends, name_hashtable[package->edge[i]->version]);
+                               strcat(depends, ")");
+                       }
+               }
+       }
+       return(depends);
+}
+#endif
+
+void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
+{
+       char *name;
+       char *value;
+       int start = 0;
+       while (1) {
+               start += read_package_field(&control_buffer[start], &name, &value);
+               if (name == NULL) {
+                       break;
+               }
+               if (strcmp(name, "Status") != 0) {
+                       fprintf(new_status_file, "%s: %s\n", name, value);
+               }
+       }
+       return;
+}
+
+/* This could do with a cleanup */
+void write_status_file(deb_file_t **deb_file)
+{
+       FILE *old_status_file = bb_xfopen("/var/lib/dpkg/status", "r");
+       FILE *new_status_file = bb_xfopen("/var/lib/dpkg/status.udeb", "w");
+       char *package_name;
+       char *status_from_file;
+       char *control_buffer = NULL;
+       char *tmp_string;
+       int status_num;
+       int field_start = 0;
+       int write_flag;
+       int i = 0;
+
+       /* Update previously known packages */
+       while ((control_buffer = fgets_str(old_status_file, "\n\n")) != NULL) {
+               if ((tmp_string = strstr(control_buffer, "Package:")) == NULL) {
+                       continue;
+               }
+
+               tmp_string += 8;
+               tmp_string += strspn(tmp_string, " \n\t");
+               package_name = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n\0"));
+               write_flag = FALSE;
+               tmp_string = strstr(control_buffer, "Status:");
+               if (tmp_string != NULL) {
+                       /* Seperate the status value from the control buffer */
+                       tmp_string += 7;
+                       tmp_string += strspn(tmp_string, " \n\t");
+                       status_from_file = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+               } else {
+                       status_from_file = NULL;
+               }
+
+               /* Find this package in the status hashtable */
+               status_num = search_status_hashtable(package_name);
+               if (status_hashtable[status_num] != NULL) {
+                       const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status];
+                       if (strcmp(status_from_file, status_from_hashtable) != 0) {
+                               /* New status isnt exactly the same as old status */
+                               const int state_status = get_status(status_num, 3);
+                               if ((strcmp("installed", name_hashtable[state_status]) == 0) ||
+                                       (strcmp("unpacked", name_hashtable[state_status]) == 0)) {
+                                       /* We need to add the control file from the package */
+                                       i = 0;
+                                       while(deb_file[i] != NULL) {
+                                               if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) {
+                                                       /* Write a status file entry with a modified status */
+                                                       /* remove trailing \n's */
+                                                       write_buffer_no_status(new_status_file, deb_file[i]->control_file);
+                                                       set_status(status_num, "ok", 2);
+                                                       fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
+                                                       write_flag = TRUE;
+                                                       break;
+                                               }
+                                               i++;
+                                       }
+                                       /* This is temperary, debugging only */
+                                       if (deb_file[i] == NULL) {
+                                               bb_error_msg_and_die("ALERT: Couldnt find a control file, your status file may be broken, status may be incorrect for %s", package_name);
+                                       }
+                               }
+                               else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
+                                       /* Only write the Package, Status, Priority and Section lines */
+                                       fprintf(new_status_file, "Package: %s\n", package_name);
+                                       fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+
+                                       while (1) {
+                                               char *field_name;
+                                               char *field_value;
+                                               field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+                                               if (field_name == NULL) {
+                                                       break;
+                                               }
+                                               if ((strcmp(field_name, "Priority") == 0) ||
+                                                       (strcmp(field_name, "Section") == 0)) {
+                                                       fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+                                               }
+                                       }
+                                       write_flag = TRUE;
+                                       fputs("\n", new_status_file);
+                               }
+                               else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
+                                       /* only change the status line */
+                                       while (1) {
+                                               char *field_name;
+                                               char *field_value;
+                                               field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+                                               if (field_name == NULL) {
+                                                       break;
+                                               }
+                                               /* Setup start point for next field */
+                                               if (strcmp(field_name, "Status") == 0) {
+                                                       fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+                                               } else {
+                                                       fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+                                               }
+                                       }
+                                       write_flag = TRUE;
+                                       fputs("\n", new_status_file);
+                               }
+                       }
+               }
+               /* If the package from the status file wasnt handle above, do it now*/
+               if (! write_flag) {
+                       fprintf(new_status_file, "%s\n\n", control_buffer);
+               }
+
+               free(status_from_file);
+               free(package_name);
+               free(control_buffer);
+       }
+
+       /* Write any new packages */
+       for(i = 0; deb_file[i] != NULL; i++) {
+               status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]);
+               if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) {
+                       write_buffer_no_status(new_status_file, deb_file[i]->control_file);
+                       set_status(status_num, "ok", 2);
+                       fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
+               }
+       }
+       fclose(old_status_file);
+       fclose(new_status_file);
+
+
+       /* Create a separate backfile to dpkg */
+       if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) {
+               struct stat stat_buf;
+               if (stat("/var/lib/dpkg/status", &stat_buf) == 0) {
+                       bb_error_msg_and_die("Couldnt create backup status file");
+               }
+               /* Its ok if renaming the status file fails because status
+                * file doesnt exist, maybe we are starting from scratch */
+               bb_error_msg("No status file found, creating new one");
+       }
+
+       if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) {
+               bb_error_msg_and_die("DANGER: Couldnt create status file, you need to manually repair your status file");
+       }
+}
+
+/* This function returns TRUE if the given package can satisfy a
+ * dependency of type depend_type.
+ *
+ * A pre-depends is satisfied only if a package is already installed,
+ * which a regular depends can be satisfied by a package which we want
+ * to install.
+ */
+int package_satisfies_dependency(int package, int depend_type)
+{
+       int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
+
+       /* status could be unknown if package is a pure virtual
+        * provides which cannot satisfy any dependency by itself.
+        */
+       if ( status_hashtable[status_num] == NULL )
+               return 0;
+
+       switch (depend_type) {
+       case EDGE_PRE_DEPENDS:  return get_status(status_num, 3) == search_name_hashtable("installed");
+       case EDGE_DEPENDS:      return get_status(status_num, 1) == search_name_hashtable("install");
+       }
+       return 0;
+}
+
+int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count)
+{
+       int *conflicts = NULL;
+       int conflicts_num = 0;
+       int i = deb_start;
+       int j;
+
+       /* Check for conflicts
+        * TODO: TEST if conflicts with other packages to be installed
+        *
+        * Add install packages and the packages they provide
+        * to the list of files to check conflicts for
+        */
+
+       /* Create array of package numbers to check against
+        * installed package for conflicts*/
+       while (deb_file[i] != NULL) {
+               const unsigned int package_num = deb_file[i]->package;
+               conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
+               conflicts[conflicts_num] = package_num;
+               conflicts_num++;
+               /* add provides to conflicts list */
+               for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
+                       if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) {
+                               const int conflicts_package_num = search_package_hashtable(
+                                       package_hashtable[package_num]->edge[j]->name,
+                                       package_hashtable[package_num]->edge[j]->version,
+                                       package_hashtable[package_num]->edge[j]->operator);
+                               if (package_hashtable[conflicts_package_num] == NULL) {
+                                       /* create a new package */
+                                       common_node_t *new_node = (common_node_t *) xmalloc(sizeof(common_node_t));
+                                       new_node->name = package_hashtable[package_num]->edge[j]->name;
+                                       new_node->version = package_hashtable[package_num]->edge[j]->version;
+                                       new_node->num_of_edges = 0;
+                                       new_node->edge = NULL;
+                                       package_hashtable[conflicts_package_num] = new_node;
+                               }
+                               conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
+                               conflicts[conflicts_num] = conflicts_package_num;
+                               conflicts_num++;
+                       }
+               }
+               i++;
+       }
+
+       /* Check conflicts */
+       i = 0;
+       while (deb_file[i] != NULL) {
+               const common_node_t *package_node = package_hashtable[deb_file[i]->package];
+               int status_num = 0;
+               status_num = search_status_hashtable(name_hashtable[package_node->name]);
+
+               if (get_status(status_num, 3) == search_name_hashtable("installed")) {
+                       i++;
+                       continue;
+               }
+
+               for (j = 0; j < package_node->num_of_edges; j++) {
+                       const edge_t *package_edge = package_node->edge[j];
+
+                       if (package_edge->type == EDGE_CONFLICTS) {
+                               const unsigned int package_num =
+                                       search_package_hashtable(package_edge->name,
+                                                                package_edge->version,
+                                                                package_edge->operator);
+                               int result = 0;
+                               if (package_hashtable[package_num] != NULL) {
+                                       status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
+
+                                       if (get_status(status_num, 1) == search_name_hashtable("install")) {
+                                               result = test_version(package_hashtable[deb_file[i]->package]->version,
+                                                       package_edge->version, package_edge->operator);
+                                       }
+                               }
+
+                               if (result) {
+                                       bb_error_msg_and_die("Package %s conflicts with %s",
+                                               name_hashtable[package_node->name],
+                                               name_hashtable[package_edge->name]);
+                               }
+                       }
+               }
+               i++;
+       }       
+
+
+       /* Check dependendcies */
+       for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
+               int status_num = 0;
+               int number_of_alternatives = 0;
+               const edge_t * root_of_alternatives = NULL;
+               const common_node_t *package_node = package_hashtable[i];
+
+               /* If the package node does not exist then this
+                * package is a virtual one. In which case there are
+                * no dependencies to check.
+                */
+               if ( package_node == NULL ) continue;
+
+               status_num = search_status_hashtable(name_hashtable[package_node->name]);
+
+               /* If there is no status then this package is a
+                * virtual one provided by something else. In which
+                * case there are no dependencies to check.
+                */
+               if ( status_hashtable[status_num] == NULL ) continue;
+
+               /* If we don't want this package installed then we may
+                * as well ignore it's dependencies.
+                */
+               if (get_status(status_num, 1) != search_name_hashtable("install")) {
+                       continue;
+               }
+
+#if 0
+               /* This might be needed so we don't complain about
+                * things which are broken but unrelated to the
+                * packages that are currently being installed
+                */
+                if (state_status == search_name_hashtable("installed"))
+                        continue;
+#endif
+
+               /* This code is tested only for EDGE_DEPENDS, since I
+                * have no suitable pre-depends available. There is no
+                * reason that it shouldn't work though :-)
+                */
+               for (j = 0; j < package_node->num_of_edges; j++) {
+                       const edge_t *package_edge = package_node->edge[j];
+                       unsigned int package_num;
+                       
+                       if ( package_edge->type == EDGE_OR_PRE_DEPENDS ||
+                            package_edge->type == EDGE_OR_DEPENDS ) {  /* start an EDGE_OR_ list */
+                               number_of_alternatives = package_edge->version;
+                               root_of_alternatives = package_edge;
+                               continue;
+                       } else if ( number_of_alternatives == 0 ) {     /* not in the middle of an EDGE_OR_ list */
+                               number_of_alternatives = 1;
+                               root_of_alternatives = NULL;
+                       }
+
+                       package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
+
+                       if (package_edge->type == EDGE_PRE_DEPENDS ||
+                           package_edge->type == EDGE_DEPENDS ) {
+                               int result=1;
+                               status_num = 0;
+
+                               /* If we are inside an alternative then check
+                                * this edge is the right type.
+                                *
+                                * EDGE_DEPENDS == OR_DEPENDS -1
+                                * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1
+                                */
+                               if ( root_of_alternatives && package_edge->type != root_of_alternatives->type - 1)
+                                       bb_error_msg_and_die("Fatal error. Package dependencies corrupt: %d != %d - 1 \n",
+                                                            package_edge->type, root_of_alternatives->type);
+
+                               if (package_hashtable[package_num] != NULL)
+                                       result = !package_satisfies_dependency(package_num, package_edge->type);
+
+                               if (result) { /* check for other package which provide what we are looking for */
+                                       int provider = -1;
+
+                                       while ( (provider = search_for_provides(package_edge->name, provider) ) > -1 ) {
+                                               if ( package_hashtable[provider] == NULL ) {
+                                                       printf("Have a provider but no package information for it\n");
+                                                       continue;
+                                               }
+                                               result = !package_satisfies_dependency(provider, package_edge->type);
+
+                                               if ( result == 0 )
+                                                       break;
+                                       }
+                               }
+
+                               /* It must be already installed, or to be installed */
+                               number_of_alternatives--;
+                               if (result && number_of_alternatives == 0) {
+                                       if ( root_of_alternatives )
+                                               bb_error_msg_and_die(
+                                                       "Package %s %sdepends on %s, "
+                                                       "which cannot be satisfied",
+                                                       name_hashtable[package_node->name],
+                                                       package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
+                                                       name_hashtable[root_of_alternatives->name]);
+                                       else
+                                               bb_error_msg_and_die(
+                                                       "Package %s %sdepends on %s, which %s\n",
+                                                       name_hashtable[package_node->name],
+                                                       package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
+                                                       name_hashtable[package_edge->name],
+                                                       describe_status(status_num));
+                               } else if ( result == 0 && number_of_alternatives ) {
+                                       /* we've found a package which
+                                        * satisfies the dependency,
+                                        * so skip over the rest of
+                                        * the alternatives.
+                                        */
+                                       j += number_of_alternatives;
+                                       number_of_alternatives = 0;
+                               }
+                       }
+               }
+       }
+       free(conflicts);
+       return(TRUE);
+}
+
+char **create_list(const char *filename)
+{
+       FILE *list_stream;
+       char **file_list = NULL;
+       char *line = NULL;
+       int count = 0;
+
+       /* don't use [xw]fopen here, handle error ourself */
+       list_stream = fopen(filename, "r");
+       if (list_stream == NULL) {
+               return(NULL);
+       }
+
+       while ((line = bb_get_chomped_line_from_file(list_stream)) != NULL) {
+               file_list = xrealloc(file_list, sizeof(char *) * (count + 2));
+               file_list[count] = line;
+               count++;
+       }
+       fclose(list_stream);
+
+       if (count == 0) {
+               return(NULL);
+       } else {
+               file_list[count] = NULL;
+               return(file_list);
+       }
+}
+
+/* maybe i should try and hook this into remove_file.c somehow */
+int remove_file_array(char **remove_names, char **exclude_names)
+{
+       struct stat path_stat;
+       int match_flag;
+       int remove_flag = FALSE;
+       int i,j;
+
+       if (remove_names == NULL) {
+               return(FALSE);
+       }
+       for (i = 0; remove_names[i] != NULL; i++) {
+               match_flag = FALSE;
+               if (exclude_names != NULL) {
+                       for (j = 0; exclude_names[j] != 0; j++) {
+                               if (strcmp(remove_names[i], exclude_names[j]) == 0) {
+                                       match_flag = TRUE;
+                                       break;
+                               }
+                       }
+               }
+               if (!match_flag) {
+                       if (lstat(remove_names[i], &path_stat) < 0) {
+                               continue;
+                       }
+                       if (S_ISDIR(path_stat.st_mode)) {
+                               if (rmdir(remove_names[i]) != -1) {
+                                       remove_flag = TRUE;
+                               }
+                       } else {
+                               if (unlink(remove_names[i]) != -1) {
+                                       remove_flag = TRUE;
+                               }
+                       }
+               }
+       }
+       return(remove_flag);
+}
+
+int run_package_script(const char *package_name, const char *script_type)
+{
+       struct stat path_stat;
+       char *script_path;
+       int result;
+
+       script_path = xmalloc(strlen(package_name) + strlen(script_type) + 21);
+       sprintf(script_path, "/var/lib/dpkg/info/%s.%s", package_name, script_type);
+
+       /* If the file doesnt exist is isnt a fatal */
+       if (lstat(script_path, &path_stat) < 0) {
+               result = EXIT_SUCCESS;
+       } else {
+               result = system(script_path);
+       }
+       free(script_path);
+       return(result);
+}
+
+const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm",
+       "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL };
+
+char **all_control_list(const char *package_name)
+{
+       unsigned short i = 0;
+       char **remove_files;
+
+       /* Create a list of all /var/lib/dpkg/info/<package> files */
+       remove_files = malloc(sizeof(all_control_files));
+       while (all_control_files[i]) {
+               remove_files[i] = xmalloc(strlen(package_name) + strlen(all_control_files[i]) + 21);
+               sprintf(remove_files[i], "/var/lib/dpkg/info/%s.%s", package_name, all_control_files[i]);
+               i++;
+       }
+       remove_files[sizeof(all_control_files)/sizeof(char*) - 1] = NULL;
+
+       return(remove_files);
+}
+
+void free_array(char **array)
+{
+
+       if (array) {
+               unsigned short i = 0;
+               while (array[i]) {
+                       free(array[i]);
+                       i++;
+               }
+               free(array);
+       }
+}
+
+/* This function lists information on the installed packages. It loops through
+ * the status_hashtable to retrieve the info. This results in smaller code than
+ * scanning the status file. The resulting list, however, is unsorted.
+ */
+void list_packages(void)
+{
+        int i;
+
+       printf("    Name           Version\n");
+       printf("+++-==============-==============\n");
+
+       /* go through status hash, dereference package hash and finally strings */
+       for (i=0; i<STATUS_HASH_PRIME+1; i++) {
+
+               if (status_hashtable[i]) {
+                       const char *stat_str;  /* status string */
+                       const char *name_str;  /* package name */
+                       const char *vers_str;  /* version */
+                       char  s1, s2;          /* status abbreviations */
+                       int   spccnt;          /* space count */
+                       int   j;
+
+                       stat_str = name_hashtable[status_hashtable[i]->status];
+                       name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
+                       vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
+
+                       /* get abbreviation for status field 1 */
+                       s1 = stat_str[0] == 'i' ? 'i' : 'r';
+
+                       /* get abbreviation for status field 2 */
+                       for (j=0, spccnt=0; stat_str[j] && spccnt<2; j++) {
+                               if (stat_str[j] == ' ') spccnt++;
+                       }
+                       s2 = stat_str[j];
+
+                       /* print out the line formatted like Debian dpkg */
+                       printf("%c%c  %-14s %s\n", s1, s2, name_str, vers_str);
+               }
+    }
+}
+
+void remove_package(const unsigned int package_num, int noisy)
+{
+       const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+       const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+       const unsigned int status_num = search_status_hashtable(package_name);
+       const int package_name_length = strlen(package_name);
+       char **remove_files;
+       char **exclude_files;
+       char list_name[package_name_length + 25];
+       char conffile_name[package_name_length + 30];
+       int return_value;
+
+       if ( noisy )
+               printf("Removing %s (%s) ...\n", package_name, package_version);
+
+       /* run prerm script */
+       return_value = run_package_script(package_name, "prerm");
+       if (return_value == -1) {
+               bb_error_msg_and_die("script failed, prerm failure");
+       }
+
+       /* Create a list of files to remove, and a separate list of those to keep */
+       sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
+       remove_files = create_list(list_name);
+
+       sprintf(conffile_name, "/var/lib/dpkg/info/%s.conffiles", package_name);
+       exclude_files = create_list(conffile_name);
+
+       /* Some directories cant be removed straight away, so do multiple passes */
+       while (remove_file_array(remove_files, exclude_files));
+       free_array(exclude_files);
+       free_array(remove_files);
+
+       /* Create a list of files in /var/lib/dpkg/info/<package>.* to keep  */
+       exclude_files = xmalloc(sizeof(char*) * 3);
+       exclude_files[0] = bb_xstrdup(conffile_name);
+       exclude_files[1] = xmalloc(package_name_length + 27);
+       sprintf(exclude_files[1], "/var/lib/dpkg/info/%s.postrm", package_name);
+       exclude_files[2] = NULL;
+
+       /* Create a list of all /var/lib/dpkg/info/<package> files */
+       remove_files = all_control_list(package_name);
+
+       remove_file_array(remove_files, exclude_files);
+       free_array(remove_files);
+       free_array(exclude_files);
+
+       /* rename <package>.conffile to <package>.list */
+       rename(conffile_name, list_name);
+
+       /* Change package status */
+       set_status(status_num, "config-files", 3);
+}
+
+void purge_package(const unsigned int package_num)
+{
+       const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+       const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+       const unsigned int status_num = search_status_hashtable(package_name);
+       char **remove_files;
+       char **exclude_files;
+       char list_name[strlen(package_name) + 25];
+
+       printf("Purging %s (%s) ...\n", package_name, package_version);
+
+       /* run prerm script */
+       if (run_package_script(package_name, "prerm") != 0) {
+               bb_error_msg_and_die("script failed, prerm failure");
+       }
+
+       /* Create a list of files to remove */
+       sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
+       remove_files = create_list(list_name);
+
+       exclude_files = xmalloc(sizeof(char*));
+       exclude_files[0] = NULL;
+
+       /* Some directories cant be removed straight away, so do multiple passes */
+       while (remove_file_array(remove_files, exclude_files));
+       free_array(remove_files);
+
+       /* Create a list of all /var/lib/dpkg/info/<package> files */
+       remove_files = all_control_list(package_name);
+       remove_file_array(remove_files, exclude_files);
+       free_array(remove_files);
+       free(exclude_files);
+
+       /* run postrm script */
+       if (run_package_script(package_name, "postrm") == -1) {
+               bb_error_msg_and_die("postrm fialure.. set status to what?");
+       }
+
+       /* Change package status */
+       set_status(status_num, "not-installed", 3);
+}
+
+static archive_handle_t *init_archive_deb_ar(const char *filename)
+{
+       archive_handle_t *ar_handle;
+
+       /* Setup an ar archive handle that refers to the gzip sub archive */
+       ar_handle = init_handle();
+       ar_handle->filter = filter_accept_list_reassign;
+       ar_handle->src_fd = bb_xopen(filename, O_RDONLY);
+
+       return(ar_handle);
+}
+
+static void init_archive_deb_control(archive_handle_t *ar_handle)
+{
+       archive_handle_t *tar_handle;
+
+       /* Setup the tar archive handle */
+       tar_handle = init_handle();
+       tar_handle->src_fd = ar_handle->src_fd;
+
+       /* We don't care about data.tar.* or debian-binary, just control.tar.* */
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+       ar_handle->accept = llist_add_to(NULL, "control.tar.gz");
+#endif
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+       ar_handle->accept = llist_add_to(ar_handle->accept, "control.tar.bz2");
+#endif
+
+       /* Assign the tar handle as a subarchive of the ar handle */
+       ar_handle->sub_archive = tar_handle;
+
+       return;
+}
+
+static void init_archive_deb_data(archive_handle_t *ar_handle)
+{
+       archive_handle_t *tar_handle;
+
+       /* Setup the tar archive handle */
+       tar_handle = init_handle();
+       tar_handle->src_fd = ar_handle->src_fd;
+
+       /* We don't care about control.tar.* or debian-binary, just data.tar.* */
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+       ar_handle->accept = llist_add_to(NULL, "data.tar.gz");
+#endif
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+       ar_handle->accept = llist_add_to(ar_handle->accept, "data.tar.bz2");
+#endif
+
+       /* Assign the tar handle as a subarchive of the ar handle */
+       ar_handle->sub_archive = tar_handle;
+
+       return;
+}
+
+static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
+{
+       ar_handle->sub_archive->action_data = data_extract_to_buffer;
+       ar_handle->sub_archive->accept = myaccept;
+
+       unpack_ar_archive(ar_handle);
+       close(ar_handle->src_fd);
+
+       return(ar_handle->sub_archive->buffer);
+}
+
+static void data_extract_all_prefix(archive_handle_t *archive_handle)
+{
+       char *name_ptr = archive_handle->file_header->name;
+
+       name_ptr += strspn(name_ptr, "./");
+       if (name_ptr[0] != '\0') {
+               archive_handle->file_header->name = xmalloc(strlen(archive_handle->buffer) + 2 + strlen(name_ptr));
+               strcpy(archive_handle->file_header->name, archive_handle->buffer);
+               strcat(archive_handle->file_header->name, name_ptr);
+               data_extract_all(archive_handle);
+       }
+       return;
+}
+
+static void unpack_package(deb_file_t *deb_file)
+{
+       const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
+       const unsigned int status_num = search_status_hashtable(package_name);
+       const unsigned int status_package_num = status_hashtable[status_num]->package;
+       char *info_prefix;
+       archive_handle_t *archive_handle;
+       FILE *out_stream;
+       llist_t *accept_list = NULL;
+       int i = 0;
+
+       /* If existing version, remove it first */
+       if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
+               /* Package is already installed, remove old version first */
+               printf("Preparing to replace %s %s (using %s) ...\n", package_name,
+                       name_hashtable[package_hashtable[status_package_num]->version],
+                       deb_file->filename);
+               remove_package(status_package_num, 0);
+       } else {
+               printf("Unpacking %s (from %s) ...\n", package_name, deb_file->filename);
+       }
+
+       /* Extract control.tar.gz to /var/lib/dpkg/info/<package>.filename */
+       info_prefix = (char *) xmalloc(strlen(package_name) + 20 + 4 + 2);
+       sprintf(info_prefix, "/var/lib/dpkg/info/%s.", package_name);
+       archive_handle = init_archive_deb_ar(deb_file->filename);
+       init_archive_deb_control(archive_handle);
+
+       while(all_control_files[i]) {
+               char *c = (char *) xmalloc(3 + bb_strlen(all_control_files[i]));
+               sprintf(c, "./%s", all_control_files[i]);
+               accept_list= llist_add_to(accept_list, c);
+               i++;
+       }
+       archive_handle->sub_archive->accept = accept_list;
+       archive_handle->sub_archive->filter = filter_accept_list;
+       archive_handle->sub_archive->action_data = data_extract_all_prefix;
+       archive_handle->sub_archive->buffer = info_prefix;
+       archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
+       unpack_ar_archive(archive_handle);
+
+       /* Run the preinst prior to extracting */
+       if (run_package_script(package_name, "preinst") != 0) {
+               /* when preinst returns exit code != 0 then quit installation process */
+               bb_error_msg_and_die("subprocess pre-installation script returned error.");
+       }
+
+       /* Extract data.tar.gz to the root directory */
+       archive_handle = init_archive_deb_ar(deb_file->filename);
+       init_archive_deb_data(archive_handle);
+       archive_handle->sub_archive->action_data = data_extract_all_prefix;
+       archive_handle->sub_archive->buffer = "/";
+       archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
+       unpack_ar_archive(archive_handle);
+
+       /* Create the list file */
+       strcat(info_prefix, "list");
+       out_stream = bb_xfopen(info_prefix, "w");
+       while (archive_handle->sub_archive->passed) {
+               /* the leading . has been stripped by data_extract_all_prefix already */
+               fputs(archive_handle->sub_archive->passed->data, out_stream);
+               fputc('\n', out_stream);
+               archive_handle->sub_archive->passed = archive_handle->sub_archive->passed->link;
+       }
+       fclose(out_stream);
+
+       /* change status */
+       set_status(status_num, "install", 1);
+       set_status(status_num, "unpacked", 3);
+
+       free(info_prefix);
+}
+
+void configure_package(deb_file_t *deb_file)
+{
+       const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
+       const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version];
+       const int status_num = search_status_hashtable(package_name);
+
+       printf("Setting up %s (%s) ...\n", package_name, package_version);
+
+       /* Run the postinst script */
+       if (run_package_script(package_name, "postinst") != 0) {
+               /* TODO: handle failure gracefully */
+               bb_error_msg_and_die("postrm failure.. set status to what?");
+       }
+       /* Change status to reflect success */
+       set_status(status_num, "install", 1);
+       set_status(status_num, "installed", 3);
+}
+
+int dpkg_main(int argc, char **argv)
+{
+       deb_file_t **deb_file = NULL;
+       status_node_t *status_node;
+       int opt;
+       int package_num;
+       int dpkg_opt = 0;
+       int deb_count = 0;
+       int state_status;
+       int status_num;
+       int i;
+
+       while ((opt = getopt(argc, argv, "CF:ilPru")) != -1) {
+               switch (opt) {
+                       case 'C': // equivalent to --configure in official dpkg
+                               dpkg_opt |= dpkg_opt_configure;
+                               dpkg_opt |= dpkg_opt_package_name;
+                               break;
+                       case 'F': // equivalent to --force in official dpkg
+                               if (strcmp(optarg, "depends") == 0) {
+                                       dpkg_opt |= dpkg_opt_force_ignore_depends;
+                               }
+                               break;
+                       case 'i':
+                               dpkg_opt |= dpkg_opt_install;
+                               dpkg_opt |= dpkg_opt_filename;
+                               break;
+                       case 'l':
+                               dpkg_opt |= dpkg_opt_list_installed;
+                               break;
+                       case 'P':
+                               dpkg_opt |= dpkg_opt_purge;
+                               dpkg_opt |= dpkg_opt_package_name;
+                               break;
+                       case 'r':
+                               dpkg_opt |= dpkg_opt_remove;
+                               dpkg_opt |= dpkg_opt_package_name;
+                               break;
+                       case 'u':       /* Equivalent to --unpack in official dpkg */
+                               dpkg_opt |= dpkg_opt_unpack;
+                               dpkg_opt |= dpkg_opt_filename;
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+       /* check for non-otion argument if expected  */
+       if ((dpkg_opt == 0) || ((argc == optind) && !(dpkg_opt && dpkg_opt_list_installed))) {
+               bb_show_usage();
+       }
+
+/*     puts("(Reading database ... xxxxx files and directories installed.)"); */
+       index_status_file("/var/lib/dpkg/status");
+
+       /* if the list action was given print the installed packages and exit */
+       if (dpkg_opt & dpkg_opt_list_installed) {
+               list_packages();
+               return(EXIT_SUCCESS);
+       }
+
+       /* Read arguments and store relevant info in structs */
+       while (optind < argc) {
+               /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */
+               deb_file = xrealloc(deb_file, sizeof(deb_file_t *) * (deb_count + 2));
+               deb_file[deb_count] = (deb_file_t *) xmalloc(sizeof(deb_file_t));
+               if (dpkg_opt & dpkg_opt_filename) {
+                       archive_handle_t *archive_handle;
+                       llist_t *control_list = NULL;
+
+                       /* Extract the control file */
+                       control_list = llist_add_to(NULL, "./control");
+                       archive_handle = init_archive_deb_ar(argv[optind]);
+                       init_archive_deb_control(archive_handle);
+                       deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
+                       if (deb_file[deb_count]->control_file == NULL) {
+                               bb_error_msg_and_die("Couldnt extract control file");
+                       }
+                       deb_file[deb_count]->filename = bb_xstrdup(argv[optind]);
+                       package_num = fill_package_struct(deb_file[deb_count]->control_file);
+
+                       if (package_num == -1) {
+                               bb_error_msg("Invalid control file in %s", argv[optind]);
+                               continue;
+                       }
+                       deb_file[deb_count]->package = (unsigned int) package_num;
+
+                       /* Add the package to the status hashtable */
+                       if ((dpkg_opt & dpkg_opt_unpack) || (dpkg_opt & dpkg_opt_install)) {
+                               /* Try and find a currently installed version of this package */
+                               status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]);
+                               /* If no previous entry was found initialise a new entry */
+                               if ((status_hashtable[status_num] == NULL) ||
+                                       (status_hashtable[status_num]->status == 0)) {
+                                       status_node = (status_node_t *) xmalloc(sizeof(status_node_t));
+                                       status_node->package = deb_file[deb_count]->package;
+                                       /* reinstreq isnt changed to "ok" until the package control info
+                                        * is written to the status file*/
+                                       status_node->status = search_name_hashtable("install reinstreq not-installed");
+                                       status_hashtable[status_num] = status_node;
+                               } else {
+                                       set_status(status_num, "install", 1);
+                                       set_status(status_num, "reinstreq", 2);
+                               }
+                       }
+               }
+               else if (dpkg_opt & dpkg_opt_package_name) {
+                       deb_file[deb_count]->filename = NULL;
+                       deb_file[deb_count]->control_file = NULL;
+                       deb_file[deb_count]->package = search_package_hashtable(
+                               search_name_hashtable(argv[optind]),
+                               search_name_hashtable("ANY"), VER_ANY);
+                       if (package_hashtable[deb_file[deb_count]->package] == NULL) {
+                               bb_error_msg_and_die("Package %s is uninstalled or unknown\n", argv[optind]);
+                       }
+                       package_num = deb_file[deb_count]->package;
+                       status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
+                       state_status = get_status(status_num, 3);
+
+                       /* check package status is "installed" */
+                       if (dpkg_opt & dpkg_opt_remove) {
+                               if ((strcmp(name_hashtable[state_status], "not-installed") == 0) ||
+                                       (strcmp(name_hashtable[state_status], "config-files") == 0)) {
+                                       bb_error_msg_and_die("%s is already removed.", name_hashtable[package_hashtable[package_num]->name]);
+                               }
+                               set_status(status_num, "deinstall", 1);
+                       }
+                       else if (dpkg_opt & dpkg_opt_purge) {
+                               /* if package status is "conf-files" then its ok */
+                               if (strcmp(name_hashtable[state_status], "not-installed") == 0) {
+                                       bb_error_msg_and_die("%s is already purged.", name_hashtable[package_hashtable[package_num]->name]);
+                               }
+                               set_status(status_num, "purge", 1);
+                       }
+               }
+               deb_count++;
+               optind++;
+       }
+       deb_file[deb_count] = NULL;
+
+       /* Check that the deb file arguments are installable */
+       if ((dpkg_opt & dpkg_opt_force_ignore_depends) != dpkg_opt_force_ignore_depends) {
+               if (!check_deps(deb_file, 0, deb_count)) {
+                       bb_error_msg_and_die("Dependency check failed");
+               }
+       }
+
+       /* TODO: install or remove packages in the correct dependency order */
+       for (i = 0; i < deb_count; i++) {
+               /* Remove or purge packages */
+               if (dpkg_opt & dpkg_opt_remove) {
+                       remove_package(deb_file[i]->package, 1);
+               }
+               else if (dpkg_opt & dpkg_opt_purge) {
+                       purge_package(deb_file[i]->package);
+               }
+               else if (dpkg_opt & dpkg_opt_unpack) {
+                       unpack_package(deb_file[i]);
+               }
+               else if (dpkg_opt & dpkg_opt_install) {
+                       unpack_package(deb_file[i]);
+                       /* package is configured in second pass below */
+               }
+               else if (dpkg_opt & dpkg_opt_configure) {
+                       configure_package(deb_file[i]);
+               }
+       }
+       /* configure installed packages */
+       if (dpkg_opt & dpkg_opt_install) {
+               for (i = 0; i < deb_count; i++)
+                       configure_package(deb_file[i]);
+       }
+
+       write_status_file(deb_file);
+
+       for (i = 0; i < deb_count; i++) {
+               free(deb_file[i]->control_file);
+               free(deb_file[i]->filename);
+               free(deb_file[i]);
+       }
+
+       free(deb_file);
+
+       for (i = 0; i < NAME_HASH_PRIME; i++) {
+               free(name_hashtable[i]);
+       }
+
+       for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
+               if (package_hashtable[i] != NULL) {
+                       free_package(package_hashtable[i]);
+               }
+       }
+
+       for (i = 0; i < STATUS_HASH_PRIME; i++) {
+               free(status_hashtable[i]);
+       }
+
+       return(EXIT_SUCCESS);
+}
+
diff --git a/busybox/archival/dpkg_deb.c b/busybox/archival/dpkg_deb.c
new file mode 100644 (file)
index 0000000..5aa9881
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "unarchive.h"
+#include "busybox.h"
+
+#define DPKG_DEB_OPT_CONTENTS  1
+#define DPKG_DEB_OPT_CONTROL   2
+#define DPKG_DEB_OPT_FIELD     4
+#define DPKG_DEB_OPT_EXTRACT   8
+#define DPKG_DEB_OPT_EXTRACT_VERBOSE   16
+
+extern int dpkg_deb_main(int argc, char **argv)
+{
+       archive_handle_t *ar_archive;
+       archive_handle_t *tar_archive;
+       llist_t *control_tar_llist = NULL;
+       unsigned long opt;
+       char *extract_dir = NULL;
+       short argcount = 1;
+
+       /* Setup the tar archive handle */
+       tar_archive = init_handle();
+
+       /* Setup an ar archive handle that refers to the gzip sub archive */
+       ar_archive = init_handle();
+       ar_archive->sub_archive = tar_archive;
+       ar_archive->filter = filter_accept_list_reassign;
+
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+       ar_archive->accept = llist_add_to(NULL, "data.tar.gz");
+       control_tar_llist = llist_add_to(NULL, "control.tar.gz");
+#endif
+
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+       ar_archive->accept = llist_add_to(ar_archive->accept, "data.tar.bz2");
+       control_tar_llist = llist_add_to(control_tar_llist, "control.tar.bz2");
+#endif
+
+       bb_opt_complementaly = "c~efXx:e~cfXx:f~ceXx:X~cefx:x~cefX";
+       opt = bb_getopt_ulflags(argc, argv, "cefXx");
+
+       if (opt & DPKG_DEB_OPT_CONTENTS) {
+               tar_archive->action_header = header_verbose_list;
+       }
+       if (opt & DPKG_DEB_OPT_CONTROL) {
+               ar_archive->accept = control_tar_llist;
+               tar_archive->action_data = data_extract_all;
+               if (optind + 1 == argc) {
+                       extract_dir = "./DEBIAN";
+               } else {
+                       argcount++;
+               }
+       }
+       if (opt & DPKG_DEB_OPT_FIELD) {
+               /* Print the entire control file
+                * it should accept a second argument which specifies a
+                * specific field to print */
+               ar_archive->accept = control_tar_llist;
+               tar_archive->accept = llist_add_to(NULL, "./control");;
+               tar_archive->filter = filter_accept_list;
+               tar_archive->action_data = data_extract_to_stdout;
+       }
+       if (opt & DPKG_DEB_OPT_EXTRACT) {
+               tar_archive->action_header = header_list;
+       }
+       if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
+               tar_archive->action_data = data_extract_all;
+               argcount = 2;
+       }
+
+       if ((optind + argcount != argc) || (opt & 0x80000000UL)) {
+               bb_show_usage();
+       }
+
+       tar_archive->src_fd = ar_archive->src_fd = bb_xopen(argv[optind++], O_RDONLY);
+
+       /* Workout where to extract the files */
+       /* 2nd argument is a dir name */
+       if (argv[optind]) {
+               extract_dir = argv[optind];
+       }
+       if (extract_dir) {
+               mkdir(extract_dir, 0777);
+               chdir(extract_dir);
+       }
+       unpack_ar_archive(ar_archive);
+
+       /* Cleanup */
+       close (ar_archive->src_fd);
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/archival/gunzip.c b/busybox/archival/gunzip.c
new file mode 100644 (file)
index 0000000..beb7bd1
--- /dev/null
@@ -0,0 +1,198 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Gzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
+ * well as stdin/stdout, and to generally behave itself wrt command line
+ * handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ * busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * Portions of the lzw code are derived from the public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ * Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ * See the license_msg below and the file COPYING for the software license.
+ * See the file algorithm.doc for the compression algorithms and file formats.
+ */
+
+#if 0
+static char *license_msg[] = {
+       "   Copyright (C) 1992-1993 Jean-loup Gailly",
+       "   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., 675 Mass Ave, Cambridge, MA 02139, USA.",
+       0
+};
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "busybox.h"
+#include "unarchive.h"
+
+#define GUNZIP_OPT_STDOUT      1
+#define GUNZIP_OPT_FORCE       2
+#define GUNZIP_OPT_TEST                4
+#define GUNZIP_OPT_DECOMPRESS  8
+
+extern int gunzip_main(int argc, char **argv)
+{
+       char status = EXIT_SUCCESS;
+       unsigned long opt;
+
+       opt = bb_getopt_ulflags(argc, argv, "cftd");
+       /* if called as zcat */
+       if (strcmp(bb_applet_name, "zcat") == 0) {
+               opt |= GUNZIP_OPT_STDOUT;
+       }
+
+       do {
+               struct stat stat_buf;
+               const char *old_path = argv[optind];
+               const char *delete_path = NULL;
+               char *new_path = NULL;
+               int src_fd;
+               int dst_fd;
+
+               optind++;
+
+               if (old_path == NULL || strcmp(old_path, "-") == 0) {
+                       src_fd = STDIN_FILENO;
+                       opt |= GUNZIP_OPT_STDOUT;
+               } else {
+                       src_fd = bb_xopen(old_path, O_RDONLY);
+
+                       /* Get the time stamp on the input file. */
+                       if (stat(old_path, &stat_buf) < 0) {
+                               bb_error_msg_and_die("Couldn't stat file %s", old_path);
+                       }
+               }
+
+               /* Check that the input is sane.  */
+               if (isatty(src_fd) && ((opt & GUNZIP_OPT_FORCE) == 0)) {
+                       bb_error_msg_and_die
+                               ("compressed data not read from terminal.  Use -f to force it.");
+               }
+
+               /* Set output filename and number */
+               if (opt & GUNZIP_OPT_TEST) {
+                       dst_fd = bb_xopen("/dev/null", O_WRONLY);       /* why does test use filenum 2 ? */
+               } else if (opt & GUNZIP_OPT_STDOUT) {
+                       dst_fd = STDOUT_FILENO;
+               } else {
+                       char *extension;
+
+                       new_path = bb_xstrdup(old_path);
+
+                       extension = strrchr(new_path, '.');
+#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
+                       if (extension && (strcmp(extension, ".Z") == 0)) {
+                               *extension = '\0';
+                       } else
+#endif
+                       if (extension && (strcmp(extension, ".gz") == 0)) {
+                               *extension = '\0';
+                       } else if (extension && (strcmp(extension, ".tgz") == 0)) {
+                               extension[2] = 'a';
+                               extension[3] = 'r';
+                       } else {
+                               bb_error_msg_and_die("Invalid extension");
+                       }
+
+                       /* Open output file */
+                       dst_fd = bb_xopen(new_path, O_WRONLY | O_CREAT);
+
+                       /* Set permissions on the file */
+                       chmod(new_path, stat_buf.st_mode);
+
+                       /* If unzip succeeds remove the old file */
+                       delete_path = old_path;
+               }
+
+               /* do the decompression, and cleanup */
+               if (bb_xread_char(src_fd) == 0x1f) {
+                       unsigned char magic2;
+
+                       magic2 = bb_xread_char(src_fd);
+#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
+                       if (magic2 == 0x9d) {
+                               status = uncompress(src_fd, dst_fd);
+                       } else
+#endif
+                               if (magic2 == 0x8b) {
+                                       check_header_gzip(src_fd);
+                                       status = inflate_gunzip(src_fd, dst_fd);
+                                       if (status != 0) {
+                                               bb_error_msg_and_die("Error inflating");
+                                       }
+                               } else {
+                                       bb_error_msg_and_die("Invalid magic");
+                               }
+               } else {
+                       bb_error_msg_and_die("Invalid magic");
+               }
+
+               if ((status != EXIT_SUCCESS) && (new_path)) {
+                       /* Unzip failed, remove new path instead of old path */
+                       delete_path = new_path;
+               }
+
+               if (dst_fd != STDOUT_FILENO) {
+                       close(dst_fd);
+               }
+               if (src_fd != STDIN_FILENO) {
+                       close(src_fd);
+               }
+
+               /* delete_path will be NULL if in test mode or from stdin */
+               if (delete_path && (unlink(delete_path) == -1)) {
+                       bb_error_msg_and_die("Couldn't remove %s", delete_path);
+               }
+
+               free(new_path);
+
+       } while (optind < argc);
+
+       return status;
+}
diff --git a/busybox/archival/gzip.c b/busybox/archival/gzip.c
new file mode 100644 (file)
index 0000000..d494aa3
--- /dev/null
@@ -0,0 +1,2548 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Gzip implementation for busybox
+ *
+ * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Charles P. Wright <cpw@unix.asb.com>
+ *             "this is a stripped down version of gzip I put into busybox, it does
+ *             only standard in to standard out with -9 compression.  It also requires
+ *             the zcat module for some important functions."
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
+ * files as well as stdin/stdout, and to generally behave itself wrt
+ * command line handling.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* These defines are very important for BusyBox.  Without these,
+ * huge chunks of ram are pre-allocated making the BusyBox bss
+ * size Freaking Huge(tm), which is a bad thing.*/
+#define SMALL_MEM
+#define DYN_ALLOC
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <utime.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <time.h>
+#include "busybox.h"
+
+#define memzero(s, n)     memset ((void *)(s), 0, (n))
+
+#ifndef RETSIGTYPE
+#  define RETSIGTYPE void
+#endif
+
+typedef unsigned char uch;
+typedef unsigned short ush;
+typedef unsigned long ulg;
+
+/* Return codes from gzip */
+#define OK      0
+#define ERROR   1
+#define WARNING 2
+
+/* Compression methods (see algorithm.doc) */
+/* Only STORED and DEFLATED are supported by this BusyBox module */
+#define STORED      0
+/* methods 4 to 7 reserved */
+#define DEFLATED    8
+
+/* To save memory for 16 bit systems, some arrays are overlaid between
+ * the various modules:
+ * deflate:  prev+head   window      d_buf  l_buf  outbuf
+ * unlzw:    tab_prefix  tab_suffix  stack  inbuf  outbuf
+ * For compression, input is done in window[]. For decompression, output
+ * is done in window except for unlzw.
+ */
+
+#ifndef        INBUFSIZ
+#  ifdef SMALL_MEM
+#    define INBUFSIZ  0x2000   /* input buffer size */
+#  else
+#    define INBUFSIZ  0x8000   /* input buffer size */
+#  endif
+#endif
+#define INBUF_EXTRA  64        /* required by unlzw() */
+
+#ifndef        OUTBUFSIZ
+#  ifdef SMALL_MEM
+#    define OUTBUFSIZ   8192   /* output buffer size */
+#  else
+#    define OUTBUFSIZ  16384   /* output buffer size */
+#  endif
+#endif
+#define OUTBUF_EXTRA 2048      /* required by unlzw() */
+
+#ifndef DIST_BUFSIZE
+#  ifdef SMALL_MEM
+#    define DIST_BUFSIZE 0x2000        /* buffer for distances, see trees.c */
+#  else
+#    define DIST_BUFSIZE 0x8000        /* buffer for distances, see trees.c */
+#  endif
+#endif
+
+#ifdef DYN_ALLOC
+#  define DECLARE(type, array, size)  static type * array
+#  define ALLOC(type, array, size) { \
+      array = (type*)xcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \
+   }
+#  define FREE(array) {free(array), array=NULL;}
+#else
+#  define DECLARE(type, array, size)  static type array[size]
+#  define ALLOC(type, array, size)
+#  define FREE(array)
+#endif
+
+#define tab_suffix window
+#define tab_prefix prev        /* hash link (see deflate.c) */
+#define head (prev+WSIZE)      /* hash head (see deflate.c) */
+
+static long bytes_in;  /* number of input bytes */
+
+#define isize bytes_in
+/* for compatibility with old zip sources (to be cleaned) */
+
+typedef int file_t;            /* Do not use stdio */
+
+#define NO_FILE  (-1)  /* in memory compression */
+
+
+#define        PACK_MAGIC     "\037\036"       /* Magic header for packed files */
+#define        GZIP_MAGIC     "\037\213"       /* Magic header for gzip files, 1F 8B */
+#define        OLD_GZIP_MAGIC "\037\236"       /* Magic header for gzip 0.5 = freeze 1.x */
+#define        LZH_MAGIC      "\037\240"       /* Magic header for SCO LZH Compress files */
+#define PKZIP_MAGIC    "\120\113\003\004"      /* Magic header for pkzip files */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01      /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02      /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04      /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08      /* bit 3 set: original file name present */
+#define COMMENT      0x10      /* bit 4 set: file comment present */
+#define RESERVED     0xC0      /* bit 6,7:   reserved */
+
+/* internal file attribute */
+#define UNKNOWN 0xffff
+#define BINARY  0
+#define ASCII   1
+
+#ifndef WSIZE
+#  define WSIZE 0x8000 /* window size--must be a power of two, and */
+#endif                                                 /*  at least 32K for zip's deflate method */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST  (WSIZE-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+/* put_byte is used for the compressed output */
+#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
+   flush_outbuf();}
+
+
+/* Output a 32 bit value to the bit stream, lsb first */
+#if 0
+#define put_long(n) { \
+    put_short((n) & 0xffff); \
+    put_short(((ulg)(n)) >> 16); \
+}
+#endif
+
+#define seekable()    0        /* force sequential output */
+#define translate_eol 0        /* no option -a yet */
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) bb_error_msg(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+#define WARN(msg) {if (!quiet) fprintf msg ; \
+                  if (exit_code == OK) exit_code = WARNING;}
+
+#ifndef MAX_PATH_LEN
+#  define MAX_PATH_LEN   1024  /* max pathname length */
+#endif
+
+
+       /* from zip.c: */
+static int zip(int in, int out);
+static int file_read(char *buf, unsigned size);
+
+       /* from gzip.c */
+static RETSIGTYPE abort_gzip(void);
+
+               /* from deflate.c */
+static void lm_init(ush * flags);
+static ulg deflate(void);
+
+               /* from trees.c */
+static void ct_init(ush * attr, int *methodp);
+static int ct_tally(int dist, int lc);
+static ulg flush_block(char *buf, ulg stored_len, int eof);
+
+               /* from bits.c */
+static void bi_init(file_t zipfile);
+static void send_bits(int value, int length);
+static unsigned bi_reverse(unsigned value, int length);
+static void bi_windup(void);
+static void copy_block(char *buf, unsigned len, int header);
+static int (*read_buf) (char *buf, unsigned size);
+
+       /* from util.c: */
+static void flush_outbuf(void);
+
+/* lzw.h -- define the lzw functions.
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#if !defined(OF) && defined(lint)
+#  include "gzip.h"
+#endif
+
+#ifndef BITS
+#  define BITS 16
+#endif
+#define INIT_BITS 9            /* Initial number of bits per code */
+
+#define BIT_MASK    0x1f       /* Mask for 'number of compression bits' */
+/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
+ * It's a pity that old uncompress does not check bit 0x20. That makes
+ * extension of the format actually undesirable because old compress
+ * would just crash on the new format instead of giving a meaningful
+ * error message. It does check the number of bits, but it's more
+ * helpful to say "unsupported format, get a new version" than
+ * "can only handle 16 bits".
+ */
+
+/* tailor.h -- target dependent definitions
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+/* The target dependent definitions should be defined here only.
+ * The target dependent functions should be defined in tailor.c.
+ */
+
+
+       /* Common defaults */
+
+#ifndef OS_CODE
+#  define OS_CODE  0x03        /* assume Unix */
+#endif
+
+#ifndef PATH_SEP
+#  define PATH_SEP '/'
+#endif
+
+#ifndef OPTIONS_VAR
+#  define OPTIONS_VAR "GZIP"
+#endif
+
+#ifndef Z_SUFFIX
+#  define Z_SUFFIX ".gz"
+#endif
+
+#ifdef MAX_EXT_CHARS
+#  define MAX_SUFFIX  MAX_EXT_CHARS
+#else
+#  define MAX_SUFFIX  30
+#endif
+
+               /* global buffers */
+
+DECLARE(uch, inbuf, INBUFSIZ + INBUF_EXTRA);
+DECLARE(uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA);
+DECLARE(ush, d_buf, DIST_BUFSIZE);
+DECLARE(uch, window, 2L * WSIZE);
+DECLARE(ush, tab_prefix, 1L << BITS);
+
+static int foreground; /* set if program run in foreground */
+static int method = DEFLATED;  /* compression method */
+static int exit_code = OK;     /* program exit code */
+static int part_nb;            /* number of parts in .gz file */
+static long time_stamp;        /* original time stamp (modification time) */
+static long ifile_size;        /* input file size, -1 for devices (debug only) */
+static char z_suffix[MAX_SUFFIX + 1];  /* default suffix (can be set with --suffix) */
+static int z_len;              /* strlen(z_suffix) */
+
+static int ifd;                        /* input file descriptor */
+static int ofd;                        /* output file descriptor */
+static unsigned insize;        /* valid bytes in inbuf */
+static unsigned outcnt;        /* bytes in output buffer */
+
+
+/* Output a 16 bit value, lsb first */
+static void put_short(ush w)
+{
+       if (outcnt < OUTBUFSIZ - 2) {
+               outbuf[outcnt++] = (uch) ((w) & 0xff);
+               outbuf[outcnt++] = (uch) ((ush) (w) >> 8);
+       } else {
+               put_byte((uch) ((w) & 0xff));
+               put_byte((uch) ((ush) (w) >> 8));
+       }
+}
+
+/* ========================================================================
+ * Signal and error handler.
+ */
+static void abort_gzip()
+{
+       exit(ERROR);
+}
+
+/* ===========================================================================
+ * Clear input and output buffers
+ */
+static void clear_bufs(void)
+{
+       outcnt = 0;
+       insize = 0;
+       bytes_in = 0L;
+}
+
+static void write_bb_error_msg(void)
+{
+       fputc('\n', stderr);
+       bb_perror_nomsg();
+       abort_gzip();
+}
+
+/* ===========================================================================
+ * Does the same as write(), but also handles partial pipe writes and checks
+ * for error return.
+ */
+static void write_buf(int fd, void *buf, unsigned cnt)
+{
+       unsigned n;
+
+       while ((n = write(fd, buf, cnt)) != cnt) {
+               if (n == (unsigned) (-1)) {
+                       write_bb_error_msg();
+               }
+               cnt -= n;
+               buf = (void *) ((char *) buf + n);
+       }
+}
+
+/* ===========================================================================
+ * Run a set of bytes through the crc shift register.  If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+static ulg updcrc(uch * s, unsigned n)
+{
+       static ulg crc = (ulg) 0xffffffffL;     /* shift register contents */
+       register ulg c;         /* temporary variable */
+       static unsigned long crc_32_tab[256];
+
+       if (crc_32_tab[1] == 0x00000000L) {
+               unsigned long csr;      /* crc shift register */
+               const unsigned long e = 0xedb88320L;    /* polynomial exclusive-or pattern */
+               int i;                  /* counter for all possible eight bit values */
+               int k;                  /* byte being shifted into crc apparatus */
+
+               /* Compute table of CRC's. */
+               for (i = 1; i < 256; i++) {
+                       csr = i;
+                       /* The idea to initialize the register with the byte instead of
+                          * zero was stolen from Haruhiko Okumura's ar002
+                        */
+                       for (k = 8; k; k--)
+                               csr = csr & 1 ? (csr >> 1) ^ e : csr >> 1;
+                       crc_32_tab[i] = csr;
+               }
+       }
+
+       if (s == NULL) {
+               c = 0xffffffffL;
+       } else {
+               c = crc;
+               if (n)
+                       do {
+                               c = crc_32_tab[((int) c ^ (*s++)) & 0xff] ^ (c >> 8);
+                       } while (--n);
+       }
+       crc = c;
+       return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */
+}
+
+/* bits.c -- output variable-length bit strings
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+
+/*
+ *  PURPOSE
+ *
+ *      Output variable-length bit strings. Compression can be done
+ *      to a file or to memory. (The latter is not supported in this version.)
+ *
+ *  DISCUSSION
+ *
+ *      The PKZIP "deflate" file format interprets compressed file data
+ *      as a sequence of bits.  Multi-bit strings in the file may cross
+ *      byte boundaries without restriction.
+ *
+ *      The first bit of each byte is the low-order bit.
+ *
+ *      The routines in this file allow a variable-length bit value to
+ *      be output right-to-left (useful for literal values). For
+ *      left-to-right output (useful for code strings from the tree routines),
+ *      the bits must have been reversed first with bi_reverse().
+ *
+ *      For in-memory compression, the compressed bit stream goes directly
+ *      into the requested output buffer. The input data is read in blocks
+ *      by the mem_read() function. The buffer is limited to 64K on 16 bit
+ *      machines.
+ *
+ *  INTERFACE
+ *
+ *      void bi_init (FILE *zipfile)
+ *          Initialize the bit string routines.
+ *
+ *      void send_bits (int value, int length)
+ *          Write out a bit string, taking the source bits right to
+ *          left.
+ *
+ *      int bi_reverse (int value, int length)
+ *          Reverse the bits of a bit string, taking the source bits left to
+ *          right and emitting them right to left.
+ *
+ *      void bi_windup (void)
+ *          Write out any remaining bits in an incomplete byte.
+ *
+ *      void copy_block(char *buf, unsigned len, int header)
+ *          Copy a stored block to the zip file, storing first the length and
+ *          its one's complement if requested.
+ *
+ */
+
+/* ===========================================================================
+ * Local data used by the "bit string" routines.
+ */
+
+static file_t zfile;   /* output gzip file */
+
+static unsigned short bi_buf;
+
+/* Output buffer. bits are inserted starting at the bottom (least significant
+ * bits).
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+static int bi_valid;
+
+/* Current input function. Set to mem_read for in-memory compression */
+
+#ifdef DEBUG
+ulg bits_sent;                 /* bit length of the compressed data */
+#endif
+
+/* ===========================================================================
+ * Initialize the bit string routines.
+ */
+static void bi_init(file_t zipfile)
+{
+       zfile = zipfile;
+       bi_buf = 0;
+       bi_valid = 0;
+#ifdef DEBUG
+       bits_sent = 0L;
+#endif
+
+       /* Set the defaults for file compression. They are set by memcompress
+        * for in-memory compression.
+        */
+       if (zfile != NO_FILE) {
+               read_buf = file_read;
+       }
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+static void send_bits(int value, int length)
+{
+#ifdef DEBUG
+       Tracev((stderr, " l %2d v %4x ", length, value));
+       Assert(length > 0 && length <= 15, "invalid length");
+       bits_sent += (ulg) length;
+#endif
+       /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+        * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+        * unused bits in value.
+        */
+       if (bi_valid > (int) Buf_size - length) {
+               bi_buf |= (value << bi_valid);
+               put_short(bi_buf);
+               bi_buf = (ush) value >> (Buf_size - bi_valid);
+               bi_valid += length - Buf_size;
+       } else {
+               bi_buf |= value << bi_valid;
+               bi_valid += length;
+       }
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+static unsigned bi_reverse(unsigned code, int len)
+{
+       register unsigned res = 0;
+
+       do {
+               res |= code & 1;
+               code >>= 1, res <<= 1;
+       } while (--len > 0);
+       return res >> 1;
+}
+
+/* ===========================================================================
+ * Write out any remaining bits in an incomplete byte.
+ */
+static void bi_windup()
+{
+       if (bi_valid > 8) {
+               put_short(bi_buf);
+       } else if (bi_valid > 0) {
+               put_byte(bi_buf);
+       }
+       bi_buf = 0;
+       bi_valid = 0;
+#ifdef DEBUG
+       bits_sent = (bits_sent + 7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block to the zip file, storing first the length and its
+ * one's complement if requested.
+ */
+static void copy_block(char *buf, unsigned len, int header)
+{
+       bi_windup();            /* align on byte boundary */
+
+       if (header) {
+               put_short((ush) len);
+               put_short((ush) ~ len);
+#ifdef DEBUG
+               bits_sent += 2 * 16;
+#endif
+       }
+#ifdef DEBUG
+       bits_sent += (ulg) len << 3;
+#endif
+       while (len--) {
+               put_byte(*buf++);
+       }
+}
+
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+/*
+ *  PURPOSE
+ *
+ *      Identify new text as repetitions of old text within a fixed-
+ *      length sliding window trailing behind the new text.
+ *
+ *  DISCUSSION
+ *
+ *      The "deflation" process depends on being able to identify portions
+ *      of the input text which are identical to earlier input (within a
+ *      sliding window trailing behind the input currently being processed).
+ *
+ *      The most straightforward technique turns out to be the fastest for
+ *      most input files: try all possible matches and select the longest.
+ *      The key feature of this algorithm is that insertions into the string
+ *      dictionary are very simple and thus fast, and deletions are avoided
+ *      completely. Insertions are performed at each input character, whereas
+ *      string matches are performed only when the previous match ends. So it
+ *      is preferable to spend more time in matches to allow very fast string
+ *      insertions and avoid deletions. The matching algorithm for small
+ *      strings is inspired from that of Rabin & Karp. A brute force approach
+ *      is used to find longer strings when a small match has been found.
+ *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ *      (by Leonid Broukhis).
+ *         A previous version of this file used a more sophisticated algorithm
+ *      (by Fiala and Greene) which is guaranteed to run in linear amortized
+ *      time, but has a larger average cost, uses more memory and is patented.
+ *      However the F&G algorithm may be faster for some highly redundant
+ *      files if the parameter max_chain_length (described below) is too large.
+ *
+ *  ACKNOWLEDGMENTS
+ *
+ *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ *      I found it in 'freeze' written by Leonid Broukhis.
+ *      Thanks to many info-zippers for bug reports and testing.
+ *
+ *  REFERENCES
+ *
+ *      APPNOTE.TXT documentation file in PKZIP 1.93a distribution.
+ *
+ *      A description of the Rabin and Karp algorithm is given in the book
+ *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ *      Fiala,E.R., and Greene,D.H.
+ *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ *  INTERFACE
+ *
+ *      void lm_init (int pack_level, ush *flags)
+ *          Initialize the "longest match" routines for a new file
+ *
+ *      ulg deflate (void)
+ *          Processes a new input file and return its compressed length. Sets
+ *          the compressed length, crc, deflate flags and internal file
+ *          attributes.
+ */
+
+
+/* ===========================================================================
+ * Configuration parameters
+ */
+
+/* Compile with MEDIUM_MEM to reduce the memory requirements or
+ * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the
+ * entire input file can be held in memory (not possible on 16 bit systems).
+ * Warning: defining these symbols affects HASH_BITS (see below) and thus
+ * affects the compression ratio. The compressed output
+ * is still correct, and might even be smaller in some cases.
+ */
+
+#ifdef SMALL_MEM
+#   define HASH_BITS  13       /* Number of bits used to hash strings */
+#endif
+#ifdef MEDIUM_MEM
+#   define HASH_BITS  14
+#endif
+#ifndef HASH_BITS
+#   define HASH_BITS  15
+   /* For portability to 16 bit machines, do not use values above 15. */
+#endif
+
+/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and
+ * window with tab_suffix. Check that we can do this:
+ */
+#if (WSIZE<<1) > (1<<BITS)
+#  error cannot overlay window with tab_suffix and prev with tab_prefix0
+#endif
+#if HASH_BITS > BITS-1
+#  error cannot overlay head with tab_prefix1
+#endif
+#define HASH_SIZE (unsigned)(1<<HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+#define WMASK     (WSIZE-1)
+/* HASH_SIZE and WSIZE must be powers of two */
+#define NIL 0
+/* Tail of hash chains */
+#define FAST 4
+#define SLOW 2
+/* speed options for the general purpose bit flag */
+#ifndef TOO_FAR
+#  define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+/* ===========================================================================
+ * Local data used by the "longest match" routines.
+ */
+typedef ush Pos;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+/* DECLARE(uch, window, 2L*WSIZE); */
+/* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least WSIZE
+ * bytes. With this organization, matches are limited to a distance of
+ * WSIZE-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would
+ * be less efficient).
+ */
+
+/* DECLARE(Pos, prev, WSIZE); */
+/* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+/* DECLARE(Pos, head, 1<<HASH_BITS); */
+/* Heads of the hash chains or NIL. */
+
+static const ulg window_size = (ulg) 2 * WSIZE;
+
+/* window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the
+ * input file length plus MIN_LOOKAHEAD.
+ */
+
+static long block_start;
+
+/* window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+static unsigned ins_h; /* hash index of string to be inserted */
+
+#define H_SHIFT  ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH)
+/* Number of bits by which ins_h and del_h must be shifted at each
+ * input step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ *   H_SHIFT * MIN_MATCH >= HASH_BITS
+ */
+
+static unsigned int prev_length;
+
+/* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+static unsigned strstart;      /* start of string to insert */
+static unsigned match_start;   /* start of matching string */
+static int eofile;             /* flag set at end of input file */
+static unsigned lookahead;     /* number of valid bytes ahead in window */
+
+static const unsigned max_chain_length = 4096;
+
+/* To speed up deflation, hash chains are never searched beyond this length.
+ * A higher limit improves compression ratio but degrades the speed.
+ */
+
+static const unsigned int max_lazy_match = 258;
+
+/* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+#define max_insert_length  max_lazy_match
+/* Insert new strings in the hash table only if the match length
+ * is not greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+static const unsigned good_match = 32;
+
+/* Use a faster search when the previous match is longer than this */
+
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+
+static const int nice_match = 258;     /* Stop searching when current match exceeds this */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+/* ===========================================================================
+ *  Prototypes for local functions.
+ */
+static void fill_window(void);
+
+static int longest_match(IPos cur_match);
+
+#ifdef DEBUG
+static void check_match(IPos start, IPos match, int length);
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
+ *    input characters, so that a running hash key can be computed from the
+ *    previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK)
+
+/* ===========================================================================
+ * Insert string s in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN  assertion: all calls to to INSERT_STRING are made with consecutive
+ *    input characters and the first MIN_MATCH bytes of s are valid
+ *    (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, match_head) \
+   (UPDATE_HASH(ins_h, window[(s) + MIN_MATCH-1]), \
+    prev[(s) & WMASK] = match_head = head[ins_h], \
+    head[ins_h] = (s))
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new file
+ */
+static void lm_init(ush * flags)
+{
+       register unsigned j;
+
+       /* Initialize the hash table. */
+       memzero((char *) head, HASH_SIZE * sizeof(*head));
+       /* prev will be initialized on the fly */
+
+       *flags |= SLOW;
+       /* ??? reduce max_chain_length for binary files */
+
+       strstart = 0;
+       block_start = 0L;
+
+       lookahead = read_buf((char *) window,
+                                                sizeof(int) <= 2 ? (unsigned) WSIZE : 2 * WSIZE);
+
+       if (lookahead == 0 || lookahead == (unsigned) EOF) {
+               eofile = 1, lookahead = 0;
+               return;
+       }
+       eofile = 0;
+       /* Make sure that we always have enough lookahead. This is important
+        * if input comes from a device such as a tty.
+        */
+       while (lookahead < MIN_LOOKAHEAD && !eofile)
+               fill_window();
+
+       ins_h = 0;
+       for (j = 0; j < MIN_MATCH - 1; j++)
+               UPDATE_HASH(ins_h, window[j]);
+       /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
+        * not important since only literal bytes will be emitted.
+        */
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ */
+
+/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or
+ * match.s. The code is functionally equivalent, so you can use the C version
+ * if desired.
+ */
+static int longest_match(IPos cur_match)
+{
+       unsigned chain_length = max_chain_length;       /* max hash chain length */
+       register uch *scan = window + strstart; /* current string */
+       register uch *match;    /* matched string */
+       register int len;       /* length of current match */
+       int best_len = prev_length;     /* best match length so far */
+       IPos limit =
+               strstart > (IPos) MAX_DIST ? strstart - (IPos) MAX_DIST : NIL;
+       /* Stop when cur_match becomes <= limit. To simplify the code,
+        * we prevent matches with the string of window index 0.
+        */
+
+/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+#if HASH_BITS < 8 || MAX_MATCH != 258
+#  error Code too clever
+#endif
+       register uch *strend = window + strstart + MAX_MATCH;
+       register uch scan_end1 = scan[best_len - 1];
+       register uch scan_end = scan[best_len];
+
+       /* Do not waste too much time if we already have a good match: */
+       if (prev_length >= good_match) {
+               chain_length >>= 2;
+       }
+       Assert(strstart <= window_size - MIN_LOOKAHEAD, "insufficient lookahead");
+
+       do {
+               Assert(cur_match < strstart, "no future");
+               match = window + cur_match;
+
+               /* Skip to next match if the match length cannot increase
+                * or if the match length is less than 2:
+                */
+               if (match[best_len] != scan_end ||
+                       match[best_len - 1] != scan_end1 ||
+                       *match != *scan || *++match != scan[1])
+                       continue;
+
+               /* The check at best_len-1 can be removed because it will be made
+                * again later. (This heuristic is not always a win.)
+                * It is not necessary to compare scan[2] and match[2] since they
+                * are always equal when the other bytes match, given that
+                * the hash keys are equal and that HASH_BITS >= 8.
+                */
+               scan += 2, match++;
+
+               /* We check for insufficient lookahead only every 8th comparison;
+                * the 256th check will be made at strstart+258.
+                */
+               do {
+               } while (*++scan == *++match && *++scan == *++match &&
+                                *++scan == *++match && *++scan == *++match &&
+                                *++scan == *++match && *++scan == *++match &&
+                                *++scan == *++match && *++scan == *++match && scan < strend);
+
+               len = MAX_MATCH - (int) (strend - scan);
+               scan = strend - MAX_MATCH;
+
+               if (len > best_len) {
+                       match_start = cur_match;
+                       best_len = len;
+                       if (len >= nice_match)
+                               break;
+                       scan_end1 = scan[best_len - 1];
+                       scan_end = scan[best_len];
+               }
+       } while ((cur_match = prev[cur_match & WMASK]) > limit
+                        && --chain_length != 0);
+
+       return best_len;
+}
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+static void check_match(IPos start, IPos match, int length)
+{
+       /* check that the match is indeed a match */
+       if (memcmp((char *) window + match,
+                          (char *) window + start, length) != EQUAL) {
+               bb_error_msg(" start %d, match %d, length %d", start, match, length);
+               bb_error_msg("invalid match");
+       }
+       if (verbose > 1) {
+               bb_error_msg("\\[%d,%d]", start - match, length);
+               do {
+                       putc(window[start++], stderr);
+               } while (--length != 0);
+       }
+}
+#else
+#  define check_match(start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead, and sets eofile if end of input file.
+ * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0
+ * OUT assertions: at least one byte has been read, or eofile is set;
+ *    file reads are performed for at least two bytes (required for the
+ *    translate_eol option).
+ */
+static void fill_window()
+{
+       register unsigned n, m;
+       unsigned more =
+               (unsigned) (window_size - (ulg) lookahead - (ulg) strstart);
+       /* Amount of free space at the end of the window. */
+
+       /* If the window is almost full and there is insufficient lookahead,
+        * move the upper half to the lower one to make room in the upper half.
+        */
+       if (more == (unsigned) EOF) {
+               /* Very unlikely, but possible on 16 bit machine if strstart == 0
+                * and lookahead == 1 (input done one byte at time)
+                */
+               more--;
+       } else if (strstart >= WSIZE + MAX_DIST) {
+               /* By the IN assertion, the window is not empty so we can't confuse
+                * more == 0 with more == 64K on a 16 bit machine.
+                */
+               Assert(window_size == (ulg) 2 * WSIZE, "no sliding with BIG_MEM");
+
+               memcpy((char *) window, (char *) window + WSIZE, (unsigned) WSIZE);
+               match_start -= WSIZE;
+               strstart -= WSIZE;      /* we now have strstart >= MAX_DIST: */
+
+               block_start -= (long) WSIZE;
+
+               for (n = 0; n < HASH_SIZE; n++) {
+                       m = head[n];
+                       head[n] = (Pos) (m >= WSIZE ? m - WSIZE : NIL);
+               }
+               for (n = 0; n < WSIZE; n++) {
+                       m = prev[n];
+                       prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : NIL);
+                       /* If n is not on any hash chain, prev[n] is garbage but
+                        * its value will never be used.
+                        */
+               }
+               more += WSIZE;
+       }
+       /* At this point, more >= 2 */
+       if (!eofile) {
+               n = read_buf((char *) window + strstart + lookahead, more);
+               if (n == 0 || n == (unsigned) EOF) {
+                       eofile = 1;
+               } else {
+                       lookahead += n;
+               }
+       }
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK(eof) \
+   flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \
+                (char*)NULL, (long)strstart - block_start, (eof))
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+static ulg deflate()
+{
+       IPos hash_head;         /* head of hash chain */
+       IPos prev_match;        /* previous match */
+       int flush;                      /* set if current block must be flushed */
+       int match_available = 0;        /* set if previous match exists */
+       register unsigned match_length = MIN_MATCH - 1; /* length of best match */
+
+       /* Process the input block. */
+       while (lookahead != 0) {
+               /* Insert the string window[strstart .. strstart+2] in the
+                * dictionary, and set hash_head to the head of the hash chain:
+                */
+               INSERT_STRING(strstart, hash_head);
+
+               /* Find the longest match, discarding those <= prev_length.
+                */
+               prev_length = match_length, prev_match = match_start;
+               match_length = MIN_MATCH - 1;
+
+               if (hash_head != NIL && prev_length < max_lazy_match &&
+                       strstart - hash_head <= MAX_DIST) {
+                       /* To simplify the code, we prevent matches with the string
+                        * of window index 0 (in particular we have to avoid a match
+                        * of the string with itself at the start of the input file).
+                        */
+                       match_length = longest_match(hash_head);
+                       /* longest_match() sets match_start */
+                       if (match_length > lookahead)
+                               match_length = lookahead;
+
+                       /* Ignore a length 3 match if it is too distant: */
+                       if (match_length == MIN_MATCH && strstart - match_start > TOO_FAR) {
+                               /* If prev_match is also MIN_MATCH, match_start is garbage
+                                * but we will ignore the current match anyway.
+                                */
+                               match_length--;
+                       }
+               }
+               /* If there was a match at the previous step and the current
+                * match is not better, output the previous match:
+                */
+               if (prev_length >= MIN_MATCH && match_length <= prev_length) {
+
+                       check_match(strstart - 1, prev_match, prev_length);
+
+                       flush =
+                               ct_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH);
+
+                       /* Insert in hash table all strings up to the end of the match.
+                        * strstart-1 and strstart are already inserted.
+                        */
+                       lookahead -= prev_length - 1;
+                       prev_length -= 2;
+                       do {
+                               strstart++;
+                               INSERT_STRING(strstart, hash_head);
+                               /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+                                * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
+                                * these bytes are garbage, but it does not matter since the
+                                * next lookahead bytes will always be emitted as literals.
+                                */
+                       } while (--prev_length != 0);
+                       match_available = 0;
+                       match_length = MIN_MATCH - 1;
+                       strstart++;
+                       if (flush)
+                               FLUSH_BLOCK(0), block_start = strstart;
+
+               } else if (match_available) {
+                       /* If there was no match at the previous position, output a
+                        * single literal. If there was a match but the current match
+                        * is longer, truncate the previous match to a single literal.
+                        */
+                       Tracevv((stderr, "%c", window[strstart - 1]));
+                       if (ct_tally(0, window[strstart - 1])) {
+                               FLUSH_BLOCK(0), block_start = strstart;
+                       }
+                       strstart++;
+                       lookahead--;
+               } else {
+                       /* There is no previous match to compare with, wait for
+                        * the next step to decide.
+                        */
+                       match_available = 1;
+                       strstart++;
+                       lookahead--;
+               }
+               Assert(strstart <= isize && lookahead <= isize, "a bit too far");
+
+               /* Make sure that we always have enough lookahead, except
+                * at the end of the input file. We need MAX_MATCH bytes
+                * for the next match, plus MIN_MATCH bytes to insert the
+                * string following the next match.
+                */
+               while (lookahead < MIN_LOOKAHEAD && !eofile)
+                       fill_window();
+       }
+       if (match_available)
+               ct_tally(0, window[strstart - 1]);
+
+       return FLUSH_BLOCK(1);  /* eof */
+}
+
+/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * Portions of the lzw code are derived from the public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ * Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ * See the license_msg below and the file COPYING for the software license.
+ * See the file algorithm.doc for the compression algorithms and file formats.
+ */
+
+/* Compress files with zip algorithm and 'compress' interface.
+ * See usage() and help() functions below for all options.
+ * Outputs:
+ *        file.gz:   compressed file with same mode, owner, and utimes
+ *     or stdout with -c option or if stdin used as input.
+ * If the output file name had to be truncated, the original name is kept
+ * in the compressed file.
+ */
+
+               /* configuration */
+
+typedef struct dirent dir_type;
+
+typedef RETSIGTYPE(*sig_type) (int);
+
+/* ======================================================================== */
+int gzip_main(int argc, char **argv)
+{
+       int result;
+       int inFileNum;
+       int outFileNum;
+       struct stat statBuf;
+       char *delFileName;
+       int tostdout = 0;
+       int force = 0;
+       int opt;
+
+       while ((opt = getopt(argc, argv, "cf123456789dq")) != -1) {
+               switch (opt) {
+               case 'c':
+                       tostdout = 1;
+                       break;
+               case 'f':
+                       force = 1;
+                       break;
+                       /* Ignore 1-9 (compression level) options */
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       break;
+               case 'q':
+                       break;
+#ifdef CONFIG_GUNZIP
+               case 'd':
+                       optind = 1;
+                       return gunzip_main(argc, argv);
+#endif
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
+       if (foreground) {
+               (void) signal(SIGINT, (sig_type) abort_gzip);
+       }
+#ifdef SIGTERM
+       if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+               (void) signal(SIGTERM, (sig_type) abort_gzip);
+       }
+#endif
+#ifdef SIGHUP
+       if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
+               (void) signal(SIGHUP, (sig_type) abort_gzip);
+       }
+#endif
+
+       strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix) - 1);
+       z_len = strlen(z_suffix);
+
+       /* Allocate all global buffers (for DYN_ALLOC option) */
+       ALLOC(uch, inbuf, INBUFSIZ + INBUF_EXTRA);
+       ALLOC(uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA);
+       ALLOC(ush, d_buf, DIST_BUFSIZE);
+       ALLOC(uch, window, 2L * WSIZE);
+       ALLOC(ush, tab_prefix, 1L << BITS);
+
+       clear_bufs();
+       part_nb = 0;
+
+       if (optind == argc) {
+               time_stamp = 0;
+               ifile_size = -1L;
+               zip(STDIN_FILENO, STDOUT_FILENO);
+       } else {
+               int i;
+
+               for (i = optind; i < argc; i++) {
+                       char *path = NULL;
+
+                       if (strcmp(argv[i], "-") == 0) {
+                               time_stamp = 0;
+                               ifile_size = -1L;
+                               inFileNum = STDIN_FILENO;
+                               outFileNum = STDOUT_FILENO;
+                       } else {
+                               inFileNum = open(argv[i], O_RDONLY);
+                               if (inFileNum < 0 || fstat(inFileNum, &statBuf) < 0)
+                                       bb_perror_msg_and_die("%s", argv[i]);
+                               time_stamp = statBuf.st_ctime;
+                               ifile_size = statBuf.st_size;
+
+                               if (!tostdout) {
+                                       path = xmalloc(strlen(argv[i]) + 4);
+                                       strcpy(path, argv[i]);
+                                       strcat(path, ".gz");
+
+                                       /* Open output file */
+#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
+                                       outFileNum =
+                                               open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW);
+#else
+                                       outFileNum = open(path, O_RDWR | O_CREAT | O_EXCL);
+#endif
+                                       if (outFileNum < 0) {
+                                               bb_perror_msg("%s", path);
+                                               free(path);
+                                               continue;
+                                       }
+
+                                       /* Set permissions on the file */
+                                       fchmod(outFileNum, statBuf.st_mode);
+                               } else
+                                       outFileNum = STDOUT_FILENO;
+                       }
+
+                       if (path == NULL && isatty(outFileNum) && force == 0) {
+                               bb_error_msg
+                                       ("compressed data not written to a terminal. Use -f to force compression.");
+                               free(path);
+                               continue;
+                       }
+
+                       result = zip(inFileNum, outFileNum);
+
+                       if (path != NULL) {
+                               close(inFileNum);
+                               close(outFileNum);
+
+                               /* Delete the original file */
+                               if (result == OK)
+                                       delFileName = argv[i];
+                               else
+                                       delFileName = path;
+
+                               if (unlink(delFileName) < 0)
+                                       bb_perror_msg("%s", delFileName);
+                       }
+
+                       free(path);
+               }
+       }
+
+       return (exit_code);
+}
+
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+/*
+ *  PURPOSE
+ *
+ *      Encode various sets of source values using variable-length
+ *      binary code trees.
+ *
+ *  DISCUSSION
+ *
+ *      The PKZIP "deflation" process uses several Huffman trees. The more
+ *      common source values are represented by shorter bit sequences.
+ *
+ *      Each code tree is stored in the ZIP file in a compressed form
+ *      which is itself a Huffman encoding of the lengths of
+ *      all the code strings (in ascending order by source values).
+ *      The actual code strings are reconstructed from the lengths in
+ *      the UNZIP process, as described in the "application note"
+ *      (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program.
+ *
+ *  REFERENCES
+ *
+ *      Lynch, Thomas J.
+ *          Data Compression:  Techniques and Applications, pp. 53-55.
+ *          Lifetime Learning Publications, 1985.  ISBN 0-534-03418-7.
+ *
+ *      Storer, James A.
+ *          Data Compression:  Methods and Theory, pp. 49-50.
+ *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
+ *
+ *      Sedgewick, R.
+ *          Algorithms, p290.
+ *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ *
+ *  INTERFACE
+ *
+ *      void ct_init (ush *attr, int *methodp)
+ *          Allocate the match buffer, initialize the various tables and save
+ *          the location of the internal file attribute (ascii/binary) and
+ *          method (DEFLATE/STORE)
+ *
+ *      void ct_tally (int dist, int lc);
+ *          Save the match info and tally the frequency counts.
+ *
+ *      long flush_block (char *buf, ulg stored_len, int eof)
+ *          Determine the best encoding for the current block: dynamic trees,
+ *          static trees or store, and output the encoded block to the zip
+ *          file. Returns the total compressed length for the file so far.
+ *
+ */
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+typedef uch extra_bits_t;
+
+/* extra bits for each length code */
+static const extra_bits_t extra_lbits[LENGTH_CODES]
+       = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
+       4, 4, 5, 5, 5, 5, 0
+};
+
+/* extra bits for each distance code */
+static const extra_bits_t extra_dbits[D_CODES]
+       = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
+       10, 10, 11, 11, 12, 12, 13, 13
+};
+
+/* extra bits for each bit length code */
+static const extra_bits_t extra_blbits[BL_CODES]
+= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 };
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#ifndef LIT_BUFSIZE
+#  ifdef SMALL_MEM
+#    define LIT_BUFSIZE  0x2000
+#  else
+#  ifdef MEDIUM_MEM
+#    define LIT_BUFSIZE  0x4000
+#  else
+#    define LIT_BUFSIZE  0x8000
+#  endif
+#  endif
+#endif
+#ifndef DIST_BUFSIZE
+#  define DIST_BUFSIZE  LIT_BUFSIZE
+#endif
+/* Sizes of match buffers for literals/lengths and distances.  There are
+ * 4 reasons for limiting LIT_BUFSIZE to 64K:
+ *   - frequencies can be kept in 16 bit counters
+ *   - if compression is not successful for the first block, all input data is
+ *     still in the window so we can still emit a stored block even when input
+ *     comes from standard input.  (This can also be done for all blocks if
+ *     LIT_BUFSIZE is not greater than 32K.)
+ *   - if compression is not successful for a file smaller than 64K, we can
+ *     even emit a stored file instead of a stored block (saving 5 bytes).
+ *   - creating new Huffman trees less frequently may not provide fast
+ *     adaptation to changes in the input data statistics. (Take for
+ *     example a binary file with poorly compressible code followed by
+ *     a highly compressible string table.) Smaller buffer sizes give
+ *     fast adaptation but have of course the overhead of transmitting trees
+ *     more frequently.
+ *   - I can't count above 4
+ * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save
+ * memory at the expense of compression). Some optimizations would be possible
+ * if we rely on DIST_BUFSIZE == LIT_BUFSIZE.
+ */
+#if LIT_BUFSIZE > INBUFSIZ
+#error cannot overlay l_buf and inbuf
+#endif
+#define REP_3_6      16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+#define REPZ_3_10    17
+/* repeat a zero length 3-10 times  (3 bits of repeat count) */
+#define REPZ_11_138  18
+/* repeat a zero length 11-138 times  (7 bits of repeat count) */
+
+/* ===========================================================================
+ * Local data
+ */
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data {
+       union {
+               ush freq;               /* frequency count */
+               ush code;               /* bit string */
+       } fc;
+       union {
+               ush dad;                /* father node in Huffman tree */
+               ush len;                /* length of bit string */
+       } dl;
+} ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+static ct_data dyn_ltree[HEAP_SIZE];   /* literal and length tree */
+static ct_data dyn_dtree[2 * D_CODES + 1];     /* distance tree */
+
+static ct_data static_ltree[L_CODES + 2];
+
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see ct_init
+ * below).
+ */
+
+static ct_data static_dtree[D_CODES];
+
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+static ct_data bl_tree[2 * BL_CODES + 1];
+
+/* Huffman tree for the bit lengths */
+
+typedef struct tree_desc {
+       ct_data *dyn_tree;      /* the dynamic tree */
+       ct_data *static_tree;   /* corresponding static tree or NULL */
+       const extra_bits_t *extra_bits; /* extra bits for each code or NULL */
+       int extra_base;         /* base index for extra_bits */
+       int elems;                      /* max number of elements in the tree */
+       int max_length;         /* max bit length for the codes */
+       int max_code;           /* largest code with non zero frequency */
+} tree_desc;
+
+static tree_desc l_desc =
+       { dyn_ltree, static_ltree, extra_lbits, LITERALS + 1, L_CODES,
+       MAX_BITS, 0
+};
+
+static tree_desc d_desc =
+       { dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0 };
+
+static tree_desc bl_desc =
+       { bl_tree, (ct_data *) 0, extra_blbits, 0, BL_CODES, MAX_BL_BITS,
+       0
+};
+
+
+static ush bl_count[MAX_BITS + 1];
+
+/* number of codes at each bit length for an optimal tree */
+
+static const uch bl_order[BL_CODES]
+= { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+static int heap[2 * L_CODES + 1];      /* heap used to build the Huffman trees */
+static int heap_len;   /* number of elements in the heap */
+static int heap_max;   /* element of largest frequency */
+
+/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+static uch depth[2 * L_CODES + 1];
+
+/* Depth of each subtree used as tie breaker for trees of equal frequency */
+
+static uch length_code[MAX_MATCH - MIN_MATCH + 1];
+
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+static uch dist_code[512];
+
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+static int base_length[LENGTH_CODES];
+
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+static int base_dist[D_CODES];
+
+/* First normalized distance for each code (0 = distance of 1) */
+
+#define l_buf inbuf
+/* DECLARE(uch, l_buf, LIT_BUFSIZE);  buffer for literals or lengths */
+
+/* DECLARE(ush, d_buf, DIST_BUFSIZE); buffer for distances */
+
+static uch flag_buf[(LIT_BUFSIZE / 8)];
+
+/* flag_buf is a bit array distinguishing literals from lengths in
+ * l_buf, thus indicating the presence or absence of a distance.
+ */
+
+static unsigned last_lit;      /* running index in l_buf */
+static unsigned last_dist;     /* running index in d_buf */
+static unsigned last_flags;    /* running index in flag_buf */
+static uch flags;              /* current flags not yet saved in flag_buf */
+static uch flag_bit;   /* current bit used in flags */
+
+/* bits are filled in flags starting at bit 0 (least significant).
+ * Note: these flags are overkill in the current code since we don't
+ * take advantage of DIST_BUFSIZE == LIT_BUFSIZE.
+ */
+
+static ulg opt_len;            /* bit length of current block with optimal trees */
+static ulg static_len; /* bit length of current block with static trees */
+
+static ulg compressed_len;     /* total bit length of compressed file */
+
+
+static ush *file_type; /* pointer to UNKNOWN, BINARY or ASCII */
+static int *file_method;       /* pointer to DEFLATE or STORE */
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+static void init_block(void);
+static void pqdownheap(ct_data * tree, int k);
+static void gen_bitlen(tree_desc * desc);
+static void gen_codes(ct_data * tree, int max_code);
+static void build_tree(tree_desc * desc);
+static void scan_tree(ct_data * tree, int max_code);
+static void send_tree(ct_data * tree, int max_code);
+static int build_bl_tree(void);
+static void send_all_trees(int lcodes, int dcodes, int blcodes);
+static void compress_block(ct_data * ltree, ct_data * dtree);
+static void set_file_type(void);
+
+
+#ifndef DEBUG
+#  define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len)
+   /* Send a code of the given tree. c and tree must not have side effects */
+
+#else                                                  /* DEBUG */
+#  define send_code(c, tree) \
+     { if (verbose>1) bb_error_msg("\ncd %3d ",(c)); \
+       send_bits(tree[c].Code, tree[c].Len); }
+#endif
+
+#define d_code(dist) \
+   ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ */
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Allocate the match buffer, initialize the various tables and save the
+ * location of the internal file attribute (ascii/binary) and method
+ * (DEFLATE/STORE).
+ */
+static void ct_init(ush * attr, int *methodp)
+{
+       int n;                          /* iterates over tree elements */
+       int bits;                       /* bit counter */
+       int length;                     /* length value */
+       int code;                       /* code value */
+       int dist;                       /* distance index */
+
+       file_type = attr;
+       file_method = methodp;
+       compressed_len = 0L;
+
+       if (static_dtree[0].Len != 0)
+               return;                 /* ct_init already called */
+
+       /* Initialize the mapping length (0..255) -> length code (0..28) */
+       length = 0;
+       for (code = 0; code < LENGTH_CODES - 1; code++) {
+               base_length[code] = length;
+               for (n = 0; n < (1 << extra_lbits[code]); n++) {
+                       length_code[length++] = (uch) code;
+               }
+       }
+       Assert(length == 256, "ct_init: length != 256");
+       /* Note that the length 255 (match length 258) can be represented
+        * in two different ways: code 284 + 5 bits or code 285, so we
+        * overwrite length_code[255] to use the best encoding:
+        */
+       length_code[length - 1] = (uch) code;
+
+       /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+       dist = 0;
+       for (code = 0; code < 16; code++) {
+               base_dist[code] = dist;
+               for (n = 0; n < (1 << extra_dbits[code]); n++) {
+                       dist_code[dist++] = (uch) code;
+               }
+       }
+       Assert(dist == 256, "ct_init: dist != 256");
+       dist >>= 7;                     /* from now on, all distances are divided by 128 */
+       for (; code < D_CODES; code++) {
+               base_dist[code] = dist << 7;
+               for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
+                       dist_code[256 + dist++] = (uch) code;
+               }
+       }
+       Assert(dist == 256, "ct_init: 256+dist != 512");
+
+       /* Construct the codes of the static literal tree */
+       for (bits = 0; bits <= MAX_BITS; bits++)
+               bl_count[bits] = 0;
+       n = 0;
+       while (n <= 143)
+               static_ltree[n++].Len = 8, bl_count[8]++;
+       while (n <= 255)
+               static_ltree[n++].Len = 9, bl_count[9]++;
+       while (n <= 279)
+               static_ltree[n++].Len = 7, bl_count[7]++;
+       while (n <= 287)
+               static_ltree[n++].Len = 8, bl_count[8]++;
+       /* Codes 286 and 287 do not exist, but we must include them in the
+        * tree construction to get a canonical Huffman tree (longest code
+        * all ones)
+        */
+       gen_codes((ct_data *) static_ltree, L_CODES + 1);
+
+       /* The static distance tree is trivial: */
+       for (n = 0; n < D_CODES; n++) {
+               static_dtree[n].Len = 5;
+               static_dtree[n].Code = bi_reverse(n, 5);
+       }
+
+       /* Initialize the first block of the first file: */
+       init_block();
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+static void init_block()
+{
+       int n;                          /* iterates over tree elements */
+
+       /* Initialize the trees. */
+       for (n = 0; n < L_CODES; n++)
+               dyn_ltree[n].Freq = 0;
+       for (n = 0; n < D_CODES; n++)
+               dyn_dtree[n].Freq = 0;
+       for (n = 0; n < BL_CODES; n++)
+               bl_tree[n].Freq = 0;
+
+       dyn_ltree[END_BLOCK].Freq = 1;
+       opt_len = static_len = 0L;
+       last_lit = last_dist = last_flags = 0;
+       flags = 0;
+       flag_bit = 1;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(tree, top) \
+{\
+    top = heap[SMALLEST]; \
+    heap[SMALLEST] = heap[heap_len--]; \
+    pqdownheap(tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m) \
+   (tree[n].Freq < tree[m].Freq || \
+   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+static void pqdownheap(ct_data * tree, int k)
+{
+       int v = heap[k];
+       int j = k << 1;         /* left son of k */
+
+       while (j <= heap_len) {
+               /* Set j to the smallest of the two sons: */
+               if (j < heap_len && smaller(tree, heap[j + 1], heap[j]))
+                       j++;
+
+               /* Exit if v is smaller than both sons */
+               if (smaller(tree, v, heap[j]))
+                       break;
+
+               /* Exchange v with the smallest son */
+               heap[k] = heap[j];
+               k = j;
+
+               /* And continue down the tree, setting j to the left son of k */
+               j <<= 1;
+       }
+       heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ *    above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ *     array bl_count contains the frequencies for each bit length.
+ *     The length opt_len is updated; static_len is also updated if stree is
+ *     not null.
+ */
+static void gen_bitlen(tree_desc * desc)
+{
+       ct_data *tree = desc->dyn_tree;
+       const extra_bits_t *extra = desc->extra_bits;
+       int base = desc->extra_base;
+       int max_code = desc->max_code;
+       int max_length = desc->max_length;
+       ct_data *stree = desc->static_tree;
+       int h;                          /* heap index */
+       int n, m;                       /* iterate over the tree elements */
+       int bits;                       /* bit length */
+       int xbits;                      /* extra bits */
+       ush f;                          /* frequency */
+       int overflow = 0;       /* number of elements with bit length too large */
+
+       for (bits = 0; bits <= MAX_BITS; bits++)
+               bl_count[bits] = 0;
+
+       /* In a first pass, compute the optimal bit lengths (which may
+        * overflow in the case of the bit length tree).
+        */
+       tree[heap[heap_max]].Len = 0;   /* root of the heap */
+
+       for (h = heap_max + 1; h < HEAP_SIZE; h++) {
+               n = heap[h];
+               bits = tree[tree[n].Dad].Len + 1;
+               if (bits > max_length)
+                       bits = max_length, overflow++;
+               tree[n].Len = (ush) bits;
+               /* We overwrite tree[n].Dad which is no longer needed */
+
+               if (n > max_code)
+                       continue;       /* not a leaf node */
+
+               bl_count[bits]++;
+               xbits = 0;
+               if (n >= base)
+                       xbits = extra[n - base];
+               f = tree[n].Freq;
+               opt_len += (ulg) f *(bits + xbits);
+
+               if (stree)
+                       static_len += (ulg) f *(stree[n].Len + xbits);
+       }
+       if (overflow == 0)
+               return;
+
+       Trace((stderr, "\nbit length overflow\n"));
+       /* This happens for example on obj2 and pic of the Calgary corpus */
+
+       /* Find the first bit length which could increase: */
+       do {
+               bits = max_length - 1;
+               while (bl_count[bits] == 0)
+                       bits--;
+               bl_count[bits]--;       /* move one leaf down the tree */
+               bl_count[bits + 1] += 2;        /* move one overflow item as its brother */
+               bl_count[max_length]--;
+               /* The brother of the overflow item also moves one step up,
+                * but this does not affect bl_count[max_length]
+                */
+               overflow -= 2;
+       } while (overflow > 0);
+
+       /* Now recompute all bit lengths, scanning in increasing frequency.
+        * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+        * lengths instead of fixing only the wrong ones. This idea is taken
+        * from 'ar' written by Haruhiko Okumura.)
+        */
+       for (bits = max_length; bits != 0; bits--) {
+               n = bl_count[bits];
+               while (n != 0) {
+                       m = heap[--h];
+                       if (m > max_code)
+                               continue;
+                       if (tree[m].Len != (unsigned) bits) {
+                               Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len,
+                                          bits));
+                               opt_len +=
+                                       ((long) bits - (long) tree[m].Len) * (long) tree[m].Freq;
+                               tree[m].Len = (ush) bits;
+                       }
+                       n--;
+               }
+       }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ *     zero code length.
+ */
+static void gen_codes(ct_data * tree, int max_code)
+{
+       ush next_code[MAX_BITS + 1];    /* next code value for each bit length */
+       ush code = 0;           /* running code value */
+       int bits;                       /* bit index */
+       int n;                          /* code index */
+
+       /* The distribution counts are first used to generate the code values
+        * without bit reversal.
+        */
+       for (bits = 1; bits <= MAX_BITS; bits++) {
+               next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
+       }
+       /* Check that the bit counts in bl_count are consistent. The last code
+        * must be all ones.
+        */
+       Assert(code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1,
+                  "inconsistent bit counts");
+       Tracev((stderr, "\ngen_codes: max_code %d ", max_code));
+
+       for (n = 0; n <= max_code; n++) {
+               int len = tree[n].Len;
+
+               if (len == 0)
+                       continue;
+               /* Now reverse the bits */
+               tree[n].Code = bi_reverse(next_code[len]++, len);
+
+               Tracec(tree != static_ltree,
+                          (stderr, "\nn %3d %c l %2d c %4x (%x) ", n,
+                               (isgraph(n) ? n : ' '), len, tree[n].Code,
+                               next_code[len] - 1));
+       }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ *     and corresponding code. The length opt_len is updated; static_len is
+ *     also updated if stree is not null. The field max_code is set.
+ */
+static void build_tree(tree_desc * desc)
+{
+       ct_data *tree = desc->dyn_tree;
+       ct_data *stree = desc->static_tree;
+       int elems = desc->elems;
+       int n, m;                       /* iterate over heap elements */
+       int max_code = -1;      /* largest code with non zero frequency */
+       int node = elems;       /* next internal node of the tree */
+
+       /* Construct the initial heap, with least frequent element in
+        * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+        * heap[0] is not used.
+        */
+       heap_len = 0, heap_max = HEAP_SIZE;
+
+       for (n = 0; n < elems; n++) {
+               if (tree[n].Freq != 0) {
+                       heap[++heap_len] = max_code = n;
+                       depth[n] = 0;
+               } else {
+                       tree[n].Len = 0;
+               }
+       }
+
+       /* The pkzip format requires that at least one distance code exists,
+        * and that at least one bit should be sent even if there is only one
+        * possible code. So to avoid special checks later on we force at least
+        * two codes of non zero frequency.
+        */
+       while (heap_len < 2) {
+               int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0);
+
+               tree[new].Freq = 1;
+               depth[new] = 0;
+               opt_len--;
+               if (stree)
+                       static_len -= stree[new].Len;
+               /* new is 0 or 1 so it does not have extra bits */
+       }
+       desc->max_code = max_code;
+
+       /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+        * establish sub-heaps of increasing lengths:
+        */
+       for (n = heap_len / 2; n >= 1; n--)
+               pqdownheap(tree, n);
+
+       /* Construct the Huffman tree by repeatedly combining the least two
+        * frequent nodes.
+        */
+       do {
+               pqremove(tree, n);      /* n = node of least frequency */
+               m = heap[SMALLEST];     /* m = node of next least frequency */
+
+               heap[--heap_max] = n;   /* keep the nodes sorted by frequency */
+               heap[--heap_max] = m;
+
+               /* Create a new node father of n and m */
+               tree[node].Freq = tree[n].Freq + tree[m].Freq;
+               depth[node] = (uch) (MAX(depth[n], depth[m]) + 1);
+               tree[n].Dad = tree[m].Dad = (ush) node;
+#ifdef DUMP_BL_TREE
+               if (tree == bl_tree) {
+                       bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)",
+                                       node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+               }
+#endif
+               /* and insert the new node in the heap */
+               heap[SMALLEST] = node++;
+               pqdownheap(tree, SMALLEST);
+
+       } while (heap_len >= 2);
+
+       heap[--heap_max] = heap[SMALLEST];
+
+       /* At this point, the fields freq and dad are set. We can now
+        * generate the bit lengths.
+        */
+       gen_bitlen((tree_desc *) desc);
+
+       /* The field len is now set, we can generate the bit codes */
+       gen_codes((ct_data *) tree, max_code);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree. Updates opt_len to take into account the repeat
+ * counts. (The contribution of the bit length codes will be added later
+ * during the construction of bl_tree.)
+ */
+static void scan_tree(ct_data * tree, int max_code)
+{
+       int n;                          /* iterates over all tree elements */
+       int prevlen = -1;       /* last emitted length */
+       int curlen;                     /* length of current code */
+       int nextlen = tree[0].Len;      /* length of next code */
+       int count = 0;          /* repeat count of the current code */
+       int max_count = 7;      /* max repeat count */
+       int min_count = 4;      /* min repeat count */
+
+       if (nextlen == 0)
+               max_count = 138, min_count = 3;
+       tree[max_code + 1].Len = (ush) 0xffff;  /* guard */
+
+       for (n = 0; n <= max_code; n++) {
+               curlen = nextlen;
+               nextlen = tree[n + 1].Len;
+               if (++count < max_count && curlen == nextlen) {
+                       continue;
+               } else if (count < min_count) {
+                       bl_tree[curlen].Freq += count;
+               } else if (curlen != 0) {
+                       if (curlen != prevlen)
+                               bl_tree[curlen].Freq++;
+                       bl_tree[REP_3_6].Freq++;
+               } else if (count <= 10) {
+                       bl_tree[REPZ_3_10].Freq++;
+               } else {
+                       bl_tree[REPZ_11_138].Freq++;
+               }
+               count = 0;
+               prevlen = curlen;
+               if (nextlen == 0) {
+                       max_count = 138, min_count = 3;
+               } else if (curlen == nextlen) {
+                       max_count = 6, min_count = 3;
+               } else {
+                       max_count = 7, min_count = 4;
+               }
+       }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+static void send_tree(ct_data * tree, int max_code)
+{
+       int n;                          /* iterates over all tree elements */
+       int prevlen = -1;       /* last emitted length */
+       int curlen;                     /* length of current code */
+       int nextlen = tree[0].Len;      /* length of next code */
+       int count = 0;          /* repeat count of the current code */
+       int max_count = 7;      /* max repeat count */
+       int min_count = 4;      /* min repeat count */
+
+/* tree[max_code+1].Len = -1; *//* guard already set */
+       if (nextlen == 0)
+               max_count = 138, min_count = 3;
+
+       for (n = 0; n <= max_code; n++) {
+               curlen = nextlen;
+               nextlen = tree[n + 1].Len;
+               if (++count < max_count && curlen == nextlen) {
+                       continue;
+               } else if (count < min_count) {
+                       do {
+                               send_code(curlen, bl_tree);
+                       } while (--count != 0);
+
+               } else if (curlen != 0) {
+                       if (curlen != prevlen) {
+                               send_code(curlen, bl_tree);
+                               count--;
+                       }
+                       Assert(count >= 3 && count <= 6, " 3_6?");
+                       send_code(REP_3_6, bl_tree);
+                       send_bits(count - 3, 2);
+
+               } else if (count <= 10) {
+                       send_code(REPZ_3_10, bl_tree);
+                       send_bits(count - 3, 3);
+
+               } else {
+                       send_code(REPZ_11_138, bl_tree);
+                       send_bits(count - 11, 7);
+               }
+               count = 0;
+               prevlen = curlen;
+               if (nextlen == 0) {
+                       max_count = 138, min_count = 3;
+               } else if (curlen == nextlen) {
+                       max_count = 6, min_count = 3;
+               } else {
+                       max_count = 7, min_count = 4;
+               }
+       }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+static const int build_bl_tree()
+{
+       int max_blindex;        /* index of last bit length code of non zero freq */
+
+       /* Determine the bit length frequencies for literal and distance trees */
+       scan_tree((ct_data *) dyn_ltree, l_desc.max_code);
+       scan_tree((ct_data *) dyn_dtree, d_desc.max_code);
+
+       /* Build the bit length tree: */
+       build_tree((tree_desc *) (&bl_desc));
+       /* opt_len now includes the length of the tree representations, except
+        * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+        */
+
+       /* Determine the number of bit length codes to send. The pkzip format
+        * requires that at least 4 bit length codes be sent. (appnote.txt says
+        * 3 but the actual value used is 4.)
+        */
+       for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
+               if (bl_tree[bl_order[max_blindex]].Len != 0)
+                       break;
+       }
+       /* Update opt_len to include the bit length tree and counts */
+       opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
+       Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len));
+
+       return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+static void send_all_trees(int lcodes, int dcodes, int blcodes)
+{
+       int rank;                       /* index in bl_order */
+
+       Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+       Assert(lcodes <= L_CODES && dcodes <= D_CODES
+                  && blcodes <= BL_CODES, "too many codes");
+       Tracev((stderr, "\nbl counts: "));
+       send_bits(lcodes - 257, 5);     /* not +255 as stated in appnote.txt */
+       send_bits(dcodes - 1, 5);
+       send_bits(blcodes - 4, 4);      /* not -3 as stated in appnote.txt */
+       for (rank = 0; rank < blcodes; rank++) {
+               Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+               send_bits(bl_tree[bl_order[rank]].Len, 3);
+       }
+       Tracev((stderr, "\nbl tree: sent %ld", bits_sent));
+
+       send_tree((ct_data *) dyn_ltree, lcodes - 1);   /* send the literal tree */
+       Tracev((stderr, "\nlit tree: sent %ld", bits_sent));
+
+       send_tree((ct_data *) dyn_dtree, dcodes - 1);   /* send the distance tree */
+       Tracev((stderr, "\ndist tree: sent %ld", bits_sent));
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+static ulg flush_block(char *buf, ulg stored_len, int eof)
+{
+       ulg opt_lenb, static_lenb;      /* opt_len and static_len in bytes */
+       int max_blindex;        /* index of last bit length code of non zero freq */
+
+       flag_buf[last_flags] = flags;   /* Save the flags for the last 8 items */
+
+       /* Check if the file is ascii or binary */
+       if (*file_type == (ush) UNKNOWN)
+               set_file_type();
+
+       /* Construct the literal and distance trees */
+       build_tree((tree_desc *) (&l_desc));
+       Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len));
+
+       build_tree((tree_desc *) (&d_desc));
+       Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len));
+       /* At this point, opt_len and static_len are the total bit lengths of
+        * the compressed block data, excluding the tree representations.
+        */
+
+       /* Build the bit length tree for the above two trees, and get the index
+        * in bl_order of the last bit length code to send.
+        */
+       max_blindex = build_bl_tree();
+
+       /* Determine the best encoding. Compute first the block length in bytes */
+       opt_lenb = (opt_len + 3 + 7) >> 3;
+       static_lenb = (static_len + 3 + 7) >> 3;
+
+       Trace((stderr,
+                  "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
+                  opt_lenb, opt_len, static_lenb, static_len, stored_len,
+                  last_lit, last_dist));
+
+       if (static_lenb <= opt_lenb)
+               opt_lenb = static_lenb;
+
+       /* If compression failed and this is the first and last block,
+        * and if the zip file can be seeked (to rewrite the local header),
+        * the whole file is transformed into a stored file:
+        */
+       if (stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) {
+               /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+               if (buf == (char *) 0)
+                       bb_error_msg("block vanished");
+
+               copy_block(buf, (unsigned) stored_len, 0);      /* without header */
+               compressed_len = stored_len << 3;
+               *file_method = STORED;
+
+       } else if (stored_len + 4 <= opt_lenb && buf != (char *) 0) {
+               /* 4: two words for the lengths */
+               /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+                * Otherwise we can't have processed more than WSIZE input bytes since
+                * the last block flush, because compression would have been
+                * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+                * transform a block into a stored block.
+                */
+               send_bits((STORED_BLOCK << 1) + eof, 3);        /* send block type */
+               compressed_len = (compressed_len + 3 + 7) & ~7L;
+               compressed_len += (stored_len + 4) << 3;
+
+               copy_block(buf, (unsigned) stored_len, 1);      /* with header */
+
+       } else if (static_lenb == opt_lenb) {
+               send_bits((STATIC_TREES << 1) + eof, 3);
+               compress_block((ct_data *) static_ltree, (ct_data *) static_dtree);
+               compressed_len += 3 + static_len;
+       } else {
+               send_bits((DYN_TREES << 1) + eof, 3);
+               send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1,
+                                          max_blindex + 1);
+               compress_block((ct_data *) dyn_ltree, (ct_data *) dyn_dtree);
+               compressed_len += 3 + opt_len;
+       }
+       Assert(compressed_len == bits_sent, "bad compressed size");
+       init_block();
+
+       if (eof) {
+               bi_windup();
+               compressed_len += 7;    /* align on byte boundary */
+       }
+       Tracev((stderr, "\ncomprlen %lu(%lu) ", compressed_len >> 3,
+                       compressed_len - 7 * eof));
+
+       return compressed_len >> 3;
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+static int ct_tally(int dist, int lc)
+{
+       l_buf[last_lit++] = (uch) lc;
+       if (dist == 0) {
+               /* lc is the unmatched char */
+               dyn_ltree[lc].Freq++;
+       } else {
+               /* Here, lc is the match length - MIN_MATCH */
+               dist--;                 /* dist = match distance - 1 */
+               Assert((ush) dist < (ush) MAX_DIST &&
+                          (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH) &&
+                          (ush) d_code(dist) < (ush) D_CODES, "ct_tally: bad match");
+
+               dyn_ltree[length_code[lc] + LITERALS + 1].Freq++;
+               dyn_dtree[d_code(dist)].Freq++;
+
+               d_buf[last_dist++] = (ush) dist;
+               flags |= flag_bit;
+       }
+       flag_bit <<= 1;
+
+       /* Output the flags if they fill a byte: */
+       if ((last_lit & 7) == 0) {
+               flag_buf[last_flags++] = flags;
+               flags = 0, flag_bit = 1;
+       }
+       /* Try to guess if it is profitable to stop the current block here */
+       if ((last_lit & 0xfff) == 0) {
+               /* Compute an upper bound for the compressed length */
+               ulg out_length = (ulg) last_lit * 8L;
+               ulg in_length = (ulg) strstart - block_start;
+               int dcode;
+
+               for (dcode = 0; dcode < D_CODES; dcode++) {
+                       out_length +=
+                               (ulg) dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]);
+               }
+               out_length >>= 3;
+               Trace((stderr,
+                          "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
+                          last_lit, last_dist, in_length, out_length,
+                          100L - out_length * 100L / in_length));
+               if (last_dist < last_lit / 2 && out_length < in_length / 2)
+                       return 1;
+       }
+       return (last_lit == LIT_BUFSIZE - 1 || last_dist == DIST_BUFSIZE);
+       /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K
+        * on 16 bit machines and because stored blocks are restricted to
+        * 64K-1 bytes.
+        */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+static void compress_block(ct_data * ltree, ct_data * dtree)
+{
+       unsigned dist;          /* distance of matched string */
+       int lc;                         /* match length or unmatched char (if dist == 0) */
+       unsigned lx = 0;        /* running index in l_buf */
+       unsigned dx = 0;        /* running index in d_buf */
+       unsigned fx = 0;        /* running index in flag_buf */
+       uch flag = 0;           /* current flags */
+       unsigned code;          /* the code to send */
+       int extra;                      /* number of extra bits to send */
+
+       if (last_lit != 0)
+               do {
+                       if ((lx & 7) == 0)
+                               flag = flag_buf[fx++];
+                       lc = l_buf[lx++];
+                       if ((flag & 1) == 0) {
+                               send_code(lc, ltree);   /* send a literal byte */
+                               Tracecv(isgraph(lc), (stderr, " '%c' ", lc));
+                       } else {
+                               /* Here, lc is the match length - MIN_MATCH */
+                               code = length_code[lc];
+                               send_code(code + LITERALS + 1, ltree);  /* send the length code */
+                               extra = extra_lbits[code];
+                               if (extra != 0) {
+                                       lc -= base_length[code];
+                                       send_bits(lc, extra);   /* send the extra length bits */
+                               }
+                               dist = d_buf[dx++];
+                               /* Here, dist is the match distance - 1 */
+                               code = d_code(dist);
+                               Assert(code < D_CODES, "bad d_code");
+
+                               send_code(code, dtree); /* send the distance code */
+                               extra = extra_dbits[code];
+                               if (extra != 0) {
+                                       dist -= base_dist[code];
+                                       send_bits(dist, extra); /* send the extra distance bits */
+                               }
+                       }                       /* literal or match pair ? */
+                       flag >>= 1;
+               } while (lx < last_lit);
+
+       send_code(END_BLOCK, ltree);
+}
+
+/* ===========================================================================
+ * Set the file type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+static void set_file_type()
+{
+       int n = 0;
+       unsigned ascii_freq = 0;
+       unsigned bin_freq = 0;
+
+       while (n < 7)
+               bin_freq += dyn_ltree[n++].Freq;
+       while (n < 128)
+               ascii_freq += dyn_ltree[n++].Freq;
+       while (n < LITERALS)
+               bin_freq += dyn_ltree[n++].Freq;
+       *file_type = bin_freq > (ascii_freq >> 2) ? BINARY : ASCII;
+       if (*file_type == BINARY && translate_eol) {
+               bb_error_msg("-l used on binary file");
+       }
+}
+
+/* zip.c -- compress files to the gzip or pkzip format
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+
+static ulg crc;                        /* crc on uncompressed file data */
+static long header_bytes;      /* number of bytes in gzip header */
+
+static void put_long(ulg n)
+{
+       put_short((n) & 0xffff);
+       put_short(((ulg) (n)) >> 16);
+}
+
+/* put_header_byte is used for the compressed output
+ * - for the initial 4 bytes that can't overflow the buffer.
+ */
+#define put_header_byte(c) {outbuf[outcnt++]=(uch)(c);}
+
+/* ===========================================================================
+ * Deflate in to out.
+ * IN assertions: the input and output buffers are cleared.
+ *   The variables time_stamp and save_orig_name are initialized.
+ */
+static int zip(int in, int out)
+{
+       uch my_flags = 0;       /* general purpose bit flags */
+       ush attr = 0;           /* ascii/binary flag */
+       ush deflate_flags = 0;  /* pkzip -es, -en or -ex equivalent */
+
+       ifd = in;
+       ofd = out;
+       outcnt = 0;
+
+       /* Write the header to the gzip file. See algorithm.doc for the format */
+
+
+       method = DEFLATED;
+       put_header_byte(GZIP_MAGIC[0]); /* magic header */
+       put_header_byte(GZIP_MAGIC[1]);
+       put_header_byte(DEFLATED);      /* compression method */
+
+       put_header_byte(my_flags);      /* general flags */
+       put_long(time_stamp);
+
+       /* Write deflated file to zip file */
+       crc = updcrc(0, 0);
+
+       bi_init(out);
+       ct_init(&attr, &method);
+       lm_init(&deflate_flags);
+
+       put_byte((uch) deflate_flags);  /* extra flags */
+       put_byte(OS_CODE);      /* OS identifier */
+
+       header_bytes = (long) outcnt;
+
+       (void) deflate();
+
+       /* Write the crc and uncompressed size */
+       put_long(crc);
+       put_long(isize);
+       header_bytes += 2 * sizeof(long);
+
+       flush_outbuf();
+       return OK;
+}
+
+
+/* ===========================================================================
+ * Read a new buffer from the current input file, perform end-of-line
+ * translation, and update the crc and input file size.
+ * IN assertion: size >= 2 (for end-of-line translation)
+ */
+static int file_read(char *buf, unsigned size)
+{
+       unsigned len;
+
+       Assert(insize == 0, "inbuf not empty");
+
+       len = read(ifd, buf, size);
+       if (len == (unsigned) (-1) || len == 0)
+               return (int) len;
+
+       crc = updcrc((uch *) buf, len);
+       isize += (ulg) len;
+       return (int) len;
+}
+
+/* ===========================================================================
+ * Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
+ * (used for the compressed data only)
+ */
+static void flush_outbuf()
+{
+       if (outcnt == 0)
+               return;
+
+       write_buf(ofd, (char *) outbuf, outcnt);
+       outcnt = 0;
+}
diff --git a/busybox/archival/libunarchive/Makefile b/busybox/archival/libunarchive/Makefile
new file mode 100644 (file)
index 0000000..e985fa4
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=../..
+top_builddir=../..
+srcdir=$(top_srcdir)/archival/libunarchive
+LIBUNARCHIVE_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/archival/libunarchive/Makefile.in b/busybox/archival/libunarchive/Makefile.in
new file mode 100644 (file)
index 0000000..809b0e1
--- /dev/null
@@ -0,0 +1,84 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LIBUNARCHIVE_AR:=libunarchive.a
+ifndef $(LIBUNARCHIVE_DIR)
+LIBUNARCHIVE_DIR:=$(top_builddir)/archival/libunarchive/
+endif
+srcdir=$(top_srcdir)/archvial/libunarchive
+
+LIBUNARCHIVE-y:= \
+\
+       data_skip.o \
+       data_extract_all.o \
+       data_extract_to_stdout.o \
+       data_extract_to_buffer.o \
+\
+       filter_accept_all.o \
+       filter_accept_list.o \
+       filter_accept_reject_list.o \
+\
+       header_skip.o \
+       header_list.o \
+       header_verbose_list.o \
+\
+       archive_xread_all.o \
+       archive_xread_all_eof.o \
+\
+       seek_by_char.o \
+       seek_by_jump.o \
+\
+       data_align.o \
+       find_list_entry.o \
+       open_transformer.o \
+       init_handle.o
+
+GUNZIP_FILES:= check_header_gzip.o decompress_unzip.o
+DPKG_FILES:= \
+       get_header_ar.o \
+       unpack_ar_archive.o \
+       get_header_tar.o \
+       filter_accept_list_reassign.o
+
+LIBUNARCHIVE-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o
+LIBUNARCHIVE-$(CONFIG_BUNZIP2) += decompress_bunzip2.o
+LIBUNARCHIVE-$(CONFIG_CPIO) += get_header_cpio.o
+LIBUNARCHIVE-$(CONFIG_DPKG) += $(DPKG_FILES)
+LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += $(DPKG_FILES)
+LIBUNARCHIVE-$(CONFIG_FEATURE_DEB_TAR_GZ) += $(GUNZIP_FILES) get_header_tar_gz.o
+LIBUNARCHIVE-$(CONFIG_FEATURE_DEB_TAR_BZ2) += decompress_bunzip2.o get_header_tar_bz2.o
+LIBUNARCHIVE-$(CONFIG_GUNZIP) += $(GUNZIP_FILES)
+LIBUNARCHIVE-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o
+LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o
+LIBUNARCHIVE-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o
+LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o
+LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o
+LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_GZIP) += $(GUNZIP_FILES) get_header_tar_gz.o
+LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_COMPRESS) += decompress_uncompress.o
+LIBUNARCHIVE-$(CONFIG_UNCOMPRESS) += decompress_uncompress.o
+LIBUNARCHIVE-$(CONFIG_UNZIP) += $(GUNZIP_FILES)
+
+libraries-y+=$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR)
+
+$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR): $(patsubst %,$(LIBUNARCHIVE_DIR)%, $(LIBUNARCHIVE-y))
+       $(AR) -ro $@ $(patsubst %,$(LIBUNARCHIVE_DIR)%, $(LIBUNARCHIVE-y))
+
+$(LIBUNARCHIVA_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/archival/libunarchive/archive_xread_all.c b/busybox/archival/libunarchive/archive_xread_all.c
new file mode 100644 (file)
index 0000000..ba9ade2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern void archive_xread_all(const archive_handle_t *archive_handle, void *buf, const size_t count)
+{
+       ssize_t size;
+
+       size = bb_full_read(archive_handle->src_fd, buf, count);
+       if (size != count) {
+               bb_error_msg_and_die("Short read");
+       }
+       return;
+}
diff --git a/busybox/archival/libunarchive/archive_xread_all_eof.c b/busybox/archival/libunarchive/archive_xread_all_eof.c
new file mode 100644 (file)
index 0000000..8084e35
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count)
+{
+       ssize_t size;
+
+       size = bb_full_read(archive_handle->src_fd, buf, count);
+       if ((size != 0) && (size != count)) {
+               bb_perror_msg_and_die("Short read, read %d of %d", size, count);
+       }
+       return(size);
+}
diff --git a/busybox/archival/libunarchive/check_header_gzip.c b/busybox/archival/libunarchive/check_header_gzip.c
new file mode 100644 (file)
index 0000000..13832c2
--- /dev/null
@@ -0,0 +1,57 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include "libbb.h"
+
+extern void check_header_gzip(int src_fd)
+{
+       union {
+               unsigned char raw[8];
+               struct {
+                       unsigned char method;
+                       unsigned char flags;
+                       unsigned int mtime;
+                       unsigned char xtra_flags;
+                       unsigned char os_flags;
+               } formated;
+       } header;
+
+       bb_xread_all(src_fd, header.raw, 8);
+
+       /* Check the compression method */
+       if (header.formated.method != 8) {
+               bb_error_msg_and_die("Unknown compression method %d",
+                                                 header.formated.method);
+       }
+
+       if (header.formated.flags & 0x04) {
+               /* bit 2 set: extra field present */
+               unsigned char extra_short;
+
+               extra_short = bb_xread_char(src_fd) + (bb_xread_char(src_fd) << 8);
+               while (extra_short > 0) {
+                       /* Ignore extra field */
+                       bb_xread_char(src_fd);
+                       extra_short--;
+               }
+       }
+
+       /* Discard original name if any */
+       if (header.formated.flags & 0x08) {
+               /* bit 3 set: original file name present */
+               while(bb_xread_char(src_fd) != 0);
+       }
+
+       /* Discard file comment if any */
+       if (header.formated.flags & 0x10) {
+               /* bit 4 set: file comment present */
+               while(bb_xread_char(src_fd) != 0);
+       }
+
+       /* Read the header checksum */
+       if (header.formated.flags & 0x02) {
+               bb_xread_char(src_fd);
+               bb_xread_char(src_fd);
+       }
+
+       return;
+}
diff --git a/busybox/archival/libunarchive/data_align.c b/busybox/archival/libunarchive/data_align.c
new file mode 100644 (file)
index 0000000..1d43395
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary)
+{
+       const unsigned short skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary;
+
+       archive_handle->seek(archive_handle, skip_amount);
+       archive_handle->offset += skip_amount;
+
+       return;
+}
diff --git a/busybox/archival/libunarchive/data_extract_all.c b/busybox/archival/libunarchive/data_extract_all.c
new file mode 100644 (file)
index 0000000..d10d665
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <utime.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void data_extract_all(archive_handle_t *archive_handle)
+{
+       file_header_t *file_header = archive_handle->file_header;
+       int dst_fd;
+       int res;
+
+       if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
+               char *name = bb_xstrdup(file_header->name);
+               bb_make_directory (dirname(name), -1, FILEUTILS_RECUR);
+               free(name);
+       }
+
+       /* Check if the file already exists */
+       if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
+               /* Remove the existing entry if it exists */
+               if (((file_header->mode & S_IFMT) != S_IFDIR) && (unlink(file_header->name) == -1) && (errno != ENOENT)) {
+                       bb_perror_msg_and_die("Couldnt remove old file");
+               }
+       }
+       else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) {
+               /* Remove the existing entry if its older than the extracted entry */
+               struct stat statbuf;
+               if (lstat(file_header->name, &statbuf) == -1) {
+                       if (errno != ENOENT) {
+                               bb_perror_msg_and_die("Couldnt stat old file");
+                       }
+               }
+               else if (statbuf.st_mtime <= file_header->mtime) {
+                       if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+                               bb_error_msg("%s not created: newer or same age file exists", file_header->name);
+                       }
+                       data_skip(archive_handle);
+                       return;
+               }
+               else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
+                       bb_perror_msg_and_die("Couldnt remove old file %s", file_header->name);
+               }
+       }
+
+       /* Handle hard links separately
+        * We identified hard links as regular files of size 0 with a symlink */
+       if (S_ISREG(file_header->mode) && (file_header->link_name) && (file_header->size == 0)) {
+               /* hard link */
+               res = link(file_header->link_name, file_header->name);
+               if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+                       bb_perror_msg("Couldnt create hard link");
+               }
+       } else {
+               /* Create the filesystem entry */
+               switch(file_header->mode & S_IFMT) {
+                       case S_IFREG: {
+                               /* Regular file */
+                               dst_fd = bb_xopen(file_header->name, O_WRONLY | O_CREAT | O_EXCL);
+                               bb_copyfd_size(archive_handle->src_fd, dst_fd, file_header->size);
+                               close(dst_fd);
+                               break;
+                               }
+                       case S_IFDIR:
+                               res = mkdir(file_header->name, file_header->mode);
+                               if ((errno != EISDIR) && (res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+                                       bb_perror_msg("extract_archive: %s", file_header->name);
+                               }
+                               break;
+                       case S_IFLNK:
+                               /* Symlink */
+                               res = symlink(file_header->link_name, file_header->name);
+                               if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+                                       bb_perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name);
+                               }
+                               break;
+                       case S_IFSOCK:
+                       case S_IFBLK:
+                       case S_IFCHR:
+                       case S_IFIFO:
+                               res = mknod(file_header->name, file_header->mode, file_header->device);
+                               if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+                                       bb_perror_msg("Cannot create node %s", file_header->name);
+                               }
+                               break;
+                       default:
+                               bb_error_msg_and_die("Unrecognised file type");
+               }
+       }
+
+       lchown(file_header->name, file_header->uid, file_header->gid);
+       if ((file_header->mode & S_IFMT) != S_IFLNK) {
+               chmod(file_header->name, file_header->mode);
+       }
+
+       if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
+               struct utimbuf t;
+               t.actime = t.modtime = file_header->mtime;
+               utime(file_header->name, &t);
+       }
+}
diff --git a/busybox/archival/libunarchive/data_extract_to_buffer.c b/busybox/archival/libunarchive/data_extract_to_buffer.c
new file mode 100644 (file)
index 0000000..db5521b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void data_extract_to_buffer(archive_handle_t *archive_handle)
+{
+       const unsigned int size = archive_handle->file_header->size;
+
+       archive_handle->buffer = xmalloc(size + 1);
+
+       archive_xread_all(archive_handle, archive_handle->buffer, size);
+       archive_handle->buffer[size] = '\0';
+}
diff --git a/busybox/archival/libunarchive/data_extract_to_stdout.c b/busybox/archival/libunarchive/data_extract_to_stdout.c
new file mode 100644 (file)
index 0000000..df2bca6
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "unarchive.h"
+#include <unistd.h>
+
+extern void data_extract_to_stdout(archive_handle_t *archive_handle)
+{
+       bb_copyfd_size(archive_handle->src_fd, STDOUT_FILENO, archive_handle->file_header->size);
+}
diff --git a/busybox/archival/libunarchive/data_skip.c b/busybox/archival/libunarchive/data_skip.c
new file mode 100644 (file)
index 0000000..b82c906
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern void data_skip(archive_handle_t *archive_handle)
+{
+       archive_handle->seek(archive_handle, archive_handle->file_header->size);
+}
diff --git a/busybox/archival/libunarchive/decompress_bunzip2.c b/busybox/archival/libunarchive/decompress_bunzip2.c
new file mode 100644 (file)
index 0000000..259a477
--- /dev/null
@@ -0,0 +1,611 @@
+/* vi: set sw=4 ts=4: */
+/*     Small bzip2 deflate implementation, by Rob Landley (rob@landley.net).
+
+       Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
+       which also acknowledges contributions by Mike Burrows, David Wheeler,
+       Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
+       Robert Sedgewick, and Jon L. Bentley.
+
+       This code is licensed under the LGPLv2:
+               LGPL (http://www.gnu.org/copyleft/lgpl.html
+*/
+
+/*
+       Size and speed optimizations by Manuel Novoa III  (mjn3@codepoet.org).
+
+       More efficient reading of Huffman codes, a streamlined read_bunzip()
+       function, and various other tweaks.  In (limited) tests, approximately
+       20% faster than bzcat on x86 and about 10% faster on arm.
+
+       Note that about 2/3 of the time is spent in read_unzip() reversing
+       the Burrows-Wheeler transformation.  Much of that time is delay
+       resulting from cache misses.
+
+       I would ask that anyone benefiting from this work, especially those
+       using it in commercial products, consider making a donation to my local
+       non-profit hospice organization in the name of the woman I loved, who
+       passed away Feb. 12, 2003.
+
+               In memory of Toni W. Hagan
+
+               Hospice of Acadiana, Inc.
+               2600 Johnston St., Suite 200
+               Lafayette, LA 70503-3240
+
+               Phone (337) 232-1234 or 1-800-738-2226
+               Fax   (337) 232-1297
+
+               http://www.hospiceacadiana.com/
+
+       Manuel
+ */
+
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "libbb.h"
+
+/* Constants for Huffman coding */
+#define MAX_GROUPS                     6
+#define GROUP_SIZE             50              /* 64 would have been more efficient */
+#define MAX_HUFCODE_BITS       20              /* Longest Huffman code allowed */
+#define MAX_SYMBOLS            258             /* 256 literals + RUNA + RUNB */
+#define SYMBOL_RUNA                    0
+#define SYMBOL_RUNB                    1
+
+/* Status return values */
+#define RETVAL_OK                                              0
+#define RETVAL_LAST_BLOCK                              (-1)
+#define RETVAL_NOT_BZIP_DATA                   (-2)
+#define RETVAL_UNEXPECTED_INPUT_EOF            (-3)
+#define RETVAL_UNEXPECTED_OUTPUT_EOF   (-4)
+#define RETVAL_DATA_ERROR                              (-5)
+#define RETVAL_OUT_OF_MEMORY                   (-6)
+#define RETVAL_OBSOLETE_INPUT                  (-7)
+
+/* Other housekeeping constants */
+#define IOBUF_SIZE                     4096
+
+/* This is what we know about each Huffman coding group */
+struct group_data {
+       /* We have an extra slot at the end of limit[] for a sentinal value. */
+       int limit[MAX_HUFCODE_BITS+1],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS];
+       int minLen, maxLen;
+};
+
+/* Structure holding all the housekeeping data, including IO buffers and
+   memory that persists between calls to bunzip */
+typedef struct {
+       /* State for interrupting output loop */
+       int writeCopies,writePos,writeRunCountdown,writeCount,writeCurrent;
+       /* I/O tracking data (file handles, buffers, positions, etc.) */
+       int in_fd,out_fd,inbufCount,inbufPos /*,outbufPos*/;
+       unsigned char *inbuf /*,*outbuf*/;
+       unsigned int inbufBitCount, inbufBits;
+       /* The CRC values stored in the block header and calculated from the data */
+       unsigned int crc32Table[256],headerCRC, totalCRC, writeCRC;
+       /* Intermediate buffer and its size (in bytes) */
+       unsigned int *dbuf, dbufSize;
+       /* These things are a bit too big to go on the stack */
+       unsigned char selectors[32768];                 /* nSelectors=15 bits */
+       struct group_data groups[MAX_GROUPS];   /* Huffman coding tables */
+       /* For I/O error handling */
+       jmp_buf jmpbuf;
+} bunzip_data;
+
+/* 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(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((bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)) <= 0)
+                               longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_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;
+}
+
+/* Unpacks the next block and sets up for the inverse burrows-wheeler step. */
+
+static int get_next_block(bunzip_data *bd)
+{
+       /* Note: Ignore the warning about hufGroup, base and limit being used uninitialized.
+        * They will be initialized on the fist pass of the loop. */
+       struct group_data *hufGroup;
+       int dbufCount,nextSym,dbufSize,groupCount,*base,*limit,selector,
+               i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256];
+       unsigned char uc, symToByte[256], mtfSymbol[256], *selectors;
+       unsigned int *dbuf,origPtr;
+
+       dbuf=bd->dbuf;
+       dbufSize=bd->dbufSize;
+       selectors=bd->selectors;
+       /* Reset longjmp I/O error handling */
+       i=setjmp(bd->jmpbuf);
+       if(i) return i;
+       /* Read in header signature and CRC, then validate signature.
+          (last block signature means CRC is for whole file, return now) */
+       i = get_bits(bd,24);
+       j = get_bits(bd,24);
+       bd->headerCRC=get_bits(bd,32);
+       if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK;
+       if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA;
+       /* We can add support for blockRandomised if anybody complains.  There was
+          some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
+          it didn't actually work. */
+       if(get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT;
+       if((origPtr=get_bits(bd,24)) > 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. */
+       t=get_bits(bd, 16);
+       symTotal=0;
+       for (i=0;i<16;i++) {
+               if(t&(1<<(15-i))) {
+                       k=get_bits(bd,16);
+                       for(j=0;j<16;j++)
+                               if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j;
+               }
+       }
+       /* How many different Huffman coding groups does this block use? */
+       groupCount=get_bits(bd,3);
+       if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR;
+       /* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding
+          group.  Read in the group selector list, which is stored as MTF encoded
+          bit runs.  (MTF=Move To Front, as each value is used it's moved to the
+          start of the list.) */
+       if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR;
+       for(i=0; i<groupCount; i++) mtfSymbol[i] = i;
+       for(i=0; i<nSelectors; i++) {
+               /* Get next value */
+               for(j=0;get_bits(bd,1);j++) if (j>=groupCount) return RETVAL_DATA_ERROR;
+               /* Decode MTF to get the next selector */
+               uc = mtfSymbol[j];
+               for(;j;j--) mtfSymbol[j] = mtfSymbol[j-1];
+               mtfSymbol[0]=selectors[i]=uc;
+       }
+       /* Read the Huffman coding tables for each group, which code for symTotal
+          literal symbols, plus two run symbols (RUNA, RUNB) */
+       symCount=symTotal+2;
+       for (j=0; j<groupCount; j++) {
+               unsigned char length[MAX_SYMBOLS],temp[MAX_HUFCODE_BITS+1];
+               int     minLen, maxLen, pp;
+               /* Read Huffman code lengths for each symbol.  They're stored in
+                  a way similar to mtf; record a starting value for the first symbol,
+                  and an offset from the previous value for everys symbol after that.
+                  (Subtracting 1 before the loop and then adding it back at the end is
+                  an optimization that makes the test inside the loop simpler: symbol
+                  length 0 becomes negative, so an unsigned inequality catches it.) */
+               t=get_bits(bd, 5)-1;
+               for (i = 0; i < symCount; i++) {
+                       for(;;) {
+                               if (((unsigned)t) > (MAX_HUFCODE_BITS-1))
+                                       return RETVAL_DATA_ERROR;
+                               /* If first bit is 0, stop.  Else second bit indicates whether
+                                  to increment or decrement the value.  Optimization: grab 2
+                                  bits and unget the second if the first was 0. */
+                               k = get_bits(bd,2);
+                               if (k < 2) {
+                                       bd->inbufBitCount++;
+                                       break;
+                               }
+                               /* Add one if second bit 1, else subtract 1.  Avoids if/else */
+                               t+=(((k+1)&2)-1);
+                       }
+                       /* Correct for the initial -1, to get the final symbol length */
+                       length[i]=t+1;
+               }
+               /* Find largest and smallest lengths in this group */
+               minLen=maxLen=length[0];
+               for(i = 1; i < symCount; i++) {
+                       if(length[i] > maxLen) maxLen = length[i];
+                       else if(length[i] < minLen) minLen = length[i];
+               }
+               /* Calculate permute[], base[], and limit[] tables from length[].
+                *
+                * permute[] is the lookup table for converting Huffman coded symbols
+                * into decoded symbols.  base[] is the amount to subtract from the
+                * value of a Huffman symbol of a given length when using permute[].
+                *
+                * limit[] indicates the largest numerical value a symbol with a given
+                * number of bits can have.  This is how the Huffman codes can vary in
+                * length: each code with a value>limit[length] needs another bit.
+                */
+               hufGroup=bd->groups+j;
+               hufGroup->minLen = minLen;
+               hufGroup->maxLen = maxLen;
+               /* Note that minLen can't be smaller than 1, so we adjust the base
+                  and limit array pointers so we're not always wasting the first
+                  entry.  We do this again when using them (during symbol decoding).*/
+               base=hufGroup->base-1;
+               limit=hufGroup->limit-1;
+               /* Calculate permute[].  Concurently, initialize temp[] and limit[]. */
+               pp=0;
+               for(i=minLen;i<=maxLen;i++) {
+                       temp[i]=limit[i]=0;
+                       for(t=0;t<symCount;t++)
+                               if(length[t]==i) hufGroup->permute[pp++] = t;
+               }
+               /* Count symbols coded for at each bit length */
+               for (i=0;i<symCount;i++) temp[length[i]]++;
+               /* Calculate limit[] (the largest symbol-coding value at each bit
+                * length, which is (previous limit<<1)+symbols at this level), and
+                * base[] (number of symbols to ignore at each bit length, which is
+                * limit minus the cumulative count of symbols coded for already). */
+               pp=t=0;
+               for (i=minLen; i<maxLen; i++) {
+                       pp+=temp[i];
+                       /* We read the largest possible symbol size and then unget bits
+                          after determining how many we need, and those extra bits could
+                          be set to anything.  (They're noise from future symbols.)  At
+                          each level we're really only interested in the first few bits,
+                          so here we set all the trailing to-be-ignored bits to 1 so they
+                          don't affect the value>limit[length] comparison. */
+                       limit[i]= (pp << (maxLen - i)) - 1;
+                       pp<<=1;
+                       base[i+1]=pp-(t+=temp[i]);
+               }
+               limit[maxLen+1] = INT_MAX; /* Sentinal value for reading next sym. */
+               limit[maxLen]=pp+temp[maxLen]-1;
+               base[minLen]=0;
+       }
+       /* We've finished reading and digesting the block header.  Now read this
+          block's Huffman coded symbols from the file and undo the Huffman coding
+          and run length encoding, saving the result into dbuf[dbufCount++]=uc */
+
+       /* Initialize symbol occurrence counters and symbol Move To Front table */
+       for(i=0;i<256;i++) {
+               byteCount[i] = 0;
+               mtfSymbol[i]=(unsigned char)i;
+       }
+       /* Loop through compressed symbols. */
+       runPos=dbufCount=symCount=selector=0;
+       for(;;) {
+               /* Determine which Huffman coding group to use. */
+               if(!(symCount--)) {
+                       symCount=GROUP_SIZE-1;
+                       if(selector>=nSelectors) return RETVAL_DATA_ERROR;
+                       hufGroup=bd->groups+selectors[selector++];
+                       base=hufGroup->base-1;
+                       limit=hufGroup->limit-1;
+               }
+               /* Read next Huffman-coded symbol. */
+               /* Note: It is far cheaper to read maxLen bits and back up than it is
+                  to read minLen bits and then an additional bit at a time, testing
+                  as we go.  Because there is a trailing last block (with file CRC),
+                  there is no danger of the overread causing an unexpected EOF for a
+                  valid compressed file.  As a further optimization, we do the read
+                  inline (falling back to a call to get_bits if the buffer runs
+                  dry).  The following (up to got_huff_bits:) is equivalent to
+                  j=get_bits(bd,hufGroup->maxLen);
+                */
+               while (bd->inbufBitCount<hufGroup->maxLen) {
+                       if(bd->inbufPos==bd->inbufCount) {
+                               j = get_bits(bd,hufGroup->maxLen);
+                               goto got_huff_bits;
+                       }
+                       bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
+                       bd->inbufBitCount+=8;
+               };
+               bd->inbufBitCount-=hufGroup->maxLen;
+               j = (bd->inbufBits>>bd->inbufBitCount)&((1<<hufGroup->maxLen)-1);
+got_huff_bits:
+               /* Figure how how many bits are in next symbol and unget extras */
+               i=hufGroup->minLen;
+               while(j>limit[i]) ++i;
+               bd->inbufBitCount += (hufGroup->maxLen - i);
+               /* Huffman decode value to get nextSym (with bounds checking) */
+               if ((i > hufGroup->maxLen)
+                       || (((unsigned)(j=(j>>(hufGroup->maxLen-i))-base[i]))
+                               >= MAX_SYMBOLS))
+                       return RETVAL_DATA_ERROR;
+               nextSym = hufGroup->permute[j];
+               /* We have now decoded the symbol, which indicates either a new literal
+                  byte, or a repeated run of the most recent literal byte.  First,
+                  check if nextSym indicates a repeated run, and if so loop collecting
+                  how many times to repeat the last literal. */
+               if (((unsigned)nextSym) <= SYMBOL_RUNB) { /* RUNA or RUNB */
+                       /* If this is the start of a new run, zero out counter */
+                       if(!runPos) {
+                               runPos = 1;
+                               t = 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. */
+                       t += (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+t>=dbufSize) return RETVAL_DATA_ERROR;
+
+                       uc = symToByte[mtfSymbol[0]];
+                       byteCount[uc] += t;
+                       while(t--) dbuf[dbufCount++]=uc;
+               }
+               /* Is this the terminating symbol? */
+               if(nextSym>symTotal) break;
+               /* At this point, nextSym indicates a new literal character.  Subtract
+                  one to get the position in the MTF array at which this literal is
+                  currently to be found.  (Note that the result can't be -1 or 0,
+                  because 0 and 1 are RUNA and RUNB.  But another instance of the
+                  first symbol in the mtf array, position 0, would have been handled
+                  as part of a run above.  Therefore 1 unused mtf position minus
+                  2 non-literal nextSym values equals -1.) */
+               if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR;
+               i = nextSym - 1;
+               uc = mtfSymbol[i];
+               /* Adjust the MTF array.  Since we typically expect to move only a
+                * small number of symbols, and are bound by 256 in any case, using
+                * memmove here would typically be bigger and slower due to function
+                * call overhead and other assorted setup costs. */
+               do {
+                       mtfSymbol[i] = mtfSymbol[i-1];
+               } while (--i);
+               mtfSymbol[0] = uc;
+               uc=symToByte[uc];
+               /* We have our literal byte.  Save it into dbuf. */
+               byteCount[uc]++;
+               dbuf[dbufCount++] = (unsigned int)uc;
+       }
+       /* At this point, we've read all the Huffman-coded symbols (and repeated
+       runs) for this block from the input stream, and decoded them into the
+          intermediate buffer.  There are dbufCount many decoded bytes in dbuf[].
+          Now undo the Burrows-Wheeler transform on dbuf.
+          See http://dogma.net/markn/articles/bwt/bwt.htm
+        */
+       /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
+       j=0;
+       for(i=0;i<256;i++) {
+               k=j+byteCount[i];
+               byteCount[i] = j;
+               j=k;
+       }
+       /* Figure out what order dbuf would be in if we sorted it. */
+       for (i=0;i<dbufCount;i++) {
+               uc=(unsigned char)(dbuf[i] & 0xff);
+               dbuf[byteCount[uc]] |= (i << 8);
+               byteCount[uc]++;
+       }
+       /* Decode first byte by hand to initialize "previous" byte.  Note that it
+          doesn't get output, and if the first three characters are identical
+          it doesn't qualify as a run (hence writeRunCountdown=5). */
+       if(dbufCount) {
+               if(origPtr>=dbufCount) return RETVAL_DATA_ERROR;
+               bd->writePos=dbuf[origPtr];
+           bd->writeCurrent=(unsigned char)(bd->writePos&0xff);
+               bd->writePos>>=8;
+               bd->writeRunCountdown=5;
+       }
+       bd->writeCount=dbufCount;
+
+       return RETVAL_OK;
+}
+
+/* Undo burrows-wheeler transform on intermediate buffer to produce output.
+   If start_bunzip was initialized with out_fd=-1, then up to len bytes of
+   data are written to outbuf.  Return value is number of bytes written or
+   error (all errors are negative numbers).  If out_fd!=-1, outbuf and len
+   are ignored, data is written to out_fd and return is RETVAL_OK or error.
+*/
+
+static int read_bunzip(bunzip_data *bd, char *outbuf, int len)
+{
+       const unsigned int *dbuf;
+       int pos,current,previous,gotcount;
+
+       /* If last read was short due to end of file, return last block now */
+       if(bd->writeCount<0) return bd->writeCount;
+
+       gotcount = 0;
+       dbuf=bd->dbuf;
+       pos=bd->writePos;
+       current=bd->writeCurrent;
+
+       /* We will always have pending decoded data to write into the output
+          buffer unless this is the very first call (in which case we haven't
+          Huffman-decoded a block into the intermediate buffer yet). */
+
+       if (bd->writeCopies) {
+               /* Inside the loop, writeCopies means extra copies (beyond 1) */
+               --bd->writeCopies;
+               /* Loop outputting bytes */
+               for(;;) {
+                       /* If the output buffer is full, snapshot state and return */
+                       if(gotcount >= len) {
+                               bd->writePos=pos;
+                               bd->writeCurrent=current;
+                               bd->writeCopies++;
+                               return len;
+                       }
+                       /* Write next byte into output buffer, updating CRC */
+                       outbuf[gotcount++] = current;
+                       bd->writeCRC=(((bd->writeCRC)<<8)
+                                                 ^bd->crc32Table[((bd->writeCRC)>>24)^current]);
+                       /* Loop now if we're outputting multiple copies of this byte */
+                       if (bd->writeCopies) {
+                               --bd->writeCopies;
+                               continue;
+                       }
+decode_next_byte:
+                       if (!bd->writeCount--) break;
+                       /* Follow sequence vector to undo Burrows-Wheeler transform */
+                       previous=current;
+                       pos=dbuf[pos];
+                       current=pos&0xff;
+                       pos>>=8;
+                       /* After 3 consecutive copies of the same byte, the 4th is a repeat
+                          count.  We count down from 4 instead
+                        * of counting up because testing for non-zero is faster */
+                       if(--bd->writeRunCountdown) {
+                               if(current!=previous) bd->writeRunCountdown=4;
+                       } else {
+                               /* We have a repeated run, this byte indicates the count */
+                               bd->writeCopies=current;
+                               current=previous;
+                               bd->writeRunCountdown=5;
+                               /* Sometimes there are just 3 bytes (run length 0) */
+                               if(!bd->writeCopies) goto decode_next_byte;
+                               /* Subtract the 1 copy we'd output anyway to get extras */
+                               --bd->writeCopies;
+                       }
+               }
+               /* Decompression of this block completed successfully */
+               bd->writeCRC=~bd->writeCRC;
+               bd->totalCRC=((bd->totalCRC<<1) | (bd->totalCRC>>31)) ^ bd->writeCRC;
+               /* If this block had a CRC error, force file level CRC error. */
+               if(bd->writeCRC!=bd->headerCRC) {
+                       bd->totalCRC=bd->headerCRC+1;
+                       return RETVAL_LAST_BLOCK;
+               }
+       }
+
+       /* Refill the intermediate buffer by Huffman-decoding next block of input */
+       /* (previous is just a convenient unused temp variable here) */
+       previous=get_next_block(bd);
+       if(previous) {
+               bd->writeCount=previous;
+               return (previous!=RETVAL_LAST_BLOCK) ? previous : gotcount;
+       }
+       bd->writeCRC=0xffffffffUL;
+       pos=bd->writePos;
+       current=bd->writeCurrent;
+       goto decode_next_byte;
+}
+
+/* Allocate the structure, read file header.  If in_fd==-1, inbuf must contain
+   a complete bunzip file (len bytes long).  If in_fd!=-1, inbuf and len are
+   ignored, and data is read from file handle into temporary buffer. */
+static int start_bunzip(bunzip_data **bdp, int in_fd, char *inbuf, int len)
+{
+       bunzip_data *bd;
+       unsigned int i,j,c;
+       const unsigned int BZh0=(((unsigned int)'B')<<24)+(((unsigned int)'Z')<<16)
+                                                       +(((unsigned int)'h')<<8)+(unsigned int)'0';
+
+       /* Figure out how much data to allocate */
+       i=sizeof(bunzip_data);
+       if(in_fd!=-1) i+=IOBUF_SIZE;
+       /* Allocate bunzip_data.  Most fields initialize to zero. */
+       bd=*bdp=xmalloc(i);
+       memset(bd,0,sizeof(bunzip_data));
+       /* Setup input buffer */
+       if(-1==(bd->in_fd=in_fd)) {
+               bd->inbuf=inbuf;
+               bd->inbufCount=len;
+       } else bd->inbuf=(unsigned char *)(bd+1);
+       /* Init the CRC32 table (big endian) */
+       for(i=0;i<256;i++) {
+               c=i<<24;
+               for(j=8;j;j--)
+                       c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
+               bd->crc32Table[i]=c;
+       }
+       /* Setup for I/O error handling via longjmp */
+       i=setjmp(bd->jmpbuf);
+       if(i) return i;
+
+       /* Ensure that file starts with "BZh['1'-'9']." */
+       i = get_bits(bd,32);
+       if (((unsigned int)(i-BZh0-1)) >= 9) return RETVAL_NOT_BZIP_DATA;
+
+       /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of
+          uncompressed data.  Allocate intermediate buffer for block. */
+       bd->dbufSize=100000*(i-BZh0);
+
+       bd->dbuf=xmalloc(bd->dbufSize * sizeof(int));
+       return RETVAL_OK;
+}
+
+/* Example usage: decompress src_fd to dst_fd.  (Stops at end of bzip data,
+   not end of file.) */
+extern int uncompressStream(int src_fd, int dst_fd)
+{
+       char *outbuf;
+       bunzip_data *bd;
+       int i;
+
+       outbuf=xmalloc(IOBUF_SIZE);
+       if(!(i=start_bunzip(&bd,src_fd,0,0))) {
+               for(;;) {
+                       if((i=read_bunzip(bd,outbuf,IOBUF_SIZE)) <= 0) break;
+                       if(i!=write(dst_fd,outbuf,i)) {
+                               i=RETVAL_UNEXPECTED_OUTPUT_EOF;
+                               break;
+                       }
+               }
+       }
+       /* Check CRC and release memory */
+       if(i==RETVAL_LAST_BLOCK) {
+               if (bd->headerCRC!=bd->totalCRC) {
+                       bb_error_msg("Data integrity error when decompressing.");
+               } else {
+                       i=RETVAL_OK;
+               }
+       }
+       else if (i==RETVAL_UNEXPECTED_OUTPUT_EOF) {
+               bb_error_msg("Compressed file ends unexpectedly");
+       } else {
+               bb_error_msg("Decompression failed");
+       }
+       if(bd->dbuf) free(bd->dbuf);
+       free(bd);
+       free(outbuf);
+
+       return i;
+}
+
+#ifdef TESTING
+
+static char * const bunzip_errors[]={NULL,"Bad file checksum","Not bzip data",
+               "Unexpected input EOF","Unexpected output EOF","Data error",
+                "Out of memory","Obsolete (pre 0.9.5) bzip format not supported."};
+
+/* Dumb little test thing, decompress stdin to stdout */
+int main(int argc, char *argv[])
+{
+       int i=uncompressStream(0,1);
+       char c;
+
+       if(i) fprintf(stderr,"%s\n", bunzip_errors[-i]);
+    else if(read(0,&c,1)) fprintf(stderr,"Trailing garbage ignored\n");
+       return -i;
+}
+#endif
diff --git a/busybox/archival/libunarchive/decompress_uncompress.c b/busybox/archival/libunarchive/decompress_uncompress.c
new file mode 100644 (file)
index 0000000..e39872c
--- /dev/null
@@ -0,0 +1,293 @@
+#include "config.h"
+#include "libbb.h"
+
+/* uncompress for busybox -- (c) 2002 Robert Griebl
+ *
+ * based on the original compress42.c source
+ * (see disclaimer below)
+ */
+
+
+/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
+ *
+ * Authors:
+ *   Spencer W. Thomas   (decvax!harpo!utah-cs!utah-gr!thomas)
+ *   Jim McKie           (decvax!mcvax!jim)
+ *   Steve Davies        (decvax!vax135!petsd!peora!srd)
+ *   Ken Turkowski       (decvax!decwrl!turtlevax!ken)
+ *   James A. Woods      (decvax!ihnp4!ames!jaw)
+ *   Joe Orost           (decvax!vax135!petsd!joe)
+ *   Dave Mack           (csu@alembic.acs.com)
+ *   Peter Jannesen, Network Communication Systems
+ *                       (peter@ncs.nl)
+ *
+ * marc@suse.de : a small security fix for a buffer overflow
+ *
+ * [... History snipped ...]
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Default input buffer size */
+#define        IBUFSIZ 2048
+
+/* Default output buffer size */
+#define        OBUFSIZ 2048
+
+/* Defines for third byte of header */
+#define        MAGIC_1         (char_type)'\037'       /* First byte of compressed file               */
+#define        MAGIC_2         (char_type)'\235'       /* Second byte of compressed file              */
+#define BIT_MASK       0x1f    /* Mask for 'number of compresssion bits'       */
+                                                       /* Masks 0x20 and 0x40 are free.                */
+                                                       /* I think 0x20 should mean that there is       */
+                                                       /* a fourth header byte (for expansion).        */
+#define BLOCK_MODE     0x80    /* Block compresssion if table is full and      */
+                       /* compression rate is dropping flush tables    */
+                       /* the next two codes should not be changed lightly, as they must not   */
+                       /* lie within the contiguous general code space.                        */
+#define FIRST  257             /* first free entry                             */
+#define        CLEAR   256             /* table clear output code                      */
+
+#define INIT_BITS 9            /* initial number of bits/code */
+
+
+/* machine variants which require cc -Dmachine:  pdp11, z8000, DOS */
+#define FAST
+
+#define        HBITS           17      /* 50% occupancy */
+#define        HSIZE      (1<<HBITS)
+#define        HMASK      (HSIZE-1)
+#define        HPRIME           9941
+#define        BITS               16
+#undef MAXSEG_64K
+#define MAXCODE(n)     (1L << (n))
+
+/* Block compress mode -C compatible with 2.0 */
+int block_mode = BLOCK_MODE;
+
+/* user settable max # bits/code */
+int maxbits = BITS;
+
+/* Exitcode of compress (-1 no file compressed) */
+int exit_code = -1;
+
+/* Input buffer */
+unsigned char inbuf[IBUFSIZ + 64];
+
+/* Output buffer */
+unsigned char outbuf[OBUFSIZ + 2048];
+
+
+long int htab[HSIZE];
+unsigned short codetab[HSIZE];
+
+#define        htabof(i)                               htab[i]
+#define        codetabof(i)                    codetab[i]
+#define        tab_prefixof(i)                 codetabof(i)
+#define        tab_suffixof(i)                 ((unsigned char *)(htab))[i]
+#define        de_stack                                ((unsigned char *)&(htab[HSIZE-1]))
+#define        clear_htab()                    memset(htab, -1, sizeof(htab))
+#define        clear_tab_prefixof()    memset(codetab, 0, 256);
+
+
+/*
+ * Decompress stdin to stdout.  This routine adapts to the codes in the
+ * file building the "string" table on-the-fly; requiring no table to
+ * be stored in the compressed file.  The tables used herein are shared
+ * with those of the compress() routine.  See the definitions above.
+ */
+
+extern int uncompress(int fd_in, int fd_out)
+{
+       unsigned char *stackp;
+       long int code;
+       int finchar;
+       long int oldcode;
+       long int incode;
+       int inbits;
+       int posbits;
+       int outpos;
+       int insize;
+       int bitmask;
+       long int free_ent;
+       long int maxcode;
+       long int maxmaxcode;
+       int n_bits;
+       int rsize = 0;
+
+       insize = 0;
+
+       inbuf[0] = bb_xread_char(fd_in);
+
+       maxbits = inbuf[0] & BIT_MASK;
+       block_mode = inbuf[0] & BLOCK_MODE;
+       maxmaxcode = MAXCODE(maxbits);
+
+       if (maxbits > BITS) {
+               bb_error_msg("compressed with %d bits, can only handle %d bits", maxbits,
+                                 BITS);
+               return -1;
+       }
+
+       maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
+       bitmask = (1 << n_bits) - 1;
+       oldcode = -1;
+       finchar = 0;
+       outpos = 0;
+       posbits = 0 << 3;
+
+       free_ent = ((block_mode) ? FIRST : 256);
+
+       /* As above, initialize the first 256 entries in the table. */
+       clear_tab_prefixof();
+
+       for (code = 255; code >= 0; --code) {
+               tab_suffixof(code) = (unsigned char) code;
+       }
+
+       do {
+         resetbuf:;
+               {
+                       int i;
+                       int e;
+                       int o;
+
+                       e = insize - (o = (posbits >> 3));
+
+                       for (i = 0; i < e; ++i)
+                               inbuf[i] = inbuf[i + o];
+
+                       insize = e;
+                       posbits = 0;
+               }
+
+               if (insize < (int) sizeof(inbuf) - IBUFSIZ) {
+                       rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ);
+                       insize += rsize;
+               }
+
+               inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 :
+                                 (insize << 3) - (n_bits - 1));
+
+               while (inbits > posbits) {
+                       if (free_ent > maxcode) {
+                               posbits =
+                                       ((posbits - 1) +
+                                        ((n_bits << 3) -
+                                         (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
+                               ++n_bits;
+                               if (n_bits == maxbits) {
+                                       maxcode = maxmaxcode;
+                               } else {
+                                       maxcode = MAXCODE(n_bits) - 1;
+                               }
+                               bitmask = (1 << n_bits) - 1;
+                               goto resetbuf;
+                       }
+                       {
+                               unsigned char *p = &inbuf[posbits >> 3];
+
+                               code =
+                                       ((((long) (p[0])) | ((long) (p[1]) << 8) |
+                                         ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask;
+                       }
+                       posbits += n_bits;
+
+
+                       if (oldcode == -1) {
+                               outbuf[outpos++] = (unsigned char) (finchar =
+                                                                                               (int) (oldcode = code));
+                               continue;
+                       }
+
+                       if (code == CLEAR && block_mode) {
+                               clear_tab_prefixof();
+                               free_ent = FIRST - 1;
+                               posbits =
+                                       ((posbits - 1) +
+                                        ((n_bits << 3) -
+                                         (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
+                               maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
+                               bitmask = (1 << n_bits) - 1;
+                               goto resetbuf;
+                       }
+
+                       incode = code;
+                       stackp = de_stack;
+
+                       /* Special case for KwKwK string. */
+                       if (code >= free_ent) {
+                               if (code > free_ent) {
+                                       unsigned char *p;
+
+                                       posbits -= n_bits;
+                                       p = &inbuf[posbits >> 3];
+
+                                       bb_error_msg
+                                               ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)",
+                                                insize, posbits, p[-1], p[0], p[1], p[2], p[3],
+                                                (posbits & 07));
+                                       bb_error_msg("uncompress: corrupt input");
+                                       return -1;
+                               }
+
+                               *--stackp = (unsigned char) finchar;
+                               code = oldcode;
+                       }
+
+                       /* Generate output characters in reverse order */
+                       while ((long int) code >= (long int) 256) {
+                               *--stackp = tab_suffixof(code);
+                               code = tab_prefixof(code);
+                       }
+
+                       *--stackp = (unsigned char) (finchar = tab_suffixof(code));
+
+                       /* And put them out in forward order */
+                       {
+                               int i;
+
+                               if (outpos + (i = (de_stack - stackp)) >= OBUFSIZ) {
+                                       do {
+                                               if (i > OBUFSIZ - outpos) {
+                                                       i = OBUFSIZ - outpos;
+                                               }
+
+                                               if (i > 0) {
+                                                       memcpy(outbuf + outpos, stackp, i);
+                                                       outpos += i;
+                                               }
+
+                                               if (outpos >= OBUFSIZ) {
+                                                       write(fd_out, outbuf, outpos);
+                                                       outpos = 0;
+                                               }
+                                               stackp += i;
+                                       } while ((i = (de_stack - stackp)) > 0);
+                               } else {
+                                       memcpy(outbuf + outpos, stackp, i);
+                                       outpos += i;
+                               }
+                       }
+
+                       /* Generate the new entry. */
+                       if ((code = free_ent) < maxmaxcode) {
+                               tab_prefixof(code) = (unsigned short) oldcode;
+                               tab_suffixof(code) = (unsigned char) finchar;
+                               free_ent = code + 1;
+                       }
+
+                       /* Remember previous code.  */
+                       oldcode = incode;
+               }
+
+       } while (rsize > 0);
+
+       if (outpos > 0) {
+               write(fd_out, outbuf, outpos);
+       }
+
+       return 0;
+}
diff --git a/busybox/archival/libunarchive/decompress_unzip.c b/busybox/archival/libunarchive/decompress_unzip.c
new file mode 100644 (file)
index 0000000..e8cf54b
--- /dev/null
@@ -0,0 +1,982 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gunzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
+ * files as well as stdin/stdout, and to generally behave itself wrt
+ * command line handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ * busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ *
+ * read_gz interface + associated hacking by Laurence Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * Portions of the lzw code are derived from the public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ * Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ * See the license_msg below and the file COPYING for the software license.
+ * See the file algorithm.doc for the compression algorithms and file formats.
+ */
+
+#if 0
+static char *license_msg[] = {
+       "   Copyright (C) 1992-1993 Jean-loup Gailly",
+       "   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., 675 Mass Ave, Cambridge, MA 02139, USA.",
+       0
+};
+#endif
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "config.h"
+#include "busybox.h"
+#include "unarchive.h"
+
+typedef struct huft_s {
+       unsigned char e;        /* number of extra bits or operation */
+       unsigned char b;        /* number of bits in this code or subcode */
+       union {
+               unsigned short n;       /* literal, length base, or distance base */
+               struct huft_s *t;       /* pointer to next level of table */
+       } v;
+} huft_t;
+
+static int gunzip_src_fd;
+unsigned int gunzip_bytes_out; /* number of output bytes */
+static unsigned int gunzip_outbuf_count;       /* bytes in output buffer */
+
+/* gunzip_window size--must be a power of two, and
+ *  at least 32K for zip's deflate method */
+static const int gunzip_wsize = 0x8000;
+static unsigned char *gunzip_window;
+
+static unsigned int *gunzip_crc_table;
+unsigned int gunzip_crc;
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16        /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288      /* maximum number of codes in any set */
+
+/* bitbuffer */
+static unsigned int gunzip_bb; /* bit buffer */
+static unsigned char gunzip_bk;        /* bits in bit buffer */
+
+/* These control the size of the bytebuffer */
+static unsigned int bytebuffer_max = 0x8000;
+static unsigned char *bytebuffer = NULL;
+static unsigned int bytebuffer_offset = 0;
+static unsigned int bytebuffer_size = 0;
+
+static const unsigned short mask_bits[] = {
+       0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+       0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+       3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+               67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+               5, 5, 5, 0, 99, 99
+};                                             /* 99==invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+       1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+               769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+       0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+               11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+       16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
+{
+       while (*current < required) {
+               if (bytebuffer_offset >= bytebuffer_size) {
+                       /* Leave the first 4 bytes empty so we can always unwind the bitbuffer
+                        * to the front of the bytebuffer, leave 4 bytes free at end of tail
+                        * so we can easily top up buffer in check_trailer_gzip() */
+                       bytebuffer_size = 4 + bb_xread(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8);
+                       bytebuffer_offset = 4;
+               }
+               bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
+               bytebuffer_offset++;
+               *current += 8;
+       }
+       return(bitbuffer);
+}
+
+static void make_gunzip_crc_table(void)
+{
+       const unsigned int poly = 0xedb88320;   /* polynomial exclusive-or pattern */
+       unsigned short i;       /* counter for all possible eight bit values */
+
+       /* initial shift register value */
+       gunzip_crc = 0xffffffffL;
+       gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int));
+
+       /* Compute and print table of CRC's, five per line */
+       for (i = 0; i < 256; i++) {
+               unsigned int table_entry;       /* crc shift register */
+               unsigned char k;        /* byte being shifted into crc apparatus */
+
+               table_entry = i;
+               /* The idea to initialize the register with the byte instead of
+                  * zero was stolen from Haruhiko Okumura's ar002
+                */
+               for (k = 8; k; k--) {
+                       if (table_entry & 1) {
+                               table_entry = (table_entry >> 1) ^ poly;
+                       } else {
+                               table_entry >>= 1;
+       }
+       }
+               gunzip_crc_table[i] = table_entry;
+       }
+}
+
+/*
+ * Free the malloc'ed tables built by huft_build(), which makes a linked
+ * list of the tables it made, with the links in a dummy first entry of
+ * each table.
+ * t: table to free
+ */
+static int huft_free(huft_t * t)
+{
+       huft_t *p;
+       huft_t *q;
+
+       /* Go through linked list, freeing from the malloced (t[-1]) address. */
+       p = t;
+       while (p != (huft_t *) NULL) {
+               q = (--p)->v.t;
+               free((char *) p);
+               p = q;
+       }
+       return 0;
+}
+
+/* Given a list of code lengths and a maximum table size, make a set of
+ * tables to decode that set of codes.  Return zero on success, one if
+ * the given code set is incomplete (the tables are still built in this
+ * case), two if the input is invalid (all zero length codes or an
+ * oversubscribed set of lengths), and three if not enough memory.
+ *
+ * b:  code lengths in bits (all assumed <= BMAX)
+ * n:  number of codes (assumed <= N_MAX)
+ * s:  number of simple-valued codes (0..s-1)
+ * d:  list of base values for non-simple codes
+ * e:  list of extra bits for non-simple codes
+ * t:  result: starting table
+ * m:  maximum lookup bits, returns actual
+ */
+static int huft_build(unsigned int *b, const unsigned int n,
+                                         const unsigned int s, const unsigned short *d,
+                                         const unsigned char *e, huft_t ** t, int *m)
+{
+       unsigned a;                     /* counter for codes of length k */
+       unsigned c[BMAX + 1];   /* bit length count table */
+       unsigned f;                     /* i repeats in table every f entries */
+       int g;                          /* maximum code length */
+       int h;                          /* table level */
+       register unsigned i;    /* counter, current code */
+       register unsigned j;    /* counter */
+       register int k;         /* number of bits in current code */
+       int l;                          /* bits per table (returned in m) */
+       register unsigned *p;   /* pointer into c[], b[], or v[] */
+       register huft_t *q;     /* points to current table */
+       huft_t r;                       /* table entry for structure assignment */
+       huft_t *u[BMAX];        /* table stack */
+       unsigned v[N_MAX];      /* values in order of bit length */
+       register int w;         /* bits before this table == (l * h) */
+       unsigned x[BMAX + 1];   /* bit offsets, then code stack */
+       unsigned *xp;           /* pointer into x */
+       int y;                          /* number of dummy codes added */
+       unsigned z;                     /* number of entries in current table */
+
+       /* Generate counts for each bit length */
+       memset((void *) (c), 0, sizeof(c));
+       p = b;
+       i = n;
+       do {
+               c[*p]++;                /* assume all entries <= BMAX */
+               p++;                    /* Can't combine with above line (Solaris bug) */
+       } while (--i);
+       if (c[0] == n) {        /* null input--all zero length codes */
+               *t = (huft_t *) NULL;
+               *m = 0;
+               return 0;
+       }
+
+       /* Find minimum and maximum length, bound *m by those */
+       l = *m;
+       for (j = 1; j <= BMAX; j++) {
+               if (c[j]) {
+                       break;
+               }
+       }
+       k = j;                          /* minimum code length */
+       if ((unsigned) l < j) {
+               l = j;
+       }
+       for (i = BMAX; i; i--) {
+               if (c[i]) {
+                       break;
+               }
+       }
+       g = i;                          /* maximum code length */
+       if ((unsigned) l > i) {
+               l = i;
+       }
+       *m = l;
+
+       /* Adjust last length count to fill out codes, if needed */
+       for (y = 1 << j; j < i; j++, y <<= 1) {
+               if ((y -= c[j]) < 0) {
+                       return 2;       /* bad input: more codes than bits */
+               }
+       }
+       if ((y -= c[i]) < 0) {
+               return 2;
+       }
+       c[i] += y;
+
+       /* Generate starting offsets into the value table for each length */
+       x[1] = j = 0;
+       p = c + 1;
+       xp = x + 2;
+       while (--i) {           /* note that i == g from above */
+               *xp++ = (j += *p++);
+       }
+
+       /* Make a table of values in order of bit lengths */
+       p = b;
+       i = 0;
+       do {
+               if ((j = *p++) != 0) {
+                       v[x[j]++] = i;
+               }
+       } while (++i < n);
+
+       /* Generate the Huffman codes and for each, make the table entries */
+       x[0] = i = 0;           /* first Huffman code is zero */
+       p = v;                          /* grab values in bit order */
+       h = -1;                         /* no tables yet--level -1 */
+       w = -l;                         /* bits decoded == (l * h) */
+       u[0] = (huft_t *) NULL; /* just to keep compilers happy */
+       q = (huft_t *) NULL;    /* ditto */
+       z = 0;                          /* ditto */
+
+       /* go through the bit lengths (k already is bits in shortest code) */
+       for (; k <= g; k++) {
+               a = c[k];
+               while (a--) {
+                       /* here i is the Huffman code of length k bits for value *p */
+                       /* make tables up to required level */
+                       while (k > w + l) {
+                               h++;
+                               w += l; /* previous table always l bits */
+
+                               /* compute minimum size table less than or equal to l bits */
+                               z = (z = g - w) > (unsigned) l ? l : z; /* upper limit on table size */
+                               if ((f = 1 << (j = k - w)) > a + 1) {   /* try a k-w bit table *//* too few codes for k-w bit table */
+                                       f -= a + 1;     /* deduct codes from patterns left */
+                                       xp = c + k;
+                                       while (++j < z) {       /* try smaller tables up to z bits */
+                                               if ((f <<= 1) <= *++xp) {
+                                                       break;  /* enough codes to use up j bits */
+                                               }
+                                               f -= *xp;       /* else deduct codes from patterns */
+                                       }
+                               }
+                               z = 1 << j;     /* table entries for j-bit table */
+
+                               /* allocate and link in new table */
+                               q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
+
+                               *t = q + 1;     /* link to list for huft_free() */
+                               *(t = &(q->v.t)) = NULL;
+                               u[h] = ++q;     /* table starts after link */
+
+                               /* connect to last table, if there is one */
+                               if (h) {
+                                       x[h] = i;       /* save pattern for backing up */
+                                       r.b = (unsigned char) l;        /* bits to dump before this table */
+                                       r.e = (unsigned char) (16 + j); /* bits in this table */
+                                       r.v.t = q;      /* pointer to this table */
+                                       j = i >> (w - l);       /* (get around Turbo C bug) */
+                                       u[h - 1][j] = r;        /* connect to last table */
+                               }
+                       }
+
+                       /* set up table entry in r */
+                       r.b = (unsigned char) (k - w);
+                       if (p >= v + n) {
+                               r.e = 99;       /* out of values--invalid code */
+                       } else if (*p < s) {
+                               r.e = (unsigned char) (*p < 256 ? 16 : 15);     /* 256 is end-of-block code */
+                               r.v.n = (unsigned short) (*p);  /* simple code is just the value */
+                               p++;    /* one compiler does not like *p++ */
+                       } else {
+                               r.e = (unsigned char) e[*p - s];        /* non-simple--look up in lists */
+                               r.v.n = d[*p++ - s];
+                       }
+
+                       /* fill code-like entries with r */
+                       f = 1 << (k - w);
+                       for (j = i >> w; j < z; j += f) {
+                               q[j] = r;
+                       }
+
+                       /* backwards increment the k-bit code i */
+                       for (j = 1 << (k - 1); i & j; j >>= 1) {
+                               i ^= j;
+                       }
+                       i ^= j;
+
+                       /* backup over finished tables */
+                       while ((i & ((1 << w) - 1)) != x[h]) {
+                               h--;    /* don't need to update q */
+                               w -= l;
+                       }
+               }
+       }
+       /* Return true (1) if we were given an incomplete table */
+       return y != 0 && g != 1;
+}
+
+/*
+ * inflate (decompress) the codes in a deflated (compressed) block.
+ * Return an error code or zero if it all goes ok.
+ *
+ * tl, td: literal/length and distance decoder tables
+ * bl, bd: number of bits decoded by tl[] and td[]
+ */
+static int inflate_codes(huft_t * my_tl, huft_t * my_td, const unsigned int my_bl, const unsigned int my_bd, int setup)
+{
+       static unsigned int e;  /* table entry flag/number of extra bits */
+       static unsigned int n, d;       /* length and index for copy */
+       static unsigned int w;  /* current gunzip_window position */
+       static huft_t *t;                       /* pointer to table entry */
+       static unsigned int ml, md;     /* masks for bl and bd bits */
+       static unsigned int b;  /* bit buffer */
+       static unsigned int k;                  /* number of bits in bit buffer */
+       static huft_t *tl, *td;
+       static unsigned int bl, bd;
+       static int resumeCopy = 0;
+
+       if (setup) { // 1st time we are called, copy in variables
+               tl = my_tl;
+               td = my_td;
+               bl = my_bl;
+               bd = my_bd;
+               /* make local copies of globals */
+               b = gunzip_bb;                          /* initialize bit buffer */
+               k = gunzip_bk;
+               w = gunzip_outbuf_count;                        /* initialize gunzip_window position */
+
+               /* inflate the coded data */
+               ml = mask_bits[bl];     /* precompute masks for speed */
+               md = mask_bits[bd];
+               return 0; // Don't actually do anything the first time
+       }
+
+       if (resumeCopy) goto do_copy;
+
+       while (1) {                     /* do until end of block */
+               b = fill_bitbuffer(b, &k, bl);
+               if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
+                       do {
+                               if (e == 99) {
+                                       bb_error_msg_and_die("inflate_codes error 1");;
+                               }
+                               b >>= t->b;
+                               k -= t->b;
+                               e -= 16;
+                               b = fill_bitbuffer(b, &k, e);
+                       } while ((e =
+                                         (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
+               b >>= t->b;
+               k -= t->b;
+               if (e == 16) {  /* then it's a literal */
+                       gunzip_window[w++] = (unsigned char) t->v.n;
+                       if (w == gunzip_wsize) {
+                               gunzip_outbuf_count = (w);
+                               //flush_gunzip_window();
+                               w = 0;
+                               return 1; // We have a block to read
+                       }
+               } else {                /* it's an EOB or a length */
+
+                       /* exit if end of block */
+                       if (e == 15) {
+                               break;
+                       }
+
+                       /* get length of block to copy */
+                       b = fill_bitbuffer(b, &k, e);
+                       n = t->v.n + ((unsigned) b & mask_bits[e]);
+                       b >>= e;
+                       k -= e;
+
+                       /* decode distance of block to copy */
+                       b = fill_bitbuffer(b, &k, bd);
+                       if ((e = (t = td + ((unsigned) b & md))->e) > 16)
+                               do {
+                                       if (e == 99)
+                                               bb_error_msg_and_die("inflate_codes error 2");;
+                                       b >>= t->b;
+                                       k -= t->b;
+                                       e -= 16;
+                                       b = fill_bitbuffer(b, &k, e);
+                               } while ((e =
+                                                 (t =
+                                                  t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
+                       b >>= t->b;
+                       k -= t->b;
+                       b = fill_bitbuffer(b, &k, e);
+                       d = w - t->v.n - ((unsigned) b & mask_bits[e]);
+                       b >>= e;
+                       k -= e;
+
+                       /* do the copy */
+do_copy:               do {
+                               n -= (e =
+                                         (e =
+                                          gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
+                          /* copy to new buffer to prevent possible overwrite */
+                               if (w - d >= e) {       /* (this test assumes unsigned comparison) */
+                                       memcpy(gunzip_window + w, gunzip_window + d, e);
+                                       w += e;
+                                       d += e;
+                               } else {
+                                  /* do it slow to avoid memcpy() overlap */
+                                  /* !NOMEMCPY */
+                                       do {
+                                               gunzip_window[w++] = gunzip_window[d++];
+                                       } while (--e);
+                               }
+                               if (w == gunzip_wsize) {
+                                       gunzip_outbuf_count = (w);
+                                       if (n) resumeCopy = 1;
+                                       else resumeCopy = 0;
+                                       //flush_gunzip_window();
+                                       w = 0;
+                                       return 1;
+                               }
+                       } while (n);
+                       resumeCopy = 0;
+               }
+       }
+
+       /* restore the globals from the locals */
+       gunzip_outbuf_count = w;                        /* restore global gunzip_window pointer */
+       gunzip_bb = b;                          /* restore global bit buffer */
+       gunzip_bk = k;
+
+       /* normally just after call to inflate_codes, but save code by putting it here */
+       /* free the decoding tables, return */
+       huft_free(tl);
+       huft_free(td);
+
+       /* done */
+       return 0;
+}
+
+static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup)
+{
+       static int n, b_stored, k_stored, w;
+       if (setup) {
+               n = my_n;
+               b_stored = my_b_stored;
+               k_stored = my_k_stored;
+               w = gunzip_outbuf_count;                /* initialize gunzip_window position */
+               return 0; // Don't do anything first time
+       }
+
+       /* read and output the compressed data */
+       while (n--) {
+               b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+               gunzip_window[w++] = (unsigned char) b_stored;
+               if (w == (unsigned int) gunzip_wsize) {
+                       gunzip_outbuf_count = (w);
+                       //flush_gunzip_window();
+                       w = 0;
+                       b_stored >>= 8;
+                       k_stored -= 8;
+                       return 1; // We have a block
+               }
+               b_stored >>= 8;
+               k_stored -= 8;
+       }
+
+       /* restore the globals from the locals */
+       gunzip_outbuf_count = w;                /* restore global gunzip_window pointer */
+       gunzip_bb = b_stored;   /* restore global bit buffer */
+       gunzip_bk = k_stored;
+       return 0; // Finished
+}
+
+/*
+ * decompress an inflated block
+ * e: last block flag
+ *
+ * GLOBAL VARIABLES: bb, kk,
+ */
+ // Return values: -1 = inflate_stored, -2 = inflate_codes
+static int inflate_block(int *e)
+{
+       unsigned t;                     /* block type */
+       register unsigned int b;        /* bit buffer */
+       unsigned int k; /* number of bits in bit buffer */
+
+       /* make local bit buffer */
+
+       b = gunzip_bb;
+       k = gunzip_bk;
+
+       /* read in last block bit */
+       b = fill_bitbuffer(b, &k, 1);
+       *e = (int) b & 1;
+       b >>= 1;
+       k -= 1;
+
+       /* read in block type */
+       b = fill_bitbuffer(b, &k, 2);
+       t = (unsigned) b & 3;
+       b >>= 2;
+       k -= 2;
+
+       /* restore the global bit buffer */
+       gunzip_bb = b;
+       gunzip_bk = k;
+
+       /* inflate that block type */
+       switch (t) {
+       case 0:                 /* Inflate stored */
+       {
+               unsigned int n; /* number of bytes in block */
+               unsigned int b_stored;  /* bit buffer */
+               unsigned int k_stored;  /* number of bits in bit buffer */
+
+               /* make local copies of globals */
+               b_stored = gunzip_bb;   /* initialize bit buffer */
+               k_stored = gunzip_bk;
+
+               /* go to byte boundary */
+               n = k_stored & 7;
+               b_stored >>= n;
+               k_stored -= n;
+
+               /* get the length and its complement */
+               b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
+               n = ((unsigned) b_stored & 0xffff);
+               b_stored >>= 16;
+               k_stored -= 16;
+
+               b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
+               if (n != (unsigned) ((~b_stored) & 0xffff)) {
+                       return 1;       /* error in compressed data */
+               }
+               b_stored >>= 16;
+               k_stored -= 16;
+
+               inflate_stored(n, b_stored, k_stored, 1); // Setup inflate_stored
+               return -1;
+       }
+       case 1:                 /* Inflate fixed
+                                                  * decompress an inflated type 1 (fixed Huffman codes) block.  We should
+                                                  * either replace this with a custom decoder, or at least precompute the
+                                                  * Huffman tables.
+                                                */
+       {
+               int i;                  /* temporary variable */
+               huft_t *tl;             /* literal/length code table */
+               huft_t *td;             /* distance code table */
+               unsigned int bl;                        /* lookup bits for tl */
+               unsigned int bd;                        /* lookup bits for td */
+               unsigned int l[288];    /* length list for huft_build */
+
+               /* set up literal table */
+               for (i = 0; i < 144; i++) {
+                       l[i] = 8;
+               }
+               for (; i < 256; i++) {
+                       l[i] = 9;
+               }
+               for (; i < 280; i++) {
+                       l[i] = 7;
+               }
+               for (; i < 288; i++) {  /* make a complete, but wrong code set */
+                       l[i] = 8;
+               }
+               bl = 7;
+               if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) {
+                       return i;
+               }
+
+               /* set up distance table */
+               for (i = 0; i < 30; i++) {      /* make an incomplete code set */
+                       l[i] = 5;
+               }
+               bd = 5;
+               if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) {
+                       huft_free(tl);
+                       return i;
+               }
+
+               /* decompress until an end-of-block code */
+               inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
+
+               /* huft_free code moved into inflate_codes */
+
+               return -2;
+       }
+       case 2:                 /* Inflate dynamic */
+       {
+               const int dbits = 6;    /* bits in base distance lookup table */
+               const int lbits = 9;    /* bits in base literal/length lookup table */
+
+               huft_t *tl;             /* literal/length code table */
+               huft_t *td;             /* distance code table */
+               unsigned int i;                 /* temporary variables */
+               unsigned int j;
+               unsigned int l;         /* last length */
+               unsigned int m;         /* mask for bit lengths table */
+               unsigned int n;         /* number of lengths to get */
+               unsigned int bl;                        /* lookup bits for tl */
+               unsigned int bd;                        /* lookup bits for td */
+               unsigned int nb;        /* number of bit length codes */
+               unsigned int nl;        /* number of literal/length codes */
+               unsigned int nd;        /* number of distance codes */
+
+               unsigned int ll[286 + 30];      /* literal/length and distance code lengths */
+               unsigned int b_dynamic; /* bit buffer */
+               unsigned int k_dynamic; /* number of bits in bit buffer */
+
+               /* make local bit buffer */
+               b_dynamic = gunzip_bb;
+               k_dynamic = gunzip_bk;
+
+               /* read in table lengths */
+               b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+               nl = 257 + ((unsigned int) b_dynamic & 0x1f);   /* number of literal/length codes */
+
+               b_dynamic >>= 5;
+               k_dynamic -= 5;
+               b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+               nd = 1 + ((unsigned int) b_dynamic & 0x1f);     /* number of distance codes */
+
+               b_dynamic >>= 5;
+               k_dynamic -= 5;
+               b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
+               nb = 4 + ((unsigned int) b_dynamic & 0xf);      /* number of bit length codes */
+
+               b_dynamic >>= 4;
+               k_dynamic -= 4;
+               if (nl > 286 || nd > 30) {
+                       return 1;       /* bad lengths */
+               }
+
+               /* read in bit-length-code lengths */
+               for (j = 0; j < nb; j++) {
+                       b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+                       ll[border[j]] = (unsigned int) b_dynamic & 7;
+                       b_dynamic >>= 3;
+                       k_dynamic -= 3;
+               }
+               for (; j < 19; j++) {
+                       ll[border[j]] = 0;
+               }
+
+               /* build decoding table for trees--single level, 7 bit lookup */
+               bl = 7;
+               i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
+               if (i != 0) {
+                       if (i == 1) {
+                               huft_free(tl);
+                       }
+                       return i;       /* incomplete code set */
+               }
+
+               /* read in literal and distance code lengths */
+               n = nl + nd;
+               m = mask_bits[bl];
+               i = l = 0;
+               while ((unsigned int) i < n) {
+                       b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
+                       j = (td = tl + ((unsigned int) b_dynamic & m))->b;
+                       b_dynamic >>= j;
+                       k_dynamic -= j;
+                       j = td->v.n;
+                       if (j < 16) {   /* length of code in bits (0..15) */
+                               ll[i++] = l = j;        /* save last length in l */
+                       } else if (j == 16) {   /* repeat last length 3 to 6 times */
+                               b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
+                               j = 3 + ((unsigned int) b_dynamic & 3);
+                               b_dynamic >>= 2;
+                               k_dynamic -= 2;
+                               if ((unsigned int) i + j > n) {
+                                       return 1;
+                               }
+                               while (j--) {
+                                       ll[i++] = l;
+                               }
+                       } else if (j == 17) {   /* 3 to 10 zero length codes */
+                               b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+                               j = 3 + ((unsigned int) b_dynamic & 7);
+                               b_dynamic >>= 3;
+                               k_dynamic -= 3;
+                               if ((unsigned int) i + j > n) {
+                                       return 1;
+                               }
+                               while (j--) {
+                                       ll[i++] = 0;
+                               }
+                               l = 0;
+                       } else {        /* j == 18: 11 to 138 zero length codes */
+                               b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
+                               j = 11 + ((unsigned int) b_dynamic & 0x7f);
+                               b_dynamic >>= 7;
+                               k_dynamic -= 7;
+                               if ((unsigned int) i + j > n) {
+                                       return 1;
+                               }
+                               while (j--) {
+                                       ll[i++] = 0;
+                               }
+                               l = 0;
+                       }
+               }
+
+               /* free decoding table for trees */
+               huft_free(tl);
+
+               /* restore the global bit buffer */
+               gunzip_bb = b_dynamic;
+               gunzip_bk = k_dynamic;
+
+               /* build the decoding tables for literal/length and distance codes */
+               bl = lbits;
+
+               if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) {
+                       if (i == 1) {
+                               bb_error_msg_and_die("Incomplete literal tree");
+                               huft_free(tl);
+                       }
+                       return i;       /* incomplete code set */
+               }
+
+               bd = dbits;
+               if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) {
+                       if (i == 1) {
+                               bb_error_msg_and_die("incomplete distance tree");
+                               huft_free(td);
+                       }
+                       huft_free(tl);
+                       return i;       /* incomplete code set */
+               }
+
+               /* decompress until an end-of-block code */
+               inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
+
+               /* huft_free code moved into inflate_codes */
+
+               return -2;
+       }
+       default:
+               /* bad block type */
+               bb_error_msg_and_die("bad block type %d\n", t);
+       }
+}
+
+static void calculate_gunzip_crc(void)
+{
+       int n;
+       for (n = 0; n < gunzip_outbuf_count; n++) {
+               gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
+       }
+       gunzip_bytes_out += gunzip_outbuf_count;
+}
+
+static int inflate_get_next_window(void)
+{
+       static int method = -1; // Method == -1 for stored, -2 for codes
+       static int e = 0;
+       static int needAnotherBlock = 1;
+
+       gunzip_outbuf_count = 0;
+
+       while(1) {
+               int ret;
+
+               if (needAnotherBlock) {
+                       if(e) {
+                               calculate_gunzip_crc();
+                               e = 0;
+                               needAnotherBlock = 1;
+                               return 0;
+                       } // Last block
+                       method = inflate_block(&e);
+                       needAnotherBlock = 0;
+               }
+
+               switch (method) {
+                       case -1:        ret = inflate_stored(0,0,0,0);
+                                       break;
+                       case -2:        ret = inflate_codes(0,0,0,0,0);
+                                       break;
+                       default:        bb_error_msg_and_die("inflate error %d", method);
+               }
+
+               if (ret == 1) {
+                       calculate_gunzip_crc();
+                       return 1; // More data left
+               } else needAnotherBlock = 1; // End of that block
+       }
+       /* Doesnt get here */
+}
+
+/* Initialise bytebuffer, be careful not to overfill the buffer */
+extern void inflate_init(unsigned int bufsize)
+{
+       /* Set the bytebuffer size, default is same as gunzip_wsize */
+       bytebuffer_max = bufsize + 8;
+       bytebuffer_offset = 4;
+       bytebuffer_size = 0;
+}
+
+extern int inflate_unzip(int in, int out)
+{
+       ssize_t nwrote;
+       typedef void (*sig_type) (int);
+
+       /* Allocate all global buffers (for DYN_ALLOC option) */
+       gunzip_window = xmalloc(gunzip_wsize);
+       gunzip_outbuf_count = 0;
+       gunzip_bytes_out = 0;
+       gunzip_src_fd = in;
+
+       /* initialize gunzip_window, bit buffer */
+       gunzip_bk = 0;
+       gunzip_bb = 0;
+
+       /* Create the crc table */
+       make_gunzip_crc_table();
+
+       /* Allocate space for buffer */
+       bytebuffer = xmalloc(bytebuffer_max);
+
+       while(1) {
+               int ret = inflate_get_next_window();
+               nwrote = bb_full_write(out, gunzip_window, gunzip_outbuf_count);
+               if (nwrote == -1) {
+                       bb_perror_msg("write");
+                       return -1;
+               }
+               if (ret == 0) break;
+       }
+
+       /* Cleanup */
+       free(gunzip_window);
+       free(gunzip_crc_table);
+
+       /* Store unused bytes in a global buffer so calling applets can access it */
+       if (gunzip_bk >= 8) {
+               /* Undo too much lookahead. The next read will be byte aligned
+                * so we can discard unused bits in the last meaningful byte. */
+               bytebuffer_offset--;
+               bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
+               gunzip_bb >>= 8;
+               gunzip_bk -= 8;
+       }
+       return 0;
+}
+
+extern int inflate_gunzip(int in, int out)
+{
+       unsigned int stored_crc = 0;
+       unsigned char count;
+
+       inflate_unzip(in, out);
+
+       /* top up the input buffer with the rest of the trailer */
+       count = bytebuffer_size - bytebuffer_offset;
+       if (count < 8) {
+               bb_xread_all(in, &bytebuffer[bytebuffer_size], 8 - count);
+               bytebuffer_size += 8 - count;
+       }
+       for (count = 0; count != 4; count++) {
+               stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
+               bytebuffer_offset++;
+       }
+
+       /* Validate decompression - crc */
+       if (stored_crc != (gunzip_crc ^ 0xffffffffL)) {
+               bb_error_msg("crc error");
+       }
+
+       /* Validate decompression - size */
+       if (gunzip_bytes_out !=
+               (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
+               (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24))) {
+               bb_error_msg("Incorrect length");
+       }
+
+       return 0;
+}
diff --git a/busybox/archival/libunarchive/filter_accept_all.c b/busybox/archival/libunarchive/filter_accept_all.c
new file mode 100644 (file)
index 0000000..baf9e4b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (C) 2002 by Glenn McGrath
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fnmatch.h>
+#include <stdlib.h>
+
+#include "unarchive.h"
+
+/* Accept any non-null name, its not really a filter at all */
+extern char filter_accept_all(archive_handle_t *archive_handle)
+{
+       if (archive_handle->file_header->name) {
+               return(EXIT_SUCCESS);
+       } else {
+               return(EXIT_FAILURE);
+       }
+}
diff --git a/busybox/archival/libunarchive/filter_accept_list.c b/busybox/archival/libunarchive/filter_accept_list.c
new file mode 100644 (file)
index 0000000..e1c4827
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (C) 2002 by Glenn McGrath
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fnmatch.h>
+#include <stdlib.h>
+
+#include "unarchive.h"
+
+/*
+ * Accept names that are in the accept list, ignoring reject list.
+ */
+extern char filter_accept_list(archive_handle_t *archive_handle)
+{
+       if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
+               return(EXIT_SUCCESS);
+       } else {
+               return(EXIT_FAILURE);
+       }
+}
diff --git a/busybox/archival/libunarchive/filter_accept_list_reassign.c b/busybox/archival/libunarchive/filter_accept_list_reassign.c
new file mode 100644 (file)
index 0000000..d043654
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2002 by Glenn McGrath
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+/*
+ *     Reassign the subarchive metadata parser based on the filename extension
+ *  e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz
+ *       or if its a .tar.bz2 make archive_handle->sub_archive handle that
+ */
+extern char filter_accept_list_reassign(archive_handle_t *archive_handle)
+{
+       /* Check the file entry is in the accept list */
+       if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
+               const char *name_ptr;
+
+               /* Extract the last 2 extensions */
+               name_ptr = strrchr(archive_handle->file_header->name, '.');
+
+               /* Modify the subarchive handler based on the extension */
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+               if (strcmp(name_ptr, ".gz") == 0) {
+                       archive_handle->action_data_subarchive = get_header_tar_gz;
+                       return(EXIT_SUCCESS);
+               }
+#endif
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+               if (strcmp(name_ptr, ".bz2") == 0) {
+                       archive_handle->action_data_subarchive = get_header_tar_bz2;
+                       return(EXIT_SUCCESS);
+               }
+#endif
+       }
+       return(EXIT_FAILURE);
+}
diff --git a/busybox/archival/libunarchive/filter_accept_reject_list.c b/busybox/archival/libunarchive/filter_accept_reject_list.c
new file mode 100644 (file)
index 0000000..657f7a0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (C) 2002 by Glenn McGrath
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fnmatch.h>
+#include <stdlib.h>
+
+#include "unarchive.h"
+
+/*
+ * Accept names that are in the accept list and not in the reject list
+ */
+extern char filter_accept_reject_list(archive_handle_t *archive_handle)
+{
+       const char *key = archive_handle->file_header->name;
+       const llist_t *accept_entry = find_list_entry(archive_handle->accept, key);
+       const llist_t *reject_entry = find_list_entry(archive_handle->reject, key);
+
+       /* If the key is in a reject list fail */
+       if (reject_entry) {
+               return(EXIT_FAILURE);
+       }
+
+       /* Fail if an accept list was specified and the key wasnt in there */
+       if (archive_handle->accept && (accept_entry == NULL)) {
+               return(EXIT_FAILURE);
+       }
+
+       /* Accepted */
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/archival/libunarchive/find_list_entry.c b/busybox/archival/libunarchive/find_list_entry.c
new file mode 100644 (file)
index 0000000..7ed9e33
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fnmatch.h>
+#include <stdlib.h>
+#include "unarchive.h"
+
+extern const llist_t *find_list_entry(const llist_t *list, const char *filename)
+{
+       while (list) {
+               if (fnmatch(list->data, filename, 0) == 0) {
+                       return(list);
+               }
+               list = list->link;
+       }
+       return(NULL);
+}
diff --git a/busybox/archival/libunarchive/get_header_ar.c b/busybox/archival/libunarchive/get_header_ar.c
new file mode 100644 (file)
index 0000000..ebb6f8c
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern char get_header_ar(archive_handle_t *archive_handle)
+{
+       file_header_t *typed = archive_handle->file_header;
+       union {
+               char raw[60];
+               struct {
+                       char name[16];
+                       char date[12];
+                       char uid[6];
+                       char gid[6];
+                       char mode[8];
+                       char size[10];
+                       char magic[2];
+               } formated;
+       } ar;
+#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
+       static char *ar_long_names;
+       static unsigned int ar_long_name_size;
+#endif
+
+       /* dont use bb_xread as we want to handle the error ourself */
+       if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
+               /* End Of File */
+               return(EXIT_FAILURE);
+       }
+
+       /* ar header starts on an even byte (2 byte aligned)
+        * '\n' is used for padding
+        */
+       if (ar.raw[0] == '\n') {
+               /* fix up the header, we started reading 1 byte too early */
+               memmove(ar.raw, &ar.raw[1], 59);
+               ar.raw[59] = bb_xread_char(archive_handle->src_fd);
+               archive_handle->offset++;
+       }
+       archive_handle->offset += 60;
+
+       /* align the headers based on the header magic */
+       if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
+               bb_error_msg_and_die("Invalid ar header");
+       }
+
+       typed->mode = strtol(ar.formated.mode, NULL, 8);
+       typed->mtime = atoi(ar.formated.date);
+       typed->uid = atoi(ar.formated.uid);
+       typed->gid = atoi(ar.formated.gid);
+       typed->size = atoi(ar.formated.size);
+
+       /* long filenames have '/' as the first character */
+       if (ar.formated.name[0] == '/') {
+#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
+               if (ar.formated.name[1] == '/') {
+                       /* If the second char is a '/' then this entries data section
+                        * stores long filename for multiple entries, they are stored
+                        * in static variable long_names for use in future entries */
+                       ar_long_name_size = typed->size;
+                       ar_long_names = xmalloc(ar_long_name_size);
+                       bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size);
+                       archive_handle->offset += ar_long_name_size;
+                       /* This ar entries data section only contained filenames for other records
+                        * they are stored in the static ar_long_names for future reference */
+                       return (get_header_ar(archive_handle)); /* Return next header */
+               } else if (ar.formated.name[1] == ' ') {
+                       /* This is the index of symbols in the file for compilers */
+                       data_skip(archive_handle);
+                       archive_handle->offset += typed->size;
+                       return (get_header_ar(archive_handle)); /* Return next header */
+               } else {
+                       /* The number after the '/' indicates the offset in the ar data section
+                       (saved in variable long_name) that conatains the real filename */
+                       const unsigned int long_offset = atoi(&ar.formated.name[1]);
+                       if (long_offset >= ar_long_name_size) {
+                               bb_error_msg_and_die("Cant resolve long filename");
+                       }
+                       typed->name = bb_xstrdup(ar_long_names + long_offset);
+               }
+#else
+               bb_error_msg_and_die("long filenames not supported");
+#endif
+       } else {
+               /* short filenames */
+               typed->name = bb_xstrndup(ar.formated.name, 16);
+       }
+
+       typed->name[strcspn(typed->name, " /")] = '\0';
+
+       if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+               archive_handle->action_header(typed);
+               if (archive_handle->sub_archive) {
+                       while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
+               } else {
+                       archive_handle->action_data(archive_handle);
+               }
+       } else {
+               data_skip(archive_handle);
+       }
+
+       archive_handle->offset += typed->size;
+       /* Set the file pointer to the correct spot, we may have been reading a compressed file */
+       lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/archival/libunarchive/get_header_cpio.c b/busybox/archival/libunarchive/get_header_cpio.c
new file mode 100644 (file)
index 0000000..11925c4
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/sysmacros.h>     /* major() and minor() */
+#include "unarchive.h"
+#include "libbb.h"
+
+typedef struct hardlinks_s {
+       file_header_t *entry;
+       int inode;
+       struct hardlinks_s *next;
+} hardlinks_t;
+
+extern char get_header_cpio(archive_handle_t *archive_handle)
+{
+       static hardlinks_t *saved_hardlinks = NULL;
+       static unsigned short pending_hardlinks = 0;
+       file_header_t *file_header = archive_handle->file_header;
+       char cpio_header[110];
+       int namesize;
+       char dummy[16];
+       int major, minor, nlink, inode;
+
+       if (pending_hardlinks) { /* Deal with any pending hardlinks */
+               hardlinks_t *tmp;
+               hardlinks_t *oldtmp;
+
+               tmp = saved_hardlinks;
+               oldtmp = NULL;
+
+               while (tmp) {
+                       bb_error_msg_and_die("need to fix this\n");
+                       if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */
+                               file_header = tmp->entry;
+                               if (oldtmp) {
+                                       oldtmp->next = tmp->next; /* Remove item from linked list */
+                               } else {
+                                       saved_hardlinks = tmp->next;
+                               }
+                               free(tmp);
+                               continue;
+                       }
+                       oldtmp = tmp;
+                       tmp = tmp->next;
+               }
+               pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */
+       }
+
+       /* There can be padding before archive header */
+       data_align(archive_handle, 4);
+
+       if (archive_xread_all_eof(archive_handle, cpio_header, 110) == 0) {
+               return(EXIT_FAILURE);
+       }
+       archive_handle->offset += 110;
+
+       if ((strncmp(&cpio_header[0], "07070", 5) != 0) || ((cpio_header[5] != '1') && (cpio_header[5] != '2'))) {
+               bb_error_msg_and_die("Unsupported cpio format, use newc or crc");
+       }
+
+       {
+           unsigned long tmpsize;
+           sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
+                   dummy, &inode, (unsigned int*)&file_header->mode,
+                   (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
+                   &nlink, &file_header->mtime, &tmpsize,
+                   dummy, &major, &minor, &namesize, dummy);
+           file_header->size = tmpsize;
+       }
+
+       file_header->name = (char *) xmalloc(namesize + 1);
+       archive_xread_all(archive_handle, file_header->name, namesize); /* Read in filename */
+       file_header->name[namesize] = '\0';
+       archive_handle->offset += namesize;
+
+       /* Update offset amount and skip padding before file contents */
+       data_align(archive_handle, 4);
+
+       if (strcmp(file_header->name, "TRAILER!!!") == 0) {
+               printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */
+               if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
+                       hardlinks_t *tmp = saved_hardlinks;
+                       hardlinks_t *oldtmp = NULL;
+                       while (tmp) {
+                               bb_error_msg("%s not created: cannot resolve hardlink", tmp->entry->name);
+                               oldtmp = tmp;
+                               tmp = tmp->next;
+                               free (oldtmp->entry->name);
+                               free (oldtmp->entry);
+                               free (oldtmp);
+                       }
+                       saved_hardlinks = NULL;
+                       pending_hardlinks = 0;
+               }
+               return(EXIT_FAILURE);
+       }
+
+       if (S_ISLNK(file_header->mode)) {
+               file_header->link_name = (char *) xmalloc(file_header->size + 1);
+               archive_xread_all(archive_handle, file_header->link_name, file_header->size);
+               file_header->link_name[file_header->size] = '\0';
+               archive_handle->offset += file_header->size;
+               file_header->size = 0; /* Stop possible seeks in future */
+       } else {
+               file_header->link_name = NULL;
+       }
+       if (nlink > 1 && !S_ISDIR(file_header->mode)) {
+               if (file_header->size == 0) { /* Put file on a linked list for later */
+                       hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
+                       new->next = saved_hardlinks;
+                       new->inode = inode;
+                       new->entry = file_header;
+                       saved_hardlinks = new;
+                       return(EXIT_SUCCESS); // Skip this one
+               } else { /* Found the file with data in */
+                       hardlinks_t *tmp = saved_hardlinks;
+                       pending_hardlinks = 1;
+                       while (tmp) {
+                               if (tmp->inode == inode) {
+                                       tmp->entry->link_name = bb_xstrdup(file_header->name);
+                                       nlink--;
+                               }
+                               tmp = tmp->next;
+                       }
+                       if (nlink > 1) {
+                               bb_error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?");
+                       }
+               }
+       }
+       file_header->device = makedev(major, minor);
+
+       if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+               archive_handle->action_data(archive_handle);
+               archive_handle->action_header(archive_handle->file_header);
+       } else {
+               data_skip(archive_handle);
+       }
+
+       archive_handle->offset += file_header->size;
+
+       free(file_header->link_name);
+
+       return (EXIT_SUCCESS);
+}
diff --git a/busybox/archival/libunarchive/get_header_tar.c b/busybox/archival/libunarchive/get_header_tar.c
new file mode 100644 (file)
index 0000000..1ad9ac5
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  FIXME:
+ *    In privileged mode if uname and gname map to a uid and gid then use the
+ *    mapped value instead of the uid/gid values in tar header
+ *
+ *  References:
+ *    GNU tar and star man pages,
+ *    Opengroup's ustar interchange format,
+ *             http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
+static char *longname = NULL;
+static char *linkname = NULL;
+#endif
+
+extern char get_header_tar(archive_handle_t *archive_handle)
+{
+       file_header_t *file_header = archive_handle->file_header;
+       union {
+               /* ustar header, Posix 1003.1 */
+               unsigned char raw[512];
+               struct {
+                       char name[100]; /*   0-99 */
+                       char mode[8];   /* 100-107 */
+                       char uid[8];    /* 108-115 */
+                       char gid[8];    /* 116-123 */
+                       char size[12];  /* 124-135 */
+                       char mtime[12]; /* 136-147 */
+                       char chksum[8]; /* 148-155 */
+                       char typeflag;  /* 156-156 */
+                       char linkname[100];     /* 157-256 */
+                       char magic[6];  /* 257-262 */
+                       char version[2];        /* 263-264 */
+                       char uname[32]; /* 265-296 */
+                       char gname[32]; /* 297-328 */
+                       char devmajor[8];       /* 329-336 */
+                       char devminor[8];       /* 337-344 */
+                       char prefix[155];       /* 345-499 */
+                       char padding[12];       /* 500-512 */
+               } formated;
+       } tar;
+       long sum = 0;
+       long i;
+
+       /* Align header */
+       data_align(archive_handle, 512);
+
+       if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) {
+               /* Assume end of file */
+               return(EXIT_FAILURE);
+       }
+       archive_handle->offset += 512;
+
+       /* If there is no filename its an empty header */
+       if (tar.formated.name[0] == 0) {
+               return(EXIT_SUCCESS);
+       }
+
+       /* Check header has valid magic, "ustar" is for the proper tar
+        * 0's are for the old tar format
+        */
+       if (strncmp(tar.formated.magic, "ustar", 5) != 0) {
+#ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY
+               if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0)
+#endif
+                       bb_error_msg_and_die("Invalid tar magic");
+       }
+       /* Do checksum on headers */
+       for (i =  0; i < 148 ; i++) {
+               sum += tar.raw[i];
+       }
+       sum += ' ' * 8;
+       for (i =  156; i < 512 ; i++) {
+               sum += tar.raw[i];
+       }
+       if (sum != strtol(tar.formated.chksum, NULL, 8)) {
+               bb_error_msg("Invalid tar header checksum");
+               return(EXIT_FAILURE);
+       }
+
+#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
+       if (longname) {
+               file_header->name = longname;
+               longname = NULL;
+       }
+       else if (linkname) {
+               file_header->name = linkname;
+               linkname = NULL;
+       } else
+#endif
+       if (tar.formated.prefix[0] == 0) {
+               file_header->name = strdup(tar.formated.name);
+       } else {
+               file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name);
+       }
+
+       file_header->uid = strtol(tar.formated.uid, NULL, 8);
+       file_header->gid = strtol(tar.formated.gid, NULL, 8);
+       file_header->size = strtol(tar.formated.size, NULL, 8);
+       file_header->mtime = strtol(tar.formated.mtime, NULL, 8);
+       file_header->link_name = (tar.formated.linkname[0] != '\0') ?
+           bb_xstrdup(tar.formated.linkname) : NULL;
+       file_header->device = makedev(strtol(tar.formated.devmajor, NULL, 8),
+               strtol(tar.formated.devminor, NULL, 8));
+
+       /* Set bits 0-11 of the files mode */
+       file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8);
+
+       /* Set bits 12-15 of the files mode */
+       switch (tar.formated.typeflag) {
+       /* busybox identifies hard links as being regular files with 0 size and a link name */
+       case '1':
+               file_header->mode |= S_IFREG;
+               break;
+       case 'x':
+       case 'g':
+               bb_error_msg_and_die("pax is not tar");
+               break;
+       case '7':
+               /* Reserved for high performance files, treat as normal file */
+       case 0:
+       case '0':
+#ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY
+               if (last_char_is(file_header->name, '/')) {
+                       file_header->mode |= S_IFDIR;
+               } else
+#endif
+                       file_header->mode |= S_IFREG;
+               break;
+       case '2':
+               file_header->mode |= S_IFLNK;
+               break;
+       case '3':
+               file_header->mode |= S_IFCHR;
+               break;
+       case '4':
+               file_header->mode |= S_IFBLK;
+               break;
+       case '5':
+               file_header->mode |= S_IFDIR;
+               break;
+       case '6':
+               file_header->mode |= S_IFIFO;
+               break;
+#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
+       case 'L': {
+                       longname = xmalloc(file_header->size + 1);
+                       archive_xread_all(archive_handle, longname, file_header->size);
+                       longname[file_header->size] = '\0';
+                       archive_handle->offset += file_header->size;
+
+                       return(get_header_tar(archive_handle));
+               }
+       case 'K': {
+                       linkname = xmalloc(file_header->size + 1);
+                       archive_xread_all(archive_handle, linkname, file_header->size);
+                       linkname[file_header->size] = '\0';
+                       archive_handle->offset += file_header->size;
+
+                       file_header->name = linkname;
+                       return(get_header_tar(archive_handle));
+               }
+       case 'D':       /* GNU dump dir */
+       case 'M':       /* Continuation of multi volume archive*/
+       case 'N':       /* Old GNU for names > 100 characters */
+       case 'S':       /* Sparse file */
+       case 'V':       /* Volume header */
+               bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag);
+#endif
+       default:
+               bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag);
+       }
+       {       /* Strip trailing '/' in directories */
+               /* Must be done after mode is set as '/' is used to check if its a directory */
+               char *tmp = last_char_is(file_header->name, '/');
+               if (tmp) {
+                       *tmp = '\0';
+               }
+       }
+
+       if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+               archive_handle->action_header(archive_handle->file_header);
+               archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
+               archive_handle->action_data(archive_handle);
+               archive_handle->passed = llist_add_to(archive_handle->passed, file_header->name);
+       } else {
+               data_skip(archive_handle);
+       }
+       archive_handle->offset += file_header->size;
+
+       free(file_header->link_name);
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/archival/libunarchive/get_header_tar_bz2.c b/busybox/archival/libunarchive/get_header_tar_bz2.c
new file mode 100644 (file)
index 0000000..d49d6b9
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+extern char get_header_tar_bz2(archive_handle_t *archive_handle)
+{
+       /* Cant lseek over pipe's */
+       archive_handle->seek = seek_by_char;
+
+       archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompressStream);
+       archive_handle->offset = 0;
+       while (get_header_tar(archive_handle) == EXIT_SUCCESS);
+
+       /* Can only do one file at a time */
+       return(EXIT_FAILURE);
+}
diff --git a/busybox/archival/libunarchive/get_header_tar_gz.c b/busybox/archival/libunarchive/get_header_tar_gz.c
new file mode 100644 (file)
index 0000000..9c708a9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+extern char get_header_tar_gz(archive_handle_t *archive_handle)
+{
+       unsigned char magic[2];
+
+       /* Cant lseek over pipe's */
+       archive_handle->seek = seek_by_char;
+
+       archive_xread_all(archive_handle, &magic, 2);
+       if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+               bb_error_msg_and_die("Invalid gzip magic");
+       }
+
+       check_header_gzip(archive_handle->src_fd);
+
+       archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
+       archive_handle->offset = 0;
+       while (get_header_tar(archive_handle) == EXIT_SUCCESS);
+
+       /* Can only do one file at a time */
+       return(EXIT_FAILURE);
+}
diff --git a/busybox/archival/libunarchive/header_list.c b/busybox/archival/libunarchive/header_list.c
new file mode 100644 (file)
index 0000000..5849a76
--- /dev/null
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include "unarchive.h"
+
+extern void header_list(const file_header_t *file_header)
+{
+       puts(file_header->name);
+}
diff --git a/busybox/archival/libunarchive/header_skip.c b/busybox/archival/libunarchive/header_skip.c
new file mode 100644 (file)
index 0000000..4430178
--- /dev/null
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include "unarchive.h"
+
+extern void header_skip(const file_header_t *file_header)
+{
+       return;
+}
diff --git a/busybox/archival/libunarchive/header_verbose_list.c b/busybox/archival/libunarchive/header_verbose_list.c
new file mode 100644 (file)
index 0000000..6739dd3
--- /dev/null
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void header_verbose_list(const file_header_t *file_header)
+{
+       struct tm *mtime = localtime(&file_header->mtime);
+
+       printf("%s %d/%d%10u %4u-%02u-%02u %02u:%02u:%02u %s",
+               bb_mode_string(file_header->mode),
+               file_header->uid,
+               file_header->gid,
+               (unsigned int) file_header->size,
+               1900 + mtime->tm_year,
+               1 + mtime->tm_mon,
+               mtime->tm_mday,
+               mtime->tm_hour,
+               mtime->tm_min,
+               mtime->tm_sec,
+               file_header->name);
+
+       if (file_header->link_name) {
+               printf(" -> %s", file_header->link_name);
+       }
+       /* putchar isnt used anywhere else i dont think */
+       puts("");
+}
diff --git a/busybox/archival/libunarchive/init_handle.c b/busybox/archival/libunarchive/init_handle.c
new file mode 100644 (file)
index 0000000..3cee84f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+archive_handle_t *init_handle(void)
+{
+       archive_handle_t *archive_handle;
+
+       /* Initialise default values */
+       archive_handle = xmalloc(sizeof(archive_handle_t));
+       memset(archive_handle, 0, sizeof(archive_handle_t));
+       archive_handle->file_header = xmalloc(sizeof(file_header_t));
+       archive_handle->action_header = header_skip;
+       archive_handle->action_data = data_skip;
+       archive_handle->filter = filter_accept_all;
+       archive_handle->seek = seek_by_jump;
+
+       return(archive_handle);
+}
diff --git a/busybox/archival/libunarchive/open_transformer.c b/busybox/archival/libunarchive/open_transformer.c
new file mode 100644 (file)
index 0000000..fb149fc
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libbb.h"
+
+/* transformer(), more than meets the eye */
+extern int open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd))
+{
+       int fd_pipe[2];
+       int pid;
+
+       if (pipe(fd_pipe) != 0) {
+               bb_perror_msg_and_die("Can't create pipe");
+       }
+
+       pid = fork();
+       if (pid == -1) {
+               bb_perror_msg_and_die("Fork failed");
+       }
+
+       if (pid == 0) {
+               /* child process */
+           close(fd_pipe[0]); /* We don't wan't to read from the parent */
+           transformer(src_fd, fd_pipe[1]);
+           close(fd_pipe[1]); /* Send EOF */
+               close(src_fd);
+           exit(0);
+           /* notreached */
+       }
+
+       /* parent process */
+       close(fd_pipe[1]); /* Don't want to write to the child */
+
+       return(fd_pipe[0]);
+}
diff --git a/busybox/archival/libunarchive/seek_by_char.c b/busybox/archival/libunarchive/seek_by_char.c
new file mode 100644 (file)
index 0000000..a50d566
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+
+#include "unarchive.h"
+#include "busybox.h"
+
+/*     If we are reading through a pipe(), or from stdin then we cant lseek,
+ *  we must read and discard the data to skip over it.
+ *
+ *  TODO: rename to seek_by_read
+ */
+extern void seek_by_char(const archive_handle_t *archive_handle, const unsigned int jump_size)
+{
+       if (jump_size) {
+               bb_copyfd_size(archive_handle->src_fd, -1, jump_size);
+       }
+}
diff --git a/busybox/archival/libunarchive/seek_by_jump.c b/busybox/archival/libunarchive/seek_by_jump.c
new file mode 100644 (file)
index 0000000..578870d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount)
+{
+       if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) {
+#ifdef CONFIG_FEATURE_UNARCHIVE_TAPE
+               if (errno == ESPIPE) {
+                       seek_by_char(archive_handle, amount);
+               } else
+#endif
+                       bb_perror_msg_and_die("Seek failure");
+       }
+}
diff --git a/busybox/archival/libunarchive/unpack_ar_archive.c b/busybox/archival/libunarchive/unpack_ar_archive.c
new file mode 100644 (file)
index 0000000..e8f113b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+extern void unpack_ar_archive(archive_handle_t *ar_archive)
+{
+       char magic[7];
+
+       archive_xread_all(ar_archive, magic, 7);
+       if (strncmp(magic, "!<arch>", 7) != 0) {
+               bb_error_msg_and_die("Invalid ar magic");
+       }
+       ar_archive->offset += 7;
+
+       while (get_header_ar(ar_archive) == EXIT_SUCCESS);
+}
diff --git a/busybox/archival/rpm.c b/busybox/archival/rpm.c
new file mode 100644 (file)
index 0000000..30cdc93
--- /dev/null
@@ -0,0 +1,349 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rpm applet for busybox
+ *
+ * Copyright (C) 2001,2002 by Laurence Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <netinet/in.h> /* For ntohl & htonl function */
+#include <string.h> /* For strncmp */
+#include <sys/mman.h> /* For mmap */
+#include <time.h> /* For ctime */
+
+#include "busybox.h"
+#include "unarchive.h"
+
+#define RPM_HEADER_MAGIC "\216\255\350"
+#define RPM_CHAR_TYPE          1
+#define RPM_INT8_TYPE          2
+#define RPM_INT16_TYPE         3
+#define RPM_INT32_TYPE         4
+/* #define RPM_INT64_TYPE      5   ---- These aren't supported (yet) */
+#define RPM_STRING_TYPE                6
+#define RPM_BIN_TYPE           7
+#define RPM_STRING_ARRAY_TYPE  8
+#define RPM_I18NSTRING_TYPE    9
+
+#define        RPMTAG_NAME                     1000
+#define        RPMTAG_VERSION                  1001
+#define        RPMTAG_RELEASE                  1002
+#define        RPMTAG_SUMMARY                  1004
+#define        RPMTAG_DESCRIPTION              1005
+#define        RPMTAG_BUILDTIME                1006
+#define        RPMTAG_BUILDHOST                1007
+#define        RPMTAG_SIZE                     1009
+#define        RPMTAG_VENDOR                   1011
+#define        RPMTAG_LICENSE                  1014
+#define        RPMTAG_PACKAGER                 1015
+#define        RPMTAG_GROUP                    1016
+#define RPMTAG_URL                     1020
+#define        RPMTAG_PREIN                    1023
+#define        RPMTAG_POSTIN                   1024
+#define        RPMTAG_FILEFLAGS                1037
+#define        RPMTAG_FILEUSERNAME             1039
+#define        RPMTAG_FILEGROUPNAME            1040
+#define        RPMTAG_SOURCERPM                1044
+#define        RPMTAG_PREINPROG                1085
+#define        RPMTAG_POSTINPROG               1086
+#define        RPMTAG_PREFIXS                  1098
+#define        RPMTAG_DIRINDEXES               1116
+#define        RPMTAG_BASENAMES                1117
+#define        RPMTAG_DIRNAMES                 1118
+#define        RPMFILE_CONFIG                  (1 << 0)
+#define        RPMFILE_DOC                     (1 << 1)
+
+enum rpm_functions_e {
+       rpm_query = 1,
+       rpm_install = 2,
+       rpm_query_info = 4,
+       rpm_query_package = 8,
+       rpm_query_list = 16,
+       rpm_query_list_doc = 32,
+       rpm_query_list_config = 64
+};
+
+typedef struct {
+       uint32_t tag; /* 4 byte tag */
+       uint32_t type; /* 4 byte type */
+       uint32_t offset; /* 4 byte offset */
+       uint32_t count; /* 4 byte count */
+} rpm_index;
+
+static void *map;
+static rpm_index **mytags;
+static int tagcount;
+
+void extract_cpio_gz(int fd);
+rpm_index **rpm_gettags(int fd, int *num_tags);
+int bsearch_rpmtag(const void *key, const void *item);
+char *rpm_getstring(int tag, int itemindex);
+int rpm_getint(int tag, int itemindex);
+int rpm_getcount(int tag);
+void exec_script(int progtag, int datatag, char *prefix);
+void fileaction_dobackup(char *filename, int fileref);
+void fileaction_setowngrp(char *filename, int fileref);
+void fileaction_list(char *filename, int itemno);
+void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
+
+int rpm_main(int argc, char **argv)
+{
+       int opt = 0, func = 0, rpm_fd, offset;
+
+       while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
+               switch (opt) {
+               case 'i': // First arg: Install mode, with q: Information
+                       if (!func) func |= rpm_install;
+                       else func |= rpm_query_info;
+                       break;
+               case 'q': // First arg: Query mode
+                       if (!func) func |= rpm_query;
+                       else bb_show_usage();
+                       break;
+               case 'p': // Query a package
+                       func |= rpm_query_package;
+                       break;
+               case 'l': // List files in a package
+                       func |= rpm_query_list;
+                       break;
+               case 'd': // List doc files in a package (implies list)
+                       func |= rpm_query_list;
+                       func |= rpm_query_list_doc;
+                       break;
+               case 'c': // List config files in a package (implies list)
+                       func |= rpm_query_list;
+                       func |= rpm_query_list_config;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       if (optind == argc) bb_show_usage();
+       while (optind < argc) {
+               rpm_fd = bb_xopen(argv[optind], O_RDONLY);
+               mytags = rpm_gettags(rpm_fd, (int *) &tagcount);
+               offset = lseek(rpm_fd, 0, SEEK_CUR);
+               if (!mytags) { printf("Error reading rpm header\n"); exit(-1); }
+               map = mmap(0, offset > getpagesize() ? (offset + offset % getpagesize()) : getpagesize(), PROT_READ, MAP_SHARED, rpm_fd, 0); // Mimimum is one page
+               if (func & rpm_install) {
+                       loop_through_files(RPMTAG_BASENAMES, fileaction_dobackup); /* Backup any config files */
+                       extract_cpio_gz(rpm_fd); // Extact the archive
+                       loop_through_files(RPMTAG_BASENAMES, fileaction_setowngrp); /* Set the correct file uid/gid's */
+               } else if (func & rpm_query && func & rpm_query_package) {
+                       if (!((func & rpm_query_info) || (func & rpm_query_list))) { // If just a straight query, just give package name
+                               printf("%s-%s-%s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_RELEASE, 0));
+                       }
+                       if (func & rpm_query_info) {
+                               /* Do the nice printout */
+                               time_t bdate_time;
+                               struct tm *bdate;
+                               char bdatestring[50];
+                               printf("Name        : %-29sRelocations: %s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_PREFIXS, 0) ? rpm_getstring(RPMTAG_PREFIXS, 0) : "(not relocateable)");
+                               printf("Version     : %-34sVendor: %s\n", rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_VENDOR, 0) ? rpm_getstring(RPMTAG_VENDOR, 0) : "(none)");
+                               bdate_time = rpm_getint(RPMTAG_BUILDTIME, 0);
+                               bdate = localtime((time_t *) &bdate_time);
+                               strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate);
+                               printf("Release     : %-30sBuild Date: %s\n", rpm_getstring(RPMTAG_RELEASE, 0), bdatestring);
+                               printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstring(RPMTAG_BUILDHOST, 0));
+                               printf("Group       : %-30sSource RPM: %s\n", rpm_getstring(RPMTAG_GROUP, 0), rpm_getstring(RPMTAG_SOURCERPM, 0));
+                               printf("Size        : %-33dLicense: %s\n", rpm_getint(RPMTAG_SIZE, 0), rpm_getstring(RPMTAG_LICENSE, 0));
+                               printf("URL         : %s\n", rpm_getstring(RPMTAG_URL, 0));
+                               printf("Summary     : %s\n", rpm_getstring(RPMTAG_SUMMARY, 0));
+                               printf("Description :\n%s\n", rpm_getstring(RPMTAG_DESCRIPTION, 0));
+                       }
+                       if (func & rpm_query_list) {
+                               int count, it, flags;
+                               count = rpm_getcount(RPMTAG_BASENAMES);
+                               for (it = 0; it < count; it++) {
+                                       flags = rpm_getint(RPMTAG_FILEFLAGS, it);
+                                       switch ((func & rpm_query_list_doc) + (func & rpm_query_list_config))
+                                       {
+                                               case rpm_query_list_doc: if (!(flags & RPMFILE_DOC)) continue; break;
+                                               case rpm_query_list_config: if (!(flags & RPMFILE_CONFIG)) continue; break;
+                                               case rpm_query_list_doc + rpm_query_list_config: if (!((flags & RPMFILE_CONFIG) || (flags & RPMFILE_DOC))) continue; break;
+                                       }
+                                       printf("%s%s\n", rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, it)), rpm_getstring(RPMTAG_BASENAMES, it));
+                               }
+                       }
+               }
+               optind++;
+               free (mytags);
+       }
+       return 0;
+}
+
+void extract_cpio_gz(int fd) {
+       archive_handle_t *archive_handle;
+       unsigned char magic[2];
+
+       /* Initialise */
+       archive_handle = init_handle();
+       archive_handle->seek = seek_by_char;
+       //archive_handle->action_header = header_list;
+       archive_handle->action_data = data_extract_all;
+       archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
+       archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
+       archive_handle->src_fd = fd;
+       archive_handle->offset = 0;
+
+       bb_xread_all(archive_handle->src_fd, &magic, 2);
+       if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+               bb_error_msg_and_die("Invalid gzip magic");
+       }
+       check_header_gzip(archive_handle->src_fd);
+       chdir("/"); // Install RPM's to root
+
+       archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
+       archive_handle->offset = 0;
+       while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
+}
+
+
+rpm_index **rpm_gettags(int fd, int *num_tags)
+{
+       rpm_index **tags = calloc(200, sizeof(struct rpmtag *)); /* We should never need mode than 200, and realloc later */
+       int pass, tagindex = 0;
+       lseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
+
+       for (pass = 0; pass < 2; pass++) { /* 1st pass is the signature headers, 2nd is the main stuff */
+               struct {
+                       char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
+                       uint8_t version; /* 1 byte version number */
+                       uint32_t reserved; /* 4 bytes reserved */
+                       uint32_t entries; /* Number of entries in header (4 bytes) */
+                       uint32_t size; /* Size of store (4 bytes) */
+               } header;
+               rpm_index *tmpindex;
+               int storepos;
+
+               read(fd, &header, sizeof(header));
+               if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) return NULL; /* Invalid magic */
+               if (header.version != 1) return NULL; /* This program only supports v1 headers */
+               header.size = ntohl(header.size);
+               header.entries = ntohl(header.entries);
+               storepos = lseek(fd,0,SEEK_CUR) + header.entries * 16;
+
+               while (header.entries--) {
+                       tmpindex = tags[tagindex++] = malloc(sizeof(rpm_index));
+                       read(fd, tmpindex, sizeof(rpm_index));
+                       tmpindex->tag = ntohl(tmpindex->tag); tmpindex->type = ntohl(tmpindex->type); tmpindex->count = ntohl(tmpindex->count);
+                       tmpindex->offset = storepos + ntohl(tmpindex->offset);
+                       if (pass==0) tmpindex->tag -= 743;
+               }
+               lseek(fd, header.size, SEEK_CUR); /* Seek past store */
+               if (pass==0) lseek(fd, (8 - (lseek(fd,0,SEEK_CUR) % 8)) % 8, SEEK_CUR); /* Skip padding to 8 byte boundary after reading signature headers */
+       }
+       tags = realloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */
+       *num_tags = tagindex;
+       return tags; /* All done, leave the file at the start of the gzipped cpio archive */
+}
+
+int bsearch_rpmtag(const void *key, const void *item)
+{
+       rpm_index **tmp = (rpm_index **) item;
+       return ((int) key - tmp[0]->tag);
+}
+
+int rpm_getcount(int tag)
+{
+       rpm_index **found;
+       found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       if (!found) return 0;
+       else return found[0]->count;
+}
+
+char *rpm_getstring(int tag, int itemindex)
+{
+       rpm_index **found;
+       found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       if (!found || itemindex >= found[0]->count) return NULL;
+       if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) {
+               int n;
+               char *tmpstr = (char *) (map + found[0]->offset);
+               for (n=0; n < itemindex; n++) tmpstr = tmpstr + strlen(tmpstr) + 1;
+               return tmpstr;
+       } else return NULL;
+}
+
+int rpm_getint(int tag, int itemindex)
+{
+       rpm_index **found;
+       int n, *tmpint;
+       found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       if (!found || itemindex >= found[0]->count) return -1;
+       tmpint = (int *) (map + found[0]->offset);
+       if (found[0]->type == RPM_INT32_TYPE) {
+               for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 4);
+               return ntohl(*tmpint);
+       } else if (found[0]->type == RPM_INT16_TYPE) {
+               for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 2);
+               return ntohs(*tmpint);
+       } else if (found[0]->type == RPM_INT8_TYPE) {
+               for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 1);
+               return ntohs(*tmpint);
+       } else return -1;
+}
+
+void fileaction_dobackup(char *filename, int fileref)
+{
+       struct stat oldfile;
+       int stat_res;
+       char *newname;
+       if (rpm_getint(RPMTAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { /* Only need to backup config files */
+               stat_res = lstat (filename, &oldfile);
+               if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { /* File already exists  - really should check MD5's etc to see if different */
+                       newname = bb_xstrdup(filename);
+                       newname = strcat(newname, ".rpmorig");
+                       copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
+                       remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
+                       free(newname);
+               }
+       }
+}
+
+void fileaction_setowngrp(char *filename, int fileref)
+{
+       int uid, gid;
+       uid = my_getpwnam(rpm_getstring(RPMTAG_FILEUSERNAME, fileref));
+       gid = my_getgrnam(rpm_getstring(RPMTAG_FILEGROUPNAME, fileref));
+       chown (filename, uid, gid);
+}
+
+void fileaction_list(char *filename, int fileref)
+{
+       printf("%s\n", filename);
+}
+
+void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
+{
+       int count = 0;
+       char *filename, *tmp_dirname, *tmp_basename;
+       while (rpm_getstring(filetag, count)) {
+               tmp_dirname = rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, count)); /* 1st put on the directory */
+               tmp_basename = rpm_getstring(RPMTAG_BASENAMES, count);
+               filename = xmalloc(strlen(tmp_basename) + strlen(tmp_dirname) + 1);
+               strcpy(filename, tmp_dirname); /* First the directory name */
+               strcat(filename, tmp_basename); /* then the filename */
+               fileaction(filename, count++);
+               free(filename);
+       }
+}
diff --git a/busybox/archival/rpm2cpio.c b/busybox/archival/rpm2cpio.c
new file mode 100644 (file)
index 0000000..5314e53
--- /dev/null
@@ -0,0 +1,106 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rpm2cpio implementation for busybox
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <sys/types.h>
+#include <netinet/in.h> /* For ntohl & htonl function */
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include "busybox.h"
+#include "unarchive.h"
+
+#define RPM_MAGIC "\355\253\356\333"
+#define RPM_HEADER_MAGIC "\216\255\350"
+
+struct rpm_lead {
+    unsigned char magic[4];
+    uint8_t major, minor;
+    uint16_t type;
+    uint16_t archnum;
+    char name[66];
+    uint16_t osnum;
+    uint16_t signature_type;
+    char reserved[16];
+};
+
+struct rpm_header {
+       char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
+       uint8_t version; /* 1 byte version number */
+       uint32_t reserved; /* 4 bytes reserved */
+       uint32_t entries; /* Number of entries in header (4 bytes) */
+       uint32_t size; /* Size of store (4 bytes) */
+};
+
+void skip_header(int rpm_fd)
+{
+       struct rpm_header header;
+
+       bb_xread_all(rpm_fd, &header, sizeof(struct rpm_header));
+       if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) {
+               bb_error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */
+       }
+       if (header.version != 1) {
+               bb_error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */
+       }
+       header.entries = ntohl(header.entries);
+       header.size = ntohl(header.size);
+       lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
+       lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */
+}
+
+/* No getopt required */
+extern int rpm2cpio_main(int argc, char **argv)
+{
+       struct rpm_lead lead;
+       int rpm_fd;
+       unsigned char magic[2];
+
+       if (argc == 1) {
+               rpm_fd = STDIN_FILENO;
+       } else {
+               rpm_fd = bb_xopen(argv[1], O_RDONLY);
+       }
+
+       bb_xread_all(rpm_fd, &lead, sizeof(struct rpm_lead));
+       if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
+               bb_error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
+       }
+
+       /* Skip the signature header */
+       skip_header(rpm_fd);
+       lseek(rpm_fd, (8 - (lseek(rpm_fd, 0, SEEK_CUR) % 8)) % 8, SEEK_CUR);
+
+       /* Skip the main header */
+       skip_header(rpm_fd);
+
+       bb_xread_all(rpm_fd, &magic, 2);
+       if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+               bb_error_msg_and_die("Invalid gzip magic");
+       }
+
+       check_header_gzip(rpm_fd);
+       if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) {
+               bb_error_msg("Error inflating");
+       }
+
+       close(rpm_fd);
+
+       return 0;
+}
diff --git a/busybox/archival/tar.c b/busybox/archival/tar.c
new file mode 100644 (file)
index 0000000..950e21d
--- /dev/null
@@ -0,0 +1,891 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tar implementation for busybox
+ *
+ * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg
+ *  Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
+ * ground up.  It still has remnants of the old code lying about, but it is
+ * very different now (i.e., cleaner, less global variables, etc.)
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Based in part in the tar implementation in sash
+ *  Copyright (c) 1999 by David I. Bell
+ *  Permission is granted to use, distribute, or modify this source,
+ *  provided that this copyright notice remains intact.
+ *  Permission to distribute sash derived code under the GPL has been granted.
+ *
+ * Based in part on the tar implementation from busybox-0.28
+ *  Copyright (C) 1995 Bruce Perens
+ *  This is free software under the GNU General Public License.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <search.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>     /* major() and minor() */
+#include "unarchive.h"
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_TAR_CREATE
+
+/* Tar file constants  */
+# define TAR_MAGIC          "ustar"    /* ustar and a null */
+# define TAR_VERSION        "  "       /* Be compatable with GNU tar format */
+
+static const int TAR_BLOCK_SIZE = 512;
+static const int TAR_MAGIC_LEN = 6;
+static const int TAR_VERSION_LEN = 2;
+
+/* POSIX tar Header Block, from POSIX 1003.1-1990  */
+enum { NAME_SIZE = 100 };      /* because gcc won't let me use 'static const int' */
+struct TarHeader {             /* byte offset */
+       char name[NAME_SIZE];   /*   0-99 */
+       char mode[8];           /* 100-107 */
+       char uid[8];            /* 108-115 */
+       char gid[8];            /* 116-123 */
+       char size[12];          /* 124-135 */
+       char mtime[12];         /* 136-147 */
+       char chksum[8];         /* 148-155 */
+       char typeflag;          /* 156-156 */
+       char linkname[NAME_SIZE];       /* 157-256 */
+       char magic[6];          /* 257-262 */
+       char version[2];        /* 263-264 */
+       char uname[32];         /* 265-296 */
+       char gname[32];         /* 297-328 */
+       char devmajor[8];       /* 329-336 */
+       char devminor[8];       /* 337-344 */
+       char prefix[155];       /* 345-499 */
+       char padding[12];       /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
+};
+typedef struct TarHeader TarHeader;
+
+/*
+** writeTarFile(),  writeFileToTarball(), and writeTarHeader() are
+** the only functions that deal with the HardLinkInfo structure.
+** Even these functions use the xxxHardLinkInfo() functions.
+*/
+typedef struct HardLinkInfo HardLinkInfo;
+struct HardLinkInfo {
+       HardLinkInfo *next;     /* Next entry in list */
+       dev_t dev;                      /* Device number */
+       ino_t ino;                      /* Inode number */
+       short linkCount;        /* (Hard) Link Count */
+       char name[1];           /* Start of filename (must be last) */
+};
+
+/* Some info to be carried along when creating a new tarball */
+struct TarBallInfo {
+       char *fileName;         /* File name of the tarball */
+       int tarFd;                      /* Open-for-write file descriptor
+                                                  for the tarball */
+       struct stat statBuf;    /* Stat info for the tarball, letting
+                                                          us know the inode and device that the
+                                                          tarball lives, so we can avoid trying
+                                                          to include the tarball into itself */
+       int verboseFlag;        /* Whether to print extra stuff or not */
+       const llist_t *excludeList;     /* List of files to not include */
+       HardLinkInfo *hlInfoHead;       /* Hard Link Tracking Information */
+       HardLinkInfo *hlInfo;   /* Hard Link Info for the current file */
+};
+typedef struct TarBallInfo TarBallInfo;
+
+/* A nice enum with all the possible tar file content types */
+enum TarFileType {
+       REGTYPE = '0',          /* regular file */
+       REGTYPE0 = '\0',        /* regular file (ancient bug compat) */
+       LNKTYPE = '1',          /* hard link */
+       SYMTYPE = '2',          /* symbolic link */
+       CHRTYPE = '3',          /* character special */
+       BLKTYPE = '4',          /* block special */
+       DIRTYPE = '5',          /* directory */
+       FIFOTYPE = '6',         /* FIFO special */
+       CONTTYPE = '7',         /* reserved */
+       GNULONGLINK = 'K',      /* GNU long (>100 chars) link name */
+       GNULONGNAME = 'L',      /* GNU long (>100 chars) file name */
+};
+typedef enum TarFileType TarFileType;
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static inline void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
+                                       struct stat *statbuf,
+                                       const char *name)
+{
+       /* Note: hlInfoHeadPtr can never be NULL! */
+       HardLinkInfo *hlInfo;
+
+       hlInfo = (HardLinkInfo *) xmalloc(sizeof(HardLinkInfo) + strlen(name));
+       hlInfo->next = *hlInfoHeadPtr;
+       *hlInfoHeadPtr = hlInfo;
+       hlInfo->dev = statbuf->st_dev;
+       hlInfo->ino = statbuf->st_ino;
+       hlInfo->linkCount = statbuf->st_nlink;
+       strcpy(hlInfo->name, name);
+}
+
+static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
+{
+       HardLinkInfo *hlInfo = NULL;
+       HardLinkInfo *hlInfoNext = NULL;
+
+       if (hlInfoHeadPtr) {
+               hlInfo = *hlInfoHeadPtr;
+               while (hlInfo) {
+                       hlInfoNext = hlInfo->next;
+                       free(hlInfo);
+                       hlInfo = hlInfoNext;
+               }
+               *hlInfoHeadPtr = NULL;
+       }
+       return;
+}
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static inline HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
+{
+       while (hlInfo) {
+               if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
+                       break;
+               hlInfo = hlInfo->next;
+       }
+       return (hlInfo);
+}
+
+/* Put an octal string into the specified buffer.
+ * The number is zero and space padded and possibly null padded.
+ * Returns TRUE if successful.  */
+static int putOctal(char *cp, int len, long value)
+{
+       int tempLength;
+       char tempBuffer[32];
+       char *tempString = tempBuffer;
+
+       /* Create a string of the specified length with an initial space,
+        * leading zeroes and the octal number, and a trailing null.  */
+       sprintf(tempString, "%0*lo", len - 1, value);
+
+       /* If the string is too large, suppress the leading space.  */
+       tempLength = strlen(tempString) + 1;
+       if (tempLength > len) {
+               tempLength--;
+               tempString++;
+       }
+
+       /* If the string is still too large, suppress the trailing null.  */
+       if (tempLength > len)
+               tempLength--;
+
+       /* If the string is still too large, fail.  */
+       if (tempLength > len)
+               return FALSE;
+
+       /* Copy the string to the field.  */
+       memcpy(cp, tempString, len);
+
+       return TRUE;
+}
+
+/* Write out a tar header for the specified file/directory/whatever */
+static inline int writeTarHeader(struct TarBallInfo *tbInfo,
+                                                                const char *header_name,
+                                                                const char *real_name, struct stat *statbuf)
+{
+       long chksum = 0;
+       struct TarHeader header;
+       const unsigned char *cp = (const unsigned char *) &header;
+       ssize_t size = sizeof(struct TarHeader);
+
+       memset(&header, 0, size);
+
+       strncpy(header.name, header_name, sizeof(header.name));
+
+       putOctal(header.mode, sizeof(header.mode), statbuf->st_mode);
+       putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
+       putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
+       putOctal(header.size, sizeof(header.size), 0);  /* Regular file size is handled later */
+       putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
+       strncpy(header.magic, TAR_MAGIC TAR_VERSION,
+                       TAR_MAGIC_LEN + TAR_VERSION_LEN);
+
+       /* Enter the user and group names (default to root if it fails) */
+       if (my_getpwuid(header.uname, statbuf->st_uid, sizeof(header.uname)) == NULL)
+               strcpy(header.uname, "root");
+       if (my_getgrgid(header.gname, statbuf->st_gid, sizeof(header.gname)) == NULL)
+               strcpy(header.gname, "root");
+
+       if (tbInfo->hlInfo) {
+               /* This is a hard link */
+               header.typeflag = LNKTYPE;
+               strncpy(header.linkname, tbInfo->hlInfo->name,
+                               sizeof(header.linkname));
+       } else if (S_ISLNK(statbuf->st_mode)) {
+               char *lpath = xreadlink(real_name);
+
+               if (!lpath)             /* Already printed err msg inside xreadlink() */
+                       return (FALSE);
+               header.typeflag = SYMTYPE;
+               strncpy(header.linkname, lpath, sizeof(header.linkname));
+               free(lpath);
+       } else if (S_ISDIR(statbuf->st_mode)) {
+               header.typeflag = DIRTYPE;
+               strncat(header.name, "/", sizeof(header.name));
+       } else if (S_ISCHR(statbuf->st_mode)) {
+               header.typeflag = CHRTYPE;
+               putOctal(header.devmajor, sizeof(header.devmajor),
+                                major(statbuf->st_rdev));
+               putOctal(header.devminor, sizeof(header.devminor),
+                                minor(statbuf->st_rdev));
+       } else if (S_ISBLK(statbuf->st_mode)) {
+               header.typeflag = BLKTYPE;
+               putOctal(header.devmajor, sizeof(header.devmajor),
+                                major(statbuf->st_rdev));
+               putOctal(header.devminor, sizeof(header.devminor),
+                                minor(statbuf->st_rdev));
+       } else if (S_ISFIFO(statbuf->st_mode)) {
+               header.typeflag = FIFOTYPE;
+       } else if (S_ISREG(statbuf->st_mode)) {
+               header.typeflag = REGTYPE;
+               putOctal(header.size, sizeof(header.size), statbuf->st_size);
+       } else {
+               bb_error_msg("%s: Unknown file type", real_name);
+               return (FALSE);
+       }
+
+       /* Calculate and store the checksum (i.e., the sum of all of the bytes of
+        * the header).  The checksum field must be filled with blanks for the
+        * calculation.  The checksum field is formatted differently from the
+        * other fields: it has [6] digits, a null, then a space -- rather than
+        * digits, followed by a null like the other fields... */
+       memset(header.chksum, ' ', sizeof(header.chksum));
+       cp = (const unsigned char *) &header;
+       while (size-- > 0)
+               chksum += *cp++;
+       putOctal(header.chksum, 7, chksum);
+
+       /* Now write the header out to disk */
+       if ((size =
+                bb_full_write(tbInfo->tarFd, (char *) &header,
+                                       sizeof(struct TarHeader))) < 0) {
+               bb_error_msg(bb_msg_io_error, real_name);
+               return (FALSE);
+       }
+       /* Pad the header up to the tar block size */
+       for (; size < TAR_BLOCK_SIZE; size++) {
+               write(tbInfo->tarFd, "\0", 1);
+       }
+       /* Now do the verbose thing (or not) */
+
+       if (tbInfo->verboseFlag) {
+               FILE *vbFd = stdout;
+
+               if (tbInfo->tarFd == STDOUT_FILENO)     /* If the archive goes to stdout, verbose to stderr */
+                       vbFd = stderr;
+
+               fprintf(vbFd, "%s\n", header.name);
+       }
+
+       return (TRUE);
+}
+
+# ifdef CONFIG_FEATURE_TAR_FROM
+static inline int exclude_file(const llist_t *excluded_files, const char *file)
+{
+       while (excluded_files) {
+               if (excluded_files->data[0] == '/') {
+                       if (fnmatch(excluded_files->data, file,
+                                               FNM_PATHNAME | FNM_LEADING_DIR) == 0)
+                               return 1;
+               } else {
+                       const char *p;
+
+                       for (p = file; p[0] != '\0'; p++) {
+                               if ((p == file || p[-1] == '/') && p[0] != '/' &&
+                                       fnmatch(excluded_files->data, p,
+                                                       FNM_PATHNAME | FNM_LEADING_DIR) == 0)
+                                       return 1;
+                       }
+               }
+               excluded_files = excluded_files->link;
+       }
+
+       return 0;
+}
+# endif
+
+static int writeFileToTarball(const char *fileName, struct stat *statbuf,
+                                                         void *userData)
+{
+       struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
+       const char *header_name;
+
+       /*
+          ** Check to see if we are dealing with a hard link.
+          ** If so -
+          ** Treat the first occurance of a given dev/inode as a file while
+          ** treating any additional occurances as hard links.  This is done
+          ** by adding the file information to the HardLinkInfo linked list.
+        */
+       tbInfo->hlInfo = NULL;
+       if (statbuf->st_nlink > 1) {
+               tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
+               if (tbInfo->hlInfo == NULL)
+                       addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
+       }
+
+       /* It is against the rules to archive a socket */
+       if (S_ISSOCK(statbuf->st_mode)) {
+               bb_error_msg("%s: socket ignored", fileName);
+               return (TRUE);
+       }
+
+       /* It is a bad idea to store the archive we are in the process of creating,
+        * so check the device and inode to be sure that this particular file isn't
+        * the new tarball */
+       if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
+               tbInfo->statBuf.st_ino == statbuf->st_ino) {
+               bb_error_msg("%s: file is the archive; skipping", fileName);
+               return (TRUE);
+       }
+
+       header_name = fileName;
+       while (header_name[0] == '/') {
+               static int alreadyWarned = FALSE;
+
+               if (alreadyWarned == FALSE) {
+                       bb_error_msg("Removing leading '/' from member names");
+                       alreadyWarned = TRUE;
+               }
+               header_name++;
+       }
+
+       if (strlen(fileName) >= NAME_SIZE) {
+               bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE);
+               return (TRUE);
+       }
+
+       if (header_name[0] == '\0')
+               return TRUE;
+
+# ifdef CONFIG_FEATURE_TAR_FROM
+       if (exclude_file(tbInfo->excludeList, header_name)) {
+               return SKIP;
+       }
+# endif                                                        /* CONFIG_FEATURE_TAR_FROM */
+
+       if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
+               return (FALSE);
+       }
+
+       /* Now, if the file is a regular file, copy it out to the tarball */
+       if ((tbInfo->hlInfo == NULL)
+               && (S_ISREG(statbuf->st_mode))) {
+               int inputFileFd;
+               ssize_t readSize = 0;
+
+               /* open the file we want to archive, and make sure all is well */
+               if ((inputFileFd = open(fileName, O_RDONLY)) < 0) {
+                       bb_perror_msg("%s: Cannot open", fileName);
+                       return (FALSE);
+               }
+
+               /* write the file to the archive */
+               readSize = bb_copyfd_eof(inputFileFd, tbInfo->tarFd);
+
+               /* Pad the file up to the tar block size */
+               for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++) {
+                       write(tbInfo->tarFd, "\0", 1);
+               }
+               close(inputFileFd);
+       }
+
+       return (TRUE);
+}
+
+static inline int writeTarFile(const int tar_fd, const int verboseFlag,
+       const unsigned long dereferenceFlag, const llist_t *include,
+       const llist_t *exclude, const int gzip)
+{
+#ifdef CONFIG_FEATURE_TAR_GZIP
+       int gzipDataPipe[2] = { -1, -1 };
+       int gzipStatusPipe[2] = { -1, -1 };
+       pid_t gzipPid = 0;
+       volatile int vfork_exec_errno = 0;
+#endif
+
+       int errorFlag = FALSE;
+       ssize_t size;
+       struct TarBallInfo tbInfo;
+
+       tbInfo.hlInfoHead = NULL;
+
+       fchmod(tar_fd, 0644);
+       tbInfo.tarFd = tar_fd;
+       tbInfo.verboseFlag = verboseFlag;
+
+       /* Store the stat info for the tarball's file, so
+        * can avoid including the tarball into itself....  */
+       if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
+               bb_perror_msg_and_die("Couldnt stat tar file");
+
+#ifdef CONFIG_FEATURE_TAR_GZIP
+       if (gzip) {
+               if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0) {
+                       bb_perror_msg_and_die("Failed to create gzip pipe");
+               }
+
+               signal(SIGPIPE, SIG_IGN);       /* we only want EPIPE on errors */
+
+# if __GNUC__
+                       /* Avoid vfork clobbering */
+                       (void) &include;
+                       (void) &errorFlag;
+# endif
+
+               gzipPid = vfork();
+
+               if (gzipPid == 0) {
+                       dup2(gzipDataPipe[0], 0);
+                       close(gzipDataPipe[1]);
+
+                       if (tbInfo.tarFd != 1)
+                               dup2(tbInfo.tarFd, 1);
+
+                       close(gzipStatusPipe[0]);
+                       fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC);  /* close on exec shows sucess */
+
+                       execl("/bin/gzip", "gzip", "-f", 0);
+                       vfork_exec_errno = errno;
+
+                       close(gzipStatusPipe[1]);
+                       exit(-1);
+               } else if (gzipPid > 0) {
+                       close(gzipDataPipe[0]);
+                       close(gzipStatusPipe[1]);
+
+                       while (1) {
+                               char buf;
+
+                               int n = bb_full_read(gzipStatusPipe[0], &buf, 1);
+
+                               if (n == 0 && vfork_exec_errno != 0) {
+                                       errno = vfork_exec_errno;
+                                       bb_perror_msg_and_die("Could not exec gzip process");
+                               } else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
+                                       continue;       /* try it again */
+                               break;
+                       }
+                       close(gzipStatusPipe[0]);
+
+                       tbInfo.tarFd = gzipDataPipe[1];
+               } else {
+                       bb_perror_msg_and_die("Failed to vfork gzip process");
+               }
+       }
+#endif
+
+       tbInfo.excludeList = exclude;
+
+       /* Read the directory/files and iterate over them one at a time */
+       while (include) {
+               if (!recursive_action(include->data, TRUE, dereferenceFlag, FALSE,
+                                                         writeFileToTarball, writeFileToTarball,
+                                                         (void *) &tbInfo)) {
+                       errorFlag = TRUE;
+               }
+               include = include->link;
+       }
+       /* Write two empty blocks to the end of the archive */
+       for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) {
+               write(tbInfo.tarFd, "\0", 1);
+       }
+
+       /* To be pedantically correct, we would check if the tarball
+        * is smaller than 20 tar blocks, and pad it if it was smaller,
+        * but that isn't necessary for GNU tar interoperability, and
+        * so is considered a waste of space */
+
+       /* Hang up the tools, close up shop, head home */
+       close(tbInfo.tarFd);
+       if (errorFlag)
+               bb_error_msg("Error exit delayed from previous errors");
+
+       freeHardLinkInfo(&tbInfo.hlInfoHead);
+
+#ifdef CONFIG_FEATURE_TAR_GZIP
+       if (gzip && gzipPid) {
+               if (waitpid(gzipPid, NULL, 0) == -1)
+                       printf("Couldnt wait ?");
+       }
+#endif
+
+       return !errorFlag;
+}
+#endif                                                 /* tar_create */
+
+#ifdef CONFIG_FEATURE_TAR_FROM
+static llist_t *append_file_list_to_list(llist_t *list)
+{
+       FILE *src_stream;
+       llist_t *cur = list;
+       llist_t *tmp;
+       char *line;
+       llist_t *newlist = NULL;
+
+       while(cur) {
+               src_stream = bb_xfopen(cur->data, "r");
+               tmp = cur;
+               cur = cur->link;
+               free(tmp);
+       while((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
+                       newlist = llist_add_to(newlist, line);
+       }
+       fclose(src_stream);
+       }
+       return newlist;
+}
+#endif
+
+#ifdef CONFIG_FEATURE_TAR_COMPRESS
+static char get_header_tar_Z(archive_handle_t *archive_handle)
+{
+       /* Cant lseek over pipe's */
+       archive_handle->seek = seek_by_char;
+
+       /* do the decompression, and cleanup */
+       if ((bb_xread_char(archive_handle->src_fd) != 0x1f) || (bb_xread_char(archive_handle->src_fd) != 0x9d)) {
+               bb_error_msg_and_die("Invalid magic");
+       }
+
+       archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress);
+       archive_handle->offset = 0;
+       while (get_header_tar(archive_handle) == EXIT_SUCCESS);
+
+       /* Can only do one file at a time */
+       return(EXIT_FAILURE);
+}
+#endif
+
+#define CTX_TEST       (1 << 0)
+#define CTX_EXTRACT    (1 << 1)
+#define TAR_OPT_BASEDIR  (1 << 2)
+#define TAR_OPT_TARNAME  (1 << 3)
+#define TAR_OPT_2STDOUT  (1 << 4)
+#define TAR_OPT_P        (1 << 5)
+#define TAR_OPT_VERBOSE  (1 << 6)
+#define TAR_OPT_KEEP_OLD       (1 << 7)
+
+#ifdef CONFIG_FEATURE_TAR_CREATE
+# define CTX_CREATE    (1 << 8)
+# define TAR_OPT_DEREFERNCE (1 << 9)
+# define TAR_OPT_STR_CREATE    "ch"
+# define TAR_OPT_FLAG_CREATE   2
+#else
+//# define CTX_CREATE  0
+# define TAR_OPT_STR_CREATE    ""
+# define TAR_OPT_FLAG_CREATE   0
+#endif
+
+#ifdef CONFIG_FEATURE_TAR_BZIP2
+# define TAR_OPT_BZIP2 (1 << (8 + TAR_OPT_FLAG_CREATE))
+# define TAR_OPT_STR_BZIP2     "j"
+# define TAR_OPT_FLAG_BZIP2    1
+#else
+# define TAR_OPT_STR_BZIP2     ""
+# define TAR_OPT_FLAG_BZIP2    0
+#endif
+
+#ifdef CONFIG_FEATURE_TAR_FROM
+# define TAR_OPT_INCLUDE_FROM  (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2))
+# define TAR_OPT_EXCLUDE_FROM  (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + 1))
+# define TAR_OPT_STR_FROM      "T:X:"
+# define TAR_OPT_FLAG_FROM     2
+#else
+# define TAR_OPT_STR_FROM      ""
+# define TAR_OPT_FLAG_FROM     0
+#endif
+
+#ifdef CONFIG_FEATURE_TAR_GZIP
+# define TAR_OPT_GZIP  (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + TAR_OPT_FLAG_FROM))
+# define TAR_OPT_STR_GZIP      "z"
+# define TAR_OPT_FLAG_GZIP     1
+#else
+# define TAR_OPT_STR_GZIP      ""
+# define TAR_OPT_FLAG_GZIP     0
+#endif
+
+#ifdef CONFIG_FEATURE_TAR_COMPRESS
+# define TAR_OPT_UNCOMPRESS    (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + TAR_OPT_FLAG_FROM + TAR_OPT_FLAG_GZIP))
+# define TAR_OPT_STR_COMPRESS  "Z"
+#else
+# define TAR_OPT_STR_COMPRESS  ""
+#endif
+
+static const char tar_options[]="txC:f:Opvk" \
+       TAR_OPT_STR_CREATE \
+       TAR_OPT_STR_BZIP2 \
+       TAR_OPT_STR_FROM \
+       TAR_OPT_STR_GZIP \
+       TAR_OPT_STR_COMPRESS;
+
+#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS
+static const struct option tar_long_options[] = {
+       { "list",                       0,      NULL,   't' },
+       { "extract",            0,      NULL,   'x' },
+       { "directory",          1,      NULL,   'C' },
+       { "file",                       1,      NULL,   'f'     },
+       { "to-stdout",          0,      NULL,   'O' },
+       { "same-permissions",   0,      NULL,   'p' },
+       { "verbose",            0,      NULL,   'v' },
+       { "keep-old",           0,      NULL,   'k' },
+# ifdef CONFIG_FEATURE_TAR_CREATE
+       { "create",                     0,      NULL,   'c' },
+       { "dereference",        0,      NULL,   'h' },
+# endif
+# ifdef CONFIG_FEATURE_TAR_BZIP2
+       { "bzip2",                      0,      NULL,   'j' },
+# endif
+# ifdef CONFIG_FEATURE_TAR_FROM
+       { "files-from",         1,      NULL,   'T' },
+       { "exclude-from",       1,      NULL,   'X' },
+# endif
+# ifdef CONFIG_FEATURE_TAR_GZIP
+       { "gzip",                       0,      NULL,   'z' },
+# endif
+# ifdef CONFIG_FEATURE_TAR_COMPRESS
+       { "compress",           0,      NULL,   'Z' },
+# endif
+       { 0,                 0, 0, 0 }
+};
+#endif
+
+int tar_main(int argc, char **argv)
+{
+       char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
+       archive_handle_t *tar_handle;
+       char *base_dir = NULL;
+       const char *tar_filename = "-";
+       unsigned long opt;
+       unsigned long ctx_flag = 0;
+
+       if (argc < 2) {
+               bb_show_usage();
+       }
+
+       /* Prepend '-' to the first argument if required */
+       if (argv[1][0] != '-') {
+               char *tmp;
+
+               bb_xasprintf(&tmp, "-%s", argv[1]);
+               argv[1] = tmp;
+       }
+
+       /* Initialise default values */
+       tar_handle = init_handle();
+       tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL;
+
+       bb_opt_complementaly = "c~tx:t~cx:x~ct:X*:T*";
+#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS
+       bb_applet_long_options = tar_long_options;
+#endif
+
+       opt = bb_getopt_ulflags(argc, argv, tar_options,
+                               &base_dir,      /* Change to dir <optarg> */
+                               &tar_filename /* archive filename */
+#ifdef CONFIG_FEATURE_TAR_FROM
+                               , &(tar_handle->accept),
+                               &(tar_handle->reject)
+#endif
+                               );
+
+       /* Check one and only one context option was given */
+       if(opt & 0x80000000UL) {
+               bb_show_usage();
+       }
+#ifdef CONFIG_FEATURE_TAR_CREATE
+       ctx_flag = opt & (CTX_CREATE | CTX_TEST | CTX_EXTRACT);
+#else
+       ctx_flag = opt & (CTX_TEST | CTX_EXTRACT);
+#endif
+       if (ctx_flag == 0) {
+               bb_show_usage();
+       }
+       if(ctx_flag & CTX_TEST) {
+               if ((tar_handle->action_header == header_list) ||
+                       (tar_handle->action_header == header_verbose_list)) {
+                       tar_handle->action_header = header_verbose_list;
+               } else {
+                       tar_handle->action_header = header_list;
+               }
+       }
+       if(ctx_flag & CTX_EXTRACT) {
+               if (tar_handle->action_data != data_extract_to_stdout)
+                       tar_handle->action_data = data_extract_all;
+               }
+       if(opt & TAR_OPT_2STDOUT) {
+               /* To stdout */
+               tar_handle->action_data = data_extract_to_stdout;
+       }
+       if(opt & TAR_OPT_VERBOSE) {
+               if ((tar_handle->action_header == header_list) ||
+                       (tar_handle->action_header == header_verbose_list))
+               {
+               tar_handle->action_header = header_verbose_list;
+               } else {
+                       tar_handle->action_header = header_list;
+               }
+       }
+       if (opt & TAR_OPT_KEEP_OLD) {
+               tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
+       }
+
+#ifdef CONFIG_FEATURE_TAR_GZIP
+       if(opt & TAR_OPT_GZIP) {
+               get_header_ptr = get_header_tar_gz;
+       }
+#endif
+#ifdef CONFIG_FEATURE_TAR_BZIP2
+       if(opt & TAR_OPT_BZIP2) {
+               get_header_ptr = get_header_tar_bz2;
+       }
+#endif
+#ifdef CONFIG_FEATURE_TAR_COMPRESS
+       if(opt & TAR_OPT_UNCOMPRESS) {
+               get_header_ptr = get_header_tar_Z;
+       }
+#endif
+#ifdef CONFIG_FEATURE_TAR_FROM
+       if(opt & TAR_OPT_EXCLUDE_FROM) {
+               tar_handle->reject = append_file_list_to_list(tar_handle->reject);
+       }
+       if(opt & TAR_OPT_INCLUDE_FROM) {
+               tar_handle->accept = append_file_list_to_list(tar_handle->accept);
+       }
+#endif
+
+       /* Check if we are reading from stdin */
+       if ((argv[optind]) && (*argv[optind] == '-')) {
+               /* Default is to read from stdin, so just skip to next arg */
+               optind++;
+       }
+
+       /* Setup an array of filenames to work with */
+       /* TODO: This is the same as in ar, separate function ? */
+       while (optind < argc) {
+               char *filename_ptr = last_char_is(argv[optind], '/');
+               if (filename_ptr) {
+                       *filename_ptr = '\0';
+               }
+               tar_handle->accept = llist_add_to(tar_handle->accept, argv[optind]);
+               optind++;
+       }
+
+       if ((tar_handle->accept) || (tar_handle->reject)) {
+               tar_handle->filter = filter_accept_reject_list;
+       }
+
+       /* Open the tar file */
+       {
+               FILE *tar_stream;
+               int flags;
+
+#ifdef CONFIG_FEATURE_TAR_CREATE
+               if (opt & CTX_CREATE) {
+                       /* Make sure there is at least one file to tar up.  */
+                       if (tar_handle->accept == NULL) {
+                               bb_error_msg_and_die("Cowardly refusing to create an empty archive");
+                       }
+                       tar_stream = stdout;
+                       flags = O_WRONLY | O_CREAT | O_EXCL;
+                       unlink(tar_filename);
+               } else
+#endif
+               {
+                       tar_stream = stdin;
+                       flags = O_RDONLY;
+               }
+
+               if ((tar_filename[0] == '-') && (tar_filename[1] == '\0')) {
+                       tar_handle->src_fd = fileno(tar_stream);
+                       tar_handle->seek = seek_by_char;
+               } else {
+                       tar_handle->src_fd = bb_xopen(tar_filename, flags);
+               }
+       }
+
+       if ((base_dir) && (chdir(base_dir))) {
+               bb_perror_msg_and_die("Couldnt chdir to %s", base_dir);
+       }
+
+#ifdef CONFIG_FEATURE_TAR_CREATE
+       /* create an archive */
+       if (opt & CTX_CREATE) {
+               int verboseFlag = FALSE;
+               int gzipFlag = FALSE;
+
+# ifdef CONFIG_FEATURE_TAR_GZIP
+               if (get_header_ptr == get_header_tar_gz) {
+                       gzipFlag = TRUE;
+               }
+# endif /* CONFIG_FEATURE_TAR_GZIP */
+# ifdef CONFIG_FEATURE_TAR_BZIP2
+               if (get_header_ptr == get_header_tar_bz2) {
+                       bb_error_msg_and_die("Creating bzip2 compressed archives is not currently supported.");
+               }
+# endif /* CONFIG_FEATURE_TAR_BZIP2 */
+
+               if ((tar_handle->action_header == header_list) ||
+                               (tar_handle->action_header == header_verbose_list)) {
+                       verboseFlag = TRUE;
+               }
+               writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERNCE, tar_handle->accept,
+                       tar_handle->reject, gzipFlag);
+       } else
+#endif /* CONFIG_FEATURE_TAR_CREATE */
+       {
+               while (get_header_ptr(tar_handle) == EXIT_SUCCESS);
+
+               /* Ckeck that every file that should have been extracted was */
+               while (tar_handle->accept) {
+                       if (find_list_entry(tar_handle->reject, tar_handle->accept->data) == NULL) {
+                               if (find_list_entry(tar_handle->passed, tar_handle->accept->data) == NULL) {
+                                       bb_error_msg_and_die("%s: Not found in archive\n", tar_handle->accept->data);
+                               }
+                       }
+                       tar_handle->accept = tar_handle->accept->link;
+               }
+       }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       if (tar_handle->src_fd != STDIN_FILENO) {
+               close(tar_handle->src_fd);
+       }
+#endif /* CONFIG_FEATURE_CLEAN_UP */
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/archival/uncompress.c b/busybox/archival/uncompress.c
new file mode 100644 (file)
index 0000000..48b4e2c
--- /dev/null
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *     Uncompress applet for busybox (c) 2002 Glenn McGrath
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+#define GUNZIP_TO_STDOUT       1
+#define GUNZIP_FORCE   2
+
+extern int uncompress_main(int argc, char **argv)
+{
+       int status = EXIT_SUCCESS;
+       unsigned long flags;
+
+       flags = bb_getopt_ulflags(argc, argv, "cf");
+
+       while (optind < argc) {
+               const char *compressed_file = argv[optind++];
+               const char *delete_path = NULL;
+               char *uncompressed_file = NULL;
+               int src_fd;
+               int dst_fd;
+
+               if (strcmp(compressed_file, "-") == 0) {
+                       src_fd = STDIN_FILENO;
+                       flags |= GUNZIP_TO_STDOUT;
+               } else {
+                       src_fd = bb_xopen(compressed_file, O_RDONLY);
+               }
+
+               /* Check that the input is sane.  */
+               if (isatty(src_fd) && ((flags & GUNZIP_FORCE) == 0)) {
+                       bb_error_msg_and_die
+                               ("compressed data not read from terminal.  Use -f to force it.");
+               }
+
+               /* Set output filename and number */
+               if (flags & GUNZIP_TO_STDOUT) {
+                       dst_fd = STDOUT_FILENO;
+               } else {
+                       struct stat stat_buf;
+                       char *extension;
+
+                       uncompressed_file = bb_xstrdup(compressed_file);
+
+                       extension = strrchr(uncompressed_file, '.');
+                       if (!extension || (strcmp(extension, ".Z") != 0)) {
+                               bb_error_msg_and_die("Invalid extension");
+                       }
+                       *extension = '\0';
+
+                       /* Open output file */
+                       dst_fd = bb_xopen(uncompressed_file, O_WRONLY | O_CREAT);
+
+                       /* Set permissions on the file */
+                       stat(compressed_file, &stat_buf);
+                       chmod(uncompressed_file, stat_buf.st_mode);
+
+                       /* If unzip succeeds remove the old file */
+                       delete_path = compressed_file;
+               }
+
+               /* do the decompression, and cleanup */
+               if ((bb_xread_char(src_fd) != 0x1f) || (bb_xread_char(src_fd) != 0x9d)) {
+                       bb_error_msg_and_die("Invalid magic");
+               }
+
+               status = uncompress(src_fd, dst_fd);
+
+               if ((status != EXIT_SUCCESS) && (uncompressed_file)) {
+                       /* Unzip failed, remove the uncomressed file instead of compressed file */
+                       delete_path = uncompressed_file;
+               }
+
+               if (dst_fd != STDOUT_FILENO) {
+                       close(dst_fd);
+               }
+               if (src_fd != STDIN_FILENO) {
+                       close(src_fd);
+               }
+
+               /* delete_path will be NULL if in test mode or from stdin */
+               if (delete_path && (unlink(delete_path) == -1)) {
+                       bb_error_msg_and_die("Couldn't remove %s", delete_path);
+               }
+
+               free(uncompressed_file);
+       }
+
+       return status;
+}
diff --git a/busybox/archival/unzip.c b/busybox/archival/unzip.c
new file mode 100644 (file)
index 0000000..eea2f54
--- /dev/null
@@ -0,0 +1,247 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini unzip implementation for busybox
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* For reference see
+ * http://www.pkware.com/products/enterprise/white_papers/appnote.txt
+ * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
+ */
+
+/* TODO Endian issues, exclude, should we accept input from stdin ? */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+#define ZIP_FILEHEADER_MAGIC   0x04034b50
+#define ZIP_CDS_MAGIC                  0x02014b50
+#define ZIP_CDS_END_MAGIC              0x06054b50
+#define ZIP_DD_MAGIC                   0x08074b50
+
+extern unsigned int gunzip_crc;
+extern unsigned int gunzip_bytes_out;
+
+static void header_list_unzip(const file_header_t *file_header)
+{
+       printf("  inflating: %s\n", file_header->name);
+}
+
+static void header_verbose_list_unzip(const file_header_t *file_header)
+{
+       unsigned int dostime = (unsigned int) file_header->mtime;
+
+       /* can printf arguments cut of the decade component ? */
+       unsigned short year = 1980 + ((dostime & 0xfe000000) >> 25);
+       while (year >= 100) {
+               year -= 100;
+       }
+
+       printf("%9u  %02u-%02u-%02u %02u:%02u   %s\n",
+               (unsigned int) file_header->size,
+               (dostime & 0x01e00000) >> 21,
+               (dostime & 0x001f0000) >> 16,
+               year,
+               (dostime & 0x0000f800) >> 11,
+               (dostime & 0x000007e0) >> 5,
+               file_header->name);
+}
+
+extern int unzip_main(int argc, char **argv)
+{
+       union {
+               unsigned char raw[26];
+               struct {
+                       unsigned short version; /* 0-1 */
+                       unsigned short flags;   /* 2-3 */
+                       unsigned short method;  /* 4-5 */
+                       unsigned short modtime; /* 6-7 */
+                       unsigned short moddate; /* 8-9 */
+                       unsigned int crc32 __attribute__ ((packed));            /* 10-13 */
+                       unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */
+                       unsigned int ucmpsize __attribute__ ((packed));;        /* 18-21 */
+                       unsigned short filename_len;            /* 22-23 */
+                       unsigned short extra_len;               /* 24-25 */
+               } formated __attribute__ ((packed));
+       } zip_header;
+
+       archive_handle_t *archive_handle;
+       unsigned int total_size = 0;
+       unsigned int total_entries = 0;
+       char *base_dir = NULL;
+       int opt = 0;
+
+       /* Initialise */
+       archive_handle = init_handle();
+       archive_handle->action_data = NULL;
+       archive_handle->action_header = header_list_unzip;
+
+       while ((opt = getopt(argc, argv, "lnopqd:")) != -1) {
+               switch (opt) {
+                       case 'l':       /* list */
+                               archive_handle->action_header = header_verbose_list_unzip;
+                               archive_handle->action_data = data_skip;
+                               break;
+                       case 'n':       /* never overwright existing files */
+                               break;
+                       case 'o':
+                               archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL;
+                               break;
+                       case 'p':       /* extract files to stdout */
+                               archive_handle->action_data = data_extract_to_stdout;
+                               break;
+                       case 'q':       /* Extract files quietly */
+                               archive_handle->action_header = header_skip;
+                               break;
+                       case 'd':       /* Extract files to specified base directory*/
+                               base_dir = optarg;
+                               break;
+#if 0
+                       case 'x':       /* Exclude the specified files */
+                               archive_handle->filter = filter_accept_reject_list;
+                               break;
+#endif
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       if (argc == optind) {
+               bb_show_usage();
+       }
+
+       printf("Archive:  %s\n", argv[optind]);
+       if (archive_handle->action_header == header_verbose_list_unzip) {
+               printf("  Length     Date   Time    Name\n");
+               printf(" --------    ----   ----    ----\n");
+       }
+
+       if (*argv[optind] == '-') {
+               archive_handle->src_fd = STDIN_FILENO;
+               archive_handle->seek = seek_by_char;
+       } else {
+               archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY);
+       }
+
+       if ((base_dir) && (chdir(base_dir))) {
+               bb_perror_msg_and_die("Couldnt chdir");
+       }
+
+       while (optind < argc) {
+               archive_handle->filter = filter_accept_list;
+               archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]);
+               optind++;
+       }
+
+       while (1) {
+               unsigned int magic;
+               int dst_fd;
+
+               /* TODO Endian issues */
+               archive_xread_all(archive_handle, &magic, 4);
+               archive_handle->offset += 4;
+
+               if (magic == ZIP_CDS_MAGIC) {
+                       break;
+               }
+               else if (magic != ZIP_FILEHEADER_MAGIC) {
+                       bb_error_msg_and_die("Invlaide zip magic");
+               }
+
+               /* Read the file header */
+               archive_xread_all(archive_handle, zip_header.raw, 26);
+               archive_handle->offset += 26;
+               archive_handle->file_header->mode = S_IFREG | 0777;
+
+               if (zip_header.formated.method != 8) {
+                       bb_error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method);
+               }
+
+               /* Read filename */
+               archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1);
+               archive_xread_all(archive_handle, archive_handle->file_header->name, zip_header.formated.filename_len);
+               archive_handle->offset += zip_header.formated.filename_len;
+               archive_handle->file_header->name[zip_header.formated.filename_len] = '\0';
+
+               /* Skip extra header bits */
+               archive_handle->file_header->size = zip_header.formated.extra_len;
+               data_skip(archive_handle);
+               archive_handle->offset += zip_header.formated.extra_len;
+
+               /* Handle directories */
+               archive_handle->file_header->mode = S_IFREG | 0777;
+               if (last_char_is(archive_handle->file_header->name, '/')) {
+                       archive_handle->file_header->mode ^= S_IFREG;
+                       archive_handle->file_header->mode |= S_IFDIR;
+               }
+
+               /* Data section */
+               archive_handle->file_header->size = zip_header.formated.cmpsize;
+               if (archive_handle->action_data) {
+                       archive_handle->action_data(archive_handle);
+               } else {
+                       dst_fd = bb_xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT);
+                       inflate_init(zip_header.formated.cmpsize);
+                       inflate_unzip(archive_handle->src_fd, dst_fd);
+                       close(dst_fd);
+                       chmod(archive_handle->file_header->name, archive_handle->file_header->mode);
+
+                       /* Validate decompression - crc */
+                       if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) {
+                               bb_error_msg("Invalid compressed data--crc error");
+                       }
+
+                       /* Validate decompression - size */
+                       if (gunzip_bytes_out != zip_header.formated.ucmpsize) {
+                               bb_error_msg("Invalid compressed data--length error");
+                       }
+               }
+
+               /* local file descriptor section */
+               archive_handle->offset += zip_header.formated.cmpsize;
+               /* This ISNT unix time */
+               archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16);
+               archive_handle->file_header->size = zip_header.formated.ucmpsize;
+               total_size += archive_handle->file_header->size;
+               total_entries++;
+
+               archive_handle->action_header(archive_handle->file_header);
+
+               /* Data descriptor section */
+               if (zip_header.formated.flags & 4) {
+                       /* skip over duplicate crc, compressed size and uncompressed size */
+                       unsigned char data_description[12];
+                       archive_xread_all(archive_handle, data_description, 12);
+                       archive_handle->offset += 12;
+               }
+       }
+       /* Central directory section */
+
+       if (archive_handle->action_header == header_verbose_list_unzip) {
+               printf(" --------                   -------\n");
+               printf("%9d                   %d files\n", total_size, total_entries);
+       }
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/console-tools/Config.in b/busybox/console-tools/Config.in
new file mode 100644 (file)
index 0000000..e261794
--- /dev/null
@@ -0,0 +1,68 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Console Utilities"
+
+config CONFIG_CHVT
+       bool "chvt"
+       default n
+       help
+         This program is used to change to another terminal.
+         Example: chvt 4 (change to terminal /dev/tty4)
+
+config CONFIG_CLEAR
+       bool "clear"
+       default n
+       help
+         This program clears the terminal screen.
+
+config CONFIG_DEALLOCVT
+       bool "deallocvt"
+       default n
+       help
+         This program deallocates unused virtual consoles.
+
+config CONFIG_DUMPKMAP
+       bool "dumpkmap"
+       default n
+       help
+         This program dumps the kernel's keyboard translation table to
+         stdout, in binary format. You can then use loadkmap to load it.
+
+config CONFIG_LOADFONT
+       bool "loadfont"
+       default n
+       help
+         This program loads a console font from standard input.
+
+config CONFIG_LOADKMAP
+       bool "loadkmap"
+       default n
+       help
+         This program loads a keyboard translation table from
+         standard input.
+
+config CONFIG_OPENVT
+       bool "openvt"
+       default n
+       help
+         This program is used to start a command on an unused
+         virtual terminal.
+
+config CONFIG_RESET
+       bool "reset"
+       default n
+       help
+         This program is used to reset the terminal screen, if it
+         gets messed up.
+
+config CONFIG_SETKEYCODES
+       bool "setkeycodes"
+       default n
+       help
+         This program loads entries into the kernel's scancode-to-keycode
+         map, allowing unusual keyboards to generate usable keycodes.
+
+endmenu
diff --git a/busybox/console-tools/Makefile b/busybox/console-tools/Makefile
new file mode 100644 (file)
index 0000000..42cf2c8
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/console/tools
+CONSOLETOOLS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/console-tools/Makefile.in b/busybox/console-tools/Makefile.in
new file mode 100644 (file)
index 0000000..b19ce5c
--- /dev/null
@@ -0,0 +1,44 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+CONSOLETOOLS_AR:=console-tools.a
+ifndef $(CONSOLETOOLS_DIR)
+CONSOLETOOLS_DIR:=$(top_builddir)/console-tools/
+endif
+srcdir=$(top_srcdir)/console-tools
+
+CONSOLETOOLS_DIR-y:=
+CONSOLETOOLS_DIR-$(CONFIG_CHVT)                += chvt.o
+CONSOLETOOLS_DIR-$(CONFIG_CLEAR)       += clear.o
+CONSOLETOOLS_DIR-$(CONFIG_DEALLOCVT)   += deallocvt.o
+CONSOLETOOLS_DIR-$(CONFIG_DUMPKMAP)    += dumpkmap.o
+CONSOLETOOLS_DIR-$(CONFIG_LOADFONT)    += loadfont.o
+CONSOLETOOLS_DIR-$(CONFIG_LOADKMAP)    += loadkmap.o
+CONSOLETOOLS_DIR-$(CONFIG_OPENVT)      += openvt.o
+CONSOLETOOLS_DIR-$(CONFIG_RESET)       += reset.o
+CONSOLETOOLS_DIR-$(CONFIG_SETKEYCODES) += setkeycodes.o
+
+libraries-y+=$(CONSOLETOOLS_DIR)$(CONSOLETOOLS_AR)
+
+$(CONSOLETOOLS_DIR)$(CONSOLETOOLS_AR): $(patsubst %,$(CONSOLETOOLS_DIR)%, $(CONSOLETOOLS_DIR-y))
+       $(AR) -ro $@ $(patsubst %,$(CONSOLETOOLS_DIR)%, $(CONSOLETOOLS_DIR-y))
+
+$(CONSOLETOOLS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/console-tools/chvt.c b/busybox/console-tools/chvt.c
new file mode 100644 (file)
index 0000000..3398892
--- /dev/null
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chvt implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* no options, no getopt */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+/* From <linux/vt.h> */
+static const int VT_ACTIVATE = 0x5606;  /* make vt active */
+static const int VT_WAITACTIVE = 0x5607;  /* wait for vt active */
+
+int chvt_main(int argc, char **argv)
+{
+       int fd, num;
+
+       if (argc != 2) {
+               bb_show_usage();
+       }
+
+       fd = get_console_fd();
+       num =  bb_xgetlarg(argv[1], 10, 0, INT_MAX);
+       if (ioctl(fd, VT_ACTIVATE, num)) {
+               bb_perror_msg_and_die("VT_ACTIVATE");
+       }
+       if (ioctl(fd, VT_WAITACTIVE, num)) {
+               bb_perror_msg_and_die("VT_WAITACTIVE");
+       }
+       return EXIT_SUCCESS;
+}
+
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/console-tools/clear.c b/busybox/console-tools/clear.c
new file mode 100644 (file)
index 0000000..e43ed0e
--- /dev/null
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini clear implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* no options, no getopt */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+
+extern int clear_main(int argc, char **argv)
+{
+       printf("\033[H\033[J");
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/console-tools/deallocvt.c b/busybox/console-tools/deallocvt.c
new file mode 100644 (file)
index 0000000..08a9d21
--- /dev/null
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Disallocate virtual terminal(s)
+ *
+ * Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* no options, no getopt */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+/* From <linux/vt.h> */
+static const int VT_DISALLOCATE = 0x5608;  /* free memory associated to vt */
+
+int deallocvt_main(int argc, char *argv[])
+{
+       /* num = 0 deallocate all unused consoles */
+       int num = 0;
+
+       switch(argc)
+       {
+               case 2:
+                       if((num = bb_xgetlarg(argv[1], 10, 0, INT_MAX)) == 0)
+                               bb_error_msg_and_die("0: illegal VT number");
+               /* Falltrough */
+               case 1:
+                       break;
+               default:
+                       bb_show_usage();
+       }
+
+       if (ioctl( get_console_fd(), VT_DISALLOCATE, num )) {
+               bb_perror_msg_and_die("VT_DISALLOCATE");
+       }
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/console-tools/dumpkmap.c b/busybox/console-tools/dumpkmap.c
new file mode 100644 (file)
index 0000000..6085a44
--- /dev/null
@@ -0,0 +1,91 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dumpkmap implementation for busybox
+ *
+ * Copyright (C) Arne Bernin <arne@matrix.loopback.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+/* From <linux/kd.h> */
+struct kbentry {
+       unsigned char kb_table;
+       unsigned char kb_index;
+       unsigned short kb_value;
+};
+static const int KDGKBENT = 0x4B46;  /* gets one entry in translation table */
+
+/* From <linux/keyboard.h> */
+static const int NR_KEYS = 128;
+static const int MAX_NR_KEYMAPS = 256;
+
+int dumpkmap_main(int argc, char **argv)
+{
+       struct kbentry ke;
+       int i, j, fd;
+       char flags[MAX_NR_KEYMAPS], magic[] = "bkeymap";
+
+       if (argc>=2 && *argv[1]=='-') {
+               bb_show_usage();
+       }
+
+       fd=bb_xopen(CURRENT_VC, O_RDWR);
+
+       write(1, magic, 7);
+
+       for (i=0; i < MAX_NR_KEYMAPS; i++) flags[i]=0;
+       flags[0]=1;
+       flags[1]=1;
+       flags[2]=1;
+       flags[4]=1;
+       flags[5]=1;
+       flags[6]=1;
+       flags[8]=1;
+       flags[9]=1;
+       flags[10]=1;
+       flags[12]=1;
+
+       /* dump flags */
+       for (i=0; i < MAX_NR_KEYMAPS; i++) write(1,&flags[i],1);
+
+       for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+               if (flags[i] == 1) {
+                       for (j = 0; j < NR_KEYS; j++) {
+                               ke.kb_index = j;
+                               ke.kb_table = i;
+                               if (ioctl(fd, KDGKBENT, &ke) < 0) {
+
+                                       bb_error_msg("ioctl returned: %m, %s, %s, %xqq", (char *)&ke.kb_index,(char *)&ke.kb_table,(int)&ke.kb_value);
+                                       }
+                               else {
+                                       write(1,(void*)&ke.kb_value,2);
+                                       }
+
+                       }
+               }
+       }
+       close(fd);
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/console-tools/loadfont.c b/busybox/console-tools/loadfont.c
new file mode 100644 (file)
index 0000000..4580dc4
--- /dev/null
@@ -0,0 +1,207 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * loadfont.c - Eugene Crosser & Andries Brouwer
+ *
+ * Version 0.96bb
+ *
+ * Loads the console font, and possibly the corresponding screen map(s).
+ * (Adapted for busybox by Matej Vela.)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/kd.h>
+#include <endian.h>
+#include "busybox.h"
+
+static const int PSF_MAGIC1 = 0x36;
+static const int PSF_MAGIC2 = 0x04;
+
+static const int PSF_MODE512 = 0x01;
+static const int PSF_MODEHASTAB = 0x02;
+static const int PSF_MAXMODE = 0x03;
+static const int PSF_SEPARATOR = 0xFFFF;
+
+struct psf_header {
+       unsigned char magic1, magic2;   /* Magic number */
+       unsigned char mode;                     /* PSF font mode */
+       unsigned char charsize;         /* Character size */
+};
+
+#define PSF_MAGIC_OK(x)        ((x).magic1 == PSF_MAGIC1 && (x).magic2 == PSF_MAGIC2)
+
+static void loadnewfont(int fd);
+
+extern int loadfont_main(int argc, char **argv)
+{
+       int fd;
+
+       if (argc != 1)
+               bb_show_usage();
+
+       fd = bb_xopen(CURRENT_VC, O_RDWR);
+       loadnewfont(fd);
+
+       return EXIT_SUCCESS;
+}
+
+static void do_loadfont(int fd, char *inbuf, int unit, int fontsize)
+{
+       char buf[16384];
+       int i;
+
+       memset(buf, 0, sizeof(buf));
+
+       if (unit < 1 || unit > 32)
+               bb_error_msg_and_die("Bad character size %d", unit);
+
+       for (i = 0; i < fontsize; i++)
+               memcpy(buf + (32 * i), inbuf + (unit * i), unit);
+
+#if defined( PIO_FONTX ) && !defined( __sparc__ )
+       {
+               struct consolefontdesc cfd;
+
+               cfd.charcount = fontsize;
+               cfd.charheight = unit;
+               cfd.chardata = buf;
+
+               if (ioctl(fd, PIO_FONTX, &cfd) == 0)
+                       return;                         /* success */
+               bb_perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)");
+       }
+#endif
+       if (ioctl(fd, PIO_FONT, buf))
+               bb_perror_msg_and_die("PIO_FONT ioctl error");
+}
+
+static void
+do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize)
+{
+       struct unimapinit advice;
+       struct unimapdesc ud;
+       struct unipair *up;
+       int ct = 0, maxct;
+       int glyph;
+       u_short unicode;
+
+       maxct = tailsz;                         /* more than enough */
+       up = (struct unipair *) xmalloc(maxct * sizeof(struct unipair));
+
+       for (glyph = 0; glyph < fontsize; glyph++) {
+               while (tailsz >= 2) {
+                       unicode = (((u_short) inbuf[1]) << 8) + inbuf[0];
+                       tailsz -= 2;
+                       inbuf += 2;
+                       if (unicode == PSF_SEPARATOR)
+                               break;
+                       up[ct].unicode = unicode;
+                       up[ct].fontpos = glyph;
+                       ct++;
+               }
+       }
+
+       /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
+          this printf did not work on many kernels */
+
+       advice.advised_hashsize = 0;
+       advice.advised_hashstep = 0;
+       advice.advised_hashlevel = 0;
+       if (ioctl(fd, PIO_UNIMAPCLR, &advice)) {
+#ifdef ENOIOCTLCMD
+               if (errno == ENOIOCTLCMD) {
+                       bb_error_msg("It seems this kernel is older than 1.1.92");
+                       bb_error_msg_and_die("No Unicode mapping table loaded.");
+               } else
+#endif
+                       bb_perror_msg_and_die("PIO_UNIMAPCLR");
+       }
+       ud.entry_ct = ct;
+       ud.entries = up;
+       if (ioctl(fd, PIO_UNIMAP, &ud)) {
+#if 0
+               if (errno == ENOMEM) {
+                       /* change advice parameters */
+               }
+#endif
+               bb_perror_msg_and_die("PIO_UNIMAP");
+       }
+}
+
+static void loadnewfont(int fd)
+{
+       int unit;
+       char inbuf[32768];                      /* primitive */
+       unsigned int inputlth, offset;
+
+       /*
+        * We used to look at the length of the input file
+        * with stat(); now that we accept compressed files,
+        * just read the entire file.
+        */
+       inputlth = fread(inbuf, 1, sizeof(inbuf), stdin);
+       if (ferror(stdin))
+               bb_perror_msg_and_die("Error reading input font");
+       /* use malloc/realloc in case of giant files;
+          maybe these do not occur: 16kB for the font,
+          and 16kB for the map leaves 32 unicode values
+          for each font position */
+       if (!feof(stdin))
+               bb_perror_msg_and_die("Font too large");
+
+       /* test for psf first */
+       {
+               struct psf_header psfhdr;
+               int fontsize;
+               int hastable;
+               unsigned int head0, head;
+
+               if (inputlth < sizeof(struct psf_header))
+                       goto no_psf;
+
+               psfhdr = *(struct psf_header *) &inbuf[0];
+
+               if (!PSF_MAGIC_OK(psfhdr))
+                       goto no_psf;
+
+               if (psfhdr.mode > PSF_MAXMODE)
+                       bb_error_msg_and_die("Unsupported psf file mode");
+               fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256);
+#if !defined( PIO_FONTX ) || defined( __sparc__ )
+               if (fontsize != 256)
+                       bb_error_msg_and_die("Only fontsize 256 supported");
+#endif
+               hastable = (psfhdr.mode & PSF_MODEHASTAB);
+               unit = psfhdr.charsize;
+               head0 = sizeof(struct psf_header);
+
+               head = head0 + fontsize * unit;
+               if (head > inputlth || (!hastable && head != inputlth))
+                       bb_error_msg_and_die("Input file: bad length");
+               do_loadfont(fd, inbuf + head0, unit, fontsize);
+               if (hastable)
+                       do_loadtable(fd, inbuf + head, inputlth - head, fontsize);
+               return;
+       }
+  no_psf:
+
+       /* file with three code pages? */
+       if (inputlth == 9780) {
+               offset = 40;
+               unit = 16;
+       } else {
+               /* bare font */
+               if (inputlth & 0377)
+                       bb_error_msg_and_die("Bad input file size");
+               offset = 0;
+               unit = inputlth / 256;
+       }
+       do_loadfont(fd, inbuf + offset, unit, 256);
+}
diff --git a/busybox/console-tools/loadkmap.c b/busybox/console-tools/loadkmap.c
new file mode 100644 (file)
index 0000000..849d747
--- /dev/null
@@ -0,0 +1,82 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini loadkmap implementation for busybox
+ *
+ * Copyright (C) 1998 Enrique Zanardi <ezanardi@ull.es>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+#define BINARY_KEYMAP_MAGIC "bkeymap"
+
+/* From <linux/kd.h> */
+struct kbentry {
+       unsigned char kb_table;
+       unsigned char kb_index;
+       unsigned short kb_value;
+};
+/* sets one entry in translation table */
+#define KDSKBENT        0x4B47
+
+/* From <linux/keyboard.h> */
+#define NR_KEYS         128
+#define MAX_NR_KEYMAPS  256
+
+int loadkmap_main(int argc, char **argv)
+{
+       struct kbentry ke;
+       int i, j, fd;
+       u_short ibuff[NR_KEYS];
+       char flags[MAX_NR_KEYMAPS];
+       char buff[7];
+
+       if (argc != 1)
+               bb_show_usage();
+
+       fd = bb_xopen(CURRENT_VC, O_RDWR);
+
+       if ((bb_full_read(0, buff, 7) != 7) || (strncmp(buff, BINARY_KEYMAP_MAGIC, 7) != 0))
+               bb_error_msg_and_die("This is not a valid binary keymap.");
+
+       if (bb_full_read(0, flags, MAX_NR_KEYMAPS) != MAX_NR_KEYMAPS)
+               bb_perror_msg_and_die("Error reading keymap flags");
+
+       for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+               if (flags[i] == 1) {
+                       bb_full_read(0, ibuff, NR_KEYS * sizeof(u_short));
+                       for (j = 0; j < NR_KEYS; j++) {
+                               ke.kb_index = j;
+                               ke.kb_table = i;
+                               ke.kb_value = ibuff[j];
+                               ioctl(fd, KDSKBENT, &ke);
+                       }
+               }
+       }
+
+       /* Don't bother to close files.  Exit does that
+        * automagically, so we can save a few bytes */
+       /* close(fd); */
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/console-tools/openvt.c b/busybox/console-tools/openvt.c
new file mode 100644 (file)
index 0000000..5f24457
--- /dev/null
@@ -0,0 +1,84 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  openvt.c - open a vt to run a command.
+ *
+ *  busyboxed by Quy Tonthat <quy@signal3.com>
+ *  hacked by Tito <farmatito@tiscali.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* getopt not needed */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "busybox.h"
+
+int openvt_main(int argc, char **argv)
+{
+       int fd;
+       char vtname[sizeof VC_FORMAT + 2];
+
+
+       if (argc < 3)
+        bb_show_usage();
+
+       /* check for Illegal vt number: < 1 or > 12 */
+       sprintf(vtname, VC_FORMAT,(int)bb_xgetlarg(argv[1], 10, 1, 12));
+
+       argv+=2;
+       argc-=2;
+
+       if(fork() == 0) {
+               /* leave current vt */
+
+#ifdef   ESIX_5_3_2_D
+               if (setpgrp() < 0) {
+#else
+               if (setsid() < 0) {
+#endif
+
+                       bb_perror_msg_and_die("Unable to set new session");     
+               }
+               close(0);                       /* so that new vt becomes stdin */
+
+               /* and grab new one */
+               fd = bb_xopen(vtname, O_RDWR);
+
+               /* Reassign stdout and sterr */
+               close(1);
+               close(2);
+               dup(fd);
+               dup(fd);
+
+               execvp(argv[0], argv);
+               _exit(1);
+       }
+       return EXIT_SUCCESS;
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/console-tools/reset.c b/busybox/console-tools/reset.c
new file mode 100644 (file)
index 0000000..9d38e7a
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini reset implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Written by Erik Andersen and Kent Robotti <robotti@metconnect.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* no options, no getopt */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int reset_main(int argc, char **argv)
+{
+       if (isatty(0) || isatty(0) || isatty(0)) {
+               /* See 'man 4 console_codes' for details:
+                * "ESC c"                      -- Reset
+                * "ESC ( K"            -- Select user mapping
+                * "ESC [ J"            -- Erase display
+                * "ESC [ 0 m"          -- Reset all Graphics Rendition display attributes
+                * "ESC [ ? 25 h"       -- Make cursor visible.
+                */
+               printf("\033c\033(K\033[J\033[0m\033[?25h");
+       }
+       return EXIT_SUCCESS;
+}
+
diff --git a/busybox/console-tools/setkeycodes.c b/busybox/console-tools/setkeycodes.c
new file mode 100644 (file)
index 0000000..169d0bb
--- /dev/null
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setkeycodes
+ *
+ * Copyright (C) 1994-1998 Andries E. Brouwer <aeb@cwi.nl>
+ *
+ * Adjusted for BusyBox by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+
+/* From <linux/kd.h> */
+struct kbkeycode {
+       unsigned int scancode, keycode;
+};
+static const int KDSETKEYCODE = 0x4B4D;  /* write kernel keycode table entry */
+
+extern int
+setkeycodes_main(int argc, char** argv)
+{
+    char *ep;
+    int fd, sc;
+    struct kbkeycode a;
+
+    if (argc % 2 != 1 || argc < 2) {
+      bb_show_usage();
+       }
+       
+       fd = get_console_fd();
+
+    while (argc > 2) {
+       a.keycode = atoi(argv[2]);
+       a.scancode = sc = strtol(argv[1], &ep, 16);
+       if (*ep) {
+      bb_error_msg_and_die("error reading SCANCODE: '%s'", argv[1]);
+       }
+       if (a.scancode > 127) {
+           a.scancode -= 0xe000;
+           a.scancode += 128;
+       }
+       if (a.scancode > 255 || a.keycode > 127) {
+      bb_error_msg_and_die("SCANCODE or KEYCODE outside bounds");
+       }
+       if (ioctl(fd,KDSETKEYCODE,&a)) {
+           perror("KDSETKEYCODE");
+               bb_error_msg_and_die("failed to set SCANCODE %x to KEYCODE %d", sc, a.keycode);
+       }
+       argc -= 2;
+       argv += 2;
+    }
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/Config.in b/busybox/coreutils/Config.in
new file mode 100644 (file)
index 0000000..e1f0516
--- /dev/null
@@ -0,0 +1,613 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Coreutils"
+
+config CONFIG_BASENAME
+       bool "basename"
+       default n
+       help
+         basename is used to strip the directory and suffix from filenames,
+         leaving just the filename itself.  Enable this option if you wish
+         to enable the 'basename' utility.
+
+config CONFIG_CAL
+       bool "cal"
+       default n
+       help
+         cal is used to display a monthly calender.
+
+config CONFIG_CAT
+       bool "cat"
+       default n
+       help
+         cat is used to concatenate files and print them to the standard
+         output.  Enable this option if you wish to enable the 'cat' utility.
+
+config CONFIG_CHGRP
+       bool "chgrp"
+       default n
+       help
+         chgrp is used to change the group ownership of files.
+
+config CONFIG_CHMOD
+       bool "chmod"
+       default n
+       help
+         chmod is used to change the access permission of files.
+
+config CONFIG_CHOWN
+       bool "chown"
+       default n
+       help
+         chown is used to change the user and/or group ownership
+         of files.
+
+config CONFIG_CHROOT
+       bool "chroot"
+       default n
+       help
+         chroot is used to change the root directory and run a command.
+         The default command is `/bin/sh'.
+
+config CONFIG_CMP
+       bool "cmp"
+       default n
+       help
+         cmp is used to compare two files and returns the result
+         to standard output.
+
+config CONFIG_CP
+       bool "cp"
+       default n
+       help
+         cp is used to copy files and directories.
+
+config CONFIG_CUT
+       bool "cut"
+       default n
+       help
+         cut is used to print selected parts of lines from
+         each file to stdout.
+
+if CONFIG_WATCH
+       config CONFIG_DATE
+               default y
+       comment "date (forced enabled for use with watch)"
+endif
+
+if !CONFIG_WATCH
+    config CONFIG_DATE
+           bool "date"
+           default n
+           help
+             date is used to set the system date or display the
+             current time in the given format.
+endif
+
+config CONFIG_FEATURE_DATE_ISOFMT
+       bool "  Enable ISO date format output (-I)"
+       default y
+       depends on CONFIG_DATE
+       help
+         Enable option (-I) to output an ISO-8601 compliant
+         date/time string.
+
+config CONFIG_DD
+       bool "dd"
+       default n
+       help
+         dd copies a file (from standard input to standard output,
+         by default) using specific input and output blocksizes,
+         while optionally performing conversions on it.
+
+config CONFIG_DF
+       bool "df"
+       default n
+       help
+         df reports the amount of disk space used and available
+         on filesystems.
+
+config CONFIG_DIRNAME
+       bool "dirname"
+       default n
+       help
+         dirname is used to strip a non-directory suffix from
+         a file name.
+
+config CONFIG_DOS2UNIX
+       bool "dos2unix/unix2dos"
+       default n
+       help
+         dos2unix is used to convert a text file from DOS format to
+         UNIX format, and vice versa.
+
+config CONFIG_UNIX2DOS
+       bool
+       default y
+       depends on CONFIG_DOS2UNIX
+
+config CONFIG_DU
+       bool "du (default blocksize of 512 bytes)"
+       default n
+       help
+         du is used to report the amount of disk space used
+         for specified files.
+
+config CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
+       bool "  Use a default blocksize of 1024 bytes (1K)"
+       default y
+       depends on CONFIG_DU
+       help
+         Use a blocksize of (1K) instead of the default 512b.
+
+config CONFIG_ECHO
+       bool "echo (basic SUSv3 version taking no options)"
+       default n
+       help
+         echo is used to print a specified string to stdout.
+
+config CONFIG_FEATURE_FANCY_ECHO
+       bool "  Enable echo options (-n and -e)"
+       default y
+       depends on CONFIG_ECHO
+       help
+         This adds options (-n and -e) to echo.
+
+config CONFIG_ENV
+       bool "env"
+       default n
+       help
+         env is used to set an environment variable and run
+         a command; without options it displays the current
+         environment.
+
+config CONFIG_EXPR
+       bool "expr"
+       default n
+       help
+         expr is used to calculate numbers and print the result
+         to standard output.
+
+if CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH
+       config CONFIG_FALSE
+               default y
+       comment "false (forced enabled for use with shell)"
+endif
+
+if !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH
+    config CONFIG_FALSE
+           bool "false"
+           default n
+           help
+             false returns an exit code of FALSE (1).
+endif
+
+config CONFIG_FOLD
+       bool "fold"
+       default n
+       help
+         Wrap text to fit a specific width.
+
+config CONFIG_HEAD
+       bool "head"
+       default n
+       help
+         head is used to print the first specified number of lines
+         from files.
+
+config CONFIG_FEATURE_FANCY_HEAD
+       bool "  Enable head options (-c, -q, and -v)"
+       default n
+       depends on CONFIG_HEAD
+       help
+         This enables the head options (-c, -q, and -v).
+
+config CONFIG_HOSTID
+       bool "hostid"
+       default n
+       help
+         hostid prints the numeric identifier (in hexadecimal) for
+         the current host.
+
+config CONFIG_ID
+       bool "id"
+       default n
+       help
+         id displays the current user and group ID names.
+
+config CONFIG_INSTALL
+       bool "install"
+       default n
+       help
+         Copy files and set attributes.
+
+config CONFIG_LENGTH
+       bool "length"
+       default n
+       help
+         length is used to print out the length of a specified string.
+
+config CONFIG_LN
+       bool "ln"
+       default n
+       help
+         ln is used to create hard or soft links between files.
+
+config CONFIG_LOGNAME
+       bool "logname"
+       default n
+       help
+         logname is used to print the current user's login name.
+
+config CONFIG_LS
+       bool "ls"
+       default n
+       help
+         ls is used to list the contents of directories.
+
+config CONFIG_FEATURE_LS_FILETYPES
+       bool "  Enable filetyping options (-p and -F)"
+       default y
+       depends on CONFIG_LS
+       help
+         Enable the ls options (-p and -F).
+
+config CONFIG_FEATURE_LS_FOLLOWLINKS
+       bool "  Enable symlinks dereferencing (-L)"
+       default y
+       depends on CONFIG_LS
+       help
+         Enable the ls option (-L).
+
+config CONFIG_FEATURE_LS_RECURSIVE
+       bool "  Enable recursion (-R)"
+       default y
+       depends on CONFIG_LS
+       help
+         Enable the ls option (-R).
+
+config CONFIG_FEATURE_LS_SORTFILES
+       bool "  Sort the file names"
+       default y
+       depends on CONFIG_LS
+       help
+         Allow ls to sort file names alphabetically.
+
+config CONFIG_FEATURE_LS_TIMESTAMPS
+       bool "  Show file timestamps"
+       default y
+       depends on CONFIG_LS
+       help
+         Allow ls to display timestamps for files.
+
+config CONFIG_FEATURE_LS_USERNAME
+       bool "  Show username/groupnames"
+       default y
+       depends on CONFIG_LS
+       help
+         Allow ls to display username/groupname for files.
+
+config CONFIG_FEATURE_LS_COLOR
+       bool "  Use color to identify file types"
+       default y
+       depends on CONFIG_LS
+       help
+         Allow ls to use color when displaying files.
+
+config CONFIG_MD5SUM
+       bool "md5sum"
+       default n
+       help
+         md5sum is used to print or check MD5 checksums.
+
+config CONFIG_MKDIR
+       bool "mkdir"
+       default n
+       help
+         mkdir is used to create directories with the specified names.
+
+config CONFIG_MKFIFO
+       bool "mkfifo"
+       default n
+       help
+         mkfifo is used to create FIFOs (named pipes).
+         The `mknod' program can also create FIFOs.
+
+config CONFIG_MKNOD
+       bool "mknod"
+       default n
+       help
+         mknod is used to create FIFOs or block/character special
+         files with the specified names.
+
+config CONFIG_MV
+       bool "mv"
+       default n
+       help
+         mv is used to move or rename files or directories.
+
+config CONFIG_OD
+       bool "od"
+       default n
+       help
+         od is used to dump binary files in octal and other formats.
+
+config CONFIG_PRINTF
+       bool "printf"
+       default n
+       help
+         printf is used to format and print specified strings.
+         It's similar to `echo' except it has more options.
+
+config CONFIG_PWD
+       bool "pwd"
+       default n
+       help
+         pwd is used to print the current directory.
+
+config CONFIG_REALPATH
+       bool "realpath"
+       default n
+       help
+         Return the canonicalized absolute pathname.
+         This isn't provided by GNU shellutils, but where else does it belong.
+
+config CONFIG_RM
+       bool "rm"
+       default n
+       help
+         rm is used to remove files or directories.
+
+config CONFIG_RMDIR
+       bool "rmdir"
+       default n
+       help
+         rmdir is used to remove empty directories.
+
+config CONFIG_SEQ
+       bool "seq"
+       default n
+       help
+         print a sequence of numbers
+
+config CONFIG_SHA1SUM
+       bool "sha1sum"
+       default n
+       help
+         Compute and check SHA1 message digest
+
+config CONFIG_SLEEP
+       bool "sleep (single integer arg with no suffix)"
+       default n
+       help
+         sleep is used to pause for a specified number of seconds,
+
+config CONFIG_FEATURE_FANCY_SLEEP
+       bool "  Enable multiple integer args and optional time suffixes"
+       default n
+       depends on CONFIG_SLEEP
+       help
+         Allow sleep to pause for specified minutes, hours, and days.
+
+config CONFIG_SORT
+       bool "sort"
+       default n
+       help
+         sort is used to sort lines of text in specified files.
+
+config CONFIG_STTY
+       bool "stty"
+       default n
+       help
+         stty is used to change and print terminal line settings.
+
+config CONFIG_SYNC
+       bool "sync"
+       default n
+       help
+         sync is used to flush filesystem buffers.
+
+config CONFIG_TAIL
+       bool "tail"
+       default n
+       help
+         tail is used to print the last specified number of lines
+         from files.
+
+config CONFIG_FEATURE_FANCY_TAIL
+       bool "  Enable extra tail options (-q, -s, and -v)"
+       default y
+       depends on CONFIG_TAIL
+       help
+         The options (-q, -s, and -v) are provided by GNU tail, but
+         are not specific in the SUSv3 standard.
+
+config CONFIG_TEE
+       bool "tee"
+       default n
+       help
+         tee is used to read from standard input and write
+         to standard output and files.
+
+config CONFIG_FEATURE_TEE_USE_BLOCK_IO
+       bool "  Enable block i/o (larger/faster) instead of byte i/o."
+       default n
+       depends on CONFIG_TEE
+       help
+         Enable this option for a faster tee, at expense of size.
+
+if CONFIG_ASH || CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH
+       config CONFIG_TEST
+               default y
+       comment "test (forced enabled for use with shell)"
+endif
+
+if !CONFIG_ASH && !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH
+    config CONFIG_TEST
+           bool "test"
+           default n
+           help
+             test is used to check file types and compare values,
+             returning an appropriate exit code. The shells (ash
+             and bash) have test builtin.
+endif
+
+config CONFIG_FEATURE_TEST_64
+       bool "  Extend test to 64 bit"
+       default n
+       depends on CONFIG_TEST
+       help
+         Enable 64-bit support in test.
+
+config CONFIG_TOUCH
+       bool "touch"
+       default n
+       help
+         touch is used to create or change the access and/or
+         modification timestamp of specified files.
+
+config CONFIG_TR
+       bool "tr"
+       default n
+       help
+         tr is used to squeeze, and/or delete characters from standard
+         input, writing to standard output.
+
+if CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH
+       config CONFIG_TRUE
+               default y
+       comment "true (forced enabled for use with shell)"
+endif
+
+if !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH
+    config CONFIG_TRUE
+           bool "true"
+           default n
+           help
+             true returns an exit code of TRUE (0).
+
+endif
+
+config CONFIG_TTY
+       bool "tty"
+       default n
+       help
+         tty is used to print the name of the current terminal to
+         standard output.
+
+config CONFIG_UNAME
+       bool "uname"
+       default n
+       help
+         uname is used to print system information.
+
+config CONFIG_UNIQ
+       bool "uniq"
+       default n
+       help
+         uniq is used to remove duplicate lines from a sorted file.
+
+config CONFIG_USLEEP
+       bool "usleep"
+       default n
+       help
+         usleep is used to pause for a specified number of microseconds.
+
+config CONFIG_UUDECODE
+       bool "uudecode"
+       default n
+       help
+         uudecode is used to decode a uuencoded file.
+
+config CONFIG_UUENCODE
+       bool "uuencode"
+       default n
+       help
+         uuencode is used to uuencode a file.
+
+config CONFIG_WATCH
+       bool "watch"
+       default n
+       help
+         watch is used to execute a program periodically, showing
+         output to the screen.
+
+config CONFIG_WC
+       bool "wc"
+       default n
+       help
+         wc is used to print the number of bytes, words, and lines,
+         in specified files.
+
+config CONFIG_WHO
+       bool "who"
+       default n
+       select CONFIG_FEATURE_U_W_TMP
+       help
+         who is used to show who is logged on.
+
+config CONFIG_WHOAMI
+       bool "whoami"
+       default n
+       help
+         whoami is used to print the username of the current
+         user id (same as id -un).
+
+config CONFIG_YES
+       bool "yes"
+       default n
+       help
+         yes is used to repeatedly output a specific string, or
+         the default string `y'.
+
+comment "Common options for cp and mv"
+    depends on CONFIG_CP || CONFIG_MV
+
+config CONFIG_FEATURE_PRESERVE_HARDLINKS
+       bool "  Preserve hard links"
+       default n
+       depends on CONFIG_CP || CONFIG_MV
+       help
+         Allow cp and mv to preserve hard links.
+
+comment "Common options for ls and more"
+       depends on CONFIG_LS || CONFIG_MORE
+
+config CONFIG_FEATURE_AUTOWIDTH
+       bool "  Calculate terminal & column widths"
+       default y
+       depends on CONFIG_LS || CONFIG_MORE
+       help
+         This option allows utilities such as 'ls' and 'more' to determine the
+         width of the screen, which can allow them to display additional text
+         or avoid wrapping text onto the next line.  If you leave this
+         disabled, your utilities will be especially primitive and will be
+         unable to determine the current screen width.
+
+comment "Common options for df, du, ls"
+    depends on CONFIG_DF || CONFIG_DU || CONFIG_LS
+
+config CONFIG_FEATURE_HUMAN_READABLE
+       bool "  Support for human readable output (example 13k, 23M, 235G)"
+       default n
+       depends on CONFIG_DF || CONFIG_DU || CONFIG_LS
+       help
+         Allow df, du, and ls to have human readable output.
+
+comment "Common options for md5sum, sha1sum"
+    depends on CONFIG_MD5SUM || CONFIG_SHA1SUM
+
+config CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+       bool "  Enable -c, -s and -w options"
+       default n
+       depends on CONFIG_MD5SUM || CONFIG_SHA1SUM
+       help
+         Enabling the -c options allows files to be checked
+         against pre-calculated hash values.
+
+         -s and -w are useful options when verifying checksums.
+
+endmenu
diff --git a/busybox/coreutils/Makefile b/busybox/coreutils/Makefile
new file mode 100644 (file)
index 0000000..50fdac2
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/coreutils
+SHELLUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/coreutils/Makefile.in b/busybox/coreutils/Makefile.in
new file mode 100644 (file)
index 0000000..aacb813
--- /dev/null
@@ -0,0 +1,98 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+COREUTILS_AR:=coreutils.a
+ifndef $(COREUTILS_DIR)
+COREUTILS_DIR:=$(top_builddir)/coreutils/
+endif
+srcdir=$(top_srcdir)/coreutils
+
+COREUTILS-y:=
+COREUTILS-$(CONFIG_BASENAME)   += basename.o
+COREUTILS-$(CONFIG_CAL)        += cal.o
+COREUTILS-$(CONFIG_CAT)        += cat.o
+COREUTILS-$(CONFIG_CHGRP)      += chgrp.o
+COREUTILS-$(CONFIG_CHMOD)      += chmod.o
+COREUTILS-$(CONFIG_CHOWN)      += chown.o
+COREUTILS-$(CONFIG_CHROOT)     += chroot.o
+COREUTILS-$(CONFIG_CMP)        += cmp.o
+COREUTILS-$(CONFIG_CP)         += cp.o
+COREUTILS-$(CONFIG_CUT)        += cut.o
+COREUTILS-$(CONFIG_DATE)       += date.o
+COREUTILS-$(CONFIG_DD)         += dd.o
+COREUTILS-$(CONFIG_DF)         += df.o
+COREUTILS-$(CONFIG_DIRNAME)    += dirname.o
+COREUTILS-$(CONFIG_DOS2UNIX)   += dos2unix.o
+COREUTILS-$(CONFIG_DU)         += du.o
+COREUTILS-$(CONFIG_ECHO)       += echo.o
+COREUTILS-$(CONFIG_ENV)        += env.o
+COREUTILS-$(CONFIG_EXPR)       += expr.o
+COREUTILS-$(CONFIG_FALSE)      += false.o
+COREUTILS-$(CONFIG_FOLD)       += fold.o
+COREUTILS-$(CONFIG_HEAD)       += head.o
+COREUTILS-$(CONFIG_HOSTID)     += hostid.o
+COREUTILS-$(CONFIG_ID)         += id.o
+COREUTILS-$(CONFIG_INSTALL)    += install.o
+COREUTILS-$(CONFIG_LENGTH)     += length.o
+COREUTILS-$(CONFIG_LN)         += ln.o
+COREUTILS-$(CONFIG_LOGNAME)    += logname.o
+COREUTILS-$(CONFIG_LS)         += ls.o
+COREUTILS-$(CONFIG_MD5SUM)     += md5_sha1_sum.o
+COREUTILS-$(CONFIG_MKDIR)      += mkdir.o
+COREUTILS-$(CONFIG_MKFIFO)     += mkfifo.o
+COREUTILS-$(CONFIG_MKNOD)      += mknod.o
+COREUTILS-$(CONFIG_MV)         += mv.o
+COREUTILS-$(CONFIG_OD)         += od.o
+COREUTILS-$(CONFIG_PRINTF)     += printf.o
+COREUTILS-$(CONFIG_PWD)        += pwd.o
+COREUTILS-$(CONFIG_REALPATH)   += realpath.o
+COREUTILS-$(CONFIG_RM)         += rm.o
+COREUTILS-$(CONFIG_RMDIR)      += rmdir.o
+COREUTILS-$(CONFIG_SEQ)        += seq.o
+COREUTILS-$(CONFIG_SHA1SUM)    += md5_sha1_sum.o
+COREUTILS-$(CONFIG_SLEEP)      += sleep.o
+COREUTILS-$(CONFIG_SORT)       += sort.o
+COREUTILS-$(CONFIG_STTY)       += stty.o
+COREUTILS-$(CONFIG_SYNC)       += sync.o
+COREUTILS-$(CONFIG_TAIL)       += tail.o
+COREUTILS-$(CONFIG_TEE)        += tee.o
+COREUTILS-$(CONFIG_TEST)       += test.o
+COREUTILS-$(CONFIG_TOUCH)      += touch.o
+COREUTILS-$(CONFIG_TR)         += tr.o
+COREUTILS-$(CONFIG_TRUE)       += true.o
+COREUTILS-$(CONFIG_TTY)        += tty.o
+COREUTILS-$(CONFIG_UNAME)      += uname.o
+COREUTILS-$(CONFIG_UNIQ)       += uniq.o
+COREUTILS-$(CONFIG_USLEEP)     += usleep.o
+COREUTILS-$(CONFIG_UUDECODE)   += uudecode.o
+COREUTILS-$(CONFIG_UUENCODE)   += uuencode.o
+COREUTILS-$(CONFIG_WATCH)      += watch.o
+COREUTILS-$(CONFIG_WC)         += wc.o
+COREUTILS-$(CONFIG_WHO)        += who.o
+COREUTILS-$(CONFIG_WHOAMI)     += whoami.o
+COREUTILS-$(CONFIG_YES)        += yes.o
+
+libraries-y+=$(COREUTILS_DIR)$(COREUTILS_AR)
+
+$(COREUTILS_DIR)$(COREUTILS_AR): $(patsubst %,$(COREUTILS_DIR)%, $(COREUTILS-y))
+       $(AR) -ro $@ $(patsubst %,$(COREUTILS_DIR)%, $(COREUTILS-y))
+
+$(COREUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/coreutils/basename.c b/busybox/coreutils/basename.c
new file mode 100644 (file)
index 0000000..7b8b7b6
--- /dev/null
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini basename implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
+
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Changes:
+ * 1) Now checks for too many args.  Need at least one and at most two.
+ * 2) Don't check for options, as per SUSv3.
+ * 3) Save some space by using strcmp().  Calling strncmp() here was silly.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "busybox.h"
+
+extern int basename_main(int argc, char **argv)
+{
+       size_t m, n;
+       char *s;
+
+       if (((unsigned int)(argc-2)) >= 2) {
+               bb_show_usage();
+       }
+
+       s = bb_get_last_path_component(*++argv);
+
+       if (*++argv) {
+               n = strlen(*argv);
+               m = strlen(s);
+               if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) {
+                       s[m-n] = '\0';
+               }
+       }
+
+       puts(s);
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/cal.c b/busybox/coreutils/cal.c
new file mode 100644 (file)
index 0000000..93c5692
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Calendar implementation for busybox
+ *
+ * See original copyright at the end of this file
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
+/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect.  The upstream
+ * BB_AUDIT BUG: version in util-linux seems to be broken as well. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Major size reduction... over 50% (>1.5k) on i386.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "busybox.h"
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#include <locale.h>
+#endif
+
+#define        THURSDAY                4               /* for reformation */
+#define        SATURDAY                6               /* 1 Jan 1 was a Saturday */
+
+#define        FIRST_MISSING_DAY       639787          /* 3 Sep 1752 */
+#define        NUMBER_MISSING_DAYS     11              /* 11 day correction */
+
+#define        MAXDAYS                 42              /* max slots in a month array */
+#define        SPACE                   -1              /* used in day array */
+
+static const char days_in_month[] = {
+       0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static const char sep1752[] = {
+                1,     2,      14,     15,     16,
+       17,     18,     19,     20,     21,     22,     23,
+       24,     25,     26,     27,     28,     29,     30
+};
+
+static int julian;
+
+/* leap year -- account for Gregorian reformation in 1752 */
+#define        leap_year(yr) \
+       ((yr) <= 1752 ? !((yr) % 4) : \
+       (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
+
+static int is_leap_year(int year)
+{
+       return leap_year(year);
+}
+#undef leap_year
+#define leap_year(yr) is_leap_year(yr)
+
+/* number of centuries since 1700, not inclusive */
+#define        centuries_since_1700(yr) \
+       ((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define        quad_centuries_since_1700(yr) \
+       ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define        leap_years_since_year_1(yr) \
+       ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+static void center __P((char *, int, int));
+static void day_array __P((int, int, int *));
+static void trim_trailing_spaces_and_print __P((char *));
+
+static void blank_string(char *buf, size_t buflen);
+static char *build_row(char *p, int *dp);
+
+#define        DAY_LEN         3               /* 3 spaces per day */
+#define        J_DAY_LEN       (DAY_LEN + 1)
+#define        WEEK_LEN        20              /* 7 * 3 - one space at the end */
+#define        J_WEEK_LEN      (WEEK_LEN + 7)
+#define        HEAD_SEP        2               /* spaces between day headings */
+
+int cal_main(int argc, char **argv)
+{
+       struct tm *local_time;
+       struct tm zero_tm;
+       time_t now;
+       int month, year, flags, i;
+       char *month_names[12];
+       char day_headings[28];  /* 28 for julian, 21 for nonjulian */
+       char buf[40];
+
+#ifdef CONFIG_LOCALE_SUPPORT
+       setlocale(LC_TIME, "");
+#endif
+
+       flags = bb_getopt_ulflags(argc, argv, "jy");
+
+       julian = flags & 1;
+
+       argv += optind;
+
+       month = 0;
+
+       if ((argc -= optind) > 2) {
+               bb_show_usage();
+       }
+
+       if (!argc) {
+               time(&now);
+               local_time = localtime(&now);
+               year = local_time->tm_year + 1900;
+               if (!(flags & 2)) {
+                       month = local_time->tm_mon + 1;
+               }
+       } else {
+               if (argc == 2) {
+                       month = bb_xgetularg10_bnd(*argv++, 1, 12);
+               }
+               year = bb_xgetularg10_bnd(*argv, 1, 9999);
+       }
+
+       blank_string(day_headings, sizeof(day_headings) - 7 +  7*julian);
+
+       i = 0;
+       do {
+               zero_tm.tm_mon = i;
+               strftime(buf, sizeof(buf), "%B", &zero_tm);
+               month_names[i] = bb_xstrdup(buf);
+
+               if (i < 7) {
+                       zero_tm.tm_wday = i;
+                       strftime(buf, sizeof(buf), "%a", &zero_tm);
+                       strncpy(day_headings + i * (3+julian) + julian, buf, 2);
+               }
+       } while (++i < 12);
+
+       if (month) {
+               int row, len, days[MAXDAYS];
+               int *dp = days;
+               char lineout[30];
+
+               day_array(month, year, dp);
+               len = sprintf(lineout, "%s %d", month_names[month - 1], year);
+               bb_printf("%*s%s\n%s\n",
+                          ((7*julian + WEEK_LEN) - len) / 2, "",
+                          lineout, day_headings);
+               for (row = 0; row < 6; row++) {
+                       build_row(lineout, dp)[0] = '\0';
+                       dp += 7;
+                       trim_trailing_spaces_and_print(lineout);
+               }
+       } else {
+               int row, which_cal, week_len, days[12][MAXDAYS];
+               int *dp;
+               char lineout[80];
+
+               sprintf(lineout, "%d", year);
+               center(lineout,
+                          (WEEK_LEN * 3 + HEAD_SEP * 2)
+                          + julian * (J_WEEK_LEN * 2 + HEAD_SEP
+                                                  - (WEEK_LEN * 3 + HEAD_SEP * 2)),
+                          0);
+               puts("\n");             /* two \n's */
+               for (i = 0; i < 12; i++) {
+                       day_array(i + 1, year, days[i]);
+               }
+               blank_string(lineout, sizeof(lineout));
+               week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN);
+               for (month = 0; month < 12; month += 3-julian) {
+                       center(month_names[month], week_len, HEAD_SEP);
+                       if (!julian) {
+                               center(month_names[month + 1], week_len, HEAD_SEP);
+                       }
+                       center(month_names[month + 2 - julian], week_len, 0);
+                       bb_printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings);
+                       if (!julian) {
+                               bb_printf("%*s%s", HEAD_SEP, "", day_headings);
+                       }
+                       putchar('\n');
+                       for (row = 0; row < (6*7); row += 7) {
+                               for (which_cal = 0; which_cal < 3-julian; which_cal++) {
+                                       dp = days[month + which_cal] + row;
+                                       build_row(lineout + which_cal * (week_len + 2), dp);
+                               }
+                               /* blank_string took care of nul termination. */
+                               trim_trailing_spaces_and_print(lineout);
+                       }
+               }
+       }
+
+       bb_fflush_stdout_and_exit(0);
+}
+
+/*
+ * day_array --
+ *     Fill in an array of 42 integers with a calendar.  Assume for a moment
+ *     that you took the (maximum) 6 rows in a calendar and stretched them
+ *     out end to end.  You would have 42 numbers or spaces.  This routine
+ *     builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+static void day_array(int month, int year, int *days)
+{
+       long temp;
+       int i;
+       int j_offset;
+       int day, dw, dm;
+
+       memset(days, SPACE, MAXDAYS * sizeof(int));
+
+       if ((month == 9) && (year == 1752)) {
+               j_offset = julian * 244;
+               i = 0;
+               do {
+                       days[i+2] = sep1752[i] + j_offset;
+               } while (++i < sizeof(sep1752));
+
+               return;
+       }
+
+       /* day_in_year
+        *      return the 1 based day number within the year
+        */
+       day = 1;
+       if ((month > 2) && leap_year(year)) {
+               ++day;
+       }
+
+       i = month;
+       while (i) {
+               day += days_in_month[--i];
+       }
+
+       /* day_in_week
+        *      return the 0 based day number for any date from 1 Jan. 1 to
+        *      31 Dec. 9999.  Assumes the Gregorian reformation eliminates
+        *      3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all
+        *      missing days.
+        */
+       dw = THURSDAY;
+       temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+               + day;
+       if (temp < FIRST_MISSING_DAY) {
+               dw = ((temp - 1 + SATURDAY) % 7);
+       } else if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) {
+               dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+       }
+
+       if (!julian) {
+               day = 1;
+       }
+
+       dm = days_in_month[month];
+       if ((month == 2) && leap_year(year)) {
+               ++dm;
+       }
+
+       while (dm) {
+               days[dw++] = day++;
+               --dm;
+       }
+}
+
+static void trim_trailing_spaces_and_print(char *s)
+{
+       char *p = s;
+
+       while (*p) {
+               ++p;
+       }
+       while (p > s) {
+               --p;
+               if (!(isspace)(*p)) {   /* We want the function... not the inline. */
+                       p[1] = '\0';
+                       break;
+               }
+       }
+
+       puts(s);
+}
+
+static void center(char *str, int len, int separate)
+{
+       int n = strlen(str);
+       len -= n;
+       bb_printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, "");
+}
+
+static void blank_string(char *buf, size_t buflen)
+{
+       memset(buf, ' ', buflen);
+       buf[buflen-1] = '\0';
+}
+
+static char *build_row(char *p, int *dp)
+{
+       int col, val, day;
+
+       memset(p, ' ', (julian + DAY_LEN) * 7);
+
+       col = 0;
+       do {
+               if ((day = *dp++) != SPACE) {
+                       if (julian) {
+                               ++p;
+                               if (day >= 100) {
+                                       *p = '0';
+                                       p[-1] = (day / 100) + '0';
+                                       day %= 100;
+                               }
+                       }
+                       if ((val = day / 10) > 0) {
+                               *p = val + '0';
+                       }
+                       *++p = day % 10 + '0';
+                       p += 2;
+               } else {
+                       p += DAY_LEN + julian;
+               }
+       } while (++col < 7);
+
+       return p;
+}
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
diff --git a/busybox/coreutils/cat.c b/busybox/coreutils/cat.c
new file mode 100644 (file)
index 0000000..62af6c5
--- /dev/null
@@ -0,0 +1,67 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cat implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * This is a new implementation of 'cat' which aims to be SUSv3 compliant.
+ *
+ * Changes from the previous implementation include:
+ * 1) Multiple '-' args are accepted as required by SUSv3.  The previous
+ *    implementation would close stdin and segfault on a subsequent '-'.
+ * 2) The '-u' options is required by SUSv3.  Note that the specified
+ *    behavior for '-u' is done by default, so all we need do is accept
+ *    the option.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int cat_main(int argc, char **argv)
+{
+       FILE *f;
+       int retval = EXIT_SUCCESS;
+
+       bb_getopt_ulflags(argc, argv, "u");
+
+       argv += optind;
+       if (!*argv) {
+               *--argv = "-";
+       }
+
+       do {
+               if ((f = bb_wfopen_input(*argv)) != NULL) {
+                       int r = bb_copyfd_eof(fileno(f), STDOUT_FILENO);
+                       bb_fclose_nonstdin(f);
+                       if (r >= 0) {
+                               continue;
+                       }
+               }
+               retval = EXIT_FAILURE;
+       } while (*++argv);
+
+       return retval;
+}
diff --git a/busybox/coreutils/chgrp.c b/busybox/coreutils/chgrp.c
new file mode 100644 (file)
index 0000000..8cfb542
--- /dev/null
@@ -0,0 +1,81 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chgrp implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 defects - unsupported options -h, -H, -L, and -P. */
+/* BB_AUDIT GNU defects - unsupported options -h, -c, -f, -v, and long options. */
+/* BB_AUDIT Note: gnu chgrp does not support -H, -L, or -P. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+/* Don't use lchown glibc older then 2.1.x */
+#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
+#define lchown chown
+#endif
+
+static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
+{
+       if (lchown(fileName, statbuf->st_uid, *((long *) junk)) == 0) {
+               return (TRUE);
+       }
+       bb_perror_msg("%s", fileName);  /* Avoid multibyte problems. */
+       return (FALSE);
+}
+
+int chgrp_main(int argc, char **argv)
+{
+       long gid;
+       int recursiveFlag;
+       int retval = EXIT_SUCCESS;
+
+       recursiveFlag = bb_getopt_ulflags(argc, argv, "R");
+
+       if (argc - optind < 2) {
+               bb_show_usage();
+       }
+
+       argv += optind;
+
+       /* Find the selected group */
+       gid = get_ug_id(*argv, my_getgrnam);
+       ++argv;
+
+       /* Ok, ready to do the deed now */
+       do {
+               if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE,
+                                                               fileAction, fileAction, &gid)) {
+                       retval = EXIT_FAILURE;
+               }
+       } while (*++argv);
+
+       return retval;
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/coreutils/chmod.c b/busybox/coreutils/chmod.c
new file mode 100644 (file)
index 0000000..0cb8886
--- /dev/null
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
+ *  to correctly parse '-rwxgoa'
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU defects - unsupported options -c, -f, -v, and long options. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "busybox.h"
+
+static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
+{
+       if (!bb_parse_mode((char *)junk, &(statbuf->st_mode)))
+               bb_error_msg_and_die( "invalid mode: %s", (char *)junk);
+       if (chmod(fileName, statbuf->st_mode) == 0)
+               return (TRUE);
+       bb_perror_msg("%s", fileName);  /* Avoid multibyte problems. */
+       return (FALSE);
+}
+
+int chmod_main(int argc, char **argv)
+{
+       int retval = EXIT_SUCCESS;
+       int recursiveFlag = FALSE;
+       int count;
+       char *smode;
+       char **p;
+       char *p0;
+       char opt = '-';
+
+       ++argv;
+       count = 0;
+
+       for (p = argv  ; *p ; p++) {
+               p0 = p[0];
+               if (p0[0] == opt) {
+                       if ((p0[1] == '-') && !p0[2]) {
+                               opt = 0;        /* Disable further option processing. */
+                               continue;
+                       }
+                       if (p0[1] == 'R') {
+                               char *s = p0 + 2;
+                               while (*s == 'R') {
+                                       ++s;
+                               }
+                               if (*s) {
+                                       bb_show_usage();
+                               }
+                               recursiveFlag = TRUE;
+                               continue;
+                       }
+                       if (count) {
+                               bb_show_usage();
+                       }
+               }
+               argv[count] = p0;
+               ++count;
+       }
+
+       argv[count] = NULL;
+
+       if (count < 2) {
+               bb_show_usage();
+       }
+
+       smode = *argv;
+       ++argv;
+
+       /* Ok, ready to do the deed now */
+       do {
+               if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE,
+                                                               fileAction,     fileAction, smode)) {
+                       retval = EXIT_FAILURE;
+               }
+       } while (*++argv);
+
+       return retval;
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/coreutils/chown.c b/busybox/coreutils/chown.c
new file mode 100644 (file)
index 0000000..638745f
--- /dev/null
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chown implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 defects - unsupported options -h, -H, -L, and -P. */
+/* BB_AUDIT GNU defects - unsupported options -h, -c, -f, -v, and long options. */
+/* BB_AUDIT Note: gnu chown does not support -H, -L, or -P. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "busybox.h"
+
+/* Don't use lchown for glibc older then 2.1.x */
+#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
+#define lchown chown
+#endif
+
+static long uid;
+static long gid;
+
+static int (*chown_func)(const char *, uid_t, gid_t) = chown;
+
+static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
+{
+       if (chown_func(fileName, uid, (gid == -1) ? statbuf->st_gid : gid) == 0) {
+               chmod(fileName, statbuf->st_mode);
+               return (TRUE);
+       }
+       bb_perror_msg("%s", fileName);  /* Avoid multibyte problems. */
+       return (FALSE);
+}
+
+#define FLAG_R 1
+#define FLAG_h 2
+
+int chown_main(int argc, char **argv)
+{
+       int flags;
+       int retval = EXIT_SUCCESS;
+       char *groupName;
+
+       flags = bb_getopt_ulflags(argc, argv, "Rh");
+
+       if (flags & FLAG_h) chown_func = lchown;
+
+       if (argc - optind < 2) {
+               bb_show_usage();
+       }
+
+       argv += optind;
+
+       /* First, check if there is a group name here */
+       if ((groupName = strchr(*argv, '.')) == NULL) {
+               groupName = strchr(*argv, ':');
+       }
+
+       gid = -1;
+       if (groupName) {
+               *groupName++ = '\0';
+               gid = get_ug_id(groupName, my_getgrnam);
+       }
+
+       /* Now check for the username */
+       uid = get_ug_id(*argv, my_getpwnam);
+
+       ++argv;
+
+       /* Ok, ready to do the deed now */
+       do {
+               if (! recursive_action (*argv, (flags & FLAG_R), FALSE, FALSE,
+                                                               fileAction, fileAction, NULL)) {
+                       retval = EXIT_FAILURE;
+               }
+       } while (*++argv);
+
+       return retval;
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/coreutils/chroot.c b/busybox/coreutils/chroot.c
new file mode 100644 (file)
index 0000000..6225702
--- /dev/null
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chroot implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include "busybox.h"
+
+int chroot_main(int argc, char **argv)
+{
+       if (argc < 2) {
+               bb_show_usage();
+       }
+
+       ++argv;
+       if (chroot(*argv) || (chdir("/"))) {
+               bb_perror_msg_and_die("cannot change root directory to %s", *argv);
+       }
+
+       ++argv;
+       if (argc == 2) {
+               argv -= 2;
+               if (!(*argv = getenv("SHELL"))) {
+                       *argv = (char *) DEFAULT_SHELL;
+               }
+               argv[1] = (char *) "-i";
+       }
+
+       execvp(*argv, argv);
+       bb_perror_msg_and_die("cannot execute %s", *argv);
+}
diff --git a/busybox/coreutils/cmp.c b/busybox/coreutils/cmp.c
new file mode 100644 (file)
index 0000000..d0fc662
--- /dev/null
@@ -0,0 +1,152 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cmp implementation for busybox
+ *
+ * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Original version majorly reworked for SUSv3 compliance, bug fixes, and
+ * size optimizations.  Changes include:
+ * 1) Now correctly distinguishes between errors and actual file differences.
+ * 2) Proper handling of '-' args.
+ * 3) Actual error checking of i/o.
+ * 4) Accept SUSv3 -l option.  Note that we use the slightly nicer gnu format
+ *    in the '-l' case.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+static FILE *cmp_xfopen_input(const char *filename)
+{
+       FILE *fp;
+
+       if ((fp = bb_wfopen_input(filename)) != NULL) {
+               return fp;
+       }
+
+       exit(bb_default_error_retval);  /* We already output an error message. */
+}
+
+static const char fmt_eof[] = "cmp: EOF on %s\n";
+static const char fmt_differ[] = "%s %s differ: char %d, line %d\n";
+#if 0
+static const char fmt_l_opt[] = "%.0s%.0s%d %o %o\n";  /* SUSv3 format */
+#else
+static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n";        /* nicer gnu format */
+#endif
+
+static const char opt_chars[] = "sl";
+
+enum {
+       OPT_s = 1,
+       OPT_l = 2
+};
+
+int cmp_main(int argc, char **argv)
+{
+       FILE *fp1, *fp2, *outfile = stdout;
+       const char *filename1, *filename2;
+       const char *fmt;
+       int c1, c2, char_pos, line_pos;
+       int opt_flags;
+       int exit_val = 0;
+
+       bb_default_error_retval = 2;    /* 1 is returned if files are different. */
+
+       opt_flags = bb_getopt_ulflags(argc, argv, opt_chars);
+
+       if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > 1)) {
+               bb_show_usage();
+       }
+
+       fp1 = cmp_xfopen_input(filename1 = *(argv += optind));
+
+       filename2 = "-";
+       if (*++argv) {
+               filename2 = *argv;
+       }
+       fp2 = cmp_xfopen_input(filename2);
+
+       if (fp1 == fp2) {                       /* Paranioa check... stdin == stdin? */
+               /* Note that we don't bother reading stdin.  Neither does gnu wc.
+                * But perhaps we should, so that other apps down the chain don't
+                * get the input.  Consider 'echo hello | (cmp - - && cat -)'.
+                */
+               return 0;
+       }
+
+       fmt = fmt_differ;
+       if (opt_flags == OPT_l) {
+               fmt = fmt_l_opt;
+       }
+
+       char_pos = 0;
+       line_pos = 1;
+       do {
+               c1 = getc(fp1);
+               c2 = getc(fp2);
+               ++char_pos;
+               if (c1 != c2) {                 /* Remember -- a read error may have occurred. */
+                       exit_val = 1;           /* But assume the files are different for now. */
+                       if (c2 == EOF) {
+                               /* We know that fp1 isn't at EOF or in an error state.  But to
+                                * save space below, things are setup to expect an EOF in fp1
+                                * if an EOF occurred.  So, swap things around.
+                                */
+                               fp1 = fp2;
+                               filename1 = filename2;
+                               c1 = c2;
+                       }
+                       if (c1 == EOF) {
+                               bb_xferror(fp1, filename1);
+                               fmt = fmt_eof;  /* Well, no error, so it must really be EOF. */
+                               outfile = stderr;
+                               /* There may have been output to stdout (option -l), so
+                                * make sure we fflush before writing to stderr. */
+                               bb_xfflush_stdout();
+                       }
+                       if (opt_flags != OPT_s) {
+                               if (opt_flags == OPT_l) {
+                                       line_pos = c1;  /* line_pos is unused in the -l case. */
+                               }
+                               bb_fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
+                               if (opt_flags) {        /* This must be -l since not -s. */
+                                       /* If we encountered and EOF, the while check will catch it. */
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               if (c1 == '\n') {
+                       ++line_pos;
+               }
+       } while (c1 != EOF);
+
+       bb_xferror(fp1, filename1);
+       bb_xferror(fp2, filename2);
+
+       bb_fflush_stdout_and_exit(exit_val);
+}
diff --git a/busybox/coreutils/cp.c b/busybox/coreutils/cp.c
new file mode 100644 (file)
index 0000000..6a82f6b
--- /dev/null
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cp implementation for busybox
+ *
+ * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */
+/* BB_AUDIT GNU defects - only extension options supported are -a and -d.  */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <errno.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+/* WARNING!! ORDER IS IMPORTANT!! */
+static const char cp_opts[] = "pdRfiar";
+
+extern int cp_main(int argc, char **argv)
+{
+       struct stat source_stat;
+       struct stat dest_stat;
+       const char *last;
+       const char *dest;
+       int s_flags;
+       int d_flags;
+       int flags;
+       int status = 0;
+
+       /* Since these are enums, #if tests will not work.  So use assert()s. */
+       assert(FILEUTILS_PRESERVE_STATUS == 1);
+       assert(FILEUTILS_DEREFERENCE == 2);
+       assert(FILEUTILS_RECUR == 4);
+       assert(FILEUTILS_FORCE == 8);
+       assert(FILEUTILS_INTERACTIVE == 16);
+
+       flags = bb_getopt_ulflags(argc, argv, cp_opts);
+
+       if (flags & 32) {
+               flags |= (FILEUTILS_PRESERVE_STATUS | FILEUTILS_RECUR | FILEUTILS_DEREFERENCE);
+       }
+       if (flags & 64) {
+               /* Make -r a synonym for -R,
+                * -r was marked as obsolete in SUSv3, but is included for compatability
+                */
+               flags |= FILEUTILS_RECUR;
+       }
+
+       flags ^= FILEUTILS_DEREFERENCE;         /* The sense of this flag was reversed. */
+
+       if (optind + 2 > argc) {
+               bb_show_usage();
+       }
+
+       last = argv[argc - 1];
+       argv += optind;
+
+       /* If there are only two arguments and...  */
+       if (optind + 2 == argc) {
+               s_flags = cp_mv_stat2(*argv, &source_stat,
+                                                                (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
+               if ((s_flags < 0) || ((d_flags = cp_mv_stat(last, &dest_stat)) < 0)) {
+                       exit(EXIT_FAILURE);
+               }
+               /* ...if neither is a directory or...  */
+               if ( !((s_flags | d_flags) & 2) ||
+                       /* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */
+                       /* ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) */
+                       /* Simplify the above since FILEUTILS_RECUR >> 1 == 2. */
+                       ((((flags & FILEUTILS_RECUR) >> 1) & s_flags) && !d_flags)
+               ) {
+                       /* ...do a simple copy.  */
+                               dest = last;
+                               goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */
+               }
+       }
+
+       do {
+               dest = concat_path_file(last, bb_get_last_path_component(*argv));
+       DO_COPY:
+               if (copy_file(*argv, dest, flags) < 0) {
+                       status = 1;
+               }
+               if (*++argv == last) {
+                       break;
+               }
+               free((void *) dest);
+       } while (1);
+
+       exit(status);
+}
diff --git a/busybox/coreutils/cut.c b/busybox/coreutils/cut.c
new file mode 100644 (file)
index 0000000..d26e80e
--- /dev/null
@@ -0,0 +1,344 @@
+/* vi: set sw=8 ts=8: */
+/*
+ * cut.c - minimalist version of cut
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc.
+ * Written by Mark Whitley <markw@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include "busybox.h"
+
+
+/* option vars */
+static const char optstring[] = "b:c:f:d:sn";
+#define OPT_BYTE_FLGS    1
+#define OPT_CHAR_FLGS    2
+#define OPT_FIELDS_FLGS  4
+#define OPT_DELIM_FLGS   8
+#define OPT_SUPRESS_FLGS 16
+static char part; /* (b)yte, (c)har, (f)ields */
+static unsigned int supress_non_delimited_lines;
+static char delim = '\t'; /* delimiter, default is tab */
+
+struct cut_list {
+       int startpos;
+       int endpos;
+};
+
+static const int BOL = 0;
+static const int EOL = INT_MAX;
+static const int NON_RANGE = -1;
+
+static struct cut_list *cut_lists = NULL; /* growable array holding a series of lists */
+static unsigned int nlists = 0; /* number of elements in above list */
+
+
+static int cmpfunc(const void *a, const void *b)
+{
+       struct cut_list *la = (struct cut_list *)a;
+       struct cut_list *lb = (struct cut_list *)b;
+
+       if (la->startpos > lb->startpos)
+               return 1;
+       if (la->startpos < lb->startpos)
+               return -1;
+       return 0;
+}
+
+
+/*
+ * parse_lists() - parses a list and puts values into startpos and endpos.
+ * valid list formats: N, N-, N-M, -M
+ * more than one list can be separated by commas
+ */
+static void parse_lists(char *lists)
+{
+       char *ltok = NULL;
+       char *ntok = NULL;
+       char *junk;
+       int s = 0, e = 0;
+
+       /* take apart the lists, one by one (they are separated with commas */
+       while ((ltok = strsep(&lists, ",")) != NULL) {
+
+               /* it's actually legal to pass an empty list */
+               if (strlen(ltok) == 0)
+                       continue;
+
+               /* get the start pos */
+               ntok = strsep(&ltok, "-");
+               if (ntok == NULL) {
+                       fprintf(stderr, "Help ntok is null for starting position! What do I do?\n");
+               } else if (strlen(ntok) == 0) {
+                       s = BOL;
+               } else {
+                       s = strtoul(ntok, &junk, 10);
+                       if(*junk != '\0' || s < 0)
+                               bb_error_msg_and_die("invalid byte or field list");
+
+                       /* account for the fact that arrays are zero based, while the user
+                        * expects the first char on the line to be char # 1 */
+                       if (s != 0)
+                               s--;
+               }
+
+               /* get the end pos */
+               ntok = strsep(&ltok, "-");
+               if (ntok == NULL) {
+                       e = NON_RANGE;
+               } else if (strlen(ntok) == 0) {
+                       e = EOL;
+               } else {
+                       e = strtoul(ntok, &junk, 10);
+                       if(*junk != '\0' || e < 0)
+                               bb_error_msg_and_die("invalid byte or field list");
+                       /* if the user specified and end position of 0, that means "til the
+                        * end of the line */
+                       if (e == 0)
+                               e = INT_MAX;
+                       e--; /* again, arrays are zero based, lines are 1 based */
+                       if (e == s)
+                               e = NON_RANGE;
+               }
+
+               /* if there's something left to tokenize, the user past an invalid list */
+               if (ltok)
+                       bb_error_msg_and_die("invalid byte or field list");
+
+               /* add the new list */
+               cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists));
+               cut_lists[nlists-1].startpos = s;
+               cut_lists[nlists-1].endpos = e;
+       }
+
+       /* make sure we got some cut positions out of all that */
+       if (nlists == 0)
+               bb_error_msg_and_die("missing list of positions");
+
+       /* now that the lists are parsed, we need to sort them to make life easier
+        * on us when it comes time to print the chars / fields / lines */
+       qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc);
+
+}
+
+
+static void cut_line_by_chars(const char *line)
+{
+       int c, l;
+       /* set up a list so we can keep track of what's been printed */
+       char *printed = xcalloc(strlen(line), sizeof(char));
+
+       /* print the chars specified in each cut list */
+       for (c = 0; c < nlists; c++) {
+               l = cut_lists[c].startpos;
+               while (l < strlen(line)) {
+                       if (!printed[l]) {
+                               putchar(line[l]);
+                               printed[l] = 'X';
+                       }
+                       l++;
+                       if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos)
+                               break;
+               }
+       }
+       putchar('\n'); /* cuz we were handed a chomped line */
+       free(printed);
+}
+
+
+static void cut_line_by_fields(char *line)
+{
+       int c, f;
+       int ndelim = -1; /* zero-based / one-based problem */
+       int nfields_printed = 0;
+       char *field = NULL;
+       char d[2] = { delim, 0 };
+       char *printed;
+
+       /* test the easy case first: does this line contain any delimiters? */
+       if (strchr(line, delim) == NULL) {
+               if (!supress_non_delimited_lines)
+                       puts(line);
+               return;
+       }
+
+       /* set up a list so we can keep track of what's been printed */
+       printed = xcalloc(strlen(line), sizeof(char));
+
+       /* process each list on this line, for as long as we've got a line to process */
+       for (c = 0; c < nlists && line; c++) {
+               f = cut_lists[c].startpos;
+               do {
+
+                       /* find the field we're looking for */
+                       while (line && ndelim < f) {
+                               field = strsep(&line, d);
+                               ndelim++;
+                       }
+
+                       /* we found it, and it hasn't been printed yet */
+                       if (field && ndelim == f && !printed[ndelim]) {
+                               /* if this isn't our first time through, we need to print the
+                                * delimiter after the last field that was printed */
+                               if (nfields_printed > 0)
+                                       putchar(delim);
+                               fputs(field, stdout);
+                               printed[ndelim] = 'X';
+                               nfields_printed++;
+                       }
+
+                       f++;
+
+                       /* keep going as long as we have a line to work with, this is a
+                        * list, and we're not at the end of that list */
+               } while (line && cut_lists[c].endpos != NON_RANGE && f <= cut_lists[c].endpos);
+       }
+
+       /* if we printed anything at all, we need to finish it with a newline cuz
+        * we were handed a chomped line */
+       putchar('\n');
+
+       free(printed);
+}
+
+
+static void cut_file_by_lines(const char *line, unsigned int linenum)
+{
+       static int c = 0;
+       static int l = -1;
+
+       /* I can't initialize this above cuz the "initializer isn't
+        * constant" *sigh* */
+       if (l == -1)
+               l = cut_lists[c].startpos;
+
+       /* get out if we have no more lists to process or if the lines are lower
+        * than what we're interested in */
+       if (c >= nlists || linenum < l)
+               return;
+
+       /* if the line we're looking for is lower than the one we were passed, it
+        * means we displayed it already, so move on */
+       while (l < linenum) {
+               l++;
+               /* move on to the next list if we're at the end of this one */
+               if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos) {
+                       c++;
+                       /* get out if there's no more lists to process */
+                       if (c >= nlists)
+                               return;
+                       l = cut_lists[c].startpos;
+                       /* get out if the current line is lower than the one we just became
+                        * interested in */
+                       if (linenum < l)
+                               return;
+               }
+       }
+
+       /* If we made it here, it means we've found the line we're looking for, so print it */
+       puts(line);
+}
+
+
+/*
+ * snippy-snip
+ */
+static void cut_file(FILE *file)
+{
+       char *line = NULL;
+       unsigned int linenum = 0; /* keep these zero-based to be consistent */
+
+       /* go through every line in the file */
+       while ((line = bb_get_chomped_line_from_file(file)) != NULL) {
+
+               /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */
+               if ((part & (OPT_CHAR_FLGS | OPT_BYTE_FLGS)))
+                       cut_line_by_chars(line);
+
+               /* cut based on fields */
+               else {
+                       if (delim == '\n')
+                               cut_file_by_lines(line, linenum);
+                       else
+                               cut_line_by_fields(line);
+               }
+
+               linenum++;
+               free(line);
+       }
+}
+
+
+extern int cut_main(int argc, char **argv)
+{
+       unsigned long opt;
+       char *sopt, *sdopt;
+
+       bb_opt_complementaly = "b~bcf:c~bcf:f~bcf";
+       opt = bb_getopt_ulflags(argc, argv, optstring, &sopt, &sopt, &sopt, &sdopt);
+       part = opt & (OPT_BYTE_FLGS|OPT_CHAR_FLGS|OPT_FIELDS_FLGS);
+       if(part == 0)
+               bb_error_msg_and_die("you must specify a list of bytes, characters, or fields");
+       if(opt & 0x80000000UL)
+               bb_error_msg_and_die("only one type of list may be specified");
+       parse_lists(sopt);
+       if((opt & (OPT_DELIM_FLGS))) {
+               if (strlen(sdopt) > 1) {
+                       bb_error_msg_and_die("the delimiter must be a single character");
+               }
+               delim = sdopt[0];
+       }
+       supress_non_delimited_lines = opt & OPT_SUPRESS_FLGS;
+
+       /*  non-field (char or byte) cutting has some special handling */
+       if (part != OPT_FIELDS_FLGS) {
+               if (supress_non_delimited_lines) {
+                       bb_error_msg_and_die("suppressing non-delimited lines makes sense"
+                                       " only when operating on fields");
+               }
+               if (delim != '\t') {
+                       bb_error_msg_and_die("a delimiter may be specified only when operating on fields");
+               }
+       }
+
+       /* argv[(optind)..(argc-1)] should be names of file to process. If no
+        * files were specified or '-' was specified, take input from stdin.
+        * Otherwise, we process all the files specified. */
+       if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) {
+               cut_file(stdin);
+       }
+       else {
+               int i;
+               FILE *file;
+               for (i = optind; i < argc; i++) {
+                       file = bb_wfopen(argv[i], "r");
+                       if(file) {
+                               cut_file(file);
+                               fclose(file);
+                       }
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/date.c b/busybox/coreutils/date.c
new file mode 100644 (file)
index 0000000..3608df6
--- /dev/null
@@ -0,0 +1,292 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini date implementation for busybox
+ *
+ * by Matthew Grant <grantma@anathoth.gen.nz>
+ *
+ * iso-format handling added by Robert Griebl <griebl@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include "busybox.h"
+
+
+/* This 'date' command supports only 2 time setting formats,
+   all the GNU strftime stuff (its in libc, lets use it),
+   setting time using UTC and displaying int, as well as
+   an RFC 822 complient date output for shell scripting
+   mail commands */
+
+/* Input parsing code is always bulky - used heavy duty libc stuff as
+   much as possible, missed out a lot of bounds checking */
+
+/* Default input handling to save surprising some people */
+
+static struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
+{
+       int nr;
+       char *cp;
+
+       nr = sscanf(t_string, "%2d%2d%2d%2d%d", &(tm_time->tm_mon),
+                               &(tm_time->tm_mday), &(tm_time->tm_hour), &(tm_time->tm_min),
+                               &(tm_time->tm_year));
+
+       if (nr < 4 || nr > 5) {
+               bb_error_msg_and_die(bb_msg_invalid_date, t_string);
+       }
+
+       cp = strchr(t_string, '.');
+       if (cp) {
+               nr = sscanf(cp + 1, "%2d", &(tm_time->tm_sec));
+               if (nr != 1) {
+                       bb_error_msg_and_die(bb_msg_invalid_date, t_string);
+               }
+       }
+
+       /* correct for century  - minor Y2K problem here? */
+       if (tm_time->tm_year >= 1900) {
+               tm_time->tm_year -= 1900;
+       }
+       /* adjust date */
+       tm_time->tm_mon -= 1;
+
+       return (tm_time);
+
+}
+
+
+/* The new stuff for LRP */
+
+static struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
+{
+       struct tm t;
+
+       /* Parse input and assign appropriately to tm_time */
+
+       if (t =
+               *tm_time, sscanf(t_string, "%d:%d:%d", &t.tm_hour, &t.tm_min,
+                                                &t.tm_sec) == 3) {
+               /* no adjustments needed */
+       } else if (t =
+                          *tm_time, sscanf(t_string, "%d:%d", &t.tm_hour,
+                                                               &t.tm_min) == 2) {
+               /* no adjustments needed */
+       } else if (t =
+                          *tm_time, sscanf(t_string, "%d.%d-%d:%d:%d", &t.tm_mon,
+                                                               &t.tm_mday, &t.tm_hour, &t.tm_min,
+                                                               &t.tm_sec) == 5) {
+               /* Adjust dates from 1-12 to 0-11 */
+               t.tm_mon -= 1;
+       } else if (t =
+                          *tm_time, sscanf(t_string, "%d.%d-%d:%d", &t.tm_mon,
+                                                               &t.tm_mday, &t.tm_hour, &t.tm_min) == 4) {
+               /* Adjust dates from 1-12 to 0-11 */
+               t.tm_mon -= 1;
+       } else if (t =
+                          *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d:%d", &t.tm_year,
+                                                               &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min,
+                                                               &t.tm_sec) == 6) {
+               t.tm_year -= 1900;      /* Adjust years */
+               t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
+       } else if (t =
+                          *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d", &t.tm_year,
+                                                               &t.tm_mon, &t.tm_mday, &t.tm_hour,
+                                                               &t.tm_min) == 5) {
+               t.tm_year -= 1900;      /* Adjust years */
+               t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
+       } else {
+               bb_error_msg_and_die(bb_msg_invalid_date, t_string);
+       }
+       *tm_time = t;
+       return (tm_time);
+}
+
+#define DATE_OPT_RFC2822       0x01
+#define DATE_OPT_SET           0x02
+#define DATE_OPT_UTC           0x04
+#define DATE_OPT_DATE          0x08
+#define DATE_OPT_REFERENCE     0x10
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+# define DATE_OPT_TIMESPEC     0x20
+#endif
+
+int date_main(int argc, char **argv)
+{
+       char *date_str = NULL;
+       char *date_fmt = NULL;
+       char *t_buff;
+       int set_time;
+       int utc;
+       int use_arg = 0;
+       time_t tm;
+       unsigned long opt;
+       struct tm tm_time;
+       char *filename = NULL;
+
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+       int ifmt = 0;
+       char *isofmt_arg;
+
+# define GETOPT_ISOFMT  "I::"
+#else
+# define GETOPT_ISOFMT
+#endif
+       bb_opt_complementaly = "d~ds:s~ds";
+       opt = bb_getopt_ulflags(argc, argv, "Rs:ud:r:" GETOPT_ISOFMT,
+                                       &date_str, &date_str, &filename
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+                                       , &isofmt_arg
+#endif
+                                       );
+       set_time = opt & DATE_OPT_SET;
+       utc = opt & DATE_OPT_UTC;
+       if ((utc) && (putenv("TZ=UTC0") != 0)) {
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
+       }
+       use_arg = opt & DATE_OPT_DATE;
+       if(opt & 0x80000000UL)
+               bb_show_usage();
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+       if(opt & DATE_OPT_TIMESPEC) {
+               if (!isofmt_arg) {
+                       ifmt = 1;
+               } else {
+                       int ifmt_len = bb_strlen(isofmt_arg);
+
+                       if ((ifmt_len <= 4)
+                               && (strncmp(isofmt_arg, "date", ifmt_len) == 0)) {
+                               ifmt = 1;
+                       } else if ((ifmt_len <= 5)
+                                  && (strncmp(isofmt_arg, "hours", ifmt_len) == 0)) {
+                               ifmt = 2;
+                       } else if ((ifmt_len <= 7)
+                                  && (strncmp(isofmt_arg, "minutes", ifmt_len) == 0)) {
+                               ifmt = 3;
+                       } else if ((ifmt_len <= 7)
+                                  && (strncmp(isofmt_arg, "seconds", ifmt_len) == 0)) {
+                               ifmt = 4;
+                       }
+               }
+               if (!ifmt) {
+                       bb_show_usage();
+               }
+       }
+#endif
+
+       if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) {
+               date_fmt = &argv[optind][1];    /* Skip over the '+' */
+       } else if (date_str == NULL) {
+               set_time = 1;
+               date_str = argv[optind];
+       }
+
+       /* Now we have parsed all the information except the date format
+          which depends on whether the clock is being set or read */
+
+       if(filename) {
+               struct stat statbuf;
+               if(stat(filename,&statbuf))
+                       bb_perror_msg_and_die("File '%s' not found.\n",filename);
+               tm=statbuf.st_mtime;
+       } else time(&tm);
+       memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
+       /* Zero out fields - take her back to midnight! */
+       if (date_str != NULL) {
+               tm_time.tm_sec = 0;
+               tm_time.tm_min = 0;
+               tm_time.tm_hour = 0;
+
+               /* Process any date input to UNIX time since 1 Jan 1970 */
+               if (strchr(date_str, ':') != NULL) {
+                       date_conv_ftime(&tm_time, date_str);
+               } else {
+                       date_conv_time(&tm_time, date_str);
+               }
+
+               /* Correct any day of week and day of year etc. fields */
+               tm_time.tm_isdst = -1;  /* Be sure to recheck dst. */
+               tm = mktime(&tm_time);
+               if (tm < 0) {
+                       bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+               }
+               if (utc && (putenv("TZ=UTC0") != 0)) {
+                       bb_error_msg_and_die(bb_msg_memory_exhausted);
+               }
+
+               /* if setting time, set it */
+               if (set_time && (stime(&tm) < 0)) {
+                       bb_perror_msg("cannot set date");
+               }
+       }
+
+       /* Display output */
+
+       /* Deal with format string */
+       if (date_fmt == NULL) {
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+               switch (ifmt) {
+               case 4:
+                       date_fmt = utc ? "%Y-%m-%dT%H:%M:%SZ" : "%Y-%m-%dT%H:%M:%S%z";
+                       break;
+               case 3:
+                       date_fmt = utc ? "%Y-%m-%dT%H:%MZ" : "%Y-%m-%dT%H:%M%z";
+                       break;
+               case 2:
+                       date_fmt = utc ? "%Y-%m-%dT%HZ" : "%Y-%m-%dT%H%z";
+                       break;
+               case 1:
+                       date_fmt = "%Y-%m-%d";
+                       break;
+               case 0:
+               default:
+#endif
+                       date_fmt = (opt & DATE_OPT_RFC2822 ?
+                                       (utc ? "%a, %d %b %Y %H:%M:%S GMT" :
+                                       "%a, %d %b %Y %H:%M:%S %z") :
+                                       "%a %b %e %H:%M:%S %Z %Y");
+
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+                       break;
+               }
+#endif
+       } else if (*date_fmt == '\0') {
+               /* Imitate what GNU 'date' does with NO format string! */
+               printf("\n");
+               return EXIT_SUCCESS;
+       }
+
+       /* Handle special conversions */
+
+       if (strncmp(date_fmt, "%f", 2) == 0) {
+               date_fmt = "%Y.%m.%d-%H:%M:%S";
+       }
+
+       /* Print OUTPUT (after ALL that!) */
+       t_buff = xmalloc(201);
+       strftime(t_buff, 200, date_fmt, &tm_time);
+       puts(t_buff);
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/dd.c b/busybox/coreutils/dd.c
new file mode 100644 (file)
index 0000000..9a149e2
--- /dev/null
@@ -0,0 +1,203 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dd implementation for busybox
+ *
+ *
+ * Copyright (C) 2000,2001  Matt Kraai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include "busybox.h"
+
+
+static const struct suffix_mult dd_suffixes[] = {
+       { "c", 1 },
+       { "w", 2 },
+       { "b", 512 },
+       { "kD", 1000 },
+       { "k", 1024 },
+       { "MD", 1000000 },
+       { "M", 1048576 },
+       { "GD", 1000000000 },
+       { "G", 1073741824 },
+       { NULL, 0 }
+};
+
+int dd_main(int argc, char **argv)
+{
+       size_t out_full = 0;
+       size_t out_part = 0;
+       size_t in_full = 0;
+       size_t in_part = 0;
+       size_t count = -1;
+       size_t bs = 512;
+       ssize_t n;
+       off_t seek = 0;
+       off_t skip = 0;
+       int sync_flag = FALSE;
+       int noerror = FALSE;
+       int trunc_flag = TRUE;
+       int oflag;
+       int ifd;
+       int ofd;
+       int i;
+       const char *infile = NULL;
+       const char *outfile = NULL;
+       char *buf;
+
+       for (i = 1; i < argc; i++) {
+               if (strncmp("bs=", argv[i], 3) == 0)
+                       bs = bb_xparse_number(argv[i]+3, dd_suffixes);
+               else if (strncmp("count=", argv[i], 6) == 0)
+                       count = bb_xparse_number(argv[i]+6, dd_suffixes);
+               else if (strncmp("seek=", argv[i], 5) == 0)
+                       seek = bb_xparse_number(argv[i]+5, dd_suffixes);
+               else if (strncmp("skip=", argv[i], 5) == 0)
+                       skip = bb_xparse_number(argv[i]+5, dd_suffixes);
+               else if (strncmp("if=", argv[i], 3) == 0)
+                       infile = argv[i]+3;
+               else if (strncmp("of=", argv[i], 3) == 0)
+                       outfile = argv[i]+3;
+               else if (strncmp("conv=", argv[i], 5) == 0) {
+                       buf = argv[i]+5;
+                       while (1) {
+                               if (strncmp("notrunc", buf, 7) == 0) {
+                                       trunc_flag = FALSE;
+                                       buf += 7;
+                               } else if (strncmp("sync", buf, 4) == 0) {
+                                       sync_flag = TRUE;
+                                       buf += 4;
+                               } else if (strncmp("noerror", buf, 7) == 0) {
+                                       noerror = TRUE;
+                                       buf += 7;
+                               } else {
+                                       bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5);
+                               }
+                               if (buf[0] == '\0')
+                                       break;
+                               if (buf[0] == ',')
+                                       buf++;
+                       }
+               } else
+                       bb_show_usage();
+       }
+
+       buf = xmalloc(bs);
+
+       if (infile != NULL) {
+               ifd = bb_xopen(infile, O_RDONLY);
+       } else {
+               ifd = STDIN_FILENO;
+               infile = bb_msg_standard_input;
+       }
+
+       if (outfile != NULL) {
+               oflag = O_WRONLY | O_CREAT;
+
+               if (!seek && trunc_flag) {
+                       oflag |= O_TRUNC;
+               }
+
+               if ((ofd = open(outfile, oflag, 0666)) < 0) {
+                       bb_perror_msg_and_die("%s", outfile);
+               }
+
+               if (seek && trunc_flag) {
+                       if (ftruncate(ofd, seek * bs) < 0) {
+                               struct stat st;
+
+                               if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) ||
+                                               S_ISDIR (st.st_mode)) {
+                                       bb_perror_msg_and_die("%s", outfile);
+                               }
+                       }
+               }
+       } else {
+               ofd = STDOUT_FILENO;
+               outfile = bb_msg_standard_output;
+       }
+
+       if (skip) {
+               if (lseek(ifd, skip * bs, SEEK_CUR) < 0) {
+                       bb_perror_msg_and_die("%s", infile);
+               }
+       }
+
+       if (seek) {
+               if (lseek(ofd, seek * bs, SEEK_CUR) < 0) {
+                       bb_perror_msg_and_die("%s", outfile);
+               }
+       }
+
+       while (in_full + in_part != count) {
+               if (noerror) {
+                       /* Pre-zero the buffer when doing the noerror thing */
+                       memset(buf, '\0', bs);
+               }
+               n = safe_read(ifd, buf, bs);
+               if (n < 0) {
+                       if (noerror) {
+                               n = bs;
+                               bb_perror_msg("%s", infile);
+                       } else {
+                               bb_perror_msg_and_die("%s", infile);
+                       }
+               }
+               if (n == 0) {
+                       break;
+               }
+               if (n == bs) {
+                       in_full++;
+               } else {
+                       in_part++;
+               }
+               if (sync_flag) {
+                       memset(buf + n, '\0', bs - n);
+                       n = bs;
+               }
+               n = bb_full_write(ofd, buf, n);
+               if (n < 0) {
+                       bb_perror_msg_and_die("%s", outfile);
+               }
+               if (n == bs) {
+                       out_full++;
+               } else {
+                       out_part++;
+               }
+       }
+
+       if (close (ifd) < 0) {
+               bb_perror_msg_and_die("%s", infile);
+       }
+
+       if (close (ofd) < 0) {
+               bb_perror_msg_and_die("%s", outfile);
+       }
+
+       fprintf(stderr, "%ld+%ld records in\n%ld+%ld records out\n",
+                       (long)in_full, (long)in_part,
+                       (long)out_full, (long)out_part);
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/df.c b/busybox/coreutils/df.c
new file mode 100644 (file)
index 0000000..ba2e7cc
--- /dev/null
@@ -0,0 +1,170 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini df implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * based on original code by (I think) Bruce Perens <bruce@pixar.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -P and -t missing.  Also blocksize. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction.  Removed floating point dependency.  Added error checking
+ * on output.  Output stats on 0-sized filesystems if specifically listed on
+ * the command line.  Properly round *-blocks, Used, and Available quantities.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mntent.h>
+#include <sys/vfs.h>
+#include "busybox.h"
+
+#ifndef CONFIG_FEATURE_HUMAN_READABLE
+static long kscale(long b, long bs)
+{
+       return ( b * (long long) bs + KILOBYTE/2 ) / KILOBYTE;
+}
+#endif
+
+extern int df_main(int argc, char **argv)
+{
+       long blocks_used;
+       long blocks_percent_used;
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+       unsigned long df_disp_hr = KILOBYTE;
+#endif
+       int status = EXIT_SUCCESS;
+       unsigned long opt;
+       FILE *mount_table;
+       struct mntent *mount_entry;
+       struct statfs s;
+       static const char hdr_1k[] = "1k-blocks"; /* default display is kilobytes */
+       const char *disp_units_hdr = hdr_1k;
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+       bb_opt_complementaly = "h-km:k-hm:m-hk";
+       opt = bb_getopt_ulflags(argc, argv, "hmk");
+       if(opt & 1) {
+                               df_disp_hr = 0;
+                               disp_units_hdr = "     Size";
+       }
+       if(opt & 2) {
+                               df_disp_hr = MEGABYTE;
+                               disp_units_hdr = "1M-blocks";
+       }
+#else
+       opt = bb_getopt_ulflags(argc, argv, "k");
+#endif
+
+       bb_printf("Filesystem%11s%-15sUsed Available Use%% Mounted on\n",
+                         "", disp_units_hdr);
+
+       mount_table = NULL;
+       argv += optind;
+       if (optind >= argc) {
+               if (!(mount_table = setmntent(bb_path_mtab_file, "r"))) {
+                       bb_perror_msg_and_die(bb_path_mtab_file);
+               }
+       }
+
+       do {
+               const char *device;
+               const char *mount_point;
+
+               if (mount_table) {
+                       if (!(mount_entry = getmntent(mount_table))) {
+                               endmntent(mount_table);
+                               break;
+                       }
+               } else {
+                       if (!(mount_point = *argv++)) {
+                               break;
+                       }
+                       if (!(mount_entry = find_mount_point(mount_point, bb_path_mtab_file))) {
+                               bb_error_msg("%s: can't find mount point.", mount_point);
+                       SET_ERROR:
+                               status = EXIT_FAILURE;
+                               continue;
+                       }
+               }
+
+               device = mount_entry->mnt_fsname;
+               mount_point = mount_entry->mnt_dir;
+
+               if (statfs(mount_point, &s) != 0) {
+                       bb_perror_msg("%s", mount_point);
+                       goto SET_ERROR;
+               }
+
+               if ((s.f_blocks > 0) || !mount_table){
+                       blocks_used = s.f_blocks - s.f_bfree;
+                       blocks_percent_used = 0;
+                       if (blocks_used + s.f_bavail) {
+                               blocks_percent_used = (((long long) blocks_used) * 100
+                                                                          + (blocks_used + s.f_bavail)/2
+                                                                          ) / (blocks_used + s.f_bavail);
+                       }
+
+                       if (strcmp(device, "rootfs") == 0) {
+                               continue;
+                       } else if (strcmp(device, "/dev/root") == 0) {
+                               /* Adjusts device to be the real root device,
+                               * or leaves device alone if it can't find it */
+                               if ((device = find_real_root_device_name()) == NULL) {
+                                       goto SET_ERROR;
+                               }
+                       }
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+                       bb_printf("%-21s%9s ", device,
+                                         make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr));
+
+                       bb_printf("%9s ",
+                                         make_human_readable_str( (s.f_blocks - s.f_bfree),
+                                                                                         s.f_bsize, df_disp_hr));
+
+                       bb_printf("%9s %3ld%% %s\n",
+                                         make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr),
+                                         blocks_percent_used, mount_point);
+#else
+                       bb_printf("%-21s%9ld %9ld %9ld %3ld%% %s\n",
+                                         device,
+                                         kscale(s.f_blocks, s.f_bsize),
+                                         kscale(s.f_blocks-s.f_bfree, s.f_bsize),
+                                         kscale(s.f_bavail, s.f_bsize),
+                                         blocks_percent_used, mount_point);
+#endif
+               }
+
+       } while (1);
+
+       bb_fflush_stdout_and_exit(status);
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/coreutils/dirname.c b/busybox/coreutils/dirname.c
new file mode 100644 (file)
index 0000000..5136e49
--- /dev/null
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dirname implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int dirname_main(int argc, char **argv)
+{
+       if (argc != 2) {
+               bb_show_usage();
+       }
+
+       puts(dirname(argv[1]));
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/dos2unix.c b/busybox/coreutils/dos2unix.c
new file mode 100644 (file)
index 0000000..df0b4f9
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * dos2unix for BusyBox
+ *
+ * dos2unix '\n' convertor 0.5.0
+ *   based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997)
+ * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.sk>.
+ * All rights reserved.
+ *
+ * dos2unix filters reading input from stdin and writing output to stdout.
+ * Without arguments it reverts the format (e.i. if source is in UNIX format,
+ * output is in DOS format and vice versa).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * See the COPYING file for license information.
+ */
+
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include "busybox.h"
+
+#define CT_AUTO         0
+#define CT_UNIX2DOS     1
+#define CT_DOS2UNIX     2
+
+/* We are making a lame pseudo-random string generator here.  in
+ * convert(), each pass through the while loop will add more and more
+ * stuff into value, which is _supposed_ to wrap.  We don't care about
+ * it being accurate.  We care about it being messy, since we then mod
+ * it by the sizeof(letters) and then use that as an index into letters
+ * to pick a random letter to add to out temporary file. */
+typedef unsigned long int bb_uint64_t;
+
+static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+// if fn is NULL then input is stdin and output is stdout
+static int convert(char *fn, int ConvType)
+{
+       int c, fd;
+       struct timeval tv;
+       char tempFn[BUFSIZ];
+       static bb_uint64_t value=0;
+       FILE *in = stdin, *out = stdout;
+
+       if (fn != NULL) {
+               in = bb_xfopen(fn, "rw");
+               safe_strncpy(tempFn, fn, sizeof(tempFn));
+               c = strlen(tempFn);
+               tempFn[c] = '.';
+               while(1) {
+                   /* tempFn is BUFSIZ so the last addressable spot it BUFSIZ-1.
+                    * The loop increments by 2. So this must check for BUFSIZ-3. */
+                   if (c >=BUFSIZ-3)
+                       bb_error_msg_and_die("unique name not found");
+                   /* Get some semi random stuff to try and make a
+                    * random filename based (and in the same dir as)
+                    * the input file... */
+                   gettimeofday (&tv, NULL);
+                   value += ((bb_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
+                   tempFn[++c] = letters[value % 62];
+                   tempFn[c+1] = '\0';
+                   value /= 62;
+
+                   if ((fd = open(tempFn, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0 ) {
+                       continue;
+                   }
+                   out = fdopen(fd, "w+");
+                   if (!out) {
+                       close(fd);
+                       remove(tempFn);
+                       continue;
+                   }
+                   break;
+               }
+       }
+
+       while ((c = fgetc(in)) != EOF) {
+               if (c == '\r') {
+                       if ((ConvType == CT_UNIX2DOS) && (fn != NULL)) {
+                               // file is alredy in DOS format so it is not necessery to touch it
+                               remove(tempFn);
+                               if (fclose(in) < 0 || fclose(out) < 0) {
+                                       bb_perror_nomsg();
+                                       return -2;
+                               }
+                               return 0;
+                       }
+                       if (!ConvType)
+                               ConvType = CT_DOS2UNIX;
+                       break;
+               }
+               if (c == '\n') {
+                       if ((ConvType == CT_DOS2UNIX) && (fn != NULL)) {
+                               // file is alredy in UNIX format so it is not necessery to touch it
+                               remove(tempFn);
+                               if ((fclose(in) < 0) || (fclose(out) < 0)) {
+                                       bb_perror_nomsg();
+                                       return -2;
+                               }
+                               return 0;
+                       }
+                       if (!ConvType) {
+                               ConvType = CT_UNIX2DOS;
+                       }
+                       if (ConvType == CT_UNIX2DOS) {
+                               fputc('\r', out);
+                       }
+                       fputc('\n', out);
+                       break;
+               }
+               fputc(c, out);
+       }
+       if (c != EOF)
+               while ((c = fgetc(in)) != EOF) {
+                       if (c == '\r')
+                               continue;
+                       if (c == '\n') {
+                               if (ConvType == CT_UNIX2DOS)
+                                       fputc('\r', out);
+                               fputc('\n', out);
+                               continue;
+                       }
+               fputc(c, out);
+       }
+
+       if (fn != NULL) {
+           if (fclose(in) < 0 || fclose(out) < 0) {
+               bb_perror_nomsg();
+               remove(tempFn);
+               return -2;
+           }
+
+           /* Assume they are both on the same filesystem (which
+            * should be true since we put them into the same directory
+            * so we _should_ be ok, but you never know... */
+           if (rename(tempFn, fn) < 0) {
+               bb_perror_msg("unable to rename '%s' as '%s'", tempFn, fn);
+               return -1;
+           }
+       }
+
+       return 0;
+}
+
+int dos2unix_main(int argc, char *argv[])
+{
+       int ConvType = CT_AUTO;
+       int o;
+
+       //See if we are supposed to be doing dos2unix or unix2dos
+       if (argv[0][0]=='d') {
+           ConvType = CT_DOS2UNIX;
+       }
+       if (argv[0][0]=='u') {
+           ConvType = CT_UNIX2DOS;
+       }
+
+       // process parameters
+       while ((o = getopt(argc, argv, "du")) != EOF) {
+               switch (o) {
+               case 'd':
+                       ConvType = CT_UNIX2DOS;
+                       break;
+               case 'u':
+                       ConvType = CT_DOS2UNIX;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       if (optind < argc) {
+               while(optind < argc)
+                       if ((o = convert(argv[optind++], ConvType)) < 0)
+                               break;
+       }
+       else
+               o = convert(NULL, ConvType);
+
+       return o;
+}
+
diff --git a/busybox/coreutils/du.c b/busybox/coreutils/du.c
new file mode 100644 (file)
index 0000000..bfa4403
--- /dev/null
@@ -0,0 +1,269 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini du implementation for busybox
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ * Copyright (C) 2002  Edward Betts <edward@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
+ * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
+ *    The -d option allows setting of max depth (similar to gnu --max-depth).
+ * 2) Fixed incorrect size calculations for links and directories, especially
+ *    when errors occurred.  Calculates sizes should now match gnu du output.
+ * 3) Added error checking of output.
+ * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+# ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
+static unsigned long disp_hr = KILOBYTE;
+# else
+static unsigned long disp_hr = 512;
+# endif
+#elif defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
+static unsigned int disp_k = 1;
+#else
+static unsigned int disp_k;    /* bss inits to 0 */
+#endif
+
+static int max_print_depth = INT_MAX;
+static int count_hardlinks = 1;
+
+static int status
+#if EXIT_SUCCESS == 0
+       = EXIT_SUCCESS
+#endif
+       ;
+static int print_files;
+static int slink_depth;
+static int du_depth;
+static int one_file_system;
+static dev_t dir_dev;
+
+
+static void print(long size, char *filename)
+{
+       /* TODO - May not want to defer error checking here. */
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+       bb_printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
+                  filename);
+#else
+       if (disp_k) {
+               size++;
+               size >>= 1;
+       }
+       bb_printf("%ld\t%s\n", size, filename);
+#endif
+}
+
+/* tiny recursive du */
+static long du(char *filename)
+{
+       struct stat statbuf;
+       long sum;
+
+       if ((lstat(filename, &statbuf)) != 0) {
+               bb_perror_msg("%s", filename);
+               status = EXIT_FAILURE;
+               return 0;
+       }
+
+       if (one_file_system) {
+               if (du_depth == 0) {
+                       dir_dev = statbuf.st_dev;
+               } else if (dir_dev != statbuf.st_dev) {
+                       return 0;
+               }
+       }
+
+       sum = statbuf.st_blocks;
+
+       if (S_ISLNK(statbuf.st_mode)) {
+               if (slink_depth > du_depth) {   /* -H or -L */
+                       if ((stat(filename, &statbuf)) != 0) {
+                               bb_perror_msg("%s", filename);
+                               status = EXIT_FAILURE;
+                               return 0;
+                       }
+                       sum = statbuf.st_blocks;
+                       if (slink_depth == 1) {
+                               slink_depth = INT_MAX;  /* Convert -H to -L. */
+                       }
+               }
+       }
+
+       if (statbuf.st_nlink > count_hardlinks) {
+               /* Add files/directories with links only once */
+               if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
+                       return 0;
+               }
+               add_to_ino_dev_hashtable(&statbuf, NULL);
+       }
+
+       if (S_ISDIR(statbuf.st_mode)) {
+               DIR *dir;
+               struct dirent *entry;
+               char *newfile;
+
+               dir = opendir(filename);
+               if (!dir) {
+                       bb_perror_msg("%s", filename);
+                       status = EXIT_FAILURE;
+                       return sum;
+               }
+
+               newfile = last_char_is(filename, '/');
+               if (newfile)
+                       *newfile = '\0';
+
+               while ((entry = readdir(dir))) {
+                       char *name = entry->d_name;
+
+                       newfile = concat_subpath_file(filename, name);
+                       if(newfile == NULL)
+                               continue;
+                       ++du_depth;
+                       sum += du(newfile);
+                       --du_depth;
+                       free(newfile);
+               }
+               closedir(dir);
+       } else if (du_depth > print_files) {
+               return sum;
+       }
+       if (du_depth <= max_print_depth) {
+               print(sum, filename);
+       }
+       return sum;
+}
+
+int du_main(int argc, char **argv)
+{
+       long total;
+       int slink_depth_save;
+       int print_final_total;
+       char *smax_print_depth;
+       unsigned long opt;
+
+#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
+       if (getenv("POSIXLY_CORRECT")) {        /* TODO - a new libbb function? */
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+               disp_hr = 512;
+#else
+               disp_k = 0;
+#endif
+       }
+#endif
+
+       /* Note: SUSv3 specifies that -a and -s options can not be used together
+        * in strictly conforming applications.  However, it also says that some
+        * du implementations may produce output when -a and -s are used together.
+        * gnu du exits with an error code in this case.  We choose to simply
+        * ignore -a.  This is consistent with -s being equivalent to -d 0.
+        */
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+       bb_opt_complementaly = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
+       opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth);
+       if((opt & (1 << 9))) {
+               /* -h opt */
+               disp_hr = 0;
+       }
+       if((opt & (1 << 10))) {
+               /* -m opt */
+               disp_hr = MEGABYTE;
+       }
+       if((opt & (1 << 2))) {
+               /* -k opt */
+                       disp_hr = KILOBYTE;
+       }
+#else
+       bb_opt_complementaly = "H-L:L-H:s-d:d-s";
+       opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth);
+#if !defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
+       if((opt & (1 << 2))) {
+               /* -k opt */
+                       disp_k = 1;
+       }
+#endif
+#endif
+       if((opt & (1 << 0))) {
+               /* -a opt */
+               print_files = INT_MAX;
+       }
+       if((opt & (1 << 1))) {
+               /* -H opt */
+               slink_depth = 1;
+       }
+       if((opt & (1 << 3))) {
+               /* -L opt */
+                       slink_depth = INT_MAX;
+       }
+       if((opt & (1 << 4))) {
+               /* -s opt */
+                       max_print_depth = 0;
+               }
+       one_file_system = opt & (1 << 5); /* -x opt */
+       if((opt & (1 << 6))) {
+               /* -d opt */
+               max_print_depth = bb_xgetularg10_bnd(smax_print_depth, 0, INT_MAX);
+       }
+       if((opt & (1 << 7))) {
+               /* -l opt */
+               count_hardlinks = INT_MAX;
+       }
+       print_final_total = opt & (1 << 8); /* -c opt */
+
+       /* go through remaining args (if any) */
+       argv += optind;
+       if (optind >= argc) {
+               *--argv = ".";
+               if (slink_depth == 1) {
+                       slink_depth = 0;
+               }
+       }
+
+       slink_depth_save = slink_depth;
+       total = 0;
+       do {
+               total += du(*argv);
+               slink_depth = slink_depth_save;
+       } while (*++argv);
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       reset_ino_dev_hashtable();
+#endif
+
+       if (print_final_total) {
+               print(total, "total");
+       }
+
+       bb_fflush_stdout_and_exit(status);
+}
diff --git a/busybox/coreutils/echo.c b/busybox/coreutils/echo.c
new file mode 100644 (file)
index 0000000..539640f
--- /dev/null
@@ -0,0 +1,165 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * echo implementation for busybox
+ *
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Because of behavioral differences, implemented configurable SUSv3
+ * or 'fancy' gnu-ish behaviors.  Also, reduced size and fixed bugs.
+ * 1) In handling '\c' escape, the previous version only suppressed the
+ *     trailing newline.  SUSv3 specifies _no_ output after '\c'.
+ * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
+ *    The previous version version did not allow 4-digit octals.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int echo_main(int argc, char** argv)
+{
+#ifndef CONFIG_FEATURE_FANCY_ECHO
+#define eflag '\\'
+       ++argv;
+#else
+       const char *p;
+       int nflag = 1;
+       int eflag = 0;
+
+       while (*++argv && (**argv == '-')) {
+               /* If it appears that we are handling options, then make sure
+                * that all of the options specified are actually valid.
+                * Otherwise, the string should just be echoed.
+                */
+
+               if (!*(p = *argv + 1)) {        /* A single '-', so echo it. */
+                       goto just_echo;
+               }
+
+               do {
+                       if (strrchr("neE", *p) == 0) {
+                               goto just_echo;
+                       }
+               } while (*++p);
+
+               /* All of the options in this arg are valid, so handle them. */
+               p = *argv + 1;
+               do {
+                       if (*p == 'n') {
+                               nflag = 0;
+                       } else if (*p == 'e') {
+                               eflag = '\\';
+                       } else {
+                               eflag = 0;
+                       }
+               } while (*++p);
+       }
+
+just_echo:
+#endif
+       while (*argv) {
+               register int c;
+
+               while ((c = *(*argv)++)) {
+                       if (c == eflag) {       /* Check for escape seq. */
+                               if (**argv == 'c') {
+                                       /* '\c' means cancel newline and
+                                        * ignore all subsequent chars. */
+                                       goto DONE;
+                               }
+#ifndef CONFIG_FEATURE_FANCY_ECHO
+                               /* SUSv3 specifies that octal escapes must begin with '0'. */
+                               if (((unsigned int)(**argv - '1')) >= 7)
+#endif
+                               {
+                                       /* Since SUSv3 mandates a first digit of 0, 4-digit octals
+                                       * of the form \0### are accepted. */
+                                       if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) {
+                                               (*argv)++;
+                                       }
+                                       /* bb_process_escape_sequence can handle nul correctly */
+                                       c = bb_process_escape_sequence((const char **) argv);
+                               }
+                       }
+                       putchar(c);
+               }
+
+               if (*++argv) {
+                       putchar(' ');
+               }
+       }
+
+#ifdef CONFIG_FEATURE_FANCY_ECHO
+       if (nflag) {
+               putchar('\n');
+       }
+#else
+       putchar('\n');
+#endif
+
+DONE:
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)echo.c      8.1 (Berkeley) 5/31/93
+ */
diff --git a/busybox/coreutils/env.c b/busybox/coreutils/env.c
new file mode 100644 (file)
index 0000000..87ab30c
--- /dev/null
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * env implementation for busybox
+ *
+ * Copyright (c) 1988, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Fixed bug involving exit return codes if execvp fails.  Also added
+ * output error checking.
+ */
+
+/*
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ * - correct "-" option usage
+ * - multiple "-u unsetenv" support
+ * - GNU long option support
+ * - save errno after exec failed before bb_perror_msg()
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "busybox.h"
+
+
+static const struct option env_long_options[] = {
+       { "ignore-environment", 0, NULL, 'i' },
+       { "unset", 1, NULL, 'u' },
+       { 0, 0, 0, 0 }
+};
+
+extern int env_main(int argc, char** argv)
+{
+       char **ep, *p;
+       char *cleanenv[1] = { NULL };
+       unsigned long opt;
+       llist_t *unset_env = NULL;
+       extern char **environ;
+
+       bb_opt_complementaly = "u*";
+       bb_applet_long_options = env_long_options;
+
+       opt = bb_getopt_ulflags(argc, argv, "+iu:", &unset_env);
+
+       argv += optind;
+       if (*argv && (argv[0][0] == '-') && !argv[0][1]) {
+               opt |= 1;
+               ++argv;
+       }
+
+       if(opt & 1)
+               environ = cleanenv;
+       else if(opt & 2) {
+               while(unset_env) {
+                       unsetenv(unset_env->data);
+                       unset_env = unset_env->link;
+               }
+       }
+
+       while (*argv && ((p = strchr(*argv, '=')) != NULL)) {
+               if (putenv(*argv) < 0) {
+                       bb_perror_msg_and_die("putenv");
+               }
+               ++argv;
+       }
+
+       if (*argv) {
+               int er;
+
+               execvp(*argv, argv);
+               er = errno;
+               bb_perror_msg("%s", *argv);     /* Avoid multibyte problems. */
+               return (er == ENOENT) ? 127 : 126;   /* SUSv3-mandated exit codes. */
+       }
+
+       for (ep = environ; *ep; ep++) {
+               puts(*ep);
+       }
+
+       bb_fflush_stdout_and_exit(0);
+}
+
+/*
+ * Copyright (c) 1988, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
diff --git a/busybox/coreutils/expr.c b/busybox/coreutils/expr.c
new file mode 100644 (file)
index 0000000..cbbd4cd
--- /dev/null
@@ -0,0 +1,528 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini expr implementation for busybox
+ *
+ * based on GNU expr Mike Parker.
+ * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
+ *
+ * Busybox modifications
+ * Copyright (c) 2000  Edward Betts <edward@debian.org>.
+ * Aug 2003  Vladimir Oleynik - reduced 464 bytes.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* This program evaluates expressions.  Each token (operator, operand,
+ * parenthesis) of the expression must be a separate argument.  The
+ * parser used is a reasonably general one, though any incarnation of
+ * it is language-specific.  It is especially nice for expressions.
+ *
+ * No parse tree is needed; a new node is evaluated immediately.
+ * One function can handle multiple operators all of equal precedence,
+ * provided they all associate ((x op x) op x). */
+
+/* no getopt needed */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "busybox.h"
+
+
+/* The kinds of value we can have.  */
+enum valtype {
+       integer,
+       string
+};
+typedef enum valtype TYPE;
+
+/* A value is.... */
+struct valinfo {
+       TYPE type;                      /* Which kind. */
+       union {                         /* The value itself. */
+               int i;
+               char *s;
+       } u;
+};
+typedef struct valinfo VALUE;
+
+/* The arguments given to the program, minus the program name.  */
+static char **args;
+
+static VALUE *docolon (VALUE *sv, VALUE *pv);
+static VALUE *eval (void);
+static VALUE *int_value (int i);
+static VALUE *str_value (char *s);
+static int nextarg (char *str);
+static int null (VALUE *v);
+static int toarith (VALUE *v);
+static void freev (VALUE *v);
+static void tostring (VALUE *v);
+
+int expr_main (int argc, char **argv)
+{
+       VALUE *v;
+
+       if (argc == 1) {
+               bb_error_msg_and_die("too few arguments");
+       }
+
+       args = argv + 1;
+
+       v = eval ();
+       if (*args)
+               bb_error_msg_and_die ("syntax error");
+
+       if (v->type == integer)
+               printf ("%d\n", v->u.i);
+       else
+               puts (v->u.s);
+
+       exit (null (v));
+}
+
+/* Return a VALUE for I.  */
+
+static VALUE *int_value (int i)
+{
+       VALUE *v;
+
+       v = xmalloc (sizeof(VALUE));
+       v->type = integer;
+       v->u.i = i;
+       return v;
+}
+
+/* Return a VALUE for S.  */
+
+static VALUE *str_value (char *s)
+{
+       VALUE *v;
+
+       v = xmalloc (sizeof(VALUE));
+       v->type = string;
+       v->u.s = bb_xstrdup (s);
+       return v;
+}
+
+/* Free VALUE V, including structure components.  */
+
+static void freev (VALUE *v)
+{
+       if (v->type == string)
+               free (v->u.s);
+       free (v);
+}
+
+/* Return nonzero if V is a null-string or zero-number.  */
+
+static int null (VALUE *v)
+{
+       switch (v->type) {
+               case integer:
+                       return v->u.i == 0;
+               default: /* string: */
+                       return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
+       }
+}
+
+/* Coerce V to a string value (can't fail).  */
+
+static void tostring (VALUE *v)
+{
+       if (v->type == integer) {
+               bb_xasprintf (&(v->u.s), "%d", v->u.i);
+               v->type = string;
+       }
+}
+
+/* Coerce V to an integer value.  Return 1 on success, 0 on failure.  */
+
+static int toarith (VALUE *v)
+{
+       if(v->type == string) {
+               int i;
+               char *e;
+
+               /* Don't interpret the empty string as an integer.  */
+               /* Currently does not worry about overflow or int/long differences. */
+               i = (int) strtol(v->u.s, &e, 10);
+               if ((v->u.s == e) || *e)
+                       return 0;
+               free (v->u.s);
+               v->u.i = i;
+               v->type = integer;
+       }
+       return 1;
+}
+
+/* Return nonzero if the next token matches STR exactly.
+   STR must not be NULL.  */
+
+static int
+nextarg (char *str)
+{
+       if (*args == NULL)
+               return 0;
+       return strcmp (*args, str) == 0;
+}
+
+/* The comparison operator handling functions.  */
+
+static int cmp_common (VALUE *l, VALUE *r, int op)
+{
+       int cmpval;
+
+       if (l->type == string || r->type == string) {
+               tostring (l);
+               tostring (r);
+               cmpval = strcmp (l->u.s, r->u.s);
+       }
+       else
+               cmpval = l->u.i - r->u.i;
+       switch(op) {
+               case '<':
+                       return cmpval < 0;
+               case ('L'+'E'):
+                       return cmpval <= 0;
+               case '=':
+                       return cmpval == 0;
+               case '!':
+                       return cmpval != 0;
+               case '>':
+                       return cmpval > 0;
+               default: /* >= */
+                       return cmpval >= 0;
+       }
+}
+
+/* The arithmetic operator handling functions.  */
+
+static int arithmetic_common (VALUE *l, VALUE *r, int op)
+{
+  int li, ri;
+
+  if (!toarith (l) || !toarith (r))
+    bb_error_msg_and_die ("non-numeric argument");
+  li = l->u.i;
+  ri = r->u.i;
+  if((op == '/' || op == '%') && ri == 0)
+    bb_error_msg_and_die ( "division by zero");
+  switch(op) {
+       case '+':
+               return li + ri;
+       case '-':
+               return li - ri;
+       case '*':
+               return li * ri;
+       case '/':
+               return li / ri;
+       default:
+               return li % ri;
+  }
+}
+
+/* Do the : operator.
+   SV is the VALUE for the lhs (the string),
+   PV is the VALUE for the rhs (the pattern).  */
+
+static VALUE *docolon (VALUE *sv, VALUE *pv)
+{
+       VALUE *v;
+       const char *errmsg;
+       struct re_pattern_buffer re_buffer;
+       struct re_registers re_regs;
+       int len;
+
+       tostring (sv);
+       tostring (pv);
+
+       if (pv->u.s[0] == '^') {
+               fprintf (stderr, "\
+warning: unportable BRE: `%s': using `^' as the first character\n\
+of a basic regular expression is not portable; it is being ignored",
+               pv->u.s);
+       }
+
+       len = strlen (pv->u.s);
+       memset (&re_buffer, 0, sizeof (re_buffer));
+       memset (&re_regs, 0, sizeof (re_regs));
+       re_buffer.allocated = 2 * len;
+       re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
+       re_buffer.translate = 0;
+       re_syntax_options = RE_SYNTAX_POSIX_BASIC;
+       errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
+       if (errmsg) {
+               bb_error_msg_and_die("%s", errmsg);
+       }
+
+       len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
+       if (len >= 0) {
+               /* Were \(...\) used? */
+               if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
+                       sv->u.s[re_regs.end[1]] = '\0';
+                       v = str_value (sv->u.s + re_regs.start[1]);
+               }
+               else
+                       v = int_value (len);
+       }
+       else {
+               /* Match failed -- return the right kind of null.  */
+               if (re_buffer.re_nsub > 0)
+                       v = str_value ("");
+               else
+                       v = int_value (0);
+       }
+       free (re_buffer.buffer);
+       return v;
+}
+
+/* Handle bare operands and ( expr ) syntax.  */
+
+static VALUE *eval7 (void)
+{
+       VALUE *v;
+
+       if (!*args)
+               bb_error_msg_and_die ( "syntax error");
+
+       if (nextarg ("(")) {
+               args++;
+               v = eval ();
+               if (!nextarg (")"))
+                       bb_error_msg_and_die ( "syntax error");
+                       args++;
+                       return v;
+               }
+
+       if (nextarg (")"))
+               bb_error_msg_and_die ( "syntax error");
+
+       return str_value (*args++);
+}
+
+/* Handle match, substr, index, length, and quote keywords.  */
+
+static VALUE *eval6 (void)
+{
+       VALUE *l, *r, *v, *i1, *i2;
+
+       if (nextarg ("quote")) {
+               args++;
+               if (!*args)
+                       bb_error_msg_and_die ( "syntax error");
+               return str_value (*args++);
+       }
+       else if (nextarg ("length")) {
+               args++;
+               r = eval6 ();
+               tostring (r);
+               v = int_value (strlen (r->u.s));
+               freev (r);
+               return v;
+       }
+       else if (nextarg ("match")) {
+               args++;
+               l = eval6 ();
+               r = eval6 ();
+               v = docolon (l, r);
+               freev (l);
+               freev (r);
+               return v;
+       }
+       else if (nextarg ("index")) {
+               args++;
+               l = eval6 ();
+               r = eval6 ();
+               tostring (l);
+               tostring (r);
+               v = int_value (strcspn (l->u.s, r->u.s) + 1);
+               if (v->u.i == (int) strlen (l->u.s) + 1)
+                       v->u.i = 0;
+               freev (l);
+               freev (r);
+               return v;
+       }
+       else if (nextarg ("substr")) {
+               args++;
+               l = eval6 ();
+               i1 = eval6 ();
+               i2 = eval6 ();
+               tostring (l);
+               if (!toarith (i1) || !toarith (i2)
+                       || i1->u.i > (int) strlen (l->u.s)
+                       || i1->u.i <= 0 || i2->u.i <= 0)
+               v = str_value ("");
+               else {
+                       v = xmalloc (sizeof(VALUE));
+                       v->type = string;
+                       v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
+               }
+               freev (l);
+               freev (i1);
+               freev (i2);
+               return v;
+       }
+       else
+               return eval7 ();
+}
+
+/* Handle : operator (pattern matching).
+   Calls docolon to do the real work.  */
+
+static VALUE *eval5 (void)
+{
+       VALUE *l, *r, *v;
+
+       l = eval6 ();
+       while (nextarg (":")) {
+               args++;
+               r = eval6 ();
+               v = docolon (l, r);
+               freev (l);
+               freev (r);
+               l = v;
+       }
+       return l;
+}
+
+/* Handle *, /, % operators.  */
+
+static VALUE *eval4 (void)
+{
+       VALUE *l, *r;
+       int op, val;
+
+       l = eval5 ();
+       while (1) {
+               if (nextarg ("*"))
+                       op = '*';
+               else if (nextarg ("/"))
+                       op = '/';
+               else if (nextarg ("%"))
+                       op = '%';
+               else
+                       return l;
+               args++;
+               r = eval5 ();
+               val = arithmetic_common (l, r, op);
+               freev (l);
+               freev (r);
+               l = int_value (val);
+       }
+}
+
+/* Handle +, - operators.  */
+
+static VALUE *eval3 (void)
+{
+       VALUE *l, *r;
+       int op, val;
+
+       l = eval4 ();
+       while (1) {
+               if (nextarg ("+"))
+                       op = '+';
+               else if (nextarg ("-"))
+                       op = '-';
+               else
+                       return l;
+               args++;
+               r = eval4 ();
+               val = arithmetic_common (l, r, op);
+               freev (l);
+               freev (r);
+               l = int_value (val);
+       }
+}
+
+/* Handle comparisons.  */
+
+static VALUE *eval2 (void)
+{
+       VALUE *l, *r;
+       int op, val;
+
+       l = eval3 ();
+       while (1) {
+               if (nextarg ("<"))
+                       op = '<';
+               else if (nextarg ("<="))
+                       op = 'L'+'E';
+               else if (nextarg ("=") || nextarg ("=="))
+                       op = '=';
+               else if (nextarg ("!="))
+                       op = '!';
+               else if (nextarg (">="))
+                       op = 'G'+'E';
+               else if (nextarg (">"))
+                       op = '>';
+               else
+                       return l;
+               args++;
+               r = eval3 ();
+               toarith (l);
+               toarith (r);
+               val = cmp_common (l, r, op);
+               freev (l);
+               freev (r);
+               l = int_value (val);
+       }
+}
+
+/* Handle &.  */
+
+static VALUE *eval1 (void)
+{
+       VALUE *l, *r;
+
+       l = eval2 ();
+       while (nextarg ("&")) {
+               args++;
+               r = eval2 ();
+               if (null (l) || null (r)) {
+                       freev (l);
+                       freev (r);
+                       l = int_value (0);
+               }
+               else
+                       freev (r);
+       }
+       return l;
+}
+
+/* Handle |.  */
+
+static VALUE *eval (void)
+{
+       VALUE *l, *r;
+
+       l = eval1 ();
+       while (nextarg ("|")) {
+               args++;
+               r = eval1 ();
+               if (null (l)) {
+                       freev (l);
+                       l = r;
+               }
+               else
+                       freev (r);
+       }
+       return l;
+}
diff --git a/busybox/coreutils/false.c b/busybox/coreutils/false.c
new file mode 100644 (file)
index 0000000..5cf2384
--- /dev/null
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini false implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/false.html */
+
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int false_main(int argc, char **argv)
+{
+       return EXIT_FAILURE;
+}
diff --git a/busybox/coreutils/fold.c b/busybox/coreutils/fold.c
new file mode 100644 (file)
index 0000000..68f24e6
--- /dev/null
@@ -0,0 +1,194 @@
+/* fold -- wrap each input line to fit in specified width.
+
+   Written by David MacKenzie, djm@gnu.ai.mit.edu.
+   Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.
+
+   Modified for busybox based on coreutils v 5.0
+   Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+
+#include "busybox.h"
+
+/* If nonzero, count bytes, not column positions. */
+static int count_bytes;
+
+/* Assuming the current column is COLUMN, return the column that
+   printing C will move the cursor to.
+   The first column is 0. */
+
+static int adjust_column(int column, char c)
+{
+       if (!count_bytes) {
+               if (c == '\b') {
+                       if (column > 0)
+                               column--;
+               } else if (c == '\r')
+                       column = 0;
+               else if (c == '\t')
+                       column = column + 8 - column % 8;
+               else                    /* if (isprint (c)) */
+                       column++;
+       } else
+               column++;
+       return column;
+}
+
+extern int fold_main(int argc, char **argv)
+{
+       /* If nonzero, try to break on whitespace. */
+       int break_spaces;
+
+       /* If nonzero, at least one of the files we read was standard input. */
+       int have_read_stdin;
+
+       int width = 80;
+       int i;
+       int optc;
+       int errs = 0;
+
+       break_spaces = count_bytes = have_read_stdin = 0;
+
+       /* Turn any numeric options into -w options.  */
+       for (i = 1; i < argc; i++) {
+               char const *a = argv[i];
+
+               if (a[0] == '-') {
+                       if (a[1] == '-' && !a[2])
+                               break;
+                       if (isdigit(a[1])) {
+                               char *s = xmalloc(strlen(a) + 2);
+
+                               s[0] = '-';
+                               s[1] = 'w';
+                               strcpy(s + 2, a + 1);
+                               argv[i] = s;
+                       }
+               }
+       }
+
+       while ((optc = getopt(argc, argv, "bsw:")) > 0) {
+               switch (optc) {
+                       case 'b':               /* Count bytes rather than columns. */
+                               count_bytes = 1;
+                               break;
+                       case 's':               /* Break at word boundaries. */
+                               break_spaces = 1;
+                               break;
+                       case 'w': {     /* Line width. */
+                               width = bb_xgetlarg(optarg, 10, 1, 10000);
+                               break;
+                       }
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       argv += optind;
+       if (!*argv) {
+               *--argv = "-";
+       }
+
+       do {
+               FILE *istream = bb_wfopen_input(*argv);
+               if (istream != NULL) {
+                       int c;
+                       int column = 0;         /* Screen column where next char will go. */
+                       int offset_out = 0;     /* Index in `line_out' for next char. */
+                       static char *line_out = NULL;
+                       static int allocated_out = 0;
+
+                       while ((c = getc(istream)) != EOF) {
+                               if (offset_out + 1 >= allocated_out) {
+                                       allocated_out += 1024;
+                                       line_out = xrealloc(line_out, allocated_out);
+                               }
+
+                               if (c == '\n') {
+                                       line_out[offset_out++] = c;
+                                       fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
+                                       column = offset_out = 0;
+                                       continue;
+                               }
+
+rescan:
+                               column = adjust_column(column, c);
+
+                               if (column > width) {
+                                       /* This character would make the line too long.
+                                         Print the line plus a newline, and make this character
+                                          start the next line. */
+                                       if (break_spaces) {
+                                               /* Look for the last blank. */
+                                               int logical_end;
+
+                                               for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) {
+                                                       if (isblank(line_out[logical_end])) {
+                                                               break;
+                                                       }
+                                               }
+                                               if (logical_end >= 0) {
+                                                       /* Found a blank.  Don't output the part after it. */
+                                                       logical_end++;
+                                                       fwrite(line_out, sizeof(char), (size_t) logical_end, stdout);
+                                                       putchar('\n');
+                                                       /* Move the remainder to the beginning of the next line.
+                                                          The areas being copied here might overlap. */
+                                                       memmove(line_out, line_out + logical_end, offset_out - logical_end);
+                                                       offset_out -= logical_end;
+                                                       for (column = i = 0; i < offset_out; i++) {
+                                                               column = adjust_column(column, line_out[i]);
+                                                       }
+                                                       goto rescan;
+                                               }
+                                       } else {
+                                               if (offset_out == 0) {
+                                                       line_out[offset_out++] = c;
+                                                       continue;
+                                               }
+                                       }
+                                       line_out[offset_out++] = '\n';
+                                       fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
+                                       column = offset_out = 0;
+                                       goto rescan;
+                               }
+
+                               line_out[offset_out++] = c;
+                       }
+
+                       if (offset_out) {
+                               fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
+                       }
+
+                       if (ferror(istream) || bb_fclose_nonstdin(istream)) {
+                               bb_perror_msg("%s", *argv);     /* Avoid multibyte problems. */
+                               errs |= EXIT_FAILURE;
+                       }
+               } else {
+                       errs |= EXIT_FAILURE;
+               }
+       } while (*++argv);
+
+       bb_fflush_stdout_and_exit(errs);
+}
diff --git a/busybox/coreutils/head.c b/busybox/coreutils/head.c
new file mode 100644 (file)
index 0000000..dab4de1
--- /dev/null
@@ -0,0 +1,138 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * head implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "busybox.h"
+
+static const char head_opts[] =
+       "n:"
+#ifdef CONFIG_FEATURE_FANCY_HEAD
+       "c:qv"
+#endif
+       ;
+
+static const char header_fmt_str[] = "\n==> %s <==\n";
+
+int head_main(int argc, char **argv)
+{
+       unsigned long count = 10;
+       unsigned long i;
+#ifdef CONFIG_FEATURE_FANCY_HEAD
+       int count_bytes = 0;
+       int header_threshhold = 1;
+#endif
+
+       FILE *fp;
+       const char *fmt;
+       char *p;
+       int opt;
+       int c;
+       int retval = EXIT_SUCCESS;
+
+       /* Allow legacy syntax of an initial numeric option without -n. */
+       if ((argc > 1) && (argv[1][0] == '-')
+               /* && (isdigit)(argv[1][1]) */
+               && (((unsigned int)(argv[1][1] - '0')) <= 9)
+       ) {
+               --argc;
+               ++argv;
+               p = (*argv) + 1;
+               goto GET_COUNT;
+       }
+
+       while ((opt = getopt(argc, argv, head_opts)) > 0) {
+               switch(opt) {
+#ifdef CONFIG_FEATURE_FANCY_HEAD
+                       case 'q':
+                               header_threshhold = INT_MAX;
+                               break;
+                       case 'v':
+                               header_threshhold = -1;
+                               break;
+                       case 'c':
+                               count_bytes = 1;
+                               /* fall through */
+#endif
+                       case 'n':
+                               p = optarg;
+                       GET_COUNT:
+                               count = bb_xgetularg10(p);
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       argv += optind;
+       if (!*argv) {
+               *--argv = "-";
+       }
+
+       fmt = header_fmt_str + 1;
+#ifdef CONFIG_FEATURE_FANCY_HEAD
+       if (argc - optind <= header_threshhold) {
+               header_threshhold = 0;
+       }
+#else
+       if (argc <= optind + 1) {
+               fmt += 11;
+       }
+       /* Now define some things here to avoid #ifdefs in the code below.
+        * These should optimize out of the if conditions below. */
+#define header_threshhold   1
+#define count_bytes         0
+#endif
+
+       do {
+               if ((fp = bb_wfopen_input(*argv)) != NULL) {
+                       if (fp == stdin) {
+                               *argv = (char *) bb_msg_standard_input;
+                       }
+                       if (header_threshhold) {
+                               bb_printf(fmt, *argv);
+                       }
+                       i = count;
+                       while (i && ((c = getc(fp)) != EOF)) {
+                               if (count_bytes || (c == '\n')) {
+                                       --i;
+                               }
+                               putchar(c);
+                       }
+                       if (bb_fclose_nonstdin(fp)) {
+                               bb_perror_msg("%s", *argv);     /* Avoid multibyte problems. */
+                               retval = EXIT_FAILURE;
+                       }
+                       bb_xferror_stdout();
+               }
+               fmt = header_fmt_str;
+       } while (*++argv);
+
+       bb_fflush_stdout_and_exit(retval);
+}
diff --git a/busybox/coreutils/hostid.c b/busybox/coreutils/hostid.c
new file mode 100644 (file)
index 0000000..917dc22
--- /dev/null
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hostid implementation for busybox
+ *
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int hostid_main(int argc, char **argv)
+{
+       if (argc > 1) {
+               bb_show_usage();
+       }
+
+       bb_printf("%lx\n", gethostid());
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/id.c b/busybox/coreutils/id.c
new file mode 100644 (file)
index 0000000..d5182b9
--- /dev/null
@@ -0,0 +1,135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini id implementation for busybox
+ *
+ * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- option -G is not currently supported. */
+/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to
+ * be more similar to GNU id. 
+ */
+
+#include "busybox.h"
+#include "pwd_.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef CONFIG_SELINUX
+#include <proc_secure.h>
+#include <flask_util.h>
+#endif
+
+#define PRINT_REAL        1
+#define NAME_NOT_NUMBER   2
+#define JUST_USER         4
+#define JUST_GROUP        8
+
+static short printf_full(unsigned int id, const char *arg, const char prefix)
+{      
+       const char *fmt = "%cid=%u";
+       short status=EXIT_FAILURE;
+       
+       if(arg) {
+               fmt = "%cid=%u(%s)";
+               status=EXIT_SUCCESS;
+       }
+       bb_printf(fmt, prefix, id, arg);
+       return status;
+}
+
+extern int id_main(int argc, char **argv)
+{
+       struct passwd *p;
+       uid_t uid;
+       gid_t gid;
+       unsigned long flags;
+       short status;
+#ifdef CONFIG_SELINUX
+       int is_flask_enabled_flag = is_flask_enabled();
+#endif
+
+       bb_opt_complementaly = "u~g:g~u";
+       flags = bb_getopt_ulflags(argc, argv, "rnug");
+
+       if ((flags & 0x80000000UL)
+       /* Don't allow -n -r -nr */
+       || (flags <= 3 && flags > 0) 
+       /* Don't allow more than one username */
+       || (argc > optind + 1))
+               bb_show_usage();
+       
+       /* This values could be overwritten later */
+       uid = geteuid();
+       gid = getegid();
+       if (flags & PRINT_REAL) {
+               uid = getuid();
+               gid = getgid();
+       }
+       
+       if(argv[optind]) {
+               p=getpwnam(argv[optind]);
+               /* my_getpwnam is needed because it exits on failure */
+               uid = my_getpwnam(argv[optind]);
+               gid = p->pw_gid;
+               /* in this case PRINT_REAL is the same */ 
+       }
+
+       if(flags & (JUST_GROUP | JUST_USER)) {
+               /* JUST_GROUP and JUST_USER are mutually exclusive */
+               if(flags & NAME_NOT_NUMBER) {
+                       /* my_getpwuid and my_getgrgid exit on failure so puts cannot segfault */
+                       puts((flags & JUST_USER) ? my_getpwuid(NULL, uid, -1 ) : my_getgrgid(NULL, gid, -1 ));
+               } else {
+                       bb_printf("%u\n",(flags & JUST_USER) ? uid : gid);
+               }
+               /* exit */ 
+               bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+       }
+
+       /* Print full info like GNU id */
+       /* my_getpwuid doesn't exit on failure here */
+       status=printf_full(uid, my_getpwuid(NULL, uid, 0), 'u');
+       putchar(' ');
+       /* my_getgrgid doesn't exit on failure here */
+       status|=printf_full(gid, my_getgrgid(NULL, gid, 0), 'g');
+#ifdef CONFIG_SELINUX
+       if(is_flask_enabled_flag) {
+               security_id_t mysid = getsecsid();
+               char context[80];
+               int len = sizeof(context);
+               context[0] = '\0';
+               if(security_sid_to_context(mysid, context, &len))
+                       strcpy(context, "unknown");
+               bb_printf(" context=%s", context);
+       }
+#endif
+       putchar('\n');
+       bb_fflush_stdout_and_exit(status);
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
+
diff --git a/busybox/coreutils/install.c b/busybox/coreutils/install.c
new file mode 100644 (file)
index 0000000..36dc1d6
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
+ *
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * TODO: -d option, need a way of recursively making directories and changing
+ *           owner/group, will probably modify bb_make_directory(...)
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+#define INSTALL_OPT_CMD        1
+#define INSTALL_OPT_DIRECTORY  2
+#define INSTALL_OPT_PRESERVE_TIME      4
+#define INSTALL_OPT_STRIP      8
+#define INSTALL_OPT_GROUP  16
+#define INSTALL_OPT_MODE  32
+#define INSTALL_OPT_OWNER  64
+
+static const struct option install_long_options[] = {
+       { "directory",  0,      NULL,   'd' },
+       { "preserve-timestamps",        0,      NULL,   'p' },
+       { "strip",      0,      NULL,   's' },
+       { "group",      0,      NULL,   'g' },
+       { "mode",       0,      NULL,   'm' },
+       { "owner",      0,      NULL,   'o' },
+       { 0,    0,      0,      0 }
+};
+
+extern int install_main(int argc, char **argv)
+{
+       struct stat statbuf;
+       mode_t mode;
+       uid_t uid;
+       gid_t gid;
+       char *gid_str = "-1";
+       char *uid_str = "-1";
+       char *mode_str = "0755";
+       int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
+       int ret = EXIT_SUCCESS;
+       int flags;
+       int i;
+
+       bb_applet_long_options = install_long_options;
+       bb_opt_complementaly = "s~d:d~s";
+       /* -c exists for backwards compatability, its needed */
+       flags = bb_getopt_ulflags(argc, argv, "cdpsg:m:o:", &gid_str, &mode_str, &uid_str);     /* 'a' must be 2nd */
+
+       /* Check valid options were given */
+       if(flags & 0x80000000UL) {
+               bb_show_usage();
+       }
+
+       /* preserve access and modification time, this is GNU behaviour, BSD only preserves modification time */
+       if (flags & INSTALL_OPT_PRESERVE_TIME) {
+               copy_flags |= FILEUTILS_PRESERVE_STATUS;
+       }
+       bb_parse_mode(mode_str, &mode);
+       gid = get_ug_id(gid_str, my_getgrnam);
+       uid = get_ug_id(uid_str, my_getpwnam);
+       umask(0);
+
+       /* Create directories
+        * dont use bb_make_directory() as it cant change uid or gid
+        * perhaps bb_make_directory() should be improved.
+        */
+       if (flags & INSTALL_OPT_DIRECTORY) {
+               for (argv += optind; *argv; argv++) {
+                       char *old_argv_ptr = *argv + 1;
+                       char *argv_ptr;
+                       do {
+                               argv_ptr = strchr(old_argv_ptr, '/');
+                               old_argv_ptr = argv_ptr;
+                               if (argv_ptr) {
+                                       *argv_ptr = '\0';
+                                       old_argv_ptr++;
+                               }
+                               if (mkdir(*argv, mode) == -1) {
+                                       if (errno != EEXIST) {
+                                               bb_perror_msg("coulnt create %s", *argv);
+                                               ret = EXIT_FAILURE;
+                                               break;
+                                       }
+                               }
+                               else if (lchown(*argv, uid, gid) == -1) {
+                                       bb_perror_msg("cannot change ownership of %s", *argv);
+                                       ret = EXIT_FAILURE;
+                                       break;
+                               }
+                               if (argv_ptr) {
+                                       *argv_ptr = '/';
+                               }
+                       } while (old_argv_ptr);
+               }
+               return(ret);
+       }
+
+       cp_mv_stat2(argv[argc - 1], &statbuf, lstat);
+       for (i = optind; i < argc - 1; i++) {
+               unsigned char *dest;
+
+               if (S_ISDIR(statbuf.st_mode)) {
+                       dest = concat_path_file(argv[argc - 1], basename(argv[i]));
+               } else {
+                       dest = argv[argc - 1];
+               }
+               ret |= copy_file(argv[i], dest, copy_flags);
+
+               /* Set the file mode */
+               if (chmod(dest, mode) == -1) {
+                       bb_perror_msg("cannot change permissions of %s", dest);
+                       ret = EXIT_FAILURE;
+               }
+
+               /* Set the user and group id */
+               if (lchown(dest, uid, gid) == -1) {
+                       bb_perror_msg("cannot change ownership of %s", dest);
+                       ret = EXIT_FAILURE;
+               }
+               if (flags & INSTALL_OPT_STRIP) {
+                       if (execlp("strip", "strip", dest, NULL) == -1) {
+                               bb_error_msg("strip failed");
+                               ret = EXIT_FAILURE;
+                       }
+               }
+       }
+
+       return(ret);
+}
diff --git a/busybox/coreutils/length.c b/busybox/coreutils/length.c
new file mode 100644 (file)
index 0000000..bce43ab
--- /dev/null
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "busybox.h"
+
+extern int length_main(int argc, char **argv)
+{
+       if ((argc != 2) ||  (**(++argv) == '-')) {
+           bb_show_usage();
+       }
+
+       bb_printf("%lu\n", (unsigned long)strlen(*argv));
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/libcoreutils/Makefile b/busybox/coreutils/libcoreutils/Makefile
new file mode 100644 (file)
index 0000000..0a1c80a
--- /dev/null
@@ -0,0 +1,33 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=../..
+top_builddir=../..
+srcdir=$(top_srcdir)/coreutils/libcoreutils
+LIBCOREUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/coreutils/libcoreutils/Makefile.in b/busybox/coreutils/libcoreutils/Makefile.in
new file mode 100644 (file)
index 0000000..cf83d71
--- /dev/null
@@ -0,0 +1,37 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LIBCOREUTILS_AR:=libcoreutils.a
+ifndef $(LIBCOREUTILS_DIR)
+LIBCOREUTILS_DIR:=$(top_builddir)/coreutils/libcoreutils/
+endif
+srcdir=$(top_srcdir)/coreutils/libcoreutils
+
+LIBCOREUTILS_SRC:= cp_mv_stat.c getopt_mk_fifo_nod.c xgetoptfile_sort_uniq.c
+
+LIBCOREUTILS_OBJS=$(patsubst %.c,$(LIBCOREUTILS_DIR)%.o, $(LIBCOREUTILS_SRC))
+
+libraries-y+=$(LIBCOREUTILS_DIR)$(LIBCOREUTILS_AR)
+
+$(LIBCOREUTILS_DIR)$(LIBCOREUTILS_AR): $(LIBCOREUTILS_OBJS)
+       $(AR) -ro $@ $(LIBCOREUTILS_OBJS)
+
+$(LIBCOREUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/coreutils/libcoreutils/coreutils.h b/busybox/coreutils/libcoreutils/coreutils.h
new file mode 100644 (file)
index 0000000..eabca82
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef COREUTILS_H
+#define COREUTILS_H            1
+
+typedef int (*stat_func)(const char *fn, struct stat *ps);
+
+extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf);
+extern int cp_mv_stat(const char *fn, struct stat *fn_stat);
+
+extern mode_t getopt_mk_fifo_nod(int argc, char **argv);
+extern FILE *xgetoptfile_sort_uniq(char **argv, const char *mode);
+
+#endif
diff --git a/busybox/coreutils/libcoreutils/cp_mv_stat.c b/busybox/coreutils/libcoreutils/cp_mv_stat.c
new file mode 100644 (file)
index 0000000..5a70b02
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <sys/stat.h>
+#include "libbb.h"
+#include "coreutils.h"
+
+extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf)
+{
+       if (sf(fn, fn_stat) < 0) {
+               if (errno != ENOENT) {
+                       bb_perror_msg("unable to stat `%s'", fn);
+                       return -1;
+               }
+               return 0;
+       } else if (S_ISDIR(fn_stat->st_mode)) {
+               return 3;
+       }
+       return 1;
+}
+
+extern int cp_mv_stat(const char *fn, struct stat *fn_stat)
+{
+       return cp_mv_stat2(fn, fn_stat, stat);
+}
diff --git a/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c
new file mode 100644 (file)
index 0000000..0872bdc
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "libbb.h"
+#include "coreutils.h"
+
+extern mode_t getopt_mk_fifo_nod(int argc, char **argv)
+{
+       mode_t mode = 0666;
+       int opt;
+
+       while ((opt = getopt(argc, argv, "m:")) > 0) {
+               if (opt == 'm') {
+                       mode = 0666;
+                       if (bb_parse_mode(optarg, &mode)) {
+                               umask(0);
+                               continue;
+                       }
+               }
+               bb_show_usage();
+       }
+       return mode;
+}
diff --git a/busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c b/busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c
new file mode 100644 (file)
index 0000000..a63daf9
--- /dev/null
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include "libbb.h"
+#include "coreutils.h"
+
+extern FILE *xgetoptfile_sort_uniq(char **argv, const char *mode)
+{
+       const char *n;
+
+       if ((n = *argv) != NULL) {
+               if ((*n != '-') || n[1]) {
+                       return bb_xfopen(n, mode);
+               }
+       }
+       return (*mode == 'r') ? stdin : stdout;
+}
diff --git a/busybox/coreutils/ln.c b/busybox/coreutils/ln.c
new file mode 100644 (file)
index 0000000..885ba61
--- /dev/null
@@ -0,0 +1,102 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ln implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU options missing: -b, -d, -F, -i, -S, and -v. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Fixed bug involving -n option.  Essentially, -n was always in effect.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+#define LN_SYMLINK          1
+#define LN_FORCE            2
+#define LN_NODEREFERENCE    4
+
+extern int ln_main(int argc, char **argv)
+{
+       int status = EXIT_SUCCESS;
+       int flag;
+       char *last;
+       char *src_name;
+       char *src;
+       struct stat statbuf;
+       int (*link_func)(const char *, const char *);
+
+       flag = bb_getopt_ulflags(argc, argv, "sfn");
+
+       if (argc == optind) {
+               bb_show_usage();
+       }
+
+       last = argv[argc - 1];
+       argv += optind;
+
+       if (argc == optind + 1) {
+               *--argv = last;
+               last = bb_get_last_path_component(bb_xstrdup(last));
+       }
+
+       do {
+               src_name = NULL;
+               src = last;
+
+               if (is_directory(src,
+                                                (flag & LN_NODEREFERENCE) ^ LN_NODEREFERENCE,
+                                                NULL)) {
+                       src_name = bb_xstrdup(*argv);
+                       src = concat_path_file(src, bb_get_last_path_component(src_name));
+                       free(src_name);
+                       src_name = src;
+               }
+               if (!(flag & LN_SYMLINK) && stat(*argv, &statbuf)) {
+                       bb_perror_msg(*argv);
+                       status = EXIT_FAILURE;
+                       free(src_name);
+                       continue;
+               }
+
+               if (flag & LN_FORCE) {
+                       unlink(src);
+               }
+
+               link_func = link;
+               if (flag & LN_SYMLINK) {
+                       link_func = symlink;
+               }
+
+               if (link_func(*argv, src) != 0) {
+                       bb_perror_msg(src);
+                       status = EXIT_FAILURE;
+               }
+
+               free(src_name);
+
+       } while ((++argv)[1]);
+
+       return status;
+}
diff --git a/busybox/coreutils/logname.c b/busybox/coreutils/logname.c
new file mode 100644 (file)
index 0000000..ca5eb41
--- /dev/null
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini logname implementation for busybox
+ *
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * SUSv3 specifies the string used is that returned from getlogin().
+ * The previous implementation used getpwuid() for geteuid(), which
+ * is _not_ the same.  Erik apparently made this change almost 3 years
+ * ago to avoid failing when no utmp was available.  However, the
+ * correct course of action wrt SUSv3 for a failing getlogin() is
+ * a diagnostic message and an error return.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int logname_main(int argc, char **argv)
+{
+       const char *p;
+
+       if (argc > 1) {
+               bb_show_usage();
+       }
+
+       if ((p = getlogin()) != NULL) {
+               puts(p);
+               bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+       }
+
+       bb_perror_msg_and_die("getlogin");
+}
diff --git a/busybox/coreutils/ls.c b/busybox/coreutils/ls.c
new file mode 100644 (file)
index 0000000..4e21454
--- /dev/null
@@ -0,0 +1,1134 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny-ls.c version 0.1.0: A minimalist 'ls'
+ * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
+ *
+ *  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.
+ */
+
+/*
+ * To achieve a small memory footprint, this version of 'ls' doesn't do any
+ * file sorting, and only has the most essential command line switches
+ * (i.e., the ones I couldn't live without :-) All features which involve
+ * linking in substantial chunks of libc can be disabled.
+ *
+ * Although I don't really want to add new features to this program to
+ * keep it small, I *am* interested to receive bug fixes and ways to make
+ * it more portable.
+ *
+ * KNOWN BUGS:
+ * 1. ls -l of a directory doesn't give "total <blocks>" header
+ * 2. ls of a symlink to a directory doesn't list directory contents
+ * 3. hidden files can make column width too large
+ *
+ * NON-OPTIMAL BEHAVIOUR:
+ * 1. autowidth reads directories twice
+ * 2. if you do a short directory listing without filetype characters
+ *    appended, there's no need to stat each one
+ * PORTABILITY:
+ * 1. requires lstat (BSD) - how do you do it without?
+ */
+
+enum {
+       TERMINAL_WIDTH = 80,    /* use 79 if terminal has linefold bug */
+       COLUMN_GAP = 2,         /* includes the file type char */
+};
+
+/************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/sysmacros.h>     /* major() and minor() */
+#include "busybox.h"
+#ifdef CONFIG_SELINUX
+#include <fs_secure.h>
+#include <flask_util.h>
+#include <ss.h>
+#endif
+
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+#include <time.h>
+#endif
+
+/* what is the overall style of the listing */
+#define STYLE_AUTO      (0)
+#define STYLE_COLUMNS   (1U<<21)       /* fill columns */
+#define STYLE_LONG      (2U<<21)       /* one record per line, extended info */
+#define STYLE_SINGLE    (3U<<21)       /* one record per line */
+
+#define STYLE_MASK                 STYLE_SINGLE
+#define STYLE_ONE_RECORD_FLAG      STYLE_LONG
+
+/* 51306 lrwxrwxrwx  1 root     root         2 May 11 01:43 /bin/view -> vi* */
+/* what file information will be listed */
+#define LIST_INO               (1U<<0)
+#define LIST_BLOCKS            (1U<<1)
+#define LIST_MODEBITS  (1U<<2)
+#define LIST_NLINKS            (1U<<3)
+#define LIST_ID_NAME   (1U<<4)
+#define LIST_ID_NUMERIC        (1U<<5)
+#define LIST_CONTEXT   (1U<<6)
+#define LIST_SIZE              (1U<<7)
+#define LIST_DEV               (1U<<8)
+#define LIST_DATE_TIME (1U<<9)
+#define LIST_FULLTIME  (1U<<10)
+#define LIST_FILENAME  (1U<<11)
+#define LIST_SYMLINK   (1U<<12)
+#define LIST_FILETYPE  (1U<<13)
+#define LIST_EXEC      (1U<<14)
+
+#define LIST_MASK       ((LIST_EXEC << 1) - 1)
+
+/* what files will be displayed */
+/* TODO -- We may be able to make DISP_NORMAL 0 to save a bit slot. */
+#define DISP_NORMAL            (1U<<14)        /* show normal filenames */
+#define DISP_DIRNAME   (1U<<15)        /* 2 or more items? label directories */
+#define DISP_HIDDEN            (1U<<16)        /* show filenames starting with .  */
+#define DISP_DOT               (1U<<17)        /* show . and .. */
+#define DISP_NOLIST            (1U<<18)        /* show directory as itself, not contents */
+#define DISP_RECURSIVE (1U<<19)        /* show directory and everything below it */
+#define DISP_ROWS              (1U<<20)        /* print across rows */
+
+#define DISP_MASK       (((DISP_ROWS << 1) - 1) & ~(DISP_NORMAL - 1))
+
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+/* how will the files be sorted */
+#define SORT_ORDER_FORWARD   0                 /* sort in reverse order */
+#define SORT_ORDER_REVERSE   (1U<<27)  /* sort in reverse order */
+
+#define SORT_NAME      0                       /* sort by file name */
+#define SORT_SIZE      (1U<<28)                /* sort by file size */
+#define SORT_ATIME     (2U<<28)                /* sort by last access time */
+#define SORT_CTIME     (3U<<28)                /* sort by last change time */
+#define SORT_MTIME     (4U<<28)                /* sort by last modification time */
+#define SORT_VERSION   (5U<<28)                /* sort by version */
+#define SORT_EXT       (6U<<28)                /* sort by file name extension */
+#define SORT_DIR       (7U<<28)                /* sort by file or directory */
+
+#define SORT_MASK      (7U<<28)
+#endif
+
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+/* which of the three times will be used */
+#define TIME_MOD       0
+#define TIME_CHANGE    (1U<<23)
+#define TIME_ACCESS    (1U<<24)
+
+#define TIME_MASK      (3U<<23)
+#endif
+
+#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+#define FOLLOW_LINKS   (1U<<25)
+#endif
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+#define LS_DISP_HR     (1U<<26)
+#endif
+
+#define LIST_SHORT     (LIST_FILENAME)
+#define LIST_ISHORT    (LIST_INO | LIST_FILENAME)
+#define LIST_LONG      (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
+                                               LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK)
+#define LIST_ILONG     (LIST_INO | LIST_LONG)
+
+#define SPLIT_DIR      1
+#define SPLIT_FILE     0
+#define SPLIT_SUBDIR   2
+
+#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
+#define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
+
+#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
+# define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
+#endif
+
+/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
+#ifdef CONFIG_FEATURE_LS_COLOR
+static int show_color = 0;
+
+#define COLOR(mode)    ("\000\043\043\043\042\000\043\043"\
+                                       "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
+#define ATTR(mode)     ("\00\00\01\00\01\00\01\00"\
+                                       "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
+#endif
+
+/*
+ * a directory entry and its stat info are stored here
+ */
+struct dnode {                 /* the basic node */
+       char *name;                     /* the dir entry name */
+       char *fullname;         /* the dir entry name */
+       struct stat dstat;      /* the file stat info */
+#ifdef CONFIG_SELINUX
+       security_id_t sid;
+#endif
+       struct dnode *next;     /* point at the next node */
+};
+typedef struct dnode dnode_t;
+
+static struct dnode **list_dir(const char *);
+static struct dnode **dnalloc(int);
+static int list_single(struct dnode *);
+
+static unsigned int all_fmt;
+
+#ifdef CONFIG_SELINUX
+static int is_flask_enabled_flag;
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static int terminal_width = TERMINAL_WIDTH;
+static unsigned short tabstops = COLUMN_GAP;
+#else
+#define tabstops COLUMN_GAP
+#define terminal_width TERMINAL_WIDTH
+#endif
+
+static int status = EXIT_SUCCESS;
+
+static struct dnode *my_stat(char *fullname, char *name)
+{
+       struct stat dstat;
+       struct dnode *cur;
+#ifdef CONFIG_SELINUX
+       security_id_t sid;
+#endif
+       int rc;
+
+#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+       if (all_fmt & FOLLOW_LINKS) {
+#ifdef CONFIG_SELINUX
+               if(is_flask_enabled_flag)
+                       rc = stat_secure(fullname, &dstat, &sid);
+               else
+#endif
+                       rc = stat(fullname, &dstat);
+               if(rc)
+               {
+                       bb_perror_msg("%s", fullname);
+                       status = EXIT_FAILURE;
+                       return 0;
+               }
+       } else
+#endif
+       {
+#ifdef CONFIG_SELINUX
+               if(is_flask_enabled_flag)
+                       rc = lstat_secure(fullname, &dstat, &sid);
+               else
+#endif
+                       rc = lstat(fullname, &dstat);
+               if(rc)
+               {
+                       bb_perror_msg("%s", fullname);
+                       status = EXIT_FAILURE;
+                       return 0;
+               }
+       }
+
+       cur = (struct dnode *) xmalloc(sizeof(struct dnode));
+       cur->fullname = fullname;
+       cur->name = name;
+       cur->dstat = dstat;
+#ifdef CONFIG_SELINUX
+       cur->sid = sid;
+#endif
+       return cur;
+}
+
+/*----------------------------------------------------------------------*/
+#ifdef CONFIG_FEATURE_LS_COLOR
+static char fgcolor(mode_t mode)
+{
+       /* Check wheter the file is existing (if so, color it red!) */
+       if (errno == ENOENT) {
+               return '\037';
+       }
+       if (LIST_EXEC && S_ISREG(mode)
+               && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+               return COLOR(0xF000);   /* File is executable ... */
+       return COLOR(mode);
+}
+
+/*----------------------------------------------------------------------*/
+static char bgcolor(mode_t mode)
+{
+       if (LIST_EXEC && S_ISREG(mode)
+               && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+               return ATTR(0xF000);    /* File is executable ... */
+       return ATTR(mode);
+}
+#endif
+
+/*----------------------------------------------------------------------*/
+#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
+static char append_char(mode_t mode)
+{
+       if (!(all_fmt & LIST_FILETYPE))
+               return '\0';
+       if ((all_fmt & LIST_EXEC) && S_ISREG(mode)
+               && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+               return '*';
+       return APPCHAR(mode);
+}
+#endif
+
+/*----------------------------------------------------------------------*/
+
+#define countdirs(A,B) count_dirs((A), (B), 1)
+#define countsubdirs(A,B) count_dirs((A), (B), 0)
+
+static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
+{
+       int i, dirs;
+
+       if (dn == NULL || nfiles < 1)
+               return (0);
+       dirs = 0;
+       for (i = 0; i < nfiles; i++) {
+               if (S_ISDIR(dn[i]->dstat.st_mode)
+                       && (notsubdirs
+                               || ((dn[i]->name[0] != '.')
+                                       || (dn[i]->name[1]
+                                               && ((dn[i]->name[1] != '.')
+                                                       || dn[i]->name[2])))))
+                       dirs++;
+       }
+       return (dirs);
+}
+
+static int countfiles(struct dnode **dnp)
+{
+       int nfiles;
+       struct dnode *cur;
+
+       if (dnp == NULL)
+               return (0);
+       nfiles = 0;
+       for (cur = dnp[0]; cur->next != NULL; cur = cur->next)
+               nfiles++;
+       nfiles++;
+       return (nfiles);
+}
+
+/* get memory to hold an array of pointers */
+static struct dnode **dnalloc(int num)
+{
+       struct dnode **p;
+
+       if (num < 1)
+               return (NULL);
+
+       p = (struct dnode **) xcalloc((size_t) num,
+                                                                 (size_t) (sizeof(struct dnode *)));
+       return (p);
+}
+
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+static void dfree(struct dnode **dnp)
+{
+       struct dnode *cur, *next;
+
+       if (dnp == NULL)
+               return;
+
+       cur = dnp[0];
+       while (cur != NULL) {
+               free(cur->fullname);    /* free the filename */
+               next = cur->next;
+               free(cur);              /* free the dnode */
+               cur = next;
+       }
+       free(dnp);                      /* free the array holding the dnode pointers */
+}
+#endif
+
+static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
+{
+       int dncnt, i, d;
+       struct dnode **dnp;
+
+       if (dn == NULL || nfiles < 1)
+               return (NULL);
+
+       /* count how many dirs and regular files there are */
+       if (which == SPLIT_SUBDIR)
+               dncnt = countsubdirs(dn, nfiles);
+       else {
+               dncnt = countdirs(dn, nfiles);  /* assume we are looking for dirs */
+               if (which == SPLIT_FILE)
+                       dncnt = nfiles - dncnt; /* looking for files */
+       }
+
+       /* allocate a file array and a dir array */
+       dnp = dnalloc(dncnt);
+
+       /* copy the entrys into the file or dir array */
+       for (d = i = 0; i < nfiles; i++) {
+               if (S_ISDIR(dn[i]->dstat.st_mode)) {
+                       if (which & (SPLIT_DIR|SPLIT_SUBDIR)) {
+                               if ((which & SPLIT_DIR)
+                                       || ((dn[i]->name[0] != '.')
+                                               || (dn[i]->name[1]
+                                                       && ((dn[i]->name[1] != '.')
+                                                               || dn[i]->name[2])))) {
+                                                                       dnp[d++] = dn[i];
+                                                               }
+                       }
+               } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
+                       dnp[d++] = dn[i];
+               }
+       }
+       return (dnp);
+}
+
+/*----------------------------------------------------------------------*/
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+static int sortcmp(struct dnode *d1, struct dnode *d2)
+{
+       unsigned int sort_opts = all_fmt & SORT_MASK;
+       int dif;
+
+       dif = 0;                        /* assume SORT_NAME */
+       if (sort_opts == SORT_SIZE) {
+               dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
+       } else if (sort_opts == SORT_ATIME) {
+               dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
+       } else if (sort_opts == SORT_CTIME) {
+               dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
+       } else if (sort_opts == SORT_MTIME) {
+               dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
+       } else if (sort_opts == SORT_DIR) {
+               dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
+               /* } else if (sort_opts == SORT_VERSION) { */
+               /* } else if (sort_opts == SORT_EXT) { */
+       }
+
+       if (dif == 0) {
+               /* sort by name- may be a tie_breaker for time or size cmp */
+#ifdef CONFIG_LOCALE_SUPPORT
+               dif = strcoll(d1->name, d2->name);
+#else
+               dif = strcmp(d1->name, d2->name);
+#endif
+       }
+
+       if (all_fmt & SORT_ORDER_REVERSE) {
+               dif = -dif;
+       }
+       return (dif);
+}
+
+/*----------------------------------------------------------------------*/
+static void shellsort(struct dnode **dn, int size)
+{
+       struct dnode *temp;
+       int gap, i, j;
+
+       /* shell short the array */
+       if (dn == NULL || size < 2)
+               return;
+
+       for (gap = size / 2; gap > 0; gap /= 2) {
+               for (i = gap; i < size; i++) {
+                       for (j = i - gap; j >= 0; j -= gap) {
+                               if (sortcmp(dn[j], dn[j + gap]) <= 0)
+                                       break;
+                               /* they are out of order, swap them */
+                               temp = dn[j];
+                               dn[j] = dn[j + gap];
+                               dn[j + gap] = temp;
+                       }
+               }
+       }
+}
+#endif
+
+/*----------------------------------------------------------------------*/
+static void showfiles(struct dnode **dn, int nfiles)
+{
+       int i, ncols, nrows, row, nc;
+       int column = 0;
+       int nexttab = 0;
+       int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
+
+       if (dn == NULL || nfiles < 1)
+               return;
+
+       if (all_fmt & STYLE_ONE_RECORD_FLAG) {
+               ncols = 1;
+       } else {
+               /* find the longest file name-  use that as the column width */
+               for (i = 0; i < nfiles; i++) {
+                       int len = strlen(dn[i]->name) +
+#ifdef CONFIG_SELINUX
+                       ((all_fmt & LIST_CONTEXT) ? 33 : 0) +
+#endif
+                       ((all_fmt & LIST_INO) ? 8 : 0) +
+                       ((all_fmt & LIST_BLOCKS) ? 5 : 0);
+                       if (column_width < len)
+                               column_width = len;
+               }
+               column_width += tabstops;
+               ncols = (int) (terminal_width / column_width);
+       }
+
+       if (ncols > 1) {
+               nrows = nfiles / ncols;
+               if ((nrows * ncols) < nfiles)
+                       nrows++;                /* round up fractionals */
+       } else {
+               nrows = nfiles;
+               ncols = 1;
+       }
+
+       for (row = 0; row < nrows; row++) {
+               for (nc = 0; nc < ncols; nc++) {
+                       /* reach into the array based on the column and row */
+                       i = (nc * nrows) + row; /* assume display by column */
+                       if (all_fmt & DISP_ROWS)
+                               i = (row * ncols) + nc; /* display across row */
+                       if (i < nfiles) {
+                               if (column > 0) {
+                                       nexttab -= column;
+                                       while (nexttab--) {
+                                               putchar(' ');
+                                               column++;
+                                       }
+                       }
+                               nexttab = column + column_width;
+                               column += list_single(dn[i]);
+               }
+               }
+               putchar('\n');
+               column = 0;
+       }
+}
+
+/*----------------------------------------------------------------------*/
+static void showdirs(struct dnode **dn, int ndirs, int first)
+{
+       int i, nfiles;
+       struct dnode **subdnp;
+
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+       int dndirs;
+       struct dnode **dnd;
+#endif
+
+       if (dn == NULL || ndirs < 1)
+               return;
+
+       for (i = 0; i < ndirs; i++) {
+               if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
+                       if (!first)
+                               printf("\n");
+                       first = 0;
+                       printf("%s:\n", dn[i]->fullname);
+               }
+               subdnp = list_dir(dn[i]->fullname);
+               nfiles = countfiles(subdnp);
+               if (nfiles > 0) {
+                       /* list all files at this level */
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+                       shellsort(subdnp, nfiles);
+#endif
+                       showfiles(subdnp, nfiles);
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+                       if (all_fmt & DISP_RECURSIVE) {
+                               /* recursive- list the sub-dirs */
+                               dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
+                               dndirs = countsubdirs(subdnp, nfiles);
+                               if (dndirs > 0) {
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+                                       shellsort(dnd, dndirs);
+#endif
+                                       showdirs(dnd, dndirs, 0);
+                                       free(dnd);      /* free the array of dnode pointers to the dirs */
+                               }
+                       }
+                       dfree(subdnp);  /* free the dnodes and the fullname mem */
+#endif
+               }
+       }
+}
+
+/*----------------------------------------------------------------------*/
+static struct dnode **list_dir(const char *path)
+{
+       struct dnode *dn, *cur, **dnp;
+       struct dirent *entry;
+       DIR *dir;
+       int i, nfiles;
+
+       if (path == NULL)
+               return (NULL);
+
+       dn = NULL;
+       nfiles = 0;
+       dir = opendir(path);
+       if (dir == NULL) {
+               bb_perror_msg("%s", path);
+               status = EXIT_FAILURE;
+               return (NULL);  /* could not open the dir */
+       }
+       while ((entry = readdir(dir)) != NULL) {
+               char *fullname;
+
+               /* are we going to list the file- it may be . or .. or a hidden file */
+               if (entry->d_name[0] == '.') {
+                       if ((entry->d_name[1] == 0 || (
+                               entry->d_name[1] == '.'
+                               && entry->d_name[2] == 0))
+                                       && !(all_fmt & DISP_DOT))
+                       continue;
+                       if (!(all_fmt & DISP_HIDDEN))
+                       continue;
+               }
+               fullname = concat_path_file(path, entry->d_name);
+               cur = my_stat(fullname, strrchr(fullname, '/') + 1);
+               if (!cur)
+                       continue;
+               cur->next = dn;
+               dn = cur;
+               nfiles++;
+       }
+       closedir(dir);
+
+       /* now that we know how many files there are
+          ** allocate memory for an array to hold dnode pointers
+        */
+       if (dn == NULL)
+               return (NULL);
+       dnp = dnalloc(nfiles);
+       for (i = 0, cur = dn; i < nfiles; i++) {
+               dnp[i] = cur;   /* save pointer to node in array */
+               cur = cur->next;
+       }
+
+       return (dnp);
+}
+
+/*----------------------------------------------------------------------*/
+static int list_single(struct dnode *dn)
+{
+       int i, column = 0;
+
+#ifdef CONFIG_FEATURE_LS_USERNAME
+       char scratch[16];
+#endif
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+       char *filetime;
+       time_t ttime, age;
+#endif
+#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
+       struct stat info;
+       char append;
+#endif
+
+       if (dn->fullname == NULL)
+               return (0);
+
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+       ttime = dn->dstat.st_mtime;     /* the default time */
+       if (all_fmt & TIME_ACCESS)
+               ttime = dn->dstat.st_atime;
+       if (all_fmt & TIME_CHANGE)
+               ttime = dn->dstat.st_ctime;
+       filetime = ctime(&ttime);
+#endif
+#ifdef CONFIG_FEATURE_LS_FILETYPES
+       append = append_char(dn->dstat.st_mode);
+#endif
+
+       for (i = 0; i <= 31; i++) {
+               switch (all_fmt & (1 << i)) {
+               case LIST_INO:
+                       column += printf("%7ld ", (long int) dn->dstat.st_ino);
+                       break;
+               case LIST_BLOCKS:
+#if _FILE_OFFSET_BITS == 64
+                       column += printf("%4lld ", dn->dstat.st_blocks >> 1);
+#else
+                       column += printf("%4ld ", dn->dstat.st_blocks >> 1);
+#endif
+                       break;
+               case LIST_MODEBITS:
+                       column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
+                       break;
+               case LIST_NLINKS:
+                       column += printf("%4ld ", (long) dn->dstat.st_nlink);
+                       break;
+               case LIST_ID_NAME:
+#ifdef CONFIG_FEATURE_LS_USERNAME
+                       my_getpwuid(scratch, dn->dstat.st_uid, sizeof(scratch));
+                       printf("%-8.8s ", scratch);
+                       my_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch));
+                       printf("%-8.8s", scratch);
+                       column += 17;
+                       break;
+#endif
+               case LIST_ID_NUMERIC:
+                       column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
+                       break;
+               case LIST_SIZE:
+               case LIST_DEV:
+                       if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
+                               column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),
+                                          (int) minor(dn->dstat.st_rdev));
+                       } else {
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+                               if (all_fmt & LS_DISP_HR) {
+                                       column += printf("%9s ",
+                                                       make_human_readable_str(dn->dstat.st_size, 1, 0));
+                               } else
+#endif
+                               {
+#if _FILE_OFFSET_BITS == 64
+                                       column += printf("%9lld ", (long long) dn->dstat.st_size);
+#else
+                                       column += printf("%9ld ", dn->dstat.st_size);
+#endif
+                               }
+                       }
+                       break;
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+               case LIST_FULLTIME:
+                       printf("%24.24s ", filetime);
+                       column += 25;
+                       break;
+               case LIST_DATE_TIME:
+                       if ((all_fmt & LIST_FULLTIME) == 0) {
+                               age = time(NULL) - ttime;
+                               printf("%6.6s ", filetime + 4);
+                               if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
+                                       /* hh:mm if less than 6 months old */
+                                       printf("%5.5s ", filetime + 11);
+                               } else {
+                                       printf(" %4.4s ", filetime + 20);
+                               }
+                               column += 13;
+                       }
+                       break;
+#endif
+#ifdef CONFIG_SELINUX
+               case LIST_CONTEXT:
+                       {
+                               char context[64];
+                               int len = sizeof(context);
+                               if(security_sid_to_context(dn->sid, context, &len))
+                               {
+                                       strcpy(context, "unknown");
+                                       len = 7;
+                               }
+                               printf("%-32s ", context);
+                               column += MAX(33, len);
+                       }
+                       break;
+#endif
+               case LIST_FILENAME:
+#ifdef CONFIG_FEATURE_LS_COLOR
+                       errno = 0;
+                       if (show_color && !lstat(dn->fullname, &info)) {
+                               printf("\033[%d;%dm", bgcolor(info.st_mode),
+                                          fgcolor(info.st_mode));
+                       }
+#endif
+                       column += printf("%s", dn->name);
+#ifdef CONFIG_FEATURE_LS_COLOR
+                       if (show_color) {
+                               printf("\033[0m");
+                       }
+#endif
+                       break;
+               case LIST_SYMLINK:
+                       if (S_ISLNK(dn->dstat.st_mode)) {
+                               char *lpath = xreadlink(dn->fullname);
+
+                               if (lpath) {
+                                       printf(" -> ");
+#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
+                                       if (!stat(dn->fullname, &info)) {
+                                               append = append_char(info.st_mode);
+                                       }
+#endif
+#ifdef CONFIG_FEATURE_LS_COLOR
+                                       if (show_color) {
+                                               errno = 0;
+                                               printf("\033[%d;%dm", bgcolor(info.st_mode),
+                                                          fgcolor(info.st_mode));
+                                       }
+#endif
+                                       column += printf("%s", lpath) + 4;
+#ifdef CONFIG_FEATURE_LS_COLOR
+                                       if (show_color) {
+                                               printf("\033[0m");
+                                       }
+#endif
+                                       free(lpath);
+                               }
+                       }
+                       break;
+#ifdef CONFIG_FEATURE_LS_FILETYPES
+               case LIST_FILETYPE:
+                       if (append != '\0') {
+                               printf("%1c", append);
+                               column++;
+                       }
+                       break;
+#endif
+               }
+       }
+
+       return column;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* "[-]Cadil1", POSIX mandated options, busybox always supports */
+/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
+/* "[-]Ak" GNU options, busybox always supports */
+/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
+/* "[-]p", POSIX non-mandated options, busybox optionally supports */
+/* "[-]SXvThw", GNU options, busybox optionally supports */
+/* "[-]K", SELinux mandated options, busybox optionally supports */
+/* "[-]e", I think we made this one up */
+
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+# define LS_STR_TIMESTAMPS     "cetu"
+#else
+# define LS_STR_TIMESTAMPS     ""
+#endif
+
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+# define LS_STR_SORTFILES      "SXrv"
+#else
+# define LS_STR_SORTFILES      ""
+#endif
+
+#ifdef CONFIG_FEATURE_LS_FILETYPES
+# define LS_STR_FILETYPES      "Fp"
+#else
+# define LS_STR_FILETYPES      ""
+#endif
+
+#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+# define LS_STR_FOLLOW_LINKS   "L"
+#else
+# define LS_STR_FOLLOW_LINKS   ""
+#endif
+
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+# define LS_STR_RECURSIVE      "R"
+#else
+# define LS_STR_RECURSIVE      ""
+#endif
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+# define LS_STR_HUMAN_READABLE "h"
+#else
+# define LS_STR_HUMAN_READABLE ""
+#endif
+
+#ifdef CONFIG_SELINUX
+# define LS_STR_SELINUX        "K"
+#else
+# define LS_STR_SELINUX        ""
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+# define LS_STR_AUTOWIDTH      "T:w:"
+#else
+# define LS_STR_AUTOWIDTH      ""
+#endif
+
+static const char ls_options[]="Cadil1gnsxAk" \
+       LS_STR_TIMESTAMPS \
+       LS_STR_SORTFILES \
+       LS_STR_FILETYPES \
+       LS_STR_FOLLOW_LINKS \
+       LS_STR_RECURSIVE \
+       LS_STR_HUMAN_READABLE \
+       LS_STR_SELINUX \
+       LS_STR_AUTOWIDTH;
+
+#define LIST_MASK_TRIGGER      0
+#define STYLE_MASK_TRIGGER     STYLE_MASK
+#define SORT_MASK_TRIGGER      SORT_MASK
+#define DISP_MASK_TRIGGER      DISP_ROWS
+#define TIME_MASK_TRIGGER      TIME_MASK
+
+static const unsigned opt_flags[] = {
+       LIST_SHORT | STYLE_COLUMNS,     /* C */
+       DISP_HIDDEN | DISP_DOT,         /* a */
+       DISP_NOLIST,                    /* d */
+       LIST_INO,                       /* i */
+       LIST_LONG | STYLE_LONG,         /* l - remember LS_DISP_HR in mask! */
+       LIST_SHORT | STYLE_SINGLE,      /* 1 */
+       0,                              /* g - ingored */
+       LIST_ID_NUMERIC,                /* n */
+       LIST_BLOCKS,                    /* s */
+       DISP_ROWS,                      /* x */
+       DISP_HIDDEN,                    /* A */
+#ifdef CONFIG_SELINUX
+       LIST_CONTEXT,                   /* k */
+#else
+       0,                              /* k - ingored */
+#endif
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+# ifdef CONFIG_FEATURE_LS_SORTFILES
+       TIME_CHANGE | SORT_CTIME,       /* c */
+# else
+       TIME_CHANGE,                    /* c */
+# endif
+       LIST_FULLTIME,                  /* e */
+# ifdef CONFIG_FEATURE_LS_SORTFILES
+       SORT_MTIME,                     /* t */
+# else
+       0,                              /* t - ignored -- is this correct? */
+# endif
+# ifdef CONFIG_FEATURE_LS_SORTFILES
+       TIME_ACCESS | SORT_ATIME,       /* u */
+# else
+       TIME_ACCESS,                    /* u */
+# endif
+#endif
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+       SORT_SIZE,                      /* S */
+       SORT_EXT,                       /* X */
+       SORT_ORDER_REVERSE,             /* r */
+       SORT_VERSION,                   /* v */
+#endif
+#ifdef CONFIG_FEATURE_LS_FILETYPES
+       LIST_FILETYPE | LIST_EXEC,      /* F */
+       LIST_FILETYPE,                  /* p */
+#endif
+#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+       FOLLOW_LINKS,                   /* L */
+#endif
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+       DISP_RECURSIVE,                 /* R */
+#endif
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+       LS_DISP_HR,                     /* h */
+#endif
+#ifdef CONFIG_SELINUX
+       LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
+#endif
+       (1U<<31)
+};
+
+
+/*----------------------------------------------------------------------*/
+
+extern int ls_main(int argc, char **argv)
+{
+       struct dnode **dnd;
+       struct dnode **dnf;
+       struct dnode **dnp;
+       struct dnode *dn;
+       struct dnode *cur;
+       long opt;
+       int nfiles = 0;
+       int dnfiles;
+       int dndirs;
+       int oi;
+       int ac;
+       int i;
+       char **av;
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+       char *tabstops_str = NULL;
+       char *terminal_width_str = NULL;
+#endif
+
+#ifdef CONFIG_SELINUX
+       is_flask_enabled_flag = is_flask_enabled();
+#endif
+
+       all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+               | TIME_MOD
+#endif
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+               | SORT_NAME | SORT_ORDER_FORWARD
+#endif
+               ;
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+       /* Obtain the terminal width.  */
+       get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
+       /* Go one less... */
+       terminal_width--;
+#endif
+
+#ifdef CONFIG_FEATURE_LS_COLOR
+       if (isatty(STDOUT_FILENO))
+               show_color = 1;
+#endif
+
+       /* process options */
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+       opt = bb_getopt_ulflags(argc, argv, ls_options, &tabstops_str, &terminal_width_str);
+       if (tabstops_str) {
+               tabstops = atoi(tabstops_str);
+       }
+       if (terminal_width_str) {
+               terminal_width = atoi(terminal_width_str);
+       }
+#else
+       opt = bb_getopt_ulflags(argc, argv, ls_options);
+#endif
+       for (i = 0; opt_flags[i] != (1U<<31); i++) {
+               if (opt & (1 << i)) {
+                       unsigned int flags = opt_flags[i];
+                       if (flags & LIST_MASK_TRIGGER) {
+                               all_fmt &= ~LIST_MASK;
+                       }
+                       if (flags & STYLE_MASK_TRIGGER) {
+                               all_fmt &= ~STYLE_MASK;
+                       }
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+                       if (flags & SORT_MASK_TRIGGER) {
+                               all_fmt &= ~SORT_MASK;
+                       }
+#endif
+                       if (flags & DISP_MASK_TRIGGER) {
+                               all_fmt &= ~DISP_MASK;
+                       }
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+                       if (flags & TIME_MASK_TRIGGER) {
+                               all_fmt &= ~TIME_MASK;
+                       }
+#endif
+                       if (flags & LIST_CONTEXT) {
+                               all_fmt |= STYLE_SINGLE;
+                       }
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+                       if (opt == 'l') {
+                               all_fmt &= ~LS_DISP_HR;
+                       }
+#endif
+                       all_fmt |= flags;
+               }
+       }
+
+       /* sort out which command line options take precedence */
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+       if (all_fmt & DISP_NOLIST)
+               all_fmt &= ~DISP_RECURSIVE;     /* no recurse if listing only dir */
+#endif
+#if defined (CONFIG_FEATURE_LS_TIMESTAMPS) && defined (CONFIG_FEATURE_LS_SORTFILES)
+       if (all_fmt & TIME_CHANGE)
+               all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
+       if (all_fmt & TIME_ACCESS)
+               all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
+#endif
+       if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
+               all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
+#ifdef CONFIG_FEATURE_LS_USERNAME
+       if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
+               all_fmt &= ~LIST_ID_NAME;       /* don't list names if numeric uid */
+#endif
+
+       /* choose a display format */
+       if ((all_fmt & STYLE_MASK) == STYLE_AUTO)
+#if STYLE_AUTO != 0
+               all_fmt = (all_fmt & ~STYLE_MASK)
+                               | (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
+#else
+               all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
+#endif
+
+       /*
+        * when there are no cmd line args we have to supply a default "." arg.
+        * we will create a second argv array, "av" that will hold either
+        * our created "." arg, or the real cmd line args.  The av array
+        * just holds the pointers- we don't move the date the pointers
+        * point to.
+        */
+       ac = argc - optind;     /* how many cmd line args are left */
+       if (ac < 1) {
+               av = (char **) xcalloc((size_t) 1, (size_t) (sizeof(char *)));
+               av[0] = bb_xstrdup(".");
+               ac = 1;
+       } else {
+               av = (char **) xcalloc((size_t) ac, (size_t) (sizeof(char *)));
+               for (oi = 0; oi < ac; oi++) {
+                       av[oi] = argv[optind++];        /* copy pointer to real cmd line arg */
+               }
+       }
+
+       /* now, everything is in the av array */
+       if (ac > 1)
+               all_fmt |= DISP_DIRNAME;        /* 2 or more items? label directories */
+
+       /* stuff the command line file names into an dnode array */
+       dn = NULL;
+       for (oi = 0; oi < ac; oi++) {
+               char *fullname = bb_xstrdup(av[oi]);
+
+               cur = my_stat(fullname, fullname);
+               if (!cur)
+                       continue;
+               cur->next = dn;
+               dn = cur;
+               nfiles++;
+       }
+
+       /* now that we know how many files there are
+          ** allocate memory for an array to hold dnode pointers
+        */
+       dnp = dnalloc(nfiles);
+       for (i = 0, cur = dn; i < nfiles; i++) {
+               dnp[i] = cur;   /* save pointer to node in array */
+               cur = cur->next;
+       }
+
+       if (all_fmt & DISP_NOLIST) {
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+               shellsort(dnp, nfiles);
+#endif
+               if (nfiles > 0)
+                       showfiles(dnp, nfiles);
+       } else {
+               dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
+               dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
+               dndirs = countdirs(dnp, nfiles);
+               dnfiles = nfiles - dndirs;
+               if (dnfiles > 0) {
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+                       shellsort(dnf, dnfiles);
+#endif
+                       showfiles(dnf, dnfiles);
+               }
+               if (dndirs > 0) {
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+                       shellsort(dnd, dndirs);
+#endif
+                       showdirs(dnd, dndirs, dnfiles == 0);
+               }
+       }
+       return (status);
+}
diff --git a/busybox/coreutils/md5_sha1_sum.c b/busybox/coreutils/md5_sha1_sum.c
new file mode 100644 (file)
index 0000000..bd1c9fc
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ *  Copyright (C) 2003 Glenn L. McGrath
+ *  Copyright (C) 2003-2004 Erik Andersen
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "busybox.h"
+
+
+#define FLAG_SILENT    1
+#define FLAG_CHECK     2
+#define FLAG_WARN      4
+
+/* This might be useful elsewhere */
+static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
+                                                                         unsigned char hash_length)
+{
+       int x, len, max;
+       unsigned char *hex_value;
+
+       max = (hash_length * 2) + 2;
+       hex_value = xmalloc(max);
+       for (x = len = 0; x < hash_length; x++) {
+               len += snprintf(hex_value + len, max - len, "%02x", hash_value[x]);
+       }
+       return (hex_value);
+}
+
+static uint8_t *hash_file(const char *filename, uint8_t hash_algo)
+{
+       uint8_t *hash_value_bin;
+       uint8_t *hash_value = NULL;
+       uint8_t hash_length;
+       int src_fd;
+
+       if (strcmp(filename, "-") == 0) {
+               src_fd = STDIN_FILENO;
+       } else {
+               src_fd = open(filename, O_RDONLY);
+       }
+
+       if (hash_algo == HASH_MD5) {
+               hash_length = 16;
+       } else {
+               hash_length = 20;
+       }
+
+       hash_value_bin = xmalloc(hash_length);
+
+       if ((src_fd != -1) && (hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2)) {
+               hash_value = hash_bin_to_hex(hash_value_bin, hash_length);
+       } else {
+               bb_perror_msg("%s", filename);
+       }
+
+       close(src_fd);
+
+       return(hash_value);
+}
+
+/* This could become a common function for md5 as well, by using md5_stream */
+extern int hash_files(int argc, char **argv, const uint8_t hash_algo)
+{
+       int return_value = EXIT_SUCCESS;
+       uint8_t *hash_value;
+
+#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+       unsigned int flags;
+
+       flags = bb_getopt_ulflags(argc, argv, "scw");
+#endif
+
+#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+       if (!(flags & FLAG_CHECK)) {
+               if (flags & FLAG_SILENT) {
+                       bb_error_msg_and_die
+                               ("the -s option is meaningful only when verifying checksums");
+               } else if (flags & FLAG_WARN) {
+                       bb_error_msg_and_die
+                               ("the -w option is meaningful only when verifying checksums");
+               }
+       }
+#endif
+
+       if (argc == optind) {
+               argv[argc++] = "-";
+       }
+#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+       if (flags & FLAG_CHECK) {
+               FILE *pre_computed_stream;
+               int count_total = 0;
+               int count_failed = 0;
+               unsigned char *file_ptr = argv[optind];
+               char *line;
+
+               if (optind + 1 != argc) {
+                       bb_error_msg_and_die
+                               ("only one argument may be specified when using -c");
+               }
+
+               if (strcmp(file_ptr, "-") == 0) {
+                       pre_computed_stream = stdin;
+               } else {
+                       pre_computed_stream = bb_xfopen(file_ptr, "r");
+               }
+
+               while ((line = bb_get_chomped_line_from_file(pre_computed_stream)) != NULL) {
+                       char *filename_ptr;
+
+                       count_total++;
+                       filename_ptr = strstr(line, "  ");
+                       if (filename_ptr == NULL) {
+                               if (flags & FLAG_WARN) {
+                                       bb_error_msg("Invalid format");
+                               }
+                               free(line);
+                               continue;
+                       }
+                       *filename_ptr = '\0';
+                       filename_ptr += 2;
+
+                       hash_value = hash_file(filename_ptr, hash_algo);
+
+                       if (hash_value && (strcmp(hash_value, line) == 0)) {
+                               if (!(flags & FLAG_SILENT))
+                                       printf("%s: OK\n", filename_ptr);
+                       } else {
+                               if (!(flags & FLAG_SILENT))
+                                       printf("%s: FAILED\n", filename_ptr);
+                               count_failed++;
+                               return_value = EXIT_FAILURE;
+                       }
+                       /* possible free(NULL) */
+                       free(hash_value);
+                       free(line);
+               }
+               if (count_failed && !(flags & FLAG_SILENT)) {
+                       bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
+                                                count_failed, count_total);
+               }
+               if (bb_fclose_nonstdin(pre_computed_stream) == EOF) {
+                       bb_perror_msg_and_die("Couldnt close file %s", file_ptr);
+               }
+       } else
+#endif
+       {
+               uint8_t hash_length;
+
+               if (hash_algo == HASH_MD5) {
+                       hash_length = 16;
+               } else {
+                       hash_length = 20;
+               }
+               hash_value = xmalloc(hash_length);
+
+               while (optind < argc) {
+                       unsigned char *file_ptr = argv[optind++];
+
+                       hash_value = hash_file(file_ptr, hash_algo);
+                       if (hash_value == NULL) {
+                               return_value = EXIT_FAILURE;
+                       } else {
+                               printf("%s  %s\n", hash_value, file_ptr);
+                               free(hash_value);
+                       }
+               }
+       }
+       return (return_value);
+}
+
+#ifdef CONFIG_MD5SUM
+extern int md5sum_main(int argc, char **argv)
+{
+       return(hash_files(argc, argv, HASH_MD5));
+}
+#endif
+
+#ifdef CONFIG_SHA1SUM
+extern int sha1sum_main(int argc, char **argv)
+{
+       return(hash_files(argc, argv, HASH_SHA1));
+}
+#endif
diff --git a/busybox/coreutils/mkdir.c b/busybox/coreutils/mkdir.c
new file mode 100644 (file)
index 0000000..50364f1
--- /dev/null
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mkdir implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Fixed broken permission setting when -p was used; especially in
+ * conjunction with -m.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "busybox.h"
+
+static const struct option mkdir_long_options[] = {
+       { "mode", 1, NULL, 'm' },
+       { "parents", 0, NULL, 'p' },
+       { 0, 0, 0, 0 }
+};
+
+extern int mkdir_main (int argc, char **argv)
+{
+       mode_t mode = (mode_t)(-1);
+       int status = EXIT_SUCCESS;
+       int flags = 0;
+       unsigned long opt;
+       char *smode;
+
+       bb_applet_long_options = mkdir_long_options;
+       opt = bb_getopt_ulflags(argc, argv, "m:p", &smode);
+       if(opt & 1) {
+                       mode = 0777;
+               if (!bb_parse_mode (smode, &mode)) {
+                       bb_error_msg_and_die ("invalid mode `%s'", smode);
+               }
+       }
+       if(opt & 2)
+               flags |= FILEUTILS_RECUR;
+
+       if (optind == argc) {
+               bb_show_usage();
+       }
+
+       argv += optind;
+
+       do {
+               if (bb_make_directory(*argv, mode, flags)) {
+                       status = EXIT_FAILURE;
+               }
+       } while (*++argv);
+
+       return status;
+}
diff --git a/busybox/coreutils/mkfifo.c b/busybox/coreutils/mkfifo.c
new file mode 100644 (file)
index 0000000..77e0e6d
--- /dev/null
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfifo implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+extern int mkfifo_main(int argc, char **argv)
+{
+       mode_t mode;
+       int retval = EXIT_SUCCESS;
+
+       mode = getopt_mk_fifo_nod(argc, argv);
+
+       if (!*(argv += optind)) {
+               bb_show_usage();
+       }
+
+       do {
+               if (mkfifo(*argv, mode) < 0) {
+                       bb_perror_msg("%s", *argv);     /* Avoid multibyte problems. */
+                       retval = EXIT_FAILURE;
+               }
+       } while (*++argv);
+
+       return retval;
+}
diff --git a/busybox/coreutils/mknod.c b/busybox/coreutils/mknod.c
new file mode 100644 (file)
index 0000000..7b2467b
--- /dev/null
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mknod implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+static const char modes_chars[] = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 };
+static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK };
+
+extern int mknod_main(int argc, char **argv)
+{
+       mode_t mode;
+       dev_t dev;
+       const char *name;
+
+       mode = getopt_mk_fifo_nod(argc, argv);
+       argv += optind;
+       argc -= optind;
+
+       if ((argc >= 2) && ((name = strchr(modes_chars, argv[1][0])) != NULL)) {
+               mode |= modes_cubp[(int)(name[4])];
+
+               dev = 0;
+               if ((*name != 'p') && ((argc -= 2) == 2)) {
+                       dev = (bb_xgetularg10_bnd(argv[2], 0, 255) << 8)
+                               + bb_xgetularg10_bnd(argv[3], 0, 255);
+               }
+
+               if (argc == 2) {
+                       name = *argv;
+                       if (mknod(name, mode, dev) == 0) {
+                               return EXIT_SUCCESS;
+                       }
+                       bb_perror_msg_and_die("%s", name);
+               }
+       }
+       bb_show_usage();
+}
diff --git a/busybox/coreutils/mv.c b/busybox/coreutils/mv.c
new file mode 100644 (file)
index 0000000..4f08ded
--- /dev/null
@@ -0,0 +1,139 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mv implementation for busybox
+ *
+ * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction and improved error checking.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+static const struct option mv_long_options[] = {
+       { "interactive", 0, NULL, 'i' },
+       { "force", 0, NULL, 'f' },
+       { 0, 0, 0, 0 }
+};
+
+#define OPT_FILEUTILS_FORCE       1
+#define OPT_FILEUTILS_INTERACTIVE 2
+
+static const char fmt[] = "cannot overwrite %sdirectory with %sdirectory";
+
+extern int mv_main(int argc, char **argv)
+{
+       struct stat dest_stat;
+       const char *last;
+       const char *dest;
+       unsigned long flags;
+       int dest_exists;
+       int status = 0;
+
+       bb_applet_long_options = mv_long_options;
+       bb_opt_complementaly = "f-i:i-f";
+       flags = bb_getopt_ulflags(argc, argv, "fi");
+       if (optind + 2 > argc) {
+               bb_show_usage();
+       }
+
+       last = argv[argc - 1];
+       argv += optind;
+
+       if (optind + 2 == argc) {
+               if ((dest_exists = cp_mv_stat(last, &dest_stat)) < 0) {
+                       return 1;
+               }
+
+               if (!(dest_exists & 2)) {
+                       dest = last;
+                       goto DO_MOVE;
+               }
+       }
+
+       do {
+               dest = concat_path_file(last, bb_get_last_path_component(*argv));
+
+               if ((dest_exists = cp_mv_stat(dest, &dest_stat)) < 0) {
+                       goto RET_1;
+               }
+
+DO_MOVE:
+
+               if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) &&
+                       ((access(dest, W_OK) < 0 && isatty(0)) ||
+                       (flags & OPT_FILEUTILS_INTERACTIVE))) {
+                       if (fprintf(stderr, "mv: overwrite `%s'? ", dest) < 0) {
+                               goto RET_1;     /* Ouch! fprintf failed! */
+                       }
+                       if (!bb_ask_confirmation()) {
+                               goto RET_0;
+                       }
+               }
+               if (rename(*argv, dest) < 0) {
+                       struct stat source_stat;
+                       int source_exists;
+
+                       if (errno != EXDEV) {
+                               bb_perror_msg("unable to rename `%s'", *argv);
+                       }
+                       else if ((source_exists = cp_mv_stat(*argv, &source_stat)) >= 0) {
+                               if (dest_exists) {
+                                       if (dest_exists == 3) {
+                                               if (source_exists != 3) {
+                                                       bb_error_msg(fmt, "", "non-");
+                                                       goto RET_1;
+                                               }
+                                       } else {
+                                               if (source_exists == 3) {
+                                                       bb_error_msg(fmt, "non-", "");
+                                                       goto RET_1;
+                                               }
+                                       }
+                                       if (unlink(dest) < 0) {
+                                               bb_perror_msg("cannot remove `%s'", dest);
+                                               goto RET_1;
+                                       }
+                               }
+                               if ((copy_file(*argv, dest,
+                                       FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS) >= 0) &&
+                                       (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) {
+                                       goto RET_0;
+                               }
+                       }
+RET_1:
+                       status = 1;
+               }
+RET_0:
+               if (dest != last) {
+                       free((void *) dest);
+               }
+       } while (*++argv != last);
+
+       return (status);
+}
diff --git a/busybox/coreutils/od.c b/busybox/coreutils/od.c
new file mode 100644 (file)
index 0000000..6a138e8
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * od implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1990
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include "busybox.h"
+#include "dump.h"
+
+#define isdecdigit(c) (isdigit)(c)
+#define ishexdigit(c) (isxdigit)(c)
+
+static void
+odoffset(int argc, char ***argvp)
+{
+       register char *num, *p;
+       int base;
+       char *end;
+
+       /*
+        * The offset syntax of od(1) was genuinely bizarre.  First, if
+        * it started with a plus it had to be an offset.  Otherwise, if
+        * there were at least two arguments, a number or lower-case 'x'
+        * followed by a number makes it an offset.  By default it was
+        * octal; if it started with 'x' or '0x' it was hex.  If it ended
+        * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
+        * multiplied the number by 512 or 1024 byte units.  There was
+        * no way to assign a block count to a hex offset.
+        *
+        * We assumes it's a file if the offset is bad.
+        */
+       p = **argvp;
+
+       if (!p) {
+               /* hey someone is probably piping to us ... */
+               return;
+       }
+
+       if ((*p != '+')
+               && (argc < 2
+                       || (!isdecdigit(p[0])
+                               && ((p[0] != 'x') || !ishexdigit(p[1])))))
+               return;
+
+       base = 0;
+       /*
+        * bb_dump_skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
+        * set base.
+        */
+       if (p[0] == '+')
+               ++p;
+       if (p[0] == 'x' && ishexdigit(p[1])) {
+               ++p;
+               base = 16;
+       } else if (p[0] == '0' && p[1] == 'x') {
+               p += 2;
+               base = 16;
+       }
+
+       /* bb_dump_skip over the number */
+       if (base == 16)
+               for (num = p; ishexdigit(*p); ++p);
+       else
+               for (num = p; isdecdigit(*p); ++p);
+
+       /* check for no number */
+       if (num == p)
+               return;
+
+       /* if terminates with a '.', base is decimal */
+       if (*p == '.') {
+               if (base)
+                       return;
+               base = 10;
+       }
+
+       bb_dump_skip = strtol(num, &end, base ? base : 8);
+
+       /* if end isn't the same as p, we got a non-octal digit */
+       if (end != p)
+               bb_dump_skip = 0;
+       else {
+               if (*p) {
+                       if (*p == 'b') {
+                               bb_dump_skip *= 512;
+                               ++p;
+                       } else if (*p == 'B') {
+                               bb_dump_skip *= 1024;
+                               ++p;
+                       }
+               }
+               if (*p)
+                       bb_dump_skip = 0;
+               else {
+                       ++*argvp;
+                       /*
+                        * If the offset uses a non-octal base, the base of
+                        * the offset is changed as well.  This isn't pretty,
+                        * but it's easy.
+                        */
+#define        TYPE_OFFSET     7
+                       {
+                               char x_or_d;
+                               if (base == 16) {
+                                       x_or_d = 'x';
+                                       goto DO_X_OR_D;
+                               }
+                               if (base == 10) {
+                                       x_or_d = 'd';
+                               DO_X_OR_D:
+                                       bb_dump_fshead->nextfu->fmt[TYPE_OFFSET]
+                                               = bb_dump_fshead->nextfs->nextfu->fmt[TYPE_OFFSET]
+                                               = x_or_d;
+                               }
+                       }
+               }
+       }
+}
+
+static const char * const add_strings[] = {
+       "16/1 \"%3_u \" \"\\n\"",                               /* a */
+       "8/2 \" %06o \" \"\\n\"",                               /* B, o */
+       "16/1 \"%03o \" \"\\n\"",                               /* b */
+       "16/1 \"%3_c \" \"\\n\"",                               /* c */
+       "8/2 \"  %05u \" \"\\n\"",                              /* d */
+       "4/4 \"     %010u \" \"\\n\"",                  /* D */
+       "2/8 \"          %21.14e \" \"\\n\"",   /* e (undocumented in od), F */
+       "4/4 \" %14.7e \" \"\\n\"",                             /* f */
+       "4/4 \"       %08x \" \"\\n\"",                 /* H, X */
+       "8/2 \"   %04x \" \"\\n\"",                             /* h, x */
+       "4/4 \"    %11d \" \"\\n\"",                    /* I, L, l */
+       "8/2 \" %6d \" \"\\n\"",                                /* i */
+       "4/4 \"    %011o \" \"\\n\"",                   /* O */
+};
+
+static const signed char od_opts[] = "aBbcDdeFfHhIiLlOoXxv";
+
+static const signed char od_o2si[] = {
+       0, 1, 2, 3, 5,
+       4, 6, 6, 7, 8,
+       9, 0xa, 0xb, 0xa, 0xa,
+       0xb, 1, 8, 9,
+};
+
+int od_main(int argc, char **argv)
+{
+       int ch;
+       int first = 1;
+       signed char *p;
+       bb_dump_vflag = FIRST;
+       bb_dump_length = -1;
+
+       while ((ch = getopt(argc, argv, od_opts)) > 0) {
+               if (ch == 'v') {
+                       bb_dump_vflag = ALL;
+               } else if (((p = strchr(od_opts, ch)) != NULL) && (*p >= 0)) {
+                       if (first) {
+                               first = 0;
+                               bb_dump_add("\"%07.7_Ao\n\"");
+                               bb_dump_add("\"%07.7_ao  \"");
+                       } else {
+                               bb_dump_add("\"         \"");
+                       }
+                       bb_dump_add(add_strings[od_o2si[(int)(p-od_opts)]]);
+               } else {        /* P, p, s, w, or other unhandled */
+                       bb_show_usage();
+               }
+       }
+       if (!bb_dump_fshead) {
+               bb_dump_add("\"%07.7_Ao\n\"");
+               bb_dump_add("\"%07.7_ao  \" 8/2 \"%06o \" \"\\n\"");
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       odoffset(argc, &argv);
+
+       return(bb_dump_dump(argv));
+}
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/coreutils/printf.c b/busybox/coreutils/printf.c
new file mode 100644 (file)
index 0000000..da5e46a
--- /dev/null
@@ -0,0 +1,316 @@
+/* vi: set sw=4 ts=4: */
+/* printf - format and print data
+   Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Usage: printf format [argument...]
+
+   A front end to the printf function that lets it be used from the shell.
+
+   Backslash escapes:
+
+   \" = double quote
+   \\ = backslash
+   \a = alert (bell)
+   \b = backspace
+   \c = produce no further output
+   \f = form feed
+   \n = new line
+   \r = carriage return
+   \t = horizontal tab
+   \v = vertical tab
+   \0ooo = octal number (ooo is 0 to 3 digits)
+   \xhhh = hexadecimal number (hhh is 1 to 3 digits)
+
+   Additional directive:
+
+   %b = print an argument string, interpreting backslash escapes
+
+   The `format' argument is re-used as many times as necessary
+   to convert all of the given arguments.
+
+   David MacKenzie <djm@gnu.ai.mit.edu> */
+
+
+//   19990508 Busy Boxed! Dave Cinege
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <assert.h>
+#include "busybox.h"
+
+static double xstrtod __P((char *s));
+static long xstrtol __P((char *s));
+static unsigned long xstrtoul __P((char *s));
+static void print_esc_string __P((char *str));
+static int print_formatted __P((char *format, int argc, char **argv));
+static void print_direc __P( (char *start, size_t length,
+                       int field_width, int precision, char *argument));
+
+int printf_main(int argc, char **argv)
+{
+       char *format;
+       int args_used;
+
+       if (argc <= 1 || **(argv + 1) == '-') {
+               bb_show_usage();
+       }
+
+       format = argv[1];
+       argc -= 2;
+       argv += 2;
+
+       do {
+               args_used = print_formatted(format, argc, argv);
+               argc -= args_used;
+               argv += args_used;
+       }
+       while (args_used > 0 && argc > 0);
+
+/*
+  if (argc > 0)
+    fprintf(stderr, "excess args ignored");
+*/
+
+       return EXIT_SUCCESS;
+}
+
+/* Print the text in FORMAT, using ARGV (with ARGC elements) for
+   arguments to any `%' directives.
+   Return the number of elements of ARGV used.  */
+
+static int print_formatted(char *format, int argc, char **argv)
+{
+       int save_argc = argc;           /* Preserve original value.  */
+       char *f;                                        /* Pointer into `format'.  */
+       char *direc_start;                      /* Start of % directive.  */
+       size_t direc_length;            /* Length of % directive.  */
+       int field_width;                        /* Arg to first '*', or -1 if none.  */
+       int precision;                          /* Arg to second '*', or -1 if none.  */
+
+       for (f = format; *f; ++f) {
+               switch (*f) {
+               case '%':
+                       direc_start = f++;
+                       direc_length = 1;
+                       field_width = precision = -1;
+                       if (*f == '%') {
+                               putchar('%');
+                               break;
+                       }
+                       if (*f == 'b') {
+                               if (argc > 0) {
+                                       print_esc_string(*argv);
+                                       ++argv;
+                                       --argc;
+                               }
+                               break;
+                       }
+                       if (strchr("-+ #", *f)) {
+                               ++f;
+                               ++direc_length;
+                       }
+                       if (*f == '*') {
+                               ++f;
+                               ++direc_length;
+                               if (argc > 0) {
+                                       field_width = xstrtoul(*argv);
+                                       ++argv;
+                                       --argc;
+                               } else
+                                       field_width = 0;
+                       } else
+                               while (isdigit(*f)) {
+                                       ++f;
+                                       ++direc_length;
+                               }
+                       if (*f == '.') {
+                               ++f;
+                               ++direc_length;
+                               if (*f == '*') {
+                                       ++f;
+                                       ++direc_length;
+                                       if (argc > 0) {
+                                               precision = xstrtoul(*argv);
+                                               ++argv;
+                                               --argc;
+                                       } else
+                                               precision = 0;
+                               } else
+                                       while (isdigit(*f)) {
+                                               ++f;
+                                               ++direc_length;
+                                       }
+                       }
+                       if (*f == 'l' || *f == 'L' || *f == 'h') {
+                               ++f;
+                               ++direc_length;
+                       }
+                       /*
+                          if (!strchr ("diouxXfeEgGcs", *f))
+                          fprintf(stderr, "%%%c: invalid directive", *f);
+                        */
+                       ++direc_length;
+                       if (argc > 0) {
+                               print_direc(direc_start, direc_length, field_width,
+                                                       precision, *argv);
+                               ++argv;
+                               --argc;
+                       } else
+                               print_direc(direc_start, direc_length, field_width,
+                                                       precision, "");
+                       break;
+
+               case '\\':
+                       if (*++f == 'c')
+                               exit(0);
+                       putchar(bb_process_escape_sequence((const char **)&f));
+                       f--;
+                       break;
+
+               default:
+                       putchar(*f);
+               }
+       }
+
+       return save_argc - argc;
+}
+
+static void
+print_direc(char *start, size_t length, int field_width, int precision,
+                       char *argument)
+{
+       char *p;                                        /* Null-terminated copy of % directive. */
+
+       p = xmalloc((unsigned) (length + 1));
+       strncpy(p, start, length);
+       p[length] = 0;
+
+       switch (p[length - 1]) {
+       case 'd':
+       case 'i':
+               if (field_width < 0) {
+                       if (precision < 0)
+                               printf(p, xstrtol(argument));
+                       else
+                               printf(p, precision, xstrtol(argument));
+               } else {
+                       if (precision < 0)
+                               printf(p, field_width, xstrtol(argument));
+                       else
+                               printf(p, field_width, precision, xstrtol(argument));
+               }
+               break;
+
+       case 'o':
+       case 'u':
+       case 'x':
+       case 'X':
+               if (field_width < 0) {
+                       if (precision < 0)
+                               printf(p, xstrtoul(argument));
+                       else
+                               printf(p, precision, xstrtoul(argument));
+               } else {
+                       if (precision < 0)
+                               printf(p, field_width, xstrtoul(argument));
+                       else
+                               printf(p, field_width, precision, xstrtoul(argument));
+               }
+               break;
+
+       case 'f':
+       case 'e':
+       case 'E':
+       case 'g':
+       case 'G':
+               if (field_width < 0) {
+                       if (precision < 0)
+                               printf(p, xstrtod(argument));
+                       else
+                               printf(p, precision, xstrtod(argument));
+               } else {
+                       if (precision < 0)
+                               printf(p, field_width, xstrtod(argument));
+                       else
+                               printf(p, field_width, precision, xstrtod(argument));
+               }
+               break;
+
+       case 'c':
+               printf(p, *argument);
+               break;
+
+       case 's':
+               if (field_width < 0) {
+                       if (precision < 0)
+                               printf(p, argument);
+                       else
+                               printf(p, precision, argument);
+               } else {
+                       if (precision < 0)
+                               printf(p, field_width, argument);
+                       else
+                               printf(p, field_width, precision, argument);
+               }
+               break;
+       }
+
+       free(p);
+}
+
+static unsigned long xstrtoul(char *arg)
+{
+       unsigned long result;
+       if (safe_strtoul(arg, &result))
+               fprintf(stderr, "%s", arg);
+       return result;
+}
+
+static long xstrtol(char *arg)
+{
+       long result;
+       if (safe_strtol(arg, &result))
+               fprintf(stderr, "%s", arg);
+       return result;
+}
+
+static double xstrtod(char *arg)
+{
+       double result;
+       if (safe_strtod(arg, &result))
+               fprintf(stderr, "%s", arg);
+       return result;
+}
+
+static void print_esc_string(char *str)
+{
+       for (; *str; str++) {
+               if (*str == '\\') {
+                       str++;
+                       putchar(bb_process_escape_sequence((const char **)&str));
+               } else {
+                       putchar(*str);
+               }
+
+       }
+}
diff --git a/busybox/coreutils/pwd.c b/busybox/coreutils/pwd.c
new file mode 100644 (file)
index 0000000..7e0dc05
--- /dev/null
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini pwd implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int pwd_main(int argc, char **argv)
+{
+       char *buf;
+
+       if ((buf = xgetcwd(NULL)) != NULL) {
+               puts(buf);
+               bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+       }
+
+       return EXIT_FAILURE;
+}
diff --git a/busybox/coreutils/realpath.c b/busybox/coreutils/realpath.c
new file mode 100644 (file)
index 0000000..ec98221
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Now does proper error checking on output and returns a failure exit code
+ * if one or more paths can not be resolved.
+ */
+
+#include <limits.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int realpath_main(int argc, char **argv)
+{
+       int retval = EXIT_SUCCESS;
+
+       RESERVE_CONFIG_BUFFER(resolved_path, PATH_MAX);
+
+       if (--argc == 0) {
+               bb_show_usage();
+       }
+
+       do {
+               argv++;
+               if (realpath(*argv, resolved_path) != NULL) {
+                       puts(resolved_path);
+               } else {
+                       retval = EXIT_FAILURE;
+                       bb_perror_msg("%s", *argv);
+               }
+       } while (--argc);
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       RELEASE_CONFIG_BUFFER(resolved_path);
+#endif
+
+       bb_fflush_stdout_and_exit(retval);
+}
diff --git a/busybox/coreutils/rm.c b/busybox/coreutils/rm.c
new file mode 100644 (file)
index 0000000..39609e7
--- /dev/null
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rm implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction.
+ */
+
+#include <unistd.h>
+#include "busybox.h"
+
+extern int rm_main(int argc, char **argv)
+{
+       int status = 0;
+       int flags = 0;
+       unsigned long opt;
+
+       bb_opt_complementaly = "f-i:i-f";
+       opt = bb_getopt_ulflags(argc, argv, "fiRr");
+       if(opt & 1)
+                               flags |= FILEUTILS_FORCE;
+       if(opt & 2)
+               flags |= FILEUTILS_INTERACTIVE;
+       if(opt & 12)
+               flags |= FILEUTILS_RECUR;
+
+       if (*(argv += optind) != NULL) {
+               do {
+                       const char *base = bb_get_last_path_component(*argv);
+
+                       if ((base[0] == '.') && (!base[1] || ((base[1] == '.') && !base[2]))) {
+                               bb_error_msg("cannot remove `.' or `..'");
+                       } else if (remove_file(*argv, flags) >= 0) {
+                               continue;
+                       }
+                       status = 1;
+               } while (*++argv);
+       } else if (!(flags & FILEUTILS_FORCE)) {
+               bb_show_usage();
+       }
+
+       return status;
+}
diff --git a/busybox/coreutils/rmdir.c b/busybox/coreutils/rmdir.c
new file mode 100644 (file)
index 0000000..a10e5bb
--- /dev/null
@@ -0,0 +1,73 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rmdir implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <libgen.h>
+#include "busybox.h"
+
+extern int rmdir_main(int argc, char **argv)
+{
+       int status = EXIT_SUCCESS;
+       int flags;
+       int do_dot;
+       char *path;
+
+       flags = bb_getopt_ulflags(argc, argv, "p");
+
+       argv += optind;
+
+       if (!*argv) {
+               bb_show_usage();
+       }
+
+       do {
+               path = *argv;
+
+               /* Record if the first char was a '.' so we can use dirname later. */
+               do_dot = (*path == '.');
+
+               do {
+                       if (rmdir(path) < 0) {
+                               bb_perror_msg("`%s'", path);    /* Match gnu rmdir msg. */
+                               status = EXIT_FAILURE;
+                       } else if (flags) {
+                               /* Note: path was not empty or null since rmdir succeeded. */
+                               path = dirname(path);
+                               /* Path is now just the parent component.  Note that dirname
+                                * returns "." if there are no parents.  We must distinguish
+                                * this from the case of the original path starting with '.'.
+                 */
+                               if (do_dot || (*path != '.') || path[1]) {
+                                       continue;
+                               }
+                       }
+                       break;
+               } while (1);
+
+       } while (*++argv);
+
+       return status;
+}
diff --git a/busybox/coreutils/seq.c b/busybox/coreutils/seq.c
new file mode 100644 (file)
index 0000000..8006be8
--- /dev/null
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * seq implementation for busybox
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int seq_main(int argc, char **argv)
+{
+       double last;
+       double first = 1;
+       double increment = 1;
+       double i;
+
+       if (argc == 4) {
+               first = atof(argv[1]);
+               increment = atof(argv[2]);
+       } else if (argc == 3) {
+               first = atof(argv[1]);
+       } else if (argc != 2) {
+               bb_show_usage();
+       }
+       last = atof(argv[argc - 1]);
+
+       /* You should note that this is pos-5.0.91 semantics, -- FK. */
+       if ((first > last) && (increment > 0)) {
+               return EXIT_SUCCESS;
+       }
+
+       for (i = first; ((first <= last) ? (i <= last) : (i >= last));
+            i += increment) {
+               printf("%g\n", i);
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/sleep.c b/busybox/coreutils/sleep.c
new file mode 100644 (file)
index 0000000..506192d
--- /dev/null
@@ -0,0 +1,86 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sleep implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Rewritten to do proper arg and error checking.
+ * Also, added a 'fancy' configuration to accept multiple args with
+ * time suffixes for seconds, minutes, hours, and days.
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_FANCY_SLEEP
+static const struct suffix_mult sleep_suffixes[] = {
+       { "s", 1 },
+       { "m", 60 },
+       { "h", 60*60 },
+       { "d", 24*60*60 },
+       { NULL, 0 }
+};
+#endif
+
+extern int sleep_main(int argc, char **argv)
+{
+       unsigned int duration;
+
+#ifdef CONFIG_FEATURE_FANCY_SLEEP
+
+       if (argc < 2) {
+               bb_show_usage();
+       }
+
+       ++argv;
+       duration = 0;
+       do {
+               duration += bb_xgetularg_bnd_sfx(*argv, 10,
+                                                                                0, UINT_MAX-duration,
+                                                                                sleep_suffixes);
+       } while (*++argv);
+
+#else  /* CONFIG_FEATURE_FANCY_SLEEP */
+
+       if (argc != 2) {
+               bb_show_usage();
+       }
+
+#if UINT_MAX == ULONG_MAX
+       duration = bb_xgetularg10(argv[1]);
+#else
+       duration = bb_xgetularg10_bnd(argv[1], 0, UINT_MAX);
+#endif
+
+#endif /* CONFIG_FEATURE_FANCY_SLEEP */
+
+       if (sleep(duration)) {
+               bb_perror_nomsg_and_die();
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/sort.c b/busybox/coreutils/sort.c
new file mode 100644 (file)
index 0000000..8cc4d88
--- /dev/null
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sort implementation for busybox
+ *
+ * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- a number of options are not supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Now does proper error checking on i/o.  Plus some space savings.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+static int compare_ascii(const void *x, const void *y)
+{
+       return strcmp(*(char **)x, *(char **)y);
+}
+
+static int compare_numeric(const void *x, const void *y)
+{
+       int z = atoi(*(char **)x) - atoi(*(char **)y);
+       return z ? z : strcmp(*(char **)x, *(char **)y);
+}
+
+int sort_main(int argc, char **argv)
+{
+       FILE *fp;
+       char *line, **lines = NULL;
+       int i, nlines = 0, inc;
+       int (*compare)(const void *, const void *) = compare_ascii;
+
+       int flags;
+
+       bb_default_error_retval = 2;
+
+       flags = bb_getopt_ulflags(argc, argv, "nru");
+       if (flags & 1) {
+               compare = compare_numeric;
+       }
+
+       argv += optind;
+       if (!*argv) {
+               *--argv = "-";
+       }
+
+       do {
+               fp = xgetoptfile_sort_uniq(argv, "r");
+               while ((line = bb_get_chomped_line_from_file(fp)) != NULL) {
+                       lines = xrealloc(lines, sizeof(char *) * (nlines + 1));
+                       lines[nlines++] = line;
+               }
+               bb_xferror(fp, *argv);
+               bb_fclose_nonstdin(fp);
+       } while (*++argv);
+
+       /* sort it */
+       qsort(lines, nlines, sizeof(char *), compare);
+
+       /* print it */
+       i = 0;
+       --nlines;
+       if ((inc = 1 - (flags & 2)) < 0) {      /* reverse */
+               i = nlines;
+       }
+       flags &= 4;
+
+       while (nlines >= 0) {
+               if (!flags || !nlines || strcmp(lines[i+inc], lines[i])) {
+                       puts(lines[i]);
+               }
+               i += inc;
+               --nlines;
+       }
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/stty.c b/busybox/coreutils/stty.c
new file mode 100644 (file)
index 0000000..d620686
--- /dev/null
@@ -0,0 +1,1314 @@
+/* vi: set sw=4 ts=4: */
+/* stty -- change and print terminal line settings
+   Copyright (C) 1990-1999 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Usage: stty [-ag] [-F device] [setting...]
+
+   Options:
+   -a Write all current settings to stdout in human-readable form.
+   -g Write all current settings to stdout in stty-readable form.
+   -F Open and use the specified device instead of stdin
+
+   If no args are given, write to stdout the baud rate and settings that
+   have been changed from their defaults.  Mode reading and changes
+   are done on the specified device, or stdin if none was specified.
+
+   David MacKenzie <djm@gnu.ai.mit.edu>
+
+   Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+
+   */
+
+//#define TEST
+
+#include <stddef.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include <sys/param.h>
+#include <unistd.h>
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <memory.h>
+#include <fcntl.h>
+#include "busybox.h"
+
+#define STREQ(a, b) (strcmp ((a), (b)) == 0)
+
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE ((unsigned char) 0)
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters. */
+#ifndef CINTR
+# define CINTR Control ('c')
+#endif
+#ifndef CQUIT
+# define CQUIT 28
+#endif
+#ifndef CERASE
+# define CERASE 127
+#endif
+#ifndef CKILL
+# define CKILL Control ('u')
+#endif
+#ifndef CEOF
+# define CEOF Control ('d')
+#endif
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+# define CSTART Control ('q')
+#endif
+#ifndef CSTOP
+# define CSTOP Control ('s')
+#endif
+#ifndef CSUSP
+# define CSUSP Control ('z')
+#endif
+#if defined(VEOL2) && !defined(CEOL2)
+# define CEOL2 _POSIX_VDISABLE
+#endif
+/* ISC renamed swtch to susp for termios, but we'll accept either name.  */
+#if defined(VSUSP) && !defined(VSWTCH)
+# define VSWTCH VSUSP
+# define CSWTCH CSUSP
+#endif
+#if defined(VSWTCH) && !defined(CSWTCH)
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
+   So the default is to disable `swtch.'  */
+#if defined (__sparc__) && defined (__svr4__)
+# undef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+#if defined(VWERSE) && !defined (VWERASE)       /* AIX-3.2.5 */
+# define VWERASE VWERSE
+#endif
+#if defined(VDSUSP) && !defined (CDSUSP)
+# define CDSUSP Control ('y')
+#endif
+#if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
+# define VREPRINT VRPRNT
+#endif
+#if defined(VREPRINT) && !defined(CRPRNT)
+# define CRPRNT Control ('r')
+#endif
+#if defined(VWERASE) && !defined(CWERASE)
+# define CWERASE Control ('w')
+#endif
+#if defined(VLNEXT) && !defined(CLNEXT)
+# define CLNEXT Control ('v')
+#endif
+#if defined(VDISCARD) && !defined(VFLUSHO)
+# define VFLUSHO VDISCARD
+#endif
+#if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
+# define VFLUSHO VFLUSH
+#endif
+#if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
+# define ECHOCTL CTLECH
+#endif
+#if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
+# define ECHOCTL TCTLECH
+#endif
+#if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
+# define ECHOKE CRTKIL
+#endif
+#if defined(VFLUSHO) && !defined(CFLUSHO)
+# define CFLUSHO Control ('o')
+#endif
+#if defined(VSTATUS) && !defined(CSTATUS)
+# define CSTATUS Control ('t')
+#endif
+
+/* Which speeds to set.  */
+enum speed_setting {
+       input_speed, output_speed, both_speeds
+};
+
+/* Which member(s) of `struct termios' a mode uses.  */
+enum mode_type {
+       /* Do NOT change the order or values, as mode_type_flag()
+        * depends on them. */
+       control, input, output, local, combination
+};
+
+
+static const char evenp     [] = "evenp";
+static const char raw       [] = "raw";
+static const char stty_min  [] = "min";
+static const char stty_time [] = "time";
+static const char stty_swtch[] = "swtch";
+static const char stty_eol  [] = "eol";
+static const char stty_eof  [] = "eof";
+static const char parity    [] = "parity";
+static const char stty_oddp [] = "oddp";
+static const char stty_nl   [] = "nl";
+static const char stty_ek   [] = "ek";
+static const char stty_sane [] = "sane";
+static const char cbreak    [] = "cbreak";
+static const char stty_pass8[] = "pass8";
+static const char litout    [] = "litout";
+static const char cooked    [] = "cooked";
+static const char decctlq   [] = "decctlq";
+static const char stty_tabs [] = "tabs";
+static const char stty_lcase[] = "lcase";
+static const char stty_LCASE[] = "LCASE";
+static const char stty_crt  [] = "crt";
+static const char stty_dec  [] = "dec";
+
+
+/* Flags for `struct mode_info'. */
+#define SANE_SET 1              /* Set in `sane' mode.                  */
+#define SANE_UNSET 2            /* Unset in `sane' mode.                */
+#define REV 4                   /* Can be turned off by prepending `-'. */
+#define OMIT 8                  /* Don't display value.                 */
+
+/* Each mode.  */
+struct mode_info {
+       const char *name;       /* Name given on command line.           */
+       /* enum mode_type type; */
+       char type;              /* Which structure element to change.    */
+       char flags;             /* Setting and display options.          */
+       unsigned short mask;     /* Other bits to turn off for this mode. */
+       unsigned long bits;     /* Bits to set for this mode.            */
+};
+
+#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
+
+static const struct  mode_info mode_info[] = {
+       MI_ENTRY("parenb",   control,     REV,               PARENB,     0 ),
+       MI_ENTRY("parodd",   control,     REV,               PARODD,     0 ),
+       MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE),
+       MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE),
+       MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE),
+       MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE),
+       MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 ),
+       MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 ),
+       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 ),
+       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 ),
+       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 ),
+#ifdef CRTSCTS
+       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 ),
+#endif
+       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 ),
+       MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 ),
+       MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 ),
+       MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 ),
+       MI_ENTRY("inpck",    input,       REV,               INPCK,      0 ),
+       MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 ),
+       MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 ),
+       MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 ),
+       MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 ),
+       MI_ENTRY("ixon",     input,       REV,               IXON,       0 ),
+       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 ),
+       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 ),
+#ifdef IUCLC
+       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 ),
+#endif
+#ifdef IXANY
+       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 ),
+#endif
+#ifdef IMAXBEL
+       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 ),
+#endif
+       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 ),
+#ifdef OLCUC
+       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 ),
+#endif
+#ifdef OCRNL
+       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 ),
+#endif
+#ifdef ONLCR
+       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 ),
+#endif
+#ifdef ONOCR
+       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 ),
+#endif
+#ifdef ONLRET
+       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 ),
+#endif
+#ifdef OFILL
+       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 ),
+#endif
+#ifdef OFDEL
+       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 ),
+#endif
+#ifdef NLDLY
+       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY),
+       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY),
+#endif
+#ifdef CRDLY
+       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY),
+       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY),
+       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY),
+       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY),
+#endif
+
+#ifdef TABDLY
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY),
+       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY),
+       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY),
+       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY),
+#else
+# ifdef OXTABS
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 ),
+# endif
+#endif
+
+#ifdef BSDLY
+       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY),
+       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY),
+#endif
+#ifdef VTDLY
+       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY),
+       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY),
+#endif
+#ifdef FFDLY
+       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY),
+       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY),
+#endif
+       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 ),
+       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 ),
+#ifdef IEXTEN
+       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 ),
+#endif
+       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 ),
+       MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 ),
+       MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 ),
+       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 ),
+       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 ),
+       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 ),
+#ifdef XCASE
+       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 ),
+#endif
+#ifdef TOSTOP
+       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 ),
+#endif
+#ifdef ECHOPRT
+       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 ),
+       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 ),
+#endif
+#ifdef ECHOCTL
+       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 ),
+       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 ),
+#endif
+#ifdef ECHOKE
+       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 ),
+       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 ),
+#endif
+       MI_ENTRY(evenp,      combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(parity,     combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(stty_oddp,  combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(stty_nl,    combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(stty_ek,    combination, OMIT,              0,          0 ),
+       MI_ENTRY(stty_sane,  combination, OMIT,              0,          0 ),
+       MI_ENTRY(cooked,     combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(raw,        combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(stty_pass8, combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(litout,     combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(cbreak,     combination, REV        | OMIT, 0,          0 ),
+#ifdef IXANY
+       MI_ENTRY(decctlq,    combination, REV        | OMIT, 0,          0 ),
+#endif
+#if defined (TABDLY) || defined (OXTABS)
+       MI_ENTRY(stty_tabs,  combination, REV        | OMIT, 0,          0 ),
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+       MI_ENTRY(stty_lcase, combination, REV        | OMIT, 0,          0 ),
+       MI_ENTRY(stty_LCASE, combination, REV        | OMIT, 0,          0 ),
+#endif
+       MI_ENTRY(stty_crt,   combination, OMIT,              0,          0 ),
+       MI_ENTRY(stty_dec,   combination, OMIT,              0,          0 ),
+};
+
+static const int NUM_mode_info =
+
+       (sizeof(mode_info) / sizeof(struct mode_info));
+
+/* Control character settings.  */
+struct control_info {
+       const char *name;                       /* Name given on command line.  */
+       unsigned char saneval;          /* Value to set for `stty sane'.  */
+       unsigned char offset;                           /* Offset in c_cc.  */
+};
+
+/* Control characters. */
+
+static const struct  control_info control_info[] = {
+       {"intr",     CINTR,   VINTR},
+       {"quit",     CQUIT,   VQUIT},
+       {"erase",    CERASE,  VERASE},
+       {"kill",     CKILL,   VKILL},
+       {stty_eof,   CEOF,    VEOF},
+       {stty_eol,   CEOL,    VEOL},
+#ifdef VEOL2
+       {"eol2",     CEOL2,   VEOL2},
+#endif
+#ifdef VSWTCH
+       {stty_swtch, CSWTCH,  VSWTCH},
+#endif
+       {"start",    CSTART,  VSTART},
+       {"stop",     CSTOP,   VSTOP},
+       {"susp",     CSUSP,   VSUSP},
+#ifdef VDSUSP
+       {"dsusp",    CDSUSP,  VDSUSP},
+#endif
+#ifdef VREPRINT
+       {"rprnt",    CRPRNT,  VREPRINT},
+#endif
+#ifdef VWERASE
+       {"werase",   CWERASE, VWERASE},
+#endif
+#ifdef VLNEXT
+       {"lnext",    CLNEXT,  VLNEXT},
+#endif
+#ifdef VFLUSHO
+       {"flush",    CFLUSHO, VFLUSHO},
+#endif
+#ifdef VSTATUS
+       {"status",   CSTATUS, VSTATUS},
+#endif
+       /* These must be last because of the display routines. */
+       {stty_min,   1,       VMIN},
+       {stty_time,  0,       VTIME},
+};
+
+static const int NUM_control_info =
+       (sizeof(control_info) / sizeof(struct control_info));
+
+
+static const char *  visible(unsigned int ch);
+static int           recover_mode(char *arg, struct termios *mode);
+static int           screen_columns(void);
+static int           set_mode(const struct mode_info *info,
+                                       int reversed, struct termios *mode);
+static speed_t       string_to_baud(const char *arg);
+static tcflag_t*     mode_type_flag(enum mode_type type, struct termios *mode);
+static void          display_all(struct termios *mode, int fd);
+static void          display_changed(struct termios *mode, int fd);
+static void          display_recoverable(struct termios *mode, int fd);
+static void          display_speed(struct termios *mode, int fancy);
+static void          display_window_size(int fancy, int fd);
+static void          sane_mode(struct termios *mode);
+static void          set_control_char(const struct control_info *info,
+                                       const char *arg, struct termios *mode);
+static void          set_speed(enum speed_setting type,
+                                       const char *arg, struct termios *mode);
+static void          set_window_size(int rows, int cols, int fd);
+
+static const char *device_name;
+
+static __attribute__ ((noreturn)) void perror_on_device(const char *fmt)
+{
+       bb_perror_msg_and_die(fmt, device_name);
+}
+
+
+/* The width of the screen, for output wrapping. */
+static int max_col;
+
+/* Current position, to know when to wrap. */
+static int current_col;
+
+/* Print format string MESSAGE and optional args.
+   Wrap to next line first if it won't fit.
+   Print a space first unless MESSAGE will start a new line. */
+
+static void wrapf(const char *message, ...)
+{
+       va_list args;
+       char buf[1024];                 /* Plenty long for our needs. */
+       int buflen;
+
+       va_start(args, message);
+       vsprintf(buf, message, args);
+       va_end(args);
+       buflen = strlen(buf);
+       if (current_col + (current_col > 0) + buflen >= max_col) {
+               putchar('\n');
+               current_col = 0;
+       }
+       if (current_col > 0) {
+               putchar(' ');
+               current_col++;
+       }
+       fputs(buf, stdout);
+       current_col += buflen;
+}
+
+static const struct suffix_mult stty_suffixes[] = {
+       {"b",  512 },
+       {"k",  1024},
+       {"B",  1024},
+       {NULL, 0   }
+};
+
+#ifndef TEST
+extern int stty_main(int argc, char **argv)
+#else
+extern int main(int argc, char **argv)
+#endif
+{
+       struct termios mode;
+       void (*output_func)(struct termios *, int);
+       int    optc;
+       int    require_set_attr;
+       int    speed_was_set;
+       int    verbose_output;
+       int    recoverable_output;
+       int    k;
+       int    noargs = 1;
+       char * file_name = NULL;
+       int    fd;
+
+
+       output_func = display_changed;
+       verbose_output = 0;
+       recoverable_output = 0;
+
+       /* Don't print error messages for unrecognized options.  */
+       opterr = 0;
+
+       while ((optc = getopt(argc, argv, "agF:")) != -1) {
+               switch (optc) {
+               case 'a':
+                       verbose_output = 1;
+                       output_func = display_all;
+                       break;
+
+               case 'g':
+                       recoverable_output = 1;
+                       output_func = display_recoverable;
+                       break;
+
+               case 'F':
+                       if (file_name)
+                               bb_error_msg_and_die("only one device may be specified");
+                       file_name = optarg;
+                       break;
+
+               default:                /* unrecognized option */
+                       noargs = 0;
+                       break;
+               }
+
+               if (noargs == 0)
+                       break;
+       }
+
+       if (optind < argc)
+               noargs = 0;
+
+       /* Specifying both -a and -g gets an error.  */
+       if (verbose_output & recoverable_output)
+               bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
+
+       /* Specifying any other arguments with -a or -g gets an error.  */
+       if (~noargs & (verbose_output | recoverable_output))
+               bb_error_msg_and_die ("modes may not be set when specifying an output style");
+
+       /* FIXME: it'd be better not to open the file until we've verified
+          that all arguments are valid.  Otherwise, we could end up doing
+          only some of the requested operations and then failing, probably
+          leaving things in an undesirable state.  */
+
+       if (file_name) {
+               int fdflags;
+
+               device_name = file_name;
+               fd = bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
+               if ((fdflags = fcntl(fd, F_GETFL)) == -1
+                       || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+                       perror_on_device("%s: couldn't reset non-blocking mode");
+       } else {
+               fd = 0;
+               device_name = bb_msg_standard_input;
+       }
+
+       /* Initialize to all zeroes so there is no risk memcmp will report a
+          spurious difference in an uninitialized portion of the structure.  */
+       memset(&mode, 0, sizeof(mode));
+       if (tcgetattr(fd, &mode))
+               perror_on_device("%s");
+
+       if (verbose_output | recoverable_output | noargs) {
+               max_col = screen_columns();
+               current_col = 0;
+               output_func(&mode, fd);
+               return EXIT_SUCCESS;
+       }
+
+       speed_was_set = 0;
+       require_set_attr = 0;
+       k = 0;
+       while (++k < argc) {
+               int match_found = 0;
+               int reversed = 0;
+               int i;
+
+               if (argv[k][0] == '-') {
+                       char *find_dev_opt;
+
+                       ++argv[k];
+
+     /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
+       Find the options that have been parsed.  This is really
+       gross, but it's needed because stty SETTINGS look like options to
+       getopt(), so we need to work around things in a really horrible
+       way.  If any new options are ever added to stty, the short option
+       MUST NOT be a letter which is the first letter of one of the
+       possible stty settings.
+     */
+                       find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
+                       if(find_dev_opt) {
+                               if(find_dev_opt[1]==0)  /* -*F   /dev/foo */
+                                       k++;            /* skip  /dev/foo */
+                               continue;   /* else -*F/dev/foo - no skip */
+                       }
+                       if(argv[k][0]=='a' || argv[k][0]=='g')
+                               continue;
+                       /* Is not options - is reverse params */
+                       reversed = 1;
+               }
+               for (i = 0; i < NUM_mode_info; ++i)
+                       if (STREQ(argv[k], mode_info[i].name)) {
+                               match_found = set_mode(&mode_info[i], reversed, &mode);
+                               require_set_attr = 1;
+                               break;
+                       }
+
+               if (match_found == 0 && reversed)
+                       bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
+
+               if (match_found == 0)
+                       for (i = 0; i < NUM_control_info; ++i)
+                               if (STREQ(argv[k], control_info[i].name)) {
+                                       if (k == argc - 1)
+                                           bb_error_msg_and_die("missing argument to `%s'", argv[k]);
+                                       match_found = 1;
+                                       ++k;
+                                       set_control_char(&control_info[i], argv[k], &mode);
+                                       require_set_attr = 1;
+                                       break;
+                               }
+
+               if (match_found == 0) {
+                       if (STREQ(argv[k], "ispeed")) {
+                               if (k == argc - 1)
+                                   bb_error_msg_and_die("missing argument to `%s'", argv[k]);
+                               ++k;
+                               set_speed(input_speed, argv[k], &mode);
+                               speed_was_set = 1;
+                               require_set_attr = 1;
+                       } else if (STREQ(argv[k], "ospeed")) {
+                               if (k == argc - 1)
+                                   bb_error_msg_and_die("missing argument to `%s'", argv[k]);
+                               ++k;
+                               set_speed(output_speed, argv[k], &mode);
+                               speed_was_set = 1;
+                               require_set_attr = 1;
+                       }
+#ifdef TIOCGWINSZ
+                       else if (STREQ(argv[k], "rows")) {
+                               if (k == argc - 1)
+                                   bb_error_msg_and_die("missing argument to `%s'", argv[k]);
+                               ++k;
+                               set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
+                                                               -1, fd);
+                       } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
+                               if (k == argc - 1)
+                                   bb_error_msg_and_die("missing argument to `%s'", argv[k]);
+                               ++k;
+                               set_window_size(-1,
+                                               (int) bb_xparse_number(argv[k], stty_suffixes),
+                                               fd);
+                       } else if (STREQ(argv[k], "size")) {
+                               max_col = screen_columns();
+                               current_col = 0;
+                               display_window_size(0, fd);
+                       }
+#endif
+#ifdef HAVE_C_LINE
+                       else if (STREQ(argv[k], "line")) {
+                               if (k == argc - 1)
+                                       bb_error_msg_and_die("missing argument to `%s'", argv[k]);
+                               ++k;
+                               mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
+                               require_set_attr = 1;
+                       }
+#endif
+                       else if (STREQ(argv[k], "speed")) {
+                               max_col = screen_columns();
+                               display_speed(&mode, 0);
+                       } else if (recover_mode(argv[k], &mode) == 1)
+                               require_set_attr = 1;
+                       else if (string_to_baud(argv[k]) != (speed_t) - 1) {
+                               set_speed(both_speeds, argv[k], &mode);
+                               speed_was_set = 1;
+                               require_set_attr = 1;
+                       } else
+                               bb_error_msg_and_die("invalid argument `%s'", argv[k]);
+               }
+       }
+
+       if (require_set_attr) {
+               struct termios new_mode;
+
+               if (tcsetattr(fd, TCSADRAIN, &mode))
+                       perror_on_device("%s");
+
+               /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
+                  it performs *any* of the requested operations.  This means it
+                  can report `success' when it has actually failed to perform
+                  some proper subset of the requested operations.  To detect
+                  this partial failure, get the current terminal attributes and
+                  compare them to the requested ones.  */
+
+               /* Initialize to all zeroes so there is no risk memcmp will report a
+                  spurious difference in an uninitialized portion of the structure.  */
+               memset(&new_mode, 0, sizeof(new_mode));
+               if (tcgetattr(fd, &new_mode))
+                       perror_on_device("%s");
+
+               /* Normally, one shouldn't use memcmp to compare structures that
+                  may have `holes' containing uninitialized data, but we have been
+                  careful to initialize the storage of these two variables to all
+                  zeroes.  One might think it more efficient simply to compare the
+                  modified fields, but that would require enumerating those fields --
+                  and not all systems have the same fields in this structure.  */
+
+               if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
+#ifdef CIBAUD
+                       /* SunOS 4.1.3 (at least) has the problem that after this sequence,
+                          tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
+                          sometimes (m1 != m2).  The only difference is in the four bits
+                          of the c_cflag field corresponding to the baud rate.  To save
+                          Sun users a little confusion, don't report an error if this
+                          happens.  But suppress the error only if we haven't tried to
+                          set the baud rate explicitly -- otherwise we'd never give an
+                          error for a true failure to set the baud rate.  */
+
+                       new_mode.c_cflag &= (~CIBAUD);
+                       if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
+#endif
+                               perror_on_device ("%s: unable to perform all requested operations");
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
+
+/* Return 0 if not applied because not reversible; otherwise return 1.  */
+
+static int
+set_mode(const struct mode_info *info, int reversed, struct termios *mode)
+{
+       tcflag_t *bitsp;
+
+       if (reversed && (info->flags & REV) == 0)
+               return 0;
+
+       bitsp = mode_type_flag(info->type, mode);
+
+       if (bitsp == NULL) {
+               /* Combination mode. */
+               if (info->name == evenp || info->name == parity) {
+                       if (reversed)
+                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+                       else
+                               mode->c_cflag =
+                                       (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+               } else if (info->name == stty_oddp) {
+                       if (reversed)
+                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+                       else
+                               mode->c_cflag =
+                                       (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+               } else if (info->name == stty_nl) {
+                       if (reversed) {
+                               mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
+                               mode->c_oflag = (mode->c_oflag
+#ifdef ONLCR
+                                                                | ONLCR
+#endif
+                                       )
+#ifdef OCRNL
+                                       & ~OCRNL
+#endif
+#ifdef ONLRET
+                                       & ~ONLRET
+#endif
+                                       ;
+                       } else {
+                               mode->c_iflag = mode->c_iflag & ~ICRNL;
+#ifdef ONLCR
+                               mode->c_oflag = mode->c_oflag & ~ONLCR;
+#endif
+                       }
+               } else if (info->name == stty_ek) {
+                       mode->c_cc[VERASE] = CERASE;
+                       mode->c_cc[VKILL] = CKILL;
+               } else if (info->name == stty_sane)
+                       sane_mode(mode);
+               else if (info->name == cbreak) {
+                       if (reversed)
+                               mode->c_lflag |= ICANON;
+                       else
+                               mode->c_lflag &= ~ICANON;
+               } else if (info->name == stty_pass8) {
+                       if (reversed) {
+                               mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+                               mode->c_iflag |= ISTRIP;
+                       } else {
+                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+                               mode->c_iflag &= ~ISTRIP;
+                       }
+               } else if (info->name == litout) {
+                       if (reversed) {
+                               mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+                               mode->c_iflag |= ISTRIP;
+                               mode->c_oflag |= OPOST;
+                       } else {
+                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+                               mode->c_iflag &= ~ISTRIP;
+                               mode->c_oflag &= ~OPOST;
+                       }
+               } else if (info->name == raw || info->name == cooked) {
+                       if ((info->name[0] == 'r' && reversed)
+                               || (info->name[0] == 'c' && !reversed)) {
+                               /* Cooked mode. */
+                               mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+                               mode->c_oflag |= OPOST;
+                               mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+                               mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+                               mode->c_cc[VEOL] = CEOL;
+#endif
+                       } else {
+                               /* Raw mode. */
+                               mode->c_iflag = 0;
+                               mode->c_oflag &= ~OPOST;
+                               mode->c_lflag &= ~(ISIG | ICANON
+#ifdef XCASE
+                                                                  | XCASE
+#endif
+                                       );
+                               mode->c_cc[VMIN] = 1;
+                               mode->c_cc[VTIME] = 0;
+                       }
+               }
+#ifdef IXANY
+               else if (info->name == decctlq) {
+                       if (reversed)
+                               mode->c_iflag |= IXANY;
+                       else
+                               mode->c_iflag &= ~IXANY;
+               }
+#endif
+#ifdef TABDLY
+               else if (info->name == stty_tabs) {
+                       if (reversed)
+                               mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+                       else
+                               mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+               }
+#else
+# ifdef OXTABS
+               else if (info->name == stty_tabs) {
+                       if (reversed)
+                               mode->c_oflag = mode->c_oflag | OXTABS;
+                       else
+                               mode->c_oflag = mode->c_oflag & ~OXTABS;
+               }
+# endif
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+               else if (info->name == stty_lcase || info->name == stty_LCASE) {
+                       if (reversed) {
+                               mode->c_lflag &= ~XCASE;
+                               mode->c_iflag &= ~IUCLC;
+                               mode->c_oflag &= ~OLCUC;
+                       } else {
+                               mode->c_lflag |= XCASE;
+                               mode->c_iflag |= IUCLC;
+                               mode->c_oflag |= OLCUC;
+                       }
+               }
+#endif
+               else if (info->name == stty_crt)
+                       mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+                               | ECHOCTL
+#endif
+#ifdef ECHOKE
+                               | ECHOKE
+#endif
+                               ;
+               else if (info->name == stty_dec) {
+                       mode->c_cc[VINTR] = 3;  /* ^C */
+                       mode->c_cc[VERASE] = 127;       /* DEL */
+                       mode->c_cc[VKILL] = 21; /* ^U */
+                       mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+                               | ECHOCTL
+#endif
+#ifdef ECHOKE
+                               | ECHOKE
+#endif
+                               ;
+#ifdef IXANY
+                       mode->c_iflag &= ~IXANY;
+#endif
+               }
+       } else if (reversed)
+               *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
+       else
+               *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
+
+       return 1;
+}
+
+static void
+set_control_char(const struct control_info *info, const char *arg,
+                                struct termios *mode)
+{
+       unsigned char value;
+
+       if (info->name == stty_min || info->name == stty_time)
+               value = bb_xparse_number(arg, stty_suffixes);
+       else if (arg[0] == '\0' || arg[1] == '\0')
+               value = arg[0];
+       else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
+               value = _POSIX_VDISABLE;
+       else if (arg[0] == '^' && arg[1] != '\0') {     /* Ignore any trailing junk. */
+               if (arg[1] == '?')
+                       value = 127;
+               else
+                       value = arg[1] & ~0140; /* Non-letters get weird results. */
+       } else
+               value = bb_xparse_number(arg, stty_suffixes);
+       mode->c_cc[info->offset] = value;
+}
+
+static void
+set_speed(enum speed_setting type, const char *arg, struct termios *mode)
+{
+       speed_t baud;
+
+       baud = string_to_baud(arg);
+
+       if (type != output_speed) {     /* either input or both */
+               cfsetispeed(mode, baud);
+       }
+       if (type != input_speed) {      /* either output or both */
+               cfsetospeed(mode, baud);
+       }
+}
+
+#ifdef TIOCGWINSZ
+
+static int get_win_size(int fd, struct winsize *win)
+{
+       int err = ioctl(fd, TIOCGWINSZ, (char *) win);
+
+       return err;
+}
+
+static void
+set_window_size(int rows, int cols, int fd)
+{
+       struct winsize win;
+
+       if (get_win_size(fd, &win)) {
+               if (errno != EINVAL)
+                       perror_on_device("%s");
+               memset(&win, 0, sizeof(win));
+       }
+
+       if (rows >= 0)
+               win.ws_row = rows;
+       if (cols >= 0)
+               win.ws_col = cols;
+
+# ifdef TIOCSSIZE
+       /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
+          The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
+          This comment from sys/ttold.h describes Sun's twisted logic - a better
+          test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
+          At any rate, the problem is gone in Solaris 2.x. */
+
+       if (win.ws_row == 0 || win.ws_col == 0) {
+               struct ttysize ttysz;
+
+               ttysz.ts_lines = win.ws_row;
+               ttysz.ts_cols = win.ws_col;
+
+               win.ws_row = win.ws_col = 1;
+
+               if ((ioctl(fd, TIOCSWINSZ, (char *) &win) != 0)
+                       || (ioctl(fd, TIOCSSIZE, (char *) &ttysz) != 0)) {
+                       perror_on_device("%s");
+               }
+               return;
+       }
+# endif
+
+       if (ioctl(fd, TIOCSWINSZ, (char *) &win))
+               perror_on_device("%s");
+}
+
+static void display_window_size(int fancy, int fd)
+{
+       const char *fmt_str = "%s" "\0" "%s: no size information for this device";
+       struct winsize win;
+
+       if (get_win_size(fd, &win)) {
+               if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
+                       perror_on_device(fmt_str);
+               }
+       } else {
+               wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
+                         win.ws_row, win.ws_col);
+               if (!fancy)
+                       current_col = 0;
+       }
+}
+#endif
+
+static int screen_columns(void)
+{
+       int columns;
+       const char *s;
+
+#ifdef TIOCGWINSZ
+       struct winsize win;
+
+       /* With Solaris 2.[123], this ioctl fails and errno is set to
+          EINVAL for telnet (but not rlogin) sessions.
+          On ISC 3.0, it fails for the console and the serial port
+          (but it works for ptys).
+          It can also fail on any system when stdout isn't a tty.
+          In case of any failure, just use the default.  */
+       if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
+               return win.ws_col;
+#endif
+
+       columns = 80;
+       if ((s = getenv("COLUMNS"))) {
+               columns = atoi(s);
+       }
+       return columns;
+}
+
+static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
+{
+       static const unsigned char tcflag_offsets[] = {
+               offsetof(struct termios, c_cflag), /* control */
+               offsetof(struct termios, c_iflag), /* input */
+               offsetof(struct termios, c_oflag), /* output */
+               offsetof(struct termios, c_lflag) /* local */
+       };
+
+       if (((unsigned int) type) <= local) {
+               return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
+       }
+       return NULL;
+}
+
+static void display_changed(struct termios *mode, int fd)
+{
+       int i;
+       int empty_line;
+       tcflag_t *bitsp;
+       unsigned long mask;
+       enum mode_type prev_type = control;
+
+       display_speed(mode, 1);
+#ifdef HAVE_C_LINE
+       wrapf("line = %d;", mode->c_line);
+#endif
+       putchar('\n');
+       current_col = 0;
+
+       empty_line = 1;
+       for (i = 0; control_info[i].name != stty_min; ++i) {
+               if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
+                       continue;
+               /* If swtch is the same as susp, don't print both.  */
+#if VSWTCH == VSUSP
+               if (control_info[i].name == stty_swtch)
+                       continue;
+#endif
+               /* If eof uses the same slot as min, only print whichever applies.  */
+#if VEOF == VMIN
+               if ((mode->c_lflag & ICANON) == 0
+                       && (control_info[i].name == stty_eof
+                               || control_info[i].name == stty_eol)) continue;
+#endif
+
+               empty_line = 0;
+               wrapf("%s = %s;", control_info[i].name,
+                         visible(mode->c_cc[control_info[i].offset]));
+       }
+       if ((mode->c_lflag & ICANON) == 0) {
+               wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
+                         (int) mode->c_cc[VTIME]);
+       } else if (empty_line == 0)
+               putchar('\n');
+       current_col = 0;
+
+       empty_line = 1;
+       for (i = 0; i < NUM_mode_info; ++i) {
+               if (mode_info[i].flags & OMIT)
+                       continue;
+               if (mode_info[i].type != prev_type) {
+                       if (empty_line == 0) {
+                               putchar('\n');
+                               current_col = 0;
+                               empty_line = 1;
+                       }
+                       prev_type = mode_info[i].type;
+               }
+
+               bitsp = mode_type_flag(mode_info[i].type, mode);
+               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+               if ((*bitsp & mask) == mode_info[i].bits) {
+                       if (mode_info[i].flags & SANE_UNSET) {
+                               wrapf("%s", mode_info[i].name);
+                               empty_line = 0;
+                       }
+               }
+                       else if ((mode_info[i].flags & (SANE_SET | REV)) ==
+                                        (SANE_SET | REV)) {
+                       wrapf("-%s", mode_info[i].name);
+                       empty_line = 0;
+               }
+       }
+       if (empty_line == 0)
+               putchar('\n');
+       current_col = 0;
+}
+
+static void
+display_all(struct termios *mode, int fd)
+{
+       int i;
+       tcflag_t *bitsp;
+       unsigned long mask;
+       enum mode_type prev_type = control;
+
+       display_speed(mode, 1);
+#ifdef TIOCGWINSZ
+       display_window_size(1, fd);
+#endif
+#ifdef HAVE_C_LINE
+       wrapf("line = %d;", mode->c_line);
+#endif
+       putchar('\n');
+       current_col = 0;
+
+       for (i = 0; control_info[i].name != stty_min; ++i) {
+               /* If swtch is the same as susp, don't print both.  */
+#if VSWTCH == VSUSP
+               if (control_info[i].name == stty_swtch)
+                       continue;
+#endif
+               /* If eof uses the same slot as min, only print whichever applies.  */
+#if VEOF == VMIN
+               if ((mode->c_lflag & ICANON) == 0
+                       && (control_info[i].name == stty_eof
+                               || control_info[i].name == stty_eol)) continue;
+#endif
+               wrapf("%s = %s;", control_info[i].name,
+                         visible(mode->c_cc[control_info[i].offset]));
+       }
+#if VEOF == VMIN
+       if ((mode->c_lflag & ICANON) == 0)
+#endif
+               wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+       if (current_col != 0)
+               putchar('\n');
+       current_col = 0;
+
+       for (i = 0; i < NUM_mode_info; ++i) {
+               if (mode_info[i].flags & OMIT)
+                       continue;
+               if (mode_info[i].type != prev_type) {
+                       putchar('\n');
+                       current_col = 0;
+                       prev_type = mode_info[i].type;
+               }
+
+               bitsp = mode_type_flag(mode_info[i].type, mode);
+               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+               if ((*bitsp & mask) == mode_info[i].bits)
+                       wrapf("%s", mode_info[i].name);
+               else if (mode_info[i].flags & REV)
+                       wrapf("-%s", mode_info[i].name);
+       }
+       putchar('\n');
+       current_col = 0;
+}
+
+static void display_speed(struct termios *mode, int fancy)
+{
+       unsigned long ispeed, ospeed;
+       const char *fmt_str =
+               "%lu %lu\n\0"        "ispeed %lu baud; ospeed %lu baud;\0"
+               "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
+
+       ospeed = ispeed = cfgetispeed(mode);
+       if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
+               ispeed = ospeed;                /* in case ispeed was 0 */
+               fmt_str += 43;
+       }
+       if (fancy) {
+               fmt_str += 9;
+       }
+       wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed));
+       if (!fancy)
+               current_col = 0;
+}
+
+static void display_recoverable(struct termios *mode, int fd)
+{
+       int i;
+
+       printf("%lx:%lx:%lx:%lx",
+                  (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
+                  (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
+       for (i = 0; i < NCCS; ++i)
+               printf(":%x", (unsigned int) mode->c_cc[i]);
+       putchar('\n');
+}
+
+static int recover_mode(char *arg, struct termios *mode)
+{
+       int i, n;
+       unsigned int chr;
+       unsigned long iflag, oflag, cflag, lflag;
+
+       /* Scan into temporaries since it is too much trouble to figure out
+          the right format for `tcflag_t'.  */
+       if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
+                          &iflag, &oflag, &cflag, &lflag, &n) != 4)
+               return 0;
+       mode->c_iflag = iflag;
+       mode->c_oflag = oflag;
+       mode->c_cflag = cflag;
+       mode->c_lflag = lflag;
+       arg += n;
+       for (i = 0; i < NCCS; ++i) {
+               if (sscanf(arg, ":%x%n", &chr, &n) != 1)
+                       return 0;
+               mode->c_cc[i] = chr;
+               arg += n;
+       }
+
+       /* Fail if there are too many fields.  */
+       if (*arg != '\0')
+               return 0;
+
+       return 1;
+}
+
+static speed_t string_to_baud(const char *arg)
+{
+       return bb_value_to_baud(bb_xparse_number(arg, 0));
+}
+
+static void sane_mode(struct termios *mode)
+{
+       int i;
+       tcflag_t *bitsp;
+
+       for (i = 0; i < NUM_control_info; ++i) {
+#if VMIN == VEOF
+               if (control_info[i].name == stty_min)
+                       break;
+#endif
+               mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+       }
+
+       for (i = 0; i < NUM_mode_info; ++i) {
+               if (mode_info[i].flags & SANE_SET) {
+                       bitsp = mode_type_flag(mode_info[i].type, mode);
+                       *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
+                               | mode_info[i].bits;
+               } else if (mode_info[i].flags & SANE_UNSET) {
+                       bitsp = mode_type_flag(mode_info[i].type, mode);
+                       *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
+                               & ~mode_info[i].bits;
+               }
+       }
+}
+
+/* Return a string that is the printable representation of character CH.  */
+/* Adapted from `cat' by Torbjorn Granlund.  */
+
+static const char *visible(unsigned int ch)
+{
+       static char buf[10];
+       char *bpout = buf;
+
+       if (ch == _POSIX_VDISABLE) {
+               return "<undef>";
+       }
+
+       if (ch >= 128) {
+               ch -= 128;
+               *bpout++ = 'M';
+               *bpout++ = '-';
+       }
+
+       if (ch < 32) {
+               *bpout++ = '^';
+               *bpout++ = ch + 64;
+       } else if (ch < 127) {
+               *bpout++ = ch;
+       } else {
+               *bpout++ = '^';
+               *bpout++ = '?';
+       }
+
+       *bpout = '\0';
+       return (const char *) buf;
+}
+
+#ifdef TEST
+
+const char *bb_applet_name = "stty";
+
+#endif
diff --git a/busybox/coreutils/sync.c b/busybox/coreutils/sync.c
new file mode 100644 (file)
index 0000000..8474631
--- /dev/null
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sync implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int sync_main(int argc, char **argv)
+{
+       bb_warn_ignoring_args(argc - 1);
+
+       sync();
+
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/tail.c b/busybox/coreutils/tail.c
new file mode 100644 (file)
index 0000000..e3f89d2
--- /dev/null
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tail implementation for busybox
+ *
+ * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
+/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
+ * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
+ * 1) mixing printf/write without fflush()ing stdout
+ * 2) no check that any open files are present
+ * 3) optstring had -q taking an arg
+ * 4) no error checking on write in some cases, and a warning even then
+ * 5) q and s interaction bug
+ * 6) no check for lseek error
+ * 7) lseek attempted when count==0 even if arg was +0 (from top)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "busybox.h"
+
+static const struct suffix_mult tail_suffixes[] = {
+       { "b", 512 },
+       { "k", 1024 },
+       { "m", 1048576 },
+       { NULL, 0 }
+};
+
+static int status
+#if EXIT_SUCCESS != 0
+       = EXIT_SUCCESS  /* If it is 0 (paranoid check), let bss initialize it. */
+#endif
+       ;
+
+static void tail_xprint_header(const char *fmt, const char *filename)
+{
+       /* If we get an output error, there is really no sense in continuing. */
+       if (dprintf(STDOUT_FILENO, fmt, filename) < 0) {
+               bb_perror_nomsg_and_die();
+       }
+}
+
+/* len should probably be size_t */
+static void tail_xbb_full_write(const char *buf, size_t len)
+{
+       /* If we get a write error, there is really no sense in continuing. */
+       if (bb_full_write(STDOUT_FILENO, buf, len) < 0) {
+               bb_perror_nomsg_and_die();
+       }
+}
+
+static ssize_t tail_read(int fd, char *buf, size_t count)
+{
+       ssize_t r;
+
+       if ((r = safe_read(fd, buf, count)) < 0) {
+               bb_perror_msg("read");
+               status = EXIT_FAILURE;
+       }
+
+       return r;
+}
+
+static const char tail_opts[] =
+       "fn:c:"
+#ifdef CONFIG_FEATURE_FANCY_TAIL
+       "qs:v"
+#endif
+       ;
+
+static const char header_fmt[] = "\n==> %s <==\n";
+
+int tail_main(int argc, char **argv)
+{
+       long count = 10;
+       unsigned int sleep_period = 1;
+       int from_top = 0;
+       int follow = 0;
+       int header_threshhold = 1;
+       int count_bytes = 0;
+
+       char *tailbuf;
+       size_t tailbufsize;
+       int taillen = 0;
+       int newline = 0;
+
+       int *fds, nfiles, nread, nwrite, seen, i, opt;
+       char *s, *buf;
+       const char *fmt;
+
+       /* Allow legacy syntax of an initial numeric option without -n. */
+       if (argc >=2 && ((argv[1][0] == '+') || ((argv[1][0] == '-')
+                       /* && (isdigit)(argv[1][1]) */
+                       && (((unsigned int)(argv[1][1] - '0')) <= 9))))
+       {
+               optind = 2;
+               optarg = argv[1];
+               goto GET_COUNT;
+       }
+
+       while ((opt = getopt(argc, argv, tail_opts)) > 0) {
+               switch (opt) {
+                       case 'f':
+                               follow = 1;
+                               break;
+                       case 'c':
+                               count_bytes = 1;
+                               /* FALLS THROUGH */
+                       case 'n':
+                       GET_COUNT:
+                               count = bb_xgetlarg10_sfx(optarg, tail_suffixes);
+                               /* Note: Leading whitespace is an error trapped above. */
+                               if (*optarg == '+') {
+                                       from_top = 1;
+                               } else {
+                                       from_top = 0;
+                               }
+                               if (count < 0) {
+                                       count = -count;
+                               }
+                               break;
+#ifdef CONFIG_FEATURE_FANCY_TAIL
+                       case 'q':
+                               header_threshhold = INT_MAX;
+                               break;
+                       case 's':
+                               sleep_period =bb_xgetularg10_bnd(optarg, 0, UINT_MAX);
+                               break;
+                       case 'v':
+                               header_threshhold = 0;
+                               break;
+#endif
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       /* open all the files */
+       fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
+
+       argv += optind;
+       nfiles = i = 0;
+
+       if ((argc -= optind) == 0) {
+               struct stat statbuf;
+
+               if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
+                       follow = 0;
+               }
+               /* --argv; */
+               *argv = (char *) bb_msg_standard_input;
+               goto DO_STDIN;
+       }
+
+       do {
+               if ((argv[i][0] == '-') && !argv[i][1]) {
+               DO_STDIN:
+                       fds[nfiles] = STDIN_FILENO;
+               } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) {
+                       bb_perror_msg("%s", argv[i]);
+                       status = EXIT_FAILURE;
+                       continue;
+               }
+               argv[nfiles] = argv[i];
+               ++nfiles;
+       } while (++i < argc);
+
+       if (!nfiles) {
+               bb_error_msg_and_die("no files");
+       }
+
+       tailbufsize = BUFSIZ;
+
+       /* tail the files */
+       if (from_top < count_bytes) {   /* Each is 0 or 1, so true iff 0 < 1. */
+               /* Hence, !from_top && count_bytes */
+               if (tailbufsize < count) {
+                       tailbufsize = count + BUFSIZ;
+               }
+       }
+
+       buf = tailbuf = xmalloc(tailbufsize);
+
+       fmt = header_fmt + 1;   /* Skip header leading newline on first output. */
+       i = 0;
+       do {
+               /* Be careful.  It would be possible to optimize the count-bytes
+                * case if the file is seekable.  If you do though, remember that
+                * starting file position may not be the beginning of the file.
+                * Beware of backing up too far.  See example in wc.c.
+                */
+               if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) {
+                       continue;
+               }
+
+               if (nfiles > header_threshhold) {
+                       tail_xprint_header(fmt, argv[i]);
+                       fmt = header_fmt;
+               }
+
+               buf = tailbuf;
+               taillen = 0;
+               seen = 1;
+               newline = 0;
+
+               while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
+                       if (from_top) {
+                               nwrite = nread;
+                               if (seen < count) {
+                                       if (count_bytes) {
+                                               nwrite -= (count - seen);
+                                               seen = count;
+                                       } else {
+                                               s = buf;
+                                               do {
+                                                       --nwrite;
+                                                       if ((*s++ == '\n') && (++seen == count)) {
+                                                               break;
+                                                       }
+                                               } while (nwrite);
+                                       }
+                               }
+                               tail_xbb_full_write(buf + nread - nwrite, nwrite);
+                       } else if (count) {
+                               if (count_bytes) {
+                                       taillen += nread;
+                                       if (taillen > count) {
+                                               memmove(tailbuf, tailbuf + taillen - count, count);
+                                               taillen = count;
+                                       }
+                               } else {
+                                       int k = nread;
+                                       int nbuf = 0;
+
+                                       while (k) {
+                                               --k;
+                                               if (buf[k] == '\n') {
+                                                       ++nbuf;
+                                               }
+                                       }
+
+                                       if (newline + nbuf < count) {
+                                               newline += nbuf;
+                                               taillen += nread;
+
+                                       } else {
+                                               int extra = 0;
+                                               if (buf[nread-1] != '\n') {
+                                                       extra = 1;
+                                               }
+
+                                               k = newline + nbuf + extra - count;
+                                               s = tailbuf;
+                                               while (k) {
+                                                       if (*s == '\n') {
+                                                               --k;
+                                                       }
+                                                       ++s;
+                                               }
+
+                                               taillen += nread - (s - tailbuf);
+                                               memmove(tailbuf, s, taillen);
+                                               newline = count - extra;
+                                       }
+                                       if (tailbufsize < taillen + BUFSIZ) {
+                                               tailbufsize = taillen + BUFSIZ;
+                                               tailbuf = xrealloc(tailbuf, tailbufsize);
+                                       }
+                               }
+                               buf = tailbuf + taillen;
+                       }
+               }
+
+               if (!from_top) {
+                       tail_xbb_full_write(tailbuf, taillen);
+               }
+
+               taillen = 0;
+       } while (++i < nfiles);
+
+       buf = xrealloc(tailbuf, BUFSIZ);
+
+       fmt = NULL;
+
+       while (follow) {
+               sleep(sleep_period);
+               i = 0;
+               do {
+                       if (nfiles > header_threshhold) {
+                               fmt = header_fmt;
+                       }
+                       while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
+                               if (fmt) {
+                                       tail_xprint_header(fmt, argv[i]);
+                                       fmt = NULL;
+                               }
+                               tail_xbb_full_write(buf, nread);
+                       }
+               } while (++i < nfiles);
+       }
+
+       return status;
+}
diff --git a/busybox/coreutils/tee.c b/busybox/coreutils/tee.c
new file mode 100644 (file)
index 0000000..ba2e10f
--- /dev/null
@@ -0,0 +1,120 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tee implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int tee_main(int argc, char **argv)
+{
+       const char *mode = "w\0a";
+       FILE **files;
+       FILE **p;
+       char **filenames;
+       int flags;
+       int retval = EXIT_SUCCESS;
+#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO
+       ssize_t c;
+       RESERVE_CONFIG_BUFFER(buf, BUFSIZ);
+#else
+       int c;
+#endif
+
+       flags = bb_getopt_ulflags(argc, argv, "ia");    /* 'a' must be 2nd */
+
+       mode += (flags & 2);    /* Since 'a' is the 2nd option... */
+
+       if (flags & 1) {
+               signal(SIGINT, SIG_IGN);        /* TODO - switch to sigaction.*/
+       }
+
+       /* gnu tee ignores SIGPIPE in case one of the output files is a pipe
+        * that doesn't consume all its input.  Good idea... */
+       signal(SIGPIPE, SIG_IGN);               /* TODO - switch to sigaction.*/
+
+       /* Allocate an array of FILE *'s, with one extra for a sentinal. */
+       p = files = (FILE **)xmalloc(sizeof(FILE *) * (argc - optind + 2));
+       *p = stdout;
+       argv += optind - 1;
+       filenames = argv - 1;
+       *filenames = (char *) bb_msg_standard_input;    /* for later */
+       goto GOT_NEW_FILE;
+
+       do {
+               if ((*p = bb_wfopen(*argv, mode)) == NULL) {
+                       retval = EXIT_FAILURE;
+                       continue;
+               }
+               filenames[(int)(p - files)] = *argv;
+       GOT_NEW_FILE:
+               setbuf(*p, NULL);       /* tee must not buffer output. */
+               ++p;
+       } while (*++argv);
+
+       *p = NULL;                              /* Store the sentinal value. */
+
+#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO
+       while ((c = safe_read(STDIN_FILENO, buf, BUFSIZ)) > 0) {
+               for (p=files ; *p ; p++) {
+                       fwrite(buf, 1, c, *p);
+               }
+       }
+
+       if (c < 0) {                    /* Make sure read errors are signaled. */
+               retval = EXIT_FAILURE;
+       }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       RELEASE_CONFIG_BUFFER(buf);
+#endif
+
+#else
+       setvbuf(stdout, NULL, _IONBF, 0);
+       while ((c = getchar()) != EOF) {
+               for (p=files ; *p ; p++) {
+                       putc(c, *p);
+               }
+       }
+#endif
+
+       /* Now we need to check for i/o errors on stdin and the various
+        * output files.  Since we know that the first entry in the output
+        * file table is stdout, we can save one "if ferror" test by
+        * setting the first entry to stdin and checking stdout error
+        * status with bb_fflush_stdout_and_exit()... although fflush()ing
+        * is unnecessary here. */
+
+       p = files;
+       *p = stdin;
+       do {            /* Now check for (input and) output errors. */
+               /* Checking ferror should be sufficient, but we may want to fclose.
+                * If we do, remember not to close stdin! */
+               bb_xferror(*p, filenames[(int)(p - files)]);
+       } while (*++p);
+
+       bb_fflush_stdout_and_exit(retval);
+}
diff --git a/busybox/coreutils/test.c b/busybox/coreutils/test.c
new file mode 100644 (file)
index 0000000..8fa6d16
--- /dev/null
@@ -0,0 +1,559 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * test implementation for busybox
+ *
+ * Copyright (c) by a whole pile of folks:
+ *
+ *     test(1); version 7-like  --  author Erik Baalbergen
+ *     modified by Eric Gisin to be used as built-in.
+ *     modified by Arnold Robbins to add SVR3 compatibility
+ *     (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
+ *     modified by J.T. Conklin for NetBSD.
+ *     modified by Herbert Xu to be used as built-in in ash.
+ *     modified by Erik Andersen <andersen@codepoet.org> to be used
+ *     in busybox.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice states:
+ *     "This program is in the Public Domain."
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "busybox.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,
+       FILRD,
+       FILWR,
+       FILEX,
+       FILEXIST,
+       FILREG,
+       FILDIR,
+       FILCDEV,
+       FILBDEV,
+       FILFIFO,
+       FILSOCK,
+       FILSYM,
+       FILGZ,
+       FILTT,
+       FILSUID,
+       FILSGID,
+       FILSTCK,
+       FILNT,
+       FILOT,
+       FILEQ,
+       FILUID,
+       FILGID,
+       STREZ,
+       STRNZ,
+       STREQ,
+       STRNE,
+       STRLT,
+       STRGT,
+       INTEQ,
+       INTNE,
+       INTGE,
+       INTGT,
+       INTLE,
+       INTLT,
+       UNOT,
+       BAND,
+       BOR,
+       LPAREN,
+       RPAREN,
+       OPERAND
+};
+
+enum token_types {
+       UNOP,
+       BINOP,
+       BUNOP,
+       BBINOP,
+       PAREN
+};
+
+static const struct t_op {
+       const char *op_text;
+       short op_num, op_type;
+} ops[] = {
+       {
+       "-r", FILRD, UNOP}, {
+       "-w", FILWR, UNOP}, {
+       "-x", FILEX, UNOP}, {
+       "-e", FILEXIST, UNOP}, {
+       "-f", FILREG, UNOP}, {
+       "-d", FILDIR, UNOP}, {
+       "-c", FILCDEV, UNOP}, {
+       "-b", FILBDEV, UNOP}, {
+       "-p", FILFIFO, UNOP}, {
+       "-u", FILSUID, UNOP}, {
+       "-g", FILSGID, UNOP}, {
+       "-k", FILSTCK, UNOP}, {
+       "-s", FILGZ, UNOP}, {
+       "-t", FILTT, UNOP}, {
+       "-z", STREZ, UNOP}, {
+       "-n", STRNZ, UNOP}, {
+       "-h", FILSYM, UNOP},    /* for backwards compat */
+       {
+       "-O", FILUID, UNOP}, {
+       "-G", FILGID, UNOP}, {
+       "-L", FILSYM, UNOP}, {
+       "-S", FILSOCK, UNOP}, {
+       "=", STREQ, BINOP}, {
+       "!=", STRNE, BINOP}, {
+       "<", STRLT, BINOP}, {
+       ">", STRGT, BINOP}, {
+       "-eq", INTEQ, BINOP}, {
+       "-ne", INTNE, BINOP}, {
+       "-ge", INTGE, BINOP}, {
+       "-gt", INTGT, BINOP}, {
+       "-le", INTLE, BINOP}, {
+       "-lt", INTLT, BINOP}, {
+       "-nt", FILNT, BINOP}, {
+       "-ot", FILOT, BINOP}, {
+       "-ef", FILEQ, BINOP}, {
+       "!", UNOT, BUNOP}, {
+       "-a", BAND, BBINOP}, {
+       "-o", BOR, BBINOP}, {
+       "(", LPAREN, PAREN}, {
+       ")", RPAREN, PAREN}, {
+       0, 0, 0}
+};
+
+#ifdef CONFIG_FEATURE_TEST_64
+typedef int64_t arith_t;
+#else
+typedef int arith_t;
+#endif
+
+static char **t_wp;
+static struct t_op const *t_wp_op;
+static gid_t *group_array = NULL;
+static int ngroups;
+
+static enum token t_lex(char *s);
+static arith_t oexpr(enum token n);
+static arith_t aexpr(enum token n);
+static arith_t nexpr(enum token n);
+static int binop(void);
+static arith_t primary(enum token n);
+static int filstat(char *nm, enum token mode);
+static arith_t getn(const char *s);
+static int newerf(const char *f1, const char *f2);
+static int olderf(const char *f1, const char *f2);
+static int equalf(const char *f1, const char *f2);
+static void syntax(const char *op, const char *msg);
+static int test_eaccess(char *path, int mode);
+static int is_a_group_member(gid_t gid);
+static void initialize_group_array(void);
+
+extern int test_main(int argc, char **argv)
+{
+       int res;
+
+       if (strcmp(bb_applet_name, "[") == 0) {
+               if (strcmp(argv[--argc], "]"))
+                       bb_error_msg_and_die("missing ]");
+               argv[argc] = NULL;
+       }
+       /* Implement special cases from POSIX.2, section 4.62.4 */
+       switch (argc) {
+       case 1:
+               exit(1);
+       case 2:
+               exit(*argv[1] == '\0');
+       case 3:
+               if (argv[1][0] == '!' && argv[1][1] == '\0') {
+                       exit(!(*argv[2] == '\0'));
+               }
+               break;
+       case 4:
+               if (argv[1][0] != '!' || argv[1][1] != '\0') {
+                       if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) {
+                               t_wp = &argv[1];
+                               exit(binop() == 0);
+                       }
+               }
+               break;
+       case 5:
+               if (argv[1][0] == '!' && argv[1][1] == '\0') {
+                       if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) {
+                               t_wp = &argv[2];
+                               exit(!(binop() == 0));
+                       }
+               }
+               break;
+       }
+
+       t_wp = &argv[1];
+       res = !oexpr(t_lex(*t_wp));
+
+       if (*t_wp != NULL && *++t_wp != NULL)
+               syntax(*t_wp, "unknown operand");
+
+       return (res);
+}
+
+static void syntax(const char *op, const char *msg)
+{
+       if (op && *op) {
+               bb_error_msg_and_die("%s: %s", op, msg);
+       } else {
+               bb_error_msg_and_die("%s", msg);
+       }
+}
+
+static arith_t oexpr(enum token n)
+{
+       arith_t res;
+
+       res = aexpr(n);
+       if (t_lex(*++t_wp) == BOR) {
+               return oexpr(t_lex(*++t_wp)) || res;
+       }
+       t_wp--;
+       return res;
+}
+
+static arith_t aexpr(enum token n)
+{
+       arith_t res;
+
+       res = nexpr(n);
+       if (t_lex(*++t_wp) == BAND)
+               return aexpr(t_lex(*++t_wp)) && res;
+       t_wp--;
+       return res;
+}
+
+static arith_t nexpr(enum token n)
+{
+       if (n == UNOT)
+               return !nexpr(t_lex(*++t_wp));
+       return primary(n);
+}
+
+static arith_t primary(enum token n)
+{
+       arith_t res;
+
+       if (n == EOI) {
+               syntax(NULL, "argument expected");
+       }
+       if (n == LPAREN) {
+               res = oexpr(t_lex(*++t_wp));
+               if (t_lex(*++t_wp) != RPAREN)
+                       syntax(NULL, "closing paren expected");
+               return res;
+       }
+       if (t_wp_op && t_wp_op->op_type == UNOP) {
+               /* unary expression */
+               if (*++t_wp == NULL)
+                       syntax(t_wp_op->op_text, "argument expected");
+               switch (n) {
+               case STREZ:
+                       return strlen(*t_wp) == 0;
+               case STRNZ:
+                       return strlen(*t_wp) != 0;
+               case FILTT:
+                       return isatty(getn(*t_wp));
+               default:
+                       return filstat(*t_wp, n);
+               }
+       }
+
+       if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
+               return binop();
+       }
+
+       return strlen(*t_wp) > 0;
+}
+
+static int binop()
+{
+       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) == (char *) 0)
+               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:
+               return newerf(opnd1, opnd2);
+       case FILOT:
+               return olderf(opnd1, opnd2);
+       case FILEQ:
+               return equalf(opnd1, opnd2);
+       }
+       /* NOTREACHED */
+       return 1;
+}
+
+static int filstat(char *nm, enum token mode)
+{
+       struct stat s;
+       unsigned int i;
+
+       if (mode == FILSYM) {
+#ifdef S_IFLNK
+               if (lstat(nm, &s) == 0) {
+                       i = S_IFLNK;
+                       goto filetype;
+               }
+#endif
+               return 0;
+       }
+
+       if (stat(nm, &s) != 0)
+               return 0;
+
+       switch (mode) {
+       case FILRD:
+               return test_eaccess(nm, R_OK) == 0;
+       case FILWR:
+               return test_eaccess(nm, W_OK) == 0;
+       case FILEX:
+               return test_eaccess(nm, X_OK) == 0;
+       case FILEXIST:
+               return 1;
+       case FILREG:
+               i = S_IFREG;
+               goto filetype;
+       case FILDIR:
+               i = S_IFDIR;
+               goto filetype;
+       case FILCDEV:
+               i = S_IFCHR;
+               goto filetype;
+       case FILBDEV:
+               i = S_IFBLK;
+               goto filetype;
+       case FILFIFO:
+#ifdef S_IFIFO
+               i = S_IFIFO;
+               goto filetype;
+#else
+               return 0;
+#endif
+       case FILSOCK:
+#ifdef S_IFSOCK
+               i = S_IFSOCK;
+               goto filetype;
+#else
+               return 0;
+#endif
+       case FILSUID:
+               i = S_ISUID;
+               goto filebit;
+       case FILSGID:
+               i = S_ISGID;
+               goto filebit;
+       case FILSTCK:
+               i = S_ISVTX;
+               goto filebit;
+       case FILGZ:
+               return s.st_size > 0L;
+       case FILUID:
+               return s.st_uid == geteuid();
+       case FILGID:
+               return s.st_gid == getegid();
+       default:
+               return 1;
+       }
+
+  filetype:
+       return ((s.st_mode & S_IFMT) == i);
+
+  filebit:
+       return ((s.st_mode & i) != 0);
+}
+
+static enum token t_lex(char *s)
+{
+       struct t_op const *op = ops;
+
+       if (s == 0) {
+               t_wp_op = (struct t_op *) 0;
+               return EOI;
+       }
+       while (op->op_text) {
+               if (strcmp(s, op->op_text) == 0) {
+                       t_wp_op = op;
+                       return op->op_num;
+               }
+               op++;
+       }
+       t_wp_op = (struct t_op *) 0;
+       return OPERAND;
+}
+
+/* atoi with error detection */
+static arith_t getn(const char *s)
+{
+       char *p;
+#ifdef CONFIG_FEATURE_TEST_64
+       long long r;
+#else
+       long r;
+#endif
+
+       errno = 0;
+#ifdef CONFIG_FEATURE_TEST_64
+       r = strtoll(s, &p, 10);
+#else
+       r = strtol(s, &p, 10);
+#endif
+
+       if (errno != 0)
+               bb_error_msg_and_die("%s: out of range", s);
+
+       /*   p = bb_skip_whitespace(p); avoid const warning */
+       if (*(bb_skip_whitespace(p)))
+               bb_error_msg_and_die("%s: bad number", s);
+
+       return 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);
+}
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+   and don't make the mistake of telling root that any file is
+   executable. */
+static int test_eaccess(char *path, int mode)
+{
+       struct stat st;
+       unsigned int euid = geteuid();
+
+       if (stat(path, &st) < 0)
+               return (-1);
+
+       if (euid == 0) {
+               /* Root can read or write any file. */
+               if (mode != X_OK)
+                       return (0);
+
+               /* Root can execute any file that has any one of the execute
+                  bits set. */
+               if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+                       return (0);
+       }
+
+       if (st.st_uid == euid)  /* owner */
+               mode <<= 6;
+       else if (is_a_group_member(st.st_gid))
+               mode <<= 3;
+
+       if (st.st_mode & mode)
+               return (0);
+
+       return (-1);
+}
+
+static void initialize_group_array()
+{
+       ngroups = getgroups(0, NULL);
+       group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
+       getgroups(ngroups, group_array);
+}
+
+/* Return non-zero if GID is one that we have in our groups list. */
+static int is_a_group_member(gid_t gid)
+{
+       register int i;
+
+       /* Short-circuit if possible, maybe saving a call to getgroups(). */
+       if (gid == getgid() || gid == getegid())
+               return (1);
+
+       if (ngroups == 0)
+               initialize_group_array();
+
+       /* Search through the list looking for GID. */
+       for (i = 0; i < ngroups; i++)
+               if (gid == group_array[i])
+                       return (1);
+
+       return (0);
+}
diff --git a/busybox/coreutils/touch.c b/busybox/coreutils/touch.c
new file mode 100644 (file)
index 0000000..645fb21
--- /dev/null
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini touch implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Previous version called open() and then utime().  While this will be
+ * be necessary to implement -r and -t, it currently only makes things bigger.
+ * Also, exiting on a failure was a bug.  All args should be processed.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int touch_main(int argc, char **argv)
+{
+       int fd;
+       int flags;
+       int status = EXIT_SUCCESS;
+
+       flags = bb_getopt_ulflags(argc, argv, "c");
+
+       argv += optind;
+
+       if (!*argv) {
+               bb_show_usage();
+       }
+
+       do {
+               if (utime(*argv, NULL)) {
+                       if (errno == ENOENT) {  /* no such file*/
+                               if (flags & 1) {        /* Creation is disabled, so ignore. */
+                                       continue;
+                               }
+                               /* Try to create the file. */
+                               fd = open(*argv, O_RDWR | O_CREAT,
+                                                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
+                                                 );
+                               if ((fd >= 0) && !close(fd)) {
+                                       continue;
+                               }
+                       }
+                       status = EXIT_FAILURE;
+                       bb_perror_msg("%s", *argv);
+               }
+       } while (*++argv);
+
+       return status;
+}
diff --git a/busybox/coreutils/tr.c b/busybox/coreutils/tr.c
new file mode 100644 (file)
index 0000000..1325245
--- /dev/null
@@ -0,0 +1,248 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tr implementation for busybox
+ *
+ * Copyright (c) Michiel Huisjes
+ *
+ * This version of tr is adapted from Minix tr and was modified
+ * by Erik Andersen <andersen@codepoet.org> to be used in busybox.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "busybox.h"
+
+/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
+ * enabled, we otherwise get a "storage size isn't constant error. */
+#define ASCII 0377
+
+/* some "globals" shared across this file */
+static char com_fl, del_fl, sq_fl;
+static short in_index, out_index;
+/* these last are pointers to static buffers declared in tr_main */
+static unsigned char *poutput, *pinput;
+static unsigned char *pvector;
+static char *pinvec, *poutvec;
+
+
+static void convert(void)
+{
+       short read_chars = 0;
+       short c, coded;
+       short last = -1;
+
+       for (;;) {
+               if (in_index == read_chars) {
+                       if ((read_chars = read(0, (char *) pinput, BUFSIZ)) <= 0) {
+                               if (write(1, (char *) poutput, out_index) != out_index)
+                                       bb_error_msg(bb_msg_write_error);
+                               exit(0);
+                       }
+                       in_index = 0;
+               }
+               c = pinput[in_index++];
+               coded = pvector[c];
+               if (del_fl && pinvec[c])
+                       continue;
+               if (sq_fl && last == coded && (pinvec[c] || poutvec[coded]))
+                       continue;
+               poutput[out_index++] = last = coded;
+               if (out_index == BUFSIZ) {
+                       if (write(1, (char *) poutput, out_index) != out_index)
+                               bb_error_msg_and_die(bb_msg_write_error);
+                       out_index = 0;
+               }
+       }
+
+       /* NOTREACHED */
+}
+
+static void map(register unsigned char *string1, unsigned int string1_len,
+               register unsigned char *string2, unsigned int string2_len)
+{
+       unsigned char last = '0';
+       unsigned int i, j;
+
+       for (j = 0, i = 0; i < string1_len; i++) {
+               if (string2_len <= j)
+                       pvector[string1[i]] = last;
+               else
+                       pvector[string1[i]] = last = string2[j++];
+       }
+}
+
+/* supported constructs:
+ *   Ranges,  e.g.,  [0-9]  ==>  0123456789
+ *   Escapes, e.g.,  \a     ==>  Control-G
+ */
+static unsigned int expand(const char *arg, register unsigned char *buffer)
+{
+       unsigned char *buffer_start = buffer;
+       int i, ac;
+
+       while (*arg) {
+               if (*arg == '\\') {
+                       arg++;
+                       *buffer++ = bb_process_escape_sequence(&arg);
+               } else if (*(arg+1) == '-') {
+                       ac = *(arg+2);
+                       if(ac == 0) {
+                               *buffer++ = *arg++;
+                               continue;
+                       }
+                       i = *arg;
+                       while (i <= ac)
+                               *buffer++ = i++;
+                       arg += 3; /* Skip the assumed a-z */
+               } else if (*arg == '[') {
+                       arg++;
+                       i = *arg++;
+                       if (*arg++ != '-') {
+                               *buffer++ = '[';
+                               arg -= 2;
+                               continue;
+                       }
+                       ac = *arg++;
+                       while (i <= ac)
+                               *buffer++ = i++;
+                       arg++;                          /* Skip the assumed ']' */
+               } else
+                       *buffer++ = *arg++;
+       }
+
+       return (buffer - buffer_start);
+}
+
+static int complement(unsigned char *buffer, int buffer_len)
+{
+       register short i, j, ix;
+       char conv[ASCII + 2];
+
+       ix = 0;
+       for (i = 0; i <= ASCII; i++) {
+               for (j = 0; j < buffer_len; j++)
+                       if (buffer[j] == i)
+                               break;
+               if (j == buffer_len)
+                       conv[ix++] = i & ASCII;
+       }
+       memcpy(buffer, conv, ix);
+       return ix;
+}
+
+extern int tr_main(int argc, char **argv)
+{
+       register unsigned char *ptr;
+       int output_length=0, input_length;
+       int idx = 1;
+       int i;
+       RESERVE_CONFIG_BUFFER(output, BUFSIZ);
+       RESERVE_CONFIG_BUFFER(input,  BUFSIZ);
+       RESERVE_CONFIG_UBUFFER(vector, ASCII+1);
+       RESERVE_CONFIG_BUFFER(invec,  ASCII+1);
+       RESERVE_CONFIG_BUFFER(outvec, ASCII+1);
+
+       /* ... but make them available globally */
+       poutput = output;
+       pinput  = input;
+       pvector = vector;
+       pinvec  = invec;
+       poutvec = outvec;
+
+       if (argc > 1 && argv[idx][0] == '-') {
+               for (ptr = (unsigned char *) &argv[idx][1]; *ptr; ptr++) {
+                       switch (*ptr) {
+                       case 'c':
+                               com_fl = TRUE;
+                               break;
+                       case 'd':
+                               del_fl = TRUE;
+                               break;
+                       case 's':
+                               sq_fl = TRUE;
+                               break;
+                       default:
+                               bb_show_usage();
+                       }
+               }
+               idx++;
+       }
+       for (i = 0; i <= ASCII; i++) {
+               vector[i] = i;
+               invec[i] = outvec[i] = FALSE;
+       }
+
+       if (argv[idx] != NULL) {
+               input_length = expand(argv[idx++], input);
+               if (com_fl)
+                       input_length = complement(input, input_length);
+               if (argv[idx] != NULL) {
+                       if (*argv[idx] == '\0')
+                               bb_error_msg_and_die("STRING2 cannot be empty");
+                       output_length = expand(argv[idx], output);
+                       map(input, input_length, output, output_length);
+               }
+               for (i = 0; i < input_length; i++)
+                       invec[(unsigned char)input[i]] = TRUE;
+               for (i = 0; i < output_length; i++)
+                       outvec[(unsigned char)output[i]] = TRUE;
+       }
+       convert();
+       return (0);
+}
+
+/*
+ * Copyright (c) 1987,1997, Prentice Hall
+ * All rights reserved.
+ *
+ * Redistribution and use of the MINIX operating system 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 Prentice Hall nor the names of the software
+ * authors or 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, AUTHORS, 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 PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
diff --git a/busybox/coreutils/true.c b/busybox/coreutils/true.c
new file mode 100644 (file)
index 0000000..3e7eb01
--- /dev/null
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini true implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */
+
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int true_main(int argc, char **argv)
+{
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/tty.c b/busybox/coreutils/tty.c
new file mode 100644 (file)
index 0000000..cd2c784
--- /dev/null
@@ -0,0 +1,58 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tty implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tty.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int tty_main(int argc, char **argv)
+{
+       const char *s;
+       int silent;             /* Note: No longer relevant in SUSv3. */
+       int retval;
+
+       bb_default_error_retval = 2;    /* SUSv3 requires > 1 for error. */
+
+       silent = bb_getopt_ulflags(argc, argv, "s");
+
+       /* gnu tty outputs a warning that it is ignoring all args. */
+       bb_warn_ignoring_args(argc - optind);
+
+       retval = 0;
+
+       if ((s = ttyname(0)) == NULL) {
+       /* According to SUSv3, ttyname can on fail with EBADF or ENOTTY.
+        * We know the file descriptor is good, so failure means not a tty. */
+               s = "not a tty";
+               retval = 1;
+       }
+
+       if (!silent) {
+               puts(s);
+       }
+
+       bb_fflush_stdout_and_exit(retval);
+}
diff --git a/busybox/coreutils/uname.c b/busybox/coreutils/uname.c
new file mode 100644 (file)
index 0000000..a3e52e3
--- /dev/null
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+/* uname -- print system information
+   Copyright (C) 1989-1999 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
+
+/* Option              Example
+
+   -s, --sysname       SunOS
+   -n, --nodename      rocky8
+   -r, --release       4.0
+   -v, --version
+   -m, --machine       sun
+   -a, --all           SunOS rocky8 4.0  sun
+
+   The default behavior is equivalent to `-s'.
+
+   David MacKenzie <djm@gnu.ai.mit.edu> */
+
+/* Busyboxed by Erik Andersen */
+
+/* Further size reductions by Glenn McGrath and Manuel Novoa III. */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Now does proper error checking on i/o.  Plus some further space savings.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include "busybox.h"
+
+typedef struct {
+       struct utsname name;
+       char processor[8];                      /* for "unknown" */
+} uname_info_t;
+
+static const char options[] = "snrvmpa";
+static const unsigned short int utsname_offset[] = {
+       offsetof(uname_info_t,name.sysname),
+       offsetof(uname_info_t,name.nodename),
+       offsetof(uname_info_t,name.release),
+       offsetof(uname_info_t,name.version),
+       offsetof(uname_info_t,name.machine),
+       offsetof(uname_info_t,processor)
+};
+
+int uname_main(int argc, char **argv)
+{
+       uname_info_t uname_info;
+#if defined(__sparc__) && defined(__linux__)
+       char *fake_sparc = getenv("FAKE_SPARC");
+#endif
+       const unsigned short int *delta;
+       char toprint;
+
+       toprint = bb_getopt_ulflags(argc, argv, options);
+
+       if (argc != optind) {
+               bb_show_usage();
+       }
+
+       if (toprint & (1 << 6)) {
+               toprint = 0x3f;
+       }
+
+       if (toprint == 0) {
+               toprint = 1;                    /* sysname */
+       }
+
+       if (uname(&uname_info.name) == -1) {
+               bb_error_msg_and_die("cannot get system name");
+       }
+
+#if defined(__sparc__) && defined(__linux__)
+       if ((fake_sparc != NULL)
+               && ((fake_sparc[0] == 'y')
+                       || (fake_sparc[0] == 'Y'))) {
+               strcpy(uname_info.name.machine, "sparc");
+       }
+#endif
+
+       strcpy(uname_info.processor, "unknown");
+
+       delta=utsname_offset;
+       do {
+               if (toprint & 1) {
+                       bb_printf(((char *)(&uname_info)) + *delta);
+                       if (toprint > 1) {
+                               putchar(' ');
+                       }
+               }
+               ++delta;
+       } while (toprint >>= 1);
+       putchar('\n');
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/uniq.c b/busybox/coreutils/uniq.c
new file mode 100644 (file)
index 0000000..6caab5d
--- /dev/null
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uniq implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+static const char uniq_opts[] = "f:s:cdu\0\7\3\5\1\2\4";
+
+int uniq_main(int argc, char **argv)
+{
+       FILE *in, *out;
+       /* Note: Ignore the warning about dups and e0 being used uninitialized.
+        * They will be initialized on the fist pass of the loop (since s0 is NULL). */
+       unsigned long dups, skip_fields, skip_chars, i;
+       const char *s0, *e0, *s1, *e1, *input_filename;
+       int opt;
+       int uniq_flags = 6;             /* -u */
+
+       skip_fields = skip_chars = 0;
+
+       while ((opt = getopt(argc, argv, uniq_opts)) > 0) {
+               if (opt == 'f') {
+                       skip_fields = bb_xgetularg10(optarg);
+               } else if (opt == 's') {
+                       skip_chars = bb_xgetularg10(optarg);
+               } else if ((s0 = strchr(uniq_opts, opt)) != NULL) {
+                       uniq_flags &= s0[4];
+                       uniq_flags |= s0[7];
+               } else {
+                       bb_show_usage();
+               }
+       }
+
+       input_filename = *(argv += optind);
+
+       in = xgetoptfile_sort_uniq(argv, "r");
+       if (*argv) {
+               ++argv;
+       }
+       out = xgetoptfile_sort_uniq(argv, "w");
+       if (*argv && argv[1]) {
+               bb_show_usage();
+       }
+
+       s0 = NULL;
+
+       /* gnu uniq ignores newlines */
+       while ((s1 = bb_get_chomped_line_from_file(in)) != NULL) {
+               e1 = s1;
+               for (i=skip_fields ; i ; i--) {
+                       e1 = bb_skip_whitespace(e1);
+                       while (*e1 && !isspace(*e1)) {
+                               ++e1;
+                       }
+               }
+               for (i = skip_chars ; *e1 && i ; i--) {
+                       ++e1;
+               }
+               if (s0) {
+                       if (strcmp(e0, e1) == 0) {
+                               ++dups;         /* Note: Testing for overflow seems excessive. */
+                               continue;
+                       }
+               DO_LAST:
+                       if ((dups && (uniq_flags & 2)) || (!dups && (uniq_flags & 4))) {
+                               bb_fprintf(out, "\0%7d\t" + (uniq_flags & 1), dups + 1);
+                               bb_fprintf(out, "%s\n", s0);
+                       }
+                       free((void *)s0);
+               }
+
+               s0 = s1;
+               e0 = e1;
+               dups = 0;
+       }
+
+       if (s0) {
+               e1 = NULL;
+               goto DO_LAST;
+       }
+
+       bb_xferror(in, input_filename);
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/usleep.c b/busybox/coreutils/usleep.c
new file mode 100644 (file)
index 0000000..f570f27
--- /dev/null
@@ -0,0 +1,41 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * usleep implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int usleep_main(int argc, char **argv)
+{
+       if (argc != 2) {
+               bb_show_usage();
+       }
+
+       if (usleep(bb_xgetularg10_bnd(argv[1], 0, UINT_MAX))) {
+               bb_perror_nomsg_and_die();
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/coreutils/uudecode.c b/busybox/coreutils/uudecode.c
new file mode 100644 (file)
index 0000000..57d4e83
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ *  GPLv2
+ *  Copyright 2003, Glenn McGrath <bug1@iinet.net.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation; either version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Based on specification from
+ *  http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
+ *
+ *  Bugs: the spec doesnt mention anything about "`\n`\n" prior to the "end" line
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+
+static int read_stduu(FILE *src_stream, FILE *dst_stream)
+{
+       char *line;
+
+       while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
+               int length;
+               char *line_ptr = line;
+
+               if (strcmp(line, "end") == 0) {
+                       return(EXIT_SUCCESS);
+               }
+               length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3;
+
+               if (length <= 0) {
+                       /* Ignore the "`\n" line, why is it even in the encode file ? */
+                       continue;
+               }
+               if (length > 60) {
+                       bb_error_msg_and_die("Line too long");
+               }
+
+               line_ptr++;
+               /* Tolerate an overly long line to acomadate a possible exta '`' */
+               if (strlen(line_ptr) < length) {
+                       bb_error_msg_and_die("Short file");
+               }
+
+               while (length > 0) {
+                       /* Merge four 6 bit chars to three 8 bit chars */
+                   fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream);
+                       line_ptr++;
+                       length--;
+                       if (length == 0) {
+                               break;
+                       }
+
+                       fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream);
+                       line_ptr++;
+                       length--;
+                       if (length == 0) {
+                               break;
+                       }
+
+                       fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream);
+                       line_ptr += 2;
+                       length -= 2;
+               }
+               free(line);
+       }
+       bb_error_msg_and_die("Short file");
+}
+
+static int read_base64(FILE *src_stream, FILE *dst_stream)
+{
+       const char *base64_table =
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n";
+       char term_count = 0;
+
+       while (1) {
+               char translated[4];
+               int count = 0;
+
+               while (count < 4) {
+                       char *table_ptr;
+                       char ch;
+
+                       /* Get next _valid_ character */
+                       do {
+                               ch = fgetc(src_stream);
+                               if (ch == EOF) {
+                                       bb_error_msg_and_die("Short file");
+                               }
+                       } while ((table_ptr = strchr(base64_table, ch)) == NULL);
+
+                       /* Convert encoded charcter to decimal */
+                       ch = table_ptr - base64_table;
+
+                       if (*table_ptr == '=') {
+                               if (term_count == 0) {
+                                       translated[count] = 0;
+                                       break;
+                               }
+                               term_count++;
+                       }
+                       else if (*table_ptr == '\n') {
+                               /* Check for terminating line */
+                               if (term_count == 5) {
+                                       return(EXIT_SUCCESS);
+                               }
+                               term_count = 1;
+                               continue;
+                       } else {
+                               translated[count] = ch;
+                               count++;
+                               term_count = 0;
+                       }
+               }
+
+               /* Merge 6 bit chars to 8 bit */
+           fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
+               if (count > 2) {
+                       fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
+               }
+               if (count > 3) {
+                       fputc(translated[2] << 6 | translated[3], dst_stream);
+               }
+       }
+}
+
+extern int uudecode_main(int argc, char **argv)
+{
+       int (*decode_fn_ptr) (FILE * src, FILE * dst);
+       FILE *src_stream;
+       char *outname = NULL;
+       char *line;
+       int opt;
+
+       opt = bb_getopt_ulflags(argc, argv, "o:", &outname);
+
+       if (optind == argc) {
+               src_stream = stdin;
+       } else if (optind + 1 == argc) {
+               src_stream = bb_xfopen(argv[optind], "r");
+       } else {
+               bb_show_usage();
+       }
+
+       /* Search for the start of the encoding */
+       while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
+               char *line_ptr = NULL;
+
+               if (line == NULL) {
+                       break;
+               } else if (strncmp(line, "begin-base64 ", 13) == 0) {
+                       line_ptr = line + 13;
+                       decode_fn_ptr = read_base64;
+               } else if (strncmp(line, "begin ", 6) == 0) {
+                       line_ptr = line + 6;
+                       decode_fn_ptr = read_stduu;
+               }
+
+               if (line_ptr) {
+                       FILE *dst_stream;
+                       int mode;
+                       int ret;
+
+                       mode = strtoul(line_ptr, NULL, 8);
+                       if (outname == NULL) {
+                               outname = strchr(line_ptr, ' ');
+                               if ((outname == NULL) || (*outname == '\0')) {
+                                       break;
+                               }
+                               outname++;
+                       }
+                       if (strcmp(outname, "-") == 0) {
+                               dst_stream = stdout;
+                       } else {
+                               dst_stream = bb_xfopen(outname, "w");
+                               chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+                       }
+                       free(line);
+                       ret = decode_fn_ptr(src_stream, dst_stream);
+                       bb_fclose_nonstdin(src_stream);
+                       return(ret);
+               }
+               free(line);
+       }
+       bb_error_msg_and_die("No `begin' line");
+}
diff --git a/busybox/coreutils/uuencode.c b/busybox/coreutils/uuencode.c
new file mode 100644 (file)
index 0000000..42f629f
--- /dev/null
@@ -0,0 +1,149 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  Copyright (C) 2000 by Glenn McGrath
+ *
+ *  based on the function base64_encode from http.c in wget v1.6
+ *  Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
+ *
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "busybox.h"
+
+/* Conversion table.  for base 64 */
+static const char tbl_base64[65] = {
+       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+       'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+       'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+       'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+       'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+       'w', 'x', 'y', 'z', '0', '1', '2', '3',
+       '4', '5', '6', '7', '8', '9', '+', '/',
+       '=' /* termination character */
+};
+
+static const char tbl_std[65] = {
+       '`', '!', '"', '#', '$', '%', '&', '\'',
+       '(', ')', '*', '+', ',', '-', '.', '/',
+       '0', '1', '2', '3', '4', '5', '6', '7',
+       '8', '9', ':', ';', '<', '=', '>', '?',
+       '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+       'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+       'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+       'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+       '`' /* termination character */
+};
+
+/*
+ * Encode the string S of length LENGTH to base64 format and place it
+ * to STORE.  STORE will be 0-terminated, and must point to a writable
+ * buffer of at least 1+BASE64_LENGTH(length) bytes.
+ * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
+ */
+static void uuencode (const unsigned char *s, const char *store, const int length, const char *tbl)
+{
+       int i;
+       unsigned char *p = (unsigned char *)store;
+
+       /* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
+       for (i = 0; i < length; i += 3) {
+               *p++ = tbl[s[0] >> 2];
+               *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
+               *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
+               *p++ = tbl[s[2] & 0x3f];
+               s += 3;
+       }
+       /* Pad the result if necessary...  */
+       if (i == length + 1) {
+               *(p - 1) = tbl[64];
+       }
+       else if (i == length + 2) {
+               *(p - 1) = *(p - 2) = tbl[64];
+       }
+       /* ...and zero-terminate it.  */
+       *p = '\0';
+}
+
+#define SRC_BUF_SIZE   45  // This *MUST* be a multiple of 3
+#define DST_BUF_SIZE    4 * ((SRC_BUF_SIZE + 2) / 3)
+int uuencode_main(int argc, char **argv)
+{
+       const int src_buf_size = SRC_BUF_SIZE;
+       const int dst_buf_size = DST_BUF_SIZE;
+       int write_size = dst_buf_size;
+       struct stat stat_buf;
+       FILE *src_stream = stdin;
+       const char *tbl;
+       size_t size;
+       mode_t mode;
+       RESERVE_CONFIG_BUFFER(src_buf, SRC_BUF_SIZE + 1);
+       RESERVE_CONFIG_BUFFER(dst_buf, DST_BUF_SIZE + 1);
+
+       tbl = tbl_std;
+       if (bb_getopt_ulflags(argc, argv, "m") & 1) {
+               tbl = tbl_base64;
+       }
+
+       switch (argc - optind) {
+               case 2:
+                       src_stream = bb_xfopen(argv[optind], "r");
+                       if (stat(argv[optind], &stat_buf) < 0) {
+                               bb_perror_msg_and_die("stat");
+                       }
+                       mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+                       if (src_stream == stdout) {
+                               puts("NULL");
+                       }
+                       break;
+               case 1:
+                       mode = 0666 & ~umask(0666);
+                       break;
+               default:
+                       bb_show_usage();
+       }
+
+       bb_printf("begin%s %o %s", tbl == tbl_std ? "" : "-base64", mode, argv[argc - 1]);
+
+       while ((size = fread(src_buf, 1, src_buf_size, src_stream)) > 0) {
+               if (size != src_buf_size) {
+                       /* write_size is always 60 until the last line */
+                       write_size=(4 * ((size + 2) / 3));
+                       /* pad with 0s so we can just encode extra bits */
+                       memset(&src_buf[size], 0, src_buf_size - size);
+               }
+               /* Encode the buffer we just read in */
+               uuencode(src_buf, dst_buf, size, tbl);
+
+               putchar('\n');
+               if (tbl == tbl_std) {
+                       putchar(tbl[size]);
+               }
+               if (fwrite(dst_buf, 1, write_size, stdout) != write_size) {
+                       bb_perror_msg_and_die(bb_msg_write_error);
+               }
+       }
+       bb_printf(tbl == tbl_std ? "\n`\nend\n" : "\n====\n");
+
+       bb_xferror(src_stream, "source");       /* TODO - Fix this! */
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/watch.c b/busybox/coreutils/watch.c
new file mode 100644 (file)
index 0000000..f9f4018
--- /dev/null
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini watch implementation for busybox
+ *
+ * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+/* BB_AUDIT GNU defects -- only option -n is supported. */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Removed dependency on date_main(), added proper error checking, and
+ * reduced size.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include "busybox.h"
+
+extern int watch_main(int argc, char **argv)
+{
+       const int header_len = 40;
+       time_t t;
+       pid_t pid;
+       unsigned period = 2;
+       int old_stdout;
+       int len, len2;
+       char **watched_argv;
+       char header[header_len + 1];
+
+       if (argc < 2) {
+               bb_show_usage();
+       }
+
+       /* don't use getopt, because it permutes the arguments */
+       ++argv;
+       if ((argc > 3) && !strcmp(*argv, "-n")
+       ) {
+               period = bb_xgetularg10_bnd(argv[1], 1, UINT_MAX);
+               argv += 2;
+       }
+       watched_argv = argv;
+
+       /* create header */
+
+       len = snprintf(header, header_len, "Every %ds:", period);
+       /* Don't bother checking for len < 0, as it should never happen.
+        * But, just to be prepared... */
+       assert(len >= 0);
+       do {
+               len2 = strlen(*argv);
+               if (len + len2 >= header_len-1) {
+                       break;
+               }
+               header[len] = ' ';
+               memcpy(header+len+1, *argv, len2);
+               len += len2+1;
+       } while (*++argv);
+
+       header[len] = 0;
+
+       /* thanks to lye, who showed me how to redirect stdin/stdout */
+       old_stdout = dup(1);
+
+       while (1) {
+               time(&t);
+               /* Use dprintf to avoid fflush()ing stdout. */
+               if (dprintf(1, "\033[H\033[J%-*s%s\n", header_len, header, ctime(&t)) < 0) {
+                       bb_perror_msg_and_die("printf");
+               }
+
+               pid = vfork();  /* vfork, because of ucLinux */
+               if (pid > 0) {
+                       //parent
+                       wait(0);
+                       sleep(period);
+               } else if (0 == pid) {
+                       //child
+                       close(1);
+                       dup(old_stdout);
+                       if (execvp(*watched_argv, watched_argv)) {
+                               bb_error_msg_and_die("Couldn't run command\n");
+                       }
+               } else {
+                       bb_error_msg_and_die("Couldn't vfork\n");
+               }
+       }
+}
diff --git a/busybox/coreutils/wc.c b/busybox/coreutils/wc.c
new file mode 100644 (file)
index 0000000..0eb795c
--- /dev/null
@@ -0,0 +1,227 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wc implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- option -m is not currently supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Rewritten to fix a number of problems and do some size optimizations.
+ * Problems in the previous busybox implementation (besides bloat) included:
+ *  1) broken 'wc -c' optimization (read note below)
+ *  2) broken handling of '-' args
+ *  3) no checking of ferror on EOF returns
+ *  4) isprint() wasn't considered when word counting.
+ *
+ * TODO:
+ *
+ * When locale support is enabled, count multibyte chars in the '-m' case.
+ *
+ * NOTES:
+ *
+ * The previous busybox wc attempted an optimization using stat for the
+ * case of counting chars only.  I omitted that because it was broken.
+ * It didn't take into account the possibility of input coming from a
+ * pipe, or input from a file with file pointer not at the beginning.
+ *
+ * To implement such a speed optimization correctly, not only do you
+ * need the size, but also the file position.  Note also that the
+ * file position may be past the end of file.  Consider the example
+ * (adapted from example in gnu wc.c)
+ *
+ *      echo hello > /tmp/testfile &&
+ *      (dd ibs=1k skip=1 count=0 &> /dev/null ; wc -c) < /tmp/testfile
+ *
+ * for which 'wc -c' should output '0'.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "busybox.h"
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#include <locale.h>
+#include <ctype.h>
+#define isspace_given_isprint(c) isspace(c)
+#else
+#undef isspace
+#undef isprint
+#define isspace(c) ((((c) == ' ') || (((unsigned int)((c) - 9)) <= (13 - 9))))
+#define isprint(c) (((unsigned int)((c) - 0x20)) <= (0x7e - 0x20))
+#define isspace_given_isprint(c) ((c) == ' ')
+#endif
+
+enum {
+       WC_LINES        = 0,
+       WC_WORDS        = 1,
+       WC_CHARS        = 2,
+       WC_LENGTH       = 3
+};
+
+/* Note: If this changes, remember to change the initialization of
+ *       'name' in wc_main.  It needs to point to the terminating nul. */
+static const char wc_opts[] = "lwcL";  /* READ THE WARNING ABOVE! */
+
+enum {
+       OP_INC_LINE     = 1, /* OP_INC_LINE must be 1. */
+       OP_SPACE        = 2,
+       OP_NEWLINE      = 4,
+       OP_TAB          = 8,
+       OP_NUL          = 16,
+};
+
+/* Note: If fmt_str changes, the offsets to 's' in the OUTPUT section
+ *       will need to be updated. */
+static const char fmt_str[] = " %7u\0 %s\n";
+static const char total_str[] = "total";
+
+int wc_main(int argc, char **argv)
+{
+       FILE *fp;
+       const char *s;
+       unsigned int *pcounts;
+       unsigned int counts[4];
+       unsigned int totals[4];
+       unsigned int linepos;
+       unsigned int u;
+       int num_files = 0;
+       int c;
+       char status = EXIT_SUCCESS;
+       char in_word;
+       char print_type;
+
+       print_type = bb_getopt_ulflags(argc, argv, wc_opts);
+
+       if (print_type == 0) {
+               print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS);
+       }
+
+       argv += optind;
+       if (!*argv) {
+               *--argv = (char *) bb_msg_standard_input;
+       }
+
+       memset(totals, 0, sizeof(totals));
+
+       pcounts = counts;
+
+       do {
+               ++num_files;
+               if (!(fp = bb_wfopen_input(*argv))) {
+                       status = EXIT_FAILURE;
+                       continue;
+               }
+
+               memset(counts, 0, sizeof(counts));
+               linepos = 0;
+               in_word = 0;
+
+               do {
+                       ++counts[WC_CHARS];
+                       c = getc(fp);
+                       if (isprint(c)) {
+                               ++linepos;
+                               if (!isspace_given_isprint(c)) {
+                                       in_word = 1;
+                                       continue;
+                               }
+                       } else if (((unsigned int)(c - 9)) <= 4) {
+                               /* \t  9
+                                * \n 10
+                                * \v 11
+                                * \f 12
+                                * \r 13
+                                */
+                               if (c == '\t') {
+                                       linepos = (linepos | 7) + 1;
+                               } else {                        /* '\n', '\r', '\f', or '\v' */
+                               DO_EOF:
+                                       if (linepos > counts[WC_LENGTH]) {
+                                               counts[WC_LENGTH] = linepos;
+                                       }
+                                       if (c == '\n') {
+                                               ++counts[WC_LINES];
+                                       }
+                                       if (c != '\v') {
+                                               linepos = 0;
+                                       }
+                               }
+                       } else if (c == EOF) {
+                               if (ferror(fp)) {
+                                       bb_perror_msg("%s", *argv);
+                                       status = EXIT_FAILURE;
+                               }
+                               --counts[WC_CHARS];
+                               goto DO_EOF;            /* Treat an EOF as '\r'. */
+                       } else {
+                               continue;
+                       }
+
+                       counts[WC_WORDS] += in_word;
+                       in_word = 0;
+                       if (c == EOF) {
+                               break;
+                       }
+               } while (1);
+
+               if (totals[WC_LENGTH] < counts[WC_LENGTH]) {
+                       totals[WC_LENGTH] = counts[WC_LENGTH];
+               }
+               totals[WC_LENGTH] -= counts[WC_LENGTH];
+
+               bb_fclose_nonstdin(fp);
+
+       OUTPUT:
+               s = fmt_str + 1;                        /* Skip the leading space on 1st pass. */
+               u = 0;
+               do {
+                       if (print_type & (1 << u)) {
+                               bb_printf(s, pcounts[u]);
+                               s = fmt_str;            /* Ok... restore the leading space. */
+                       }
+                       totals[u] += pcounts[u];
+               } while (++u < 4);
+
+               s += 8;                                         /* Set the format to the empty string. */
+
+               if (*argv != bb_msg_standard_input) {
+                       s -= 3;                                 /* We have a name, so do %s conversion. */
+               }
+               bb_printf(s, *argv);
+
+       } while (*++argv);
+
+       /* If more than one file was processed, we want the totals.  To save some
+        * space, we set the pcounts ptr to the totals array.  This has the side
+        * effect of trashing the totals array after outputting it, but that's
+        * irrelavent since we no longer need it. */
+       if (num_files > 1) {
+               num_files = 0;                          /* Make sure we don't get here again. */
+               *--argv = (char *) total_str;
+               pcounts = totals;
+               goto OUTPUT;
+       }
+
+       bb_fflush_stdout_and_exit(status);
+}
diff --git a/busybox/coreutils/who.c b/busybox/coreutils/who.c
new file mode 100644 (file)
index 0000000..9561db1
--- /dev/null
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*----------------------------------------------------------------------
+ * Mini who is used to display user name, login time,
+ * idle time and host name.
+ *
+ * Author: Da Chen  <dchen@ayrnetworks.com>
+ *
+ * This is a free document; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation:
+ *    http://www.gnu.org/copyleft/gpl.html
+ *
+ * Copyright (c) 2002 AYR Networks, Inc.
+ *----------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include "busybox.h"
+
+extern int who_main(int argc, char **argv)
+{
+    struct utmp *ut;
+    struct stat st;
+    int         devlen, len;
+    time_t      now, idle;
+
+    if (argc > 1)
+        bb_show_usage();
+
+    setutent();
+    devlen = sizeof("/dev/") - 1;
+    printf("USER       TTY      IDLE      FROM           HOST\n");
+
+    while ((ut = getutent()) != NULL) {
+        char name[40];
+
+        if (ut->ut_user[0] && ut->ut_type == USER_PROCESS) {
+            len = strlen(ut->ut_line);
+            if (ut->ut_line[0] == '/') {
+               strncpy(name, ut->ut_line, len);
+               name[len] = '\0';
+               strcpy(ut->ut_line, ut->ut_line + devlen);
+            } else {
+               strcpy(name, "/dev/");
+               strncpy(name+devlen, ut->ut_line, len);
+               name[devlen+len] = '\0';
+            }
+
+            printf("%-10s %-8s ", ut->ut_user, ut->ut_line);
+
+            if (stat(name, &st) == 0) {
+                now = time(NULL);
+                idle = now -  st.st_atime;
+
+                if (idle < 60)
+                    printf("00:00m    ");
+                else if (idle < (60 * 60))
+                    printf("00:%02dm    ", (int)(idle / 60));
+                else if (idle < (24 * 60 * 60))
+                    printf("%02d:%02dm    ", (int)(idle / (60 * 60)),
+                           (int)(idle % (60 * 60)) / 60);
+                else if (idle < (24 * 60 * 60 * 365))
+                    printf("%03ddays   ", (int)(idle / (24 * 60 * 60)));
+                else
+                    printf("%02dyears   ", (int) (idle / (24 * 60 * 60 * 365)));
+            } else
+                printf("%-8s  ", "?");
+
+            printf("%-12.12s   %s\n", ctime(&(ut->ut_tv.tv_sec)) + 4, ut->ut_host);
+        }
+    }
+    endutent();
+
+    return 0;
+}
diff --git a/busybox/coreutils/whoami.c b/busybox/coreutils/whoami.c
new file mode 100644 (file)
index 0000000..6a6e2ee
--- /dev/null
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini whoami implementation for busybox
+ *
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+extern int whoami_main(int argc, char **argv)
+{
+       if (argc > 1)
+               bb_show_usage();
+
+       puts(my_getpwuid(NULL, geteuid(), -1));
+       /* exits on error */
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/coreutils/yes.c b/busybox/coreutils/yes.c
new file mode 100644 (file)
index 0000000..74f7571
--- /dev/null
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * yes implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reductions and removed redundant applet name prefix from error messages.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int yes_main(int argc, char **argv)
+{
+       static const char fmt_str[] = " %s";
+       const char *fmt;
+       char **first_arg;
+
+       *argv = "y";
+       if (argc != 1) {
+               ++argv;
+       }
+
+       first_arg = argv;
+       do {
+               fmt = fmt_str + 1;
+               do {
+                       bb_printf(fmt, *argv);
+                       fmt = fmt_str;
+               } while (*++argv);
+               argv = first_arg;
+       } while (putchar('\n') != EOF);
+
+       bb_perror_nomsg_and_die();
+}
diff --git a/busybox/debian/busybox-cvs-doc.docs b/busybox/debian/busybox-cvs-doc.docs
new file mode 100644 (file)
index 0000000..e6f531b
--- /dev/null
@@ -0,0 +1 @@
+docs/busybox.pdf
diff --git a/busybox/debian/busybox-cvs-static.dirs b/busybox/debian/busybox-cvs-static.dirs
new file mode 100644 (file)
index 0000000..f088365
--- /dev/null
@@ -0,0 +1,2 @@
+bin
+usr/share/lintian/overrides
diff --git a/busybox/debian/busybox-cvs-static.manpages b/busybox/debian/busybox-cvs-static.manpages
new file mode 100644 (file)
index 0000000..c28fa90
--- /dev/null
@@ -0,0 +1 @@
+busybox.1
diff --git a/busybox/debian/busybox-cvs-static.override b/busybox/debian/busybox-cvs-static.override
new file mode 100644 (file)
index 0000000..480494f
--- /dev/null
@@ -0,0 +1 @@
+busybox-cvs-static: statically-linked-binary ./bin/busybox
diff --git a/busybox/debian/busybox-cvs.dirs b/busybox/debian/busybox-cvs.dirs
new file mode 100644 (file)
index 0000000..ba077a4
--- /dev/null
@@ -0,0 +1 @@
+bin
diff --git a/busybox/debian/busybox-cvs.manpages b/busybox/debian/busybox-cvs.manpages
new file mode 100644 (file)
index 0000000..c28fa90
--- /dev/null
@@ -0,0 +1 @@
+busybox.1
diff --git a/busybox/debian/changelog b/busybox/debian/changelog
new file mode 100644 (file)
index 0000000..d9f82e3
--- /dev/null
@@ -0,0 +1,479 @@
+busybox-cvs (20031212-3) unstable; urgency=low
+
+  * debian/config-udeb-linux:
+    - Enable freeramdisk. (closes: #225360)
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 30 Dec 2003 22:36:50 +0100
+
+busybox-cvs (20031212-2) unstable; urgency=low
+
+  * debian/config-udeb:
+    - Enable freeramdisk
+  * archival/libunarchive/data_extract_to_stdout.c:
+    - Don't extract to much (upstream).
+  * archival/libunarchive/data_extract_all.c:
+    - Don't set permissions on symlinks (upstream).
+  * editors/sed.c:
+    - Fix (upstream). (closes: #224676)
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 23 Dec 2003 16:59:13 +0100
+
+busybox-cvs (20031212-1) unstable; urgency=low
+
+  * new cvs version
+    - fixes IOR in fdisk. (closes: #223773)
+  * debian/config*
+    - update
+    - remove mtab support from mount in busybox-static. (closes: #222386)
+    - enable progress bar for wget. (closes: #223770)
+  * modutils/obj/obj_s390{,x}.c
+    - fix abi change, R_390_GOTOFF -> R_390_GOTOFF32. (closes: #216528)
+  * acknowledge nmu. (closes: #216950, #216756, #215169, #215613)
+
+ -- Bastian Blank <waldi@debian.org>  Fri, 12 Dec 2003 21:19:41 +0100
+
+busybox-cvs (20030926-2.1) unstable; urgency=low
+
+  * NMU
+  * Remove /sbin/init from the udebs, while still leaving init support
+    compiled in. rootskel takes over providing init, but then calls bb init.
+    Remove linuxrc support from the udebs entirely. Closes: #216756
+  * config-floppy-udeb-linux: add minimal find, grep. Closes: #215169
+  * net-udeb-linux-i386: add loopback mount support. Closes: #215613
+
+ -- Joey Hess <joeyh@debian.org>  Tue, 21 Oct 2003 12:47:52 -0400
+
+busybox-cvs (20030926-2) unstable; urgency=low
+
+  * debian/config-floppy-udeb-linux
+    - update for new floppy images (closes: #212986, #214102)
+
+ -- Bastian Blank <waldi@debian.org>  Thu, 09 Oct 2003 12:25:49 +0200
+
+busybox-cvs (20030926-1) unstable; urgency=low
+
+  * new cvs version
+  * Makefile
+    - fix libpwdgrp link (upstream) (closes: #211675)
+  * Rules.mak.in
+    - fix optimization (closes: #212485)
+  * debian/config*
+    - update
+  * debian/config*udeb*
+    - move linux-i386 to linux (enable modutils on any linux arch)
+    - rename net to floppy
+    - enable wget status bar (closes: #211457)
+  * init/init.c
+    - workaround race conditions (closes: #212764)
+
+ -- Bastian Blank <waldi@debian.org>  Fri, 26 Sep 2003 15:10:14 +0200
+
+busybox-cvs (0.60.99.cvs20030819-3) unstable; urgency=low
+
+  * shell/ash.c
+    - fix signal handling (upstream)
+
+ -- Bastian Blank <waldi@debian.org>  Mon, 15 Sep 2003 18:12:09 +0200
+
+busybox-cvs (0.60.99.cvs20030819-2) unstable; urgency=low
+
+  * Fix configure permisions
+  * Set source Section to embedded
+
+ -- Glenn McGrath <bug1@debian.org>  Mon, 25 Aug 2003 06:33:19 +0000
+
+busybox-cvs (0.60.99.cvs20030819-1) unstable; urgency=low
+
+  * new cvs version
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 19 Aug 2003 13:18:54 +0200
+
+busybox-cvs (0.60.99.cvs20030426-10) unstable; urgency=low
+
+  * archival/libunarchive/*
+    - add hardlink support (pending)
+  * debian/config-*udeb*
+    - add support for oldgnu tar format
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 10 Jun 2003 12:06:41 +0200
+
+busybox-cvs (0.60.99.cvs20030426-9) unstable; urgency=low
+
+  * modutils/depmod.c
+    - fix base_dir for modules.dep
+
+ -- Bastian Blank <waldi@debian.org>  Sat, 07 Jun 2003 14:52:54 +0200
+
+busybox-cvs (0.60.99.cvs20030426-8) unstable; urgency=low
+
+  * util-linux/fdisk.c
+    - fix syscalls. (pending)
+  * libpwd/*
+    - don't build setgroups.o
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 03 Jun 2003 11:26:28 +0200
+
+busybox-cvs (0.60.99.cvs20030426-7) unstable; urgency=low
+
+  * libbb/*syscallc.c
+    - fix syscalls. (upstream) (closes: #194631)
+  * modutils/*
+    - update complete objects code from upstream. (pending)
+  * debian/config-*udeb*
+    - update to busybox-applets.txt:1.14
+
+ -- Bastian Blank <waldi@debian.org>  Thu, 29 May 2003 16:17:14 +0200
+
+busybox-cvs (0.60.99.cvs20030426-6) unstable; urgency=low
+
+  * modutils/Makefile.in
+    - don't build anything if deactivated
+
+ -- Bastian Blank <waldi@debian.org>  Sat, 24 May 2003 18:57:14 +0200
+
+busybox-cvs (0.60.99.cvs20030426-5) unstable; urgency=low
+
+  * debian/config-*udeb*
+    - revert changes (closes: #192717, #192753)
+    - readd initrd support (workaround)
+    - make init quiet
+  * archival/tar.c
+    - fix usage of tar -O (upstream) (closes: #193788)
+
+ -- Bastian Blank <waldi@debian.org>  Sat, 24 May 2003 13:25:36 +0200
+
+busybox-cvs (0.60.99.cvs20030426-4) unstable; urgency=low
+
+  * debian/config-*udeb*
+    - update to busybox-applets.txt:1.12
+  * debian/rules
+    - use system instead of arch to determine which config file to use
+
+ -- Bastian Blank <waldi@debian.org>  Fri, 09 May 2003 11:21:13 +0200
+
+busybox-cvs (0.60.99.cvs20030426-3) unstable; urgency=low
+
+  * debian/config-udeb*
+    - update to busybox-applets.txt:1.11
+
+ -- Bastian Blank <waldi@debian.org>  Wed, 07 May 2003 10:37:40 +0200
+
+busybox-cvs (0.60.99.cvs20030426-2) unstable; urgency=low
+
+  * modutils/*
+    - modprobe failes gracefully if the module is already loaded. (pending)
+    - implement depmod. (pending)
+  * debian/config*
+    - update
+
+ -- Bastian Blank <waldi@debian.org>  Wed, 30 Apr 2003 14:12:36 +0200
+
+busybox-cvs (0.60.99.cvs20030426-1) unstable; urgency=low
+
+  * new cvs version
+
+ -- Bastian Blank <waldi@debian.org>  Sat, 26 Apr 2003 18:33:17 +0200
+
+busybox-cvs (0.60.99.cvs20030420-4) unstable; urgency=low
+
+  * network/libiproute/iproute.c
+    - fix usage of ip route flush (upstream)
+
+ -- Bastian Blank <waldi@debian.org>  Fri, 25 Apr 2003 16:48:43 +0200
+
+busybox-cvs (0.60.99.cvs20030420-3) unstable; urgency=low
+
+  * debian/config-*udeb*
+    - update to busybox-applets.txt:1.10
+
+ -- Bastian Blank <waldi@debian.org>  Fri, 25 Apr 2003 14:03:23 +0200
+
+busybox-cvs (0.60.99.cvs20030420-2) unstable; urgency=low
+
+  * archival/tar.c, archival/libunarchive/data_extract_all.c
+    - unlink files first (upstream patch)
+
+ -- Bastian Blank <waldi@debian.org>  Mon, 21 Apr 2003 12:42:06 +0200
+
+busybox-cvs (0.60.99.cvs20030420-1) unstable; urgency=low
+
+  * new cvs version
+  * debian/config*-udeb*
+    - add ifconfig/route for easier transition
+  * debian/config-net-udeb-i386
+    - add logger
+  * debian/config-udeb*
+    - add uniq
+
+ -- Bastian Blank <waldi@debian.org>  Sun, 20 Apr 2003 21:02:58 +0200
+
+busybox-cvs (0.60.99.cvs20030405-1) unstable; urgency=low
+
+  * new cvs version
+  * debian/control
+    - add busybox-cvs-net-udeb
+  * debian/rules
+    - arch depend debs
+    - new net-udeb
+  * debian/config*
+    - cleanup applet list
+
+ -- Bastian Blank <waldi@debian.org>  Sat, 05 Apr 2003 11:44:50 +0200
+
+busybox-cvs (0.60.99.cvs20030221-1) unstable; urgency=low
+
+  * new cvs version
+  * enable new applets in udeb
+
+ -- Bastian Blank <waldi@debian.org>  Fri, 21 Feb 2003 23:15:16 +0100
+
+busybox-cvs (0.60.99.cvs20030114-1) unstable; urgency=low
+
+  * new cvs version
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 14 Jan 2003 17:06:43 +0000
+
+busybox-cvs (0.60.99.cvs20030105-1) unstable; urgency=low
+
+  * Fix ip command build failure on ia64 (Closes: #172580
+  * Dont build with BSD partition table support in fdisk, fails on m68k
+
+ -- Glenn McGrath <bug1@home>  Sun,  5 Jan 2003 12:48:05 +1100
+
+busybox-cvs (0.60.99.cvs20030104-2) unstable; urgency=low
+
+  * floppy-retriever needs the cut command in the udeb
+
+ -- Glenn McGrath <bug1@debian.org>  Sat,  4 Jan 2003 17:13:05 +1100
+
+busybox-cvs (0.60.99.cvs20030104-1) unstable; urgency=low
+
+  * new cvs version
+  * Include new applets in the static package
+  * Include ash in the udeb
+
+ -- Glenn McGrath <bug1@debian.org>  Sat,  4 Jan 2003 13:39:04 +1100
+
+busybox-cvs (0.60.99.cvs20021214-1) unstable; urgency=low
+
+  * new cvs version
+    - udhcp merge.
+  * fix location of ip link.
+
+ -- Bastian Blank <waldi@debian.org>  Sat, 14 Dec 2002 13:52:15 +0100
+
+busybox-cvs (0.60.99.cvs20021210-1) unstable; urgency=low
+
+  * new cvs version
+    - various upstream fixes found in the last version.
+  * busybox-cvs-udeb
+    - include readlink and realpath.
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 10 Dec 2002 21:23:40 +0100
+
+busybox-cvs (0.60.99.cvs20021209-2) unstable; urgency=low
+
+  * cleanup scripts/config/ within make clean (closes: #172413)
+  * busybox-cvs-udeb
+    - include readlink
+  * include manpages within busybox-cvs and busybox-cvs-static
+
+ -- Bastian Blank <waldi@debian.org>  Mon, 09 Dec 2002 22:09:52 +0100
+
+busybox-cvs (0.60.99.cvs20021209-1) unstable; urgency=low
+
+  * New cvs version.
+    - fix udhcpc for use with ip.
+    - klogd supports -c.
+  * busybox-cvs-udeb
+    - set priority to extra. (closes: #172302)
+    - don't longer provide busybox-udeb.
+
+ -- Bastian Blank <waldi@debian.org>  Mon, 09 Dec 2002 16:22:03 +0100
+
+busybox-cvs (0.60.99.cvs20021203-1) unstable; urgency=low
+
+  * New packages based on busybox cvs.
+  * changes for the udeb
+    - enable ip applet with address, link and route part.
+    - enable udhcpc.
+    - disable ifconfig and route applet.
+    - disable ls color.
+
+ -- Bastian Blank <waldi@debian.org>  Tue, 03 Dec 2002 18:51:00 +0100
+
+busybox (1:0.60.0-1) unstable; urgency=low
+
+  * New version released.  See changelog for details.
+
+ -- Erik Andersen <andersee@debian.org>  Thu,  2 Aug 2001 12:12:37 -0600
+
+busybox (1:0.52-1.1) unstable; urgency=high
+
+  * Non-maintainer upload
+  * Fixed wget -P handling (closes: #106223).
+
+ -- Matt Kraai <kraai@debian.org>  Wed, 25 Jul 2001 11:01:38 -0600
+
+busybox (1:0.52-1) unstable; urgency=high
+
+  * New version released.  See changelog for details.
+
+ -- Erik Andersen <andersee@debian.org>  Sat,  7 Jul 2001 01:23:45 -0600
+
+busybox (1:0.51-10) unstable; urgency=high
+
+  * Fix a compile problem with gcc 3.0 on hppa (closes: #102045)
+
+ -- Erik Andersen <andersee@debian.org>  Sat, 23 Jun 2001 23:55:57 -0600
+
+busybox (1:0.51-9) unstable; urgency=high
+
+  * tar was creating leading directories with 0777 permissions as
+    as reult of faulty umask handling.  This fixes it, repairing
+    a grave security problem in the woody the boot floppies.
+    (closes: #101169)
+
+ -- Erik Andersen <andersee@debian.org>  Wed, 20 Jun 2001 16:17:38 -0600
+
+busybox (1:0.51-8) unstable; urgency=high
+
+  * Fix cp from /proc, where size=0 (closes: #100369)
+  * Add some padding to struct sysinfo for m68k.
+  * Apparently some bugs failed to be closed when master choked
+    (closes: #99627, #99637, #98571)
+  * Disable the busybox shell for the .deb, since it is not needed
+    for the boot floppies.
+
+ -- Erik Andersen <andersee@debian.org>  Mon, 11 Jun 2001 13:26:07 -0600
+
+busybox (1:0.51-7) unstable; urgency=high
+
+  * Fix tar permission setting for existing directories (closes: #99627)
+  * Do not remove the .cvsignore files on 'make release' (closes: #99637)
+
+ -- Erik Andersen <andersee@debian.org>  Mon,  4 Jun 2001 10:55:19 -0600
+
+busybox (1:0.51-6) testing unstable; urgency=high
+
+  * Update the version in testing so DHCP in the woody boot-floppies will work.
+  * Enable expr for the boot-floppies (closes: #98433)
+  * It builds on arm just fine now (closes: #97510)
+
+ -- Erik Andersen <andersee@debian.org>  Wed, 23 May 2001 14:50:13 -0600
+
+busybox (1:0.51-5) unstable; urgency=low
+
+  * Backport a sed fix from 0.52pre
+  * Backport chroot fix from 0.52pre
+
+ -- Erik Andersen <andersee@debian.org>  Wed, 16 May 2001 23:50:33 -0600
+
+busybox (1:0.51-4) unstable; urgency=low
+
+  * Backport from 0.52pre an endianness bugfix for md5sum
+  * Backport some updates to grep and sed
+  * Fix 'wget -O -' so it sets the quiet flag
+
+ -- Erik Andersen <andersee@debian.org>  Mon, 14 May 2001 14:17:36 -0600
+
+busybox (1:0.51-3) unstable; urgency=low
+
+  * This is the "I am an idiot" release.
+  * Make cp and mv work again (closes: #97290)
+  * Fix the version number.
+
+ -- Erik Andersen <andersee@debian.org>  Sat, 12 May 2001 17:35:58 -0600
+
+busybox (0.51-2) unstable; urgency=low
+
+  * Backport several release critical fixes into the 0.51 codebase
+    so the boot-floppies will work again.
+  * Fix a link ordering problem. (closes: #93362)
+  * Fixed gunzip (closes: #94331)
+  * Fixed cp permission setting (closes: #94580)
+
+ -- Erik Andersen <andersee@debian.org>  Sat, 12 May 2001 11:22:35 -0600
+
+busybox (0.51-1) unstable; urgency=low
+
+  * Fixes several critical bugs (see the busybox changelog
+    for complete details)
+  * Force USE_SYSTEM_PWD_GRP=false, so busybox bypasses
+    the glibc NSS libraries. (closes: #93362)
+  * Fixed a bug in sed's address range handling (closes: #91758)
+  * Removed irrelevant cruft from the bottem of debian/changelog
+
+ -- Erik Andersen <andersee@debian.org>  Tue, 10 Apr 2001 14:07:29 -0600
+
+busybox (0.50-2) unstable; urgency=low
+
+  * Enabled freeramdisk and pivot_root in the udeb (closes: #91336)
+  * Disabled lash (the busybox shell) in the udeb (closes: #91337)
+  * fixed a bug in syslog, a problem with rebooting when booted as
+    an initrd, and a few other minor problems.
+
+ -- Erik Andersen <andersee@debian.org>  Sun, 25 Mar 2001 20:59:44 -0700
+
+
+busybox (0.50-2) unstable; urgency=low
+
+  * Enabled freeramdisk and pivot_root in the udeb (closes: #91336)
+  * Disabled lash (the busybox shell) in the udeb (closes: #91337)
+  * fixed a bug in syslog, a problem with rebooting when booted as
+    an initrd, and a few other minor problems.
+
+ -- Erik Andersen <andersee@debian.org>  Sun, 25 Mar 2001 20:59:44 -0700
+
+busybox (0.50-1) unstable; urgency=low
+
+  * Tons on improvements all around -- See changelog for details.
+  * Fix malformed Build-Depends (closes: #86930)
+  * grep has worked for some time now (closes: #81084)
+  * init compiles with DEBUG_INIT enabled (closes: #85794)
+  * md5sum no longer displays filename when reading stdin (closes: #81283)
+  * lsmod, rmmod, and insmod are no longer enabled (closes: #85642)
+  * busybox and buxybox-static now conflict/replace each other (closes: #80421)
+
+ -- Erik Andersen <andersee@debian.org>  Thu, 15 Mar 2001 14:45:00 -0700
+
+busybox (0.49-1) unstable; urgency=low
+
+  * Lots more source updates and bug fixes.  See changelog for details.
+
+ -- Erik Andersen <andersee@debian.org>  Sat, 27 Jan 2001 01:45:53 -0700
+
+busybox (0.48-1) unstable; urgency=low
+
+  * Lots more source updates and bug fixes.  See changelog for details.
+  * Now includes .udeb support for the debian-installer.  The .udeb
+    probably needs some more work, but this should be a good start.
+
+ -- Erik Andersen <andersee@debian.org>  Wed, 13 Dec 2000 08:36:07 -0700
+
+busybox (0.47-1) unstable; urgency=low
+
+  * New version released.  See changelog for details.
+
+ -- Erik Andersen <andersee@debian.org>  Mon, 25 Sep 2000 23:00:56 -0600
+
+busybox (0.46-1) unstable; urgency=low
+
+  * New version released.  See changelog for details.
+
+ -- Erik Andersen <andersee@debian.org>  Tue, 11 Jul 2000 12:15:44 -0600
+
+busybox (0.45-1) unstable; urgency=low
+
+  * First attempt at packaging BusyBox as a .deb.  This has been in
+    in the Debian boot-floppies CVS tree forever.  Hopefully, having it as a
+    standalone app will make life easier for me, the debian-installer team, and
+    everyone else as well...
+  * I have created a busybox-static that can be used as a rescue shell when you
+    hose your system.  Just invoke "busybox sh" to fir up the shell.  This has
+    every app provided by busybox staically linked in.  There have been several
+    times in the past that I would have loved to have this sitting on my system
+    (i.e. when libc gets screwed up.)
+
+ -- Erik Andersen <andersee@debian.org>  Tue, 27 Jun 2000 12:26:41 -0600
+
diff --git a/busybox/debian/compat b/busybox/debian/compat
new file mode 100644 (file)
index 0000000..b8626c4
--- /dev/null
@@ -0,0 +1 @@
+4
diff --git a/busybox/debian/config-deb b/busybox/debian/config-deb
new file mode 100644 (file)
index 0000000..9c1034a
--- /dev/null
@@ -0,0 +1,379 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+# CONFIG_FEATURE_VERBOSE_USAGE is not set
+CONFIG_FEATURE_INSTALLER=y
+# CONFIG_LOCALE_SUPPORT is not set
+# CONFIG_FEATURE_DEVFS is not set
+# CONFIG_FEATURE_DEVPTS is not set
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+CONFIG_LFS=y
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+CONFIG_AR=y
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+# CONFIG_BUNZIP2 is not set
+# CONFIG_CPIO is not set
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GUNZIP=y
+# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
+CONFIG_GZIP=y
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+# CONFIG_FEATURE_TAR_BZIP2 is not set
+# CONFIG_FEATURE_TAR_EXCLUDE is not set
+CONFIG_FEATURE_TAR_GZIP=y
+# CONFIG_FEATURE_TAR_COMPRESS is not set
+# CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY is not set
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+# CONFIG_UNCOMPRESS is not set
+# CONFIG_UNZIP is not set
+
+#
+# Common options for cpio and tar
+#
+# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
+
+#
+# Coreutils
+#
+# CONFIG_BASENAME is not set
+# CONFIG_CAL is not set
+CONFIG_CAT=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+# CONFIG_CMP is not set
+CONFIG_CP=y
+CONFIG_CUT=y
+# CONFIG_DATE is not set
+# CONFIG_DD is not set
+CONFIG_DF=y
+CONFIG_DIRNAME=y
+# CONFIG_DOS2UNIX is not set
+# CONFIG_DU is not set
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+# CONFIG_ENV is not set
+CONFIG_EXPR=y
+CONFIG_FALSE=y
+# CONFIG_FOLD is not set
+CONFIG_HEAD=y
+# CONFIG_FEATURE_FANCY_HEAD is not set
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+# CONFIG_INSTALL is not set
+# CONFIG_LENGTH is not set
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+# CONFIG_MKFIFO is not set
+CONFIG_MKNOD=y
+CONFIG_MV=y
+# CONFIG_OD is not set
+# CONFIG_PRINTF is not set
+CONFIG_PWD=y
+# CONFIG_REALPATH is not set
+CONFIG_RM=y
+CONFIG_RMDIR=y
+# CONFIG_SHA1SUM is not set
+CONFIG_SLEEP=y
+# CONFIG_FEATURE_FANCY_SLEEP is not set
+CONFIG_SORT=y
+# CONFIG_STTY is not set
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+# CONFIG_TEE is not set
+CONFIG_TEST=y
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+# CONFIG_USLEEP is not set
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+# CONFIG_WATCH is not set
+CONFIG_WC=y
+# CONFIG_WHO is not set
+CONFIG_WHOAMI=y
+# CONFIG_YES is not set
+
+#
+# Common options for cp and mv
+#
+# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
+
+#
+# Common options for ls and more
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+
+#
+# Console Utilities
+#
+# CONFIG_CHVT is not set
+CONFIG_CLEAR=y
+# CONFIG_DEALLOCVT is not set
+# CONFIG_DUMPKMAP is not set
+# CONFIG_LOADACM is not set
+# CONFIG_LOADFONT is not set
+CONFIG_LOADKMAP=y
+# CONFIG_OPENVT is not set
+CONFIG_RESET=y
+# CONFIG_SETKEYCODES is not set
+
+#
+# Debian Utilities
+#
+# CONFIG_MKTEMP is not set
+# CONFIG_PIPE_PROGRESS is not set
+# CONFIG_READLINK is not set
+# CONFIG_RUN_PARTS is not set
+# CONFIG_START_STOP_DAEMON is not set
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+# CONFIG_AWK is not set
+# CONFIG_PATCH is not set
+CONFIG_SED=y
+# CONFIG_VI is not set
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+# CONFIG_FEATURE_FIND_XDEV is not set
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_GREP=y
+# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+# CONFIG_FEATURE_GREP_CONTEXT is not set
+# CONFIG_XARGS is not set
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_INITRD=y
+# CONFIG_FEATURE_INIT_COREDUMPS is not set
+# CONFIG_FEATURE_EXTRA_QUIET is not set
+CONFIG_HALT=y
+CONFIG_POWEROFF=y
+CONFIG_REBOOT=y
+# CONFIG_MESG is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_USE_BB_PWD_GRP is not set
+# CONFIG_ADDGROUP is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DELUSER is not set
+# CONFIG_GETTY is not set
+# CONFIG_LOGIN is not set
+# CONFIG_PASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+# CONFIG_CROND is not set
+# CONFIG_CRONTAB is not set
+# CONFIG_DC is not set
+# CONFIG_DEVFSD is not set
+# CONFIG_LAST is not set
+# CONFIG_HDPARM is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_MT is not set
+# CONFIG_STRINGS is not set
+# CONFIG_TIME is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Linux Module Utilities
+#
+# CONFIG_DEPMOD is not set
+# CONFIG_INSMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_RMMOD is not set
+
+#
+# Networking Utilities
+#
+# CONFIG_FEATURE_IPV6 is not set
+# CONFIG_ARPING is not set
+# CONFIG_FTPGET is not set
+# CONFIG_FTPPUT is not set
+CONFIG_HOSTNAME=y
+# CONFIG_HTTPD is not set
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+CONFIG_FEATURE_IFCONFIG_HW=y
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+# CONFIG_IFUPDOWN is not set
+# CONFIG_INETD is not set
+# CONFIG_IP is not set
+# CONFIG_IPCALC is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_NAMEIF is not set
+# CONFIG_NC is not set
+# CONFIG_NETSTAT is not set
+# CONFIG_NSLOOKUP is not set
+CONFIG_PING=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_ROUTE=y
+# CONFIG_TELNET is not set
+# CONFIG_TELNETD is not set
+# CONFIG_TFTP is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_IP6_LITERAL=y
+
+#
+# udhcp Server/Client
+#
+# CONFIG_UDHCPD is not set
+# CONFIG_UDHCPC is not set
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+# CONFIG_PIDOF is not set
+CONFIG_PS=y
+# CONFIG_RENICE is not set
+# CONFIG_TOP is not set
+CONFIG_UPTIME=y
+
+#
+# Another Bourne-like Shell
+#
+# CONFIG_FEATURE_SH_IS_ASH is not set
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+CONFIG_FEATURE_SH_IS_NONE=y
+# CONFIG_ASH is not set
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+# CONFIG_FBSET is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+# CONFIG_FDISK is not set
+# CONFIG_FREERAMDISK is not set
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_GETOPT is not set
+# CONFIG_HEXDUMP is not set
+# CONFIG_HWCLOCK is not set
+CONFIG_LOSETUP=y
+CONFIG_MKSWAP=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+# CONFIG_PIVOT_ROOT is not set
+# CONFIG_RDATE is not set
+CONFIG_SWAPONOFF=y
+CONFIG_MOUNT=y
+CONFIG_NFSMOUNT=y
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_MOUNT_FORCE=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/debian/config-floppy-udeb-linux b/busybox/debian/config-floppy-udeb-linux
new file mode 100644 (file)
index 0000000..b5f728c
--- /dev/null
@@ -0,0 +1,359 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+# CONFIG_FEATURE_VERBOSE_USAGE is not set
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_FEATURE_DEVFS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+CONFIG_LFS=y
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+# CONFIG_AR is not set
+# CONFIG_BUNZIP2 is not set
+# CONFIG_CPIO is not set
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GUNZIP=y
+# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
+# CONFIG_GZIP is not set
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+# CONFIG_TAR is not set
+# CONFIG_UNCOMPRESS is not set
+# CONFIG_UNZIP is not set
+
+#
+# Coreutils
+#
+# CONFIG_BASENAME is not set
+# CONFIG_CAL is not set
+CONFIG_CAT=y
+# CONFIG_CHGRP is not set
+# CONFIG_CHMOD is not set
+# CONFIG_CHOWN is not set
+CONFIG_CHROOT=y
+# CONFIG_CMP is not set
+CONFIG_CP=y
+# CONFIG_CUT is not set
+# CONFIG_DATE is not set
+# CONFIG_DD is not set
+# CONFIG_DF is not set
+# CONFIG_DIRNAME is not set
+# CONFIG_DOS2UNIX is not set
+# CONFIG_DU is not set
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+# CONFIG_ENV is not set
+# CONFIG_EXPR is not set
+# CONFIG_FALSE is not set
+# CONFIG_FOLD is not set
+# CONFIG_HEAD is not set
+# CONFIG_HOSTID is not set
+# CONFIG_ID is not set
+# CONFIG_INSTALL is not set
+# CONFIG_LENGTH is not set
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+# CONFIG_LS is not set
+# CONFIG_MD5SUM is not set
+# CONFIG_MKDIR is not set
+# CONFIG_MKFIFO is not set
+# CONFIG_MKNOD is not set
+# CONFIG_MV is not set
+# CONFIG_OD is not set
+# CONFIG_PRINTF is not set
+# CONFIG_PWD is not set
+# CONFIG_REALPATH is not set
+CONFIG_RM=y
+# CONFIG_RMDIR is not set
+# CONFIG_SHA1SUM is not set
+CONFIG_SLEEP=y
+# CONFIG_FEATURE_FANCY_SLEEP is not set
+# CONFIG_SORT is not set
+# CONFIG_STTY is not set
+# CONFIG_SYNC is not set
+# CONFIG_TAIL is not set
+# CONFIG_TEE is not set
+CONFIG_TEST=y
+
+#
+# test (forced enabled for use with shell)
+#
+# CONFIG_TOUCH is not set
+# CONFIG_TR is not set
+# CONFIG_TRUE is not set
+# CONFIG_TTY is not set
+# CONFIG_UNAME is not set
+# CONFIG_UNIQ is not set
+# CONFIG_USLEEP is not set
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+# CONFIG_WATCH is not set
+# CONFIG_WC is not set
+# CONFIG_WHO is not set
+# CONFIG_WHOAMI is not set
+# CONFIG_YES is not set
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Console Utilities
+#
+# CONFIG_CHVT is not set
+# CONFIG_CLEAR is not set
+# CONFIG_DEALLOCVT is not set
+# CONFIG_DUMPKMAP is not set
+# CONFIG_LOADACM is not set
+# CONFIG_LOADFONT is not set
+# CONFIG_LOADKMAP is not set
+# CONFIG_OPENVT is not set
+# CONFIG_RESET is not set
+# CONFIG_SETKEYCODES is not set
+
+#
+# Debian Utilities
+#
+# CONFIG_MKTEMP is not set
+# CONFIG_PIPE_PROGRESS is not set
+# CONFIG_READLINK is not set
+# CONFIG_RUN_PARTS is not set
+# CONFIG_START_STOP_DAEMON is not set
+# CONFIG_WHICH is not set
+
+#
+# Editors
+#
+# CONFIG_AWK is not set
+# CONFIG_PATCH is not set
+# CONFIG_SED is not set
+# CONFIG_VI is not set
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+# CONFIG_FEATURE_FIND_MTIME is not set
+# CONFIG_FEATURE_FIND_PERM is not set
+# CONFIG_FEATURE_FIND_TYPE is not set
+# CONFIG_FEATURE_FIND_XDEV is not set
+# CONFIG_FEATURE_FIND_NEWER is not set
+# CONFIG_FEATURE_FIND_INUM is not set
+CONFIG_GREP=y
+# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
+# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
+# CONFIG_FEATURE_GREP_CONTEXT is not set
+# CONFIG_XARGS is not set
+
+#
+# Init Utilities
+#
+# CONFIG_INIT is not set
+# CONFIG_HALT is not set
+# CONFIG_POWEROFF is not set
+# CONFIG_REBOOT is not set
+# CONFIG_MINIT is not set
+# CONFIG_MESG is not set
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_USE_BB_PWD_GRP=y
+# CONFIG_ADDGROUP is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DELUSER is not set
+# CONFIG_GETTY is not set
+# CONFIG_LOGIN is not set
+# CONFIG_PASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+# CONFIG_CROND is not set
+# CONFIG_CRONTAB is not set
+# CONFIG_DC is not set
+# CONFIG_DEVFSD is not set
+# CONFIG_LAST is not set
+# CONFIG_HDPARM is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_MT is not set
+# CONFIG_STRINGS is not set
+# CONFIG_TIME is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Linux Module Utilities
+#
+CONFIG_MODUTILS_OBJ=y
+# CONFIG_DEPMOD is not set
+CONFIG_INSMOD=y
+# CONFIG_FEATURE_2_2_MODULES is not set
+CONFIG_FEATURE_2_4_MODULES=y
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
+# CONFIG_LSMOD is not set
+CONFIG_MODPROBE=y
+# CONFIG_RMMOD is not set
+CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
+
+#
+# Networking Utilities
+#
+# CONFIG_FEATURE_IPV6 is not set
+# CONFIG_ARPING is not set
+# CONFIG_FTPGET is not set
+# CONFIG_FTPPUT is not set
+# CONFIG_HOSTNAME is not set
+# CONFIG_HTTPD is not set
+# CONFIG_IFCONFIG is not set
+# CONFIG_IFUPDOWN is not set
+# CONFIG_INETD is not set
+# CONFIG_IP is not set
+# CONFIG_IPCALC is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_NAMEIF is not set
+# CONFIG_NC is not set
+# CONFIG_NETSTAT is not set
+# CONFIG_NSLOOKUP is not set
+# CONFIG_PING is not set
+# CONFIG_ROUTE is not set
+# CONFIG_TELNET is not set
+# CONFIG_TELNETD is not set
+# CONFIG_TFTP is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_VCONFIG is not set
+# CONFIG_WGET is not set
+
+#
+# udhcp Server/Client
+#
+# CONFIG_UDHCPD is not set
+# CONFIG_UDHCPC is not set
+
+#
+# Process Utilities
+#
+# CONFIG_FREE is not set
+# CONFIG_KILL is not set
+# CONFIG_PIDOF is not set
+# CONFIG_PS is not set
+# CONFIG_RENICE is not set
+# CONFIG_TOP is not set
+# CONFIG_UPTIME is not set
+
+#
+# Another Bourne-like Shell
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_MATH_SUPPORT is not set
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+# CONFIG_FEATURE_COMMAND_EDITING is not set
+# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
+# CONFIG_FEATURE_COMMAND_TAB_COMPLETION is not set
+# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_COMMAND_HISTORY=15
+# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
+# CONFIG_FEATURE_SH_FANCY_PROMPT is not set
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
+
+#
+# System Logging Utilities
+#
+# CONFIG_SYSLOGD is not set
+# CONFIG_LOGGER is not set
+
+#
+# Linux System Utilities
+#
+# CONFIG_DMESG is not set
+# CONFIG_FBSET is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+# CONFIG_FDISK is not set
+# CONFIG_FREERAMDISK is not set
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_GETOPT is not set
+# CONFIG_HEXDUMP is not set
+# CONFIG_HWCLOCK is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_MKSWAP is not set
+# CONFIG_MORE is not set
+CONFIG_PIVOT_ROOT=y
+# CONFIG_RDATE is not set
+# CONFIG_SWAPONOFF is not set
+CONFIG_MOUNT=y
+# CONFIG_NFSMOUNT is not set
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_MOUNT_FORCE=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/debian/config-static b/busybox/debian/config-static
new file mode 100644 (file)
index 0000000..0353546
--- /dev/null
@@ -0,0 +1,503 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_FEATURE_VERBOSE_USAGE=y
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_FEATURE_DEVFS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+CONFIG_STATIC=y
+CONFIG_LFS=y
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+CONFIG_AR=y
+CONFIG_FEATURE_AR_LONG_FILENAMES=y
+CONFIG_BUNZIP2=y
+CONFIG_CPIO=y
+CONFIG_DPKG=y
+CONFIG_DPKG_DEB=y
+# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
+CONFIG_FEATURE_DEB_TAR_GZ=y
+CONFIG_FEATURE_DEB_TAR_BZ2=y
+CONFIG_GUNZIP=y
+CONFIG_FEATURE_GUNZIP_UNCOMPRESS=y
+CONFIG_GZIP=y
+CONFIG_RPM2CPIO=y
+CONFIG_RPM=y
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_BZIP2=y
+CONFIG_FEATURE_TAR_EXCLUDE=y
+CONFIG_FEATURE_TAR_GZIP=y
+# CONFIG_FEATURE_TAR_COMPRESS is not set
+CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+CONFIG_UNCOMPRESS=y
+CONFIG_UNZIP=y
+
+#
+# Common options for cpio and tar
+#
+CONFIG_FEATURE_UNARCHIVE_TAPE=y
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAL=y
+CONFIG_CAT=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+CONFIG_CMP=y
+CONFIG_CP=y
+CONFIG_CUT=y
+CONFIG_DATE=y
+
+#
+# date (forced enabled for use with watch)
+#
+CONFIG_FEATURE_DATE_ISOFMT=y
+CONFIG_DD=y
+CONFIG_DF=y
+CONFIG_DIRNAME=y
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_EXPR=y
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=y
+CONFIG_ID=y
+# CONFIG_INSTALL is not set
+CONFIG_LENGTH=y
+CONFIG_LN=y
+CONFIG_LOGNAME=y
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+CONFIG_OD=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+CONFIG_SHA1SUM=y
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_SORT=y
+CONFIG_STTY=y
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TEST=y
+
+#
+# test (forced enabled for use with shell)
+#
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
+CONFIG_WATCH=y
+CONFIG_WC=y
+CONFIG_WHO=y
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls and more
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_LOADACM=y
+CONFIG_LOADFONT=y
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+CONFIG_SETKEYCODES=y
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+CONFIG_READLINK=y
+CONFIG_RUN_PARTS=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_MATH=y
+CONFIG_PATCH=y
+CONFIG_SED=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
+# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
+# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
+# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_INITRD=y
+# CONFIG_FEATURE_INIT_COREDUMPS is not set
+# CONFIG_FEATURE_EXTRA_QUIET is not set
+CONFIG_HALT=y
+CONFIG_POWEROFF=y
+CONFIG_REBOOT=y
+CONFIG_MESG=y
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_USE_BB_PWD_GRP=y
+CONFIG_ADDGROUP=y
+CONFIG_DELGROUP=y
+CONFIG_ADDUSER=y
+CONFIG_DELUSER=y
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_SU=y
+CONFIG_SULOGIN=y
+CONFIG_VLOCK=y
+
+#
+# Common options for adduser, deluser, login, su
+#
+CONFIG_FEATURE_SHADOWPASSWDS=y
+CONFIG_USE_BB_SHADOW=y
+
+#
+# Miscellaneous Utilities
+#
+CONFIG_ADJTIMEX=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
+CONFIG_CRONTAB=y
+CONFIG_DC=y
+# CONFIG_DEVFSD is not set
+CONFIG_LAST=y
+# CONFIG_HDPARM is not set
+CONFIG_MAKEDEVS=y
+CONFIG_MT=y
+CONFIG_STRINGS=y
+CONFIG_TIME=y
+CONFIG_WATCHDOG=y
+
+#
+# Linux Module Utilities
+#
+# CONFIG_DEPMOD is not set
+# CONFIG_INSMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_RMMOD is not set
+
+#
+# Networking Utilities
+#
+CONFIG_FEATURE_IPV6=y
+CONFIG_ARPING=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
+CONFIG_HOSTNAME=y
+CONFIG_HTTPD=y
+# CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY is not set
+CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
+# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
+# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set
+# CONFIG_FEATURE_HTTPD_SETUID is not set
+# CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES is not set
+# CONFIG_FEATURE_HTTPD_CGI is not set
+# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+CONFIG_FEATURE_IFCONFIG_SLIP=y
+CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
+CONFIG_FEATURE_IFCONFIG_HW=y
+CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
+CONFIG_IFUPDOWN=y
+# CONFIG_FEATURE_IFUPDOWN_IP is not set
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
+CONFIG_FEATURE_IFUPDOWN_IPV6=y
+CONFIG_FEATURE_IFUPDOWN_IPX=y
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
+# CONFIG_INETD is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+
+#
+#   address (forced enabled for ipaddr)
+#
+CONFIG_FEATURE_IP_LINK=y
+
+#
+#   link (forced enabled for iplink)
+#
+CONFIG_FEATURE_IP_ROUTE=y
+
+#
+#   route (forced enabled for iproute)
+#
+CONFIG_FEATURE_IP_TUNNEL=y
+
+#
+#   tunnel (forced enabled for iptunnel)
+#
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_NAMEIF=y
+CONFIG_NC=y
+CONFIG_NETSTAT=y
+CONFIG_NSLOOKUP=y
+CONFIG_PING=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING6=y
+CONFIG_ROUTE=y
+CONFIG_TELNET=y
+CONFIG_FEATURE_TELNET_TTYPE=y
+CONFIG_TELNETD=y
+# CONFIG_FEATURE_TELNETD_INETD is not set
+CONFIG_TFTP=y
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+CONFIG_FEATURE_TFTP_BLOCKSIZE=y
+CONFIG_FEATURE_TFTP_DEBUG=y
+CONFIG_TRACEROUTE=y
+CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_IP6_LITERAL=y
+
+#
+# udhcp Server/Client
+#
+CONFIG_UDHCPD=y
+CONFIG_UDHCPC=y
+CONFIG_DUMPLEASES=y
+CONFIG_FEATURE_UDHCP_SYSLOG=y
+CONFIG_FEATURE_UDHCP_DEBUG=y
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_PIDOF=y
+CONFIG_PS=y
+CONFIG_RENICE=y
+CONFIG_TOP=y
+FEATURE_CPU_USAGE_PERCENTAGE=y
+CONFIG_UPTIME=y
+
+#
+# Another Bourne-like Shell
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_ALIAS=y
+CONFIG_ASH_MATH_SUPPORT=y
+CONFIG_ASH_GETOPTS=y
+CONFIG_ASH_CMDCMD=y
+CONFIG_ASH_MAIL=y
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+CONFIG_FEATURE_COMMAND_EDITING=y
+CONFIG_FEATURE_COMMAND_SAVEHISTORY=y
+CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
+CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION=y
+CONFIG_FEATURE_COMMAND_HISTORY=15
+CONFIG_FEATURE_SH_STANDALONE_SHELL=y
+CONFIG_FEATURE_SH_FANCY_PROMPT=y
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+CONFIG_FEATURE_REMOTE_LOG=y
+CONFIG_FEATURE_IPC_SYSLOG=y
+CONFIG_LOGREAD=y
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+CONFIG_FEATURE_FBSET_READMODE=y
+CONFIG_FDFLUSH=y
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+CONFIG_FEATURE_FDISK_WRITABLE=y
+CONFIG_FEATURE_AIX_LABEL=y
+CONFIG_FEATURE_SGI_LABEL=y
+CONFIG_FEATURE_SUN_LABEL=y
+# CONFIG_FEATURE_OSF_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+CONFIG_FREERAMDISK=y
+CONFIG_FSCK_MINIX=y
+CONFIG_MKFS_MINIX=y
+
+#
+# Minix filesystem support
+#
+CONFIG_FEATURE_MINIX2=y
+CONFIG_GETOPT=y
+CONFIG_HEXDUMP=y
+CONFIG_HWCLOCK=y
+CONFIG_FEATURE_HWCLOCK_LONGOPTIONS=y
+CONFIG_LOSETUP=y
+CONFIG_MKSWAP=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_PIVOT_ROOT=y
+CONFIG_RDATE=y
+CONFIG_SWAPONOFF=y
+CONFIG_MOUNT=y
+CONFIG_NFSMOUNT=y
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_MOUNT_FORCE=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/debian/config-udeb b/busybox/debian/config-udeb
new file mode 100644 (file)
index 0000000..7359abc
--- /dev/null
@@ -0,0 +1,410 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+# CONFIG_FEATURE_VERBOSE_USAGE is not set
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_FEATURE_DEVFS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+CONFIG_LFS=y
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+CONFIG_AR=y
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+# CONFIG_BUNZIP2 is not set
+# CONFIG_CPIO is not set
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GUNZIP=y
+# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
+# CONFIG_GZIP is not set
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+CONFIG_TAR=y
+# CONFIG_FEATURE_TAR_CREATE is not set
+# CONFIG_FEATURE_TAR_BZIP2 is not set
+# CONFIG_FEATURE_TAR_EXCLUDE is not set
+CONFIG_FEATURE_TAR_GZIP=y
+# CONFIG_FEATURE_TAR_COMPRESS is not set
+CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+# CONFIG_UNCOMPRESS is not set
+# CONFIG_UNZIP is not set
+
+#
+# Common options for cpio and tar
+#
+# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+# CONFIG_CAL is not set
+CONFIG_CAT=y
+# CONFIG_CHGRP is not set
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+# CONFIG_CMP is not set
+CONFIG_CP=y
+CONFIG_CUT=y
+# CONFIG_DATE is not set
+# CONFIG_DD is not set
+CONFIG_DF=y
+CONFIG_DIRNAME=y
+# CONFIG_DOS2UNIX is not set
+# CONFIG_DU is not set
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_EXPR=y
+CONFIG_FALSE=y
+# CONFIG_FOLD is not set
+CONFIG_HEAD=y
+# CONFIG_FEATURE_FANCY_HEAD is not set
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+# CONFIG_INSTALL is not set
+# CONFIG_LENGTH is not set
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+CONFIG_LS=y
+# CONFIG_FEATURE_LS_FILETYPES is not set
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+# CONFIG_FEATURE_LS_RECURSIVE is not set
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+# CONFIG_FEATURE_LS_COLOR is not set
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+# CONFIG_MKFIFO is not set
+CONFIG_MKNOD=y
+CONFIG_MV=y
+# CONFIG_OD is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+# CONFIG_SHA1SUM is not set
+# CONFIG_SLEEP is not set
+CONFIG_SORT=y
+# CONFIG_STTY is not set
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+# CONFIG_FEATURE_FANCY_TAIL is not set
+# CONFIG_TEE is not set
+CONFIG_TEST=y
+
+#
+# test (forced enabled for use with shell)
+#
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_TRUE=y
+# CONFIG_TTY is not set
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+# CONFIG_USLEEP is not set
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+# CONFIG_WATCH is not set
+CONFIG_WC=y
+# CONFIG_WHO is not set
+# CONFIG_WHOAMI is not set
+# CONFIG_YES is not set
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls and more
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+
+#
+# Console Utilities
+#
+# CONFIG_CHVT is not set
+# CONFIG_CLEAR is not set
+# CONFIG_DEALLOCVT is not set
+# CONFIG_DUMPKMAP is not set
+# CONFIG_LOADACM is not set
+# CONFIG_LOADFONT is not set
+# CONFIG_LOADKMAP is not set
+# CONFIG_OPENVT is not set
+# CONFIG_RESET is not set
+# CONFIG_SETKEYCODES is not set
+
+#
+# Debian Utilities
+#
+# CONFIG_MKTEMP is not set
+# CONFIG_PIPE_PROGRESS is not set
+CONFIG_READLINK=y
+# CONFIG_RUN_PARTS is not set
+# CONFIG_START_STOP_DAEMON is not set
+# CONFIG_WHICH is not set
+
+#
+# Editors
+#
+# CONFIG_AWK is not set
+# CONFIG_PATCH is not set
+CONFIG_SED=y
+# CONFIG_VI is not set
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_GREP=y
+# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
+# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
+# CONFIG_FEATURE_GREP_CONTEXT is not set
+# CONFIG_XARGS is not set
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+# CONFIG_FEATURE_INITRD is not set
+# CONFIG_FEATURE_INIT_COREDUMPS is not set
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_HALT=y
+# CONFIG_POWEROFF is not set
+CONFIG_REBOOT=y
+# CONFIG_MESG is not set
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_USE_BB_PWD_GRP=y
+# CONFIG_ADDGROUP is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DELUSER is not set
+# CONFIG_GETTY is not set
+# CONFIG_LOGIN is not set
+# CONFIG_PASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+# CONFIG_CROND is not set
+# CONFIG_CRONTAB is not set
+# CONFIG_DC is not set
+# CONFIG_DEVFSD is not set
+# CONFIG_LAST is not set
+# CONFIG_HDPARM is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_MT is not set
+# CONFIG_STRINGS is not set
+# CONFIG_TIME is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Linux Module Utilities
+#
+# CONFIG_DEPMOD is not set
+# CONFIG_INSMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_RMMOD is not set
+
+#
+# Networking Utilities
+#
+# CONFIG_FEATURE_IPV6 is not set
+# CONFIG_ARPING is not set
+# CONFIG_FTPGET is not set
+# CONFIG_FTPPUT is not set
+# CONFIG_HOSTNAME is not set
+# CONFIG_HTTPD is not set
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+# CONFIG_FEATURE_IFCONFIG_HW is not set
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+# CONFIG_IFUPDOWN is not set
+# CONFIG_INETD is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+# CONFIG_FEATURE_IP_TUNNEL is not set
+# CONFIG_IPCALC is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_NAMEIF is not set
+# CONFIG_NC is not set
+# CONFIG_NETSTAT is not set
+# CONFIG_NSLOOKUP is not set
+# CONFIG_PING is not set
+CONFIG_ROUTE=y
+# CONFIG_TELNET is not set
+# CONFIG_TELNETD is not set
+# CONFIG_TFTP is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_IP6_LITERAL=y
+
+#
+# udhcp Server/Client
+#
+# CONFIG_UDHCPD is not set
+CONFIG_UDHCPC=y
+CONFIG_FEATURE_UDHCP_SYSLOG=y
+# CONFIG_FEATURE_UDHCP_DEBUG is not set
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_KILL=y
+# CONFIG_KILLALL is not set
+CONFIG_PIDOF=y
+CONFIG_PS=y
+# CONFIG_RENICE is not set
+# CONFIG_TOP is not set
+# CONFIG_UPTIME is not set
+
+#
+# Another Bourne-like Shell
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_ALIAS is not set
+CONFIG_ASH_MATH_SUPPORT=y
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+CONFIG_FEATURE_COMMAND_EDITING=y
+# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
+CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
+# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_COMMAND_HISTORY=15
+CONFIG_FEATURE_SH_STANDALONE_SHELL=y
+CONFIG_FEATURE_SH_FANCY_PROMPT=y
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+# CONFIG_FBSET is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+# CONFIG_FDISK is not set
+CONFIG_FREERAMDISK=y
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_GETOPT is not set
+# CONFIG_HEXDUMP is not set
+# CONFIG_HWCLOCK is not set
+# CONFIG_LOSETUP is not set
+CONFIG_MKSWAP=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_PIVOT_ROOT=y
+# CONFIG_RDATE is not set
+CONFIG_SWAPONOFF=y
+CONFIG_MOUNT=y
+# CONFIG_NFSMOUNT is not set
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_MOUNT_FORCE=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/debian/config-udeb-linux b/busybox/debian/config-udeb-linux
new file mode 100644 (file)
index 0000000..b33fb22
--- /dev/null
@@ -0,0 +1,419 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+# CONFIG_FEATURE_VERBOSE_USAGE is not set
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_FEATURE_DEVFS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+CONFIG_LFS=y
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+CONFIG_AR=y
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+# CONFIG_BUNZIP2 is not set
+# CONFIG_CPIO is not set
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GUNZIP=y
+# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
+# CONFIG_GZIP is not set
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+CONFIG_TAR=y
+# CONFIG_FEATURE_TAR_CREATE is not set
+# CONFIG_FEATURE_TAR_BZIP2 is not set
+# CONFIG_FEATURE_TAR_EXCLUDE is not set
+CONFIG_FEATURE_TAR_GZIP=y
+# CONFIG_FEATURE_TAR_COMPRESS is not set
+CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+# CONFIG_UNCOMPRESS is not set
+# CONFIG_UNZIP is not set
+
+#
+# Common options for cpio and tar
+#
+# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+# CONFIG_CAL is not set
+CONFIG_CAT=y
+# CONFIG_CHGRP is not set
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+# CONFIG_CMP is not set
+CONFIG_CP=y
+CONFIG_CUT=y
+# CONFIG_DATE is not set
+# CONFIG_DD is not set
+CONFIG_DF=y
+CONFIG_DIRNAME=y
+# CONFIG_DOS2UNIX is not set
+# CONFIG_DU is not set
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_EXPR=y
+CONFIG_FALSE=y
+# CONFIG_FOLD is not set
+CONFIG_HEAD=y
+# CONFIG_FEATURE_FANCY_HEAD is not set
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+# CONFIG_INSTALL is not set
+# CONFIG_LENGTH is not set
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+CONFIG_LS=y
+# CONFIG_FEATURE_LS_FILETYPES is not set
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+# CONFIG_FEATURE_LS_RECURSIVE is not set
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+# CONFIG_FEATURE_LS_COLOR is not set
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+# CONFIG_MKFIFO is not set
+CONFIG_MKNOD=y
+CONFIG_MV=y
+# CONFIG_OD is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+# CONFIG_SHA1SUM is not set
+# CONFIG_SLEEP is not set
+CONFIG_SORT=y
+# CONFIG_STTY is not set
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+# CONFIG_FEATURE_FANCY_TAIL is not set
+# CONFIG_TEE is not set
+CONFIG_TEST=y
+
+#
+# test (forced enabled for use with shell)
+#
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_TRUE=y
+# CONFIG_TTY is not set
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+# CONFIG_USLEEP is not set
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+# CONFIG_WATCH is not set
+CONFIG_WC=y
+# CONFIG_WHO is not set
+# CONFIG_WHOAMI is not set
+# CONFIG_YES is not set
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls and more
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+
+#
+# Console Utilities
+#
+# CONFIG_CHVT is not set
+# CONFIG_CLEAR is not set
+# CONFIG_DEALLOCVT is not set
+# CONFIG_DUMPKMAP is not set
+# CONFIG_LOADACM is not set
+# CONFIG_LOADFONT is not set
+# CONFIG_LOADKMAP is not set
+# CONFIG_OPENVT is not set
+# CONFIG_RESET is not set
+# CONFIG_SETKEYCODES is not set
+
+#
+# Debian Utilities
+#
+# CONFIG_MKTEMP is not set
+# CONFIG_PIPE_PROGRESS is not set
+CONFIG_READLINK=y
+# CONFIG_RUN_PARTS is not set
+# CONFIG_START_STOP_DAEMON is not set
+# CONFIG_WHICH is not set
+
+#
+# Editors
+#
+# CONFIG_AWK is not set
+# CONFIG_PATCH is not set
+CONFIG_SED=y
+# CONFIG_VI is not set
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_GREP=y
+# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
+# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
+# CONFIG_FEATURE_GREP_CONTEXT is not set
+# CONFIG_XARGS is not set
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+# CONFIG_FEATURE_INITRD is not set
+# CONFIG_FEATURE_INIT_COREDUMPS is not set
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_HALT=y
+# CONFIG_POWEROFF is not set
+CONFIG_REBOOT=y
+# CONFIG_MESG is not set
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_USE_BB_PWD_GRP=y
+# CONFIG_ADDGROUP is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DELUSER is not set
+# CONFIG_GETTY is not set
+# CONFIG_LOGIN is not set
+# CONFIG_PASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+# CONFIG_CROND is not set
+# CONFIG_CRONTAB is not set
+# CONFIG_DC is not set
+# CONFIG_DEVFSD is not set
+# CONFIG_LAST is not set
+# CONFIG_HDPARM is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_MT is not set
+# CONFIG_STRINGS is not set
+# CONFIG_TIME is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Linux Module Utilities
+#
+CONFIG_MODUTILS_OBJ=y
+CONFIG_DEPMOD=y
+CONFIG_INSMOD=y
+# CONFIG_FEATURE_2_2_MODULES is not set
+CONFIG_FEATURE_2_4_MODULES=y
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
+CONFIG_LSMOD=y
+CONFIG_FEATURE_QUERY_MODULE_INTERFACE=y
+CONFIG_MODPROBE=y
+# CONFIG_RMMOD is not set
+CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
+
+#
+# Networking Utilities
+#
+# CONFIG_FEATURE_IPV6 is not set
+# CONFIG_ARPING is not set
+# CONFIG_FTPGET is not set
+# CONFIG_FTPPUT is not set
+# CONFIG_HOSTNAME is not set
+# CONFIG_HTTPD is not set
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+# CONFIG_FEATURE_IFCONFIG_HW is not set
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+# CONFIG_IFUPDOWN is not set
+# CONFIG_INETD is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+# CONFIG_FEATURE_IP_TUNNEL is not set
+# CONFIG_IPCALC is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_NAMEIF is not set
+# CONFIG_NC is not set
+# CONFIG_NETSTAT is not set
+# CONFIG_NSLOOKUP is not set
+# CONFIG_PING is not set
+CONFIG_ROUTE=y
+# CONFIG_TELNET is not set
+# CONFIG_TELNETD is not set
+# CONFIG_TFTP is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_IP6_LITERAL=y
+
+#
+# udhcp Server/Client
+#
+# CONFIG_UDHCPD is not set
+CONFIG_UDHCPC=y
+CONFIG_FEATURE_UDHCP_SYSLOG=y
+# CONFIG_FEATURE_UDHCP_DEBUG is not set
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_KILL=y
+# CONFIG_KILLALL is not set
+CONFIG_PIDOF=y
+CONFIG_PS=y
+# CONFIG_RENICE is not set
+# CONFIG_TOP is not set
+# CONFIG_UPTIME is not set
+
+#
+# Another Bourne-like Shell
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_ALIAS is not set
+CONFIG_ASH_MATH_SUPPORT=y
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+CONFIG_FEATURE_COMMAND_EDITING=y
+# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
+CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
+# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_COMMAND_HISTORY=15
+CONFIG_FEATURE_SH_STANDALONE_SHELL=y
+CONFIG_FEATURE_SH_FANCY_PROMPT=y
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+# CONFIG_FBSET is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+# CONFIG_FDISK is not set
+CONFIG_FREERAMDISK=y
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_GETOPT is not set
+# CONFIG_HEXDUMP is not set
+# CONFIG_HWCLOCK is not set
+# CONFIG_LOSETUP is not set
+CONFIG_MKSWAP=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_PIVOT_ROOT=y
+# CONFIG_RDATE is not set
+CONFIG_SWAPONOFF=y
+CONFIG_MOUNT=y
+# CONFIG_NFSMOUNT is not set
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_MOUNT_FORCE=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/debian/control b/busybox/debian/control
new file mode 100644 (file)
index 0000000..391a02d
--- /dev/null
@@ -0,0 +1,88 @@
+Source: busybox-cvs
+Priority: optional
+Maintainer: Debian Install System Team <debian-boot@lists.debian.org>
+Uploaders: Erik Andersen <andersee@debian.org>, Bastian Blank <waldi@debian.org>, Tollef Fog Heen <tfheen@debian.org>, Glenn McGrath <bug1@debian.org>
+Build-Depends: debhelper (>= 4.0.0)
+Standards-Version: 3.6.0
+
+Package: busybox-cvs
+Architecture: any
+Depends: ${shlibs:Depends}
+Conflicts: busybox-cvs-static, busybox, busybox-static
+Replaces: busybox-cvs-static, busybox, busybox-static
+Section: utils
+Description: Tiny utilities for small and embedded systems
+ BusyBox combines tiny versions of many common UNIX utilities into a single
+ small executable. It provides minimalist replacements for the most common
+ utilities you would usually find on your desktop system (i.e., ls, cp, mv,
+ mount, tar, etc.).  The utilities in BusyBox generally have fewer options than
+ their full-featured GNU cousins; however, the options that are included
+ provide the expected functionality and behave very much like their GNU
+ counterparts.
+ .
+ This package installs the BusyBox binary but does not install symlinks
+ for any of the supported utilities.  You can use /bin/busybox --install
+ to install BusyBox to the current directory (you do not want to do this
+ in / on your Debian system!).
+
+Package: busybox-cvs-static
+Architecture: any
+Depends: ${shlibs:Depends}
+Conflicts: busybox-cvs, busybox-static, busybox
+Replaces: busybox-cvs, busybox-static, busybox
+Section: shells
+Description: Standalone rescue shell with tons of builtin utilities
+ BusyBox combines tiny versions of many common UNIX utilities into a single
+ small executable. It provides minimalist replacements for the most common
+ utilities you would usually find on your desktop system (i.e., ls, cp, mv,
+ mount, tar, etc.).  The utilities in BusyBox generally have fewer options than
+ their full-featured GNU cousins; however, the options that are included
+ provide the expected functionality and behave very much like their GNU
+ counterparts.
+ .
+ BusyBox-static provides you with a statically linked simple stand alone shell
+ that provides all the utilities available in BusyBox.  This package is
+ intended to be used as a rescue shell, in the event that you screw up your
+ system.  Invoke "busybox sh" and you have a standalone shell ready to save
+ your system from certain destruction.  Invoke "busybox", and it will list the
+ available builtin commands.
+
+Package: busybox-cvs-udeb
+Architecture: any
+Depends: ${shlibs:Depends}
+Section: debian-installer
+Conflicts: busybox-udeb
+Enhances: busybox-cvs-floppy-udeb
+Priority: extra
+Description: Tiny utilities for the debian-installer
+ BusyBox combines tiny versions of many common UNIX utilities into a single
+ small executable. It provides minimalist replacements for the most common
+ utilities you would usually find on your desktop system (i.e., ls, cp, mv,
+ mount, tar, etc.).  The utilities in BusyBox generally have fewer options than
+ their full-featured GNU cousins; however, the options that are included
+ provide the expected functionality and behave very much like their GNU
+ counterparts.
+ .
+ busybox-udeb is used by the debian-installer, so unless you are working
+ on the debian-installer, this package is not for you.  Installing this
+ on your Debian system is a very, very bad idea.  You have been warned.
+
+Package: busybox-cvs-floppy-udeb
+Architecture: i386
+Depends: ${shlibs:Depends}
+Section: debian-installer
+Conflicts: busybox-udeb
+Priority: extra
+Description: Tiny utilities for the debian-installer floppy images
+ BusyBox combines tiny versions of many common UNIX utilities into a single
+ small executable. It provides minimalist replacements for the most common
+ utilities you would usually find on your desktop system (i.e., ls, cp, mv,
+ mount, tar, etc.).  The utilities in BusyBox generally have fewer options than
+ their full-featured GNU cousins; however, the options that are included
+ provide the expected functionality and behave very much like their GNU
+ counterparts.
+ .
+ busybox-udeb is used by the debian-installer, so unless you are working
+ on the debian-installer, this package is not for you.  Installing this
+ on your Debian system is a very, very bad idea.  You have been warned.
+
diff --git a/busybox/debian/control-extract b/busybox/debian/control-extract
new file mode 100644 (file)
index 0000000..ed65f29
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+awk "BEGIN { i = 0; } { if (/^Package: $2/) i = 1; if (/^\s*\$/) i = 0; if (/$1:/ && i) print }" < debian/control | sed -e "s,^$1: ,,"
diff --git a/busybox/debian/copyright b/busybox/debian/copyright
new file mode 100644 (file)
index 0000000..cf9f368
--- /dev/null
@@ -0,0 +1,24 @@
+This package was debianized by Erik Andersen <andersee@debian.org> on
+Sun, 18 Jun 2000 23:31:02 -0600
+
+It was downloaded from ftp://ftp.busybox.net/busybox
+
+Copyright:
+
+   This package is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991.
+
+   This package is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this package; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.
+
+On Debian GNU/Linux systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
+
diff --git a/busybox/debian/rules b/busybox/debian/rules
new file mode 100755 (executable)
index 0000000..e77b4ef
--- /dev/null
@@ -0,0 +1,196 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)
+DEB_HOST_GNU_CPU ?= $(shell dpkg-architecture -qDEB_HOST_GNU_CPU)
+DEB_HOST_GNU_SYSTEM ?= $(shell dpkg-architecture -qDEB_HOST_GNU_SYSTEM)
+
+VERSION = $(shell dpkg-parsechangelog | grep ^Version: | cut -d ' ' -f 2)
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+       CONFIG_DEBUG = true
+endif
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+endif
+
+PACKAGE_PREFIX = busybox-cvs
+
+ARCH_FLOPPY_UDEB = $(shell sh debian/control-extract Architecture $(PACKAGE_PREFIX)-floppy-udeb)
+
+PACKAGES_DEB = $(PACKAGE_PREFIX) $(PACKAGE_PREFIX)-static
+PACKAGES_UDEB = $(PACKAGE_PREFIX)-udeb
+ifneq ($(filter $(DEB_HOST_ARCH),$(ARCH_FLOPPY_UDEB)),)
+PACKAGES_UDEB += $(PACKAGE_PREFIX)-floppy-udeb
+endif
+PACKAGES = $(PACKAGES_DEB) $(PACKAGES_UDEB)
+
+DEBHELPER_PACKAGES_DEB = $(patsubst %,-p%,$(PACKAGES_DEB))
+DEBHELPER_PACKAGES_UDEB = $(patsubst %,-p%,$(PACKAGES_UDEB))
+
+CONFIG = $(firstword $(wildcard ./debian/config-$(1)-$(DEB_HOST_GNU_SYSTEM)-$(DEB_HOST_GNU_CPU) ./debian/config-$(1)-$(DEB_HOST_GNU_SYSTEM) ./debian/config-$(1)))
+CONFIG_DEB = $(call CONFIG,deb)
+CONFIG_STATIC = $(call CONFIG,static)
+CONFIG_UDEB = $(call CONFIG,udeb)
+CONFIG_FLOPPY_UDEB = $(call CONFIG,floppy-udeb)
+
+configure: configure-stamp
+configure-stamp:
+       sh ./configure
+
+       touch $@
+
+build-arch: build-arch-deb-all build-arch-udeb-all
+build-arch-deb-all: build-arch-deb build-arch-static build-arch-doc
+build-arch-udeb-all: build-arch-udeb build-arch-floppy-udeb
+
+build-arch-deb: build-arch-deb-stamp
+build-arch-deb-stamp: configure-stamp
+       $(MAKE) clean
+
+       cp $(CONFIG_DEB) .config
+
+       $(MAKE) dep
+       $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
+
+       install -d install-$(PACKAGE_PREFIX)/bin
+       install busybox install-$(PACKAGE_PREFIX)/bin/busybox
+
+       touch $@
+
+build-arch-static: build-arch-static-stamp
+build-arch-static-stamp: configure-stamp
+       $(MAKE) clean
+
+       cp $(CONFIG_STATIC) .config
+
+       $(MAKE) dep
+       $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
+
+       install -d install-$(PACKAGE_PREFIX)-static/bin
+       install busybox install-$(PACKAGE_PREFIX)-static/bin/busybox
+
+       touch $@
+
+build-arch-udeb: build-arch-udeb-stamp
+build-arch-udeb-stamp: configure-stamp
+       $(MAKE) clean
+
+       cp $(CONFIG_UDEB) .config
+
+       $(MAKE) dep
+       $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
+
+       $(MAKE) PREFIX=$(CURDIR)/install-$(PACKAGE_PREFIX)-udeb install
+       # Remove init link, but init support is still compiled in to be
+       # used.
+       rm -f $(CURDIR)/install-$(PACKAGE_PREFIX)-udeb/sbin/init
+
+       touch $@
+
+build-arch-floppy-udeb: build-arch-floppy-udeb-stamp
+build-arch-floppy-udeb-stamp: configure-stamp
+ifneq ($(filter $(DEB_HOST_ARCH),$(ARCH_FLOPPY_UDEB)),)
+       $(MAKE) clean
+
+       cp $(CONFIG_FLOPPY_UDEB) .config
+
+       $(MAKE) dep
+       $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
+
+       $(MAKE) PREFIX=$(CURDIR)/install-$(PACKAGE_PREFIX)-floppy-udeb install
+endif
+
+       touch $@
+
+build-arch-doc: build-arch-doc-stamp
+build-arch-doc-stamp: configure-stamp
+       $(MAKE) docs/BusyBox.1
+
+       cp docs/BusyBox.1 busybox.1
+
+       touch $@
+
+build: build-arch
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-*-stamp configure-stamp debian/files~
+
+       -$(MAKE) distclean
+       -rm -rf busybox-deb busybox-static install* busybox.1
+
+       dh_clean
+
+install-deb: build-arch-deb-all
+       dh_testdir
+       dh_testroot
+       dh_clean -k $(DEBHELPER_PACKAGES_DEB)
+       dh_installdirs $(DEBHELPER_PACKAGES_DEB)
+
+       for i in $(PACKAGES_DEB); do \
+       ( \
+               cd install-$$i; \
+               find -type d -exec install -d $(CURDIR)/debian/$$i/{} \;; \
+               find \( -type f -o -type l \) -exec cp -a {} $(CURDIR)/debian/$$i/{} \;; \
+       ); \
+       done
+
+       install -m644 debian/$(PACKAGE_PREFIX)-static.override \
+               debian/$(PACKAGE_PREFIX)-static/usr/share/lintian/overrides/$(PACKAGE_PREFIX)-static
+
+install-udeb: build-arch-udeb-all
+       dh_testdir
+       dh_testroot
+       dh_clean -k $(DEBHELPER_PACKAGES_UDEB)
+       dh_installdirs $(DEBHELPER_PACKAGES_UDEB)
+
+       for i in $(PACKAGES_UDEB); do \
+       ( \
+               cd install-$$i; \
+               find -type d -exec install -d $(CURDIR)/debian/$$i/{} \;; \
+               find \( -type f -o -type l \) -exec cp -a {} $(CURDIR)/debian/$$i/{} \;; \
+       ); \
+       done
+
+binary-arch: binary-arch-deb binary-arch-udeb
+
+# Build architecture-dependent files here.
+binary-arch-deb: build-arch-deb-all install-deb
+       dh_testdir
+       dh_testroot
+       dh_installdocs $(DEBHELPER_PACKAGES_DEB)
+       dh_installman $(DEBHELPER_PACKAGES_DEB)
+       dh_installchangelogs Changelog $(DEBHELPER_PACKAGES_DEB)
+       dh_strip $(DEBHELPER_PACKAGES_DEB)
+       dh_link $(DEBHELPER_PACKAGES_DEB)
+       dh_compress $(DEBHELPER_PACKAGES_DEB)
+       dh_fixperms $(DEBHELPER_PACKAGES_DEB)
+       dh_installdeb $(DEBHELPER_PACKAGES_DEB)
+       dh_shlibdeps $(DEBHELPER_PACKAGES_DEB)
+       dh_gencontrol $(DEBHELPER_PACKAGES_DEB)
+       dh_md5sums $(DEBHELPER_PACKAGES_DEB)
+       dh_builddeb $(DEBHELPER_PACKAGES_DEB)
+
+# Build architecture-dependent files here.
+binary-arch-udeb: build-arch-udeb-all install-udeb
+       dh_testdir
+       dh_testroot
+       dh_strip $(DEBHELPER_PACKAGES_UDEB)
+       dh_link $(DEBHELPER_PACKAGES_UDEB)
+       dh_compress $(DEBHELPER_PACKAGES_UDEB)
+       dh_fixperms $(DEBHELPER_PACKAGES_UDEB)
+       dh_installdeb $(DEBHELPER_PACKAGES_UDEB)
+       dh_shlibdeps $(DEBHELPER_PACKAGES_UDEB)
+       dh_gencontrol $(DEBHELPER_PACKAGES_DEB)
+
+       $(foreach PACKAGE, $(PACKAGES_UDEB), \
+       dh_gencontrol -p$(PACKAGE) -- -fdebian/files~; \
+       dpkg-distaddfile $(PACKAGE)_$(VERSION)_$(DEB_HOST_ARCH).udeb debian-installer extra; \
+       dh_builddeb -p$(PACKAGE) --filename=$(PACKAGE)_$(VERSION)_$(DEB_HOST_ARCH).udeb; \
+       )
+
+binary: binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/busybox/debianutils/Config.in b/busybox/debianutils/Config.in
new file mode 100644 (file)
index 0000000..7cf7cad
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Debian Utilities"
+
+config CONFIG_MKTEMP
+       bool "mktemp"
+       default n
+       help
+         mktemp is used to create unique temporary files
+
+config CONFIG_PIPE_PROGRESS
+       bool "pipe_progress"
+       default n
+       help
+         Display a dot to indicate pipe activity.
+
+config CONFIG_READLINK
+       bool "readlink"
+       default n
+       help
+         This program reads a symbolic link and returns the name
+         of the file it points to
+
+config CONFIG_RUN_PARTS
+       bool "run-parts"
+       default n
+       help
+         run-parts is a utility designed to run all the scripts in a directory.
+
+         It is useful to set up a directory like cron.daily, where you need to
+         execute all the scripts in that directory.
+
+         In this implementation of run-parts some features (such as report mode)
+         are not implemented.
+
+         Unless you know that run-parts is used in some of your scripts
+         you can safely say N here.
+
+config CONFIG_START_STOP_DAEMON
+       bool "start-stop-daemon"
+       default y
+       help
+         start-stop-daemon is used to control the creation and
+         termination of system-level processes, usually the ones
+         started during the startup of the system.
+
+config CONFIG_WHICH
+       bool "which"
+       default n
+       help
+         which is used to find programs in your PATH and
+         print out their pathnames.
+
+endmenu
+
diff --git a/busybox/debianutils/Makefile b/busybox/debianutils/Makefile
new file mode 100644 (file)
index 0000000..10ec1cc
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/debianutils
+DEBIANUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/debianutils/Makefile.in b/busybox/debianutils/Makefile.in
new file mode 100644 (file)
index 0000000..3a20403
--- /dev/null
@@ -0,0 +1,41 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+DEBIANUTILS_AR:=debianutils.a
+ifndef $(DEBIANUTILS_DIR)
+DEBIANUTILS_DIR:=$(top_builddir)/debianutils/
+endif
+srcdir=$(top_srcdir)/debianutils
+
+DEBIANUTILS-y:=
+DEBIANUTILS-$(CONFIG_MKTEMP)           += mktemp.o
+DEBIANUTILS-$(CONFIG_PIPE_PROGRESS)    += pipe_progress.o
+DEBIANUTILS-$(CONFIG_READLINK)         += readlink.o
+DEBIANUTILS-$(CONFIG_RUN_PARTS)                += run_parts.o
+DEBIANUTILS-$(CONFIG_START_STOP_DAEMON)        += start_stop_daemon.o
+DEBIANUTILS-$(CONFIG_WHICH)            += which.o
+
+libraries-y+=$(DEBIANUTILS_DIR)$(DEBIANUTILS_AR)
+
+$(DEBIANUTILS_DIR)$(DEBIANUTILS_AR): $(patsubst %,$(DEBIANUTILS_DIR)%, $(DEBIANUTILS-y))
+       $(AR) -ro $@ $(patsubst %,$(DEBIANUTILS_DIR)%, $(DEBIANUTILS-y))
+
+$(DEBIANUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/debianutils/mktemp.c b/busybox/debianutils/mktemp.c
new file mode 100644 (file)
index 0000000..9fdf79b
--- /dev/null
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mktemp implementation for busybox
+ *
+ *
+ * Copyright (C) 2000 by Daniel Jacobowitz
+ * Written by Daniel Jacobowitz <dan@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int mktemp_main(int argc, char **argv)
+{
+       unsigned char dir_flag = 0;
+       int opt;
+
+       while ((opt = getopt(argc, argv, "qd")) != -1) {
+               if (opt == 'd') {
+                       dir_flag = 1;
+               }
+               else if (opt != 'q') {
+                       bb_show_usage();
+               }
+       }
+
+       if (optind + 1 != argc) {
+               bb_show_usage();
+       }
+
+       if (dir_flag) {
+               if (mkdtemp(argv[argc-1]) == NULL) {
+                       return EXIT_FAILURE;
+               }
+       } else {
+               if (mkstemp(argv[argc-1]) < 0) {
+                       return EXIT_FAILURE;
+               }
+       }
+
+       (void) puts(argv[argc-1]);
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/debianutils/pipe_progress.c b/busybox/debianutils/pipe_progress.c
new file mode 100644 (file)
index 0000000..ab05202
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Monitor a pipe with a simple progress display.
+ *
+ * Copyright (C) 2003 by Rob Landley <rob@landley.net>, Joey Hess
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "busybox.h"
+
+#define PIPE_PROGRESS_SIZE 4096
+
+/*     Read a block of data from stdin, write it to stdout.
+ *  Activity is indicated by a '.' to stderr
+ */
+extern int pipe_progress_main(int argc, char **argv)
+{
+       RESERVE_CONFIG_BUFFER(buf, PIPE_PROGRESS_SIZE);
+       time_t t = time(NULL);
+       size_t len;
+
+       while ((len = fread(buf, 1, PIPE_PROGRESS_SIZE, stdin)) > 0) {
+               time_t new_time = time(NULL);
+               if (new_time != t) {
+                       t = new_time;
+                       fputc('.', stderr);
+               }
+               fwrite(buf, len, 1, stdout);
+       }
+
+       fputc('\n', stderr);
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       RELEASE_CONFIG_BUFFER(buf);
+#endif
+       return 0;
+}
diff --git a/busybox/debianutils/readlink.c b/busybox/debianutils/readlink.c
new file mode 100644 (file)
index 0000000..d8d7e8c
--- /dev/null
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini readlink implementation for busybox
+ *
+ * Copyright (C) 2000,2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int readlink_main(int argc, char **argv)
+{
+       char *buf = NULL;
+
+       /* no options, no getopt */
+
+       if (argc != 2)
+               bb_show_usage();
+
+       buf = xreadlink(argv[1]);
+       if (!buf)
+               return EXIT_FAILURE;
+       puts(buf);
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       free(buf);
+#endif
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/debianutils/run_parts.c b/busybox/debianutils/run_parts.c
new file mode 100644 (file)
index 0000000..6205595
--- /dev/null
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini run-parts implementation for busybox
+ *
+ *
+ * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
+ *
+ * Based on the Debian run-parts program, version 1.15
+ *   Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
+ *   Copyright (C) 1996-1999 Guy Maor <maor@debian.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ */
+
+/* This is my first attempt to write a program in C (well, this is my first
+ * attempt to write a program! :-) . */
+
+/* This piece of code is heavily based on the original version of run-parts,
+ * taken from debian-utils. I've only removed the long options and a the
+ * report mode. As the original run-parts support only long options, I've
+ * broken compatibility because the BusyBox policy doesn't allow them.
+ * The supported options are:
+ * -t                  test. Print the name of the files to be executed, without
+ *                             execute them.
+ * -a ARG              argument. Pass ARG as an argument the program executed. It can
+ *                             be repeated to pass multiple arguments.
+ * -u MASK             umask. Set the umask of the program executed to MASK. */
+
+/* TODO
+ * done - convert calls to error in perror... and remove error()
+ * done - convert malloc/realloc to their x... counterparts
+ * done - remove catch_sigchld
+ * done - use bb's concat_path_file()
+ * done - declare run_parts_main() as extern and any other function as static?
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+
+static const struct option runparts_long_options[] = {
+       { "test",               0,              NULL,           't' },
+       { "umask",              1,              NULL,           'u' },
+       { "arg",                1,              NULL,           'a' },
+       { 0,                    0,              0,                      0 }
+};
+
+extern char **environ;
+
+/* run_parts_main */
+/* Process options */
+int run_parts_main(int argc, char **argv)
+{
+       char **args = xmalloc(2 * sizeof(char *));
+       unsigned char test_mode = 0;
+       unsigned short argcount = 1;
+       int opt;
+
+       umask(022);
+
+       while ((opt = getopt_long (argc, argv, "tu:a:",
+                                       runparts_long_options, NULL)) > 0)
+       {
+               switch (opt) {
+                       /* Enable test mode */
+                       case 't':
+                               test_mode++;
+                               break;
+                       /* Set the umask of the programs executed */
+                       case 'u':
+                               /* Check and set the umask of the program executed. As stated in the original
+                                * run-parts, the octal conversion in libc is not foolproof; it will take the
+                                * 8 and 9 digits under some circumstances. We'll just have to live with it.
+                                */
+                               umask(bb_xgetlarg(optarg, 8, 0, 07777));
+                               break;
+                       /* Pass an argument to the programs */
+                       case 'a':
+                               /* Add an argument to the commands that we will call.
+                                * Called once for every argument. */
+                               args = xrealloc(args, (argcount + 2) * (sizeof(char *)));
+                               args[argcount++] = optarg;
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       /* We require exactly one argument: the directory name */
+       if (optind != (argc - 1)) {
+               bb_show_usage();
+       }
+
+       args[0] = argv[optind];
+       args[argcount] = 0;
+
+       return(run_parts(args, test_mode, environ));
+}
diff --git a/busybox/debianutils/start_stop_daemon.c b/busybox/debianutils/start_stop_daemon.c
new file mode 100644 (file)
index 0000000..e15944c
--- /dev/null
@@ -0,0 +1,296 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini start-stop-daemon implementation(s) for busybox
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * public domain.
+ * Adapted for busybox David Kimdon <dwhedon@gordian.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "busybox.h"
+#include "pwd_.h"
+
+static int signal_nr = 15;
+static int user_id = -1;
+static int quiet = 0;
+static char *userspec = NULL;
+static char *cmdname = NULL;
+static char *execname = NULL;
+static char *pidfile = NULL;
+
+struct pid_list {
+       struct pid_list *next;
+       pid_t pid;
+};
+
+static struct pid_list *found = NULL;
+
+static inline void
+push(pid_t pid)
+{
+       struct pid_list *p;
+
+       p = xmalloc(sizeof(*p));
+       p->next = found;
+       p->pid = pid;
+       found = p;
+}
+
+static int
+pid_is_exec(pid_t pid, const char *name)
+{
+       char buf[32];
+       struct stat sb, exec_stat;
+
+       if (name && stat(name, &exec_stat))
+               bb_perror_msg_and_die("stat %s", name);
+
+       sprintf(buf, "/proc/%d/exe", pid);
+       if (stat(buf, &sb) != 0)
+               return 0;
+       return (sb.st_dev == exec_stat.st_dev && sb.st_ino == exec_stat.st_ino);
+}
+
+static int
+pid_is_user(int pid, int uid)
+{
+       struct stat sb;
+       char buf[32];
+
+       sprintf(buf, "/proc/%d", pid);
+       if (stat(buf, &sb) != 0)
+               return 0;
+       return (sb.st_uid == uid);
+}
+
+static int
+pid_is_cmd(pid_t pid, const char *name)
+{
+       char buf[32];
+       FILE *f;
+       int c;
+
+       sprintf(buf, "/proc/%d/stat", pid);
+       f = fopen(buf, "r");
+       if (!f)
+               return 0;
+       while ((c = getc(f)) != EOF && c != '(')
+               ;
+       if (c != '(') {
+               fclose(f);
+               return 0;
+       }
+       /* this hopefully handles command names containing ')' */
+       while ((c = getc(f)) != EOF && c == *name)
+               name++;
+       fclose(f);
+       return (c == ')' && *name == '\0');
+}
+
+
+static void
+check(int pid)
+{
+       if (execname && !pid_is_exec(pid, execname)) {
+               return;
+       }
+       if (userspec && !pid_is_user(pid, user_id)) {
+               return;
+       }
+       if (cmdname && !pid_is_cmd(pid, cmdname)) {
+               return;
+       }
+       push(pid);
+}
+
+
+static void
+do_pidfile(void)
+{
+       FILE *f;
+       pid_t pid;
+
+       f = fopen(pidfile, "r");
+       if (f) {
+               if (fscanf(f, "%d", &pid) == 1)
+                       check(pid);
+               fclose(f);
+       } else if (errno != ENOENT)
+               bb_perror_msg_and_die("open pidfile %s", pidfile);
+
+}
+
+static void
+do_procinit(void)
+{
+       DIR *procdir;
+       struct dirent *entry;
+       int foundany, pid;
+
+       if (pidfile) {
+               do_pidfile();
+               return;
+       }
+
+       procdir = opendir("/proc");
+       if (!procdir)
+               bb_perror_msg_and_die ("opendir /proc");
+
+       foundany = 0;
+       while ((entry = readdir(procdir)) != NULL) {
+               if (sscanf(entry->d_name, "%d", &pid) != 1)
+                       continue;
+               foundany++;
+               check(pid);
+       }
+       closedir(procdir);
+       if (!foundany)
+               bb_error_msg_and_die ("nothing in /proc - not mounted?");
+}
+
+
+static void
+do_stop(void)
+{
+       char what[1024];
+       struct pid_list *p;
+       int killed = 0;
+
+       do_procinit();
+
+       if (cmdname)
+               strcpy(what, cmdname);
+       else if (execname)
+               strcpy(what, execname);
+       else if (pidfile)
+               sprintf(what, "process in pidfile `%.200s'", pidfile);
+       else if (userspec)
+               sprintf(what, "process(es) owned by `%s'", userspec);
+       else
+               bb_error_msg_and_die ("internal error, please report");
+
+       if (!found) {
+               if (!quiet)
+                       printf("no %s found; none killed.\n", what);
+               return;
+       }
+       for (p = found; p; p = p->next) {
+               if (kill(p->pid, signal_nr) == 0) {
+                       p->pid = -p->pid;
+                       killed++;
+               } else {
+                       bb_perror_msg("warning: failed to kill %d", p->pid);
+               }
+       }
+       if (!quiet && killed) {
+               printf("stopped %s (pid", what);
+               for (p = found; p; p = p->next)
+                       if(p->pid < 0)
+                               printf(" %d", -p->pid);
+               printf(").\n");
+       }
+}
+
+
+static const struct option ssd_long_options[] = {
+       { "stop",                               0,              NULL,           'K' },
+       { "start",                              0,              NULL,           'S' },
+       { "background",                 0,              NULL,           'b' },
+       { "quiet",                              0,              NULL,           'q' },
+       { "make-pidfile",               0,              NULL,           'm' },
+       { "startas",                    1,              NULL,           'a' },
+       { "name",                               1,              NULL,           'n' },
+       { "signal",                             1,              NULL,           's' },
+       { "user",                               1,              NULL,           'u' },
+       { "exec",                               1,              NULL,           'x' },
+       { "pidfile",                    1,              NULL,           'p' },
+       { 0,                    0,              0,                      0 }
+};
+
+#define SSD_CTX_STOP           1
+#define SSD_CTX_START          2
+#define SSD_OPT_BACKGROUND     4
+#define SSD_OPT_QUIET          8
+#define SSD_OPT_MAKEPID                16
+
+int
+start_stop_daemon_main(int argc, char **argv)
+{
+       unsigned long opt;
+       char *signame = NULL;
+       char *startas = NULL;
+
+       bb_applet_long_options = ssd_long_options;
+
+       bb_opt_complementaly = "K~S:S~K";
+       opt = bb_getopt_ulflags(argc, argv, "KSbqma:n:s:u:x:p:",
+                       &startas, &cmdname, &signame, &userspec, &execname, &pidfile);
+
+       /* Check one and only one context option was given */
+       if ((opt & 0x80000000UL) || (opt & (SSD_CTX_STOP | SSD_CTX_START)) == 0) {
+               bb_show_usage();
+       }
+
+       if (signame) {
+               signal_nr = bb_xgetlarg(signame, 10, 0, NSIG);
+       }
+
+       if (!execname && !pidfile && !userspec && !cmdname)
+               bb_error_msg_and_die ("need at least one of -x, -p, -u, or -n");
+
+       if (!startas)
+               startas = execname;
+
+       if ((opt & SSD_CTX_START) && !startas)
+               bb_error_msg_and_die ("-S needs -x or -a");
+
+       if ((opt & SSD_OPT_MAKEPID) && pidfile == NULL)
+               bb_error_msg_and_die ("-m needs -p");
+
+       argc -= optind;
+       argv += optind;
+
+       if (userspec && sscanf(userspec, "%d", &user_id) != 1)
+               user_id = my_getpwnam(userspec);
+
+       if (opt & SSD_CTX_STOP) {
+               do_stop();
+               return EXIT_SUCCESS;
+       }
+
+       do_procinit();
+
+       if (found) {
+               if (!quiet)
+                       printf("%s already running.\n%d\n", execname ,found->pid);
+               return EXIT_SUCCESS;
+       }
+       *--argv = startas;
+       if (opt & SSD_OPT_BACKGROUND) {
+               if (daemon(0, 0) == -1)
+                       bb_perror_msg_and_die ("unable to fork");
+               setsid();
+       }
+       if (opt & SSD_OPT_MAKEPID) {
+               /* user wants _us_ to make the pidfile */
+               FILE *pidf = fopen(pidfile, "w");
+               pid_t pidt = getpid();
+               if (pidf == NULL)
+                       bb_perror_msg_and_die("Unable to write pidfile '%s'", pidfile);
+               fprintf(pidf, "%d\n", pidt);
+               fclose(pidf);
+       }
+       execv(startas, argv);
+       bb_perror_msg_and_die ("unable to start %s", startas);
+}
diff --git a/busybox/debianutils/which.c b/busybox/debianutils/which.c
new file mode 100644 (file)
index 0000000..999dded
--- /dev/null
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Which implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Based on which from debianutils
+ */
+
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "busybox.h"
+
+
+extern int which_main(int argc, char **argv)
+{
+       char *path_list;
+       int i, count=1, status = EXIT_SUCCESS;
+
+       if (argc <= 1 || **(argv + 1) == '-') {
+               bb_show_usage();
+       }
+       argc--;
+
+       path_list = getenv("PATH");
+       if (path_list != NULL) {
+               for (i=strlen(path_list); i > 0; i--) {
+                       if (path_list[i]==':') {
+                               path_list[i]=0;
+                               count++;
+                       }
+               }
+       } else {
+               path_list = "/bin\0/sbin\0/usr/bin\0/usr/sbin\0/usr/local/bin";
+               count = 5;
+       }
+
+       while (argc-- > 0) {
+               char *buf;
+               char *path_n;
+               char found = 0;
+               argv++;
+
+               /*
+                * Check if we were given the full path, first.
+                * Otherwise see if the file exists in our $PATH.
+                */
+               path_n = path_list;
+               buf = *argv;
+               if (access(buf, X_OK) == 0) {
+                       found = 1;
+               } else {
+                       for (i = 0; i < count; i++) {
+                               buf = concat_path_file(path_n, *argv);
+                               if (access(buf, X_OK) == 0) {
+                                       found = 1;
+                                       break;
+                               }
+                               free(buf);
+                               path_n += (strlen(path_n) + 1);
+                       }
+               }
+               if (found) {
+                       puts(buf);
+               } else {
+                       status = EXIT_FAILURE;
+               }
+       }
+       return status;
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/docs/.cvsignore b/busybox/docs/.cvsignore
new file mode 100644 (file)
index 0000000..ec9e94b
--- /dev/null
@@ -0,0 +1,8 @@
+BusyBox.txt
+BusyBox.1
+BusyBox.html
+busybox.txt
+busybox.ps
+busybox.pdf
+busybox
+busybox.pod
diff --git a/busybox/docs/autodocifier.pl b/busybox/docs/autodocifier.pl
new file mode 100755 (executable)
index 0000000..eee67cf
--- /dev/null
@@ -0,0 +1,274 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Long;
+
+# collect lines continued with a '\' into an array
+sub continuation {
+       my $fh = shift;
+       my @line;
+
+       while (<$fh>) {
+               my $s = $_;
+               $s =~ s/\\\s*$//;
+               #$s =~ s/#.*$//;
+               push @line, $s;
+               last unless (/\\\s*$/);
+       }
+       return @line;
+}
+
+# regex && eval away unwanted strings from documentation
+sub beautify {
+       my $text = shift;
+       $text =~ s/USAGE_NOT\w+\(.*?"\s*\)//sxg;
+       $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
+       $text =~ s/"\s*"//sg;
+       my @line = split("\n", $text);
+       $text = join('',
+               map {
+                       s/^\s*"//;
+                       s/"\s*$//;
+                       s/%/%%/g;
+                       s/\$/\\\$/g;
+                       eval qq[ sprintf(qq{$_}) ]
+               } @line
+       );
+       return $text;
+}
+
+# generate POD for an applet
+sub pod_for_usage {
+       my $name  = shift;
+       my $usage = shift;
+
+       # Sigh.  Fixup the known odd-name applets.
+       $name =~ s/dpkg_deb/dpkg-deb/g;
+       $name =~ s/fsck_minix/fsck.minix/g;
+       $name =~ s/mkfs_minix/mkfs.minix/g;
+       $name =~ s/run_parts/run-parts/g;
+       $name =~ s/start_stop_daemon/start-stop-daemon/g;
+
+       # make options bold
+       my $trivial = $usage->{trivial};
+       $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
+       my @f0 =
+               map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
+               split("\n", $usage->{full});
+
+       # add "\n" prior to certain lines to make indented
+       # lines look right
+       my @f1;
+       my $len = @f0;
+       for (my $i = 0; $i < $len; $i++) {
+               push @f1, $f0[$i];
+               if (($i+1) != $len && $f0[$i] !~ /^\s/ && $f0[$i+1] =~ /^\s/) {
+                       next if ($f0[$i] =~ /^$/);
+                       push(@f1, "") unless ($f0[$i+1] =~ /^\s*$/s);
+               }
+       }
+       my $full = join("\n", @f1);
+
+       # prepare notes if they exist
+       my $notes = (defined $usage->{notes})
+               ? "$usage->{notes}\n\n"
+               : "";
+
+       # prepare examples if they exist
+       my $example = (defined $usage->{example})
+               ?
+                       "Example:\n\n" .
+                       join ("\n",
+                       map  { "\t$_" }
+                       split("\n", $usage->{example})) . "\n\n"
+               : "";
+
+       return
+               "=item B<$name>".
+               "\n\n$name $trivial\n\n".
+               "$full\n\n"   .
+               "$notes"  .
+               "$example" .
+               "-------------------------------".
+               "\n\n"
+       ;
+}
+
+# the keys are applet names, and
+# the values will contain hashrefs of the form:
+#
+# {
+#     trivial => "...",
+#     full    => "...",
+#     notes   => "...",
+#     example => "...",
+# }
+my %docs;
+
+
+# get command-line options
+
+my %opt;
+
+GetOptions(
+       \%opt,
+       "help|h",
+       "pod|p",
+       "verbose|v",
+);
+
+if (defined $opt{help}) {
+       print
+               "$0 [OPTION]... [FILE]...\n",
+               "\t--help\n",
+               "\t--pod\n",
+               "\t--verbose\n",
+       ;
+       exit 1;
+}
+
+
+# collect documenation into %docs
+
+foreach (@ARGV) {
+       open(USAGE, $_) || die("$0: $_: $!");
+       my $fh = *USAGE;
+       my ($applet, $type, @line);
+       while (<$fh>) {
+               if (/^#define (\w+)_(\w+)_usage/) {
+                       $applet = $1;
+                       $type   = $2;
+                       @line   = continuation($fh);
+                       my $doc = $docs{$applet} ||= { };
+                       my $text      = join("\n", @line);
+                       $doc->{$type} = beautify($text);
+               }
+       }
+}
+
+
+# generate structured documentation
+
+my $generator = \&pod_for_usage;
+foreach my $applet (sort keys %docs) {
+       print $generator->($applet, $docs{$applet});
+}
+
+exit 0;
+
+__END__
+
+=head1 NAME
+
+autodocifier.pl - generate docs for busybox based on usage.h
+
+=head1 SYNOPSIS
+
+autodocifier.pl [OPTION]... [FILE]...
+
+Example:
+
+    ( cat docs/busybox_header.pod; \
+      docs/autodocifier.pl usage.h; \
+      cat docs/busybox_footer.pod ) > docs/busybox.pod
+
+=head1 DESCRIPTION
+
+The purpose of this script is to automagically generate
+documentation for busybox using its usage.h as the original source
+for content.  It used to be that same content has to be duplicated
+in 3 places in slightly different formats -- F<usage.h>,
+F<docs/busybox.pod>.  This was tedious and error-prone, so it was
+decided that F<usage.h> would contain all the text in a
+machine-readable form, and scripts could be used to transform this
+text into other forms if necessary.
+
+F<autodocifier.pl> is one such script.  It is based on a script by
+Erik Andersen <andersen@codepoet.org> which was in turn based on a
+script by Mark Whitley <markw@codepoet.org>
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+This displays the help message.
+
+=item B<--pod>
+
+Generate POD (this is the default)
+
+=item B<--verbose>
+
+Be verbose (not implemented)
+
+=back
+
+=head1 FORMAT
+
+The following is an example of some data this script might parse.
+
+    #define length_trivial_usage \
+            "STRING"
+    #define length_full_usage \
+            "Prints out the length of the specified STRING."
+    #define length_example_usage \
+            "$ length Hello\n" \
+            "5\n"
+
+Each entry is a cpp macro that defines a string.  The macros are
+named systematically in the form:
+
+    $name_$type_usage
+
+$name is the name of the applet.  $type can be "trivial", "full", "notes",
+or "example".  Every documentation macro must end with "_usage".
+
+The definition of the types is as follows:
+
+=over 4
+
+=item B<trivial>
+
+This should be a brief, one-line description of parameters that
+the command expects.  This will be displayed when B<-h> is issued to
+a command.  I<REQUIRED>
+
+=item B<full>
+
+This should contain descriptions of each option.  This will also
+be displayed along with the trivial help if CONFIG_FEATURE_TRIVIAL_HELP
+is disabled.  I<REQUIRED>
+
+=item B<notes>
+
+This is documentation that is intended to go in the POD or SGML, but
+not be printed when a B<-h> is given to a command.  To see an example
+of notes being used, see init_notes_usage in F<usage.h>.  I<OPTIONAL>
+
+=item B<example>
+
+This should be an example of how the command is actually used.
+This will not be printed when a B<-h> is given to a command -- it
+will only be included in the POD or SGML documentation.  I<OPTIONAL>
+
+=back
+
+=head1 FILES
+
+F<usage.h>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001 John BEPPU.  All rights reserved.  This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=head1 AUTHOR
+
+John BEPPU <b@ax9.org>
+
+=cut
+
+# $Id: autodocifier.pl,v 1.26 2004/04/06 15:26:25 andersen Exp $
diff --git a/busybox/docs/busybox.net/.cvsignore b/busybox/docs/busybox.net/.cvsignore
new file mode 100644 (file)
index 0000000..393b002
--- /dev/null
@@ -0,0 +1,2 @@
+BusyBox.html
+busybox.tar.gz
diff --git a/busybox/docs/busybox.net/FAQ.html b/busybox/docs/busybox.net/FAQ.html
new file mode 100644 (file)
index 0000000..a9324ae
--- /dev/null
@@ -0,0 +1,324 @@
+<!--#include file="header.html" -->
+
+
+<h3>Frequently Asked Questions</h3>
+
+This is a collection of some of the more frequently asked questions
+about BusyBox.  Some of the questions even have answers. If you
+have additions to this FAQ document, we would love to add them,
+
+<ol>
+<li><a href="#kernel">Which Linux kernel versions are supported?</a>
+<li><a href="#arch">Which architectures does BusyBox run on?</a>
+<li><a href="#libc">Which C libraries are supported?</a>
+<li><a href="#commercial">Can I include BusyBox as part of the software on my device?</a>
+<li><a href="#bugs">I think I found a bug in BusyBox!  What should I do?!</a>
+<li><a href="#job_control">Why do I keep getting "sh: can't access tty; job control
+       turned off" errors?  Why doesn't Control-C work within my shell?</a>
+<li><a href="#demanding">I demand that you to add &lt;favorite feature&gt; right now!   How come
+       you don't answer all my questions on the mailing list instantly?  I demand
+       that you help me with all of my problems <em>Right Now</em>!</a>
+<li><a href="#getting_started">How can I get started using BusyBox?</a>
+<li><a href="#helpme">I need help with BusyBox!  What should I do?</a>
+<li><a href="#contracts">I need you to add &lt;favorite feature&gt;!  Are the BusyBox developers willing to
+       be paid in order to fix bugs or add in &lt;favorite feature&gt;?  Are you willing to provide
+       support contracts?</a>
+<li><a href="#support">I think you guys are great and I want to help support your work!</a>
+
+
+</ol>
+
+
+<hr />
+<p>
+<h2><a name="kernel">Which Linux kernel versions are supported?</a></h2>
+<p>
+
+
+    Full functionality requires Linux 2.2.x or better.  A large fraction of the
+    code should run on just about anything.  While the current code is fairly
+    Linux specific, it should be fairly easy to port the majority of the code
+    to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you
+    are into that sort of thing).
+
+
+<hr />
+<p>
+<h2><a name="arch">Which architectures does BusyBox run on?</a></h2>
+<p>
+
+
+    BusyBox in general will build on any architecture supported by gcc.
+    Kernel module loading for 2.2 and 2.4 Linux kernels is currently
+    limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC,
+    S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels.
+
+    With 2.6.x kernels, module loading support should work on all architectures.
+
+
+<hr />
+<p>
+<h2><a name="libc">Which C libraries are supported?</a></h2>
+<p>
+
+
+    uClibc and glibc are supported.  People have been looking at newlib and
+    dietlibc, but they are currently considered unsupported, untested, or
+    worse.  Linux-libc5 is no longer supported.  If you require a small C
+    library, you should probably use uClibc.
+
+
+<hr />
+<p>
+<h2><a name="commercial">Can I include BusyBox as part of the software on my device?</h2>
+
+    Yes.  As long as you <a href="http://busybox.net/license.html">fully comply
+    with the generous terms of the GPL BusyBox license</a> you can ship BusyBox
+    as part of the software on your device.
+
+    <a href="#support">Please consider sharing some of the money you make.</a>
+
+
+<hr />
+<p>
+<h2><a name="bugs">I think I found a bug in BusyBox!  What should I do?</h2>
+<p>
+
+    If you find a problem with BusyBox, please submit a detailed bug report to
+    the BusyBox mailing list at <a href="mailto:busybox@mail.busybox.net">
+    busybox@mail.busybox.net</a>.  Please do not send private email to Erik
+    (the maintainer of BusyBox) asking for private help unless you are planning
+    on paying for consulting services.  When we answer questions on the BusyBox
+    mailing list, it helps everyone, while private answers help only you...
+
+    <p>
+
+    If you find bugs, please submit a detailed bug report to the BusyBox mailing
+    list at busybox@mail.busybox.net.  A well-written bug report should include a
+    transcript of a shell session that demonstrates the bad behavior and enables
+    anyone else to duplicate the bug on their own machine. The following is such
+    an example:
+
+<pre>
+       To: busybox@mail.busybox.net
+       From: diligent@testing.linux.org
+       Subject: /bin/date doesn't work
+
+       Package: BusyBox
+       Version: 1.00
+
+       When I execute BusyBox 'date' it produces unexpected results.
+       With GNU date I get the following output:
+
+               $ date
+               Fri Oct  8 14:19:41 MDT 2004
+
+       But when I use BusyBox date I get this instead:
+
+               $ date
+               illegal instruction
+
+       I am using Debian unstable, kernel version 2.4.27 on a x86 system,
+       and the latest uClibc from CVS.  Thanks for the wonderful program!
+
+         -Diligent
+</pre>
+
+    Note the careful description and use of examples showing not only what BusyBox
+    does, but also a counter example showing what an equivalent GNU app does.  Bug
+    reports lacking proper detail may never be fixed...  Thanks for understanding.
+
+<hr />
+<p>
+<h2><a name="job_control">Why do I keep getting "sh: can't access tty; job control
+       turned off" errors?  Why doesn't Control-C work within my shell?</a></h2>
+<p>
+
+    Job control will be turned off since your shell can not obtain a controlling
+    terminal.  This typically happens when you run your shell on /dev/console.
+    The kernel will not provide a controlling terminal on the /dev/console
+    device.  Your should run your shell on a normal tty such as tty1 or ttyS0
+    and everything will work perfectly.  If you <em>REALLY</em> want your shell
+    to run on /dev/console, then you can hack your kernel (if you are into that
+    sortof thing) by changing drivers/char/tty_io.c to change the lines where
+    it sets "noctty = 1;" to instead set it to "0".  I recommend you instead
+    run your shell on a real console...
+
+
+<hr />
+<p>
+<h2><a name="getting_started">How can I get started using BusyBox?</a></h2>
+<p>
+
+    An easy method to build your own basic BusyBox based system, is to
+    follow these simple steps:
+    <ul>
+       <li> Point your web browser <a href="/cgi-bin/cvsweb/buildroot/">here</a>
+       <li> Click on "Download tarball"
+       <li> Unpack the tarball on your Linux system somewhere
+       <li> run 'make' and configure things to taste.
+       <li> run 'unset CC'.   Some Linux systems (i.e. Gentoo) set 'CC'
+           in the system environment which messes up cross compiles.
+       <li> run 'make'
+       <li> go have lunch, drink a pop, call a friend, play a video game, etc
+               till it finishes downloading software and compiling things.
+       <li> You should now have a shiny new BusyBox based system.
+    </ul>
+
+
+<hr />
+<p>
+<h2><a name="demanding">I demand that you to add &lt;favorite feature&gt; right now!   How come
+       you don't answer all my questions on the mailing list instantly?  I demand
+       that you help me with all of my problems <em>Right Now</em>!</a></h2>
+<p>
+
+    You have not paid us a single cent and yet you still have the product of
+    many years of our work.  We are not your slaves!  We work on BusyBox
+    because we find it useful and interesting.  If you go off flaming us, we
+    will ignore you.
+
+
+<hr />
+<p>
+<h2><a name="helpme">I need help with BusyBox!  What should I do?</a></h2>
+<p>
+
+    If you find that you need help with BusyBox, you can ask for help on the
+    BusyBox mailing list at busybox@mail.busybox.net.  In addition to the BusyBox
+    mailing list, Erik (andersee), Manuel (mjn3) and others are known to hang out
+    on the uClibc IRC channel: #uclibc on irc.freenode.net.
+
+    <p>
+
+    <b>Please do not send private email to Erik, Manuel, or the other BusyBox
+    contributors asking for private help unless you are planning on paying for
+    consulting services.</b>
+
+    <p>
+
+    When we answer questions on the BusyBox mailing list, it helps everyone
+    since people with similar problems in the future will be able to get help
+    by searching the mailing list archives.  Private help is reserved as a paid
+    service.  If you need to use private communication, or if you are serious
+    about getting timely assistance with BusyBox, you should seriously consider
+    paying for consulting services.
+
+    <p>
+
+
+
+<hr />
+<p>
+<h2><a name="contracts">I need you to add &lt;favorite feature&gt;!  Are the BusyBox
+       developers willing to be paid in order to fix bugs or add in &lt;favorite feature&gt;?
+       Are you willing to provide support contracts?</a></h2>
+<p>
+
+    Sure!  Now you have our attention!  What you should do is contact <a
+       href="mailto:andersen@codepoet.org">Erik Andersen</a> of <a
+       href="http://codepoet-consulting.com/">CodePoet Consulting</a> to bid
+    on your project.  If Erik is too busy to personally add your feature, there
+    are many other active BusyBox contributors who will almost certainly be able
+    to help you out.  Erik can contact them privatly, and may even let you to
+    post your request for services on the mailing list.
+
+
+<hr />
+<p>
+<h2><a name="support">I think you guys are great and I want to help support your work!</a></h2>
+<p>
+
+    Wow, that would be great!  Erik personally pays for all the bandwidth, and
+    all servers used for busybox.net out of his own pocket.  If you would like
+    to make a donation to help support BusyBox, and/or request features, you
+    can click here:
+
+    <!-- Begin PayPal Logo -->
+    <center>
+    <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+       <input type="hidden" name="cmd" value="_xclick">
+       <input type="hidden" name="business" value="andersen@codepoet.org">
+       <input type="hidden" name="item_name" value="Support BusyBox">
+       <input type="hidden" name="image_url" value="http://codepoet-consulting.com/images/codepoet.png">
+       <input type="hidden" name="no_shipping" value="1">
+       <input type="image" src="images/donate.png" name="submit" alt="Make donation using PayPal">
+    </form>
+    </center>
+    <!-- End PayPal Logo -->
+
+    If you prefer to contact Erik directly to make a donation, donate hardware,
+    request support, etc, you can contact
+    <a href="http://codepoet-consulting.com/">CodePoet Consulting</a> here.
+    CodePoet Consulting can accept both Visa and MasterCard for those that do not
+    trust PayPal...
+
+<hr />
+
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/about.html b/busybox/docs/busybox.net/about.html
new file mode 100644 (file)
index 0000000..c086263
--- /dev/null
@@ -0,0 +1,63 @@
+<!--#include file="header.html" -->
+
+
+<!-- Begin Introduction section -->
+
+<h3>BusyBox: The Swiss Army Knife of Embedded Linux</h3>
+
+
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides replacements for most of the utilities you
+usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox
+generally have fewer options than their full-featured GNU cousins; however,
+the options that are included provide the expected functionality and behave
+very much like their GNU counterparts.  BusyBox provides a fairly complete
+environment for any small or embedded system.
+
+<p>
+
+BusyBox has been written with size-optimization and limited resources in
+mind. It is also extremely modular so you can easily include or exclude
+commands (or features) at compile time. This makes it easy to customize
+your embedded systems. To create a working system, just add some device
+nodes in /dev, a few configuration files in /etc, and a Linux kernel.
+
+<p>
+
+BusyBox is maintained by <a href=
+"http://codepoet.org/andersen/erik/erik.html">Erik Andersen</a>, and
+licensed under the
+<a href= "http://www.gnu.org/copyleft/gpl.html">GNU GENERAL PUBLIC LICENSE</a>
+
+<p>
+<p>
+
+<h3>Sponsors</h3>
+
+Please visit our sponsors and thank them for their
+support! They have provided money for equipment and
+bandwidth. Next time you need help with a project,
+consider these fine companies!
+
+
+<ul>
+  <li><a href="http://www.penguru.net">Penguru Consulting</a><br>
+  Custom development for embedded Linux systems and multimedia platforms
+  </li>
+
+  <li><a href="http://opensource.se/">opensource.se</a><br>
+  Embedded open source consulting in Europe.
+  </li>
+
+  <li><a href="http://www.codepoet-consulting.com">Codepoet Consulting</a><br>
+  Custom Linux, embedded Linux, BusyBox, and uClibc
+  development.
+  </li>
+
+</ul>
+
+If you wish to be a sponsor, or if you have already contributed and would like
+your name added here, email <a href= "mailto:andersen@codepoet.org">Erik</a>.
+
+
+<!--#include file="footer.html" -->
diff --git a/busybox/docs/busybox.net/busybox-growth.ps b/busybox/docs/busybox.net/busybox-growth.ps
new file mode 100644 (file)
index 0000000..2379def
--- /dev/null
@@ -0,0 +1,404 @@
+%!PS-Adobe-2.0
+%%Title: busybox-growth.ps
+%%Creator: gnuplot 3.5 (pre 3.6) patchlevel beta 347
+%%CreationDate: Tue Apr 10 14:03:36 2001
+%%DocumentFonts: (atend)
+%%BoundingBox: 50 40 554 770
+%%Orientation: Landscape
+%%Pages: (atend)
+%%EndComments
+/gnudict 120 dict def
+gnudict begin
+/Color true def
+/Solid true def
+/gnulinewidth 5.000 def
+/userlinewidth gnulinewidth def
+/vshift -46 def
+/dl {10 mul} def
+/hpt_ 31.5 def
+/vpt_ 31.5 def
+/hpt hpt_ def
+/vpt vpt_ def
+/M {moveto} bind def
+/L {lineto} bind def
+/R {rmoveto} bind def
+/V {rlineto} bind def
+/vpt2 vpt 2 mul def
+/hpt2 hpt 2 mul def
+/Lshow { currentpoint stroke M
+  0 vshift R show } def
+/Rshow { currentpoint stroke M
+  dup stringwidth pop neg vshift R show } def
+/Cshow { currentpoint stroke M
+  dup stringwidth pop -2 div vshift R show } def
+/UP { dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
+  /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def } def
+/DL { Color {setrgbcolor Solid {pop []} if 0 setdash }
+ {pop pop pop Solid {pop []} if 0 setdash} ifelse } def
+/BL { stroke gnulinewidth 2 mul setlinewidth } def
+/AL { stroke gnulinewidth 2 div setlinewidth } def
+/UL { gnulinewidth mul /userlinewidth exch def } def
+/PL { stroke userlinewidth setlinewidth } def
+/LTb { BL [] 0 0 0 DL } def
+/LTa { AL [1 dl 2 dl] 0 setdash 0 0 0 setrgbcolor } def
+/LT0 { PL [] 1 0 0 DL } def
+/LT1 { PL [4 dl 2 dl] 0 1 0 DL } def
+/LT2 { PL [2 dl 3 dl] 0 0 1 DL } def
+/LT3 { PL [1 dl 1.5 dl] 1 0 1 DL } def
+/LT4 { PL [5 dl 2 dl 1 dl 2 dl] 0 1 1 DL } def
+/LT5 { PL [4 dl 3 dl 1 dl 3 dl] 1 1 0 DL } def
+/LT6 { PL [2 dl 2 dl 2 dl 4 dl] 0 0 0 DL } def
+/LT7 { PL [2 dl 2 dl 2 dl 2 dl 2 dl 4 dl] 1 0.3 0 DL } def
+/LT8 { PL [2 dl 2 dl 2 dl 2 dl 2 dl 2 dl 2 dl 4 dl] 0.5 0.5 0.5 DL } def
+/Pnt { stroke [] 0 setdash
+   gsave 1 setlinecap M 0 0 V stroke grestore } def
+/Dia { stroke [] 0 setdash 2 copy vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath stroke
+  Pnt } def
+/Pls { stroke [] 0 setdash vpt sub M 0 vpt2 V
+  currentpoint stroke M
+  hpt neg vpt neg R hpt2 0 V stroke
+  } def
+/Box { stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath stroke
+  Pnt } def
+/Crs { stroke [] 0 setdash exch hpt sub exch vpt add M
+  hpt2 vpt2 neg V currentpoint stroke M
+  hpt2 neg 0 R hpt2 vpt2 V stroke } def
+/TriU { stroke [] 0 setdash 2 copy vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath stroke
+  Pnt  } def
+/Star { 2 copy Pls Crs } def
+/BoxF { stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V  hpt2 0 V  0 vpt2 V
+  hpt2 neg 0 V  closepath fill } def
+/TriUF { stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath fill } def
+/TriD { stroke [] 0 setdash 2 copy vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath stroke
+  Pnt  } def
+/TriDF { stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath fill} def
+/DiaF { stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath fill } def
+/Pent { stroke [] 0 setdash 2 copy gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath stroke grestore Pnt } def
+/PentF { stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath fill grestore } def
+/Circle { stroke [] 0 setdash 2 copy
+  hpt 0 360 arc stroke Pnt } def
+/CircleF { stroke [] 0 setdash hpt 0 360 arc fill } def
+/C0 { BL [] 0 setdash 2 copy moveto vpt 90 450  arc } bind def
+/C1 { BL [] 0 setdash 2 copy        moveto
+       2 copy  vpt 0 90 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C2 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 90 180 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C3 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 0 180 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C4 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 180 270 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C5 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 0 90 arc
+       2 copy moveto
+       2 copy  vpt 180 270 arc closepath fill
+               vpt 0 360 arc } bind def
+/C6 { BL [] 0 setdash 2 copy moveto
+      2 copy  vpt 90 270 arc closepath fill
+              vpt 0 360 arc closepath } bind def
+/C7 { BL [] 0 setdash 2 copy moveto
+      2 copy  vpt 0 270 arc closepath fill
+              vpt 0 360 arc closepath } bind def
+/C8 { BL [] 0 setdash 2 copy moveto
+      2 copy vpt 270 360 arc closepath fill
+              vpt 0 360 arc closepath } bind def
+/C9 { BL [] 0 setdash 2 copy moveto
+      2 copy  vpt 270 450 arc closepath fill
+              vpt 0 360 arc closepath } bind def
+/C10 { BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill
+       2 copy moveto
+       2 copy vpt 90 180 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C11 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 0 180 arc closepath fill
+       2 copy moveto
+       2 copy  vpt 270 360 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C12 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 180 360 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C13 { BL [] 0 setdash  2 copy moveto
+       2 copy  vpt 0 90 arc closepath fill
+       2 copy moveto
+       2 copy  vpt 180 360 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/C14 { BL [] 0 setdash 2 copy moveto
+       2 copy  vpt 90 360 arc closepath fill
+               vpt 0 360 arc } bind def
+/C15 { BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill
+               vpt 0 360 arc closepath } bind def
+/Rec   { newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
+       neg 0 rlineto closepath } bind def
+/Square { dup Rec } bind def
+/Bsquare { vpt sub exch vpt sub exch vpt2 Square } bind def
+/S0 { BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare } bind def
+/S1 { BL [] 0 setdash 2 copy vpt Square fill Bsquare } bind def
+/S2 { BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare } bind def
+/S3 { BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare } bind def
+/S4 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare } bind def
+/S5 { BL [] 0 setdash 2 copy 2 copy vpt Square fill
+       exch vpt sub exch vpt sub vpt Square fill Bsquare } bind def
+/S6 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare } bind def
+/S7 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill
+       2 copy vpt Square fill
+       Bsquare } bind def
+/S8 { BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare } bind def
+/S9 { BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare } bind def
+/S10 { BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill
+       Bsquare } bind def
+/S11 { BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill
+       Bsquare } bind def
+/S12 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare } bind def
+/S13 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+       2 copy vpt Square fill Bsquare } bind def
+/S14 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+       2 copy exch vpt sub exch vpt Square fill Bsquare } bind def
+/S15 { BL [] 0 setdash 2 copy Bsquare fill Bsquare } bind def
+/D0 { gsave translate 45 rotate 0 0 S0 stroke grestore } bind def
+/D1 { gsave translate 45 rotate 0 0 S1 stroke grestore } bind def
+/D2 { gsave translate 45 rotate 0 0 S2 stroke grestore } bind def
+/D3 { gsave translate 45 rotate 0 0 S3 stroke grestore } bind def
+/D4 { gsave translate 45 rotate 0 0 S4 stroke grestore } bind def
+/D5 { gsave translate 45 rotate 0 0 S5 stroke grestore } bind def
+/D6 { gsave translate 45 rotate 0 0 S6 stroke grestore } bind def
+/D7 { gsave translate 45 rotate 0 0 S7 stroke grestore } bind def
+/D8 { gsave translate 45 rotate 0 0 S8 stroke grestore } bind def
+/D9 { gsave translate 45 rotate 0 0 S9 stroke grestore } bind def
+/D10 { gsave translate 45 rotate 0 0 S10 stroke grestore } bind def
+/D11 { gsave translate 45 rotate 0 0 S11 stroke grestore } bind def
+/D12 { gsave translate 45 rotate 0 0 S12 stroke grestore } bind def
+/D13 { gsave translate 45 rotate 0 0 S13 stroke grestore } bind def
+/D14 { gsave translate 45 rotate 0 0 S14 stroke grestore } bind def
+/D15 { gsave translate 45 rotate 0 0 S15 stroke grestore } bind def
+/DiaE { stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V closepath stroke } def
+/BoxE { stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V closepath stroke } def
+/TriUE { stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V closepath stroke } def
+/TriDE { stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V closepath stroke } def
+/PentE { stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  closepath stroke grestore } def
+/CircE { stroke [] 0 setdash
+  hpt 0 360 arc stroke } def
+/Opaque { gsave closepath 1 setgray fill grestore 0 setgray closepath } def
+/DiaW { stroke [] 0 setdash vpt add M
+  hpt neg vpt neg V hpt vpt neg V
+  hpt vpt V hpt neg vpt V Opaque stroke } def
+/BoxW { stroke [] 0 setdash exch hpt sub exch vpt add M
+  0 vpt2 neg V hpt2 0 V 0 vpt2 V
+  hpt2 neg 0 V Opaque stroke } def
+/TriUW { stroke [] 0 setdash vpt 1.12 mul add M
+  hpt neg vpt -1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt 1.62 mul V Opaque stroke } def
+/TriDW { stroke [] 0 setdash vpt 1.12 mul sub M
+  hpt neg vpt 1.62 mul V
+  hpt 2 mul 0 V
+  hpt neg vpt -1.62 mul V Opaque stroke } def
+/PentW { stroke [] 0 setdash gsave
+  translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+  Opaque stroke grestore } def
+/CircW { stroke [] 0 setdash
+  hpt 0 360 arc Opaque stroke } def
+/BoxFill { gsave Rec 1 setgray fill grestore } def
+end
+%%EndProlog
+%%Page: 1 1
+gnudict begin
+gsave
+50 50 translate
+0.100 0.100 scale
+90 rotate
+0 -5040 translate
+0 setgray
+newpath
+(Helvetica) findfont 140 scalefont setfont
+1.000 UL
+LTb
+560 420 M
+63 0 V
+6409 0 R
+-63 0 V
+476 420 M
+(0) Rshow
+560 1056 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(100) Rshow
+560 1692 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(200) Rshow
+560 2328 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(300) Rshow
+560 2964 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(400) Rshow
+560 3600 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(500) Rshow
+560 4236 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(600) Rshow
+560 4872 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(700) Rshow
+1531 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(400) Cshow
+2825 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(600) Cshow
+4120 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(800) Cshow
+5414 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(1000) Cshow
+6708 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(1200) Cshow
+1.000 UL
+LTb
+560 420 M
+6472 0 V
+0 4452 V
+-6472 0 V
+560 420 L
+0 2646 M
+currentpoint gsave translate 90 rotate 0 0 M
+(tar.gz size \(Kb\)) Cshow
+grestore
+3796 140 M
+(time \(days since Jan 1, 1998\)) Cshow
+1.000 UL
+LT0
+696 420 M
+0 593 V
+1255 0 V
+0 15 V
+214 0 V
+0 6 V
+958 0 V
+0 1 V
+-84 0 V
+0 37 V
+168 0 V
+0 262 V
+13 0 V
+0 56 V
+91 0 V
+0 33 V
+6 0 V
+0 1 V
+19 0 V
+0 11 V
+20 0 V
+0 13 V
+32 0 V
+0 104 V
+52 0 V
+0 27 V
+65 0 V
+0 15 V
+39 0 V
+0 126 V
+174 0 V
+0 103 V
+52 0 V
+0 49 V
+175 0 V
+0 56 V
+433 0 V
+0 661 V
+415 0 V
+0 857 V
+123 0 V
+0 -291 V
+498 0 V
+0 208 V
+505 0 V
+0 66 V
+291 0 V
+0 115 V
+311 0 V
+0 449 V
+162 0 V
+0 309 V
+stroke
+grestore
+end
+showpage
+%%Trailer
+%%DocumentFonts: Helvetica
+%%Pages: 1
diff --git a/busybox/docs/busybox.net/copyright.txt b/busybox/docs/busybox.net/copyright.txt
new file mode 100644 (file)
index 0000000..528338d
--- /dev/null
@@ -0,0 +1,29 @@
+
+The code and graphics on this website (and it's mirror sites, if any) are
+Copyright (c) 1999-2004 by Erik Andersen.  All rights reserved.
+
+Documents on this Web site including their graphical elements, design, and
+layout are protected by trade dress and other laws and MAY BE COPIED OR
+IMITATED IN WHOLE OR IN PART.  THIS WEBSITE IS LICENSED FREE OF CHARGE, THERE
+IS NO WARRANTY FOR THE WEBSITE TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+SHOULD THIS WEBSITE PROVE DEFECTIVE, YOU MAY ASSUME THAT SOMEONE MIGHT GET
+AROUND TO SERVICING, REPAIRING OR CORRECTING IT SOMETIME WHEN THEY HAVE NOTHING
+BETTER TO DO.  REGARDLESS, YOU GET TO KEEP BOTH PIECES.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
+COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THIS
+WEBSITE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THIS WEBSITE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
+LOSS OF HAIR, LOSS OF LIFE, LOSS OF MEMORY, LOSS OF YOUR CARKEYS, MISPLACEMENT
+OF YOUR PAYCHECK, OR COMMANDER DATA BEING RENDERED UNABLE TO ASSIST THE
+STARFLEET OFFICERS ABORD THE STARSHIP ENTERPRISE TO RECALIBRATE THE MAIN
+DEFLECTOR ARRAY, LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+WEBSITE TO OPERATE WITH YOUR WEBBROWSER), EVEN IF SUCH HOLDER OR OTHER PARTY
+HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+You have been warned.
+
+You can contact the webmaster at <andersen@codepoet.org> if you have some sort
+of problem with this.
+
diff --git a/busybox/docs/busybox.net/cvs_anon.html b/busybox/docs/busybox.net/cvs_anon.html
new file mode 100644 (file)
index 0000000..f823d05
--- /dev/null
@@ -0,0 +1,57 @@
+<!--#include file="header.html" -->
+
+
+<h3>Anonymous CVS</h3>
+
+We allow anonymous (read-only) CVS access to everyone.  The first command you
+need to run for anonymous CVS access is:
+<pre>
+cvs -d:pserver:anonymous@busybox.net:/var/cvs login</pre>
+<p>
+CVS will prompt you for a password. Just press the Enter key (there is no
+password for anonymous access).  This step only needs to be done once, the first
+time you attempt to access CVS.
+<p>
+Once the login is complete, you can then check the list of available
+CVS modules by running the following command (all on one line):
+<pre>
+cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -c </pre>
+
+<p>
+If you wish, you can then check out a local copy of any of the
+available modules.  The following is an example of how to grab
+a copy of busybox and tinylogin:
+<pre>
+    cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -P busybox tinylogin</pre>
+This will create a directory called <b>busybox</b> and a directory called
+<b>tinylogin</b> in the current directory.  These directories contain the
+latest and greatest source code for busybox and tinylogin.
+
+<p>
+If you are not already familiar with using CVS, I recommend you visit
+this quick <a href="/cvs_howto.html">Introduction to CVS</a>.
+
+<p>
+I usually create a ~/.cvsrc file with the following things in it, and I
+recommend you should use the same:
+<pre>
+    -z3
+    update -dP
+    rdiff -u
+    diff -ubBwpN
+    checkout -P</pre>
+
+<p>
+Once you've checked out a copy of the source tree, you can update your
+source tree at any time so it is in sync with the latest and greatest by
+running the command:
+<pre>
+cvs update</pre>
+
+Because you've only been granted anonymous access to the tree, you won't be
+able to commit any changes. Changes can be submitted for inclusion by posting
+them to the appropriate mailing list.  For those that are actively contributing
+<a href="cvs_write.html">CVS write access</a> can be made available.
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/cvs_howto.html b/busybox/docs/busybox.net/cvs_howto.html
new file mode 100644 (file)
index 0000000..837d6cd
--- /dev/null
@@ -0,0 +1,44 @@
+<!--#include file="header.html" -->
+
+
+<h3>How to use CVS</h3>
+
+
+If you want to know all the gory details, you will want to visit
+<a href="http://www.cvshome.org/">the CVS main web page</a>.<p>
+For the impatient, the following is probably about all you need to know:
+<p>
+
+<dl>
+    <dt><pre>cvs checkout -c</pre>
+    <dd>Will list the modules available for checkout
+    <dt><pre>cvs checkout &lt module name &gt</pre>
+    <dd>Will checkout the named module
+    <dt><pre>cvs co &lt module name &gt</pre>
+    <dd>Same thing
+    <dt><pre>cvs update</pre>
+
+    <dd>Updates your local archive so it is in sync with the repository
+    -- your local updates are left intact.  Tries to merge upstream updates
+    into your local updates.  You will see the following tags when it is
+    updating your local repository: C means conflict, U means update,
+    P means patched, and M means modified.
+    <dt><pre>cvs up</pre>
+    <dd>Same thing
+    <dt><pre>cvs update &lt file name &gt</pre>
+    <dd>Same thing but for just the named file(s)/directory(s).
+    <dt><pre>cvs commit</pre>
+    <dd>Will check in all your work.
+    <dt><pre>cvs add &lt file name &gt</pre>
+
+    <dd>Adds the named file/directory into CVS
+    <dt><pre>cvs remove &lt file name &gt</pre>
+    <dd>Removes the named file/directory from the upstream repository.
+    <dt><pre>cvs rm &lt file name &gt</pre>
+    <dd>Same thing
+    <dt><pre>cvs log &lt file name &gt</pre>
+</dl>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/cvs_write.html b/busybox/docs/busybox.net/cvs_write.html
new file mode 100644 (file)
index 0000000..5c882f4
--- /dev/null
@@ -0,0 +1,32 @@
+<!--#include file="header.html" -->
+
+
+<h3>CVS Read/Write Access</h3>
+
+If you want to be able to commit things to CVS, first contribute some
+stuff to show you are serious.  Then, very nicely ask
+<a href="mailto:andersen@codepoet.org">Erik Andersen</a> if he will set you up with
+an account.  To access CVS, you will want to add the following to set up your environment:
+<pre>
+$ export CVS_RSH=/usr/bin/ssh
+$ export CVSROOT='username@cvs.busybox.net:/var/cvs'</pre>
+<br>
+It goes without saying you must change <em>username</em> to your own
+username...
+<p>
+
+To obtain commit access, you will need to demonstrate you are
+serious by submitting a few good patches first.  Then, you will need to
+select a user-name to use when committing stuff, and finally, you will
+need to send me the username you have selected, an ssh key, and the email
+address where you prefer email to be sent (I will forward any email sent
+to you, but not store it).
+
+<p>
+Note that if you would prefer to keep your communications with me
+private, you can encrypt your email using my
+<a href="http://www.codepoet.org/andersen/erik/gpg.asc">public key</a>.
+
+<!--#include file="footer.html" -->
+
+
diff --git a/busybox/docs/busybox.net/docs.html b/busybox/docs/busybox.net/docs.html
new file mode 100644 (file)
index 0000000..fc9ac6d
--- /dev/null
@@ -0,0 +1,27 @@
+<!--#include file="header.html" -->
+
+
+<h3>Documentation</h3>
+Current documentation for BusyBox includes:
+
+<ul>
+  <li><a href=
+  "downloads/BusyBox.html">BusyBox.html</a>. This is a
+  list of the all the available commands in BusyBox
+  with complete usage information and examples of how
+  to use each app. I have spent a <em>lot</em> of time
+  updating these docs and trying to make them fairly
+  comprehensive. If you find any errors (factual,
+  grammatical, whatever) please let me know.</li>
+
+  <li><a href="downloads/README">README</a>. This is
+  the README file included in the busybox source
+  release.</li>
+
+  <li>If you need more help, the BusyBox <a href=
+  "lists/busybox/">mailing list</a> is a good place to
+  start.</li>
+</ul>
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/download.html b/busybox/docs/busybox.net/download.html
new file mode 100644 (file)
index 0000000..a6a86ac
--- /dev/null
@@ -0,0 +1,38 @@
+<!--#include file="header.html" -->
+
+
+
+<h3>Download</h3>
+
+Source for the latest release can always be
+downloaded from <a href="downloads">http://www.busybox.net/downloads</a>.
+
+<p>
+You can also obtain <a href= "downloads/snapshots/">Daily Snapshots</a> of
+the latest stable, and the latest development CVS source trees.
+
+<p>
+BusyBox now has <b>two</b> CVS trees.  The "busybox-stable" tree
+contains the older 0.60.x stable series.  The "busybox" tree contains
+the latest 1.0.0-preX development version of busybox.<br>
+
+<ul>
+       <li> Click here to browse the <a href="/cgi-bin/cvsweb/busybox/">
+               CVS tree for the 1.0.0-preX development version of BusyBox</a>
+       </li>
+
+       <li>Click here to browse the <a href="/cgi-bin/cvsweb/busybox.stable/">
+               CVS tree for the stable 0.60.x version of BusyBox</a>.
+       </li>
+
+       <li>Anonymous <a href="cvs_anon.html">CVS access</a> is available.
+       </li>
+
+       <li>For those that are actively contributing there is
+               even <a href="cvs_write.html">CVS write access</a>.
+       </li>
+
+</ul>
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/footer.html b/busybox/docs/busybox.net/footer.html
new file mode 100644 (file)
index 0000000..9756f5d
--- /dev/null
@@ -0,0 +1,20 @@
+<!-- Footer -->
+
+
+    </td>
+    </tr>
+    </table>
+
+<hr />
+
+    <p>
+    <font face="arial, helvetica, sans-serif" size="-1">
+       <a HREF="/copyright.txt">Copyright &copy; 1999-2003 Erik Andersen</a>
+       <br>
+       Mail all comments, insults, suggestions and bribes to
+       <br>
+       Erik Andersen <A HREF="mailto:andersen@codepoet.org">andersen@codepoet.org</A><BR>
+    </font>
+
+  </body>
+</html>
diff --git a/busybox/docs/busybox.net/header.html b/busybox/docs/busybox.net/header.html
new file mode 100644 (file)
index 0000000..77c1418
--- /dev/null
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+"http://www.w3.org/TR/REC-html40/loose.dtd">
+
+<html>
+  <head>
+    <title>BusyBox</title>
+    <style type="text/css">
+     body {
+      background-color: #DEE2DE;
+      color: #000000;
+     }
+     :link { color: #660000 }
+     :visited { color: #660000 }
+     :active { color: #660000 }
+     td.c2 {font-family: arial, helvetica, sans-serif; font-size: 80%}
+     td.c1 {font-family: lucida, helvetica; font-size: 248%}
+    </style>
+  </head>
+
+  <body>
+    <basefont face="lucida, helvetica, arial" size="3">
+
+
+
+
+<table border="0" cellpadding="0" cellspacing="0">
+
+
+<tr>
+<td>
+    <div class="c3">
+      <table border="0" cellspacing="1" cellpadding="2">
+        <tr>
+          <td class="c1">BUSYBOX</td>
+        </tr>
+      </table>
+    </div>
+
+  <a href="/"><IMG SRC="images/busybox1.png" alt="BusyBox" border="0"></a><BR>
+</td>
+</tr>
+
+<tr>
+
+<td valign="TOP">
+    <br><a href="/about.html">About</a>
+    <br><a href="/screenshot.html">Screenshot</a>
+    <br><a href="/lists.html">Mailing Lists</a>
+    <br><a href="/news.html">Latest News</a>
+    <br><a href="/download.html">Download</a>
+    <br><a href="/FAQ.html">FAQ</a>
+    <br><a href="/cvs_anon.html">Accessing CVS</a>
+    <br><a href="/cgi-bin/cvsweb/busybox/">Browse CVS</a>
+    <br><a href="/docs.html">Documentation</a>
+    <br><a href="/products.html">Products</a>
+    <br><a href="/shame.html">Hall of Shame</a>
+    <br><a href="/license.html">License</a>
+
+    <p><b>Related Sites</b>
+    <br><a href="http://uclibc.org/">uClibc.org</a>
+    <br><a href="http://udhcp.busybox.net/">udhcp</a>
+    <br><a href="http://tinylogin.busybox.net/">tinylogin</a>
+    <br><a href="http://www.ucdot.org/">uCdot</a>
+    <br><a href="http://www.linuxdevices.com">LinuxDevices</a>
+    <br><a href="http://slashdot.org/">Slashdot</a>
+    <br><a href="http://freshmeat.net/">Freshmeat</a>
+    <br><a href="http://linuxtoday.com/">Linux Today</a>
+    <br><a href="http://lwn.net/">Linux Weekly News</a>
+    <br><a href="http://www.tldp.org/HOWTO">Linux HOWTOs</a>
+
+<!--
+    <a href="http://validator.w3.org/check/referer"><img
+     src="/images/vh40.gif" height=31 width=88
+          align=left border=0 alt="Valid HTML 4.0!"></a>
+-->
+
+</td>
+
+
+<td Valign="TOP">
+
diff --git a/busybox/docs/busybox.net/images/back.png b/busybox/docs/busybox.net/images/back.png
new file mode 100644 (file)
index 0000000..7992386
Binary files /dev/null and b/busybox/docs/busybox.net/images/back.png differ
diff --git a/busybox/docs/busybox.net/images/busybox.jpeg b/busybox/docs/busybox.net/images/busybox.jpeg
new file mode 100644 (file)
index 0000000..37edc96
Binary files /dev/null and b/busybox/docs/busybox.net/images/busybox.jpeg differ
diff --git a/busybox/docs/busybox.net/images/busybox.png b/busybox/docs/busybox.net/images/busybox.png
new file mode 100644 (file)
index 0000000..b1eb92f
Binary files /dev/null and b/busybox/docs/busybox.net/images/busybox.png differ
diff --git a/busybox/docs/busybox.net/images/busybox1.png b/busybox/docs/busybox.net/images/busybox1.png
new file mode 100644 (file)
index 0000000..4d3126a
Binary files /dev/null and b/busybox/docs/busybox.net/images/busybox1.png differ
diff --git a/busybox/docs/busybox.net/images/busybox2.jpg b/busybox/docs/busybox.net/images/busybox2.jpg
new file mode 100644 (file)
index 0000000..abf8f06
Binary files /dev/null and b/busybox/docs/busybox.net/images/busybox2.jpg differ
diff --git a/busybox/docs/busybox.net/images/busybox3.jpg b/busybox/docs/busybox.net/images/busybox3.jpg
new file mode 100644 (file)
index 0000000..0fab84c
Binary files /dev/null and b/busybox/docs/busybox.net/images/busybox3.jpg differ
diff --git a/busybox/docs/busybox.net/images/dir.png b/busybox/docs/busybox.net/images/dir.png
new file mode 100644 (file)
index 0000000..1d633ce
Binary files /dev/null and b/busybox/docs/busybox.net/images/dir.png differ
diff --git a/busybox/docs/busybox.net/images/donate.png b/busybox/docs/busybox.net/images/donate.png
new file mode 100644 (file)
index 0000000..b55621b
Binary files /dev/null and b/busybox/docs/busybox.net/images/donate.png differ
diff --git a/busybox/docs/busybox.net/images/fm.mini.png b/busybox/docs/busybox.net/images/fm.mini.png
new file mode 100644 (file)
index 0000000..c0883cd
Binary files /dev/null and b/busybox/docs/busybox.net/images/fm.mini.png differ
diff --git a/busybox/docs/busybox.net/images/gfx_by_gimp.png b/busybox/docs/busybox.net/images/gfx_by_gimp.png
new file mode 100644 (file)
index 0000000..d583140
Binary files /dev/null and b/busybox/docs/busybox.net/images/gfx_by_gimp.png differ
diff --git a/busybox/docs/busybox.net/images/ltbutton2.png b/busybox/docs/busybox.net/images/ltbutton2.png
new file mode 100644 (file)
index 0000000..9bad949
Binary files /dev/null and b/busybox/docs/busybox.net/images/ltbutton2.png differ
diff --git a/busybox/docs/busybox.net/images/sdsmall.png b/busybox/docs/busybox.net/images/sdsmall.png
new file mode 100644 (file)
index 0000000..b102450
Binary files /dev/null and b/busybox/docs/busybox.net/images/sdsmall.png differ
diff --git a/busybox/docs/busybox.net/images/text.png b/busybox/docs/busybox.net/images/text.png
new file mode 100644 (file)
index 0000000..6034f89
Binary files /dev/null and b/busybox/docs/busybox.net/images/text.png differ
diff --git a/busybox/docs/busybox.net/images/vh40.gif b/busybox/docs/busybox.net/images/vh40.gif
new file mode 100644 (file)
index 0000000..c5e9402
Binary files /dev/null and b/busybox/docs/busybox.net/images/vh40.gif differ
diff --git a/busybox/docs/busybox.net/images/written.in.vi.png b/busybox/docs/busybox.net/images/written.in.vi.png
new file mode 100644 (file)
index 0000000..84f59bc
Binary files /dev/null and b/busybox/docs/busybox.net/images/written.in.vi.png differ
diff --git a/busybox/docs/busybox.net/index.html b/busybox/docs/busybox.net/index.html
new file mode 100644 (file)
index 0000000..1bab6b0
--- /dev/null
@@ -0,0 +1 @@
+<!--#include file="news.html" -->
diff --git a/busybox/docs/busybox.net/license.html b/busybox/docs/busybox.net/license.html
new file mode 100644 (file)
index 0000000..14324f1
--- /dev/null
@@ -0,0 +1,135 @@
+<!--#include file="header.html" -->
+
+
+<h3>The GPL BusyBox license</h3>
+
+There has been some confusion in the past as to exactly what is
+required to safely distribute GPL'd software such as BusyBox as
+part of a product.  To ensure that there is no confusion
+whatsoever, this page attempts to summarize what you should do to
+ensure you do not accidentally violate the law.
+
+<p>
+<h3>Complying with the BusyBox license is easy and completely free.</h3>
+
+U.S. and International Law protects copyright owners from the unauthorized
+reproduction, adaptation, display, distribution, etc of copyright protected
+works.  Copyright violations (such as shipping BusyBox in a manner contrary to
+its license) are subject to severe penalties.  The courts can award up to
+$150,000 per product shipped without even showing any actual loss by the
+copyright holder.  Criminal penalties are available for intentional acts
+undertaken for purposes of "commercial advantage" or "private financial gain."
+In addition, if it comes to my attention that you are violating the BusyBox
+license, I will list you on the <a href="/shame.html">BusyBox Hall of Shame</a>
+webpage.
+
+<p>
+
+Nobody wants that to happen.  Do everyone a favor and don't break the law -- if
+you use BusyBox, you <b>must comply with the BusyBox license</b>.
+
+<p>
+<h3>BusyBox is licensed under the GNU General Public License</h3>
+
+BusyBox is licensed under the GNU General Public License , which
+is generally just abbreviated as the GPL license, or
+just the GPL.
+<p>
+<a href="/products.html">Anyone thinking of shipping
+BusyBox as part of a product</a> should be familiar with the
+licensing terms under which they are allowed to use and
+distribute BusyBox.  You are advised to take a look over the
+
+<ul>
+<li><a href="http://www.gnu.org/licenses/gpl.html">full text of
+the GNU General Public License</a>, and
+<li><a href="http://www.gnu.org/licenses/gpl-faq.html">
+Frequently Asked Questions about the GNU GPL</a>
+</ul>
+to be sure you (and your lawyers) fully understand them.
+
+<p>
+
+The following is a quick summary for the impatient.  If you
+carefully follow these steps, it will ensure that you are 100%
+authorized to ship BusyBox with your product, and have no reason
+to worry about lawsuits or being listed on the <a
+href="/shame.html">BusyBox Hall of Shame</a> page.  You will be
+able to sleep peacefully at night knowing you have fulfilled all
+your licensing obligations.
+
+<p>
+
+If you distribute a product, it should either be accompanied by
+<b>full source for all GPL'd products</b> (including BusyBox)
+and/or a <b>written offer</b> to supply the source for all
+GPL'd products for the cost of shipping and handling.  The source
+has to be in its preferred machine readable form, so you cannot
+encrypt or obfuscate it.  You are not required to provide full
+source for all the closed source applications that happen to be
+part of the system with BusyBox, though you can certainly do so
+if you feel like it.  But providing source for the GPL licensed
+applications such as BusyBox is mandatory.
+
+<p>
+
+<b>Accompanied by source</b> generally means you distribute the full
+source code for all GPL'd products including BusyBox along with your
+product, such as by placing it somewhere on a driver CD.  Full source
+code includes the BusyBox ".config" file used when your shipping BusyBox
+binary was compiled, and any and all modifications you made to the
+BusyBox source code.
+
+<p>
+
+<b>A written offer</b> generally means that somewhere in the
+documentation for your product, you write something like
+
+<blockquote>
+The GPL source code contained in this product is available as a
+free download from http://blah.blah.blah/
+</blockquote>
+Alternatively, you can offer the source code by writing
+somewhere in the documentation for your product something like
+<blockquote>
+If you would like a copy of the GPL source code contained in this
+product shipped to you on CD, please send $9.99 to &lt;address&gt;
+which covers the cost of preparing and mailing a CD to you.
+</blockquote>
+<p>
+
+Keep in mind though that if you distribute GPL'd binaries online (as is often
+done when supplying firmware updates), it is <b>highly</b> recommended that you
+make the corresponding source available online at the same place.  Regardless,
+if you distribute a binary copy of BusyBox online (such as part of a firmware
+update) you <b>must</b> either make source available online (i.e.
+<b>accompanied by source</b>) and/or inform those downloading firmware updates
+of their right to obtain source (i.e. <b>a written offer</b>).  Failure to do
+so is a violation of your licensing obligations.
+
+
+<p>
+
+Some people have the mistaken understanding that if they use unmodified
+GPL'd source code, they do not need to distribute anything.  This belief
+is not correct, and is not supported by the
+<a href="http://www.gnu.org/licenses/gpl.html">text of GPL</a>.
+Please do re-read it -- you will find there is no such provision.
+If you distribute any GPL'd binaries, you must also make source available
+as discussed on this webpage.
+
+<p>
+<h3>A Good Example</h3>
+
+These days, <a href="http://www.linksys.com/">Linksys</a> is
+doing a good job at complying with the GPL, they get to be an
+example of how to do things right.  Please take a moment and
+check out what they do with
+<a href="http://www.linksys.com/download/firmware.asp?fwid=178">
+distributing the firmware for their WRT54G Router.</a>
+Following their example would be a fine way to ensure that you
+have also fulfilled your licensing obligations.
+
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/lists.html b/busybox/docs/busybox.net/lists.html
new file mode 100644 (file)
index 0000000..5c50c95
--- /dev/null
@@ -0,0 +1,45 @@
+<!--#include file="header.html" -->
+
+
+<!-- Begin Introduction section -->
+
+<h3>Mailing List Information</h3>
+BusyBox has a <a href="/lists/busybox/">mailing list</a> for discussion and
+development.  You can subscribe by visiting
+<a href="http://codepoet.org/mailman/listinfo/busybox">this page</a>.
+Only subscribers to the BusyBox mailing list are allowed to post
+to this list.
+
+<p>
+There is also a mailing list for <a href="/lists/busybox-cvs/">active developers</a>
+wishing to read the complete diff of each and every change to busybox -- not for the
+faint of heart.  Active developers can subscribe by visiting
+<a href="http://codepoet.org/mailman/listinfo/busybox-cvs">this page</a>.
+The CVS server is the only one permtted to post to this list.
+
+<p>
+
+
+<h3>Search the List Archives</h3>
+Please search the mailing list archives before asking questions on the mailing
+list, since there is a good chance someone else has asked the same question
+before.  Checking the archives is a great way to avoid annoying everyone on the
+list with frequently asked questions...
+<p>
+
+<center>
+<form method="GET" action="http://www.google.com/custom">
+<input type="hidden" name="domains" value="busybox.net">
+<input type="hidden" name="sitesearch" value="busybox.net">
+<input type="text" name="q" size="31" maxlength="255" value="">
+<br>
+<input type="submit" name="sa" value="search the mailing list archives">
+<br>
+<a href="http://www.google.com"><img src="http://www.google.com/logos/Logo_25wht.gif" border="0" alt="Google" height="32" width="75" align="middle"></a>
+<br>
+</form>
+</center>
+
+
+
+<!--#include file="footer.html" -->
diff --git a/busybox/docs/busybox.net/news.html b/busybox/docs/busybox.net/news.html
new file mode 100644 (file)
index 0000000..0d4c81b
--- /dev/null
@@ -0,0 +1,52 @@
+<!--#include file="header.html" -->
+
+
+<ul>
+
+  <li><b>13 October 2004 -- BusyBox 1.00 released</b><p>
+
+    When you take a careful look at nearly every embedded Linux device or
+    software distribution shipping today, you will find a copy of BusyBox.
+    With countless routers, set top boxes, wireless access points, PDAs, and
+    who knows what else, the future for Linux and BusyBox on embedded devices
+    is looking very bright.
+
+    <p>
+
+    It is therefore with great satisfaction that I declare each and every
+    device already shipping with BusyBox is now officially out of date.
+    The highly anticipated release of BusyBox 1.00 has arrived!
+
+    <p>
+
+    Over three years in development, BusyBox 1.00 represents a tremendous
+    improvement over the old 0.60.x stable series.  Now featuring a Linux
+    KernelConf based configuration system (as used by the Linux kernel),
+    Linux 2.6 kernel support, many many new applets, and the development
+    work and testing of thousands of people from around the world.
+
+    <p>
+
+    If you are already using BusyBox, you are strongly encouraged to upgrade to
+    BusyBox 1.00.  If you are considering developing an embedded Linux device
+    or software distribution, you may wish to investigate if using BusyBox is
+    right for your application.  If you need help getting started using
+    BusyBox, if you wish to donate to help cover expenses, or if you find a bug
+    and need help reporting it, you are invited to visit the <a
+    href="FAQ.html">BusyBox FAQ</a>.
+
+    <p>
+
+    As usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+
+    <p>
+    <li><b>Old News</b><p>
+    <a href="/oldnews.html">Click here to read older news</a>
+
+
+</ul>
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/oldnews.html b/busybox/docs/busybox.net/oldnews.html
new file mode 100644 (file)
index 0000000..83987ec
--- /dev/null
@@ -0,0 +1,1060 @@
+<!--#include file="header.html" -->
+
+
+<ul>
+
+  <li><b>16 August 2004 -- BusyBox 1.0.0-rc3 released</b><p>
+
+    Here goes release candidate 3...
+    <p>
+    The <a href="downloads/Changelog">changelog</a> has all the details.
+    And as usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+
+  <p>
+  <li><b>26 July 2004 -- BusyBox 1.0.0-rc2 released</b><p>
+
+    Here goes release candidate 2...
+    <p>
+    The <a href="downloads/Changelog">changelog</a> has all the details.
+    And as usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+
+  <p>
+  <li><b>20 July 2004 -- BusyBox 1.0.0-rc1 released</b><p>
+
+    Here goes release candidate 1...  This fixes all (most?) of the problems
+    that have turned up since -pre10.  In particular, loading and unloading of
+    kernel modules with 2.6.x kernels should be working much better.
+    <p>
+
+    I <b>really</b> want to get BusyBox 1.0.0 released soon and I see no real
+    reason why the 1.0.0 release shouldn't happen with things pretty much as
+    is.  BusyBox is in good shape at the moment, and it works nicely for
+    everything that I'm doing with it.  And from the reports I've been getting,
+    it works nicely for what most everyone else is doing with it as well.
+    There will eventually be a 1.0.1 anyway, so we might as well get on with
+    it.  No, BusyBox is not perfect.  No piece of software ever is.  And while
+    there is still plenty that can be done to improve things, most of that work
+    is waiting till we can get a solid 1.0.0 release out the door....
+    <p>
+
+    Please do not bother to send in patches adding cool new features at this
+    time.  Only bug-fix patches will be accepted.  If you have submitted a
+    bug-fixing patch to the busybox mailing list and no one has emailed you
+    explaining why your patch was rejected, it is safe to say that your patch
+    has been lost or forgotten.  That happens sometimes.  Please re-submit your
+    bug-fixing patch to the BusyBox mailing list, and be sure to put "[PATCH]"
+    at the beginning of the email subject line!
+
+    <p>
+    The <a href="downloads/Changelog">changelog</a> has all the details.
+    And as usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+
+    <p>
+    On a less happy note, My 92 year old grandmother (my dad's mom) passed away
+    yesterday (June 19th).  The funeral will be Thursday in a little town about
+    2 hours south of my home.  I've checked and there is absolutely no way I
+    could be back in time for the funeral if I attend <a
+    href="http://www.linuxsymposium.org/2004/">OLS</a> and give my presentation
+    as scheduled.
+    <p>
+    As such, it is with great reluctance and sadness that I have come
+    to the conclusion I will have to make my appologies and skip OLS
+    this year.
+    <p>
+
+
+  <p>
+  <li><b>13 April 2004 -- BusyBox 1.0.0-pre10 released</b><p>
+
+    Ok, I lied.  It turns out that -pre9 will not be the final BusyBox
+    pre-release.  With any luck however -pre10 will be, since I <b>really</b>
+    want to get BusyBox 1.0.0 released very soon.  As usual, please do not
+    bother to send in patches adding cool new features at this time.  Only
+    bug-fix patches will be accepted.  It would also be <b>very</b> helpful if
+    people could continue to review the BusyBox documentation and submit
+    improvements.
+
+    <p>
+    The <a href="downloads/Changelog">changelog</a> has all the details.
+    And as usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+
+  <p>
+  <li><b>6 April 2004 -- BusyBox 1.0.0-pre9 released</b><p>
+
+    Here goes the final BusyBox pre-release...  This is your last chance for
+    bug fixes.  With luck this will be released as BusyBox 1.0.0 later this
+    week.  Please do not bother to send in patches adding cool new features at
+    this time.  Only bug-fix patches will be accepted.  It would also be
+    <b>very</b> helpful if people could help review the BusyBox documentation
+    and submit improvements.  I've spent a lot of time updating the
+    documentation to make it better match reality, but I could really use some
+    assistance in checking that the features supported by the various applets
+    match the features listed in the documentation.
+
+    <p>
+    I had hoped to get this released a month ago, but
+    <a href="http://codepoet.org/gallery/baby_peter/img_1796">
+    another release on 1 March 2004</a> has kept me busy...
+
+    <p>
+    The <a href="downloads/Changelog">changelog</a> has all the details.
+    And as usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+
+  <p>
+  <li><b>23 February 2004 -- BusyBox 1.0.0-pre8 released</b><p>
+
+    Here goes yet another BusyBox pre-release...  Please do not bother to send
+    in patches supplying new features at this time.  Only bug-fix patches will
+    be accepted.  If you have a cool new feature you would like to see
+    supported, or if you have an amazing new applet you would like to submit,
+    please wait and submit such things later.  We really want to get a release
+    out we can all be proud of.  We are still aiming to finish off the -pre
+    series in February and move on to the final 1.0.0 release...  So if you
+    spot any bugs, now would be an excellent time to send in a fix to the
+    busybox mailing list.  It would also be <b>very</b> helpful if people could
+    help review the BusyBox documentation and submit improvements.  It would be
+    especially helpful if people could check that the features supported by the
+    various applets match the features listed in the documentation.
+
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all the details.
+    And as usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+
+  <li><b>4 February 2004 -- BusyBox 1.0.0-pre7 released</b><p>
+
+    There was a bug in -pre6 that broke argument parsing for a
+    number of applets, since a variable was not being zeroed out
+    properly.  This release is primarily intended to fix that one
+    problem.  In addition, this release fixes several other
+    problems, including a rewrite by mjn3 of the code for parsing
+    the busybox.conf file used for suid handling, some shell updates
+    from vodz, and a scattering of other small fixes.  We are still
+    aiming to finish off the -pre series in February and move on to
+    the final 1.0.0 release...  If you see any problems, of have
+    suggestions to make, as always, please feel free to email the
+    busybox mailing list.
+
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  And as usual you can
+    <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+
+  <p>
+  <li><b>30 January 2004 -- BusyBox 1.0.0-pre6 released</b><p>
+
+    Here goes the next pre-release for the new BusyBox stable
+    series.  This release adds a number of size optimizations,
+    updates udhcp, fixes up 2.6 modutils support, updates ash
+    and the shell command line editing, and the usual pile of
+    bug fixes both large and small.  Things appear to be
+    settling down now, so with a bit of luck and some testing
+    perhaps we can finish off the -pre series in February and
+    move on to the final 1.0.0 release...  If you see any
+    problems, of have suggestions to make, as always, please
+    feel free to email the busybox mailing list.
+
+    <p>
+
+    People who rely on the <a href= "downloads/snapshots/">daily BusyBox snapshots</a>
+    should be aware that snapshots of the old busybox 0.60.x
+    series are no longer available.  Daily snapshots are now
+    only available for the BusyBox 1.0.0 series and now use
+    the naming scheme "busybox-&lt;date&gt;.tar.bz2".  Please
+    adjust any build scripts using the old naming scheme accordingly.
+
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  And as usual you can
+    <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+
+  <p>
+  <li><b>23 December 2003 -- BusyBox 1.0.0-pre5 released</b><p>
+
+    Here goes the next pre-release for the new BusyBox stable
+    series.  The most obvious thing in this release is a fix for
+    a terribly stupid bug in mount that prevented it from working
+    properly unless you specified the filesystem type.  This
+    release also fixes a few compile problems, updates udhcp,
+    fixes a silly bug in fdisk, fixes ifup/ifdown to behave like
+    the Debian version, updates devfsd, updates the 2.6.x
+    modutils support, add a new 'rx' applet, removes the obsolete
+    'loadacm' applet, fixes a few tar bugs, fixes a sed bug, and
+    a few other odd fixes.
+
+    <p>
+
+    If you see any problems, of have suggestions to make, as
+    always, please feel free to send an email to the busybox
+    mailing list.
+
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  And as usual you can
+    <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+
+
+  <li><b>10 December 2003 -- BusyBox 1.0.0-pre4 released</b><p>
+
+    Here goes the fourth pre-release for the new BusyBox stable
+    series.  This release includes major rework to sed, lots of
+    rework on tar, a new tiny implementation of bunzip2, a new
+    devfsd applet, support for 2.6.x kernel modules, updates to
+    the ash shell, sha1sum and md5sum have been merged into a
+    common applet, the dpkg applets has been cleaned up, and tons
+    of random bugs have been fixed.  Thanks everyone for all the
+    testing, bug reports, and patches!  Once again, a big
+    thank-you goes to Glenn McGrath (bug1) for stepping in and
+    helping get patches merged!
+
+    <p>
+
+    And of course, if you are reading this, you might have noticed
+    the busybox website has been completely reworked.  Hopefully
+    things are now somewhat easier to navigate...  If you see any
+    problems, of have suggestions to make, as always, please feel
+    free to send an email to the busybox mailing list.
+
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  And as usual you can
+    <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+
+
+
+  <p>
+  <li><b>12 Sept 2003 -- BusyBox 1.0.0-pre3 released</b><p>
+
+    Here goes the third pre-release for the new BusyBox stable
+    series.  The last prerelease has held up quite well under
+    testing, but a number of problems have turned up as the number
+    of people using it has increased.  Thanks everyone for all
+    the testing, bug reports, and patches!
+
+    <p>
+
+    If you have submitted a patch or a bug report to the busybox
+    mailing list and no one has emailed you explaining why your
+    patch was rejected, it is safe to say that your patch has
+    somehow gotten lost or forgotten.  That happens sometimes.
+    Please re-submit your patch or bug report to the BusyBox
+    mailing list!
+
+    <p>
+
+    The point of the "-preX" versions is to get a larger group of
+    people and vendors testing, so any problems that turn up can be
+    fixed prior to the final 1.0.0 release.  The main feature
+    (besides additional testing) that is still still on the TODO
+    list before the final BusyBox 1.0.0 release is sorting out the
+    modutils issues.  For the new 2.6.x kernels, we already have
+    patches adding insmod and rmmod support and those need to be
+    integrated.  For 2.4.x kernels, for which busybox only supports
+    a limited number of architectures, we may want to invest a bit
+    more work before we cut 1.0.0.  Or we may just leave 2.4.x
+    module loading alone.
+
+    <p>
+
+    I had hoped this release would be out a month ago.  And of
+    course, it wasn't since Erik became busy getting a release of
+    <a href="http://www.uclibc.org/">uClibc</a>
+    out the door.  Many thanks to Glenn McGrath (bug1) for
+    stepping in and helping get a bunch of patches merged!  I am
+    not even going to state a date for releasing BusyBox 1.0.0
+    -pre4 (or the final 1.0.0).  We're aiming for late September...
+    But if this release proves as to be exceptionally stable (or
+    exceptionally unstable!), the next release may be very soon
+    indeed.
+
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  And as usual you can
+    <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+
+
+    <p>
+    <li><b>30 July 2003 -- BusyBox 1.0.0-pre2 released</b><p>
+
+    Here goes another pre release for the new BusyBox stable
+    series.  The last prerelease (pre1) was given quite a lot of
+    testing (thanks everyone!) which has helped turn up a number of
+    bugs, and these problems have now been fixed.
+
+    <p>
+
+    Highlights of -pre2 include updating the 'ash' shell to sync up
+    with the Debian 'dash' shell, a new 'hdparm' applet was added,
+    init again supports pivot_root,  The 'reboot' 'halt' and
+    'poweroff' applets can now be used without using busybox init.
+    an ifconfig buffer overflow was fixed, losetup now allows
+    read-write loop devices, uClinux daemon support was added, the
+    'watchdog', 'fdisk', and 'kill' applets were rewritten, there were
+    tons of doc updates, and there were many other bugs fixed.
+    <p>
+
+    If you have submitted a patch and it is not included in this
+    release and Erik has not emailed you explaining why your patch
+    was rejected, it is safe to say that he has lost your patch.
+    That happens sometimes.   Please re-submit your patch to the
+    BusyBox mailing list.
+    <p>
+
+    The point of the "-preX" versions is to get a larger group of
+    people and vendors testing, so any problems that turn up can be
+    fixed prior to the final 1.0.0 release.  The main feature that
+    is still still on the TODO list before the final BusyBox 1.0.0
+    release is adding module support for the new 2.6.x kernels.  If
+    necessary, a -pre3 BusyBox release will happen on August 6th.
+    Hopefully (i.e.  unless some horrible catastrophic problem
+           turns up) the final BusyBox 1.0.0 release will be ready by
+    then...
+    <p>
+
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  As usual you can <a href="downloads">download busybox here</a>.
+
+    <p>Have Fun!
+    <p>
+
+    <p>
+  <li><b>15 July 2003 -- BusyBox 1.0.0-pre1 released</b><p>
+
+    The busybox development series has been under construction for
+    nearly two years now.  Which is just entirely too long...  So
+    it is with great pleasure that I announce the imminent release
+    of a new stable series.  Due to the huge number of changes
+    since the last stable release (and the usual mindless version
+    number inflation) I am branding this new stable series verison
+    1.0.x...
+    <p>
+
+    The point of "-preX" versions is to get a larger group of
+    people and vendors testing, so any problems that turn up can be
+    fixed prior to the magic 1.0.0 release (which should happen
+    later this month)...  I plan to release BusyBox 1.0.0-pre2 next
+    Monday (July 21st), and, if necessary, -pre3 on July 28th.
+    Hopefully (i.e. unless some horrible catastrophic problem turns
+    up) the final BusyBox 1.0.0 release should be ready by the end
+    of July.
+    <p>
+
+    If you have submitted patches, and they are not in this release
+    and I have not emailed you explaining why your patch was
+    rejected, it is safe to say that I have lost your patch.  That
+    happens sometimes.  Please do <B>NOT</b> send all your patches,
+    support questions, etc, directly to Erik.  I get hundreds of
+    emails every day (which is why I end up losing patches
+    sometimes in the flood)...  The busybox mailing list is the
+    right place to send your patches, support questions, etc.
+    <p>
+
+    I would like to especially thank Vladimir Oleynik (vodz), Glenn
+    McGrath (bug1), Robert Griebl (sandman), and Manuel Novoa III
+    (mjn3) for their significant efforts and contributions that
+    have made this release possible.
+    <p>
+
+    As usual you can <a href="downloads">download busybox here</a>.
+    You don't really need to bother with the
+    <a href="downloads/Changelog">changelog</a>, as the changes
+    vs the stable version are way too extensive to easily enumerate.
+    But you can take a look if you really want too.
+
+    <p>Have Fun!
+    <p>
+
+
+
+  <p>
+  <li><b>26 October 2002 -- BusyBox 0.60.5 released</b><p>
+
+    I am very pleased to announce that the BusyBox 0.60.5 (stable)
+    is now available for download.  This is a bugfix release for
+    the stable series to address all the problems that have turned
+    up since the last release.  Unfortunately, the previous release
+    had a few nasty bugs (i.e. init could deadlock, gunzip -c tried
+    to delete source files, cp -a wouldn't copy symlinks, and init
+    was not always providing controlling ttys when it should have).
+    I know I said that the previous release would be the end of the
+    0.60.x series.  Well, it turns out I'm a liar.  But this time I
+    mean it (just like last time ;-).  This will be the last
+    release for the 0.60.x series --  all further development work
+    will be done for the development busybox tree.  Expect the development
+    version to have its first real release very very soon now...
+
+    <p>
+    The <a href="downloads/Changelog.full">changelog</a> has all
+    the details.  As usual you can <a href="downloads">download busybox here</a>.
+    <p>Have Fun!
+    <p>
+
+  <p>
+  <li><b>18 September 2002 -- BusyBox 0.60.4 released</b><p>
+
+    I am very pleased to announce that the BusyBox 0.60.4
+    (stable) is now available for download.  This is primarily
+    a bugfix release for the stable series to address all
+    the problems that have turned up since the last
+    release.  This will be the last release for the 0.60.x series.
+    I mean it this time --  all further development work will be done
+    on the development busybox tree, which is quite solid now and
+    should soon be getting its first real release.
+
+    <p>
+    The <a href="downloads/Changelog.full">changelog</a> has all
+    the details.  As usual you can <a href="downloads">download busybox here</a>.
+    <p>Have Fun!
+    <p>
+
+
+  <p>
+  <li><b>27 April 2002 -- BusyBox 0.60.3 released</b><p>
+
+    I am very pleased to announce that the BusyBox 0.60.3 (stable) is
+    now available for download.  This is primarily a bugfix release
+    for the stable series.  A number of problems have turned up since
+    the last release, and this should address most of those problems.
+    This should be the last release for the 0.60.x series.  The
+    development busybox tree has been progressing nicely, and will
+    hopefully be ready to become the next stable release.
+
+    <p>
+    The <a href="downloads/Changelog">changelog</a> has all
+    the details.  As usual you can <a href="downloads">download busybox here</a>.
+    <p>Have Fun!
+    <p>
+
+
+  <p>
+  <li><b>6 March 2002 -- busybox.net now has mirrors!</b><p>
+
+    Busybox.net is now much more available, thanks to
+    the fine folks at <a href= "http://i-netinnovations.com/">http://i-netinnovations.com/</a>
+    who are providing hosting for busybox.net and
+    uclibc.org.  In addition, we now have two mirrors:
+    <a href= "http://busybox.linuxmagic.com/">http://busybox.linuxmagic.com/</a>
+    in Canada and
+    <a href= "http://busybox.csservers.de/">http://busybox.csservers.de/</a>
+    in Germany.  I hope this makes things much more
+    accessible for everyone!
+
+
+<li>
+<b>3 January 2002 -- Welcome to busybox.net!</b>
+
+<p>Thanks to the generosity of a number of busybox
+users, we have been able to purchase busybox.net
+(which is where you are probably reading this).
+Right now, busybox.net and uclibc.org are both
+living on my home system (at the end of my DSL
+line). I apologize for the abrupt move off of
+busybox.lineo.com. Unfortunately, I no longer have
+the access needed to keep that system updated (for
+example, you might notice the daily snapshots there
+stopped some time ago).</p>
+
+<p>Busybox.net is currently hosted on my home
+server, at the end of a DSL line. Unfortunately,
+the load on them is quite heavy. To address this,
+I'm trying to make arrangements to get busybox.net
+co-located directly at an ISP. To assist in the
+co-location effort, <a href=
+"http://www.codepoet.org/~markw">Mark Whitley</a>
+(author of busybox sed, cut, and grep) has donated
+his <a href=
+"http://www.netwinder.org/">NetWinder</a> computer
+for hosting busybox.net and uclibc.org. Once this
+system is co-located, the current speed problems
+should be completely eliminated. Hopefully, too,
+some of you will volunteer to set up some mirror
+sites, to help to distribute the load a bit.</p>
+
+<p><!--
+    <center>
+    Click here to help support busybox.net!
+    <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+    <input type="hidden" name="cmd" value="_xclick">
+    <input type="hidden" name="business" value="andersen@codepoet.org">
+    <input type="hidden" name="item_name" value="Support Busybox">
+    <input type="hidden" name="image_url" value="https://codepoet-consulting.com/images/busybox2.jpg">
+    <input type="hidden" name="no_shipping" value="1">
+    <input type="image" src="images/donate.png" border="0" name="submit" alt="Make donation using PayPal">
+    </form>
+    </center>
+    -->
+ Since some people expressed concern over BusyBox
+donations, let me assure you that no one is getting
+rich here. All BusyBox and uClibc donations will be
+spent paying for bandwidth and needed hardware
+upgrades. For example, Mark's NetWinder currently
+has just 64Meg of memory. As demonstrated when
+google spidered the site the other day, 64 Megs in
+not enough, so I'm going to be ordering 256Megs of
+ram and a larger hard drive for the box today. So
+far, donations received have been sufficient to
+cover almost all expenses. In the future, we may
+have co-location fees to worry about, but for now
+we are ok. A <b>HUGE thank-you</b> goes out to
+everyone that has contributed!<br>
+ -Erik</p>
+</li>
+
+<li>
+<b>20 November 2001 -- BusyBox 0.60.2 released</b>
+
+<p>We am very pleased to announce that the BusyBox
+0.60.2 (stable) is now released to the world. This
+one is primarily a bugfix release for the stable
+series, and it should take care of most everyone's
+needs till we can get the nice new stuff we have
+been working on in CVS ready to release (with the
+wonderful new buildsystem). The biggest change in
+this release (beyond bugfixes) is the fact that msh
+(the minix shell) has been re-worked by Vladimir N.
+Oleynik (vodz) and so it no longer crashes when
+told to do complex things with backticks.</p>
+
+<p>This release has been tested on x86, ARM, and
+powerpc using glibc 2.2.4, libc5, and uClibc, so it
+should work with just about any Linux system you
+throw it at. See the <a href=
+"downloads/Changelog">changelog</a> for <small>most
+of</small> the details. The last release was
+<em>very</em> solid for people, and this one should
+be even better.</p>
+
+<p>As usual BusyBox 0.60.2 can be downloaded from
+<a href=
+"downloads">http://www.busybox.net/downloads</a>.</p>
+
+<p>Have Fun.<br>
+ -Erik</p>
+</li>
+
+<li> <b>18 November 2001 -- Help us buy busybox.net!</b>
+
+<!-- Begin PayPal Logo -->
+<center>
+Click here to help buy busybox.net!
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+<input type="hidden" name="cmd" value="_xclick">
+<input type="hidden" name="business" value="andersen@codepoet.org">
+<input type="hidden" name="item_name" value="Support Busybox">
+<input type="hidden" name="image_url" value="https://busybox.net/images/busybox2.jpg">
+<input type="hidden" name="no_shipping" value="1">
+<input type="image" src="images/donate.png" border="0" name="submit" alt="Make donation using PayPal">
+</form>
+</center>
+<!-- End PayPal Logo -->
+
+I've contacted the current owner of busybox.net and he is willing
+to sell the domain name -- for $250.  He also owns busybox.org but
+will not part with it...  I will then need to pay the registry fee
+for a couple of years and start paying for bandwidth, so this will
+initially cost about $300.  I would like to host busybox.net on my
+home machine (codepoet.org) so I have full control over the system,
+but to do that would require that I increase the level of bandwidth
+I am paying for.  Did you know that so far this month, there
+have been over 1.4 Gigabytes of busybox ftp downloads?  I don't
+even <em>know</em> how much CVS bandwidth it requires.  For the
+time being, Lineo has continued to graciously provide this
+bandwidth, despite the fact that I no longer work for them.  If I
+start running this all on my home machine, paying for the needed bandwidth
+will start costing some money.
+<p>
+
+I was going to pay it all myself, but my wife didn't like that
+idea at all (big surprise).   It turns out &lt;insert argument
+where she wins and I don't&gt; she has better ideas
+about what we should spend our money on that don't involve
+busybox.  She suggested I should ask for contributions on the
+mailing list and web page.  So...
+<p>
+
+I am hoping that if everyone could contribute a bit, we could pick
+up the busybox.net domain name and cover the bandwidth costs.  I
+know that busybox is being used by a lot of companies as well as
+individuals -- hopefully people and companies that are willing to
+contribute back a bit.  So if everyone could please help out, that
+would be wonderful!
+<p>
+
+
+<li> <b>23 August 2001 -- BusyBox 0.60.1 released</b>
+<br>
+
+     This is a relatively minor bug fixing release that fixes
+     up the bugs that have shown up in the stable release in
+     the last few weeks.  Fortunately, nothing <em>too</em>
+     serious has shown up.  This release only fixes bugs -- no
+     new features, no new applets.  So without further ado,
+     here it is.  Come and get it.
+     <p>
+     The
+     <a href="downloads/Changelog">changelog</a> has all
+     the details.  As usual BusyBox 0.60.1 can be downloaded from
+     <a href="downloads">http://busybox.net/downloads</a>.
+     <p>Have Fun!
+     <p>
+
+
+<li> <b>2 August 2001 -- BusyBox 0.60.0 released</b>
+<br>
+     I am very pleased to announce the immediate availability of
+     BusyBox 0.60.0.  I have personally tested this release with libc5, glibc,
+     and <a href="http://uclibc.org/">uClibc</a> on
+     x86, ARM, and powerpc using linux 2.2 and 2.4, and I know a number
+     of people using it on everything from ia64 to m68k with great success.
+     Everything seems to be working very nicely now, so getting a nice
+     stable bug-free(tm) release out seems to be in order.   This releases fixes
+     a memory leak in syslogd, a number of bugs in the ash and msh shells, and
+     cleans up a number of things.
+
+     <p>
+
+     Those wanting an easy way to test the 0.60.0 release with uClibc can
+     use <a href="http://user-mode-linux.sourceforge.net/">User-Mode Linux</a>
+     to give it a try by downloading and compiling
+     <a href="ftp://busybox.net/buildroot.tar.gz">buildroot.tar.gz</a>.
+     You don't have to be root or reboot your machine to run test this way.
+     Preconfigured User-Mode Linux kernel source is also on busybox.net.
+     <p>
+     Another cool thing is the nifty <a href="downloads/tutorial/index.html">
+     BusyBox Tutorial</a> contributed by K Computing.  This requires
+     a ShockWave plugin (or standalone viewer), so you may want to grab the
+     the GPLed shockwave viewer from <a href="http://www.swift-tools.com/Flash/flash-0.4.10.tgz">here</a>
+     to view the tutorial.
+     <p>
+
+     Finally, In case you didn't notice anything odd about the
+     version number of this release, let me point out that this release
+     is <em>not</em> 0.53, because I bumped the version number up a
+     bit.  This reflects the fact that this release is intended to form
+     a new stable BusyBox release series.  If you need to rely on a
+     stable version of BusyBox, you should plan on using the stable
+     0.60.x series.  If bugs show up then I will release 0.60.1, then
+     0.60.2, etc...  This is also intended to deal with the fact that
+     the BusyBox build system will be getting a major overhaul for the
+     next release and I don't want that to break products that people
+     are shipping.  To avoid that, the new build system will be
+     released as part of a new BusyBox development series that will
+     have some not-yet-decided-on odd version number.  Once things
+     stabilize and the new build system is working for everyone, then
+     I will release that as a new stable release series.
+
+     <p>
+     The
+     <a href="downloads/Changelog">changelog</a> has all
+     the details.  As usual BusyBox 0.60.0 can be downloaded from
+     <a href="downloads">http://busybox.net/downloads</a>.
+     <p>Have Fun!
+     <p>
+
+
+<li> <b>7 July 2001 -- BusyBox 0.52 released</b>
+<br>
+
+     I am very pleased to announce the immediate availability of
+     BusyBox 0.52 (the "new-and-improved rock-solid release").  This
+     release is the result of <em>many</em> hours of work and has tons
+     of bugfixes, optimizations, and cleanups.  This release adds
+     several new applets, including several new shells (such as hush, msh,
+     and ash).
+
+     <p>
+     The
+     <a href="downloads/Changelog">changelog</a> covers
+     some of the more obvious details, but there are many many things that
+     are not mentioned, but have been improved in subtle ways.  As usual,
+     BusyBox 0.52 can be downloaded from
+     <a href="downloads">http://busybox.net/downloads</a>.
+     <p>Have Fun!
+     <p>
+
+
+<li> <b>10 April 2001 - Graph of Busybox Growth </b>
+<br>
+The illustrious Larry Doolittle has made a PostScript chart of the growth
+of the Busybox tarball size over time. It is available for downloading /
+viewing <a href= "busybox-growth.ps"> right here</a>.
+
+<p> (Note that while the number of applets in Busybox has increased, you
+can still configure Busybox to be as small as you want by selectively
+turning off whichever applets you don't need.)
+<p>
+
+
+<li> <b>10 April 2001 -- BusyBox 0.51 released</b>
+<br>
+
+     BusyBox 0.51 (the "rock-solid release") is now out there.  This
+     release adds only 2 new applets: env and vi.  The vi applet,
+     contributed by Sterling Huxley, is very functional, and is only
+     22k.  This release fixes 3 critical bugs in the 0.50 release.
+     There were 2 potential segfaults in lash (the busybox shell) in
+     the 0.50 release which are now fixed.  Another critical bug in
+     0.50 which is now fixed: syslogd from 0.50 could potentially
+     deadlock the init process and thereby break your entire system.
+     <p>
+
+     There are a number of improvements in this release as well.  For
+     one thing, the wget applet is greatly improved.  Dmitry Zakharov
+     added FTP support, and Laurence Anderson make wget fully RFC
+     compliant for HTTP 1.1.  The mechanism for including utility
+     functions in previous releases was clumsy and error prone.  Now
+     all utility functions are part of a new libbb library, which makes
+     maintaining utility functions much simpler.  And BusyBox now
+     compiles on itanium systems (thanks to the Debian itanium porters
+     for letting me use their system!).
+     <p>
+     You can read the
+     <a href="downloads/Changelog">changelog</a> for
+     complete details.  BusyBox 0.51 can be downloaded from
+     <a href="downloads">http://busybox.net/downloads</a>.
+     <p>Have Fun!
+     <p>
+
+<li> <b>Busybox Boot-Floppy Image</b>
+
+<p>Because you asked for it, we have made available a <a href=
+"downloads/busybox.floppy.img"> Busybox boot floppy
+image</a>. Here's how you use it:
+
+<ol>
+
+    <li> <a href= "downloads/busybox.floppy.img">
+    Download the image</a>
+
+    <li> dd it onto a floppy like so: <tt> dd if=busybox.floppy.img
+    of=/dev/fd0 ; sync </tt>
+
+    <li> Pop it in a machine and boot up.
+
+</ol>
+
+<p> If you want to look at the contents of the initrd image, do this:
+
+<pre>
+    mount ./busybox.floppy.img /mnt -o loop -t msdos
+    cp /mnt/initrd.gz /tmp      
+    umount /mnt
+    gunzip /tmp/initrd.gz
+    mount /tmp/initrd /mnt -o loop -t minix
+</pre>
+
+
+<li> <b>15 March 2001 -- BusyBox 0.50 released</b>
+<br>
+
+     This release adds several new applets including ifconfig, route, pivot_root, stty,
+     and tftp, and also fixes tons of bugs.  Tab completion in the
+     shell is now working very well, and the shell's environment variable
+     expansion was fixed.   Tons of other things were fixed or made
+     smaller.  For a fairly complete overview, see the
+     <a href="downloads/Changelog">changelog</a>.
+     <p>
+     lash (the busybox shell) is still with us, fixed up a bit so it
+     now behaves itself quite nicely.  It really is quite usable as
+     long as you don't expect it to provide Bourne shell grammer.
+     Standard things like pipes, redirects, command line editing, and
+     environment variable expansion work great.  But we have found that
+     this shell, while very usable, does not provide an extensible
+     framework for adding in full Bourne shell behavior.  So the first order of
+     business as we begin working on the next BusyBox release will be to merge in the new shell
+     currently in progress at
+     <a href="http://doolittle.faludi.com/~larry/parser.html">Larry Doolittle's website</a>.
+     <p>
+
+
+<li> <b>27 January 2001 -- BusyBox 0.49 released</b>
+<br>
+
+     Several new applets, lots of bug fixes, cleanups, and many smaller
+     things made nicer.  Several cleanups and improvements to the shell.
+     For a list of the most interesting changes
+     you might want to look at the <a href="downloads/Changelog">changelog</a>.
+     <p>
+     Special thanks go out to Matt Kraai and Larry Doolittle for all their
+     work on this release, and for keeping on top of things while I've been
+     out of town.
+     <p>
+     <em>Special Note</em><br>
+
+     BusyBox 0.49 was supposed to have replaced lash, the BusyBox
+     shell, with a new shell that understands full Bourne shell/Posix shell grammer.
+     Well, that simply didn't happen in time for this release.  A new
+     shell that will eventually replace lash is already under
+     construction.  This new shell is being developed by Larry
+     Doolittle, and could use all of our help.  Please see the work in
+     progress on <a href="http://doolittle.faludi.com/~larry/parser.html">Larry's website</a>
+     and help out if you can.  This shell will be included in the next
+     release of BusyBox.
+     <p>
+
+<li> <b>13 December 2000 -- BusyBox 0.48 released</b>
+<br>
+
+     This release fixes lots and lots of bugs.  This has had some very
+     rigorous testing, and looks very, very clean.  The usual tar
+     update of course: tar no longer breaks hardlinks, tar -xzf is
+     optionally supported, and the LRP folks will be pleased to know
+     that 'tar -X' and 'tar --exclude' are both now in.  Applets are
+     now looked up using a binary search making lash (the busybox
+     shell) much faster.  For the new debian-installer (for Debian
+     woody) a .udeb can now be generated.
+     <p>
+     The curious can get a list of some of the more interesting changes by reading
+     the <a href="downloads/Changelog">changelog</a>.
+     <p>
+     Many thanks go out to the many many people that have contributed to
+     this release, especially Matt Kraai, Larry Doolittle, and Kent Robotti.
+     <p>
+<p> <li> <b>26 September 2000 -- BusyBox 0.47 released</b>
+<br>
+
+     This release fixes lots of bugs (including an ugly bug in 0.46
+     syslogd that could fork-bomb your system).  Added several new
+     apps: rdate, wget, getopt, dos2unix, unix2dos, reset, unrpm,
+     renice, xargs, and expr.  syslogd now supports network logging.
+     There are the usual tar updates.  Most apps now use getopt for
+     more correct option parsing.
+     See the <a href="downloads/Changelog">changelog</a>
+     for complete details.
+
+
+<p> <li> <b>11 July 2000 -- BusyBox 0.46 released</b>
+<br>
+
+     This release fixes several bugs (including a ugly bug in tar,
+     and fixes for NFSv3 mount support).  Added a dumpkmap to allow
+     people to dump a binary keymaps for use with 'loadkmap', and a
+     completely reworked 'grep' and 'sed' which should behave better.
+     BusyBox shell can now also be used as a login shell.
+     See the <a href="downloads/Changelog">changelog</a>
+     for complete details.
+
+
+<p> <li> <b>21 June 2000 -- BusyBox 0.45 released</b>
+<br>
+
+     This release has been slow in coming, but is very solid at this
+     point.  BusyBox now supports libc5 as well as GNU libc.  This
+     release provides the following new apps: cut, tr, insmod, ar,
+     mktemp, setkeycodes, md5sum, uuencode, uudecode, which, and
+     telnet.  There are bug fixes for just about every app as well (see
+     the <a href="downloads/Changelog">changelog</a> for
+     details).
+     <p>
+     Also, some exciting infrastructure news!  Busybox now has its own
+     <a href="lists/busybox/">mailing list</a>,
+     publically browsable
+     <a href="/cgi-bin/cvsweb/busybox/">CVS tree</a>,
+     anonymous
+     <a href="cvs_anon.html">CVS access</a>, and
+     for those that are actively contributing there is even
+     <a href="cvs_write.html">CVS write access</a>.
+     I think this will be a huge help to the ongoing development of BusyBox.
+     <p>
+     Also, for the curious, there is no 0.44 release.  Somehow 0.44 got announced
+     a few weeks ago prior to its actually being released.  To avoid any confusion
+     we are just skipping 0.44.
+     <p>
+     Many thanks go out to the many people that have contributed to this release
+     of BusyBox (esp. Pavel Roskin)!
+
+
+<p> <li> <b>19 April 2000 -- syslogd bugfix</b>
+<br>
+Turns out that there was still a bug in busybox syslogd.
+For example, with the following test app:
+<pre>
+#include &lt;syslog.h&gt;
+
+int do_log(char* msg, int delay)
+{
+    openlog("testlog", LOG_PID, LOG_DAEMON);
+    while(1) {
+       syslog(LOG_ERR, "%s: testing one, two, three\n", msg);
+       sleep(delay);
+    }
+    closelog();
+    return(0);
+};
+
+int main(void)
+{
+    if (fork()==0)
+       do_log("A", 2);
+    do_log("B", 3);
+}
+</pre>
+it should be logging stuff from both "A" and "B".  As released in 0.43 only stuff
+from "A" would have been logged.  This means that if init tries to log something
+while say ppp has the syslog open, init would block (which is bad, bad, bad).
+<p>
+Karl M. Hegbloom has created a fix for the problem.
+Thanks Karl!
+
+
+<p> <li> <b>18 April 2000 -- BusyBox 0.43 released (finally!)</b>
+<br>
+I have finally gotten everything into a state where I feel pretty
+good about things.  This is definitely the most stable, solid release
+so far.  A lot of bugs have been fixed, and the following new apps
+have been added: sh, basename, dirname, killall, uptime,
+freeramdisk, tr, echo, test, and usleep.  Tar has been completely
+rewritten from scratch.  Bss size has also been greatly reduced.
+More details are available in the
+<a href="downloads/Changelog">changelog</a>.
+Oh, and as a special bonus, I wrote some fairly comprehensive
+<em>documentation</em>, complete with examples and full usage information.
+
+<p>
+Many thanks go out to the fine people that have helped by submitting patches
+and bug reports; particularly instrumental in helping for this release were
+Karl Hegbloom, Pavel Roskin, Friedrich Vedder, Emanuele Caratti,
+Bob Tinsley, Nicolas Pitre, Avery Pennarun, Arne Bernin, John Beppu, and Jim Gleason.
+There were others so if I somehow forgot to mention you, I'm very sorry.
+<p>
+
+You can grab BusyBox 0.43 tarballs <a href="downloads">here</a>.
+
+<p> <li> <b>9 April 2000 -- BusyBox 0.43 pre release</b>
+<br>
+Unfortunately, I have not yet finished all the things I want to
+do for BusyBox 0.43, so I am posting this pre-release for people
+to poke at.  This contains my complete rewrite of tar, which now weighs in at
+5k (7k with all options turned on) and works for reading and writing
+tarballs (which it does correctly for everything I have been able to throw
+at it).  Tar also (optionally) supports the "--exclude" option (mainly because
+the Linux Router Project folks asked for it).  This also has a pre-release
+of the micro shell I have been writing.  This pre-release should be stable
+enough for production use -- it just isn't a release since I have some structural
+changes I still want to make.
+<p>
+The pre-release can be found <a href="downloads">here</a>.
+Please let me know ASAP if you find <em>any</em> bugs.
+
+<p> <li> <b>28 March 2000 -- Andersen Baby Boy release</b>
+<br>
+I am pleased to announce that on Tuesday March 28th at 5:48pm, weighing in at 7
+lbs. 12 oz, Micah Erik Andersen was born at LDS Hospital here in Salt Lake City.
+He was born in the emergency room less then 5 minutes after we arrived -- and
+it was such a relief that we even made it to the hospital at all.  Despite the
+fact that I was driving at an amazingly unlawful speed and honking at everybody
+and thinking decidedly unkind thoughts about the people in our way, my wife
+(inconsiderate of my feelings and complete lack of medical training) was lying
+down in the back seat saying things like "I think I need to start pushing now"
+(which she then proceeded to do despite my best encouraging statements to the
+contrary).
+<p>
+Anyway, I'm glad to note that despite the much-faster-than-we-were-expecting
+labor, both Shaunalei and our new baby boy are doing wonderfully.
+<p>
+So now that I am done with my excuse for the slow release cycle...
+Progress on the next release of BusyBox has been slow but steady.  I expect
+to have a release sometime during the first week of April.  This release will
+include a number of important changes, including the addition of a shell, a
+re-write of tar (to accommodate the Linux Router Project), and syslogd can now
+accept multiple concurrent connections, fixing lots of unexpected blocking
+problems.
+
+
+<p> <li> <b>11 February 2000 -- BusyBox 0.42 released</b>
+<br>
+
+     This is the most solid BusyBox release so far.  Many, many
+       bugs have been fixed.   See the
+       <a href="downloads/Changelog">changelog</a> for details.
+
+       Of particular interest, init will now cleanly unmount
+       filesystems on reboot, cp and mv have been rewritten and
+       behave much better, and mount and umount no longer leak
+       loop devices.  Many thanks go out to Randolph Chung,
+       Karl M. Hegbloom, Taketoshi Sano, and Pavel Roskin for
+       their hard work on this release of BusyBox.  Please pound
+       on it and let me know if you find any bugs.
+
+<p> <li> <b>19 January 2000 -- BusyBox 0.41 released</b>
+<br>
+
+     This release includes bugfixes to cp, mv, logger, true, false,
+       mkdir, syslogd, and init.  New apps include wc, hostid,
+       logname, tty, whoami, and yes.  New features include loop device
+       support in mount and umount, and better TERM handling by init.
+       The changelog can be found <a href="downloads/Changelog">here</a>.
+
+<p> <li> <b>7 January 2000 -- BusyBox 0.40 released</b>
+<br>
+
+     This release includes bugfixes to init (now includes inittab support),
+     syslogd, head, logger, du, grep, cp, mv, sed, dmesg, ls, kill, gunzip, and mknod.
+     New apps include sort, uniq, lsmod, rmmod, fbset, and loadacm.
+     In particular, this release fixes an important bug in tar which
+     in some cases produced serious security problems.
+     As always, the changelog can be found <a href="downloads/Changelog">here</a>.
+
+<p> <li> <b>11 December 1999 -- BusyBox Website</b>
+<br>
+     I have received permission from Bruce Perens (the original author of BusyBox)
+       to set up this site as the new primary website for BusyBox.  This website
+       will always contain pointers to the latest and greatest, and will also
+       contain the latest documentation on how to use BusyBox, what it can do,
+       what arguments its apps support, etc.
+
+<p> <li> <b>10 December 1999 -- BusyBox 0.39 released</b>
+<br>
+     This release includes fixes to init, reboot, halt, kill, and ls, and contains
+     the new apps ping, hostname, mkfifo, free, tail, du, tee, and head.  A full
+     changelog can be found <a href="downloads/Changelog">here</a>.
+<p> <li> <b>5 December 1999 -- BusyBox 0.38 released</b>
+<br>
+     This release includes fixes to tar, cat, ls, dd, rm, umount, find, df,
+       and make install, and includes new apps syslogd/klogd and logger.
+
+
+</ul>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/products.html b/busybox/docs/busybox.net/products.html
new file mode 100644 (file)
index 0000000..6ca0e3c
--- /dev/null
@@ -0,0 +1,166 @@
+<!--#include file="header.html" -->
+
+
+<h3>Products/Projects Using BusyBox</h3>
+
+Do you use BusyBox? I'd love to know about it and
+I'd be happy to link to you.
+
+<p>
+I know of the following products and/or projects that use BusyBox --
+listed in the order I happen to add them to the web page:
+
+<ul>
+
+
+<li><a href="/cgi-bin/cvsweb/buildroot/">buildroot</a><br>A configurable
+means for building your own busybox/uClibc based system systems.
+
+<li><a href="http://www.pengutronix.de/software/ptxdist_en.html">PTXdist</a><br>another
+configurable means for building your own busybox based system systems.
+
+</li><li><a href=
+"http://cvs.debian.org/boot-floppies/">
+Debian installer (boot floppies) project</a>
+
+</li><li><a href="http://redhat.com/">Red Hat installer</a>
+
+</li><li><a href=
+"http://distro.ibiblio.org/pub/Linux/distributions/slackware/slackware-current/source/rootdisks/">
+Slackware Installer</a>
+
+</li><li><a href="http://www.gentoo.org/">Gentoo Linux install/boot CDs</a>
+</li><li><a href="http://www.mandrake.com/">The Mandrake installer</a>
+
+</li><li><a href="http://Leaf.SourceForge.net">Linux Embedded Appliance Firewall</a><br>The sucessor of the Linux Router Project, supporting all sorts of embedded Linux gateways, routers, wireless routers, and firewalls.
+
+</li><li><a href=
+"http://www.toms.net/rb/">tomsrtbt</a>
+
+</li><li><a href="http://www.stormix.com/">Stormix
+Installer</a>
+
+</li><li><a href=
+"http://www.emacinc.com/linux2_sbc.htm">EMAC Linux
+2.0 SBC</a>
+
+</li><li><a href="http://www.trinux.org/">Trinux</a>
+
+</li><li><a href="http://oddas.sourceforge.net/">ODDAS
+project</a>
+
+</li><li><a href="http://byld.sourceforge.net/">Build Your
+Linux Disk</a>
+
+</li><li><a href=
+"http://ibiblio.org/pub/Linux/system/recovery">Zdisk</a>
+
+</li><li><a href="http://www.adtran.com">AdTran -
+VPN/firewall VPN Linux Distribution</a>
+
+</li><li><a href="http://mkcdrec.ota.be/">mkCDrec - make
+CD-ROM recovery</a>
+
+</li><li><a href=
+"http://recycle.lbl.gov/~ldoolitt/bse/">Linux on
+nanoEngine</a>
+
+</li><li><a href=
+"http://www.zelow.no/floppyfw/">Floppyfw</a>
+
+</li><li><a href="http://www.ltsp.org/">Linux Terminal
+Server Project</a>
+
+</li><li><a href="http://www.devil-linux.org/">Devil-Linux</a>
+
+</li><li><a href="http://dutnux.sourceforge.net/">DutNux</a>
+
+</li><li><a href="http://www.microwerks.net/~hugo/mindi/">Mindi</a>
+
+</li><li><a href="http://www.minimalinux.org/ttylinux/">ttylinux</a>
+
+</li><li><a href="http://www.coyotelinux.com/">Coyote Linux</a>
+
+</li><li><a href="http://www.partimage.org/">Partition
+Image</a>
+
+</li><li><a href="http://www.fli4l.de/">fli4l the on(e)-disk-router</a>
+
+</li><li><a href="http://tinfoilhat.cultists.net/">Tinfoil
+Hat Linux</a>
+
+</li><li><a href="http://sourceforge.net/projects/gp32linux/">gp32linux</a>
+</li><li><a href="http://familiar.handhelds.org/">Familiar Linux</a><br>A linux distribution for handheld computers
+</li><li><a href="http://rescuecd.sourceforge.net/">Timo's Rescue CD Set</a>
+</li><li><a href="http://sf.net/projects/netstation/">Netstation</a>
+</li><li><a href="http://www.fiwix.org/">GNU/Fiwix Operating System</a>
+</li><li><a href="http://www.softcraft.com/">Generations Linux</a>
+</li><li><a href="http://systemimager.org/relatedprojects/">SystemImager / System Installation Suite</a>
+</li><li><a href="http://www.bablokb.de/gendist/">GENDIST distribution generator</a>
+</li><li><a href="http://diet-pc.sourceforge.net/">DIET-PC embedded Linux thin client distribution</a>
+</li><li><a href="http://byzgl.sourceforge.net/">BYZantine Gnu/Linux</a>
+</li><li><a href="http://dban.sourceforge.net/">Darik's Boot and Nuke</a>
+</li><li><a href="http://www.timesys.com/">TimeSys real-time Linux</a>
+</li><li><a href="http://movix.sf.net/">MoviX</a><br>Boots from CD and automatically plays every video file on the CD
+</li><li><a href="http://katamaran.sourceforge.net">katamaran</a><br>Linux, X11, xfce windowmanager, based on BusyBox
+</li><li><a href="http://www.sourceforge.net/projects/simplygnustep">Prometheus SimplyGNUstep</a>
+</li><li><a href="http://www.renyi.hu/~ekho/lowlife/">lowlife</a><br>A documentation project on how to make your own uClibc-based systems and floppy.
+</li><li><a href="http://metadistros.hispalinux.es/">Metadistros</a><br>a project to allow you easily make Live-CD distributions.
+</li><li><a href="http://salvare.sourceforge.net/">Salvare</a><br>More Linux than tomsrtbt but less than Knoppix, aims to provide a useful workstation as well as a rescue disk.
+</li><li><a href="http://www.stresslinux.org/">stresslinux</a><br>minimal linux distribution running from a bootable cdrom or via PXE.
+</li><li><a href="http://thinstation.sourceforge.net/">thinstation</a><br>convert standard PCs into full-featured diskless thinclients.
+</li><li><a href="http://www.uhulinux.hu/">UHU-Linux Hungary</a>
+</li><li><a href="http://deep-water.berlios.de/">Deep-Water Linux</a>
+</li><li><a href="http://www.freesco.org/">Freesco router</a>
+</li><li><a href="http://Sentry.SourceForge.net/">Sentry Firewall CD</a>
+
+
+
+</li><li><a href="http://tuxscreen.net">Tuxscreen Linux Phone</a>
+</li><li><a href="http://www.kerbango.com/">The Kerbango Internet Radio</a>
+</li><li><a href="http://www.linuxmagic.com/vpn/">LinuxMagic VPN Firewall</a>
+</li><li><a href="http://www.isilver-inc.com/">I-Silver Linux appliance servers</a>
+</li><li><a href="http://zaurus.sourceforge.net/">Sharp Zaurus PDA</a>
+</li><li><a href="http://www.cyclades.com/">Cyclades-TS and other Cyclades products</a>
+</li><li><a href="http://www.linksys.com/products/product.asp?prid=508">Linksys WRT54G - Wireless-G Broadband Router</a>
+</li><li><a href="http://www.dell.com/us/en/biz/topics/sbtopic_005_truemobile.htm">Dell TrueMobile 1184</a>
+</li><li><a href="http://actiontec.com/products/modems/dual_pcmodem/dpm_overview.html">Actiontec Dual PC Modem</a>
+</li><li><a href="http://www.kiss-technology.com/">Kiss DP Series DVD players</a>
+</li><li><a href="http://www.netgear.com/products/prod_details.asp?prodID=170">NetGear WG602 wireless router</a>
+    <br>with sources <a href="http://www.netgear.com/support/support_details.asp?dnldID=453">here</a>
+</li><li><a href="http://www.trendware.com/products/TEW-411BRP.htm">TRENDnet TEW-411BRP 802.11g Wireless AP/Router/Switch</a>
+    <br>Source for busybox and udhcp <a href="http://www.trendware.com/asp/download/fileinfo.asp?file_id=277&B1=Search">here</a> though no kernel source is provided.
+</li><li><a href="http://www.buffalo-technology.com/webcontent/products/wireless/wbr-g54.htm">Buffalo WBR-G54 wireless router</a>
+  </li><li><a href="http://www.asus.com/products/communication/wireless/wl-300g/overview.htm">ASUS WL-300g Wireless LAN Access Point</a>
+    <br>with source<a href="http://www.asus.com.tw/support/download/item.aspx?ModelName=WL-300G">here</a>
+  </li><li><a href="http://catalog.belkin.com/IWCatProductPage.process?Merchant_Id=&Section_Id=201522&pcount=&Product_Id=136493">Belkin 54g Wireless DSL/Cable Gateway Router</a>
+    <br>with source<a href="http://web.belkin.com/support/gpl.asp">here</a>
+  <li><a href="http://www.acronis.com/products/partitionexpert/">Acronis PartitionExpert 2003</a>
+       <br>includes a heavily modified BusyBox v0.60.5 with built in
+       cardmgr, device detection, gpm, lspci, etc.  Also includes udhcp,
+       uClibc 0.9.26, a heavily patched up linux kernel, etc.  Source
+       can only be obtained <a href="http://www.acronis.com/files/gpl/linux.tar.bz2">here</a>
+
+</li><li><a href="http://www.usr.com/">U.S. Robotics Sureconnect 4-port ADSL router</a><br>
+    with source <a href="http://www.usr.com/support/s-gpl-code.asp">here</a>
+</li><li><a href="http://www.actiontec.com/products/broadband/54mbps_wireless_gateway_1p/index.html">
+    ActionTec GT701-WG Wireless Gateway/DSL Modem</a>
+    with source <a href="http://128.121.226.214/gtproducts/index.html">here</a>
+</li><li><a href="http://smartlinux.sourceforge.net/">S.M.A.R.T. Linux</a>
+</li><li><a href="http://www.dlink.com/">DLink - Model GSL-G604T, DSL-300T, and possibly other models</a>
+    with source <a href="ftp://ftp.dlink.co.uk/dsl_routers_modems/">here,</a>
+    with source <a href="ftp://ftp.dlink.de/dsl-products/">and here,</a>
+    and quite possibly other places as well.  You may need to dig down a bit
+    to find the source, but it does seem to be there.
+</li><li><a href="http://www.siemens-mobile.de/cds/frontdoor/0,2241,de_de_0_42931_rArNrNrNrN,00.html">Siemens SE515 DSL router</a>
+    with source <a href="http://now-portal.c-lab.de/projects/gigaset/">here, I think...</a>
+    with some details <a href="http://heinz.hippenstiel.org/familie/hp/hobby/gigaset_se515dsl.html">here.</a>
+</li><li><a href="http://frwt.stim.ru/">Free Remote Windows Terminal</a>
+    
+
+</li>
+</ul>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/screenshot.html b/busybox/docs/busybox.net/screenshot.html
new file mode 100644 (file)
index 0000000..9c05791
--- /dev/null
@@ -0,0 +1,57 @@
+<!--#include file="header.html" -->
+
+
+<!-- Begin Screenshot -->
+
+<h3> Busybox Screenshot! </h3>
+
+
+Everybody loves to look at screenshots, so here is a live action screenshot of BusyBox.
+
+<pre style="background-color: black; color: lightgreen; padding: 5px;
+font-family: monospace; font-size: smaller;" width="100">
+
+
+$ ./busybox
+BusyBox v1.00 (2004.10.13-04:49+0000) multi-call binary
+
+Usage: busybox [function] [arguments]...
+   or: [function] [arguments]...
+
+        BusyBox is a multi-call binary that combines many common Unix
+        utilities into a single executable.  Most people will create a
+        link to busybox for each function they wish to use, and BusyBox
+        will act like whatever it was invoked as.
+
+Currently defined functions:
+
+       [, addgroup, adduser, adjtimex, ar, arping, ash, awk, basename, bunzip2,
+       busybox, bzcat, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp,
+       cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt, delgroup, deluser,
+       devfsd, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap,
+       dumpleases, echo, egrep, env, expr, false, fbset, fdflush, fdformat, fdisk,
+       fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt,
+       getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname,
+       httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod,
+       install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall,
+       klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login,
+       logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir,
+       mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt,
+       mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping,
+       ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate,
+       readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm,
+       rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort,
+       start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync,
+       sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top,
+       touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname,
+       uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode,
+       vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs,
+       yes, zcat
+
+
+$ <blink>_</blink>
+
+</pre>
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox.net/shame.html b/busybox/docs/busybox.net/shame.html
new file mode 100644 (file)
index 0000000..99807c1
--- /dev/null
@@ -0,0 +1,77 @@
+<!--#include file="header.html" -->
+
+
+<h3>Hall of Shame!!!</h3>
+
+The following products and/or projects appear to use BusyBox, but do not
+appear to release source code as required by the <a
+href="/license.html">BusyBox license</a>.  This is a violation of the law!
+The distributors of these products are invited to contact <a href=
+"mailto:andersen@codepoet.org">Erik Andersen</a> if they have any confusion
+as to what is needed to bring their products into compliance, or if they have
+already brought their product into compliance and wish to be removed from the
+Hall of Shame.
+
+<p>
+
+Here are the details of <a href="/license.html">exactly how to comply
+with the BusyBox license</a>, so there should be no question as to
+exactly what is expected.
+Complying with the Busybox license is easy and completely free, so the
+companies listed below should be ashamed of themselves.  Furthermore, each
+product listed here is subject to being legally ordered to cease and desist
+distribution for violation of copyright law, and the distributor of each
+product is subject to being sued for statutory copyright infringement damages
+of up to $150,000 per work plus legal fees.  Nobody wants to be sued, and <a
+href="mailto:andersen@codepoet.org">Erik</a> certainly would prefer to spend
+his time doing better things than sue people.  But he will sue if forced to
+do so to maintain compliance.
+
+<p>
+
+Do everyone a favor and don't break the law -- if you use busybox, comply with
+the busybox license by releasing the source code with your product.
+
+<p>
+
+<ul>
+
+  <li><a href="http://www.trittontechnologies.com/products.html">Tritton Technologies NAS120</a>
+       <br>see <a href="http://www.ussg.iu.edu/hypermail/linux/kernel/0404.0/1611.html">here for details</a>
+  <li><a href="http://www.macsense.com/product/homepod/">Macsense HomePod</a>
+       <br>with details
+       <a href="http://developer.gloolabs.com/modules.php?op=modload&name=Forums&file=viewtopic&topic=123&forum=7">here</a>
+  <li><a href="http://www.cpx.com/products.asp?c=Wireless+Products">Compex Wireless Products</a>
+    <br>appears to be running v0.60.5 with Linux version 2.4.20-uc0 on ColdFire,
+    but no source code is mentioned or offered.
+  <li><a href="http://www.inventel.com/en/product/datasheet/10/">Inventel DW 200 wireless/ADSL router</a>
+  <li><a href="http://www.sweex.com/product.asp">Sweex DSL router</a>
+    <br>appears to be running BusyBox v1.00-pre2 and udhcpd, but no source
+       code is mentioned or offered.
+  <li><a href="http://www.trendware.com/products/TEW-410APB.htm">TRENDnet TEW-410APB</a>
+  </li><li><a href="http://www.hauppauge.com/Pages/products/data_mediamvp.html">Hauppauge Media MVP</a>
+  <br>Hauppauge contacted me on 16 Dec 2003, and claims to be working on resolving this problem.
+  </li><li><a href="http://www.hitex.com/download/adescom/data/">TriCore</a>
+  </li><li><a href="http://www.allnet.de/">ALLNET 0186 wireless router</a>
+  </li><li><a href="http://www.dmmtv.com/">Dreambox DM7000S DVB Satellite Receiver</a>
+  <br> Dream Multimedia contacted me on 22 Dec 2003 and is working on resolving this problem.
+  <br> Source _may_ be here: http://cvs.tuxbox.org/cgi-bin/viewcvs.cgi/tuxbox/cdk/
+  </li><li><a href="http://testing.lkml.org/slashdot.php?mid=331690">Sigma Designs EM8500 based DVD players</a>
+  <br>Source for the Sigma Designs reference platform is found here<br>
+    <a href="http://www.uclinux.org/pub/uClinux/ports/arm/EM8500/uClinux-2.4-sigma.tar.gz">uClinux-2.4-sigma.tar.gz</a>, so while Sigma Designs itself appears to be in compliance, as far as I can tell,
+    no vendors of Sigma Designs EM8500 based devices actually comply with the GPL....
+  </li><li><a href="http://testing.lkml.org/slashdot.php?mid=433790">Liteon LVD2001 DVD player using the Sigma Designs EM8500</a>
+  </li><li><a href="http://www.rimax.net/">Rimax DVD players using the Sigma Designs EM8500</a>
+  </li><li><a href="http://www.vinc.us/">Bravo DVD players using the Sigma Designs EM8500</a>
+  </li><li><a href="http://www.hb-direct.com/">H&B DX3110 Divx player based on Sigma Designs EM8500</a>
+  </li><li><a href="http://www.recospa.it/mdpro1/index.php">United *DVX4066 mpeg4 capable DVD players</a>
+  </li><li><a href="http://www.a-link.com/RR64AP.html">Avaks alink Roadrunner 64</a>
+  <br> Partial source available, based on source distributed under NDA from <a href="http://www.lsilogic.com/products/dsl_platform_solutions/hb_linuxr2_2.html"> LSILogic</a>. Why the NDA LSILogic, what are you hiding ?
+  <br>To verify the Avaks infrigment see my slashdot <a href="http://slashdot.org/~bug1/journal/">journal</a>. 
+  </li><li>Undoubtedly there are others...  Please report them so we can shame them (or if necessary sue them) into compliance.
+
+</ul>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/busybox/docs/busybox_footer.pod b/busybox/docs/busybox_footer.pod
new file mode 100644 (file)
index 0000000..f965711
--- /dev/null
@@ -0,0 +1,258 @@
+=back
+
+=head1 LIBC NSS
+
+GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior
+of the C library for the local environment, and to configure how it reads
+system data, such as passwords and group information.  This is implemented
+using an /etc/nsswitch.conf configuration file, and using one or more of the
+/lib/libnss_* libraries.  BusyBox tries to avoid using any libc calls that make
+use of NSS.  Some applets however, such as login and su, will use libc functions
+that require NSS.
+
+If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to
+directly access the /etc/passwd, /etc/group, and /etc/shadow files without
+using NSS.  This may allow you to run your system without the need for
+installing any of the NSS configuration files and libraries.
+
+When used with glibc, the BusyBox 'networking' applets will similarly require
+that you install at least some of the glibc NSS stuff (in particular,
+/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*).
+
+Shameless Plug: As an alternative, one could use a C library such as uClibc.  In
+addition to making your system significantly smaller, uClibc does not require the
+use of any NSS support files or libraries.
+
+=head1 MAINTAINER
+
+Erik Andersen <andersen@codepoet.org>
+
+=head1 AUTHORS
+
+The following people have contributed code to BusyBox whether they know it or
+not.  If you have written code included in BusyBox, you should probably be
+listed here so you can obtain your bit of eternal glory.  If you should be
+listed here, or the description of what you have done needs more detail, or is
+incorect, please send in an update.
+
+
+=for html <br>
+
+Emanuele Aina <emanuele.aina@tiscali.it>
+       run-parts
+
+=for html <br>
+
+Erik Andersen <andersen@codepoet.org>
+
+    Tons of new stuff, major rewrite of most of the
+    core apps, tons of new apps as noted in header files.
+    Lots of tedious effort writing these boring docs that
+    nobody is going to actually read.
+
+=for html <br>
+
+Laurence Anderson <l.d.anderson@warwick.ac.uk>
+
+    rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
+
+=for html <br>
+
+Jeff Angielski <jeff@theptrgroup.com>
+
+    ftpput, ftpget
+
+=for html <br>
+
+Edward Betts <edward@debian.org>
+
+    expr, hostid, logname, whoami
+
+=for html <br>
+
+John Beppu <beppu@codepoet.org>
+
+    du, nslookup, sort
+
+=for html <br>
+
+Brian Candler <B.Candler@pobox.com>
+
+    tiny-ls(ls)
+
+=for html <br>
+
+Randolph Chung <tausq@debian.org>
+
+    fbset, ping, hostname
+
+=for html <br>
+
+Dave Cinege <dcinege@psychosis.com>
+
+    more(v2), makedevs, dutmp, modularization, auto links file,
+    various fixes, Linux Router Project maintenance
+
+=for html <br>
+
+Jordan Crouse <jordan@cosmicpenguin.net>
+
+       ipcalc
+
+=for html <br>
+
+Magnus Damm <damm@opensource.se>
+
+    tftp client insmod powerpc support
+
+=for html <br>
+
+Larry Doolittle <ldoolitt@recycle.lbl.gov>
+
+    pristine source directory compilation, lots of patches and fixes.
+
+=for html <br>
+
+Glenn Engel <glenne@engel.org>
+
+    httpd
+
+=for html <br>
+
+Gennady Feldman <gfeldman@gena01.com>
+
+    Sysklogd (single threaded syslogd, IPC Circular buffer support,
+    logread), various fixes.
+
+=for html <br>
+
+Karl M. Hegbloom <karlheg@debian.org>
+
+    cp_mv.c, the test suite, various fixes to utility.c, &c.
+
+=for html <br>
+
+Daniel Jacobowitz <dan@debian.org>
+
+    mktemp.c
+
+=for html <br>
+
+Matt Kraai <kraai@alumni.cmu.edu>
+
+    documentation, bugfixes, test suite
+
+=for html <br>
+
+Stephan Linz <linz@li-pro.net>
+
+       ipcalc, Red Hat equivalence
+
+=for html <br>
+
+John Lombardo <john@deltanet.com>
+
+    tr
+
+=for html <br>
+
+Glenn McGrath <bug1@iinet.net.au>
+
+    Common unarchving code and unarchiving applets, ifupdown, ftpgetput,
+    nameif, sed, patch, fold, install, uudecode.
+    Various bugfixes, review and apply numerous patches.
+
+=for html <br>
+
+Manuel Novoa III <mjn3@codepoet.org>
+
+    cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+    mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
+    get_last_path_component, simplify_path, and a number trivial libbb routines
+
+    also bug fixes, partial rewrites, and size optimizations in
+    ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
+    mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
+    interface, dutmp, ifconfig, route
+
+=for html <br>
+
+Vladimir Oleynik <dzo@simtreas.ru>
+
+    cmdedit; xargs(current), httpd(current);
+    ports: ash, crond, fdisk, inetd, stty, traceroute, top;
+    locale, various fixes
+    and irreconcilable critic of everything not perfect.
+
+=for html <br>
+
+Bruce Perens <bruce@pixar.com>
+
+    Original author of BusyBox in 1995, 1996. Some of his code can
+    still be found hiding here and there...
+
+=for html <br>
+
+Tim Riker <Tim@Rikers.org>
+
+    bug fixes, member of fan club
+
+=for html <br>
+
+Kent Robotti <robotti@metconnect.com>
+
+    reset, tons and tons of bug reports and patches.
+
+=for html <br>
+
+Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
+
+    wget - Contributed by permission of Covad Communications
+
+=for html <br>
+
+Pavel Roskin <proski@gnu.org>
+
+    Lots of bugs fixes and patches.
+
+=for html <br>
+
+Gyepi Sam <gyepi@praxis-sw.com>
+
+    Remote logging feature for syslogd
+
+=for html <br>
+
+Linus Torvalds <torvalds@transmeta.com>
+
+    mkswap, fsck.minix, mkfs.minix
+
+=for html <br>
+
+Mark Whitley <markw@codepoet.org>
+
+    grep, sed, cut, xargs(previous),
+    style-guide, new-applet-HOWTO, bug fixes, etc.
+
+=for html <br>
+
+Charles P. Wright <cpwright@villagenet.com>
+
+    gzip, mini-netcat(nc)
+
+=for html <br>
+
+Enrique Zanardi <ezanardi@ull.es>
+
+    tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+=for html <br>
+
+Tito Ragusa <farmatito@tiscali.it>
+
+       devfsd and size optimizations in strings, openvt and deallocvt.
+
+=cut
+
+# $Id: busybox_footer.pod,v 1.18 2004/04/25 06:05:14 bug1 Exp $
+
diff --git a/busybox/docs/busybox_header.pod b/busybox/docs/busybox_header.pod
new file mode 100644 (file)
index 0000000..35631b8
--- /dev/null
@@ -0,0 +1,111 @@
+# vi: set sw=4 ts=4:
+
+=head1 NAME
+
+BusyBox - The Swiss Army Knife of Embedded Linux
+
+=head1 SYNTAX
+
+ BusyBox <function> [arguments...]  # or
+
+ <function> [arguments...]         # if symlinked
+
+=head1 DESCRIPTION
+
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides minimalist replacements for most of the utilities
+you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox
+generally have fewer options than their full-featured GNU cousins; however, the
+options that are included provide the expected functionality and behave very
+much like their GNU counterparts.
+
+BusyBox has been written with size-optimization and limited resources in mind.
+It is also extremely modular so you can easily include or exclude commands (or
+features) at compile time. This makes it easy to customize your embedded
+systems. To create a working system, just add /dev, /etc, and a Linux kernel.
+BusyBox provides a fairly complete POSIX environment for any small or embedded
+system.
+
+BusyBox is extremely configurable.  This allows you to include only the
+components you need, thereby reducing binary size. Run 'make config' or 'make
+menuconfig' to select the functionality that you wish to enable.  The run
+'make' to compile BusyBox using your configuration.
+
+After the compile has finished, you should use 'make install' to install
+BusyBox.  This will install the '/bin/busybox' binary, and will also create
+symlinks pointing to the '/bin/busybox' binary for each utility that you
+compile into BusyBox.  By default, 'make install' will place these symlinks
+into the './_install' directory, unless you have defined 'PREFIX', thereby
+specifying some alternative location (i.e., 'make PREFIX=/tmp/foo install').
+If you wish to install using hardlinks, rather than the default of using
+symlinks, you can use 'make PREFIX=/tmp/foo install-hardlinks' instead.
+
+=head1 USAGE
+
+BusyBox is a multi-call binary.  A multi-call binary is an executable program
+that performs the same job as more than one utility program.  That means there
+is just a single BusyBox binary, but that single binary acts like a large
+number of utilities.  This allows BusyBox to be smaller since all the built-in
+utility programs (we call them applets) can share code for many common operations.
+
+You can also invoke BusyBox by issuing a command as an argument on the
+command line.  For example, entering
+
+       /bin/busybox ls
+
+will also cause BusyBox to behave as 'ls'.
+
+Of course, adding '/bin/busybox' into every command would be painful.  So most
+people will invoke BusyBox using links to the BusyBox binary.
+
+For example, entering
+
+       ln -s /bin/busybox ls
+       ./ls
+
+will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled
+into BusyBox).  Generally speaking, you should never need to make all these
+links yourself, as the BusyBox build system will do this for you when you run
+the 'make install' command.
+
+If you invoke BusyBox with no arguments, it will provide you with a list of the
+applets that have been compiled into your BusyBox binary.
+
+=head1 COMMON OPTIONS
+
+Most BusyBox commands support the B<--help> argument to provide a terse runtime
+description of their behavior.  If the CONFIG_FEATURE_VERBOSE_USAGE option has
+been enabled, more detailed usage information will also be available.
+
+=head1 COMMANDS
+
+Currently defined functions include:
+
+       addgroup, adduser, adjtimex, ar, arping, ash, awk, basename, bunzip2,
+       busybox, bzcat, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp,
+       cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt, delgroup, deluser,
+       devfsd, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap,
+       dumpleases, echo, egrep, env, expr, false, fbset, fdflush, fdformat, fdisk,
+       fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt,
+       getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname,
+       httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod,
+       install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall,
+       klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login,
+       logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir,
+       mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt,
+       mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping,
+       ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate,
+       readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm,
+       rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort,
+       start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync,
+       sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top,
+       touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname,
+       uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode,
+       vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs,
+       yes, zcat
+
+=head1 COMMAND DESCRIPTIONS
+
+=over 4
+
+
diff --git a/busybox/docs/contributing.txt b/busybox/docs/contributing.txt
new file mode 100644 (file)
index 0000000..e80fc13
--- /dev/null
@@ -0,0 +1,449 @@
+Contributing To Busybox
+=======================
+
+This document describes what you need to do to contribute to Busybox, where
+you can help, guidelines on testing, and how to submit a well-formed patch
+that is more likely to be accepted.
+
+The Busybox home page is at: http://busybox.net/
+
+
+
+Pre-Contribution Checklist
+--------------------------
+
+So you want to contribute to Busybox, eh? Great, wonderful, glad you want to
+help. However, before you dive in, headlong and hotfoot, there are some things
+you need to do:
+
+
+Checkout the Latest Code from CVS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is a necessary first step. Please do not try to work with the last
+released version, as there is a good chance that somebody has already fixed
+the bug you found. Somebody might have even added the feature you had in mind.
+Don't make your work obsolete before you start!
+
+For information on how to check out Busybox from CVS, please look at the
+following links:
+
+       http://busybox.net/cvs_anon.html
+       http://busybox.net/cvs_howto.html
+
+
+Read the Mailing List
+~~~~~~~~~~~~~~~~~~~~~
+
+No one is required to read the entire archives of the mailing list, but you
+should at least read up on what people have been talking about lately. If
+you've recently discovered a problem, chances are somebody else has too. If
+you're the first to discover a problem, post a message and let the rest of us
+know.
+
+Archives can be found here:
+
+       http://busybox.net/lists/busybox/
+
+If you have a serious interest in Busybox, i.e., you are using it day-to-day or
+as part of an embedded project, it would be a good idea to join the mailing
+list.
+
+A web-based sign-up form can be found here:
+
+       http://busybox.net/mailman/listinfo/busybox
+
+
+Coordinate with the Applet Maintainer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some (not all) of the applets in Busybox are "owned" by a maintainer who has
+put significant effort into it and is probably more familiar with it than
+others. To find the maintainer of an applet, look at the top of the .c file
+for a name following the word 'Copyright' or 'Written by' or 'Maintainer'.
+
+Before plunging ahead, it's a good idea to send a message to the mailing list
+that says: "Hey, I was thinking about adding the 'transmogrify' feature to the
+'foo' applet.  Would this be useful? Is anyone else working on it?" You might
+want to CC the maintainer (if any) with your question.
+
+
+
+Areas Where You Can Help
+------------------------
+
+Busybox can always use improvement! If you're looking for ways to help, there
+are a variety of areas where you could help.
+
+
+What Busybox Doesn't Need
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before listing the areas where you _can_ help, it's worthwhile to mention the
+areas where you shouldn't bother. While Busybox strives to be the "Swiss Army
+Knife" of embedded Linux, there are some applets that will not be accepted:
+
+ - Any filesystem manipulation tools: Busybox is filesystem independent and
+   we do not want to start adding mkfs/fsck tools for every (or any)
+   filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on
+   borrowed time.) There are far too many of these tools out there.  Use
+   the upstream version. Not everything has to be part of Busybox.
+
+ - Any partitioning tools: Partitioning a device is typically done once and
+   only once, and tools which do this generally do not need to reside on the
+   target device (esp a flash device). If you need a partitioning tool, grab
+   one (such as fdisk, sfdisk, or cfdisk from util-linux) and use that, but
+   don't try to merge it into busybox. These are nasty and complex and we
+   don't want to maintain them.
+
+ - Any disk, device, or media-specific tools: Use the -utils or -tools package
+   that was designed for your device; don't try to shoehorn them into Busybox.
+
+ - Any architecture specific tools: Busybox is (or should be) architecture
+   independent. Do not send us tools that cannot be used across multiple
+   platforms / arches.
+
+ - Any daemons that are not essential to basic system operation. To date, only
+   syslogd and klogd meet this requirement. We do not need a web server, an
+   ftp daemon, a dhcp server, a mail transport agent or a dns resolver. If you
+   need one of those, you are welcome to ask the folks on the mailing list for
+   recommendations, but please don't bloat up Busybox with any of these.
+
+
+Bug Reporting
+~~~~~~~~~~~~~
+
+If you find bugs, please submit a detailed bug report to the busybox mailing
+list at busybox@busybox.net.  A well-written bug report should include a
+transcript of a shell session that demonstrates the bad behavior and enables
+anyone else to duplicate the bug on their own machine. The following is such
+an example:
+
+    To: busybox@busybox.net
+    From: diligent@testing.linux.org
+    Subject: /bin/date doesn't work
+
+    Package: busybox
+    Version: 1.00
+
+    When I execute Busybox 'date' it produces unexpected results.
+    With GNU date I get the following output:
+
+       $ date
+       Wed Mar 21 14:19:41 MST 2001
+
+    But when I use BusyBox date I get this instead:
+
+       $ date
+       llegal instruction
+
+    I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder,
+    and the latest uClibc from CVS.  Thanks for the wonderful program!
+
+       -Diligent
+
+Note the careful description and use of examples showing not only what BusyBox
+does, but also a counter example showing what an equivalent GNU app does.  Bug
+reports lacking such detail may never be fixed...  Thanks for understanding.
+
+
+
+Write Documentation
+~~~~~~~~~~~~~~~~~~~
+
+Chances are, documentation in Busybox is either missing or needs improvement.
+Either way, help is welcome.
+
+Work is being done to automatically generate documentation from sources,
+especially from the usage.h file. If you want to correct the documentation,
+please make changes to the pre-generation parts, rather than the generated
+documentation. [More to come on this later...]
+
+It is preferred that modifications to documentation be submitted in patch
+format (more on this below), but we're a little more lenient when it comes to
+docs. You could, for example, just say "after the listing of the mount
+options, the following example would be helpful..."
+
+
+Consult Existing Sources
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+For a quick listing of "needs work" spots in the sources, cd into the Busybox
+directory and run the following:
+
+       for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done
+
+This will show all of the trouble spots or 'questionable' code. Pick a spot,
+any spot, these are all invitations for you to contribute.
+
+
+Add a New Applet
+~~~~~~~~~~~~~~~~
+
+If you want to add a new applet to Busybox, we'd love to see it. However,
+before you write any code, please ask beforehand on the mailing list something
+like "Do you think applet 'foo' would be useful in Busybox?" or "Would you
+guys accept applet 'foo' into Busybox if I were to write it?" If the answer is
+"no" by the folks on the mailing list, then you've saved yourself some time.
+Conversely, you could get some positive responses from folks who might be
+interested in helping you implement it, or can recommend the best approach.
+Perhaps most importantly, this is your way of calling "dibs" on something and
+avoiding duplication of effort.
+
+Also, before you write a line of code, please read the 'new-applet-HOWTO.txt'
+file in the docs/ directory.
+
+
+Janitorial Work
+~~~~~~~~~~~~~~~
+
+These are dirty jobs, but somebody's gotta do 'em.
+
+ - Converting applets to use getopt() for option processing. Type 'find -name
+   '*.c'|grep -L getopt' to get a listing of the applets that currently don't
+   use getopt. If a .c file processes no options, it should have a line that
+   reads: /* no options, no getopt */ somewhere in the file.
+
+ - Replace any "naked" calls to malloc, calloc, realloc, str[n]dup, fopen with
+   the x* equivalents found in libbb/xfuncs.c.
+
+ - Security audits:
+   http://www.securityfocus.com/popups/forums/secprog/intro.shtml
+
+ - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This
+   is very Perl-specific, but the advice given in here applies equally well to
+   C.
+
+ - C library function use audits: Verifying that functions are being used
+   properly (called with the right args), replacing unsafe library functions
+   with safer versions, making sure return codes are being checked, etc.
+
+ - Where appropriate, replace preprocessor defined macros and values with
+   compile-time equivalents.
+
+ - Style guide compliance. See: docs/style-guide.txt
+
+ - Add testcases to tests/testcases.
+
+ - Makefile improvements:
+   http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html
+   (I think the recursive problems are pretty much taken care of at this point, non?)
+
+ - "Ten Commandments" compliance: (this is a "maybe", certainly not as
+   important as any of the previous items.)
+    http://www.lysator.liu.se/c/ten-commandments.html
+
+Other useful links:
+
+ - the comp.lang.c FAQ: http://web.onetelnet.ch/~twolf/tw/c/index.html#Sources
+
+
+
+Submitting Patches To Busybox
+-----------------------------
+
+Here are some guidelines on how to submit a patch to Busybox.
+
+
+Making A Patch
+~~~~~~~~~~~~~~
+
+If you've got anonymous CVS access set up, making a patch is simple. Just make
+sure you're in the busybox/ directory and type 'cvs diff -bwu > mychanges.patch'.
+You can send the resulting .patch file to the mailing list with a description
+of what it does. (But not before you test it! See the next section for some
+guidelines.) It is preferred that patches be sent as attachments, but it is
+not required.
+
+Also, feel free to help test other people's patches and reply to them with
+comments. You can apply a patch by saving it into your busybox/ directory and
+typing 'patch < mychanges.patch'. Then you can recompile, see if it runs, test
+if it works as advertised, and post your findings to the mailing list.
+
+NOTE: Please do not include extraneous or irrelevant changes in your patches.
+Please do not try to "bundle" two patches together into one. Make single,
+discreet changes on a per-patch basis. Sometimes you need to make a patch that
+touches code in many places, but these kind of patches are rare and should be
+coordinated with a maintainer.
+
+
+Testing Guidelines
+~~~~~~~~~~~~~~~~~~
+
+It's considered good form to test your new feature before you submit a patch
+to the mailing list, and especially before you commit a change to CVS. Here
+are some guidelines on how to test your changes.
+
+ - Always test Busybox applets against GNU counterparts and make sure the
+   behavior / output is identical between the two.
+
+ - Try several different permutations and combinations of the features you're
+   adding (i.e., different combinations of command-line switches) and make sure
+   they all work; make sure one feature does not interfere with another.
+
+ - Make sure you test compiling against the source both with the feature
+   turned on and turned off in Config.h and make sure Busybox compiles cleanly
+   both ways.
+
+ - Run the multibuild.pl script in the tests directory and make sure
+   everything checks out OK. (Do this from within the busybox/ directory by
+   typing: 'tests/multibuild.pl'.)
+
+
+Making Sure Your Patch Doesn't Get Lost
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't want your patch to be lost or forgotten, send it to the busybox
+mailing list with a subject line something like this:
+
+       [PATCH] - Adds "transmogrify" feature to "foo"
+
+In the body, you should have a pseudo-header that looks like the following:
+
+    Package: busybox
+    Version: v1.01pre (or whatever the current version is)
+    Severity: wishlist
+
+The remainder of the body should read along these lines:
+
+       This patch adds the "transmogrify" feature to the "foo" applet. I have
+       tested this on [arch] system(s) and it works. I have tested it against the
+       GNU counterparts and the outputs are identical. I have run the scripts in
+       the 'tests' directory and nothing breaks.
+
+
+
+Improving Your Chances of Patch Acceptance
+------------------------------------------
+
+Even after you send a brilliant patch to the mailing list, sometimes it can go
+unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an
+unfortunate fact of life, but there are steps you can take to help your patch
+get noticed and convince a maintainer that it should be added:
+
+
+Be Succinct
+~~~~~~~~~~~
+
+A patch that includes small, isolated, obvious changes is more likely to be
+accepted than a patch that touches code in lots of different places or makes
+sweeping, dubious changes.
+
+
+Back It Up
+~~~~~~~~~~
+
+Hard facts on why your patch is better than the existing code will go a long
+way toward convincing maintainers that your patch should be included.
+Specifically, patches are more likely to be accepted if they are provably more
+correct, smaller, faster, simpler, or more maintainable than the existing
+code.
+
+Conversely, any patch that is supported with nothing more than "I think this
+would be cool" or "this patch is good because I say it is and I've got a Phd
+in Computer Science" will likely be ignored.
+
+
+Follow The Style Guide
+~~~~~~~~~~~~~~~~~~~~~~
+
+It's considered good form to abide by the established coding style used in a
+project; Busybox is no exception. We have gone so far as to delineate the
+"elements of Busybox style" in the file docs/style-guide.txt. Please follow
+them.
+
+
+Work With Someone Else
+~~~~~~~~~~~~~~~~~~~~~~
+
+Working on a patch in isolation is less effective than working with someone
+else for a variety of reasons. If another Busybox user is interested in what
+you're doing, then it's two (or more) voices instead of one that can petition
+for inclusion of the patch. You'll also have more people that can test your
+changes, or even offer suggestions on better approaches you could take.
+
+Getting other folks interested follows as a natural course if you've received
+responses from queries to applet maintainer or positive responses from folks
+on the mailing list.
+
+We've made strident efforts to put a useful "collaboration" infrastructure in
+place in the form of mailing lists, the bug tracking system, and CVS. Please
+use these resources.
+
+
+Send Patches to the Bug Tracking System
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost"
+section, but it is worth mentioning again. A patch sent to the mailing list
+might be unnoticed and forgotten. A patch sent to the bug tracking system will
+be stored and closely connected to the bug it fixes.
+
+
+Be Polite
+~~~~~~~~~
+
+The old saying "You'll catch more flies with honey than you will with vinegar"
+applies when submitting patches to the mailing list for approval. The way you
+present your patch is sometimes just as important as the actual patch itself
+(if not more so). Being rude to the maintainers is not an effective way to
+convince them that your patch should be included; it will likely have the
+opposite effect.
+
+
+
+Committing Changes to CVS
+-------------------------
+
+If you submit several patches that demonstrate that you are a skilled and wise
+coder, you may be invited to become a committer, thus enabling you to commit
+changes directly to CVS. This is nice because you don't have to wait for
+someone else to commit your change for you, you can just do it yourself.
+
+But note that this is a priviledge that comes with some responsibilities. You
+should test your changes before you commit them. You should also talk to an
+applet maintainer before you make any kind of sweeping changes to somebody
+else's code. Big changes should still go to the mailing list first. Remember,
+being wise, polite, and discreet is more important than being clever.
+
+
+When To Commit
+~~~~~~~~~~~~~~
+
+Generally, you should feel free to commit a change if:
+
+ - Your changes are small and don't touch many files
+ - You are fixing a bug
+ - Somebody has told you that it's okay
+ - It's obviously the Right Thing
+
+The more of the above are true, the better it is to just commit a change
+directly to CVS.
+
+
+When Not To Commit
+~~~~~~~~~~~~~~~~~~
+
+Even if you have commit rights, you should probably still post a patch to the
+mailing list if:
+
+ - Your changes are broad and touch many different files
+ - You are adding a feature
+ - Your changes are speculative or experimental (i.e., trying a new algorithm)
+ - You are not the maintainer and your changes make the maintainer cringe
+
+The more of the above are true, the better it is to post a patch to the
+mailing list instead of committing.
+
+
+
+Final Words
+-----------
+
+If all of this seems complicated, don't panic, it's really not that tough. If
+you're having difficulty following some of the steps outlined in this
+document don't worry, the folks on the Busybox mailing list are a fairly
+good-natured bunch and will work with you to help get your patches into shape
+or help you make contributions.
+
+
diff --git a/busybox/docs/new-applet-HOWTO.txt b/busybox/docs/new-applet-HOWTO.txt
new file mode 100644 (file)
index 0000000..2fc95d3
--- /dev/null
@@ -0,0 +1,163 @@
+How to Add a New Applet to BusyBox
+==================================
+
+This document details the steps you must take to add a new applet to BusyBox.
+
+Credits:
+Matt Kraai - initial writeup
+Mark Whitley - the remix
+Thomas Lundquist - Added stuff for the new directory layout.
+
+Initial Write
+-------------
+
+First, write your applet.  Be sure to include copyright information at the
+top, such as who you stole the code from and so forth. Also include the
+mini-GPL boilerplate. Be sure to name the main function <applet>_main instead
+of main.  And be sure to put it in <applet>.c. Usage do not have to be taken care of by your applet.
+
+For a new applet mu, here is the code that would go in mu.c:
+
+----begin example code------
+
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mu implementation for busybox
+ *
+ *
+ * Copyright (C) [YEAR] by [YOUR NAME] <YOUR EMAIL>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ */
+
+#include "busybox.h"
+
+int mu_main(int argc, char **argv)
+{
+       int fd;
+       char mu;
+
+       if ((fd = open("/dev/random", O_RDONLY)) < 0)
+               perror_msg_and_die("/dev/random");
+
+       if ((n = safe_read(fd, &mu, 1)) < 1)
+               perror_msg_and_die("/dev/random");
+
+       return mu;
+}
+
+----end example code------
+
+
+Coding Style
+------------
+
+Before you submit your applet for inclusion in BusyBox, (or better yet, before
+you _write_ your applet) please read through the style guide in the docs
+directory and make your program compliant.
+
+
+Some Words on libbb
+-------------------
+
+As you are writing your applet, please be aware of the body of pre-existing
+useful functions in libbb. Use these instead of reinventing the wheel.
+
+Additionally, if you have any useful, general-purpose functions in your
+program that could be useful in another program, consider putting them in
+libbb.
+
+
+Placement / Directory
+---------------------
+
+Find the appropriate directory for your new applet.
+
+Make sure you find the appropriate places in the files, the applets are
+sorted alphabetically.
+
+Add the applet to Makefile.in in the chosen applet directory:
+
+obj-$(CONFIG_MU)               += mu.o
+
+Add the applet to Config.in in the chosen applet directory:
+
+config CONFIG_MU
+       bool "MU"
+       default n
+       help
+         Returns an indeterminate value.
+
+
+Usage String(s)
+---------------
+
+Next, add usage information for you applet to include/usage.h.
+This should look like the following:
+
+       #define mu_trivial_usage \
+               "-[abcde] FILES"
+       #define mu_full_usage \
+               "Returns an indeterminate value.\n\n" \
+               "Options:\n" \
+               "\t-a\t\tfirst function\n" \
+               "\t-b\t\tsecond function\n" \
+
+If your program supports flags, the flags should be mentioned on the first
+line (-[abcde]) and a detailed description of each flag should go in the
+mu_full_usage section, one flag per line. (Numerous examples of this
+currently exist in usage.h.)
+
+
+Header Files
+------------
+
+Next, add an entry to include/applets.h.  Be *sure* to keep the list
+in alphabetical order, or else it will break the binary-search lookup
+algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily:
+
+       /* all programs above here are alphabetically "less than" 'mu' */
+       #ifdef CONFIG_MU
+               APPLET("mu", mu_main, _BB_DIR_USR_BIN, mu_usage)
+       #endif
+       /* all programs below here are alphabetically "greater than" 'mu' */
+
+
+Finally, add a define for your applet to include/config.h
+
+       #undef CONFIG_MU
+
+
+Documentation
+-------------
+
+If you're feeling especially nice, you should also document your applet in the
+docs directory (but nobody ever does that).
+
+Adding some text to docs/Configure.help is a nice start.
+
+
+The Grand Announcement
+----------------------
+
+Then create a diff -urN of the files you added (<appletdir/><applet>.c,
+include/usage.c, include/applets.h, include/config.h, <appletdir>/Makefile.in, <appletdir>/config.in)
+and send it to the mailing list:
+busybox@busybox.net.
+
+Sending patches as attachments is preferred, but not required.
+
diff --git a/busybox/docs/style-guide.txt b/busybox/docs/style-guide.txt
new file mode 100644 (file)
index 0000000..915d9b2
--- /dev/null
@@ -0,0 +1,680 @@
+Busybox Style Guide
+===================
+
+This document describes the coding style conventions used in Busybox. If you
+add a new file to Busybox or are editing an existing file, please format your
+code according to this style. If you are the maintainer of a file that does
+not follow these guidelines, please -- at your own convenience -- modify the
+file(s) you maintain to bring them into conformance with this style guide.
+Please note that this is a low priority task.
+
+To help you format the whitespace of your programs, an ".indent.pro" file is
+included in the main Busybox source directory that contains option flags to
+format code as per this style guide. This way you can run GNU indent on your
+files by typing 'indent myfile.c myfile.h' and it will magically apply all the
+right formatting rules to your file. Please _do_not_ run this on all the files
+in the directory, just your own.
+
+
+
+Declaration Order
+-----------------
+
+Here is the order in which code should be laid out in a file:
+
+ - commented program name and one-line description
+ - commented author name and email address(es)
+ - commented GPL boilerplate
+ - commented longer description / notes for the program (if needed)
+ - #includes of .h files with angle brackets (<>) around them
+ - #includes of .h files with quotes ("") around them
+ - #defines (if any, note the section below titled "Avoid the Preprocessor")
+ - const and global variables
+ - function declarations (if necessary)
+ - function implementations
+
+
+
+Whitespace and Formatting
+-------------------------
+
+This is everybody's favorite flame topic so let's get it out of the way right
+up front.
+
+
+Tabs vs. Spaces in Line Indentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The preference in Busybox is to indent lines with tabs. Do not indent lines
+with spaces and do not indents lines using a mixture of tabs and spaces. (The
+indentation style in the Apache and Postfix source does this sort of thing:
+\s\s\s\sif (expr) {\n\tstmt; --ick.) The only exception to this rule is
+multi-line comments that use an asterisk at the beginning of each line, i.e.:
+
+       /t/*
+       /t * This is a block comment.
+       /t * Note that it has multiple lines
+       /t * and that the beginning of each line has a tab plus a space
+       /t * except for the opening '/*' line where the slash
+       /t * is used instead of a space.
+       /t */
+
+Furthermore, The preference is that tabs be set to display at four spaces
+wide, but the beauty of using only tabs (and not spaces) at the beginning of
+lines is that you can set your editor to display tabs at *whatever* number of
+spaces is desired and the code will still look fine.
+
+
+Operator Spacing
+~~~~~~~~~~~~~~~~
+
+Put spaces between terms and operators. Example:
+
+       Don't do this:
+
+               for(i=0;i<num_items;i++){
+
+       Do this instead:
+
+               for (i = 0; i < num_items; i++) {
+
+       While it extends the line a bit longer, the spaced version is more
+       readable. An allowable exception to this rule is the situation where
+       excluding the spacing makes it more obvious that we are dealing with a
+       single term (even if it is a compound term) such as:
+
+               if (str[idx] == '/' && str[idx-1] != '\\')
+
+       or
+
+               if ((argc-1) - (optind+1) > 0)
+
+
+Bracket Spacing
+~~~~~~~~~~~~~~~
+
+If an opening bracket starts a function, it should be on the
+next line with no spacing before it. However, if a bracket follows an opening
+control block, it should be on the same line with a single space (not a tab)
+between it and the opening control block statement. Examples:
+
+       Don't do this:
+
+               while (!done)
+               {
+
+               do
+               {
+
+       Don't do this either:
+
+               while (!done){
+
+               do{
+
+       And for heaven's sake, don't do this:
+
+               while (!done)
+                 {
+
+               do
+                 {
+
+       Do this instead:
+
+               while (!done) {
+
+               do {
+
+
+Spacing around Parentheses
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Put a space between C keywords and left parens, but not between function names
+and the left paren that starts it's parameter list (whether it is being
+declared or called). Examples:
+
+       Don't do this:
+
+               while(foo) {
+               for(i = 0; i < n; i++) {
+
+       Do this instead:
+
+               while (foo) {
+               for (i = 0; i < n; i++) {
+
+       But do functions like this:
+
+               static int my_func(int foo, char bar)
+               ...
+               baz = my_func(1, 2);
+
+Also, don't put a space between the left paren and the first term, nor between
+the last arg and the right paren.
+
+       Don't do this:
+
+               if ( x < 1 )
+               strcmp( thisstr, thatstr )
+
+       Do this instead:
+
+               if (x < 1)
+               strcmp(thisstr, thatstr)
+
+
+Cuddled Elses
+~~~~~~~~~~~~~
+
+Also, please "cuddle" your else statements by putting the else keyword on the
+same line after the right bracket that closes an 'if' statement.
+
+       Don't do this:
+
+       if (foo) {
+               stmt;
+       }
+       else {
+               stmt;
+       }
+
+       Do this instead:
+
+       if (foo) {
+               stmt;
+       } else {
+               stmt;
+       }
+
+The exception to this rule is if you want to include a comment before the else
+block. Example:
+
+       if (foo) {
+               stmts...
+       }
+       /* otherwise, we're just kidding ourselves, so re-frob the input */
+       else {
+               other_stmts...
+       }
+
+
+
+Variable and Function Names
+---------------------------
+
+Use the K&R style with names in all lower-case and underscores occasionally
+used to separate words (e.g., "variable_name" and "numchars" are both
+acceptable). Using underscores makes variable and function names more readable
+because it looks like whitespace; using lower-case is easy on the eyes.
+
+       Frowned upon:
+
+               hitList
+               TotalChars
+               szFileName
+               pf_Nfol_TriState
+
+       Preferred:
+
+               hit_list
+               total_chars
+               file_name
+               sensible_name
+
+Exceptions:
+
+ - Enums, macros, and constant variables are occasionally written in all
+   upper-case with words optionally seperatedy by underscores (i.e. FIFOTYPE,
+   ISBLKDEV()).
+
+ - Nobody is going to get mad at you for using 'pvar' as the name of a
+   variable that is a pointer to 'var'.
+
+
+Converting to K&R
+~~~~~~~~~~~~~~~~~
+
+The Busybox codebase is very much a mixture of code gathered from a variety of
+sources. This explains why the current codebase contains such a hodge-podge of
+different naming styles (Java, Pascal, K&R, just-plain-weird, etc.). The K&R
+guideline explained above should therefore be used on new files that are added
+to the repository. Furthermore, the maintainer of an existing file that uses
+alternate naming conventions should, at his own convenience, convert those
+names over to K&R style. Converting variable names is a very low priority
+task.
+
+If you want to do a search-and-replace of a single variable name in different
+files, you can do the following in the busybox directory:
+
+       $ perl -pi -e 's/\bOldVar\b/new_var/g' *.[ch]
+
+If you want to convert all the non-K&R vars in your file all at once, follow
+these steps:
+
+ - In the busybox directory type 'examples/mk2knr.pl files-to-convert'. This
+   does not do the actual conversion, rather, it generates a script called
+   'convertme.pl' that shows what will be converted, giving you a chance to
+   review the changes beforehand.
+
+ - Review the 'convertme.pl' script that gets generated in the busybox
+   directory and remove / edit any of the substitutions in there. Please
+   especially check for false positives (strings that should not be
+   converted).
+
+ - Type './convertme.pl same-files-as-before' to perform the actual
+   conversion.
+
+ - Compile and see if everything still works.
+
+Please be aware of changes that have cascading effects into other files. For
+example, if you're changing the name of something in, say utility.c, you
+should probably run 'examples/mk2knr.pl utility.c' at first, but when you run
+the 'convertme.pl' script you should run it on _all_ files like so:
+'./convertme.pl *.[ch]'.
+
+
+
+Avoid The Preprocessor
+----------------------
+
+At best, the preprocessor is a necessary evil, helping us account for platform
+and architecture differences. Using the preprocessor unnecessarily is just
+plain evil.
+
+
+The Folly of #define
+~~~~~~~~~~~~~~~~~~~~
+
+Use 'const <type> var' for declaring constants.
+
+       Don't do this:
+
+               #define var 80
+
+       Do this instead, when the variable is in a header file and will be used in
+       several source files:
+
+               const int var = 80;
+
+       Or do this when the variable is used only in a single source file:
+
+               static const int var = 80;
+
+Declaring variables as '[static] const' gives variables an actual type and
+makes the compiler do type checking for you; the preprocessor does _no_ type
+checking whatsoever, making it much more error prone. Declaring variables with
+'[static] const' also makes debugging programs much easier since the value of
+the variable can be easily queried and displayed.
+
+
+The Folly of Macros
+~~~~~~~~~~~~~~~~~~~
+
+Use 'static inline' instead of a macro.
+
+       Don't do this:
+
+               #define mini_func(param1, param2) (param1 << param2)
+
+       Do this instead:
+
+               static inline int mini_func(int param1, param2)
+               {
+                       return (param1 << param2);
+               }
+
+Static inline functions are greatly preferred over macros. They provide type
+safety, have no length limitations, no formatting limitations, have an actual
+return value, and under gcc they are as cheap as macros. Besides, really long
+macros with backslashes at the end of each line are ugly as sin.
+
+
+The Folly of #ifdef
+~~~~~~~~~~~~~~~~~~~
+
+Code cluttered with ifdefs is difficult to read and maintain. Don't do it.
+Instead, put your ifdefs at the top of your .c file (or in a header), and
+conditionally define 'static inline' functions, (or *maybe* macros), which are
+used in the code.
+
+       Don't do this:
+
+               ret = my_func(bar, baz);
+               if (!ret)
+                       return -1;
+               #ifdef CONFIG_FEATURE_FUNKY
+                       maybe_do_funky_stuff(bar, baz);
+               #endif
+
+       Do this instead:
+
+       (in .h header file)
+
+               #ifdef CONFIG_FEATURE_FUNKY
+               static inline void maybe_do_funky_stuff (int bar, int baz)
+               {
+                       /* lotsa code in here */
+               }
+               #else
+               static inline void maybe_do_funky_stuff (int bar, int baz) {}
+               #endif
+
+       (in the .c source file)
+
+               ret = my_func(bar, baz);
+               if (!ret)
+                       return -1;
+               maybe_do_funky_stuff(bar, baz);
+
+The great thing about this approach is that the compiler will optimize away
+the "no-op" case (the empty function) when the feature is turned off.
+
+Note also the use of the word 'maybe' in the function name to indicate
+conditional execution.
+
+
+
+Notes on Strings
+----------------
+
+Strings in C can get a little thorny. Here's some guidelines for dealing with
+strings in Busybox. (There is surely more that could be added to this
+section.)
+
+
+String Files
+~~~~~~~~~~~~
+
+Put all help/usage messages in usage.c. Put other strings in messages.c.
+Putting these strings into their own file is a calculated decision designed to
+confine spelling errors to a single place and aid internationalization
+efforts, if needed. (Side Note: we might want to use a single file - maybe
+called 'strings.c' - instead of two, food for thought).
+
+
+Testing String Equivalence
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There's a right way and a wrong way to test for sting equivalence with
+strcmp():
+
+       The wrong way:
+
+               if (!strcmp(string, "foo")) {
+                       ...
+
+       The right way:
+
+               if (strcmp(string, "foo") == 0){
+                       ...
+
+The use of the "equals" (==) operator in the latter example makes it much more
+obvious that you are testing for equivalence. The former example with the
+"not" (!) operator makes it look like you are testing for an error. In a more
+perfect world, we would have a streq() function in the string library, but
+that ain't the world we're living in.
+
+
+Avoid Dangerous String Functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unfortunately, the way C handles strings makes them prone to overruns when
+certain library functions are (mis)used. The following table  offers a summary
+of some of the more notorious troublemakers:
+
+function     overflows         preferred
+----------------------------------------
+strcpy       dest string       strncpy
+strcat       dest string       strncat
+gets         string it gets    fgets
+getwd        buf string        getcwd
+[v]sprintf   str buffer        [v]snprintf
+realpath     path buffer       use with pathconf
+[vf]scanf    its arguments     just avoid it
+
+
+The above is by no means a complete list. Be careful out there.
+
+
+
+Avoid Big Static Buffers
+------------------------
+
+First, some background to put this discussion in context: Static buffers look
+like this in code:
+
+       /* in a .c file outside any functions */
+       static char *buffer[BUFSIZ]; /* happily used by any function in this file,
+                                       but ick! big! */
+
+The problem with these is that any time any busybox app is run, you pay a
+memory penalty for this buffer, even if the applet that uses said buffer is
+not run. This can be fixed, thusly:
+
+       static char *buffer;
+       ...
+       other_func()
+       {
+               strcpy(buffer, lotsa_chars); /* happily uses global *buffer */
+       ...
+       foo_main()
+       {
+               buffer = xmalloc(sizeof(char)*BUFSIZ);
+       ...
+
+However, this approach trades bss segment for text segment. Rather than
+mallocing the buffers (and thus growing the text size), buffers can be
+declared on the stack in the *_main() function and made available globally by
+assigning them to a global pointer thusly:
+
+       static char *pbuffer;
+       ...
+       other_func()
+       {
+               strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */
+       ...
+       foo_main()
+       {
+               char *buffer[BUFSIZ]; /* declared locally, on stack */
+               pbuffer = buffer;     /* but available globally */
+       ...
+
+This last approach has some advantages (low code size, space not used until
+it's needed), but can be a problem in some low resource machines that have
+very limited stack space (e.g., uCLinux).
+
+A macro is declared in busybox.h that implements compile-time selection
+between xmalloc() and stack creation, so you can code the line in question as
+
+               RESERVE_CONFIG_BUFFER(buffer, BUFSIZ);
+
+and the right thing will happen, based on your configuration.
+
+
+
+Miscellaneous Coding Guidelines
+-------------------------------
+
+The following are important items that don't fit into any of the above
+sections.
+
+
+Model Busybox Applets After GNU Counterparts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When in doubt about the proper behavior of a Busybox program (output,
+formatting, options, etc.), model it after the equivalent GNU program.
+Doesn't matter how that program behaves on some other flavor of *NIX; doesn't
+matter what the POSIX standard says or doesn't say, just model Busybox
+programs after their GNU counterparts and it will make life easier on (nearly)
+everyone.
+
+The only time we deviate from emulating the GNU behavior is when:
+
+       - We are deliberately not supporting a feature (such as a command line
+         switch)
+       - Emulating the GNU behavior is prohibitively expensive (lots more code
+         would be required, lots more memory would be used, etc.)
+       - The difference is minor or cosmetic
+
+A note on the 'cosmetic' case: Output differences might be considered
+cosmetic, but if the output is significant enough to break other scripts that
+use the output, it should really be fixed.
+
+
+Scope
+~~~~~
+
+If a const variable is used only in a single source file, put it in the source
+file and not in a header file. Likewise, if a const variable is used in only
+one function, do not make it global to the file. Instead, declare it inside
+the function body. Bottom line: Make a conscious effort to limit declarations
+to the smallest scope possible.
+
+Inside applet files, all functions should be declared static so as to keep the
+global name space clean. The only exception to this rule is the "applet_main"
+function which must be declared extern.
+
+If you write a function that performs a task that could be useful outside the
+immediate file, turn it into a general-purpose function with no ties to any
+applet and put it in the utility.c file instead.
+
+
+Brackets Are Your Friends
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Please use brackets on all if and else statements, even if it is only one
+line. Example:
+
+       Don't do this:
+
+               if (foo)
+                       stmt1;
+               stmt2
+               stmt3;
+
+       Do this instead:
+
+               if (foo) {
+                       stmt1;
+               }
+               stmt2
+               stmt3;
+
+The "bracketless" approach is error prone because someday you might add a line
+like this:
+
+               if (foo)
+                       stmt1;
+                       new_line();
+               stmt2
+               stmt3;
+
+And the resulting behavior of your program would totally bewilder you. (Don't
+laugh, it happens to us all.) Remember folks, this is C, not Python.
+
+
+Function Declarations
+~~~~~~~~~~~~~~~~~~~~~
+
+Do not use old-style function declarations that declare variable types between
+the parameter list and opening bracket. Example:
+
+       Don't do this:
+
+               int foo(parm1, parm2)
+                       char parm1;
+                       float parm2;
+               {
+                       ....
+
+       Do this instead:
+
+               int foo(char parm1, float parm2)
+               {
+                       ....
+
+The only time you would ever need to use the old declaration syntax is to
+support ancient, antediluvian compilers. To our good fortune, we have access
+to more modern compilers and the old declaration syntax is neither necessary
+nor desired.
+
+
+Emphasizing Logical Blocks
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Organization and readability are improved by putting extra newlines around
+blocks of code that perform a single task. These are typically blocks that
+begin with a C keyword, but not always.
+
+Furthermore, you should put a single comment (not necessarily one line, just
+one comment) before the block, rather than commenting each and every line.
+There is an optimal ammount of commenting that a program can have; you can
+comment too much as well as too little.
+
+A picture is really worth a thousand words here, the following example
+illustrates how to emphasize logical blocks:
+
+       while (line = get_line_from_file(fp)) {
+
+               /* eat the newline, if any */
+               chomp(line);
+
+               /* ignore blank lines */
+               if (strlen(file_to_act_on) == 0) {
+                       continue;
+               }
+
+               /* if the search string is in this line, print it,
+                * unless we were told to be quiet */
+               if (strstr(line, search) && !be_quiet) {
+                       puts(line);
+               }
+
+               /* clean up */
+               free(line);
+       }
+
+
+Processing Options with getopt
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your applet needs to process  command-line switches, please use getopt() to
+do so. Numerous examples can be seen in many of the existing applets, but
+basically it boils down to two things: at the top of the .c file, have this
+line in the midst of your #includes:
+
+       #include <getopt.h>
+
+And a code block similar to the following near the top of your applet_main()
+routine:
+
+    while ((opt = getopt(argc, argv, "abc")) > 0) {
+            switch (opt) {
+            case 'a':
+                do_a_opt = 1;
+                break;
+            case 'b':
+                do_b_opt = 1;
+                break;
+            case 'c':
+                do_c_opt = 1;
+                break;
+            default:
+                show_usage();    /* in utility.c */
+            }
+    }
+
+If your applet takes no options (such as 'init'), there should be a line
+somewhere in the file reads:
+
+       /* no options, no getopt */
+
+That way, when people go grepping to see which applets need to be converted to
+use getopt, they won't get false positives.
+
+Additional Note: Do not use the getopt_long library function and do not try to
+hand-roll your own long option parsing. Busybox applets should only support
+short options. Explanations and examples of the short options should be
+documented in usage.h.
diff --git a/busybox/editors/Config.in b/busybox/editors/Config.in
new file mode 100644 (file)
index 0000000..bb02859
--- /dev/null
@@ -0,0 +1,123 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Editors"
+
+config CONFIG_AWK
+       bool "awk"
+       default n
+       help
+         Awk is used as a pattern scanning and processing language.  This is
+         the BusyBox implementation of that programming language.
+
+config CONFIG_FEATURE_AWK_MATH
+       bool "  Enable math functions (requires libm)"
+       default y
+       depends on CONFIG_AWK
+       help
+         Enable math functions of the Awk programming language.
+         NOTE: This will require libm to be present for linking.
+
+config CONFIG_PATCH
+       bool "patch"
+       default n
+       help
+         Apply a unified diff formatted patch.
+
+config CONFIG_SED
+       bool "sed"
+       default n
+       help
+         sed is used to perform text transformations on a file
+         or input from a pipeline.
+
+config CONFIG_VI
+       bool "vi"
+       default n
+       help
+         'vi' is a text editor.  More specifically, it is the One True
+         text editor <grin>.  It does, however, have a rather steep
+         learning curve.  If you are not already comfortable with 'vi'
+         you may wish to use something else.
+
+config CONFIG_FEATURE_VI_COLON
+       bool "  Enable \":\" colon commands (no \"ex\" mode)"
+       default y
+       depends on CONFIG_VI
+       help
+         Enable a limited set of colon commands for vi.  This does not
+         provide an "ex" mode.
+
+config CONFIG_FEATURE_VI_YANKMARK
+       bool "  Enable yank/put commands and mark cmds"
+       default y
+       depends on CONFIG_VI
+       help
+         This will enable you to use yank and put, as well as mark in
+         busybox vi.
+
+config CONFIG_FEATURE_VI_SEARCH
+       bool "  Enable search and replace cmds"
+       default y
+       depends on CONFIG_VI
+       help
+         Select this if you wish to be able to do search and replace in
+         busybox vi.
+
+config CONFIG_FEATURE_VI_USE_SIGNALS
+       bool "  Catch signals"
+       default y
+       depends on CONFIG_VI
+       help
+         Selecting this option will make busybox vi signal aware.  This will
+         make busybox vi support SIGWINCH to deal with Window Changes, catch
+         Ctrl-Z and Ctrl-C and alarms.
+
+config CONFIG_FEATURE_VI_DOT_CMD
+       bool "  Remember previous cmd and \".\" cmd"
+       default y
+       depends on CONFIG_VI
+       help
+         Make busybox vi remember the last command and be able to repeat it.
+
+config CONFIG_FEATURE_VI_READONLY
+       bool "  Enable -R option and \"view\" mode"
+       default y
+       depends on CONFIG_VI
+       help
+         Enable the read-only command line option, which allows the user to
+         open a file in read-only mode.
+
+config CONFIG_FEATURE_VI_SETOPTS
+       bool "  Enable set-able options, ai ic showmatch"
+       default y
+       depends on CONFIG_VI
+       help
+         Enable the editor to set some (ai, ic, showmatch) options.
+
+config CONFIG_FEATURE_VI_SET
+       bool "  Support for :set"
+       default y
+       depends on CONFIG_VI
+       help
+         Support for ":set".
+
+config CONFIG_FEATURE_VI_WIN_RESIZE
+       bool "  Handle window resize"
+       default y
+       depends on CONFIG_VI
+       help
+         Make busybox vi behave nicely with terminals that get resized.
+
+config CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+       bool "  Optimize cursor movement"
+       default y
+       depends on CONFIG_VI
+       help
+         This will make the cursor movement faster, but requires more memory
+         and it makes the applet a tiny bit larger.
+
+endmenu
+
diff --git a/busybox/editors/Makefile b/busybox/editors/Makefile
new file mode 100644 (file)
index 0000000..e6c1147
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/editors
+EDITOR_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/editors/Makefile.in b/busybox/editors/Makefile.in
new file mode 100644 (file)
index 0000000..571e055
--- /dev/null
@@ -0,0 +1,48 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+EDITOR_AR:=editors.a
+ifndef $(EDITOR_DIR)
+EDITOR_DIR:=$(top_builddir)/editors/
+endif
+srcdir=$(top_srcdir)/editors
+
+EDITOR-y:=
+EDITOR-$(CONFIG_AWK)      += awk.o
+EDITOR-$(CONFIG_PATCH)    += patch.o
+EDITOR-$(CONFIG_SED)       += sed.o
+EDITOR-$(CONFIG_VI)        += vi.o
+EDITOR_SRC:= $(EDITOR-y)
+EDITOR_OBJ:= $(patsubst %.c,$(EDITOR_DIR)%.o, $(EDITOR_SRC))
+
+libraries-y+=$(EDITOR_DIR)$(EDITOR_AR)
+
+needlibm-y:=
+needlibm-$(CONFIG_FEATURE_AWK_MATH) := y
+
+ifeq ($(needlibm-y),y)
+  LIBRARIES += -lm
+endif
+
+$(EDITOR_DIR)$(EDITOR_AR): $(patsubst %,$(EDITOR_DIR)%, $(EDITOR-y))
+       $(AR) -ro $@ $(patsubst %,$(EDITOR_DIR)%, $(EDITOR-y))
+
+$(EDITOR_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/editors/awk.c b/busybox/editors/awk.c
new file mode 100644 (file)
index 0000000..c1cb2a2
--- /dev/null
@@ -0,0 +1,2764 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * awk implementation for busybox
+ *
+ * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <regex.h>
+
+#include "busybox.h"
+
+
+#define        MAXVARFMT       240
+#define        MINNVBLOCK      64
+
+/* variable flags */
+#define        VF_NUMBER       0x0001  /* 1 = primary type is number */
+#define        VF_ARRAY        0x0002  /* 1 = it's an array */
+
+#define        VF_CACHED       0x0100  /* 1 = num/str value has cached str/num eq */
+#define        VF_USER         0x0200  /* 1 = user input (may be numeric string) */
+#define        VF_SPECIAL      0x0400  /* 1 = requires extra handling when changed */
+#define        VF_WALK         0x0800  /* 1 = variable has alloc'd x.walker list */
+#define        VF_FSTR         0x1000  /* 1 = string points to fstring buffer */
+#define        VF_CHILD        0x2000  /* 1 = function arg; x.parent points to source */
+#define        VF_DIRTY        0x4000  /* 1 = variable was set explicitly */
+
+/* these flags are static, don't change them when value is changed */
+#define        VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
+
+/* Variable */
+typedef struct var_s {
+       unsigned short type;            /* flags */
+       double number;
+       char *string;
+       union {
+               int aidx;                               /* func arg index (on compilation stage) */
+               struct xhash_s *array;  /* array ptr */
+               struct var_s *parent;   /* for func args, ptr to actual parameter */
+               char **walker;                  /* list of array elements (for..in) */
+       } x;
+} var;
+
+/* Node chain (pattern-action chain, BEGIN, END, function bodies) */
+typedef struct chain_s {
+       struct node_s *first;
+       struct node_s *last;
+       char *programname;
+} chain;
+
+/* Function */
+typedef struct func_s {
+       unsigned short nargs;
+       struct chain_s body;
+} func;
+
+/* I/O stream */
+typedef struct rstream_s {
+       FILE *F;
+       char *buffer;
+       int adv;
+       int size;
+       int pos;
+       unsigned short is_pipe;
+} rstream;
+
+typedef struct hash_item_s {
+       union {
+               struct var_s v;                 /* variable/array hash */
+               struct rstream_s rs;    /* redirect streams hash */
+               struct func_s f;                /* functions hash */
+       } data;
+       struct hash_item_s *next;       /* next in chain */
+       char name[1];                           /* really it's longer */
+} hash_item;
+
+typedef struct xhash_s {
+       unsigned int nel;                                       /* num of elements */
+       unsigned int csize;                                     /* current hash size */
+       unsigned int nprime;                            /* next hash size in PRIMES[] */
+       unsigned int glen;                                      /* summary length of item names */
+       struct hash_item_s **items;
+} xhash;
+
+/* Tree node */
+typedef struct node_s {
+       unsigned long info;
+       unsigned short lineno;
+       union {
+               struct node_s *n;
+               var *v;
+               int i;
+               char *s;
+               regex_t *re;
+       } l;
+       union {
+               struct node_s *n;
+               regex_t *ire;
+               func *f;
+               int argno;
+       } r;
+       union {
+               struct node_s *n;
+       } a;
+} node;
+
+/* Block of temporary variables */
+typedef struct nvblock_s {
+       int size;
+       var *pos;
+       struct nvblock_s *prev;
+       struct nvblock_s *next;
+       var nv[0];
+} nvblock;
+
+typedef struct tsplitter_s {
+       node n;
+       regex_t re[2];
+} tsplitter;
+
+/* simple token classes */
+/* Order and hex values are very important!!!  See next_token() */
+#define        TC_SEQSTART      1                              /* ( */
+#define        TC_SEQTERM      (1 << 1)                /* ) */
+#define        TC_REGEXP       (1 << 2)                /* /.../ */
+#define        TC_OUTRDR       (1 << 3)                /* | > >> */
+#define        TC_UOPPOST      (1 << 4)                /* unary postfix operator */
+#define        TC_UOPPRE1      (1 << 5)                /* unary prefix operator */
+#define        TC_BINOPX       (1 << 6)                /* two-opnd operator */
+#define        TC_IN           (1 << 7)
+#define        TC_COMMA        (1 << 8)
+#define        TC_PIPE         (1 << 9)                /* input redirection pipe */
+#define        TC_UOPPRE2      (1 << 10)               /* unary prefix operator */
+#define        TC_ARRTERM      (1 << 11)               /* ] */
+#define        TC_GRPSTART     (1 << 12)               /* { */
+#define        TC_GRPTERM      (1 << 13)               /* } */
+#define        TC_SEMICOL      (1 << 14)
+#define        TC_NEWLINE      (1 << 15)
+#define        TC_STATX        (1 << 16)               /* ctl statement (for, next...) */
+#define        TC_WHILE        (1 << 17)
+#define        TC_ELSE         (1 << 18)
+#define        TC_BUILTIN      (1 << 19)
+#define        TC_GETLINE      (1 << 20)
+#define        TC_FUNCDECL     (1 << 21)               /* `function' `func' */
+#define        TC_BEGIN        (1 << 22)
+#define        TC_END          (1 << 23)
+#define        TC_EOF          (1 << 24)
+#define        TC_VARIABLE     (1 << 25)
+#define        TC_ARRAY        (1 << 26)
+#define        TC_FUNCTION     (1 << 27)
+#define        TC_STRING       (1 << 28)
+#define        TC_NUMBER       (1 << 29)
+
+#define        TC_UOPPRE       (TC_UOPPRE1 | TC_UOPPRE2)
+
+/* combined token classes */
+#define        TC_BINOP        (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
+#define        TC_UNARYOP      (TC_UOPPRE | TC_UOPPOST)
+#define        TC_OPERAND      (TC_VARIABLE | TC_ARRAY | TC_FUNCTION | \
+       TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
+
+#define        TC_STATEMNT     (TC_STATX | TC_WHILE)
+#define        TC_OPTERM       (TC_SEMICOL | TC_NEWLINE)
+
+/* word tokens, cannot mean something else if not expected */
+#define        TC_WORD         (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN | \
+       TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
+
+/* discard newlines after these */
+#define        TC_NOTERM       (TC_COMMA | TC_GRPSTART | TC_GRPTERM | \
+       TC_BINOP | TC_OPTERM)
+
+/* what can expression begin with */
+#define        TC_OPSEQ        (TC_OPERAND | TC_UOPPRE | TC_REGEXP)
+/* what can group begin with */
+#define        TC_GRPSEQ       (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART)
+
+/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
+/* operator is inserted between them */
+#define        TC_CONCAT1      (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM | \
+       TC_STRING | TC_NUMBER | TC_UOPPOST)
+#define        TC_CONCAT2      (TC_OPERAND | TC_UOPPRE)
+
+#define        OF_RES1         0x010000
+#define        OF_RES2         0x020000
+#define        OF_STR1         0x040000
+#define        OF_STR2         0x080000
+#define        OF_NUM1         0x100000
+#define        OF_CHECKED      0x200000
+
+/* combined operator flags */
+#define        xx      0
+#define        xV      OF_RES2
+#define        xS      (OF_RES2 | OF_STR2)
+#define        Vx      OF_RES1
+#define        VV      (OF_RES1 | OF_RES2)
+#define        Nx      (OF_RES1 | OF_NUM1)
+#define        NV      (OF_RES1 | OF_NUM1 | OF_RES2)
+#define        Sx      (OF_RES1 | OF_STR1)
+#define        SV      (OF_RES1 | OF_STR1 | OF_RES2)
+#define        SS      (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2)
+
+#define        OPCLSMASK       0xFF00
+#define        OPNMASK         0x007F
+
+/* operator priority is a highest byte (even: r->l, odd: l->r grouping)
+ * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1,
+ * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string
+ */
+#define        P(x)    (x << 24)
+#define        PRIMASK         0x7F000000
+#define        PRIMASK2        0x7E000000
+
+/* Operation classes */
+
+#define        SHIFT_TIL_THIS  0x0600
+#define        RECUR_FROM_THIS 0x1000
+
+enum {
+       OC_DELETE=0x0100,       OC_EXEC=0x0200,         OC_NEWSOURCE=0x0300,
+       OC_PRINT=0x0400,        OC_PRINTF=0x0500,       OC_WALKINIT=0x0600,
+
+       OC_BR=0x0700,           OC_BREAK=0x0800,        OC_CONTINUE=0x0900,
+       OC_EXIT=0x0a00,         OC_NEXT=0x0b00,         OC_NEXTFILE=0x0c00,
+       OC_TEST=0x0d00,         OC_WALKNEXT=0x0e00,
+
+       OC_BINARY=0x1000,       OC_BUILTIN=0x1100,      OC_COLON=0x1200,
+       OC_COMMA=0x1300,        OC_COMPARE=0x1400,      OC_CONCAT=0x1500,
+       OC_FBLTIN=0x1600,       OC_FIELD=0x1700,        OC_FNARG=0x1800,
+       OC_FUNC=0x1900,         OC_GETLINE=0x1a00,      OC_IN=0x1b00,
+       OC_LAND=0x1c00,         OC_LOR=0x1d00,          OC_MATCH=0x1e00,
+       OC_MOVE=0x1f00,         OC_PGETLINE=0x2000,     OC_REGEXP=0x2100,
+       OC_REPLACE=0x2200,      OC_RETURN=0x2300,       OC_SPRINTF=0x2400,
+       OC_TERNARY=0x2500,      OC_UNARY=0x2600,        OC_VAR=0x2700,
+       OC_DONE=0x2800,
+
+       ST_IF=0x3000,           ST_DO=0x3100,           ST_FOR=0x3200,
+       ST_WHILE=0x3300
+};
+
+/* simple builtins */
+enum {
+       F_in=0, F_rn,   F_co,   F_ex,   F_lg,   F_si,   F_sq,   F_sr,
+       F_ti,   F_le,   F_sy,   F_ff,   F_cl
+};
+
+/* builtins */
+enum {
+       B_a2=0, B_ix,   B_ma,   B_sp,   B_ss,   B_ti,   B_lo,   B_up,
+       B_ge,   B_gs,   B_su
+};
+
+/* tokens and their corresponding info values */
+
+#define        NTC             "\377"          /* switch to next token class (tc<<1) */
+#define        NTCC    '\377'
+
+#define        OC_B    OC_BUILTIN
+
+static char * const tokenlist =
+       "\1("           NTC
+       "\1)"           NTC
+       "\1/"           NTC                                                                     /* REGEXP */
+       "\2>>"          "\1>"           "\1|"           NTC                     /* OUTRDR */
+       "\2++"          "\2--"          NTC                                             /* UOPPOST */
+       "\2++"          "\2--"          "\1$"           NTC                     /* UOPPRE1 */
+       "\2=="          "\1="           "\2+="          "\2-="          /* BINOPX */
+       "\2*="          "\2/="          "\2%="          "\2^="
+       "\1+"           "\1-"           "\3**="         "\2**"
+       "\1/"           "\1%"           "\1^"           "\1*"
+       "\2!="          "\2>="          "\2<="          "\1>"
+       "\1<"           "\2!~"          "\1~"           "\2&&"
+       "\2||"          "\1?"           "\1:"           NTC
+       "\2in"          NTC
+       "\1,"           NTC
+       "\1|"           NTC
+       "\1+"           "\1-"           "\1!"           NTC                     /* UOPPRE2 */
+       "\1]"           NTC
+       "\1{"           NTC
+       "\1}"           NTC
+       "\1;"           NTC
+       "\1\n"          NTC
+       "\2if"          "\2do"          "\3for"         "\5break"       /* STATX */
+       "\10continue"                   "\6delete"      "\5print"
+       "\6printf"      "\4next"        "\10nextfile"
+       "\6return"      "\4exit"        NTC
+       "\5while"       NTC
+       "\4else"        NTC
+
+       "\5close"       "\6system"      "\6fflush"      "\5atan2"       /* BUILTIN */
+       "\3cos"         "\3exp"         "\3int"         "\3log"
+       "\4rand"        "\3sin"         "\4sqrt"        "\5srand"
+       "\6gensub"      "\4gsub"        "\5index"       "\6length"
+       "\5match"       "\5split"       "\7sprintf"     "\3sub"
+       "\6substr"      "\7systime"     "\10strftime"
+       "\7tolower"     "\7toupper"     NTC
+       "\7getline"     NTC
+       "\4func"        "\10function"   NTC
+       "\5BEGIN"       NTC
+       "\3END"         "\0"
+       ;
+
+static unsigned long tokeninfo[] = {
+
+       0,
+       0,
+       OC_REGEXP,
+       xS|'a',         xS|'w',         xS|'|',
+       OC_UNARY|xV|P(9)|'p',           OC_UNARY|xV|P(9)|'m',
+       OC_UNARY|xV|P(9)|'P',           OC_UNARY|xV|P(9)|'M',
+               OC_FIELD|xV|P(5),
+       OC_COMPARE|VV|P(39)|5,          OC_MOVE|VV|P(74),
+               OC_REPLACE|NV|P(74)|'+',        OC_REPLACE|NV|P(74)|'-',
+       OC_REPLACE|NV|P(74)|'*',        OC_REPLACE|NV|P(74)|'/',
+               OC_REPLACE|NV|P(74)|'%',        OC_REPLACE|NV|P(74)|'&',
+       OC_BINARY|NV|P(29)|'+',         OC_BINARY|NV|P(29)|'-',
+               OC_REPLACE|NV|P(74)|'&',        OC_BINARY|NV|P(15)|'&',
+       OC_BINARY|NV|P(25)|'/',         OC_BINARY|NV|P(25)|'%',
+               OC_BINARY|NV|P(15)|'&',         OC_BINARY|NV|P(25)|'*',
+       OC_COMPARE|VV|P(39)|4,          OC_COMPARE|VV|P(39)|3,
+               OC_COMPARE|VV|P(39)|0,          OC_COMPARE|VV|P(39)|1,
+       OC_COMPARE|VV|P(39)|2,          OC_MATCH|Sx|P(45)|'!',
+               OC_MATCH|Sx|P(45)|'~',          OC_LAND|Vx|P(55),
+       OC_LOR|Vx|P(59),                        OC_TERNARY|Vx|P(64)|'?',
+               OC_COLON|xx|P(67)|':',
+       OC_IN|SV|P(49),
+       OC_COMMA|SS|P(80),
+       OC_PGETLINE|SV|P(37),
+       OC_UNARY|xV|P(19)|'+',          OC_UNARY|xV|P(19)|'-',
+               OC_UNARY|xV|P(19)|'!',
+       0,
+       0,
+       0,
+       0,
+       0,
+       ST_IF,                  ST_DO,                  ST_FOR,                 OC_BREAK,
+       OC_CONTINUE,                                    OC_DELETE|Vx,   OC_PRINT,
+       OC_PRINTF,              OC_NEXT,                OC_NEXTFILE,
+       OC_RETURN|Vx,   OC_EXIT|Nx,
+       ST_WHILE,
+       0,
+
+       OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
+       OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
+       OC_FBLTIN|F_rn,    OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
+       OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
+       OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF,        OC_B|B_su|P(0xb6),
+       OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti,    OC_B|B_ti|P(0x0b),
+       OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
+       OC_GETLINE|SV|P(0),
+       0,      0,
+       0,
+       0
+};
+
+/* internal variable names and their initial values       */
+/* asterisk marks SPECIAL vars; $ is just no-named Field0 */
+enum {
+       CONVFMT=0,      OFMT,           FS,                     OFS,
+       ORS,            RS,                     RT,                     FILENAME,
+       SUBSEP,         ARGIND,         ARGC,           ARGV,
+       ERRNO,          FNR,
+       NR,                     NF,                     IGNORECASE,
+       ENVIRON,        F0,                     _intvarcount_
+};
+
+static char * vNames =
+       "CONVFMT\0"     "OFMT\0"        "FS\0*"         "OFS\0"
+       "ORS\0"         "RS\0*"         "RT\0"          "FILENAME\0"
+       "SUBSEP\0"      "ARGIND\0"      "ARGC\0"        "ARGV\0"
+       "ERRNO\0"       "FNR\0"
+       "NR\0"          "NF\0*"         "IGNORECASE\0*"
+       "ENVIRON\0"     "$\0*"          "\0";
+
+static char * vValues =
+       "%.6g\0"        "%.6g\0"        " \0"           " \0"
+       "\n\0"          "\n\0"          "\0"            "\0"
+       "\034\0"
+       "\377";
+
+/* hash size may grow to these values */
+#define FIRST_PRIME 61;
+static const unsigned int PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
+static const unsigned int NPRIMES = sizeof(PRIMES) / sizeof(unsigned int);
+
+/* globals */
+
+extern char **environ;
+
+static var * V[_intvarcount_];
+static chain beginseq, mainseq, endseq, *seq;
+static int nextrec, nextfile;
+static node *break_ptr, *continue_ptr;
+static rstream *iF;
+static xhash *vhash, *ahash, *fdhash, *fnhash;
+static char *programname;
+static short lineno;
+static int is_f0_split;
+static int nfields = 0;
+static var *Fields = NULL;
+static tsplitter fsplitter, rsplitter;
+static nvblock *cb = NULL;
+static char *pos;
+static char *buf;
+static int icase = FALSE;
+static int exiting = FALSE;
+
+static struct {
+       unsigned long tclass;
+       unsigned long info;
+       char *string;
+       double number;
+       short lineno;
+       int rollback;
+} t;
+
+/* function prototypes */
+extern void xregcomp(regex_t *preg, const char *regex, int cflags);
+static void handle_special(var *);
+static node *parse_expr(unsigned long);
+static void chain_group(void);
+static var *evaluate(node *, var *);
+static rstream *next_input_file(void);
+static int fmt_num(char *, int, char *, double, int);
+static int awk_exit(int);
+
+/* ---- error handling ---- */
+
+static const char EMSG_INTERNAL_ERROR[] = "Internal error";
+static const char EMSG_UNEXP_EOS[] = "Unexpected end of string";
+static const char EMSG_UNEXP_TOKEN[] = "Unexpected token";
+static const char EMSG_DIV_BY_ZERO[] = "Division by zero";
+static const char EMSG_INV_FMT[] = "Invalid format specifier";
+static const char EMSG_TOO_FEW_ARGS[] = "Too few arguments for builtin";
+static const char EMSG_NOT_ARRAY[] = "Not an array";
+static const char EMSG_POSSIBLE_ERROR[] = "Possible syntax error";
+static const char EMSG_UNDEF_FUNC[] = "Call to undefined function";
+#ifndef CONFIG_FEATURE_AWK_MATH
+static const char EMSG_NO_MATH[] = "Math support is not compiled in";
+#endif
+
+static void syntax_error(const char * const message)
+{
+       bb_error_msg("%s:%i: %s", programname, lineno, message);
+       exit(1);
+}
+
+#define runtime_error(x) syntax_error(x)
+
+
+/* ---- hash stuff ---- */
+
+static unsigned int hashidx(char *name) {
+
+       register unsigned int idx=0;
+
+       while (*name)  idx = *name++ + (idx << 6) - idx;
+       return idx;
+}
+
+/* create new hash */
+static xhash *hash_init(void) {
+
+       xhash *newhash;
+
+       newhash = (xhash *)xcalloc(1, sizeof(xhash));
+       newhash->csize = FIRST_PRIME;
+       newhash->items = (hash_item **)xcalloc(newhash->csize, sizeof(hash_item *));
+
+       return newhash;
+}
+
+/* find item in hash, return ptr to data, NULL if not found */
+static void *hash_search(xhash *hash, char *name) {
+
+       hash_item *hi;
+
+       hi = hash->items [ hashidx(name) % hash->csize ];
+       while (hi) {
+               if (strcmp(hi->name, name) == 0)
+                       return &(hi->data);
+               hi = hi->next;
+       }
+       return NULL;
+}
+
+/* grow hash if it becomes too big */
+static void hash_rebuild(xhash *hash) {
+
+       unsigned int newsize, i, idx;
+       hash_item **newitems, *hi, *thi;
+
+       if (hash->nprime == NPRIMES)
+               return;
+
+       newsize = PRIMES[hash->nprime++];
+       newitems = (hash_item **)xcalloc(newsize, sizeof(hash_item *));
+
+       for (i=0; i<hash->csize; i++) {
+               hi = hash->items[i];
+               while (hi) {
+                       thi = hi;
+                       hi = thi->next;
+                       idx = hashidx(thi->name) % newsize;
+                       thi->next = newitems[idx];
+                       newitems[idx] = thi;
+               }
+       }
+
+       free(hash->items);
+       hash->csize = newsize;
+       hash->items = newitems;
+}
+
+/* find item in hash, add it if necessary. Return ptr to data */
+static void *hash_find(xhash *hash, char *name) {
+
+       hash_item *hi;
+       unsigned int idx;
+       int l;
+
+       hi = hash_search(hash, name);
+       if (! hi) {
+               if (++hash->nel / hash->csize > 10)
+                       hash_rebuild(hash);
+
+               l = bb_strlen(name) + 1;
+               hi = xcalloc(sizeof(hash_item) + l, 1);
+               memcpy(hi->name, name, l);
+
+               idx = hashidx(name) % hash->csize;
+               hi->next = hash->items[idx];
+               hash->items[idx] = hi;
+               hash->glen += l;
+       }
+       return &(hi->data);
+}
+
+#define findvar(hash, name) (var *) hash_find ( (hash) , (name) )
+#define newvar(name) (var *) hash_find ( vhash , (name) )
+#define newfile(name) (rstream *) hash_find ( fdhash , (name) )
+#define newfunc(name) (func *) hash_find ( fnhash , (name) )
+
+static void hash_remove(xhash *hash, char *name) {
+
+       hash_item *hi, **phi;
+
+       phi = &(hash->items[ hashidx(name) % hash->csize ]);
+       while (*phi) {
+               hi = *phi;
+               if (strcmp(hi->name, name) == 0) {
+                       hash->glen -= (bb_strlen(name) + 1);
+                       hash->nel--;
+                       *phi = hi->next;
+                       free(hi);
+                       break;
+               }
+               phi = &(hi->next);
+       }
+}
+
+/* ------ some useful functions ------ */
+
+static void skip_spaces(char **s) {
+
+       register char *p = *s;
+
+       while(*p == ' ' || *p == '\t' ||
+                                       (*p == '\\' && *(p+1) == '\n' && (++p, ++t.lineno))) {
+               p++;
+       }
+       *s = p;
+}
+
+static char *nextword(char **s) {
+
+       register char *p = *s;
+
+       while (*(*s)++) ;
+
+       return p;
+}
+
+static char nextchar(char **s) {
+
+       register char c, *pps;
+
+       c = *((*s)++);
+       pps = *s;
+       if (c == '\\') c = bb_process_escape_sequence((const char**)s);
+       if (c == '\\' && *s == pps) c = *((*s)++);
+       return c;
+}
+
+static inline int isalnum_(int c) {
+
+       return (isalnum(c) || c == '_');
+}
+
+static FILE *afopen(const char *path, const char *mode) {
+
+       return (*path == '-' && *(path+1) == '\0') ? stdin : bb_xfopen(path, mode);
+}
+
+/* -------- working with variables (set/get/copy/etc) -------- */
+
+static xhash *iamarray(var *v) {
+
+       var *a = v;
+
+       while (a->type & VF_CHILD)
+               a = a->x.parent;
+
+       if (! (a->type & VF_ARRAY)) {
+               a->type |= VF_ARRAY;
+               a->x.array = hash_init();
+       }
+       return a->x.array;
+}
+
+static void clear_array(xhash *array) {
+
+       unsigned int i;
+       hash_item *hi, *thi;
+
+       for (i=0; i<array->csize; i++) {
+               hi = array->items[i];
+               while (hi) {
+                       thi = hi;
+                       hi = hi->next;
+                       free(thi->data.v.string);
+                       free(thi);
+               }
+               array->items[i] = NULL;
+       }
+       array->glen = array->nel = 0;
+}
+
+/* clear a variable */
+static var *clrvar(var *v) {
+
+       if (!(v->type & VF_FSTR))
+               free(v->string);
+
+       v->type &= VF_DONTTOUCH;
+       v->type |= VF_DIRTY;
+       v->string = NULL;
+       return v;
+}
+
+/* assign string value to variable */
+static var *setvar_p(var *v, char *value) {
+
+       clrvar(v);
+       v->string = value;
+       handle_special(v);
+
+       return v;
+}
+
+/* same as setvar_p but make a copy of string */
+static var *setvar_s(var *v, char *value) {
+
+       return setvar_p(v, (value && *value) ? bb_xstrdup(value) : NULL);
+}
+
+/* same as setvar_s but set USER flag */
+static var *setvar_u(var *v, char *value) {
+
+       setvar_s(v, value);
+       v->type |= VF_USER;
+       return v;
+}
+
+/* set array element to user string */
+static void setari_u(var *a, int idx, char *s) {
+
+       register var *v;
+       static char sidx[12];
+
+       sprintf(sidx, "%d", idx);
+       v = findvar(iamarray(a), sidx);
+       setvar_u(v, s);
+}
+
+/* assign numeric value to variable */
+static var *setvar_i(var *v, double value) {
+
+       clrvar(v);
+       v->type |= VF_NUMBER;
+       v->number = value;
+       handle_special(v);
+       return v;
+}
+
+static char *getvar_s(var *v) {
+
+       /* if v is numeric and has no cached string, convert it to string */
+       if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
+               fmt_num(buf, MAXVARFMT, getvar_s(V[CONVFMT]), v->number, TRUE);
+               v->string = bb_xstrdup(buf);
+               v->type |= VF_CACHED;
+       }
+       return (v->string == NULL) ? "" : v->string;
+}
+
+static double getvar_i(var *v) {
+
+       char *s;
+
+       if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) {
+               v->number = 0;
+               s = v->string;
+               if (s && *s) {
+                       v->number = strtod(s, &s);
+                       if (v->type & VF_USER) {
+                               skip_spaces(&s);
+                               if (*s != '\0')
+                                       v->type &= ~VF_USER;
+                       }
+               } else {
+                       v->type &= ~VF_USER;
+               }
+               v->type |= VF_CACHED;
+       }
+       return v->number;
+}
+
+static var *copyvar(var *dest, var *src) {
+
+       if (dest != src) {
+               clrvar(dest);
+               dest->type |= (src->type & ~VF_DONTTOUCH);
+               dest->number = src->number;
+               if (src->string)
+                       dest->string = bb_xstrdup(src->string);
+       }
+       handle_special(dest);
+       return dest;
+}
+
+static var *incvar(var *v) {
+
+       return setvar_i(v, getvar_i(v)+1.);
+}
+
+/* return true if v is number or numeric string */
+static int is_numeric(var *v) {
+
+       getvar_i(v);
+       return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY));
+}
+
+/* return 1 when value of v corresponds to true, 0 otherwise */
+static int istrue(var *v) {
+
+       if (is_numeric(v))
+               return (v->number == 0) ? 0 : 1;
+       else
+               return (v->string && *(v->string)) ? 1 : 0;
+}
+
+/* temporary variables allocator. Last allocated should be first freed */
+static var *nvalloc(int n) {
+
+       nvblock *pb = NULL;
+       var *v, *r;
+       int size;
+
+       while (cb) {
+               pb = cb;
+               if ((cb->pos - cb->nv) + n <= cb->size) break;
+               cb = cb->next;
+       }
+
+       if (! cb) {
+               size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
+               cb = (nvblock *)xmalloc(sizeof(nvblock) + size * sizeof(var));
+               cb->size = size;
+               cb->pos = cb->nv;
+               cb->prev = pb;
+               cb->next = NULL;
+               if (pb) pb->next = cb;
+       }
+
+       v = r = cb->pos;
+       cb->pos += n;
+
+       while (v < cb->pos) {
+               v->type = 0;
+               v->string = NULL;
+               v++;
+       }
+
+       return r;
+}
+
+static void nvfree(var *v) {
+
+       var *p;
+
+       if (v < cb->nv || v >= cb->pos)
+               runtime_error(EMSG_INTERNAL_ERROR);
+
+       for (p=v; p<cb->pos; p++) {
+               if ((p->type & (VF_ARRAY|VF_CHILD)) == VF_ARRAY) {
+                       clear_array(iamarray(p));
+                       free(p->x.array->items);
+                       free(p->x.array);
+               }
+               if (p->type & VF_WALK)
+                       free(p->x.walker);
+
+               clrvar(p);
+       }
+
+       cb->pos = v;
+       while (cb->prev && cb->pos == cb->nv) {
+               cb = cb->prev;
+       }
+}
+
+/* ------- awk program text parsing ------- */
+
+/* Parse next token pointed by global pos, place results into global t.
+ * If token isn't expected, give away. Return token class
+ */
+static unsigned long next_token(unsigned long expected) {
+
+       char *p, *pp, *s;
+       char *tl;
+       unsigned long tc, *ti;
+       int l;
+       static int concat_inserted = FALSE;
+       static unsigned long save_tclass, save_info;
+       static unsigned long ltclass = TC_OPTERM;
+
+       if (t.rollback) {
+
+               t.rollback = FALSE;
+
+       } else if (concat_inserted) {
+
+               concat_inserted = FALSE;
+               t.tclass = save_tclass;
+               t.info = save_info;
+
+       } else {
+
+               p = pos;
+
+       readnext:
+               skip_spaces(&p);
+               lineno = t.lineno;
+               if (*p == '#')
+                       while (*p != '\n' && *p != '\0') p++;
+
+               if (*p == '\n')
+                       t.lineno++;
+
+               if (*p == '\0') {
+                       tc = TC_EOF;
+
+               } else if (*p == '\"') {
+                       /* it's a string */
+                       t.string = s = ++p;
+                       while (*p != '\"') {
+                               if (*p == '\0' || *p == '\n')
+                                       syntax_error(EMSG_UNEXP_EOS);
+                               *(s++) = nextchar(&p);
+                       }
+                       p++;
+                       *s = '\0';
+                       tc = TC_STRING;
+
+               } else if ((expected & TC_REGEXP) && *p == '/') {
+                       /* it's regexp */
+                       t.string = s = ++p;
+                       while (*p != '/') {
+                               if (*p == '\0' || *p == '\n')
+                                       syntax_error(EMSG_UNEXP_EOS);
+                               if ((*s++ = *p++) == '\\') {
+                                       pp = p;
+                                       *(s-1) = bb_process_escape_sequence((const char **)&p);
+                                       if (*pp == '\\') *s++ = '\\';
+                                       if (p == pp) *s++ = *p++;
+                               }
+                       }
+                       p++;
+                       *s = '\0';
+                       tc = TC_REGEXP;
+
+               } else if (*p == '.' || isdigit(*p)) {
+                       /* it's a number */
+                       t.number = strtod(p, &p);
+                       if (*p == '.')
+                               syntax_error(EMSG_UNEXP_TOKEN);
+                       tc = TC_NUMBER;
+
+               } else {
+                       /* search for something known */
+                       tl = tokenlist;
+                       tc = 0x00000001;
+                       ti = tokeninfo;
+                       while (*tl) {
+                               l = *(tl++);
+                               if (l == NTCC) {
+                                       tc <<= 1;
+                                       continue;
+                               }
+                               /* if token class is expected, token
+                                * matches and it's not a longer word,
+                                * then this is what we are looking for
+                                */
+                               if ((tc & (expected | TC_WORD | TC_NEWLINE)) &&
+                               *tl == *p && strncmp(p, tl, l) == 0 &&
+                               !((tc & TC_WORD) && isalnum_(*(p + l)))) {
+                                       t.info = *ti;
+                                       p += l;
+                                       break;
+                               }
+                               ti++;
+                               tl += l;
+                       }
+
+                       if (! *tl) {
+                               /* it's a name (var/array/function),
+                                * otherwise it's something wrong
+                                */
+                               if (! isalnum_(*p))
+                                       syntax_error(EMSG_UNEXP_TOKEN);
+
+                               t.string = --p;
+                               while(isalnum_(*(++p))) {
+                                       *(p-1) = *p;
+                               }
+                               *(p-1) = '\0';
+                               tc = TC_VARIABLE;
+                               if (*p == '(') {
+                                       tc = TC_FUNCTION;
+                               } else {
+                                       skip_spaces(&p);
+                                       if (*p == '[') {
+                                               p++;
+                                               tc = TC_ARRAY;
+                                       }
+                               }
+                       }
+               }
+               pos = p;
+
+               /* skipping newlines in some cases */
+               if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE))
+                       goto readnext;
+
+               /* insert concatenation operator when needed */
+               if ((ltclass&TC_CONCAT1) && (tc&TC_CONCAT2) && (expected&TC_BINOP)) {
+                       concat_inserted = TRUE;
+                       save_tclass = tc;
+                       save_info = t.info;
+                       tc = TC_BINOP;
+                       t.info = OC_CONCAT | SS | P(35);
+               }
+
+               t.tclass = tc;
+       }
+       ltclass = t.tclass;
+
+       /* Are we ready for this? */
+       if (! (ltclass & expected))
+               syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
+                                                               EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
+
+       return ltclass;
+}
+
+static void rollback_token(void) { t.rollback = TRUE; }
+
+static node *new_node(unsigned long info) {
+
+       register node *n;
+
+       n = (node *)xcalloc(sizeof(node), 1);
+       n->info = info;
+       n->lineno = lineno;
+       return n;
+}
+
+static node *mk_re_node(char *s, node *n, regex_t *re) {
+
+       n->info = OC_REGEXP;
+       n->l.re = re;
+       n->r.ire = re + 1;
+       xregcomp(re, s, REG_EXTENDED);
+       xregcomp(re+1, s, REG_EXTENDED | REG_ICASE);
+
+       return n;
+}
+
+static node *condition(void) {
+
+       next_token(TC_SEQSTART);
+       return parse_expr(TC_SEQTERM);
+}
+
+/* parse expression terminated by given argument, return ptr
+ * to built subtree. Terminator is eaten by parse_expr */
+static node *parse_expr(unsigned long iexp) {
+
+       node sn;
+       node *cn = &sn;
+       node *vn, *glptr;
+       unsigned long tc, xtc;
+       var *v;
+
+       sn.info = PRIMASK;
+       sn.r.n = glptr = NULL;
+       xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp;
+
+       while (! ((tc = next_token(xtc)) & iexp)) {
+               if (glptr && (t.info == (OC_COMPARE|VV|P(39)|2))) {
+                       /* input redirection (<) attached to glptr node */
+                       cn = glptr->l.n = new_node(OC_CONCAT|SS|P(37));
+                       cn->a.n = glptr;
+                       xtc = TC_OPERAND | TC_UOPPRE;
+                       glptr = NULL;
+
+               } else if (tc & (TC_BINOP | TC_UOPPOST)) {
+                       /* for binary and postfix-unary operators, jump back over
+                        * previous operators with higher priority */
+                       vn = cn;
+                       while ( ((t.info & PRIMASK) > (vn->a.n->info & PRIMASK2)) ||
+                         ((t.info == vn->info) && ((t.info & OPCLSMASK) == OC_COLON)) )
+                               vn = vn->a.n;
+                       if ((t.info & OPCLSMASK) == OC_TERNARY)
+                               t.info += P(6);
+                       cn = vn->a.n->r.n = new_node(t.info);
+                       cn->a.n = vn->a.n;
+                       if (tc & TC_BINOP) {
+                               cn->l.n = vn;
+                               xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
+                               if ((t.info & OPCLSMASK) == OC_PGETLINE) {
+                                       /* it's a pipe */
+                                       next_token(TC_GETLINE);
+                                       /* give maximum priority to this pipe */
+                                       cn->info &= ~PRIMASK;
+                                       xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+                               }
+                       } else {
+                               cn->r.n = vn;
+                               xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+                       }
+                       vn->a.n = cn;
+
+               } else {
+                       /* for operands and prefix-unary operators, attach them
+                        * to last node */
+                       vn = cn;
+                       cn = vn->r.n = new_node(t.info);
+                       cn->a.n = vn;
+                       xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
+                       if (tc & (TC_OPERAND | TC_REGEXP)) {
+                               xtc = TC_UOPPRE | TC_BINOP | TC_OPERAND | iexp;
+                               /* one should be very careful with switch on tclass -
+                                * only simple tclasses should be used! */
+                               switch (tc) {
+                                 case TC_VARIABLE:
+                                 case TC_ARRAY:
+                                       cn->info = OC_VAR;
+                                       if ((v = hash_search(ahash, t.string)) != NULL) {
+                                               cn->info = OC_FNARG;
+                                               cn->l.i = v->x.aidx;
+                                       } else {
+                                               cn->l.v = newvar(t.string);
+                                       }
+                                       if (tc & TC_ARRAY) {
+                                               cn->info |= xS;
+                                               cn->r.n = parse_expr(TC_ARRTERM);
+                                       }
+                                       xtc = TC_UOPPOST | TC_UOPPRE | TC_BINOP | TC_OPERAND | iexp;
+                                       break;
+                               
+                                 case TC_NUMBER:
+                                 case TC_STRING:
+                                       cn->info = OC_VAR;
+                                       v = cn->l.v = xcalloc(sizeof(var), 1);
+                                       if (tc & TC_NUMBER)
+                                               setvar_i(v, t.number);
+                                       else
+                                               setvar_s(v, t.string);
+                                       break;
+
+                                 case TC_REGEXP:
+                                       mk_re_node(t.string, cn,
+                                                                       (regex_t *)xcalloc(sizeof(regex_t),2));
+                                       break;
+
+                                 case TC_FUNCTION:
+                                       cn->info = OC_FUNC;
+                                       cn->r.f = newfunc(t.string);
+                                       cn->l.n = condition();
+                                       break;
+
+                                 case TC_SEQSTART:
+                                       cn = vn->r.n = parse_expr(TC_SEQTERM);
+                                       cn->a.n = vn;
+                                       break;
+
+                                 case TC_GETLINE:
+                                       glptr = cn;
+                                       xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+                                       break;
+
+                                 case TC_BUILTIN:
+                                       cn->l.n = condition();
+                                       break;
+                               }
+                       }
+               }
+       }
+       return sn.r.n;
+}
+
+/* add node to chain. Return ptr to alloc'd node */
+static node *chain_node(unsigned long info) {
+
+       register node *n;
+
+       if (! seq->first)
+               seq->first = seq->last = new_node(0);
+
+       if (seq->programname != programname) {
+               seq->programname = programname;
+               n = chain_node(OC_NEWSOURCE);
+               n->l.s = bb_xstrdup(programname);
+       }
+
+       n = seq->last;
+       n->info = info;
+       seq->last = n->a.n = new_node(OC_DONE);
+
+       return n;
+}
+
+static void chain_expr(unsigned long info) {
+
+       node *n;
+
+       n = chain_node(info);
+       n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM);
+       if (t.tclass & TC_GRPTERM)
+               rollback_token();
+}
+
+static node *chain_loop(node *nn) {
+
+       node *n, *n2, *save_brk, *save_cont;
+
+       save_brk = break_ptr;
+       save_cont = continue_ptr;
+
+       n = chain_node(OC_BR | Vx);
+       continue_ptr = new_node(OC_EXEC);
+       break_ptr = new_node(OC_EXEC);
+       chain_group();
+       n2 = chain_node(OC_EXEC | Vx);
+       n2->l.n = nn;
+       n2->a.n = n;
+       continue_ptr->a.n = n2;
+       break_ptr->a.n = n->r.n = seq->last;
+
+       continue_ptr = save_cont;
+       break_ptr = save_brk;
+
+       return n;
+}
+
+/* parse group and attach it to chain */
+static void chain_group(void) {
+
+       unsigned long c;
+       node *n, *n2, *n3;
+
+       do {
+               c = next_token(TC_GRPSEQ);
+       } while (c & TC_NEWLINE);
+
+       if (c & TC_GRPSTART) {
+               while(next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
+                       if (t.tclass & TC_NEWLINE) continue;    
+                       rollback_token();
+                       chain_group();
+               }
+       } else if (c & (TC_OPSEQ | TC_OPTERM)) {
+               rollback_token();
+               chain_expr(OC_EXEC | Vx);
+       } else {                                                /* TC_STATEMNT */
+               switch (t.info & OPCLSMASK) {
+                       case ST_IF:
+                               n = chain_node(OC_BR | Vx);
+                               n->l.n = condition();
+                               chain_group();
+                               n2 = chain_node(OC_EXEC);
+                               n->r.n = seq->last;
+                               if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE)==TC_ELSE) {
+                                       chain_group();
+                                       n2->a.n = seq->last;
+                               } else {
+                                       rollback_token();
+                               }
+                               break;
+
+                       case ST_WHILE:
+                               n2 = condition();
+                               n = chain_loop(NULL);
+                               n->l.n = n2;
+                               break;
+
+                       case ST_DO:
+                               n2 = chain_node(OC_EXEC);
+                               n = chain_loop(NULL);
+                               n2->a.n = n->a.n;
+                               next_token(TC_WHILE);
+                               n->l.n = condition();
+                               break;
+
+                       case ST_FOR:
+                               next_token(TC_SEQSTART);
+                               n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
+                               if (t.tclass & TC_SEQTERM) {                            /* for-in */
+                                       if ((n2->info & OPCLSMASK) != OC_IN)
+                                               syntax_error(EMSG_UNEXP_TOKEN);
+                                       n = chain_node(OC_WALKINIT | VV);
+                                       n->l.n = n2->l.n;
+                                       n->r.n = n2->r.n;
+                                       n = chain_loop(NULL);
+                                       n->info = OC_WALKNEXT | Vx;
+                                       n->l.n = n2->l.n;
+                               } else {                                                                        /* for(;;) */
+                                       n = chain_node(OC_EXEC | Vx);
+                                       n->l.n = n2;
+                                       n2 = parse_expr(TC_SEMICOL);
+                                       n3 = parse_expr(TC_SEQTERM);
+                                       n = chain_loop(n3);
+                                       n->l.n = n2;
+                                       if (! n2)
+                                               n->info = OC_EXEC;
+                               }
+                               break;
+
+                       case OC_PRINT:
+                       case OC_PRINTF:
+                               n = chain_node(t.info);
+                               n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM);
+                               if (t.tclass & TC_OUTRDR) {
+                                       n->info |= t.info;
+                                       n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM);
+                               }
+                               if (t.tclass & TC_GRPTERM)
+                                       rollback_token();
+                               break;
+
+                       case OC_BREAK:
+                               n = chain_node(OC_EXEC);
+                               n->a.n = break_ptr;
+                               break;
+
+                       case OC_CONTINUE:
+                               n = chain_node(OC_EXEC);
+                               n->a.n = continue_ptr;
+                               break;
+
+                       /* delete, next, nextfile, return, exit */
+                       default:
+                               chain_expr(t.info);
+
+               }
+       }
+}
+
+static void parse_program(char *p) {
+
+       unsigned long tclass;
+       node *cn;
+       func *f;
+       var *v;
+
+       pos = p;
+       t.lineno = 1;
+       while((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
+                               TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
+
+               if (tclass & TC_OPTERM)
+                       continue;
+
+               seq = &mainseq;
+               if (tclass & TC_BEGIN) {
+                       seq = &beginseq;
+                       chain_group();
+
+               } else if (tclass & TC_END) {
+                       seq = &endseq;
+                       chain_group();
+
+               } else if (tclass & TC_FUNCDECL) {
+                       next_token(TC_FUNCTION);
+                       pos++;
+                       f = newfunc(t.string);
+                       f->body.first = NULL;
+                       f->nargs = 0;
+                       while(next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
+                               v = findvar(ahash, t.string);
+                               v->x.aidx = (f->nargs)++;
+
+                               if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
+                                       break;
+                       }
+                       seq = &(f->body);
+                       chain_group();
+                       clear_array(ahash);
+
+               } else if (tclass & TC_OPSEQ) {
+                       rollback_token();
+                       cn = chain_node(OC_TEST);
+                       cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART);
+                       if (t.tclass & TC_GRPSTART) {
+                               rollback_token();
+                               chain_group();
+                       } else {
+                               chain_node(OC_PRINT);
+                       }
+                       cn->r.n = mainseq.last;
+
+               } else /* if (tclass & TC_GRPSTART) */ {
+                       rollback_token();
+                       chain_group();
+               }
+       }
+}
+
+
+/* -------- program execution part -------- */
+
+static node *mk_splitter(char *s, tsplitter *spl) {
+
+       register regex_t *re, *ire;
+       node *n;
+
+       re = &spl->re[0];
+       ire = &spl->re[1];
+       n = &spl->n;
+       if ((n->info && OPCLSMASK) == OC_REGEXP) {
+               regfree(re);
+               regfree(ire);
+       }
+       if (bb_strlen(s) > 1) {
+               mk_re_node(s, n, re);
+       } else {
+               n->info = (unsigned long) *s;
+       }
+
+       return n;
+}
+
+/* use node as a regular expression. Supplied with node ptr and regex_t
+ * storage space. Return ptr to regex (if result points to preg, it should
+ * be later regfree'd manually
+ */
+static regex_t *as_regex(node *op, regex_t *preg) {
+
+       var *v;
+       char *s;
+
+       if ((op->info & OPCLSMASK) == OC_REGEXP) {
+               return icase ? op->r.ire : op->l.re;
+       } else {
+               v = nvalloc(1);
+               s = getvar_s(evaluate(op, v));
+               xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED);
+               nvfree(v);
+               return preg;
+       }
+}
+
+/* gradually increasing buffer */
+static void qrealloc(char **b, int n, int *size) {
+
+       if (! *b || n >= *size)
+               *b = xrealloc(*b, *size = n + (n>>1) + 80);
+}
+
+/* resize field storage space */
+static void fsrealloc(int size) {
+
+       static int maxfields = 0;
+       int i;
+
+       if (size >= maxfields) {
+               i = maxfields;
+               maxfields = size + 16;
+               Fields = (var *)xrealloc(Fields, maxfields * sizeof(var));
+               for (; i<maxfields; i++) {
+                       Fields[i].type = VF_SPECIAL;
+                       Fields[i].string = NULL;
+               }
+       }
+
+       if (size < nfields) {
+               for (i=size; i<nfields; i++) {
+                       clrvar(Fields+i);
+               }
+       }
+       nfields = size;
+}
+
+static int awk_split(char *s, node *spl, char **slist) {
+
+       int l, n=0;
+       char c[4];
+       char *s1;
+       regmatch_t pmatch[2];
+
+       /* in worst case, each char would be a separate field */
+       *slist = s1 = bb_xstrndup(s, bb_strlen(s) * 2 + 3);
+
+       c[0] = c[1] = (char)spl->info;
+       c[2] = c[3] = '\0';
+       if (*getvar_s(V[RS]) == '\0') c[2] = '\n';
+
+       if ((spl->info & OPCLSMASK) == OC_REGEXP) {             /* regex split */
+               while (*s) {
+                       l = strcspn(s, c+2);
+                       if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 &&
+                       pmatch[0].rm_so <= l) {
+                               l = pmatch[0].rm_so;
+                               if (pmatch[0].rm_eo == 0) { l++; pmatch[0].rm_eo++; }
+                       } else {
+                               pmatch[0].rm_eo = l;
+                               if (*(s+l)) pmatch[0].rm_eo++;
+                       }
+
+                       memcpy(s1, s, l);
+                       *(s1+l) = '\0';
+                       nextword(&s1);
+                       s += pmatch[0].rm_eo;
+                       n++;
+               }
+       } else if (c[0] == '\0') {              /* null split */
+               while(*s) {
+                       *(s1++) = *(s++);
+                       *(s1++) = '\0';
+                       n++;
+               }
+       } else if (c[0] != ' ') {               /* single-character split */
+               if (icase) {
+                       c[0] = toupper(c[0]);
+                       c[1] = tolower(c[1]);
+               }
+               if (*s1) n++;
+               while ((s1 = strpbrk(s1, c))) {
+                       *(s1++) = '\0';
+                       n++;
+               }
+       } else {                                /* space split */
+               while (*s) {
+                       while (isspace(*s)) s++;
+                       if (! *s) break;
+                       n++;
+                       while (*s && !isspace(*s))
+                               *(s1++) = *(s++);
+                       *(s1++) = '\0';
+               }
+       }
+       return n;
+}
+
+static void split_f0(void) {
+
+       static char *fstrings = NULL;
+       int i, n;
+       char *s;
+
+       if (is_f0_split)
+               return;
+
+       is_f0_split = TRUE;
+       free(fstrings);
+       fsrealloc(0);
+       n = awk_split(getvar_s(V[F0]), &fsplitter.n, &fstrings);
+       fsrealloc(n);
+       s = fstrings;
+       for (i=0; i<n; i++) {
+               Fields[i].string = nextword(&s);
+               Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY);
+       }
+
+       /* set NF manually to avoid side effects */
+       clrvar(V[NF]);
+       V[NF]->type = VF_NUMBER | VF_SPECIAL;
+       V[NF]->number = nfields;
+}
+
+/* perform additional actions when some internal variables changed */
+static void handle_special(var *v) {
+
+       int n;
+       char *b, *sep, *s;
+       int sl, l, len, i, bsize;
+
+       if (! (v->type & VF_SPECIAL))
+               return;
+
+       if (v == V[NF]) {
+               n = (int)getvar_i(v);
+               fsrealloc(n);
+
+               /* recalculate $0 */
+               sep = getvar_s(V[OFS]);
+               sl = bb_strlen(sep);
+               b = NULL;
+               len = 0;
+               for (i=0; i<n; i++) {
+                       s = getvar_s(&Fields[i]);
+                       l = bb_strlen(s);
+                       if (b) {
+                               memcpy(b+len, sep, sl);
+                               len += sl;
+                       }
+                       qrealloc(&b, len+l+sl, &bsize);
+                       memcpy(b+len, s, l);
+                       len += l;
+               }
+               if (b) b[len] = '\0';
+               setvar_p(V[F0], b);
+               is_f0_split = TRUE;
+
+       } else if (v == V[F0]) {
+               is_f0_split = FALSE;
+
+       } else if (v == V[FS]) {
+               mk_splitter(getvar_s(v), &fsplitter);
+
+       } else if (v == V[RS]) {
+               mk_splitter(getvar_s(v), &rsplitter);
+
+       } else if (v == V[IGNORECASE]) {
+               icase = istrue(v);
+
+       } else {                                                /* $n */
+               n = getvar_i(V[NF]);
+               setvar_i(V[NF], n > v-Fields ? n : v-Fields+1);
+               /* right here v is invalid. Just to note... */
+       }
+}
+
+/* step through func/builtin/etc arguments */
+static node *nextarg(node **pn) {
+
+       node *n;
+
+       n = *pn;
+       if (n && (n->info & OPCLSMASK) == OC_COMMA) {
+               *pn = n->r.n;
+               n = n->l.n;
+       } else {
+               *pn = NULL;
+       }
+       return n;
+}
+
+static void hashwalk_init(var *v, xhash *array) {
+
+       char **w;
+       hash_item *hi;
+       int i;
+
+       if (v->type & VF_WALK)
+               free(v->x.walker);
+
+       v->type |= VF_WALK;
+       w = v->x.walker = (char **)xcalloc(2 + 2*sizeof(char *) + array->glen, 1);
+       *w = *(w+1) = (char *)(w + 2);
+       for (i=0; i<array->csize; i++) {
+               hi = array->items[i];
+               while(hi) {
+                       strcpy(*w, hi->name);
+                       nextword(w);
+                       hi = hi->next;
+               }
+       }
+}
+
+static int hashwalk_next(var *v) {
+
+       char **w;
+
+       w = v->x.walker;
+       if (*(w+1) == *w)
+               return FALSE;
+
+       setvar_s(v, nextword(w+1));
+       return TRUE;
+}
+
+/* evaluate node, return 1 when result is true, 0 otherwise */
+static int ptest(node *pattern) {
+       static var v;
+
+       return istrue(evaluate(pattern, &v));
+}
+
+/* read next record from stream rsm into a variable v */
+static int awk_getline(rstream *rsm, var *v) {
+
+       char *b;
+       regmatch_t pmatch[2];
+       int a, p, pp=0, size;
+       int fd, so, eo, r, rp;
+       char c, *m, *s;
+
+       /* we're using our own buffer since we need access to accumulating
+        * characters
+        */
+       fd = fileno(rsm->F);
+       m = rsm->buffer;
+       a = rsm->adv;
+       p = rsm->pos;
+       size = rsm->size;
+       c = (char) rsplitter.n.info;
+       rp = 0;
+
+       if (! m) qrealloc(&m, 256, &size);
+       do {
+               b = m + a;
+               so = eo = p;
+               r = 1;
+               if (p > 0) {
+                       if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) {
+                               if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re,
+                                                                                               b, 1, pmatch, 0) == 0) {
+                                       so = pmatch[0].rm_so;
+                                       eo = pmatch[0].rm_eo;
+                                       if (b[eo] != '\0')
+                                               break;
+                               }
+                       } else if (c != '\0') {
+                               s = strchr(b+pp, c);
+                               if (s) {
+                                       so = eo = s-b;
+                                       eo++;
+                                       break;
+                               }
+                       } else {
+                               while (b[rp] == '\n')
+                                       rp++;
+                               s = strstr(b+rp, "\n\n");
+                               if (s) {
+                                       so = eo = s-b;
+                                       while (b[eo] == '\n') eo++;
+                                       if (b[eo] != '\0')
+                                               break;
+                               }
+                       }
+               }
+
+               if (a > 0) {
+                       memmove(m, (const void *)(m+a), p+1);
+                       b = m;
+                       a = 0;
+               }
+
+               qrealloc(&m, a+p+128, &size);
+               b = m + a;
+               pp = p;
+               p += safe_read(fd, b+p, size-p-1);
+               if (p < pp) {
+                       p = 0;
+                       r = 0;
+                       setvar_i(V[ERRNO], errno);
+               }
+               b[p] = '\0';
+
+       } while (p > pp);
+
+       if (p == 0) {
+               r--;
+       } else {
+               c = b[so]; b[so] = '\0';
+               setvar_s(v, b+rp);
+               v->type |= VF_USER;
+               b[so] = c;
+               c = b[eo]; b[eo] = '\0';
+               setvar_s(V[RT], b+so);
+               b[eo] = c;
+       }
+
+       rsm->buffer = m;
+       rsm->adv = a + eo;
+       rsm->pos = p - eo;
+       rsm->size = size;
+
+       return r;
+}
+
+static int fmt_num(char *b, int size, char *format, double n, int int_as_int) {
+
+       int r=0;
+       char c, *s=format;
+
+       if (int_as_int && n == (int)n) {
+               r = snprintf(b, size, "%d", (int)n);
+       } else {
+               do { c = *s; } while (*s && *++s);
+               if (strchr("diouxX", c)) {
+                       r = snprintf(b, size, format, (int)n);
+               } else if (strchr("eEfgG", c)) {
+                       r = snprintf(b, size, format, n);
+               } else {
+                       runtime_error(EMSG_INV_FMT);
+               }
+       }
+       return r;
+}
+
+
+/* formatted output into an allocated buffer, return ptr to buffer */
+static char *awk_printf(node *n) {
+
+       char *b = NULL;
+       char *fmt, *s, *s1, *f;
+       int i, j, incr, bsize;
+       char c, c1;
+       var *v, *arg;
+
+       v = nvalloc(1);
+       fmt = f = bb_xstrdup(getvar_s(evaluate(nextarg(&n), v)));
+
+       i = 0;
+       while (*f) {
+               s = f;
+               while (*f && (*f != '%' || *(++f) == '%'))
+                       f++;
+               while (*f && !isalpha(*f))
+                       f++;
+
+               incr = (f - s) + MAXVARFMT;
+               qrealloc(&b, incr+i, &bsize);
+               c = *f; if (c != '\0') f++;
+               c1 = *f ; *f = '\0';
+               arg = evaluate(nextarg(&n), v);
+
+               j = i;
+               if (c == 'c' || !c) {
+                       i += sprintf(b+i, s,
+                                       is_numeric(arg) ? (char)getvar_i(arg) : *getvar_s(arg));
+
+               } else if (c == 's') {
+                   s1 = getvar_s(arg);
+                       qrealloc(&b, incr+i+bb_strlen(s1), &bsize);
+                       i += sprintf(b+i, s, s1);
+
+               } else {
+                       i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE);
+               }
+               *f = c1;
+
+               /* if there was an error while sprintf, return value is negative */
+               if (i < j) i = j;
+
+       }
+
+       b = xrealloc(b, i+1);
+       free(fmt);
+       nvfree(v);
+       b[i] = '\0';
+       return b;
+}
+
+/* common substitution routine
+ * replace (nm) substring of (src) that match (n) with (repl), store
+ * result into (dest), return number of substitutions. If nm=0, replace
+ * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable
+ * subexpression matching (\1-\9)
+ */
+static int awk_sub(node *rn, char *repl, int nm, var *src, var *dest, int ex) {
+
+       char *ds = NULL;
+       char *sp, *s;
+       int c, i, j, di, rl, so, eo, nbs, n, dssize;
+       regmatch_t pmatch[10];
+       regex_t sreg, *re;
+
+       re = as_regex(rn, &sreg);
+       if (! src) src = V[F0];
+       if (! dest) dest = V[F0];
+
+       i = di = 0;
+       sp = getvar_s(src);
+       rl = bb_strlen(repl);
+       while (regexec(re, sp, 10, pmatch, sp==getvar_s(src) ? 0:REG_NOTBOL) == 0) {
+               so = pmatch[0].rm_so;
+               eo = pmatch[0].rm_eo;
+
+               qrealloc(&ds, di + eo + rl, &dssize);
+               memcpy(ds + di, sp, eo);
+               di += eo;
+               if (++i >= nm) {
+                       /* replace */
+                       di -= (eo - so);
+                       nbs = 0;
+                       for (s = repl; *s; s++) {
+                               ds[di++] = c = *s;
+                               if (c == '\\') {
+                                       nbs++;
+                                       continue;
+                               }
+                               if (c == '&' || (ex && c >= '0' && c <= '9')) {
+                                       di -= ((nbs + 3) >> 1);
+                                       j = 0;
+                                       if (c != '&') {
+                                               j = c - '0';
+                                               nbs++;
+                                       }
+                                       if (nbs % 2) {
+                                               ds[di++] = c;
+                                       } else {
+                                               n = pmatch[j].rm_eo - pmatch[j].rm_so;
+                                               qrealloc(&ds, di + rl + n, &dssize);
+                                               memcpy(ds + di, sp + pmatch[j].rm_so, n);
+                                               di += n;
+                                       }
+                               }
+                               nbs = 0;
+                       }
+               }
+
+               sp += eo;
+               if (i == nm) break;
+               if (eo == so) {
+                       if (! (ds[di++] = *sp++)) break;
+               }
+       }
+
+       qrealloc(&ds, di + strlen(sp), &dssize);
+       strcpy(ds + di, sp);
+       setvar_p(dest, ds);
+       if (re == &sreg) regfree(re);
+       return i;
+}
+
+static var *exec_builtin(node *op, var *res) {
+
+       int (*to_xxx)(int);
+       var *tv;
+       node *an[4];
+       var  *av[4];
+       char *as[4];
+       regmatch_t pmatch[2];
+       regex_t sreg, *re;
+       static tsplitter tspl;
+       node *spl;
+       unsigned long isr, info;
+       int nargs;
+       time_t tt;
+       char *s, *s1;
+       int i, l, ll, n;
+
+       tv = nvalloc(4);
+       isr = info = op->info;
+       op = op->l.n;
+
+       av[2] = av[3] = NULL;
+       for (i=0 ; i<4 && op ; i++) {
+               an[i] = nextarg(&op);
+               if (isr & 0x09000000) av[i] = evaluate(an[i], &tv[i]);
+               if (isr & 0x08000000) as[i] = getvar_s(av[i]);
+               isr >>= 1;
+       }
+
+       nargs = i;
+       if (nargs < (info >> 30))
+               runtime_error(EMSG_TOO_FEW_ARGS);
+
+       switch (info & OPNMASK) {
+
+         case B_a2:
+#ifdef CONFIG_FEATURE_AWK_MATH
+               setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1])));
+#else
+               runtime_error(EMSG_NO_MATH);
+#endif
+               break;
+
+         case B_sp:
+               if (nargs > 2) {
+                       spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
+                               an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl);
+               } else {
+                       spl = &fsplitter.n;
+               }
+
+               n = awk_split(as[0], spl, &s);
+               s1 = s;
+               clear_array(iamarray(av[1]));
+               for (i=1; i<=n; i++)
+                       setari_u(av[1], i, nextword(&s1));
+               free(s);
+               setvar_i(res, n);
+               break;
+
+         case B_ss:
+               l = bb_strlen(as[0]);
+               i = getvar_i(av[1]) - 1;
+               if (i>l) i=l; if (i<0) i=0;
+               n = (nargs > 2) ? getvar_i(av[2]) : l-i;
+               if (n<0) n=0;
+               s = xmalloc(n+1);
+               strncpy(s, as[0]+i, n);
+               s[n] = '\0';
+               setvar_p(res, s);
+               break;
+
+         case B_lo:
+               to_xxx = tolower;
+               goto lo_cont;
+
+         case B_up:
+               to_xxx = toupper;
+lo_cont:
+               s1 = s = bb_xstrdup(as[0]);
+               while (*s1) {
+                       *s1 = (*to_xxx)(*s1);
+                       s1++;
+               }
+               setvar_p(res, s);
+               break;
+
+         case B_ix:
+               n = 0;
+               ll = bb_strlen(as[1]);
+               l = bb_strlen(as[0]) - ll;
+               if (ll > 0 && l >= 0) {
+                       if (! icase) {
+                               s = strstr(as[0], as[1]);
+                               if (s) n = (s - as[0]) + 1;
+                       } else {
+                               /* this piece of code is terribly slow and
+                                * really should be rewritten
+                                */
+                               for (i=0; i<=l; i++) {
+                                       if (strncasecmp(as[0]+i, as[1], ll) == 0) {
+                                               n = i+1;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               setvar_i(res, n);
+               break;
+
+         case B_ti:
+               if (nargs > 1)
+                       tt = getvar_i(av[1]);
+               else
+                       time(&tt);
+               s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
+               i = strftime(buf, MAXVARFMT, s, localtime(&tt));
+               buf[i] = '\0';
+               setvar_s(res, buf);
+               break;
+
+         case B_ma:
+               re = as_regex(an[1], &sreg);
+               n = regexec(re, as[0], 1, pmatch, 0);
+               if (n == 0) {
+                       pmatch[0].rm_so++;
+                       pmatch[0].rm_eo++;
+               } else {
+                       pmatch[0].rm_so = 0;
+                       pmatch[0].rm_eo = -1;
+               }
+               setvar_i(newvar("RSTART"), pmatch[0].rm_so);
+               setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so);
+               setvar_i(res, pmatch[0].rm_so);
+               if (re == &sreg) regfree(re);
+               break;
+
+         case B_ge:
+               awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE);
+               break;
+
+         case B_gs:
+               setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE));
+               break;
+
+         case B_su:
+               setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE));
+               break;
+       }
+
+       nvfree(tv);
+       return res;
+}
+
+/*
+ * Evaluate node - the heart of the program. Supplied with subtree
+ * and place where to store result. returns ptr to result.
+ */
+#define XC(n) ((n) >> 8)
+
+static var *evaluate(node *op, var *res) {
+
+       /* This procedure is recursive so we should count every byte */
+       static var *fnargs = NULL;
+       static unsigned int seed = 1;
+       static regex_t sreg;
+       node *op1;
+       var *v1;
+       union {
+               var *v;
+               char *s;
+               double d;
+               int i;
+       } L, R;
+       unsigned long opinfo;
+       short opn;
+       union {
+               char *s;
+               rstream *rsm;
+               FILE *F;
+               var *v;
+               regex_t *re;
+               unsigned long info;
+       } X;
+
+       if (! op)
+               return setvar_s(res, NULL);
+
+       v1 = nvalloc(2);
+
+       while (op) {
+
+               opinfo = op->info;
+               opn = (short)(opinfo & OPNMASK);
+               lineno = op->lineno;
+
+               /* execute inevitable things */
+               op1 = op->l.n;
+               if (opinfo & OF_RES1) X.v = L.v = evaluate(op1, v1);
+               if (opinfo & OF_RES2) R.v = evaluate(op->r.n, v1+1);
+               if (opinfo & OF_STR1) L.s = getvar_s(L.v);
+               if (opinfo & OF_STR2) R.s = getvar_s(R.v);
+               if (opinfo & OF_NUM1) L.d = getvar_i(L.v);
+
+               switch (XC(opinfo & OPCLSMASK)) {
+
+                 /* -- iterative node type -- */
+
+                 /* test pattern */
+                 case XC( OC_TEST ):
+                       if ((op1->info & OPCLSMASK) == OC_COMMA) {
+                               /* it's range pattern */
+                               if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
+                                       op->info |= OF_CHECKED;
+                                       if (ptest(op1->r.n))
+                                               op->info &= ~OF_CHECKED;
+
+                                       op = op->a.n;
+                               } else {
+                                       op = op->r.n;
+                               }
+                       } else {
+                               op = (ptest(op1)) ? op->a.n : op->r.n;
+                       }
+                       break;
+
+                 /* just evaluate an expression, also used as unconditional jump */
+                 case XC( OC_EXEC ):
+                       break;
+
+                 /* branch, used in if-else and various loops */
+                 case XC( OC_BR ):
+                       op = istrue(L.v) ? op->a.n : op->r.n;
+                       break;
+
+                 /* initialize for-in loop */
+                 case XC( OC_WALKINIT ):
+                       hashwalk_init(L.v, iamarray(R.v));
+                       break;
+
+                 /* get next array item */
+                 case XC( OC_WALKNEXT ):
+                       op = hashwalk_next(L.v) ? op->a.n : op->r.n;
+                       break;
+
+                 case XC( OC_PRINT ):
+                 case XC( OC_PRINTF ):
+                       X.F = stdout;
+                       if (op->r.n) {
+                               X.rsm = newfile(R.s);
+                               if (! X.rsm->F) {
+                                       if (opn == '|') {
+                                               if((X.rsm->F = popen(R.s, "w")) == NULL)
+                                                       bb_perror_msg_and_die("popen");
+                                               X.rsm->is_pipe = 1;
+                                       } else {
+                                               X.rsm->F = bb_xfopen(R.s, opn=='w' ? "w" : "a");
+                                       }
+                               }
+                               X.F = X.rsm->F;
+                       }
+
+                       if ((opinfo & OPCLSMASK) == OC_PRINT) {
+                               if (! op1) {
+                                       fputs(getvar_s(V[F0]), X.F);
+                               } else {
+                                       while (op1) {
+                                               L.v = evaluate(nextarg(&op1), v1);
+                                               if (L.v->type & VF_NUMBER) {
+                                                       fmt_num(buf, MAXVARFMT, getvar_s(V[OFMT]),
+                                                                                                               getvar_i(L.v), TRUE);
+                                                       fputs(buf, X.F);
+                                               } else {
+                                                       fputs(getvar_s(L.v), X.F);
+                                               }
+
+                                               if (op1) fputs(getvar_s(V[OFS]), X.F);
+                                       }
+                               }
+                               fputs(getvar_s(V[ORS]), X.F);
+
+                       } else {        /* OC_PRINTF */
+                               L.s = awk_printf(op1);
+                               fputs(L.s, X.F);
+                               free(L.s);
+                       }
+                       fflush(X.F);
+                       break;
+
+                 case XC( OC_DELETE ):
+                       X.info = op1->info & OPCLSMASK;
+                       if (X.info == OC_VAR) {
+                               R.v = op1->l.v;
+                       } else if (X.info == OC_FNARG) {
+                               R.v = &fnargs[op1->l.i];
+                       } else {
+                               runtime_error(EMSG_NOT_ARRAY);
+                       }
+
+                       if (op1->r.n) {
+                               clrvar(L.v);
+                               L.s = getvar_s(evaluate(op1->r.n, v1));
+                               hash_remove(iamarray(R.v), L.s);
+                       } else {
+                               clear_array(iamarray(R.v));
+                       }
+                       break;
+
+                 case XC( OC_NEWSOURCE ):
+                       programname = op->l.s;
+                       break;
+
+                 case XC( OC_RETURN ):
+                       copyvar(res, L.v);
+                       break;
+
+                 case XC( OC_NEXTFILE ):
+                       nextfile = TRUE;
+                 case XC( OC_NEXT ):
+                       nextrec = TRUE;
+                 case XC( OC_DONE ):
+                       clrvar(res);
+                       break;
+
+                 case XC( OC_EXIT ):
+                       awk_exit(L.d);
+
+                 /* -- recursive node type -- */
+
+                 case XC( OC_VAR ):
+                       L.v = op->l.v;
+                       if (L.v == V[NF])
+                               split_f0();
+                       goto v_cont;
+
+                 case XC( OC_FNARG ):
+                       L.v = &fnargs[op->l.i];
+
+v_cont:
+                       res = (op->r.n) ? findvar(iamarray(L.v), R.s) : L.v;
+                       break;
+
+                 case XC( OC_IN ):
+                       setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
+                       break;
+
+                 case XC( OC_REGEXP ):
+                       op1 = op;
+                       L.s = getvar_s(V[F0]);
+                       goto re_cont;
+
+                 case XC( OC_MATCH ):
+                       op1 = op->r.n;
+re_cont:
+                       X.re = as_regex(op1, &sreg);
+                       R.i = regexec(X.re, L.s, 0, NULL, 0);
+                       if (X.re == &sreg) regfree(X.re);
+                       setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0));
+                       break;
+
+                 case XC( OC_MOVE ):
+                       /* if source is a temporary string, jusk relink it to dest */
+                       if (R.v == v1+1 && R.v->string) {
+                               res = setvar_p(L.v, R.v->string);
+                               R.v->string = NULL;
+                       } else {
+                               res = copyvar(L.v, R.v);
+                       }
+                       break;
+
+                 case XC( OC_TERNARY ):
+                       if ((op->r.n->info & OPCLSMASK) != OC_COLON)
+                               runtime_error(EMSG_POSSIBLE_ERROR);
+                       res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
+                       break;
+
+                 case XC( OC_FUNC ):
+                       if (! op->r.f->body.first)
+                               runtime_error(EMSG_UNDEF_FUNC);
+
+                       X.v = R.v = nvalloc(op->r.f->nargs+1);
+                       while (op1) {
+                               L.v = evaluate(nextarg(&op1), v1);
+                               copyvar(R.v, L.v);
+                               R.v->type |= VF_CHILD;
+                               R.v->x.parent = L.v;
+                               if (++R.v - X.v >= op->r.f->nargs)
+                                       break;
+                       }
+
+                       R.v = fnargs;
+                       fnargs = X.v;
+
+                       L.s = programname;
+                       res = evaluate(op->r.f->body.first, res);
+                       programname = L.s;
+
+                       nvfree(fnargs);
+                       fnargs = R.v;
+                       break;
+
+                 case XC( OC_GETLINE ):
+                 case XC( OC_PGETLINE ):
+                       if (op1) {
+                               X.rsm = newfile(L.s);
+                               if (! X.rsm->F) {
+                                       if ((opinfo & OPCLSMASK) == OC_PGETLINE) {
+                                               X.rsm->F = popen(L.s, "r");
+                                               X.rsm->is_pipe = TRUE;
+                                       } else {
+                                               X.rsm->F = fopen(L.s, "r");             /* not bb_xfopen! */
+                                       }
+                               }
+                       } else {
+                               if (! iF) iF = next_input_file();
+                               X.rsm = iF;
+                       }
+
+                       if (! X.rsm->F) {
+                               setvar_i(V[ERRNO], errno);
+                               setvar_i(res, -1);
+                               break;
+                       }
+
+                       if (! op->r.n)
+                               R.v = V[F0];
+
+                       L.i = awk_getline(X.rsm, R.v);
+                       if (L.i > 0) {
+                               if (! op1) {
+                                       incvar(V[FNR]);
+                                       incvar(V[NR]);
+                               }
+                       }
+                       setvar_i(res, L.i);
+                       break;
+
+                 /* simple builtins */
+                 case XC( OC_FBLTIN ):
+                       switch (opn) {
+
+                         case F_in:
+                               R.d = (int)L.d;
+                               break;
+
+                         case F_rn:
+                               R.d =  (double)rand() / (double)RAND_MAX;
+                               break;
+
+#ifdef CONFIG_FEATURE_AWK_MATH
+                         case F_co:
+                               R.d = cos(L.d);
+                               break;
+
+                         case F_ex:
+                               R.d = exp(L.d);
+                               break;
+
+                         case F_lg:
+                               R.d = log(L.d);
+                               break;
+
+                         case F_si:
+                               R.d = sin(L.d);
+                               break;
+
+                         case F_sq:
+                               R.d = sqrt(L.d);
+                               break;
+#else
+                         case F_co:
+                         case F_ex:
+                         case F_lg:
+                         case F_si:
+                         case F_sq:
+                               runtime_error(EMSG_NO_MATH);
+                               break;
+#endif
+
+                         case F_sr:
+                               R.d = (double)seed;
+                               seed = op1 ? (unsigned int)L.d : (unsigned int)time(NULL);
+                               srand(seed);
+                               break;
+
+                         case F_ti:
+                               R.d = time(NULL);
+                               break;
+
+                         case F_le:
+                               if (! op1)
+                                       L.s = getvar_s(V[F0]);
+                               R.d = bb_strlen(L.s);
+                               break;
+
+                         case F_sy:
+                               fflush(NULL);
+                               R.d = (L.s && *L.s) ? system(L.s) : 0;
+                               break;
+
+                         case F_ff:
+                               if (! op1)
+                                       fflush(stdout);
+                               else {
+                                       if (L.s && *L.s) {
+                                               X.rsm = newfile(L.s);
+                                               fflush(X.rsm->F);
+                                       } else {
+                                               fflush(NULL);
+                                       }
+                               }
+                               break;
+
+                         case F_cl:
+                               X.rsm = (rstream *)hash_search(fdhash, L.s);
+                               if (X.rsm) {
+                                       R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F);
+                                       free(X.rsm->buffer);
+                                       hash_remove(fdhash, L.s);
+                               }
+                               if (R.i != 0)
+                                       setvar_i(V[ERRNO], errno);
+                               R.d = (double)R.i;
+                               break;
+                       }
+                       setvar_i(res, R.d);
+                       break;
+
+                 case XC( OC_BUILTIN ):
+                       res = exec_builtin(op, res);
+                       break;
+
+                 case XC( OC_SPRINTF ):
+                       setvar_p(res, awk_printf(op1));
+                       break;
+
+                 case XC( OC_UNARY ):
+                       X.v = R.v;
+                       L.d = R.d = getvar_i(R.v);
+                       switch (opn) {
+                         case 'P':
+                               L.d = ++R.d;
+                               goto r_op_change;
+                         case 'p':
+                               R.d++;
+                               goto r_op_change;
+                         case 'M':
+                               L.d = --R.d;
+                               goto r_op_change;
+                         case 'm':
+                               R.d--;
+                               goto r_op_change;
+                         case '!':
+                           L.d = istrue(X.v) ? 0 : 1;
+                               break;
+                         case '-':
+                               L.d = -R.d;
+                               break;
+                       r_op_change:
+                               setvar_i(X.v, R.d);
+                       }
+                       setvar_i(res, L.d);
+                       break;
+
+                 case XC( OC_FIELD ):
+                       R.i = (int)getvar_i(R.v);
+                       if (R.i == 0) {
+                               res = V[F0];
+                       } else {
+                               split_f0();
+                               if (R.i > nfields)
+                                       fsrealloc(R.i);
+
+                               res = &Fields[R.i-1];
+                       }
+                       break;
+
+                 /* concatenation (" ") and index joining (",") */
+                 case XC( OC_CONCAT ):
+                 case XC( OC_COMMA ):
+                       opn = bb_strlen(L.s) + bb_strlen(R.s) + 2;
+                       X.s = (char *)xmalloc(opn);
+                       strcpy(X.s, L.s);
+                       if ((opinfo & OPCLSMASK) == OC_COMMA) {
+                               L.s = getvar_s(V[SUBSEP]);
+                               X.s = (char *)xrealloc(X.s, opn + bb_strlen(L.s));
+                               strcat(X.s, L.s);
+                       }
+                       strcat(X.s, R.s);
+                       setvar_p(res, X.s);
+                       break;
+
+                 case XC( OC_LAND ):
+                       setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
+                       break;
+
+                 case XC( OC_LOR ):
+                       setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
+                       break;
+
+                 case XC( OC_BINARY ):
+                 case XC( OC_REPLACE ):
+                       R.d = getvar_i(R.v);
+                       switch (opn) {
+                         case '+':
+                               L.d += R.d;
+                               break;
+                         case '-':
+                               L.d -= R.d;
+                               break;
+                         case '*':
+                               L.d *= R.d;
+                               break;
+                         case '/':
+                               if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
+                               L.d /= R.d;
+                               break;
+                         case '&':
+#ifdef CONFIG_FEATURE_AWK_MATH
+                               L.d = pow(L.d, R.d);
+#else
+                               runtime_error(EMSG_NO_MATH);
+#endif
+                               break;
+                         case '%':
+                               if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
+                               L.d -= (int)(L.d / R.d) * R.d;
+                               break;
+                       }
+                       res = setvar_i(((opinfo&OPCLSMASK) == OC_BINARY) ? res : X.v, L.d);
+                       break;
+
+                 case XC( OC_COMPARE ):
+                       if (is_numeric(L.v) && is_numeric(R.v)) {
+                               L.d = getvar_i(L.v) - getvar_i(R.v);
+                       } else {
+                               L.s = getvar_s(L.v);
+                               R.s = getvar_s(R.v);
+                               L.d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s);
+                       }
+                       switch (opn & 0xfe) {
+                         case 0:
+                               R.i = (L.d > 0);
+                               break;
+                         case 2:
+                               R.i = (L.d >= 0);
+                               break;
+                         case 4:
+                               R.i = (L.d == 0);
+                               break;
+                       }
+                       setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0);
+                       break;
+
+                 default:
+                       runtime_error(EMSG_POSSIBLE_ERROR);
+               }
+               if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
+                       op = op->a.n;
+               if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
+                       break;
+               if (nextrec)
+                       break;
+       }
+       nvfree(v1);
+       return res;
+}
+
+
+/* -------- main & co. -------- */
+
+static int awk_exit(int r) {
+
+       unsigned int i;
+       hash_item *hi;
+       static var tv;
+
+       if (! exiting) {
+               exiting = TRUE;
+               nextrec = FALSE;
+               evaluate(endseq.first, &tv);
+       }
+
+       /* waiting for children */
+       for (i=0; i<fdhash->csize; i++) {
+               hi = fdhash->items[i];
+               while(hi) {
+                       if (hi->data.rs.F && hi->data.rs.is_pipe)
+                               pclose(hi->data.rs.F);
+                       hi = hi->next;
+               }
+       }
+
+       exit(r);
+}
+
+/* if expr looks like "var=value", perform assignment and return 1,
+ * otherwise return 0 */
+static int is_assignment(char *expr) {
+
+       char *exprc, *s, *s0, *s1;
+
+       exprc = bb_xstrdup(expr);
+       if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
+               free(exprc);
+               return FALSE;
+       }
+
+       *(s++) = '\0';
+       s0 = s1 = s;
+       while (*s)
+               *(s1++) = nextchar(&s);
+
+       *s1 = '\0';
+       setvar_u(newvar(exprc), s0);
+       free(exprc);
+       return TRUE;
+}
+
+/* switch to next input file */
+static rstream *next_input_file(void) {
+
+       static rstream rsm;
+       FILE *F = NULL;
+       char *fname, *ind;
+       static int files_happen = FALSE;
+
+       if (rsm.F) fclose(rsm.F);
+       rsm.F = NULL;
+       rsm.pos = rsm.adv = 0;
+
+       do {
+               if (getvar_i(V[ARGIND])+1 >= getvar_i(V[ARGC])) {
+                       if (files_happen)
+                               return NULL;
+                       fname = "-";
+                       F = stdin;
+               } else {
+                       ind = getvar_s(incvar(V[ARGIND]));
+                       fname = getvar_s(findvar(iamarray(V[ARGV]), ind));
+                       if (fname && *fname && !is_assignment(fname))
+                               F = afopen(fname, "r");
+               }
+       } while (!F);
+
+       files_happen = TRUE;
+       setvar_s(V[FILENAME], fname);
+       rsm.F = F;
+       return &rsm;
+}
+
+extern int awk_main(int argc, char **argv) {
+
+       char *s, *s1;
+       int i, j, c;
+       var *v;
+       static var tv;
+       char **envp;
+       static int from_file = FALSE;
+       rstream *rsm;
+       FILE *F, *stdfiles[3];
+       static char * stdnames = "/dev/stdin\0/dev/stdout\0/dev/stderr";
+
+       /* allocate global buffer */
+       buf = xmalloc(MAXVARFMT+1);
+
+       vhash = hash_init();
+       ahash = hash_init();
+       fdhash = hash_init();
+       fnhash = hash_init();
+
+       /* initialize variables */
+       for (i=0;  *vNames;  i++) {
+               V[i] = v = newvar(nextword(&vNames));
+               if (*vValues != '\377')
+                       setvar_s(v, nextword(&vValues));
+               else
+                       setvar_i(v, 0);
+
+               if (*vNames == '*') {
+                       v->type |= VF_SPECIAL;
+                       vNames++;
+               }
+       }
+
+       handle_special(V[FS]);
+       handle_special(V[RS]);
+
+       stdfiles[0] = stdin;
+       stdfiles[1] = stdout;
+       stdfiles[2] = stderr;
+       for (i=0; i<3; i++) {
+               rsm = newfile(nextword(&stdnames));
+               rsm->F = stdfiles[i];
+       }
+
+       for (envp=environ; *envp; envp++) {
+               s = bb_xstrdup(*envp);
+               s1 = strchr(s, '=');
+               if (!s1) {
+                       goto keep_going;
+               }
+               *(s1++) = '\0';
+               setvar_u(findvar(iamarray(V[ENVIRON]), s), s1);
+keep_going:
+               free(s);
+       }
+
+       while((c = getopt(argc, argv, "F:v:f:W:")) != EOF) {
+               switch (c) {
+                       case 'F':
+                               setvar_s(V[FS], optarg);
+                               break;
+                       case 'v':
+                               if (! is_assignment(optarg))
+                                       bb_show_usage();
+                               break;
+                       case 'f':
+                               from_file = TRUE;
+                               F = afopen(programname = optarg, "r");
+                               s = NULL;
+                               /* one byte is reserved for some trick in next_token */
+                               for (i=j=1; j>0; i+=j) {
+                                       s = (char *)xrealloc(s, i+4096);
+                                       j = fread(s+i, 1, 4094, F);
+                               }
+                               s[i] = '\0';
+                               fclose(F);
+                               parse_program(s+1);
+                               free(s);
+                               break;
+                       case 'W':
+                               bb_error_msg("Warning: unrecognized option '-W %s' ignored\n", optarg);
+                               break;
+
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       if (!from_file) {
+               if (argc == optind)
+                       bb_show_usage();
+               programname="cmd. line";
+               parse_program(argv[optind++]);
+
+       }
+
+       /* fill in ARGV array */
+       setvar_i(V[ARGC], argc - optind + 1);
+       setari_u(V[ARGV], 0, "awk");
+       for(i=optind; i < argc; i++)
+               setari_u(V[ARGV], i+1-optind, argv[i]);
+
+       evaluate(beginseq.first, &tv);
+       if (! mainseq.first && ! endseq.first)
+               awk_exit(EXIT_SUCCESS);
+
+       /* input file could already be opened in BEGIN block */
+       if (! iF) iF = next_input_file();
+
+       /* passing through input files */
+       while (iF) {
+
+               nextfile = FALSE;
+               setvar_i(V[FNR], 0);
+
+               while ((c = awk_getline(iF, V[F0])) > 0) {
+
+                       nextrec = FALSE;
+                       incvar(V[NR]);
+                       incvar(V[FNR]);
+                       evaluate(mainseq.first, &tv);
+
+                       if (nextfile)
+                               break;
+               }
+
+               if (c < 0)
+                       runtime_error(strerror(errno));
+
+               iF = next_input_file();
+
+       }
+
+       awk_exit(EXIT_SUCCESS);
+
+       return 0;
+}
+
diff --git a/busybox/editors/patch.c b/busybox/editors/patch.c
new file mode 100644 (file)
index 0000000..6a68d2e
--- /dev/null
@@ -0,0 +1,290 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  busybox patch applet to handle the unified diff format.
+ *  Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *
+ *  This applet is written to work with patches generated by GNU diff,
+ *  where there is equivalent functionality busybox patch shall behave
+ *  as per GNU patch.
+ *
+ *  There is a SUSv3 specification for patch, however it looks to be
+ *  incomplete, it doesnt even mention unified diff format.
+ *  http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
+ *
+ *  Issues
+ *   - Non-interactive
+ *   - Patches must apply cleanly or the hunk will fail.
+ *   - Reject file isnt saved
+ *   -
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+#include "libbb.h"
+
+static int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count)
+{
+       int i = 0;
+
+       while (src_stream && (i < lines_count)) {
+               char *line;
+               line = bb_get_line_from_file(src_stream);
+               if (line == NULL) {
+                       break;
+               }
+               if (fputs(line, dest_stream) == EOF) {
+                       bb_perror_msg_and_die("Error writing to new file");
+               }
+               free(line);
+
+               i++;
+       }
+       return(i);
+}
+
+/* If patch_level is -1 it will remove all directory names
+ * char *line must be greater than 4 chars
+ * returns NULL if the file doesnt exist or error
+ * returns malloc'ed filename
+ */
+
+static unsigned char *extract_filename(char *line, unsigned short patch_level)
+{
+       char *filename_start_ptr = line + 4;
+       int i;
+
+       /* Terminate string at end of source filename */
+       {
+               char *line_ptr;
+               line_ptr = strchr(filename_start_ptr, '\t');
+               if (!line_ptr) {
+                       bb_perror_msg("Malformed line %s", line);
+                       return(NULL);
+               }
+               *line_ptr = '\0';
+       }
+
+       /* Skip over (patch_level) number of leading directories */
+       for (i = 0; i < patch_level; i++) {
+               char *dirname_ptr;
+
+               dirname_ptr = strchr(filename_start_ptr, '/');
+               if (!dirname_ptr) {
+                       break;
+               }
+               filename_start_ptr = dirname_ptr + 1;
+       }
+
+       return(bb_xstrdup(filename_start_ptr));
+}
+
+static int file_doesnt_exist(const char *filename)
+{
+       struct stat statbuf;
+       return(stat(filename, &statbuf));
+}
+
+extern int patch_main(int argc, char **argv)
+{
+       unsigned int patch_level = -1;
+       char *patch_line;
+       int ret = 0;
+
+       /* Handle 'p' option */
+       if (argv[1] && (argv[1][0] == '-') && (argv[1][1] == 'p')) {
+               patch_level = atoi(&argv[1][2]);
+       }
+
+       patch_line = bb_get_line_from_file(stdin);
+       while (patch_line) {
+               FILE *src_stream;
+               FILE *dst_stream;
+               char *original_filename;
+               char *new_filename;
+               char *backup_filename;
+               unsigned int src_cur_line = 1;
+               unsigned int dest_cur_line = 0;
+               unsigned int dest_beg_line;
+               unsigned int bad_hunk_count = 0;
+               unsigned int hunk_count = 0;
+               char copy_trailing_lines_flag = 0;
+
+               /* Skip everything upto the "---" marker
+                * No need to parse the lines "Only in <dir>", and "diff <args>"
+                */
+               while (patch_line && strncmp(patch_line, "--- ", 4) != 0) {
+                       free(patch_line);
+                       patch_line = bb_get_line_from_file(stdin);
+               }
+
+               /* Extract the filename used before the patch was generated */
+               original_filename = extract_filename(patch_line, patch_level);
+               free(patch_line);
+
+               patch_line = bb_get_line_from_file(stdin);
+               if (strncmp(patch_line, "+++ ", 4) != 0) {
+                       ret = 2;
+                       bb_error_msg("Invalid patch");
+                       continue;
+               }
+               new_filename = extract_filename(patch_line, patch_level);
+               free(patch_line);
+
+               if (file_doesnt_exist(new_filename)) {
+                       char *line_ptr;
+                       /* Create leading directories */
+                       line_ptr = strrchr(new_filename, '/');
+                       if (line_ptr) {
+                               *line_ptr = '\0';
+                               bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
+                               *line_ptr = '/';
+                       }
+                       dst_stream = bb_xfopen(new_filename, "w+");
+                       backup_filename = NULL;
+               } else {
+                       backup_filename = xmalloc(strlen(new_filename) + 6);
+                       strcpy(backup_filename, new_filename);
+                       strcat(backup_filename, ".orig");
+                       if (rename(new_filename, backup_filename) == -1) {
+                               bb_perror_msg_and_die("Couldnt create file %s", backup_filename);
+                       }
+                       dst_stream = bb_xfopen(new_filename, "w");
+               }
+
+               if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) {
+                       src_stream = NULL;
+               } else {
+                       if (strcmp(original_filename, new_filename) == 0) {
+                               src_stream = bb_xfopen(backup_filename, "r");
+                       } else {
+                               src_stream = bb_xfopen(original_filename, "r");
+                       }
+               }
+
+               printf("patching file %s\n", new_filename);
+
+               /* Handle each hunk */
+               patch_line = bb_get_line_from_file(stdin);
+               while (patch_line) {
+                       unsigned int count;
+                       unsigned int src_beg_line;
+                       unsigned int unused;
+                       unsigned int hunk_offset_start = 0;
+                       int hunk_error = 0;
+
+                       /* This bit should be improved */
+                       if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) &&
+                               (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) &&
+                               (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) {
+                               /* No more hunks for this file */
+                               break;
+                       }
+                       free(patch_line);
+                       hunk_count++;
+
+                       if (src_beg_line && dest_beg_line) {
+                               /* Copy unmodified lines upto start of hunk */
+                               /* src_beg_line will be 0 if its a new file */
+                               count = src_beg_line - src_cur_line;
+                               if (copy_lines(src_stream, dst_stream, count) != count) {
+                                       bb_error_msg_and_die("Bad src file");
+                               }
+                               src_cur_line += count;
+                               dest_cur_line += count;
+                               copy_trailing_lines_flag = 1;
+                       }
+                       hunk_offset_start = src_cur_line;
+
+                       while ((patch_line = bb_get_line_from_file(stdin)) != NULL) {
+                               if ((*patch_line == '-') || (*patch_line == ' ')) {
+                                       char *src_line = NULL;
+                                       if (src_stream) {
+                                               src_line = bb_get_line_from_file(src_stream);
+                                               if (!src_line) {
+                                                       hunk_error++;
+                                                       break;
+                                               } else {
+                                                       src_cur_line++;
+                                               }
+                                               if (strcmp(src_line, patch_line + 1) != 0) {
+                                                       bb_error_msg("Hunk #%d FAILED at %d.", hunk_count, hunk_offset_start);
+                                                       hunk_error++;
+                                                       free(patch_line);
+                                                       break;
+                                               }
+                                               free(src_line);
+                                       }
+                                       if (*patch_line == ' ') {
+                                               fputs(patch_line + 1, dst_stream);
+                                               dest_cur_line++;
+                                       }
+                               } else if (*patch_line == '+') {
+                                       fputs(patch_line + 1, dst_stream);
+                                       dest_cur_line++;
+                               } else {
+                                       break;
+                               }
+                               free(patch_line);
+                       }
+                       if (hunk_error) {
+                               bad_hunk_count++;
+                       }
+               }
+
+               /* Cleanup last patched file */
+               if (copy_trailing_lines_flag) {
+                       copy_lines(src_stream, dst_stream, -1);
+               }
+               if (src_stream) {
+                       fclose(src_stream);
+               }
+               if (dst_stream) {
+                       fclose(dst_stream);
+               }
+               if (bad_hunk_count) {
+                       if (!ret) {
+                               ret = 1;
+                       }
+                       bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count);
+               } else {
+                       /* It worked, we can remove the backup */
+                       if (backup_filename) {
+                               unlink(backup_filename);
+                       }
+                       if ((dest_cur_line == 0) || (dest_beg_line == 0)) {
+                               /* The new patched file is empty, remove it */
+                               if (unlink(new_filename) == -1) {
+                                       bb_perror_msg_and_die("Couldnt remove file %s", new_filename);
+                               }
+                               if (unlink(original_filename) == -1) {
+                                       bb_perror_msg_and_die("Couldnt remove original file %s", new_filename);
+                               }
+                       }
+               }
+       }
+
+       /* 0 = SUCCESS
+        * 1 = Some hunks failed
+        * 2 = More serious problems
+        */
+       return(ret);
+}
diff --git a/busybox/editors/sed.c b/busybox/editors/sed.c
new file mode 100644 (file)
index 0000000..3d68716
--- /dev/null
@@ -0,0 +1,1220 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sed.c - very minimalist version of sed
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
+ * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ * Copyright (C) 2002  Matt Kraai
+ * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
+ * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Code overview.
+
+  Files are laid out to avoid unnecessary function declarations.  So for
+  example, every function add_cmd calls occurs before add_cmd in this file.
+
+  add_cmd() is called on each line of sed command text (from a file or from
+  the command line).  It calls get_address() and parse_cmd_args().  The
+  resulting sed_cmd_t structures are appended to a linked list
+  (sed_cmd_head/sed_cmd_tail).
+
+  process_file() does actual sedding, reading data lines from an input FILE *
+  (which could be stdin) and applying the sed command list (sed_cmd_head) to
+  each of the resulting lines.
+
+  sed_main() is where external code calls into this, with a command line.
+*/
+
+
+/*
+       Supported features and commands in this version of sed:
+
+        - comments ('#')
+        - address matching: num|/matchstr/[,num|/matchstr/|$]command
+        - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
+        - edit commands: (a)ppend, (i)nsert, (c)hange
+        - file commands: (r)ead
+        - backreferences in substitution expressions (\1, \2...\9)
+        - grouped commands: {cmd1;cmd2}
+        - transliteration (y/source-chars/dest-chars/)
+        - pattern space hold space storing / swapping (g, h, x)
+        - labels / branching (: label, b, t)
+
+        (Note: Specifying an address (range) to match is *optional*; commands
+        default to the whole pattern space if no specific address match was
+        requested.)
+
+       Unsupported features:
+
+        - GNU extensions
+        - and more.
+
+       Todo:
+
+        - Create a wrapper around regex to make libc's regex conform with sed
+        - Fix bugs
+
+
+       Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
+*/
+
+#include <stdio.h>
+#include <unistd.h>            /* for getopt() */
+#include <regex.h>
+#include <string.h>            /* for strdup() */
+#include <errno.h>
+#include <ctype.h>             /* for isspace() */
+#include <stdlib.h>
+#include "busybox.h"
+
+typedef struct sed_cmd_s {
+    /* Ordered by alignment requirements: currently 36 bytes on x86 */
+
+    /* address storage */
+    regex_t *beg_match;        /* sed -e '/match/cmd' */
+    regex_t *end_match;        /* sed -e '/match/,/end_match/cmd' */
+    regex_t *sub_match;        /* For 's/sub_match/string/' */
+    int beg_line;              /* 'sed 1p'   0 == apply commands to all lines */
+    int end_line;              /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
+
+    FILE *file;                        /* File (sr) command writes to, -1 for none. */
+    char *string;              /* Data string for (saicytb) commands. */
+
+    unsigned short which_match;                /* (s) Which match to replace (0 for all) */
+
+    /* Bitfields (gcc won't group them if we don't) */
+    unsigned int invert:1;                     /* the '!' after the address */
+    unsigned int in_match:1;           /* Next line also included in match? */
+    unsigned int no_newline:1;         /* Last line written by (sr) had no '\n' */
+    unsigned int sub_p:1;                      /* (s) print option */
+
+
+    /* GENERAL FIELDS */
+    char cmd;                          /* The command char: abcdDgGhHilnNpPqrstwxy:={} */
+    struct sed_cmd_s *next;    /* Next command (linked list, NULL terminated) */
+} sed_cmd_t;
+
+/* globals */
+/* options */
+static int be_quiet = 0, in_place=0, regex_type=0;
+FILE *nonstdout;
+char *outname;
+
+
+static const char bad_format_in_subst[] =
+       "bad format in substitution expression";
+const char *const semicolon_whitespace = "; \n\r\t\v";
+
+regmatch_t regmatch[10];
+static regex_t *previous_regex_ptr = NULL;
+
+/* linked list of sed commands */
+static sed_cmd_t sed_cmd_head;
+static sed_cmd_t *sed_cmd_tail = &sed_cmd_head;
+
+/* Linked list of append lines */
+struct append_list {
+       char *string;
+       struct append_list *next;
+};
+struct append_list *append_head=NULL, *append_tail=NULL;
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+static void free_and_close_stuff(void)
+{
+       sed_cmd_t *sed_cmd = sed_cmd_head.next;
+
+       while(append_head) {
+               append_tail=append_head->next;
+               free(append_head->string);
+               free(append_head);
+               append_head=append_tail;
+       }
+
+       while (sed_cmd) {
+               sed_cmd_t *sed_cmd_next = sed_cmd->next;
+
+               if(sed_cmd->file)
+                       bb_xprint_and_close_file(sed_cmd->file);
+
+               if (sed_cmd->beg_match) {
+                       regfree(sed_cmd->beg_match);
+                       free(sed_cmd->beg_match);
+               }
+               if (sed_cmd->end_match) {
+                       regfree(sed_cmd->end_match);
+                       free(sed_cmd->end_match);
+               }
+               if (sed_cmd->sub_match) {
+                       regfree(sed_cmd->sub_match);
+                       free(sed_cmd->sub_match);
+               }
+               free(sed_cmd->string);
+               free(sed_cmd);
+               sed_cmd = sed_cmd_next;
+       }
+}
+#endif
+
+/* If something bad happens during -i operation, delete temp file */
+
+static void cleanup_outname(void)
+{
+  if(outname) unlink(outname);
+}
+
+/* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */
+
+static void parse_escapes(char *dest, const char *string, int len, char from, char to)
+{
+       int i=0;
+
+       while(i<len) {
+               if(string[i] == '\\') {
+                       if(!to || string[i+1] == from) {
+                               *(dest++) = to ? to : string[i+1];
+                               i+=2;
+                               continue;
+                       } else *(dest++)=string[i++];
+               }
+               *(dest++) = string[i++];
+       }
+       *dest=0;
+}
+
+static char *copy_parsing_slashn(const char *string, int len)
+{
+       char *dest=xmalloc(len+1);
+
+       parse_escapes(dest,string,len,'n','\n');
+       return dest;
+}
+
+
+/*
+ * index_of_next_unescaped_regexp_delim - walks left to right through a string
+ * beginning at a specified index and returns the index of the next regular
+ * expression delimiter (typically a forward * slash ('/')) not preceded by
+ * a backslash ('\').
+ */
+static int index_of_next_unescaped_regexp_delim(const char delimiter,
+       const char *str)
+{
+       int bracket = -1;
+       int escaped = 0;
+       int idx = 0;
+       char ch;
+
+       for (; (ch = str[idx]); idx++) {
+               if (bracket != -1) {
+                       if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2
+                                       && str[idx - 1] == '^')))
+                               bracket = -1;
+               } else if (escaped)
+                       escaped = 0;
+               else if (ch == '\\')
+                       escaped = 1;
+               else if (ch == '[')
+                       bracket = idx;
+               else if (ch == delimiter)
+                       return idx;
+       }
+
+       /* if we make it to here, we've hit the end of the string */
+       return -1;
+}
+
+/*
+ *  Returns the index of the third delimiter
+ */
+static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
+{
+       const char *cmdstr_ptr = cmdstr;
+       char delimiter;
+       int idx = 0;
+
+       /* verify that the 's' or 'y' is followed by something.  That something
+        * (typically a 'slash') is now our regexp delimiter... */
+       if (*cmdstr == '\0') bb_error_msg_and_die(bad_format_in_subst);
+       delimiter = *(cmdstr_ptr++);
+
+       /* save the match string */
+       idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
+       if (idx == -1) {
+               bb_error_msg_and_die(bad_format_in_subst);
+       }
+       *match = copy_parsing_slashn(cmdstr_ptr, idx);
+
+       /* save the replacement string */
+       cmdstr_ptr += idx + 1;
+       idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
+       if (idx == -1) {
+               bb_error_msg_and_die(bad_format_in_subst);
+       }
+       *replace = copy_parsing_slashn(cmdstr_ptr, idx);
+
+       return ((cmdstr_ptr - cmdstr) + idx);
+}
+
+/*
+ * returns the index in the string just past where the address ends.
+ */
+static int get_address(char *my_str, int *linenum, regex_t ** regex)
+{
+       char *pos = my_str;
+
+       if (isdigit(*my_str)) {
+               *linenum = strtol(my_str, &pos, 10);
+               /* endstr shouldnt ever equal NULL */
+       } else if (*my_str == '$') {
+               *linenum = -1;
+               pos++;
+       } else if (*my_str == '/' || *my_str == '\\') {
+               int next;
+               char delimiter;
+               char *temp;
+
+               if (*my_str == '\\') delimiter = *(++pos);
+               else delimiter = '/';
+               next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
+               if (next == -1)
+                       bb_error_msg_and_die("unterminated match expression");
+
+               temp=copy_parsing_slashn(pos,next);
+               *regex = (regex_t *) xmalloc(sizeof(regex_t));
+               xregcomp(*regex, temp, regex_type|REG_NEWLINE);
+               free(temp);
+               /* Move position to next character after last delimiter */
+               pos+=(next+1);
+       }
+       return pos - my_str;
+}
+
+/* Grab a filename.  Whitespace at start is skipped, then goes to EOL. */
+static int parse_file_cmd(sed_cmd_t * sed_cmd, const char *filecmdstr, char **retval)
+{
+       int start = 0, idx, hack=0;
+
+       /* Skip whitespace, then grab filename to end of line */
+       while (isspace(filecmdstr[start])) start++;
+       idx=start;
+       while(filecmdstr[idx] && filecmdstr[idx]!='\n') idx++;
+       /* If lines glued together, put backslash back. */
+       if(filecmdstr[idx]=='\n') hack=1;
+       if(idx==start) bb_error_msg_and_die("Empty filename");
+       *retval = bb_xstrndup(filecmdstr+start, idx-start+hack+1);
+       if(hack) *(idx+*retval)='\\';
+
+       return idx;
+}
+
+static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr)
+{
+       int cflags = regex_type;
+       char *match;
+       int idx = 0;
+
+       /*
+        * A substitution command should look something like this:
+        *    s/match/replace/ #gIpw
+        *    ||     |        |||
+        *    mandatory       optional
+        */
+       idx = parse_regex_delim(substr, &match, &sed_cmd->string);
+
+       /* determine the number of back references in the match string */
+       /* Note: we compute this here rather than in the do_subst_command()
+        * function to save processor time, at the expense of a little more memory
+        * (4 bits) per sed_cmd */
+
+       /* process the flags */
+
+       sed_cmd->which_match=1;
+       while (substr[++idx]) {
+               /* Parse match number */
+               if(isdigit(substr[idx])) {
+                       if(match[0]!='^') {
+                               /* Match 0 treated as all, multiple matches we take the last one. */
+                               char *pos=substr+idx;
+                               sed_cmd->which_match=(unsigned short)strtol(substr+idx,&pos,10);
+                               idx=pos-substr;
+                       }
+                       continue;
+               }
+               /* Skip spaces */
+               if(isspace(substr[idx])) continue;
+
+               switch (substr[idx]) {
+                       /* Replace all occurrences */
+                       case 'g':
+                               if (match[0] != '^') sed_cmd->which_match = 0;
+                               break;
+                       /* Print pattern space */
+                       case 'p':
+                               sed_cmd->sub_p = 1;
+                               break;
+                       case 'w':
+                       {
+                               char *temp;
+                               idx+=parse_file_cmd(sed_cmd,substr+idx,&temp);
+
+                               break;
+                       }
+                       /* Ignore case (gnu exension) */
+                       case 'I':
+                               cflags |= REG_ICASE;
+                               break;
+                       case ';':
+                       case '}':
+                               goto out;
+                       default:
+                               bb_error_msg_and_die("bad option in substitution expression");
+               }
+       }
+out:
+       /* compile the match string into a regex */
+       if (*match != '\0') {
+               /* If match is empty, we use last regex used at runtime */
+               sed_cmd->sub_match = (regex_t *) xmalloc(sizeof(regex_t));
+               xregcomp(sed_cmd->sub_match, match, cflags);
+       }
+       free(match);
+
+       return idx;
+}
+
+/*
+ *  Process the commands arguments
+ */
+static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr)
+{
+       /* handle (s)ubstitution command */
+       if (sed_cmd->cmd == 's') cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
+       /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
+       else if (strchr("aic", sed_cmd->cmd)) {
+               if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
+                       bb_error_msg_and_die
+                               ("only a beginning address can be specified for edit commands");
+               for(;;) {
+                       if(*cmdstr=='\n' || *cmdstr=='\\') {
+                               cmdstr++;
+                               break;
+                       } else if(isspace(*cmdstr)) cmdstr++;
+                       else break;
+               }
+               sed_cmd->string = bb_xstrdup(cmdstr);
+               parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0);
+               cmdstr += strlen(cmdstr);
+       /* handle file cmds: (r)ead */
+       } else if(strchr("rw", sed_cmd->cmd)) {
+               if (sed_cmd->end_line || sed_cmd->end_match)
+                       bb_error_msg_and_die("Command only uses one address");
+               cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string);
+               if(sed_cmd->cmd=='w')
+                       sed_cmd->file=bb_xfopen(sed_cmd->string,"w");
+       /* handle branch commands */
+       } else if (strchr(":bt", sed_cmd->cmd)) {
+               int length;
+
+               while(isspace(*cmdstr)) cmdstr++;
+               length = strcspn(cmdstr, semicolon_whitespace);
+               if (length) {
+                       sed_cmd->string = strndup(cmdstr, length);
+                       cmdstr += length;
+               }
+       }
+       /* translation command */
+       else if (sed_cmd->cmd == 'y') {
+               char *match, *replace;
+               int i=cmdstr[0];
+
+               cmdstr+=parse_regex_delim(cmdstr, &match, &replace)+1;
+               /* \n already parsed, but \delimiter needs unescaping. */
+               parse_escapes(match,match,strlen(match),i,i);
+               parse_escapes(replace,replace,strlen(replace),i,i);
+
+               sed_cmd->string = xcalloc(1, (strlen(match) + 1) * 2);
+               for (i = 0; match[i] && replace[i]; i++) {
+                       sed_cmd->string[i * 2] = match[i];
+                       sed_cmd->string[(i * 2) + 1] = replace[i];
+               }
+               free(match);
+               free(replace);
+       }
+       /* if it wasnt a single-letter command that takes no arguments
+        * then it must be an invalid command.
+        */
+       else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) {
+               bb_error_msg_and_die("Unsupported command %c", sed_cmd->cmd);
+       }
+
+       /* give back whatever's left over */
+       return (cmdstr);
+}
+
+
+/* Parse address+command sets, skipping comment lines. */
+
+void add_cmd(char *cmdstr)
+{
+       static char *add_cmd_line=NULL;
+       sed_cmd_t *sed_cmd;
+       int temp;
+
+       /* Append this line to any unfinished line from last time. */
+       if(add_cmd_line) {
+               int lastlen=strlen(add_cmd_line);
+               char *tmp=xmalloc(lastlen+strlen(cmdstr)+2);
+
+               memcpy(tmp,add_cmd_line,lastlen);
+               tmp[lastlen]='\n';
+               strcpy(tmp+lastlen+1,cmdstr);
+               free(add_cmd_line);
+               cmdstr=add_cmd_line=tmp;
+       } else add_cmd_line=NULL;
+
+       /* If this line ends with backslash, request next line. */
+       temp=strlen(cmdstr);
+       if(temp && cmdstr[temp-1]=='\\') {
+               if(!add_cmd_line) add_cmd_line=strdup(cmdstr);
+               add_cmd_line[temp-1]=0;
+               return;
+       }
+
+       /* Loop parsing all commands in this line. */
+       while(*cmdstr) {
+               /* Skip leading whitespace and semicolons */
+               cmdstr += strspn(cmdstr, semicolon_whitespace);
+
+               /* If no more commands, exit. */
+               if(!*cmdstr) break;
+
+               /* if this is a comment, jump past it and keep going */
+               if (*cmdstr == '#') {
+                       /* "#n" is the same as using -n on the command line */
+                       if (cmdstr[1] == 'n') be_quiet++;
+                       if(!(cmdstr=strpbrk(cmdstr, "\n\r"))) break;
+                       continue;
+               }
+
+               /* parse the command
+                * format is: [addr][,addr][!]cmd
+                *            |----||-----||-|
+                *            part1 part2  part3
+                */
+
+               sed_cmd = xcalloc(1, sizeof(sed_cmd_t));
+
+               /* first part (if present) is an address: either a '$', a number or a /regex/ */
+               cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
+
+               /* second part (if present) will begin with a comma */
+               if (*cmdstr == ',') {
+                       int idx;
+
+                       cmdstr++;
+                       idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
+                       if (!idx) bb_error_msg_and_die("get_address: no address found in string\n");
+                       cmdstr += idx;
+               }
+
+               /* skip whitespace before the command */
+               while (isspace(*cmdstr)) cmdstr++;
+
+               /* Check for inversion flag */
+               if (*cmdstr == '!') {
+                       sed_cmd->invert = 1;
+                       cmdstr++;
+
+                       /* skip whitespace before the command */
+                       while (isspace(*cmdstr)) cmdstr++;
+               }
+
+               /* last part (mandatory) will be a command */
+               if (!*cmdstr) bb_error_msg_and_die("missing command");
+               sed_cmd->cmd = *(cmdstr++);
+               cmdstr = parse_cmd_args(sed_cmd, cmdstr);
+
+               /* Add the command to the command array */
+               sed_cmd_tail->next = sed_cmd;
+               sed_cmd_tail = sed_cmd_tail->next;
+       }
+
+       /* If we glued multiple lines together, free the memory. */
+       if(add_cmd_line) {
+               free(add_cmd_line);
+               add_cmd_line=NULL;
+       }
+}
+
+struct pipeline {
+       char *buf;      /* Space to hold string */
+       int idx;        /* Space used */
+       int len;        /* Space allocated */
+} pipeline;
+
+#define PIPE_GROW 64
+
+void pipe_putc(char c)
+{
+       if(pipeline.idx==pipeline.len) {
+               pipeline.buf = xrealloc(pipeline.buf, pipeline.len + PIPE_GROW);
+               pipeline.len+=PIPE_GROW;
+       }
+       pipeline.buf[pipeline.idx++] = (c);
+}
+
+static void do_subst_w_backrefs(const char *line, const char *replace)
+{
+       int i,j;
+
+       /* go through the replacement string */
+       for (i = 0; replace[i]; i++) {
+               /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */
+               if (replace[i] == '\\' && replace[i+1]>'0' && replace[i+1]<='9') {
+                       int backref=replace[++i]-'0';
+
+                       /* print out the text held in regmatch[backref] */
+                       if(regmatch[backref].rm_so != -1)
+                               for (j = regmatch[backref].rm_so; j < regmatch[backref].rm_eo; j++)
+                                       pipe_putc(line[j]);
+               }
+
+               /* if we find a backslash escaped character, print the character */
+               else if (replace[i] == '\\') pipe_putc(replace[++i]);
+
+               /* if we find an unescaped '&' print out the whole matched text. */
+               else if (replace[i] == '&')
+                       for (j = regmatch[0].rm_so; j < regmatch[0].rm_eo; j++)
+                               pipe_putc(line[j]);
+               /* Otherwise just output the character. */
+               else pipe_putc(replace[i]);
+       }
+}
+
+static int do_subst_command(sed_cmd_t * sed_cmd, char **line)
+{
+       char *oldline = *line;
+       int altered = 0;
+       int match_count=0;
+       regex_t *current_regex;
+
+       /* Handle empty regex. */
+       if (sed_cmd->sub_match == NULL) {
+               current_regex = previous_regex_ptr;
+               if(!current_regex)
+                       bb_error_msg_and_die("No previous regexp.");
+       } else previous_regex_ptr = current_regex = sed_cmd->sub_match;
+
+       /* Find the first match */
+       if(REG_NOMATCH==regexec(current_regex, oldline, 10, regmatch, 0))
+               return 0;
+
+       /* Initialize temporary output buffer. */
+       pipeline.buf=xmalloc(PIPE_GROW);
+       pipeline.len=PIPE_GROW;
+       pipeline.idx=0;
+
+       /* Now loop through, substituting for matches */
+       do {
+               int i;
+
+               /* Work around bug in glibc regexec, demonstrated by:
+                  echo " a.b" | busybox sed 's [^ .]* x g'
+                  The match_count check is so not to break
+                  echo "hi" | busybox sed 's/^/!/g' */
+               if(!regmatch[0].rm_so && !regmatch[0].rm_eo && match_count) {
+                       pipe_putc(*(oldline++));
+                       continue;
+               }
+
+               match_count++;
+
+               /* If we aren't interested in this match, output old line to
+                  end of match and continue */
+               if(sed_cmd->which_match && sed_cmd->which_match!=match_count) {
+                       for(i=0;i<regmatch[0].rm_eo;i++)
+                               pipe_putc(oldline[i]);
+                       continue;
+               }
+
+               /* print everything before the match */
+               for (i = 0; i < regmatch[0].rm_so; i++) pipe_putc(oldline[i]);
+
+               /* then print the substitution string */
+               do_subst_w_backrefs(oldline, sed_cmd->string);
+
+               /* advance past the match */
+               oldline += regmatch[0].rm_eo;
+               /* flag that something has changed */
+               altered++;
+
+               /* if we're not doing this globally, get out now */
+               if (sed_cmd->which_match) break;
+       } while (*oldline && (regexec(current_regex, oldline, 10, regmatch, 0) != REG_NOMATCH));
+
+       /* Copy rest of string into output pipeline */
+
+       while(*oldline) pipe_putc(*(oldline++));
+       pipe_putc(0);
+
+       free(*line);
+       *line = pipeline.buf;
+       return altered;
+}
+
+/* Set command pointer to point to this label.  (Does not handle null label.) */
+static sed_cmd_t *branch_to(const char *label)
+{
+       sed_cmd_t *sed_cmd;
+
+       for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
+               if ((sed_cmd->cmd == ':') && (sed_cmd->string) && (strcmp(sed_cmd->string, label) == 0)) {
+                       return (sed_cmd);
+               }
+       }
+       bb_error_msg_and_die("Can't find label for jump to `%s'", label);
+}
+
+/* Append copy of string to append buffer */
+static void append(char *s)
+{
+       struct append_list *temp=calloc(1,sizeof(struct append_list));
+
+       if(append_head)
+               append_tail=(append_tail->next=temp);
+       else append_head=append_tail=temp;
+       temp->string=strdup(s);
+}
+
+static void flush_append(void)
+{
+       /* Output appended lines. */
+       while(append_head) {
+               fprintf(nonstdout,"%s\n",append_head->string);
+               append_tail=append_head->next;
+               free(append_head->string);
+               free(append_head);
+               append_head=append_tail;
+       }
+       append_head=append_tail=NULL;
+}
+
+/* Get next line of input, flushing append buffer and noting if we hit EOF
+ * without a newline on the last line.
+ */
+static char *get_next_line(FILE * file, int *no_newline)
+{
+       char *temp;
+       int len;
+
+       flush_append();
+       temp=bb_get_line_from_file(file);
+       if(temp) {
+               len=strlen(temp);
+               if(len && temp[len-1]=='\n') temp[len-1]=0;
+               else *no_newline=1;
+       }
+
+       return temp;
+}
+
+/* Output line of text.  missing_newline means the last line output did not
+   end with a newline.  no_newline means this line does not end with a
+   newline. */
+
+static int puts_maybe_newline(char *s, FILE *file, int missing_newline, int no_newline)
+{
+       if(missing_newline) fputc('\n',file);
+       fputs(s,file);
+       if(!no_newline) fputc('\n',file);
+
+    if(ferror(file)) {
+               fprintf(stderr,"Write failed.\n");
+               exit(4);  /* It's what gnu sed exits with... */
+       }
+
+       return no_newline;
+}
+
+#define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n)
+
+static void process_file(FILE *file)
+{
+       char *pattern_space, *next_line, *hold_space=NULL;
+       static int linenum = 0, missing_newline=0;
+       int no_newline,next_no_newline=0;
+
+       next_line = get_next_line(file,&next_no_newline);
+
+       /* go through every line in the file */
+       for(;;) {
+               sed_cmd_t *sed_cmd;
+               int substituted=0;
+
+               /* Advance to next line.  Stop if out of lines. */
+               if(!(pattern_space=next_line)) break;
+               no_newline=next_no_newline;
+
+               /* Read one line in advance so we can act on the last line, the '$' address */
+               next_line = get_next_line(file,&next_no_newline);
+               linenum++;
+restart:
+               /* for every line, go through all the commands */
+               for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
+                       int old_matched, matched;
+
+                       old_matched = sed_cmd->in_match;
+
+                       /* Determine if this command matches this line: */
+
+                       /* Are we continuing a previous multi-line match? */
+
+                       sed_cmd->in_match = sed_cmd->in_match
+
+                       /* Or is no range necessary? */
+                               || (!sed_cmd->beg_line && !sed_cmd->end_line
+                                       && !sed_cmd->beg_match && !sed_cmd->end_match)
+
+                       /* Or did we match the start of a numerical range? */
+                               || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))
+
+                       /* Or does this line match our begin address regex? */
+                               || (sed_cmd->beg_match &&
+                                   !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0))
+
+                       /* Or did we match last line of input? */
+                               || (sed_cmd->beg_line == -1 && next_line == NULL);
+
+                       /* Snapshot the value */
+
+                       matched = sed_cmd->in_match;
+
+                       /* Is this line the end of the current match? */
+
+                       if(matched) {
+                               sed_cmd->in_match = !(
+                                       /* has the ending line come, or is this a single address command? */
+                                       (sed_cmd->end_line ?
+                                               sed_cmd->end_line==-1 ?
+                                                       !next_line
+                                                       : sed_cmd->end_line<=linenum
+                                               : !sed_cmd->end_match)
+                                       /* or does this line matches our last address regex */
+                                       || (sed_cmd->end_match && old_matched && (regexec(sed_cmd->end_match, pattern_space, 0, NULL, 0) == 0))
+                               );
+                       }
+
+                       /* Skip blocks of commands we didn't match. */
+                       if (sed_cmd->cmd == '{') {
+                               if(sed_cmd->invert ? matched : !matched)
+                                       while(sed_cmd && sed_cmd->cmd!='}') sed_cmd=sed_cmd->next;
+                               if(!sed_cmd) bb_error_msg_and_die("Unterminated {");
+                               continue;
+                       }
+
+                       /* Okay, so did this line match? */
+                       if (sed_cmd->invert ? !matched : matched) {
+                               /* Update last used regex in case a blank substitute BRE is found */
+                               if (sed_cmd->beg_match) {
+                                       previous_regex_ptr = sed_cmd->beg_match;
+                               }
+
+                               /* actual sedding */
+                               switch (sed_cmd->cmd) {
+
+                                       /* Print line number */
+                                       case '=':
+                                               fprintf(nonstdout,"%d\n", linenum);
+                                               break;
+
+                                       /* Write the current pattern space up to the first newline */
+                                       case 'P':
+                                       {
+                                               char *tmp = strchr(pattern_space, '\n');
+
+                                               if (tmp) {
+                                                       *tmp = '\0';
+                                                       sed_puts(pattern_space,1);
+                                                       *tmp = '\n';
+                                                       break;
+                                               }
+                                               /* Fall Through */
+                                       }
+
+                                       /* Write the current pattern space to output */
+                                       case 'p':
+                                               sed_puts(pattern_space,no_newline);
+                                               break;
+                                       /* Delete up through first newline */
+                                       case 'D':
+                                       {
+                                               char *tmp = strchr(pattern_space,'\n');
+
+                                               if(tmp) {
+                                                       tmp=bb_xstrdup(tmp+1);
+                                                       free(pattern_space);
+                                                       pattern_space=tmp;
+                                                       goto restart;
+                                               }
+                                       }
+                                       /* discard this line. */
+                                       case 'd':
+                                               goto discard_line;
+
+                                       /* Substitute with regex */
+                                       case 's':
+                                               if(do_subst_command(sed_cmd, &pattern_space)) {
+                                                       substituted|=1;
+
+                                                       /* handle p option */
+                                                       if(sed_cmd->sub_p)
+                                                               sed_puts(pattern_space,no_newline);
+                                                       /* handle w option */
+                                                       if(sed_cmd->file)
+                                                               sed_cmd->no_newline=puts_maybe_newline(pattern_space, sed_cmd->file, sed_cmd->no_newline, no_newline);
+
+                                               }
+                                               break;
+
+                                       /* Append line to linked list to be printed later */
+                                       case 'a':
+                                       {
+                                               append(sed_cmd->string);
+                                               break;
+                                       }
+
+                                       /* Insert text before this line */
+                                       case 'i':
+                                               sed_puts(sed_cmd->string,1);
+                                               break;
+
+                                       /* Cut and paste text (replace) */
+                                       case 'c':
+                                               /* Only triggers on last line of a matching range. */
+                                               if (!sed_cmd->in_match) sed_puts(sed_cmd->string,1);
+                                               goto discard_line;
+
+                                       /* Read file, append contents to output */
+                                       case 'r':
+                                       {
+                                               FILE *outfile;
+
+                                               outfile = fopen(sed_cmd->string, "r");
+                                               if (outfile) {
+                                                       char *line;
+
+                                                       while ((line = bb_get_chomped_line_from_file(outfile))
+                                                                       != NULL)
+                                                               append(line);
+                                                       bb_xprint_and_close_file(outfile);
+                                               }
+
+                                               break;
+                                       }
+
+                                       /* Write pattern space to file. */
+                                       case 'w':
+                                               sed_cmd->no_newline=puts_maybe_newline(pattern_space,sed_cmd->file, sed_cmd->no_newline,no_newline);
+                                               break;
+
+                                       /* Read next line from input */
+                                       case 'n':
+                                               if (!be_quiet)
+                                                       sed_puts(pattern_space,no_newline);
+                                               if (next_line) {
+                                                       free(pattern_space);
+                                                       pattern_space = next_line;
+                                                       no_newline=next_no_newline;
+                                                       next_line = get_next_line(file,&next_no_newline);
+                                                       linenum++;
+                                                       break;
+                                               }
+                                               /* fall through */
+
+                                       /* Quit.  End of script, end of input. */
+                                       case 'q':
+                                               /* Exit the outer while loop */
+                                               free(next_line);
+                                               next_line = NULL;
+                                               goto discard_commands;
+
+                                       /* Append the next line to the current line */
+                                       case 'N':
+                                       {
+                                               /* If no next line, jump to end of script and exit. */
+                                               if (next_line == NULL) {
+                                                       /* Jump to end of script and exit */
+                                                       free(next_line);
+                                                       next_line = NULL;
+                                                       goto discard_line;
+                                               /* append next_line, read new next_line. */
+                                               } else {
+                                                       int len=strlen(pattern_space);
+
+                                                       pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
+                                                       pattern_space[len]='\n';
+                                                       strcpy(pattern_space+len+1, next_line);
+                                                       no_newline=next_no_newline;
+                                                       next_line = get_next_line(file,&next_no_newline);
+                                                       linenum++;
+                                               }
+                                               break;
+                                       }
+
+                                       /* Test if substition worked, branch if so. */
+                                       case 't':
+                                               if (!substituted) break;
+                                               substituted=0;
+                                                       /* Fall through */
+                                       /* Branch to label */
+                                       case 'b':
+                                               if (!sed_cmd->string) goto discard_commands;
+                                               else sed_cmd = branch_to(sed_cmd->string);
+                                               break;
+                                       /* Transliterate characters */
+                                       case 'y':
+                                       {
+                                               int i;
+
+                                               for (i = 0; pattern_space[i]; i++) {
+                                                       int j;
+
+                                                       for (j = 0; sed_cmd->string[j]; j += 2) {
+                                                               if (pattern_space[i] == sed_cmd->string[j]) {
+                                                                       pattern_space[i] = sed_cmd->string[j + 1];
+                                                               }
+                                                       }
+                                               }
+
+                                               break;
+                                       }
+                                       case 'g':       /* Replace pattern space with hold space */
+                                               free(pattern_space);
+                                               if (hold_space) {
+                                                       pattern_space = strdup(hold_space);
+                                                       no_newline=0;
+                                               }
+                                               break;
+                                       case 'G':       /* Append newline and hold space to pattern space */
+                                       {
+                                               int pattern_space_size = 2;
+                                               int hold_space_size = 0;
+
+                                               if (pattern_space)
+                                                       pattern_space_size += strlen(pattern_space);
+                                               if (hold_space) hold_space_size = strlen(hold_space);
+                                               pattern_space = xrealloc(pattern_space, pattern_space_size + hold_space_size);
+                                               if (pattern_space_size == 2) pattern_space[0]=0;
+                                               strcat(pattern_space, "\n");
+                                               if (hold_space) strcat(pattern_space, hold_space);
+                                               no_newline=0;
+
+                                               break;
+                                       }
+                                       case 'h':       /* Replace hold space with pattern space */
+                                               free(hold_space);
+                                               hold_space = strdup(pattern_space);
+                                               break;
+                                       case 'H':       /* Append newline and pattern space to hold space */
+                                       {
+                                               int hold_space_size = 2;
+                                               int pattern_space_size = 0;
+
+                                               if (hold_space) hold_space_size += strlen(hold_space);
+                                               if (pattern_space)
+                                                       pattern_space_size = strlen(pattern_space);
+                                               hold_space = xrealloc(hold_space,
+                                                                                       hold_space_size + pattern_space_size);
+
+                                               if (hold_space_size == 2) hold_space[0]=0;
+                                               strcat(hold_space, "\n");
+                                               if (pattern_space) strcat(hold_space, pattern_space);
+
+                                               break;
+                                       }
+                                       case 'x': /* Exchange hold and pattern space */
+                                       {
+                                               char *tmp = pattern_space;
+                                               pattern_space = hold_space;
+                                               no_newline=0;
+                                               hold_space = tmp;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               /*
+                * exit point from sedding...
+                */
+discard_commands:
+               /* we will print the line unless we were told to be quiet ('-n')
+                  or if the line was suppressed (ala 'd'elete) */
+               if (!be_quiet) sed_puts(pattern_space,no_newline);
+
+               /* Delete and such jump here. */
+discard_line:
+               flush_append();
+               free(pattern_space);
+       }
+}
+
+/* It is possible to have a command line argument with embedded
+   newlines.  This counts as multiple command lines. */
+
+static void add_cmd_block(char *cmdstr)
+{
+       int go=1;
+       char *temp=bb_xstrdup(cmdstr),*temp2=temp;
+
+       while(go) {
+               int len=strcspn(temp2,"\n");
+               if(!temp2[len]) go=0;
+               else temp2[len]=0;
+               add_cmd(temp2);
+               temp2+=len+1;
+       }
+       free(temp);
+}
+
+extern int sed_main(int argc, char **argv)
+{
+       int status = EXIT_SUCCESS;
+       int opt;
+       uint8_t getpat = 1;
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       /* destroy command strings on exit */
+       if (atexit(free_and_close_stuff) == -1)
+               bb_perror_msg_and_die("atexit");
+#endif
+
+#define LIE_TO_AUTOCONF
+#ifdef LIE_TO_AUTOCONF
+       if(argc==2 && !strcmp(argv[1],"--version")) {
+               printf("This is not GNU sed version 4.0\n");
+               exit(0);
+       }
+#endif
+
+       /* do normal option parsing */
+       while ((opt = getopt(argc, argv, "irne:f:")) > 0) {
+               switch (opt) {
+               case 'i':
+                       in_place++;
+                       atexit(cleanup_outname);
+                       break;
+               case 'r':
+                       regex_type|=REG_EXTENDED;
+                       break;
+               case 'n':
+                       be_quiet++;
+                       break;
+               case 'e':
+                       add_cmd_block(optarg);
+                       getpat=0;
+                       break;
+               case 'f':
+               {
+                       FILE *cmdfile;
+                       char *line;
+
+                       cmdfile = bb_xfopen(optarg, "r");
+
+                       while ((line = bb_get_chomped_line_from_file(cmdfile))
+                                != NULL) {
+                               add_cmd(line);
+                               getpat=0;
+                               free(line);
+                       }
+                       bb_xprint_and_close_file(cmdfile);
+
+                       break;
+               }
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       /* if we didn't get a pattern from a -e and no command file was specified,
+        * argv[optind] should be the pattern. no pattern, no worky */
+       if(getpat) {
+               if (argv[optind] == NULL)
+                       bb_show_usage();
+               else
+                       add_cmd_block(argv[optind++]);
+       }
+       /* Flush any unfinished commands. */
+       add_cmd("");
+
+       /* By default, we write to stdout */
+       nonstdout=stdout;
+
+       /* argv[(optind)..(argc-1)] should be names of file to process. If no
+        * files were specified or '-' was specified, take input from stdin.
+        * Otherwise, we process all the files specified. */
+       if (argv[optind] == NULL) {
+               if(in_place) {
+                       fprintf(stderr,"sed: Filename required for -i\n");
+                       exit(1);
+               }
+               process_file(stdin);
+       } else {
+               int i;
+               FILE *file;
+
+               for (i = optind; i < argc; i++) {
+                       if(!strcmp(argv[i], "-") && !in_place) {
+                               process_file(stdin);
+                       } else {
+                               file = bb_wfopen(argv[i], "r");
+                               if (file) {
+                                       if(in_place) {
+                                               struct stat statbuf;
+                                               outname=bb_xstrndup(argv[i],strlen(argv[i])+6);
+                                               strcat(outname,"XXXXXX");
+                                               /* Set permissions of output file */
+                                               fstat(fileno(file),&statbuf);
+                                               mkstemp(outname);
+                                               nonstdout=bb_wfopen(outname,"w");
+                                               /* Set permissions of output file */
+                                               fstat(fileno(file),&statbuf);
+                                               fchmod(fileno(nonstdout),statbuf.st_mode);
+                                               atexit(cleanup_outname);
+                                       }
+                                       process_file(file);
+                                       fclose(file);
+                                       if(in_place) {
+                                               fclose(nonstdout);
+                                               nonstdout=stdout;
+                                               unlink(argv[i]);
+                                               rename(outname,argv[i]);
+                                               free(outname);
+                                               outname=0;
+                                       }
+                               } else {
+                                       status = EXIT_FAILURE;
+                               }
+                       }
+               }
+       }
+
+       return status;
+}
diff --git a/busybox/editors/vi.c b/busybox/editors/vi.c
new file mode 100644 (file)
index 0000000..cd6cf0e
--- /dev/null
@@ -0,0 +1,3983 @@
+/* vi: set sw=8 ts=8: */
+/*
+ * tiny vi.c: A small 'vi' clone
+ * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.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.
+ */
+
+static const char vi_Version[] =
+       "$Id: vi.c,v 1.38 2004/08/19 19:15:06 andersen Exp $";
+
+/*
+ * To compile for standalone use:
+ *     gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
+ *       or
+ *     gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c            # include testing features
+ *     strip vi
+ */
+
+/*
+ * Things To Do:
+ *     EXINIT
+ *     $HOME/.exrc  and  ./.exrc
+ *     add magic to search     /foo.*bar
+ *     add :help command
+ *     :map macros
+ *     how about mode lines:   vi: set sw=8 ts=8:
+ *     if mark[] values were line numbers rather than pointers
+ *        it would be easier to change the mark when add/delete lines
+ *     More intelligence in refresh()
+ *     ":r !cmd"  and  "!cmd"  to filter text through an external command
+ *     A true "undo" facility
+ *     An "ex" line oriented mode- maybe using "cmdedit"
+ */
+
+//----  Feature --------------  Bytes to implement
+#ifdef STANDALONE
+#define vi_main                        main
+#define CONFIG_FEATURE_VI_COLON        // 4288
+#define CONFIG_FEATURE_VI_YANKMARK     // 1408
+#define CONFIG_FEATURE_VI_SEARCH       // 1088
+#define CONFIG_FEATURE_VI_USE_SIGNALS  // 1056
+#define CONFIG_FEATURE_VI_DOT_CMD      //  576
+#define CONFIG_FEATURE_VI_READONLY     //  128
+#define CONFIG_FEATURE_VI_SETOPTS      //  576
+#define CONFIG_FEATURE_VI_SET  //  224
+#define CONFIG_FEATURE_VI_WIN_RESIZE   //  256  WIN_RESIZE
+// To test editor using CRASHME:
+//    vi -C filename
+// To stop testing, wait until all to text[] is deleted, or
+//    Ctrl-Z and kill -9 %1
+// while in the editor Ctrl-T will toggle the crashme function on and off.
+//#define CONFIG_FEATURE_VI_CRASHME            // randomly pick commands to execute
+#endif                                                 /* STANDALONE */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <regex.h>
+#include <ctype.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#ifndef STANDALONE
+#include "busybox.h"
+#endif                                                 /* STANDALONE */
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#define Isprint(c) isprint((c))
+#else
+#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
+#endif
+
+#ifndef TRUE
+#define TRUE                   ((int)1)
+#define FALSE                  ((int)0)
+#endif                                                 /* TRUE */
+#define MAX_SCR_COLS           BUFSIZ
+
+// Misc. non-Ascii keys that report an escape sequence
+#define VI_K_UP                        128     // cursor key Up
+#define VI_K_DOWN              129     // cursor key Down
+#define VI_K_RIGHT             130     // Cursor Key Right
+#define VI_K_LEFT              131     // cursor key Left
+#define VI_K_HOME              132     // Cursor Key Home
+#define VI_K_END               133     // Cursor Key End
+#define VI_K_INSERT            134     // Cursor Key Insert
+#define VI_K_PAGEUP            135     // Cursor Key Page Up
+#define VI_K_PAGEDOWN          136     // Cursor Key Page Down
+#define VI_K_FUN1              137     // Function Key F1
+#define VI_K_FUN2              138     // Function Key F2
+#define VI_K_FUN3              139     // Function Key F3
+#define VI_K_FUN4              140     // Function Key F4
+#define VI_K_FUN5              141     // Function Key F5
+#define VI_K_FUN6              142     // Function Key F6
+#define VI_K_FUN7              143     // Function Key F7
+#define VI_K_FUN8              144     // Function Key F8
+#define VI_K_FUN9              145     // Function Key F9
+#define VI_K_FUN10             146     // Function Key F10
+#define VI_K_FUN11             147     // Function Key F11
+#define VI_K_FUN12             148     // Function Key F12
+
+/* vt102 typical ESC sequence */
+/* terminal standout start/normal ESC sequence */
+static const char SOs[] = "\033[7m";
+static const char SOn[] = "\033[0m";
+/* terminal bell sequence */
+static const char bell[] = "\007";
+/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
+static const char Ceol[] = "\033[0K";
+static const char Ceos [] = "\033[0J";
+/* Cursor motion arbitrary destination ESC sequence */
+static const char CMrc[] = "\033[%d;%dH";
+/* Cursor motion up and down ESC sequence */
+static const char CMup[] = "\033[A";
+static const char CMdown[] = "\n";
+
+
+static const int YANKONLY = FALSE;
+static const int YANKDEL = TRUE;
+static const int FORWARD = 1;  // code depends on "1"  for array index
+static const int BACK = -1;    // code depends on "-1" for array index
+static const int LIMITED = 0;  // how much of text[] in char_search
+static const int FULL = 1;     // how much of text[] in char_search
+
+static const int S_BEFORE_WS = 1;      // used in skip_thing() for moving "dot"
+static const int S_TO_WS = 2;          // used in skip_thing() for moving "dot"
+static const int S_OVER_WS = 3;                // used in skip_thing() for moving "dot"
+static const int S_END_PUNCT = 4;      // used in skip_thing() for moving "dot"
+static const int S_END_ALNUM = 5;      // used in skip_thing() for moving "dot"
+
+typedef unsigned char Byte;
+
+static int vi_setops;
+#define VI_AUTOINDENT 1
+#define VI_SHOWMATCH  2
+#define VI_IGNORECASE 4
+#define VI_ERR_METHOD 8
+#define autoindent (vi_setops & VI_AUTOINDENT)
+#define showmatch  (vi_setops & VI_SHOWMATCH )
+#define ignorecase (vi_setops & VI_IGNORECASE)
+/* indicate error with beep or flash */
+#define err_method (vi_setops & VI_ERR_METHOD)
+
+
+static int editing;            // >0 while we are editing a file
+static int cmd_mode;           // 0=command  1=insert
+static int file_modified;      // buffer contents changed
+static int fn_start;           // index of first cmd line file name
+static int save_argc;          // how many file names on cmd line
+static int cmdcnt;             // repetition count
+static fd_set rfds;            // use select() for small sleeps
+static struct timeval tv;      // use select() for small sleeps
+static int rows, columns;      // the terminal screen is this size
+static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
+static Byte *status_buffer;    // mesages to the user
+static Byte *cfn;              // previous, current, and next file name
+static Byte *text, *end, *textend;     // pointers to the user data in memory
+static Byte *screen;           // pointer to the virtual screen buffer
+static int screensize;         //            and its size
+static Byte *screenbegin;      // index into text[], of top line on the screen
+static Byte *dot;              // where all the action takes place
+static int tabstop;
+static struct termios term_orig, term_vi;      // remember what the cooked mode was
+static Byte erase_char;         // the users erase character
+static Byte last_input_char;    // last char read from user
+static Byte last_forward_char;  // last char searched for with 'f'
+
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+static int last_row;           // where the cursor was last moved to
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+static jmp_buf restart;                // catch_sig()
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
+static int my_pid;
+#endif
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+static int adding2q;           // are we currently adding user input to q
+static Byte *last_modifying_cmd;       // last modifying cmd for "."
+static Byte *ioq, *ioq_start;  // pointer to string for get_one_char to "read"
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+#if    defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
+static Byte *modifying_cmds;   // cmds that modify text[]
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_READONLY
+static int vi_readonly, readonly;
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+static Byte *reg[28];          // named register a-z, "D", and "U" 0-25,26,27
+static int YDreg, Ureg;                // default delete register and orig line for "U"
+static Byte *mark[28];         // user marks points somewhere in text[]-  a-z and previous context ''
+static Byte *context_start, *context_end;
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+static Byte *last_search_pattern;      // last pattern from a '/' or '?' search
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+
+
+static void edit_file(Byte *); // edit one file
+static void do_cmd(Byte);      // execute a command
+static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
+static Byte *begin_line(Byte *);       // return pointer to cur line B-o-l
+static Byte *end_line(Byte *); // return pointer to cur line E-o-l
+static Byte *prev_line(Byte *);        // return pointer to prev line B-o-l
+static Byte *next_line(Byte *);        // return pointer to next line B-o-l
+static Byte *end_screen(void); // get pointer to last char on screen
+static int count_lines(Byte *, Byte *);        // count line from start to stop
+static Byte *find_line(int);   // find begining of line #li
+static Byte *move_to_col(Byte *, int); // move "p" to column l
+static int isblnk(Byte);       // is the char a blank or tab
+static void dot_left(void);    // move dot left- dont leave line
+static void dot_right(void);   // move dot right- dont leave line
+static void dot_begin(void);   // move dot to B-o-l
+static void dot_end(void);     // move dot to E-o-l
+static void dot_next(void);    // move dot to next line B-o-l
+static void dot_prev(void);    // move dot to prev line B-o-l
+static void dot_scroll(int, int);      // move the screen up or down
+static void dot_skip_over_ws(void);    // move dot pat WS
+static void dot_delete(void);  // delete the char at 'dot'
+static Byte *bound_dot(Byte *);        // make sure  text[0] <= P < "end"
+static Byte *new_screen(int, int);     // malloc virtual screen memory
+static Byte *new_text(int);    // malloc memory for text[] buffer
+static Byte *char_insert(Byte *, Byte);        // insert the char c at 'p'
+static Byte *stupid_insert(Byte *, Byte);      // stupidly insert the char c at 'p'
+static Byte find_range(Byte **, Byte **, Byte);        // return pointers for an object
+static int st_test(Byte *, int, int, Byte *);  // helper for skip_thing()
+static Byte *skip_thing(Byte *, int, int, int);        // skip some object
+static Byte *find_pair(Byte *, Byte);  // find matching pair ()  []  {}
+static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
+static Byte *text_hole_make(Byte *, int);      // at "p", make a 'size' byte hole
+static Byte *yank_delete(Byte *, Byte *, int, int);    // yank text[] into register then delete
+static void show_help(void);   // display some help info
+static void rawmode(void);     // set "raw" mode on tty
+static void cookmode(void);    // return to "cooked" mode on tty
+static int mysleep(int);       // sleep for 'h' 1/100 seconds
+static Byte readit(void);      // read (maybe cursor) key from stdin
+static Byte get_one_char(void);        // read 1 char from stdin
+static int file_size(const Byte *);   // what is the byte size of "fn"
+static int file_insert(Byte *, Byte *, int);
+static int file_write(Byte *, Byte *, Byte *);
+static void place_cursor(int, int, int);
+static void screen_erase(void);
+static void clear_to_eol(void);
+static void clear_to_eos(void);
+static void standout_start(void);      // send "start reverse video" sequence
+static void standout_end(void);        // send "end reverse video" sequence
+static void flash(int);                // flash the terminal screen
+static void show_status_line(void);    // put a message on the bottom line
+static void psb(const char *, ...);     // Print Status Buf
+static void psbs(const char *, ...);    // Print Status Buf in standout mode
+static void ni(Byte *);                // display messages
+static void edit_status(void); // show file status on status line
+static void redraw(int);       // force a full screen refresh
+static void format_line(Byte*, Byte*, int);
+static void refresh(int);      // update the terminal from screen[]
+
+static void Indicate_Error(void);       // use flash or beep to indicate error
+#define indicate_error(c) Indicate_Error()
+
+#ifdef CONFIG_FEATURE_VI_SEARCH
+static Byte *char_search(Byte *, Byte *, int, int);    // search for pattern starting at p
+static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+#ifdef CONFIG_FEATURE_VI_COLON
+static void Hit_Return(void);
+static Byte *get_one_address(Byte *, int *);   // get colon addr, if present
+static Byte *get_address(Byte *, int *, int *);        // get two colon addrs, if present
+static void colon(Byte *);     // execute the "colon" mode cmds
+#endif                                                 /* CONFIG_FEATURE_VI_COLON */
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+static void winch_sig(int);    // catch window size changes
+static void suspend_sig(int);  // catch ctrl-Z
+static void catch_sig(int);     // catch ctrl-C and alarm time-outs
+static void core_sig(int);     // catch a core dump signal
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(Byte);     // new queue for command
+static void end_cmd_q(void);   // stop saving input chars
+#else                                                  /* CONFIG_FEATURE_VI_DOT_CMD */
+#define end_cmd_q()
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+static void window_size_get(int);      // find out what size the window is
+#endif                                                 /* CONFIG_FEATURE_VI_WIN_RESIZE */
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+static void showmatching(Byte *);      // show the matching pair ()  []  {}
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
+static Byte *string_insert(Byte *, Byte *);    // insert the string at 'p'
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+static Byte *text_yank(Byte *, Byte *, int);   // save copy of "p" into a register
+static Byte what_reg(void);            // what is letter of current YDreg
+static void check_context(Byte);       // remember context for '' command
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_CRASHME
+static void crash_dummy();
+static void crash_test();
+static int crashme = 0;
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+
+
+static void write1(const char *out)
+{
+       fputs(out, stdout);
+}
+
+extern int vi_main(int argc, char **argv)
+{
+       int c;
+       RESERVE_CONFIG_BUFFER(STATUS_BUFFER, 200);
+
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       int i;
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
+       my_pid = getpid();
+#endif
+#ifdef CONFIG_FEATURE_VI_CRASHME
+       (void) srand((long) my_pid);
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+
+       status_buffer = STATUS_BUFFER;
+
+#ifdef CONFIG_FEATURE_VI_READONLY
+       vi_readonly = readonly = FALSE;
+       if (strncmp(argv[0], "view", 4) == 0) {
+               readonly = TRUE;
+               vi_readonly = TRUE;
+       }
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+       vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       for (i = 0; i < 28; i++) {
+               reg[i] = 0;
+       }                                       // init the yank regs
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
+       modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~";      // cmds modifying text[]
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+
+       //  1-  process $HOME/.exrc file
+       //  2-  process EXINIT variable from environment
+       //  3-  process command line args
+       while ((c = getopt(argc, argv, "hCR")) != -1) {
+               switch (c) {
+#ifdef CONFIG_FEATURE_VI_CRASHME
+               case 'C':
+                       crashme = 1;
+                       break;
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+#ifdef CONFIG_FEATURE_VI_READONLY
+               case 'R':               // Read-only flag
+                       readonly = TRUE;
+                       vi_readonly = TRUE;
+                       break;
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+                       //case 'r':     // recover flag-  ignore- we don't use tmp file
+                       //case 'x':     // encryption flag- ignore
+                       //case 'c':     // execute command first
+                       //case 'h':     // help -- just use default
+               default:
+                       show_help();
+                       return 1;
+               }
+       }
+
+       // The argv array can be used by the ":next"  and ":rewind" commands
+       // save optind.
+       fn_start = optind;      // remember first file name for :next and :rew
+       save_argc = argc;
+
+       //----- This is the main file handling loop --------------
+       if (optind >= argc) {
+               editing = 1;    // 0= exit,  1= one file,  2= multiple files
+               edit_file(0);
+       } else {
+               for (; optind < argc; optind++) {
+                       editing = 1;    // 0=exit, 1=one file, 2+ =many files
+                       free(cfn);
+                       cfn = (Byte *) bb_xstrdup(argv[optind]);
+                       edit_file(cfn);
+               }
+       }
+       //-----------------------------------------------------------
+
+       return (0);
+}
+
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+//----- See what the window size currently is --------------------
+static inline void window_size_get(int fd)
+{
+       get_terminal_width_height(fd, &columns, &rows);
+}
+#endif                                                 /* CONFIG_FEATURE_VI_WIN_RESIZE */
+
+static void edit_file(Byte * fn)
+{
+       Byte c;
+       int cnt, size, ch;
+
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+       int sig;
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       static Byte *cur_line;
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+
+       rawmode();
+       rows = 24;
+       columns = 80;
+       ch= -1;
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+       window_size_get(0);
+#endif                                                 /* CONFIG_FEATURE_VI_WIN_RESIZE */
+       new_screen(rows, columns);      // get memory for virtual screen
+
+       cnt = file_size(fn);    // file size
+       size = 2 * cnt;         // 200% of file size
+       new_text(size);         // get a text[] buffer
+       screenbegin = dot = end = text;
+       if (fn != 0) {
+               ch= file_insert(fn, text, cnt);
+       }
+       if (ch < 1) {
+               (void) char_insert(text, '\n'); // start empty buf with dummy line
+       }
+       file_modified = FALSE;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       YDreg = 26;                     // default Yank/Delete reg
+       Ureg = 27;                      // hold orig line for "U" cmd
+       for (cnt = 0; cnt < 28; cnt++) {
+               mark[cnt] = 0;
+       }                                       // init the marks
+       mark[26] = mark[27] = text;     // init "previous context"
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+
+       last_forward_char = last_input_char = '\0';
+       crow = 0;
+       ccol = 0;
+       edit_status();
+
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+       catch_sig(0);
+       core_sig(0);
+       signal(SIGWINCH, winch_sig);
+       signal(SIGTSTP, suspend_sig);
+       sig = setjmp(restart);
+       if (sig != 0) {
+               const char *msg = "";
+
+               if (sig == SIGWINCH)
+                       msg = "(window resize)";
+               if (sig == SIGHUP)
+                       msg = "(hangup)";
+               if (sig == SIGINT)
+                       msg = "(interrupt)";
+               if (sig == SIGTERM)
+                       msg = "(terminate)";
+               if (sig == SIGBUS)
+                       msg = "(bus error)";
+               if (sig == SIGSEGV)
+                       msg = "(I tried to touch invalid memory)";
+               if (sig == SIGALRM)
+                       msg = "(alarm)";
+
+               psbs("-- caught signal %d %s--", sig, msg);
+               screenbegin = dot = text;
+       }
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+
+       editing = 1;
+       cmd_mode = 0;           // 0=command  1=insert  2='R'eplace
+       cmdcnt = 0;
+       tabstop = 8;
+       offset = 0;                     // no horizontal offset
+       c = '\0';
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+       free(last_modifying_cmd);
+       free(ioq_start);
+       ioq = ioq_start = last_modifying_cmd = 0;
+       adding2q = 0;
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+       redraw(FALSE);                  // dont force every col re-draw
+       show_status_line();
+
+       //------This is the main Vi cmd handling loop -----------------------
+       while (editing > 0) {
+#ifdef CONFIG_FEATURE_VI_CRASHME
+               if (crashme > 0) {
+                       if ((end - text) > 1) {
+                               crash_dummy();  // generate a random command
+                       } else {
+                               crashme = 0;
+                               dot =
+                                       string_insert(text, (Byte *) "\n\n#####  Ran out of text to work on.  #####\n\n");      // insert the string
+                               refresh(FALSE);
+                       }
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+               last_input_char = c = get_one_char();   // get a cmd from user
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+               // save a copy of the current line- for the 'U" command
+               if (begin_line(dot) != cur_line) {
+                       cur_line = begin_line(dot);
+                       text_yank(begin_line(dot), end_line(dot), Ureg);
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+               // These are commands that change text[].
+               // Remember the input for the "." command
+               if (!adding2q && ioq_start == 0
+                       && strchr((char *) modifying_cmds, c) != NULL) {
+                       start_new_cmd_q(c);
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+               do_cmd(c);              // execute the user command
+               //
+               // poll to see if there is input already waiting. if we are
+               // not able to display output fast enough to keep up, skip
+               // the display update until we catch up with input.
+               if (mysleep(0) == 0) {
+                       // no input pending- so update output
+                       refresh(FALSE);
+                       show_status_line();
+               }
+#ifdef CONFIG_FEATURE_VI_CRASHME
+               if (crashme > 0)
+                       crash_test();   // test editor variables
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+       }
+       //-------------------------------------------------------------------
+
+       place_cursor(rows, 0, FALSE);   // go to bottom of screen
+       clear_to_eol();         // Erase to end of line
+       cookmode();
+}
+
+//----- The Colon commands -------------------------------------
+#ifdef CONFIG_FEATURE_VI_COLON
+static Byte *get_one_address(Byte * p, int *addr)      // get colon addr, if present
+{
+       int st;
+       Byte *q;
+
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       Byte c;
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+       Byte *pat, buf[BUFSIZ];
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+
+       *addr = -1;                     // assume no addr
+       if (*p == '.') {        // the current line
+               p++;
+               q = begin_line(dot);
+               *addr = count_lines(text, q);
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       } else if (*p == '\'') {        // is this a mark addr
+               p++;
+               c = tolower(*p);
+               p++;
+               if (c >= 'a' && c <= 'z') {
+                       // we have a mark
+                       c = c - 'a';
+                       q = mark[(int) c];
+                       if (q != NULL) {        // is mark valid
+                               *addr = count_lines(text, q);   // count lines
+                       }
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+       } else if (*p == '/') { // a search pattern
+               q = buf;
+               for (p++; *p; p++) {
+                       if (*p == '/')
+                               break;
+                       *q++ = *p;
+                       *q = '\0';
+               }
+               pat = (Byte *) bb_xstrdup((char *) buf);        // save copy of pattern
+               if (*p == '/')
+                       p++;
+               q = char_search(dot, pat, FORWARD, FULL);
+               if (q != NULL) {
+                       *addr = count_lines(text, q);
+               }
+               free(pat);
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+       } else if (*p == '$') { // the last line in file
+               p++;
+               q = begin_line(end - 1);
+               *addr = count_lines(text, q);
+       } else if (isdigit(*p)) {       // specific line number
+               sscanf((char *) p, "%d%n", addr, &st);
+               p += st;
+       } else {                        // I don't reconise this
+               // unrecognised address- assume -1
+               *addr = -1;
+       }
+       return (p);
+}
+
+static Byte *get_address(Byte *p, int *b, int *e)      // get two colon addrs, if present
+{
+       //----- get the address' i.e., 1,3   'a,'b  -----
+       // get FIRST addr, if present
+       while (isblnk(*p))
+               p++;                            // skip over leading spaces
+       if (*p == '%') {                        // alias for 1,$
+               p++;
+               *b = 1;
+               *e = count_lines(text, end-1);
+               goto ga0;
+       }
+       p = get_one_address(p, b);
+       while (isblnk(*p))
+               p++;
+       if (*p == ',') {                        // is there a address separator
+               p++;
+               while (isblnk(*p))
+                       p++;
+               // get SECOND addr, if present
+               p = get_one_address(p, e);
+       }
+ga0:
+       while (isblnk(*p))
+               p++;                            // skip over trailing spaces
+       return (p);
+}
+
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+static void setops(const Byte *args, const char *opname, int flg_no,
+                       const char *short_opname, int opt)
+{
+       const char *a = (char *) args + flg_no;
+       int l = strlen(opname) - 1; /* opname have + ' ' */
+
+       if (strncasecmp(a, opname, l) == 0 ||
+                       strncasecmp(a, short_opname, 2) == 0) {
+               if(flg_no)
+                       vi_setops &= ~opt;
+                else
+                       vi_setops |= opt;
+       }
+}
+#endif
+
+static void colon(Byte * buf)
+{
+       Byte c, *orig_buf, *buf1, *q, *r;
+       Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
+       int i, l, li, ch, st, b, e;
+       int useforce = FALSE, forced = FALSE;
+       struct stat st_buf;
+
+       // :3154        // if (-e line 3154) goto it  else stay put
+       // :4,33w! foo  // write a portion of buffer to file "foo"
+       // :w           // write all of buffer to current file
+       // :q           // quit
+       // :q!          // quit- dont care about modified file
+       // :'a,'z!sort -u   // filter block through sort
+       // :'f          // goto mark "f"
+       // :'fl         // list literal the mark "f" line
+       // :.r bar      // read file "bar" into buffer before dot
+       // :/123/,/abc/d    // delete lines from "123" line to "abc" line
+       // :/xyz/       // goto the "xyz" line
+       // :s/find/replace/ // substitute pattern "find" with "replace"
+       // :!<cmd>      // run <cmd> then return
+       //
+
+       if (strlen((char *) buf) <= 0)
+               goto vc1;
+       if (*buf == ':')
+               buf++;                  // move past the ':'
+
+       li = st = ch = i = 0;
+       b = e = -1;
+       q = text;                       // assume 1,$ for the range
+       r = end - 1;
+       li = count_lines(text, end - 1);
+       fn = cfn;                       // default to current file
+       memset(cmd, '\0', BUFSIZ);      // clear cmd[]
+       memset(args, '\0', BUFSIZ);     // clear args[]
+
+       // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
+       buf = get_address(buf, &b, &e);
+
+       // remember orig command line
+       orig_buf = buf;
+
+       // get the COMMAND into cmd[]
+       buf1 = cmd;
+       while (*buf != '\0') {
+               if (isspace(*buf))
+                       break;
+               *buf1++ = *buf++;
+       }
+       // get any ARGuments
+       while (isblnk(*buf))
+               buf++;
+       strcpy((char *) args, (char *) buf);
+       buf1 = last_char_is((char *)cmd, '!');
+       if (buf1) {
+               useforce = TRUE;
+               *buf1 = '\0';   // get rid of !
+       }
+       if (b >= 0) {
+               // if there is only one addr, then the addr
+               // is the line number of the single line the
+               // user wants. So, reset the end
+               // pointer to point at end of the "b" line
+               q = find_line(b);       // what line is #b
+               r = end_line(q);
+               li = 1;
+       }
+       if (e >= 0) {
+               // we were given two addrs.  change the
+               // end pointer to the addr given by user.
+               r = find_line(e);       // what line is #e
+               r = end_line(r);
+               li = e - b + 1;
+       }
+       // ------------ now look for the command ------------
+       i = strlen((char *) cmd);
+       if (i == 0) {           // :123CR goto line #123
+               if (b >= 0) {
+                       dot = find_line(b);     // what line is #b
+                       dot_skip_over_ws();
+               }
+       } else if (strncmp((char *) cmd, "!", 1) == 0) {        // run a cmd
+               // :!ls   run the <cmd>
+               (void) alarm(0);                // wait for input- no alarms
+               place_cursor(rows - 1, 0, FALSE);       // go to Status line
+               clear_to_eol();                 // clear the line
+               cookmode();
+               system(orig_buf+1);             // run the cmd
+               rawmode();
+               Hit_Return();                   // let user see results
+               (void) alarm(3);                // done waiting for input
+       } else if (strncmp((char *) cmd, "=", i) == 0) {        // where is the address
+               if (b < 0) {    // no addr given- use defaults
+                       b = e = count_lines(text, dot);
+               }
+               psb("%d", b);
+       } else if (strncasecmp((char *) cmd, "delete", i) == 0) {       // delete lines
+               if (b < 0) {    // no addr given- use defaults
+                       q = begin_line(dot);    // assume .,. for the range
+                       r = end_line(dot);
+               }
+               dot = yank_delete(q, r, 1, YANKDEL);    // save, then delete lines
+               dot_skip_over_ws();
+       } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
+               int sr;
+               sr= 0;
+               // don't edit, if the current file has been modified
+               if (file_modified && ! useforce) {
+                       psbs("No write since last change (:edit! overrides)");
+                       goto vc1;
+               }
+               if (strlen(args) > 0) {
+                       // the user supplied a file name
+                       fn= args;
+               } else if (cfn != 0 && strlen(cfn) > 0) {
+                       // no user supplied name- use the current filename
+                       fn= cfn;
+                       goto vc5;
+               } else {
+                       // no user file name, no current name- punt
+                       psbs("No current filename");
+                       goto vc1;
+               }
+
+               // see if file exists- if not, its just a new file request
+               if ((sr=stat((char*)fn, &st_buf)) < 0) {
+                       // This is just a request for a new file creation.
+                       // The file_insert below will fail but we get
+                       // an empty buffer with a file name.  Then the "write"
+                       // command can do the create.
+               } else {
+                       if ((st_buf.st_mode & (S_IFREG)) == 0) {
+                               // This is not a regular file
+                               psbs("\"%s\" is not a regular file", fn);
+                               goto vc1;
+                       }
+                       if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
+                               // dont have any read permissions
+                               psbs("\"%s\" is not readable", fn);
+                               goto vc1;
+                       }
+               }
+
+               // There is a read-able regular file
+               // make this the current file
+               q = (Byte *) bb_xstrdup((char *) fn);   // save the cfn
+               free(cfn);              // free the old name
+               cfn = q;                        // remember new cfn
+
+         vc5:
+               // delete all the contents of text[]
+               new_text(2 * file_size(fn));
+               screenbegin = dot = end = text;
+
+               // insert new file
+               ch = file_insert(fn, text, file_size(fn));
+
+               if (ch < 1) {
+                       // start empty buf with dummy line
+                       (void) char_insert(text, '\n');
+                       ch= 1;
+               }
+               file_modified = FALSE;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+               if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
+                       free(reg[Ureg]);        //   free orig line reg- for 'U'
+                       reg[Ureg]= 0;
+               }
+               if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
+                       free(reg[YDreg]);       //   free default yank/delete register
+                       reg[YDreg]= 0;
+               }
+               for (li = 0; li < 28; li++) {
+                       mark[li] = 0;
+               }                               // init the marks
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+               // how many lines in text[]?
+               li = count_lines(text, end - 1);
+               psb("\"%s\"%s"
+#ifdef CONFIG_FEATURE_VI_READONLY
+                       "%s"
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+                       " %dL, %dC", cfn,
+                       (sr < 0 ? " [New file]" : ""),
+#ifdef CONFIG_FEATURE_VI_READONLY
+                       ((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+                       li, ch);
+       } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
+               if (b != -1 || e != -1) {
+                       ni((Byte *) "No address allowed on this command");
+                       goto vc1;
+               }
+               if (strlen((char *) args) > 0) {
+                       // user wants a new filename
+                       free(cfn);
+                       cfn = (Byte *) bb_xstrdup((char *) args);
+               } else {
+                       // user wants file status info
+                       edit_status();
+               }
+       } else if (strncasecmp((char *) cmd, "features", i) == 0) {     // what features are available
+               // print out values of all features
+               place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
+               clear_to_eol(); // clear the line
+               cookmode();
+               show_help();
+               rawmode();
+               Hit_Return();
+       } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
+               if (b < 0) {    // no addr given- use defaults
+                       q = begin_line(dot);    // assume .,. for the range
+                       r = end_line(dot);
+               }
+               place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
+               clear_to_eol(); // clear the line
+               puts("\r");
+               for (; q <= r; q++) {
+                       int c_is_no_print;
+
+                       c = *q;
+                       c_is_no_print = c > 127 && !Isprint(c);
+                       if (c_is_no_print) {
+                               c = '.';
+                               standout_start();
+                               }
+                       if (c == '\n') {
+                               write1("$\r");
+                       } else if (c < ' ' || c == 127) {
+                               putchar('^');
+                               if(c == 127)
+                                       c = '?';
+                                else
+                               c += '@';
+                       }
+                       putchar(c);
+                       if (c_is_no_print)
+                               standout_end();
+               }
+#ifdef CONFIG_FEATURE_VI_SET
+         vc2:
+#endif                                                 /* CONFIG_FEATURE_VI_SET */
+               Hit_Return();
+       } else if ((strncasecmp((char *) cmd, "quit", i) == 0) ||       // Quit
+                          (strncasecmp((char *) cmd, "next", i) == 0)) {       // edit next file
+               if (useforce) {
+                       // force end of argv list
+                       if (*cmd == 'q') {
+                               optind = save_argc;
+                       }
+                       editing = 0;
+                       goto vc1;
+               }
+               // don't exit if the file been modified
+               if (file_modified) {
+                       psbs("No write since last change (:%s! overrides)",
+                                (*cmd == 'q' ? "quit" : "next"));
+                       goto vc1;
+               }
+               // are there other file to edit
+               if (*cmd == 'q' && optind < save_argc - 1) {
+                       psbs("%d more file to edit", (save_argc - optind - 1));
+                       goto vc1;
+               }
+               if (*cmd == 'n' && optind >= save_argc - 1) {
+                       psbs("No more files to edit");
+                       goto vc1;
+               }
+               editing = 0;
+       } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
+               fn = args;
+               if (strlen((char *) fn) <= 0) {
+                       psbs("No filename given");
+                       goto vc1;
+               }
+               if (b < 0) {    // no addr given- use defaults
+                       q = begin_line(dot);    // assume "dot"
+               }
+               // read after current line- unless user said ":0r foo"
+               if (b != 0)
+                       q = next_line(q);
+#ifdef CONFIG_FEATURE_VI_READONLY
+               l= readonly;                    // remember current files' status
+#endif
+               ch = file_insert(fn, q, file_size(fn));
+#ifdef CONFIG_FEATURE_VI_READONLY
+               readonly= l;
+#endif
+               if (ch < 0)
+                       goto vc1;       // nothing was inserted
+               // how many lines in text[]?
+               li = count_lines(q, q + ch - 1);
+               psb("\"%s\""
+#ifdef CONFIG_FEATURE_VI_READONLY
+                       "%s"
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+                       " %dL, %dC", fn,
+#ifdef CONFIG_FEATURE_VI_READONLY
+                       ((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+                       li, ch);
+               if (ch > 0) {
+                       // if the insert is before "dot" then we need to update
+                       if (q <= dot)
+                               dot += ch;
+                       file_modified = TRUE;
+               }
+       } else if (strncasecmp((char *) cmd, "rewind", i) == 0) {       // rewind cmd line args
+               if (file_modified && ! useforce) {
+                       psbs("No write since last change (:rewind! overrides)");
+               } else {
+                       // reset the filenames to edit
+                       optind = fn_start - 1;
+                       editing = 0;
+               }
+#ifdef CONFIG_FEATURE_VI_SET
+       } else if (strncasecmp((char *) cmd, "set", i) == 0) {  // set or clear features
+               i = 0;                  // offset into args
+               if (strlen((char *) args) == 0) {
+                       // print out values of all options
+                       place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
+                       clear_to_eol(); // clear the line
+                       printf("----------------------------------------\r\n");
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+                       if (!autoindent)
+                               printf("no");
+                       printf("autoindent ");
+                       if (!err_method)
+                               printf("no");
+                       printf("flash ");
+                       if (!ignorecase)
+                               printf("no");
+                       printf("ignorecase ");
+                       if (!showmatch)
+                               printf("no");
+                       printf("showmatch ");
+                       printf("tabstop=%d ", tabstop);
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+                       printf("\r\n");
+                       goto vc2;
+               }
+               if (strncasecmp((char *) args, "no", 2) == 0)
+                       i = 2;          // ":set noautoindent"
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+               setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
+               setops(args, "flash ", i, "fl", VI_ERR_METHOD);
+               setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
+               setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
+               if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
+                       sscanf(strchr((char *) args + i, '='), "=%d", &ch);
+                       if (ch > 0 && ch < columns - 1)
+                               tabstop = ch;
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+#endif                                                 /* CONFIG_FEATURE_VI_SET */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+       } else if (strncasecmp((char *) cmd, "s", 1) == 0) {    // substitute a pattern with a replacement pattern
+               Byte *ls, *F, *R;
+               int gflag;
+
+               // F points to the "find" pattern
+               // R points to the "replace" pattern
+               // replace the cmd line delimiters "/" with NULLs
+               gflag = 0;              // global replace flag
+               c = orig_buf[1];        // what is the delimiter
+               F = orig_buf + 2;       // start of "find"
+               R = (Byte *) strchr((char *) F, c);     // middle delimiter
+               if (!R) goto colon_s_fail;
+               *R++ = '\0';    // terminate "find"
+               buf1 = (Byte *) strchr((char *) R, c);
+               if (!buf1) goto colon_s_fail;
+               *buf1++ = '\0'; // terminate "replace"
+               if (*buf1 == 'g') {     // :s/foo/bar/g
+                       buf1++;
+                       gflag++;        // turn on gflag
+               }
+               q = begin_line(q);
+               if (b < 0) {    // maybe :s/foo/bar/
+                       q = begin_line(dot);    // start with cur line
+                       b = count_lines(text, q);       // cur line number
+               }
+               if (e < 0)
+                       e = b;          // maybe :.s/foo/bar/
+               for (i = b; i <= e; i++) {      // so, :20,23 s \0 find \0 replace \0
+                       ls = q;         // orig line start
+                 vc4:
+                       buf1 = char_search(q, F, FORWARD, LIMITED);     // search cur line only for "find"
+                       if (buf1 != NULL) {
+                               // we found the "find" pattern- delete it
+                               (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
+                               // inset the "replace" patern
+                               (void) string_insert(buf1, R);  // insert the string
+                               // check for "global"  :s/foo/bar/g
+                               if (gflag == 1) {
+                                       if ((buf1 + strlen((char *) R)) < end_line(ls)) {
+                                               q = buf1 + strlen((char *) R);
+                                               goto vc4;       // don't let q move past cur line
+                                       }
+                               }
+                       }
+                       q = next_line(ls);
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+       } else if (strncasecmp((char *) cmd, "version", i) == 0) {      // show software version
+               psb("%s", vi_Version);
+       } else if ((strncasecmp((char *) cmd, "write", i) == 0) ||      // write text to file
+                          (strncasecmp((char *) cmd, "wq", i) == 0) ||
+                          (strncasecmp((char *) cmd, "x", i) == 0)) {
+               // is there a file name to write to?
+               if (strlen((char *) args) > 0) {
+                       fn = args;
+               }
+#ifdef CONFIG_FEATURE_VI_READONLY
+               if ((vi_readonly || readonly) && ! useforce) {
+                       psbs("\"%s\" File is read only", fn);
+                       goto vc3;
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+               // how many lines in text[]?
+               li = count_lines(q, r);
+               ch = r - q + 1;
+               // see if file exists- if not, its just a new file request
+               if (useforce) {
+                       // if "fn" is not write-able, chmod u+w
+                       // sprintf(syscmd, "chmod u+w %s", fn);
+                       // system(syscmd);
+                       forced = TRUE;
+               }
+               l = file_write(fn, q, r);
+               if (useforce && forced) {
+                       // chmod u-w
+                       // sprintf(syscmd, "chmod u-w %s", fn);
+                       // system(syscmd);
+                       forced = FALSE;
+               }
+               psb("\"%s\" %dL, %dC", fn, li, l);
+               if (q == text && r == end - 1 && l == ch)
+                       file_modified = FALSE;
+               if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
+                       editing = 0;
+               }
+#ifdef CONFIG_FEATURE_VI_READONLY
+         vc3:;
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
+               if (b < 0) {    // no addr given- use defaults
+                       q = begin_line(dot);    // assume .,. for the range
+                       r = end_line(dot);
+               }
+               text_yank(q, r, YDreg);
+               li = count_lines(q, r);
+               psb("Yank %d lines (%d chars) into [%c]",
+                       li, strlen((char *) reg[YDreg]), what_reg());
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+       } else {
+               // cmd unknown
+               ni((Byte *) cmd);
+       }
+  vc1:
+       dot = bound_dot(dot);   // make sure "dot" is valid
+       return;
+#ifdef CONFIG_FEATURE_VI_SEARCH
+colon_s_fail:
+       psb(":s expression missing delimiters");
+#endif
+}
+
+static void Hit_Return(void)
+{
+       char c;
+
+       standout_start();       // start reverse video
+       write1("[Hit return to continue]");
+       standout_end();         // end reverse video
+       while ((c = get_one_char()) != '\n' && c != '\r')       /*do nothing */
+               ;
+       redraw(TRUE);           // force redraw all
+}
+#endif                                                 /* CONFIG_FEATURE_VI_COLON */
+
+//----- Synchronize the cursor to Dot --------------------------
+static void sync_cursor(Byte * d, int *row, int *col)
+{
+       Byte *beg_cur, *end_cur;        // begin and end of "d" line
+       Byte *beg_scr, *end_scr;        // begin and end of screen
+       Byte *tp;
+       int cnt, ro, co;
+
+       beg_cur = begin_line(d);        // first char of cur line
+       end_cur = end_line(d);  // last char of cur line
+
+       beg_scr = end_scr = screenbegin;        // first char of screen
+       end_scr = end_screen(); // last char of screen
+
+       if (beg_cur < screenbegin) {
+               // "d" is before  top line on screen
+               // how many lines do we have to move
+               cnt = count_lines(beg_cur, screenbegin);
+         sc1:
+               screenbegin = beg_cur;
+               if (cnt > (rows - 1) / 2) {
+                       // we moved too many lines. put "dot" in middle of screen
+                       for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
+                               screenbegin = prev_line(screenbegin);
+                       }
+               }
+       } else if (beg_cur > end_scr) {
+               // "d" is after bottom line on screen
+               // how many lines do we have to move
+               cnt = count_lines(end_scr, beg_cur);
+               if (cnt > (rows - 1) / 2)
+                       goto sc1;       // too many lines
+               for (ro = 0; ro < cnt - 1; ro++) {
+                       // move screen begin the same amount
+                       screenbegin = next_line(screenbegin);
+                       // now, move the end of screen
+                       end_scr = next_line(end_scr);
+                       end_scr = end_line(end_scr);
+               }
+       }
+       // "d" is on screen- find out which row
+       tp = screenbegin;
+       for (ro = 0; ro < rows - 1; ro++) {     // drive "ro" to correct row
+               if (tp == beg_cur)
+                       break;
+               tp = next_line(tp);
+       }
+
+       // find out what col "d" is on
+       co = 0;
+       do {                            // drive "co" to correct column
+               if (*tp == '\n' || *tp == '\0')
+                       break;
+               if (*tp == '\t') {
+                       //         7       - (co %    8  )
+                       co += ((tabstop - 1) - (co % tabstop));
+               } else if (*tp < ' ' || *tp == 127) {
+                       co++;           // display as ^X, use 2 columns
+               }
+       } while (tp++ < d && ++co);
+
+       // "co" is the column where "dot" is.
+       // The screen has "columns" columns.
+       // The currently displayed columns are  0+offset -- columns+ofset
+       // |-------------------------------------------------------------|
+       //               ^ ^                                ^
+       //        offset | |------- columns ----------------|
+       //
+       // If "co" is already in this range then we do not have to adjust offset
+       //      but, we do have to subtract the "offset" bias from "co".
+       // If "co" is outside this range then we have to change "offset".
+       // If the first char of a line is a tab the cursor will try to stay
+       //  in column 7, but we have to set offset to 0.
+
+       if (co < 0 + offset) {
+               offset = co;
+       }
+       if (co >= columns + offset) {
+               offset = co - columns + 1;
+       }
+       // if the first char of the line is a tab, and "dot" is sitting on it
+       //  force offset to 0.
+       if (d == beg_cur && *d == '\t') {
+               offset = 0;
+       }
+       co -= offset;
+
+       *row = ro;
+       *col = co;
+}
+
+//----- Text Movement Routines ---------------------------------
+static Byte *begin_line(Byte * p) // return pointer to first char cur line
+{
+       while (p > text && p[-1] != '\n')
+               p--;                    // go to cur line B-o-l
+       return (p);
+}
+
+static Byte *end_line(Byte * p) // return pointer to NL of cur line line
+{
+       while (p < end - 1 && *p != '\n')
+               p++;                    // go to cur line E-o-l
+       return (p);
+}
+
+static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
+{
+       while (p < end - 1 && *p != '\n')
+               p++;                    // go to cur line E-o-l
+       // Try to stay off of the Newline
+       if (*p == '\n' && (p - begin_line(p)) > 0)
+               p--;
+       return (p);
+}
+
+static Byte *prev_line(Byte * p) // return pointer first char prev line
+{
+       p = begin_line(p);      // goto begining of cur line
+       if (p[-1] == '\n' && p > text)
+               p--;                    // step to prev line
+       p = begin_line(p);      // goto begining of prev line
+       return (p);
+}
+
+static Byte *next_line(Byte * p) // return pointer first char next line
+{
+       p = end_line(p);
+       if (*p == '\n' && p < end - 1)
+               p++;                    // step to next line
+       return (p);
+}
+
+//----- Text Information Routines ------------------------------
+static Byte *end_screen(void)
+{
+       Byte *q;
+       int cnt;
+
+       // find new bottom line
+       q = screenbegin;
+       for (cnt = 0; cnt < rows - 2; cnt++)
+               q = next_line(q);
+       q = end_line(q);
+       return (q);
+}
+
+static int count_lines(Byte * start, Byte * stop) // count line from start to stop
+{
+       Byte *q;
+       int cnt;
+
+       if (stop < start) {     // start and stop are backwards- reverse them
+               q = start;
+               start = stop;
+               stop = q;
+       }
+       cnt = 0;
+       stop = end_line(stop);  // get to end of this line
+       for (q = start; q <= stop && q <= end - 1; q++) {
+               if (*q == '\n')
+                       cnt++;
+       }
+       return (cnt);
+}
+
+static Byte *find_line(int li) // find begining of line #li
+{
+       Byte *q;
+
+       for (q = text; li > 1; li--) {
+               q = next_line(q);
+       }
+       return (q);
+}
+
+//----- Dot Movement Routines ----------------------------------
+static void dot_left(void)
+{
+       if (dot > text && dot[-1] != '\n')
+               dot--;
+}
+
+static void dot_right(void)
+{
+       if (dot < end - 1 && *dot != '\n')
+               dot++;
+}
+
+static void dot_begin(void)
+{
+       dot = begin_line(dot);  // return pointer to first char cur line
+}
+
+static void dot_end(void)
+{
+       dot = end_line(dot);    // return pointer to last char cur line
+}
+
+static Byte *move_to_col(Byte * p, int l)
+{
+       int co;
+
+       p = begin_line(p);
+       co = 0;
+       do {
+               if (*p == '\n' || *p == '\0')
+                       break;
+               if (*p == '\t') {
+                       //         7       - (co %    8  )
+                       co += ((tabstop - 1) - (co % tabstop));
+               } else if (*p < ' ' || *p == 127) {
+                       co++;           // display as ^X, use 2 columns
+               }
+       } while (++co <= l && p++ < end);
+       return (p);
+}
+
+static void dot_next(void)
+{
+       dot = next_line(dot);
+}
+
+static void dot_prev(void)
+{
+       dot = prev_line(dot);
+}
+
+static void dot_scroll(int cnt, int dir)
+{
+       Byte *q;
+
+       for (; cnt > 0; cnt--) {
+               if (dir < 0) {
+                       // scroll Backwards
+                       // ctrl-Y  scroll up one line
+                       screenbegin = prev_line(screenbegin);
+               } else {
+                       // scroll Forwards
+                       // ctrl-E  scroll down one line
+                       screenbegin = next_line(screenbegin);
+               }
+       }
+       // make sure "dot" stays on the screen so we dont scroll off
+       if (dot < screenbegin)
+               dot = screenbegin;
+       q = end_screen();       // find new bottom line
+       if (dot > q)
+               dot = begin_line(q);    // is dot is below bottom line?
+       dot_skip_over_ws();
+}
+
+static void dot_skip_over_ws(void)
+{
+       // skip WS
+       while (isspace(*dot) && *dot != '\n' && dot < end - 1)
+               dot++;
+}
+
+static void dot_delete(void)   // delete the char at 'dot'
+{
+       (void) text_hole_delete(dot, dot);
+}
+
+static Byte *bound_dot(Byte * p) // make sure  text[0] <= P < "end"
+{
+       if (p >= end && end > text) {
+               p = end - 1;
+               indicate_error('1');
+       }
+       if (p < text) {
+               p = text;
+               indicate_error('2');
+       }
+       return (p);
+}
+
+//----- Helper Utility Routines --------------------------------
+
+//----------------------------------------------------------------
+//----- Char Routines --------------------------------------------
+/* Chars that are part of a word-
+ *    0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ * Chars that are Not part of a word (stoppers)
+ *    !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
+ * Chars that are WhiteSpace
+ *    TAB NEWLINE VT FF RETURN SPACE
+ * DO NOT COUNT NEWLINE AS WHITESPACE
+ */
+
+static Byte *new_screen(int ro, int co)
+{
+       int li;
+
+       free(screen);
+       screensize = ro * co + 8;
+       screen = (Byte *) xmalloc(screensize);
+       // initialize the new screen. assume this will be a empty file.
+       screen_erase();
+       //   non-existent text[] lines start with a tilde (~).
+       for (li = 1; li < ro - 1; li++) {
+               screen[(li * co) + 0] = '~';
+       }
+       return (screen);
+}
+
+static Byte *new_text(int size)
+{
+       if (size < 10240)
+               size = 10240;   // have a minimum size for new files
+       free(text);
+       text = (Byte *) xmalloc(size + 8);
+       memset(text, '\0', size);       // clear new text[]
+       //text += 4;            // leave some room for "oops"
+       textend = text + size - 1;
+       //textend -= 4;         // leave some root for "oops"
+       return (text);
+}
+
+#ifdef CONFIG_FEATURE_VI_SEARCH
+static int mycmp(Byte * s1, Byte * s2, int len)
+{
+       int i;
+
+       i = strncmp((char *) s1, (char *) s2, len);
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+       if (ignorecase) {
+               i = strncasecmp((char *) s1, (char *) s2, len);
+       }
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+       return (i);
+}
+
+static Byte *char_search(Byte * p, Byte * pat, int dir, int range)     // search for pattern starting at p
+{
+#ifndef REGEX_SEARCH
+       Byte *start, *stop;
+       int len;
+
+       len = strlen((char *) pat);
+       if (dir == FORWARD) {
+               stop = end - 1; // assume range is p - end-1
+               if (range == LIMITED)
+                       stop = next_line(p);    // range is to next line
+               for (start = p; start < stop; start++) {
+                       if (mycmp(start, pat, len) == 0) {
+                               return (start);
+                       }
+               }
+       } else if (dir == BACK) {
+               stop = text;    // assume range is text - p
+               if (range == LIMITED)
+                       stop = prev_line(p);    // range is to prev line
+               for (start = p - len; start >= stop; start--) {
+                       if (mycmp(start, pat, len) == 0) {
+                               return (start);
+                       }
+               }
+       }
+       // pattern not found
+       return (NULL);
+#else                                                  /*REGEX_SEARCH */
+       char *q;
+       struct re_pattern_buffer preg;
+       int i;
+       int size, range;
+
+       re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+       preg.translate = 0;
+       preg.fastmap = 0;
+       preg.buffer = 0;
+       preg.allocated = 0;
+
+       // assume a LIMITED forward search
+       q = next_line(p);
+       q = end_line(q);
+       q = end - 1;
+       if (dir == BACK) {
+               q = prev_line(p);
+               q = text;
+       }
+       // count the number of chars to search over, forward or backward
+       size = q - p;
+       if (size < 0)
+               size = p - q;
+       // RANGE could be negative if we are searching backwards
+       range = q - p;
+
+       q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
+       if (q != 0) {
+               // The pattern was not compiled
+               psbs("bad search pattern: \"%s\": %s", pat, q);
+               i = 0;                  // return p if pattern not compiled
+               goto cs1;
+       }
+
+       q = p;
+       if (range < 0) {
+               q = p - size;
+               if (q < text)
+                       q = text;
+       }
+       // search for the compiled pattern, preg, in p[]
+       // range < 0-  search backward
+       // range > 0-  search forward
+       // 0 < start < size
+       // re_search() < 0  not found or error
+       // re_search() > 0  index of found pattern
+       //            struct pattern    char     int    int    int     struct reg
+       // re_search (*pattern_buffer,  *string, size,  start, range,  *regs)
+       i = re_search(&preg, q, size, 0, range, 0);
+       if (i == -1) {
+               p = 0;
+               i = 0;                  // return NULL if pattern not found
+       }
+  cs1:
+       if (dir == FORWARD) {
+               p = p + i;
+       } else {
+               p = p - i;
+       }
+       return (p);
+#endif                                                 /*REGEX_SEARCH */
+}
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+
+static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
+{
+       if (c == 22) {          // Is this an ctrl-V?
+               p = stupid_insert(p, '^');      // use ^ to indicate literal next
+               p--;                    // backup onto ^
+               refresh(FALSE); // show the ^
+               c = get_one_char();
+               *p = c;
+               p++;
+               file_modified = TRUE;   // has the file been modified
+       } else if (c == 27) {   // Is this an ESC?
+               cmd_mode = 0;
+               cmdcnt = 0;
+               end_cmd_q();    // stop adding to q
+               strcpy((char *) status_buffer, " ");    // clear the status buffer
+               if ((p[-1] != '\n') && (dot>text)) {
+                       p--;
+               }
+       } else if (c == erase_char) {   // Is this a BS
+               //     123456789
+               if ((p[-1] != '\n') && (dot>text)) {
+                       p--;
+                       p = text_hole_delete(p, p);     // shrink buffer 1 char
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+                       // also rmove char from last_modifying_cmd
+                       if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
+                               Byte *q;
+
+                               q = last_modifying_cmd;
+                               q[strlen((char *) q) - 1] = '\0';       // erase BS
+                               q[strlen((char *) q) - 1] = '\0';       // erase prev char
+                       }
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+               }
+       } else {
+               // insert a char into text[]
+               Byte *sp;               // "save p"
+
+               if (c == 13)
+                       c = '\n';       // translate \r to \n
+               sp = p;                 // remember addr of insert
+               p = stupid_insert(p, c);        // insert the char
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+               if (showmatch && strchr(")]}", *sp) != NULL) {
+                       showmatching(sp);
+               }
+               if (autoindent && c == '\n') {  // auto indent the new line
+                       Byte *q;
+
+                       q = prev_line(p);       // use prev line as templet
+                       for (; isblnk(*q); q++) {
+                               p = stupid_insert(p, *q);       // insert the char
+                       }
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+       }
+       return (p);
+}
+
+static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
+{
+       p = text_hole_make(p, 1);
+       if (p != 0) {
+               *p = c;
+               file_modified = TRUE;   // has the file been modified
+               p++;
+       }
+       return (p);
+}
+
+static Byte find_range(Byte ** start, Byte ** stop, Byte c)
+{
+       Byte *save_dot, *p, *q;
+       int cnt;
+
+       save_dot = dot;
+       p = q = dot;
+
+       if (strchr("cdy><", c)) {
+               // these cmds operate on whole lines
+               p = q = begin_line(p);
+               for (cnt = 1; cnt < cmdcnt; cnt++) {
+                       q = next_line(q);
+               }
+               q = end_line(q);
+       } else if (strchr("^%$0bBeEft", c)) {
+               // These cmds operate on char positions
+               do_cmd(c);              // execute movement cmd
+               q = dot;
+       } else if (strchr("wW", c)) {
+               do_cmd(c);              // execute movement cmd
+               // if we are at the next word's first char
+               // step back one char
+               // but check the possibilities when it is true
+               if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
+                               || (ispunct(dot[-1]) && !ispunct(dot[0]))
+                               || (isalnum(dot[-1]) && !isalnum(dot[0]))))
+                       dot--;          // move back off of next word
+               if (dot > text && *dot == '\n')
+                       dot--;          // stay off NL
+               q = dot;
+       } else if (strchr("H-k{", c)) {
+               // these operate on multi-lines backwards
+               q = end_line(dot);      // find NL
+               do_cmd(c);              // execute movement cmd
+               dot_begin();
+               p = dot;
+       } else if (strchr("L+j}\r\n", c)) {
+               // these operate on multi-lines forwards
+               p = begin_line(dot);
+               do_cmd(c);              // execute movement cmd
+               dot_end();              // find NL
+               q = dot;
+       } else {
+               c = 27;                 // error- return an ESC char
+               //break;
+       }
+       *start = p;
+       *stop = q;
+       if (q < p) {
+               *start = q;
+               *stop = p;
+       }
+       dot = save_dot;
+       return (c);
+}
+
+static int st_test(Byte * p, int type, int dir, Byte * tested)
+{
+       Byte c, c0, ci;
+       int test, inc;
+
+       inc = dir;
+       c = c0 = p[0];
+       ci = p[inc];
+       test = 0;
+
+       if (type == S_BEFORE_WS) {
+               c = ci;
+               test = ((!isspace(c)) || c == '\n');
+       }
+       if (type == S_TO_WS) {
+               c = c0;
+               test = ((!isspace(c)) || c == '\n');
+       }
+       if (type == S_OVER_WS) {
+               c = c0;
+               test = ((isspace(c)));
+       }
+       if (type == S_END_PUNCT) {
+               c = ci;
+               test = ((ispunct(c)));
+       }
+       if (type == S_END_ALNUM) {
+               c = ci;
+               test = ((isalnum(c)) || c == '_');
+       }
+       *tested = c;
+       return (test);
+}
+
+static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
+{
+       Byte c;
+
+       while (st_test(p, type, dir, &c)) {
+               // make sure we limit search to correct number of lines
+               if (c == '\n' && --linecnt < 1)
+                       break;
+               if (dir >= 0 && p >= end - 1)
+                       break;
+               if (dir < 0 && p <= text)
+                       break;
+               p += dir;               // move to next char
+       }
+       return (p);
+}
+
+// find matching char of pair  ()  []  {}
+static Byte *find_pair(Byte * p, Byte c)
+{
+       Byte match, *q;
+       int dir, level;
+
+       match = ')';
+       level = 1;
+       dir = 1;                        // assume forward
+       switch (c) {
+       case '(':
+               match = ')';
+               break;
+       case '[':
+               match = ']';
+               break;
+       case '{':
+               match = '}';
+               break;
+       case ')':
+               match = '(';
+               dir = -1;
+               break;
+       case ']':
+               match = '[';
+               dir = -1;
+               break;
+       case '}':
+               match = '{';
+               dir = -1;
+               break;
+       }
+       for (q = p + dir; text <= q && q < end; q += dir) {
+               // look for match, count levels of pairs  (( ))
+               if (*q == c)
+                       level++;        // increase pair levels
+               if (*q == match)
+                       level--;        // reduce pair level
+               if (level == 0)
+                       break;          // found matching pair
+       }
+       if (level != 0)
+               q = NULL;               // indicate no match
+       return (q);
+}
+
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+// show the matching char of a pair,  ()  []  {}
+static void showmatching(Byte * p)
+{
+       Byte *q, *save_dot;
+
+       // we found half of a pair
+       q = find_pair(p, *p);   // get loc of matching char
+       if (q == NULL) {
+               indicate_error('3');    // no matching char
+       } else {
+               // "q" now points to matching pair
+               save_dot = dot; // remember where we are
+               dot = q;                // go to new loc
+               refresh(FALSE); // let the user see it
+               (void) mysleep(40);     // give user some time
+               dot = save_dot; // go back to old loc
+               refresh(FALSE);
+       }
+}
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+
+//  open a hole in text[]
+static Byte *text_hole_make(Byte * p, int size)        // at "p", make a 'size' byte hole
+{
+       Byte *src, *dest;
+       int cnt;
+
+       if (size <= 0)
+               goto thm0;
+       src = p;
+       dest = p + size;
+       cnt = end - src;        // the rest of buffer
+       if (memmove(dest, src, cnt) != dest) {
+               psbs("can't create room for new characters");
+       }
+       memset(p, ' ', size);   // clear new hole
+       end = end + size;       // adjust the new END
+       file_modified = TRUE;   // has the file been modified
+  thm0:
+       return (p);
+}
+
+//  close a hole in text[]
+static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
+{
+       Byte *src, *dest;
+       int cnt, hole_size;
+
+       // move forwards, from beginning
+       // assume p <= q
+       src = q + 1;
+       dest = p;
+       if (q < p) {            // they are backward- swap them
+               src = p + 1;
+               dest = q;
+       }
+       hole_size = q - p + 1;
+       cnt = end - src;
+       if (src < text || src > end)
+               goto thd0;
+       if (dest < text || dest >= end)
+               goto thd0;
+       if (src >= end)
+               goto thd_atend; // just delete the end of the buffer
+       if (memmove(dest, src, cnt) != dest) {
+               psbs("can't delete the character");
+       }
+  thd_atend:
+       end = end - hole_size;  // adjust the new END
+       if (dest >= end)
+               dest = end - 1; // make sure dest in below end-1
+       if (end <= text)
+               dest = end = text;      // keep pointers valid
+       file_modified = TRUE;   // has the file been modified
+  thd0:
+       return (dest);
+}
+
+// copy text into register, then delete text.
+// if dist <= 0, do not include, or go past, a NewLine
+//
+static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
+{
+       Byte *p;
+
+       // make sure start <= stop
+       if (start > stop) {
+               // they are backwards, reverse them
+               p = start;
+               start = stop;
+               stop = p;
+       }
+       if (dist <= 0) {
+               // we can not cross NL boundaries
+               p = start;
+               if (*p == '\n')
+                       return (p);
+               // dont go past a NewLine
+               for (; p + 1 <= stop; p++) {
+                       if (p[1] == '\n') {
+                               stop = p;       // "stop" just before NewLine
+                               break;
+                       }
+               }
+       }
+       p = start;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       text_yank(start, stop, YDreg);
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+       if (yf == YANKDEL) {
+               p = text_hole_delete(start, stop);
+       }                                       // delete lines
+       return (p);
+}
+
+static void show_help(void)
+{
+       puts("These features are available:"
+#ifdef CONFIG_FEATURE_VI_SEARCH
+       "\n\tPattern searches with / and ?"
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+       "\n\tLast command repeat with \'.\'"
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       "\n\tLine marking with  'x"
+       "\n\tNamed buffers with  \"x"
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_READONLY
+       "\n\tReadonly if vi is called as \"view\""
+       "\n\tReadonly with -R command line arg"
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+#ifdef CONFIG_FEATURE_VI_SET
+       "\n\tSome colon mode commands with \':\'"
+#endif                                                 /* CONFIG_FEATURE_VI_SET */
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+       "\n\tSettable options with \":set\""
+#endif                                                 /* CONFIG_FEATURE_VI_SETOPTS */
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+       "\n\tSignal catching- ^C"
+       "\n\tJob suspend and resume with ^Z"
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+       "\n\tAdapt to window re-sizes"
+#endif                                                 /* CONFIG_FEATURE_VI_WIN_RESIZE */
+       );
+}
+
+static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
+{
+       Byte c, b[2];
+
+       b[1] = '\0';
+       strcpy((char *) buf, "");       // init buf
+       if (strlen((char *) s) <= 0)
+               s = (Byte *) "(NULL)";
+       for (; *s > '\0'; s++) {
+               int c_is_no_print;
+
+               c = *s;
+               c_is_no_print = c > 127 && !Isprint(c);
+               if (c_is_no_print) {
+                       strcat((char *) buf, SOn);
+                       c = '.';
+               }
+               if (c < ' ' || c == 127) {
+                       strcat((char *) buf, "^");
+                       if(c == 127)
+                               c = '?';
+                        else
+                       c += '@';
+               }
+               b[0] = c;
+               strcat((char *) buf, (char *) b);
+               if (c_is_no_print)
+                       strcat((char *) buf, SOs);
+               if (*s == '\n') {
+                       strcat((char *) buf, "$");
+               }
+       }
+}
+
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(Byte c)
+{
+       // release old cmd
+       free(last_modifying_cmd);
+       // get buffer for new cmd
+       last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
+       memset(last_modifying_cmd, '\0', BUFSIZ);       // clear new cmd queue
+       // if there is a current cmd count put it in the buffer first
+       if (cmdcnt > 0)
+               sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
+       // save char c onto queue
+       last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
+       adding2q = 1;
+       return;
+}
+
+static void end_cmd_q(void)
+{
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       YDreg = 26;                     // go back to default Yank/Delete reg
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+       adding2q = 0;
+       return;
+}
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+
+#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
+static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
+{
+       int cnt, i;
+
+       i = strlen((char *) s);
+       p = text_hole_make(p, i);
+       strncpy((char *) p, (char *) s, i);
+       for (cnt = 0; *s != '\0'; s++) {
+               if (*s == '\n')
+                       cnt++;
+       }
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+       return (p);
+}
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
+
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+static Byte *text_yank(Byte * p, Byte * q, int dest)   // copy text into a register
+{
+       Byte *t;
+       int cnt;
+
+       if (q < p) {            // they are backwards- reverse them
+               t = q;
+               q = p;
+               p = t;
+       }
+       cnt = q - p + 1;
+       t = reg[dest];
+       free(t);                //  if already a yank register, free it
+       t = (Byte *) xmalloc(cnt + 1);  // get a new register
+       memset(t, '\0', cnt + 1);       // clear new text[]
+       strncpy((char *) t, (char *) p, cnt);   // copy text[] into bufer
+       reg[dest] = t;
+       return (p);
+}
+
+static Byte what_reg(void)
+{
+       Byte c;
+       int i;
+
+       i = 0;
+       c = 'D';                        // default to D-reg
+       if (0 <= YDreg && YDreg <= 25)
+               c = 'a' + (Byte) YDreg;
+       if (YDreg == 26)
+               c = 'D';
+       if (YDreg == 27)
+               c = 'U';
+       return (c);
+}
+
+static void check_context(Byte cmd)
+{
+       // A context is defined to be "modifying text"
+       // Any modifying command establishes a new context.
+
+       if (dot < context_start || dot > context_end) {
+               if (strchr((char *) modifying_cmds, cmd) != NULL) {
+                       // we are trying to modify text[]- make this the current context
+                       mark[27] = mark[26];    // move cur to prev
+                       mark[26] = dot; // move local to cur
+                       context_start = prev_line(prev_line(dot));
+                       context_end = next_line(next_line(dot));
+                       //loiter= start_loiter= now;
+               }
+       }
+       return;
+}
+
+static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
+{
+       Byte *tmp;
+
+       // the current context is in mark[26]
+       // the previous context is in mark[27]
+       // only swap context if other context is valid
+       if (text <= mark[27] && mark[27] <= end - 1) {
+               tmp = mark[27];
+               mark[27] = mark[26];
+               mark[26] = tmp;
+               p = mark[26];   // where we are going- previous context
+               context_start = prev_line(prev_line(prev_line(p)));
+               context_end = next_line(next_line(next_line(p)));
+       }
+       return (p);
+}
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+
+static int isblnk(Byte c) // is the char a blank or tab
+{
+       return (c == ' ' || c == '\t');
+}
+
+//----- Set terminal attributes --------------------------------
+static void rawmode(void)
+{
+       tcgetattr(0, &term_orig);
+       term_vi = term_orig;
+       term_vi.c_lflag &= (~ICANON & ~ECHO);   // leave ISIG ON- allow intr's
+       term_vi.c_iflag &= (~IXON & ~ICRNL);
+       term_vi.c_oflag &= (~ONLCR);
+       term_vi.c_cc[VMIN] = 1;
+       term_vi.c_cc[VTIME] = 0;
+       erase_char = term_vi.c_cc[VERASE];
+       tcsetattr(0, TCSANOW, &term_vi);
+}
+
+static void cookmode(void)
+{
+       fflush(stdout);
+       tcsetattr(0, TCSANOW, &term_orig);
+}
+
+//----- Come here when we get a window resize signal ---------
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+static void winch_sig(int sig)
+{
+       signal(SIGWINCH, winch_sig);
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+       window_size_get(0);
+#endif                                                 /* CONFIG_FEATURE_VI_WIN_RESIZE */
+       new_screen(rows, columns);      // get memory for virtual screen
+       redraw(TRUE);           // re-draw the screen
+}
+
+//----- Come here when we get a continue signal -------------------
+static void cont_sig(int sig)
+{
+       rawmode();                      // terminal to "raw"
+       *status_buffer = '\0';  // clear the status buffer
+       redraw(TRUE);           // re-draw the screen
+
+       signal(SIGTSTP, suspend_sig);
+       signal(SIGCONT, SIG_DFL);
+       kill(my_pid, SIGCONT);
+}
+
+//----- Come here when we get a Suspend signal -------------------
+static void suspend_sig(int sig)
+{
+       place_cursor(rows - 1, 0, FALSE);       // go to bottom of screen
+       clear_to_eol();         // Erase to end of line
+       cookmode();                     // terminal to "cooked"
+
+       signal(SIGCONT, cont_sig);
+       signal(SIGTSTP, SIG_DFL);
+       kill(my_pid, SIGTSTP);
+}
+
+//----- Come here when we get a signal ---------------------------
+static void catch_sig(int sig)
+{
+       signal(SIGHUP, catch_sig);
+       signal(SIGINT, catch_sig);
+       signal(SIGTERM, catch_sig);
+       signal(SIGALRM, catch_sig);
+       if(sig)
+       longjmp(restart, sig);
+}
+
+//----- Come here when we get a core dump signal -----------------
+static void core_sig(int sig)
+{
+       signal(SIGQUIT, core_sig);
+       signal(SIGILL, core_sig);
+       signal(SIGTRAP, core_sig);
+       signal(SIGIOT, core_sig);
+       signal(SIGABRT, core_sig);
+       signal(SIGFPE, core_sig);
+       signal(SIGBUS, core_sig);
+       signal(SIGSEGV, core_sig);
+#ifdef SIGSYS
+       signal(SIGSYS, core_sig);
+#endif
+
+       if(sig) {       // signaled
+       dot = bound_dot(dot);   // make sure "dot" is valid
+
+       longjmp(restart, sig);
+       }
+}
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+
+static int mysleep(int hund)   // sleep for 'h' 1/100 seconds
+{
+       // Don't hang- Wait 5/100 seconds-  1 Sec= 1000000
+       fflush(stdout);
+       FD_ZERO(&rfds);
+       FD_SET(0, &rfds);
+       tv.tv_sec = 0;
+       tv.tv_usec = hund * 10000;
+       select(1, &rfds, NULL, NULL, &tv);
+       return (FD_ISSET(0, &rfds));
+}
+
+static Byte readbuffer[BUFSIZ];
+static int readed_for_parse;
+
+//----- IO Routines --------------------------------------------
+static Byte readit(void)       // read (maybe cursor) key from stdin
+{
+       Byte c;
+       int n;
+       struct esc_cmds {
+               const char *seq;
+               Byte val;
+       };
+
+       static const struct esc_cmds esccmds[] = {
+               {"OA", (Byte) VI_K_UP},       // cursor key Up
+               {"OB", (Byte) VI_K_DOWN},     // cursor key Down
+               {"OC", (Byte) VI_K_RIGHT},    // Cursor Key Right
+               {"OD", (Byte) VI_K_LEFT},     // cursor key Left
+               {"OH", (Byte) VI_K_HOME},     // Cursor Key Home
+               {"OF", (Byte) VI_K_END},      // Cursor Key End
+               {"[A", (Byte) VI_K_UP},       // cursor key Up
+               {"[B", (Byte) VI_K_DOWN},     // cursor key Down
+               {"[C", (Byte) VI_K_RIGHT},    // Cursor Key Right
+               {"[D", (Byte) VI_K_LEFT},     // cursor key Left
+               {"[H", (Byte) VI_K_HOME},     // Cursor Key Home
+               {"[F", (Byte) VI_K_END},      // Cursor Key End
+               {"[1~", (Byte) VI_K_HOME},     // Cursor Key Home
+               {"[2~", (Byte) VI_K_INSERT},  // Cursor Key Insert
+               {"[4~", (Byte) VI_K_END},      // Cursor Key End
+               {"[5~", (Byte) VI_K_PAGEUP},  // Cursor Key Page Up
+               {"[6~", (Byte) VI_K_PAGEDOWN},        // Cursor Key Page Down
+               {"OP", (Byte) VI_K_FUN1},     // Function Key F1
+               {"OQ", (Byte) VI_K_FUN2},     // Function Key F2
+               {"OR", (Byte) VI_K_FUN3},     // Function Key F3
+               {"OS", (Byte) VI_K_FUN4},     // Function Key F4
+               {"[15~", (Byte) VI_K_FUN5},   // Function Key F5
+               {"[17~", (Byte) VI_K_FUN6},   // Function Key F6
+               {"[18~", (Byte) VI_K_FUN7},   // Function Key F7
+               {"[19~", (Byte) VI_K_FUN8},   // Function Key F8
+               {"[20~", (Byte) VI_K_FUN9},   // Function Key F9
+               {"[21~", (Byte) VI_K_FUN10},  // Function Key F10
+               {"[23~", (Byte) VI_K_FUN11},  // Function Key F11
+               {"[24~", (Byte) VI_K_FUN12},  // Function Key F12
+               {"[11~", (Byte) VI_K_FUN1},   // Function Key F1
+               {"[12~", (Byte) VI_K_FUN2},   // Function Key F2
+               {"[13~", (Byte) VI_K_FUN3},   // Function Key F3
+               {"[14~", (Byte) VI_K_FUN4},   // Function Key F4
+       };
+
+#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
+
+       (void) alarm(0);        // turn alarm OFF while we wait for input
+       fflush(stdout);
+       n = readed_for_parse;
+       // get input from User- are there already input chars in Q?
+       if (n <= 0) {
+         ri0:
+               // the Q is empty, wait for a typed char
+               n = read(0, readbuffer, BUFSIZ - 1);
+               if (n < 0) {
+                       if (errno == EINTR)
+                               goto ri0;       // interrupted sys call
+                       if (errno == EBADF)
+                               editing = 0;
+                       if (errno == EFAULT)
+                               editing = 0;
+                       if (errno == EINVAL)
+                               editing = 0;
+                       if (errno == EIO)
+                               editing = 0;
+                       errno = 0;
+               }
+               if(n <= 0)
+                       return 0;       // error
+               if (readbuffer[0] == 27) {
+       // This is an ESC char. Is this Esc sequence?
+       // Could be bare Esc key. See if there are any
+       // more chars to read after the ESC. This would
+       // be a Function or Cursor Key sequence.
+       FD_ZERO(&rfds);
+       FD_SET(0, &rfds);
+       tv.tv_sec = 0;
+       tv.tv_usec = 50000;     // Wait 5/100 seconds- 1 Sec=1000000
+
+       // keep reading while there are input chars and room in buffer
+                       while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
+               // read the rest of the ESC string
+                               int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
+                               if (r > 0) {
+                                       n += r;
+                               }
+                       }
+               }
+               readed_for_parse = n;
+       }
+       c = readbuffer[0];
+       if(c == 27 && n > 1) {
+       // Maybe cursor or function key?
+               const struct esc_cmds *eindex;
+
+               for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
+                       int cnt = strlen(eindex->seq);
+
+                       if(n <= cnt)
+                               continue;
+                       if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
+                               continue;
+                       // is a Cursor key- put derived value back into Q
+                       c = eindex->val;
+                       // for squeeze out the ESC sequence
+                       n = cnt + 1;
+                       break;
+               }
+               if(eindex == &esccmds[ESCCMDS_COUNT]) {
+                       /* defined ESC sequence not found, set only one ESC */
+                       n = 1;
+       }
+       } else {
+               n = 1;
+       }
+       // remove key sequence from Q
+       readed_for_parse -= n;
+       memmove(readbuffer, readbuffer + n, BUFSIZ - n);
+       (void) alarm(3);        // we are done waiting for input, turn alarm ON
+       return (c);
+}
+
+//----- IO Routines --------------------------------------------
+static Byte get_one_char()
+{
+       static Byte c;
+
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+       // ! adding2q  && ioq == 0  read()
+       // ! adding2q  && ioq != 0  *ioq
+       // adding2q         *last_modifying_cmd= read()
+       if (!adding2q) {
+               // we are not adding to the q.
+               // but, we may be reading from a q
+               if (ioq == 0) {
+                       // there is no current q, read from STDIN
+                       c = readit();   // get the users input
+               } else {
+                       // there is a queue to get chars from first
+                       c = *ioq++;
+                       if (c == '\0') {
+                               // the end of the q, read from STDIN
+                               free(ioq_start);
+                               ioq_start = ioq = 0;
+                               c = readit();   // get the users input
+                       }
+               }
+       } else {
+               // adding STDIN chars to q
+               c = readit();   // get the users input
+               if (last_modifying_cmd != 0) {
+                       int len = strlen((char *) last_modifying_cmd);
+                       if (len + 1 >= BUFSIZ) {
+                               psbs("last_modifying_cmd overrun");
+                       } else {
+                               // add new char to q
+                               last_modifying_cmd[len] = c;
+                       }
+               }
+       }
+#else                                                  /* CONFIG_FEATURE_VI_DOT_CMD */
+       c = readit();           // get the users input
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+       return (c);                     // return the char, where ever it came from
+}
+
+static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
+{
+       Byte buf[BUFSIZ];
+       Byte c;
+       int i;
+       static Byte *obufp = NULL;
+
+       strcpy((char *) buf, (char *) prompt);
+       *status_buffer = '\0';  // clear the status buffer
+       place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
+       clear_to_eol();         // clear the line
+       write1(prompt);      // write out the :, /, or ? prompt
+
+       for (i = strlen((char *) buf); i < BUFSIZ;) {
+               c = get_one_char();     // read user input
+               if (c == '\n' || c == '\r' || c == 27)
+                       break;          // is this end of input
+               if (c == erase_char) {  // user wants to erase prev char
+                       i--;            // backup to prev char
+                       buf[i] = '\0';  // erase the char
+                       buf[i + 1] = '\0';      // null terminate buffer
+                       write1("\b \b");     // erase char on screen
+                       if (i <= 0) {   // user backs up before b-o-l, exit
+                               break;
+                       }
+               } else {
+                       buf[i] = c;     // save char in buffer
+                       buf[i + 1] = '\0';      // make sure buffer is null terminated
+                       putchar(c);   // echo the char back to user
+                       i++;
+               }
+       }
+       refresh(FALSE);
+       free(obufp);
+       obufp = (Byte *) bb_xstrdup((char *) buf);
+       return (obufp);
+}
+
+static int file_size(const Byte * fn) // what is the byte size of "fn"
+{
+       struct stat st_buf;
+       int cnt, sr;
+
+       if (fn == 0 || strlen(fn) <= 0)
+               return (-1);
+       cnt = -1;
+       sr = stat((char *) fn, &st_buf);        // see if file exists
+       if (sr >= 0) {
+               cnt = (int) st_buf.st_size;
+       }
+       return (cnt);
+}
+
+static int file_insert(Byte * fn, Byte * p, int size)
+{
+       int fd, cnt;
+
+       cnt = -1;
+#ifdef CONFIG_FEATURE_VI_READONLY
+       readonly = FALSE;
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+       if (fn == 0 || strlen((char*) fn) <= 0) {
+               psbs("No filename given");
+               goto fi0;
+       }
+       if (size == 0) {
+               // OK- this is just a no-op
+               cnt = 0;
+               goto fi0;
+       }
+       if (size < 0) {
+               psbs("Trying to insert a negative number (%d) of characters", size);
+               goto fi0;
+       }
+       if (p < text || p > end) {
+               psbs("Trying to insert file outside of memory");
+               goto fi0;
+       }
+
+       // see if we can open the file
+#ifdef CONFIG_FEATURE_VI_READONLY
+       if (vi_readonly) goto fi1;              // do not try write-mode
+#endif
+       fd = open((char *) fn, O_RDWR);                 // assume read & write
+       if (fd < 0) {
+               // could not open for writing- maybe file is read only
+#ifdef CONFIG_FEATURE_VI_READONLY
+  fi1:
+#endif
+               fd = open((char *) fn, O_RDONLY);       // try read-only
+               if (fd < 0) {
+                       psbs("\"%s\" %s", fn, "could not open file");
+                       goto fi0;
+               }
+#ifdef CONFIG_FEATURE_VI_READONLY
+               // got the file- read-only
+               readonly = TRUE;
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+       }
+       p = text_hole_make(p, size);
+       cnt = read(fd, p, size);
+       close(fd);
+       if (cnt < 0) {
+               cnt = -1;
+               p = text_hole_delete(p, p + size - 1);  // un-do buffer insert
+               psbs("could not read file \"%s\"", fn);
+       } else if (cnt < size) {
+               // There was a partial read, shrink unused space text[]
+               p = text_hole_delete(p + cnt, p + (size - cnt) - 1);    // un-do buffer insert
+               psbs("could not read all of file \"%s\"", fn);
+       }
+       if (cnt >= size)
+               file_modified = TRUE;
+  fi0:
+       return (cnt);
+}
+
+static int file_write(Byte * fn, Byte * first, Byte * last)
+{
+       int fd, cnt, charcnt;
+
+       if (fn == 0) {
+               psbs("No current filename");
+               return (-1);
+       }
+       charcnt = 0;
+       // FIXIT- use the correct umask()
+       fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
+       if (fd < 0)
+               return (-1);
+       cnt = last - first + 1;
+       charcnt = write(fd, first, cnt);
+       if (charcnt == cnt) {
+               // good write
+               //file_modified= FALSE; // the file has not been modified
+       } else {
+               charcnt = 0;
+       }
+       close(fd);
+       return (charcnt);
+}
+
+//----- Terminal Drawing ---------------------------------------
+// The terminal is made up of 'rows' line of 'columns' columns.
+// classically this would be 24 x 80.
+//  screen coordinates
+//  0,0     ...     0,79
+//  1,0     ...     1,79
+//  .       ...     .
+//  .       ...     .
+//  22,0    ...     22,79
+//  23,0    ...     23,79   status line
+//
+
+//----- Move the cursor to row x col (count from 0, not 1) -------
+static void place_cursor(int row, int col, int opti)
+{
+       char cm1[BUFSIZ];
+       char *cm;
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+       char cm2[BUFSIZ];
+       Byte *screenp;
+       // char cm3[BUFSIZ];
+       int Rrow= last_row;
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+
+       memset(cm1, '\0', BUFSIZ - 1);  // clear the buffer
+
+       if (row < 0) row = 0;
+       if (row >= rows) row = rows - 1;
+       if (col < 0) col = 0;
+       if (col >= columns) col = columns - 1;
+
+       //----- 1.  Try the standard terminal ESC sequence
+       sprintf((char *) cm1, CMrc, row + 1, col + 1);
+       cm= cm1;
+       if (! opti) goto pc0;
+
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+       //----- find the minimum # of chars to move cursor -------------
+       //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
+       memset(cm2, '\0', BUFSIZ - 1);  // clear the buffer
+
+       // move to the correct row
+       while (row < Rrow) {
+               // the cursor has to move up
+               strcat(cm2, CMup);
+               Rrow--;
+       }
+       while (row > Rrow) {
+               // the cursor has to move down
+               strcat(cm2, CMdown);
+               Rrow++;
+       }
+
+       // now move to the correct column
+       strcat(cm2, "\r");                      // start at col 0
+       // just send out orignal source char to get to correct place
+       screenp = &screen[row * columns];       // start of screen line
+       strncat(cm2, screenp, col);
+
+       //----- 3.  Try some other way of moving cursor
+       //---------------------------------------------
+
+       // pick the shortest cursor motion to send out
+       cm= cm1;
+       if (strlen(cm2) < strlen(cm)) {
+               cm= cm2;
+       }  /* else if (strlen(cm3) < strlen(cm)) {
+               cm= cm3;
+       } */
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+  pc0:
+       write1(cm);                 // move the cursor
+}
+
+//----- Erase from cursor to end of line -----------------------
+static void clear_to_eol()
+{
+       write1(Ceol);   // Erase from cursor to end of line
+}
+
+//----- Erase from cursor to end of screen -----------------------
+static void clear_to_eos()
+{
+       write1(Ceos);   // Erase from cursor to end of screen
+}
+
+//----- Start standout mode ------------------------------------
+static void standout_start() // send "start reverse video" sequence
+{
+       write1(SOs);     // Start reverse video mode
+}
+
+//----- End standout mode --------------------------------------
+static void standout_end() // send "end reverse video" sequence
+{
+       write1(SOn);     // End reverse video mode
+}
+
+//----- Flash the screen  --------------------------------------
+static void flash(int h)
+{
+       standout_start();       // send "start reverse video" sequence
+       redraw(TRUE);
+       (void) mysleep(h);
+       standout_end();         // send "end reverse video" sequence
+       redraw(TRUE);
+}
+
+static void Indicate_Error(void)
+{
+#ifdef CONFIG_FEATURE_VI_CRASHME
+       if (crashme > 0)
+               return;                 // generate a random command
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+       if (!err_method) {
+               write1(bell);   // send out a bell character
+       } else {
+               flash(10);
+       }
+}
+
+//----- Screen[] Routines --------------------------------------
+//----- Erase the Screen[] memory ------------------------------
+static void screen_erase()
+{
+       memset(screen, ' ', screensize);        // clear new screen
+}
+
+//----- Draw the status line at bottom of the screen -------------
+static void show_status_line(void)
+{
+       static int last_cksum;
+       int l, cnt, cksum;
+
+       edit_status();
+       cnt = strlen((char *) status_buffer);
+       for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
+       // don't write the status line unless it changes
+       if (cnt > 0 && last_cksum != cksum) {
+               last_cksum= cksum;              // remember if we have seen this line
+               place_cursor(rows - 1, 0, FALSE);       // put cursor on status line
+               write1(status_buffer);
+               clear_to_eol();
+               place_cursor(crow, ccol, FALSE);        // put cursor back in correct place
+       }
+       fflush(stdout);
+}
+
+//----- format the status buffer, the bottom line of screen ------
+// print status buffer, with STANDOUT mode
+static void psbs(const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+       strcpy((char *) status_buffer, SOs);    // Terminal standout mode on
+       vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
+                        args);
+       strcat((char *) status_buffer, SOn);    // Terminal standout mode off
+       va_end(args);
+
+       return;
+}
+
+// print status buffer
+static void psb(const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+       vsprintf((char *) status_buffer, format, args);
+       va_end(args);
+       return;
+}
+
+static void ni(Byte * s) // display messages
+{
+       Byte buf[BUFSIZ];
+
+       print_literal(buf, s);
+       psbs("\'%s\' is not implemented", buf);
+}
+
+static void edit_status(void)  // show file status on status line
+{
+       int cur, tot, percent;
+
+       cur = count_lines(text, dot);
+       tot = count_lines(text, end - 1);
+       //    current line         percent
+       //   -------------    ~~ ----------
+       //    total lines            100
+       if (tot > 0) {
+               percent = (100 * cur) / tot;
+       } else {
+               cur = tot = 0;
+               percent = 100;
+       }
+       psb("\"%s\""
+#ifdef CONFIG_FEATURE_VI_READONLY
+               "%s"
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+               "%s line %d of %d --%d%%--",
+               (cfn != 0 ? (char *) cfn : "No file"),
+#ifdef CONFIG_FEATURE_VI_READONLY
+               ((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+               (file_modified ? " [modified]" : ""),
+               cur, tot, percent);
+}
+
+//----- Force refresh of all Lines -----------------------------
+static void redraw(int full_screen)
+{
+       place_cursor(0, 0, FALSE);      // put cursor in correct place
+       clear_to_eos();         // tel terminal to erase display
+       screen_erase();         // erase the internal screen buffer
+       refresh(full_screen);   // this will redraw the entire display
+}
+
+//----- Format a text[] line into a buffer ---------------------
+static void format_line(Byte *dest, Byte *src, int li)
+{
+       int co;
+       Byte c;
+
+       for (co= 0; co < MAX_SCR_COLS; co++) {
+               c= ' ';         // assume blank
+               if (li > 0 && co == 0) {
+                       c = '~';        // not first line, assume Tilde
+               }
+               // are there chars in text[] and have we gone past the end
+               if (text < end && src < end) {
+                       c = *src++;
+               }
+               if (c == '\n')
+                       break;
+               if (c > 127 && !Isprint(c)) {
+                       c = '.';
+               }
+               if (c < ' ' || c == 127) {
+                       if (c == '\t') {
+                               c = ' ';
+                               //       co %    8     !=     7
+                               for (; (co % tabstop) != (tabstop - 1); co++) {
+                                       dest[co] = c;
+                               }
+                       } else {
+                               dest[co++] = '^';
+                               if(c == 127)
+                                       c = '?';
+                                else
+                                       c += '@';       // make it visible
+                       }
+               }
+               // the co++ is done here so that the column will
+               // not be overwritten when we blank-out the rest of line
+               dest[co] = c;
+               if (src >= end)
+                       break;
+       }
+}
+
+//----- Refresh the changed screen lines -----------------------
+// Copy the source line from text[] into the buffer and note
+// if the current screenline is different from the new buffer.
+// If they differ then that line needs redrawing on the terminal.
+//
+static void refresh(int full_screen)
+{
+       static int old_offset;
+       int li, changed;
+       Byte buf[MAX_SCR_COLS];
+       Byte *tp, *sp;          // pointer into text[] and screen[]
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+       int last_li= -2;                                // last line that changed- for optimizing cursor movement
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+       window_size_get(0);
+#endif                                                 /* CONFIG_FEATURE_VI_WIN_RESIZE */
+       sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
+       tp = screenbegin;       // index into text[] of top line
+
+       // compare text[] to screen[] and mark screen[] lines that need updating
+       for (li = 0; li < rows - 1; li++) {
+               int cs, ce;                             // column start & end
+               memset(buf, ' ', MAX_SCR_COLS);         // blank-out the buffer
+               buf[MAX_SCR_COLS-1] = 0;                // NULL terminate the buffer
+               // format current text line into buf
+               format_line(buf, tp, li);
+
+               // skip to the end of the current text[] line
+               while (tp < end && *tp++ != '\n') /*no-op*/ ;
+
+               // see if there are any changes between vitual screen and buf
+               changed = FALSE;        // assume no change
+               cs= 0;
+               ce= columns-1;
+               sp = &screen[li * columns];     // start of screen line
+               if (full_screen) {
+                       // force re-draw of every single column from 0 - columns-1
+                       goto re0;
+               }
+               // compare newly formatted buffer with virtual screen
+               // look forward for first difference between buf and screen
+               for ( ; cs <= ce; cs++) {
+                       if (buf[cs + offset] != sp[cs]) {
+                               changed = TRUE; // mark for redraw
+                               break;
+                       }
+               }
+
+               // look backward for last difference between buf and screen
+               for ( ; ce >= cs; ce--) {
+                       if (buf[ce + offset] != sp[ce]) {
+                               changed = TRUE; // mark for redraw
+                               break;
+                       }
+               }
+               // now, cs is index of first diff, and ce is index of last diff
+
+               // if horz offset has changed, force a redraw
+               if (offset != old_offset) {
+  re0:
+                       changed = TRUE;
+               }
+
+               // make a sanity check of columns indexes
+               if (cs < 0) cs= 0;
+               if (ce > columns-1) ce= columns-1;
+               if (cs > ce) {  cs= 0;  ce= columns-1;  }
+               // is there a change between vitual screen and buf
+               if (changed) {
+                       //  copy changed part of buffer to virtual screen
+                       memmove(sp+cs, buf+(cs+offset), ce-cs+1);
+
+                       // move cursor to column of first change
+                       if (offset != old_offset) {
+                               // opti_cur_move is still too stupid
+                               // to handle offsets correctly
+                               place_cursor(li, cs, FALSE);
+                       } else {
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+                               // if this just the next line
+                               //  try to optimize cursor movement
+                               //  otherwise, use standard ESC sequence
+                               place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
+                               last_li= li;
+#else                                                  /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+                               place_cursor(li, cs, FALSE);    // use standard ESC sequence
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+                       }
+
+                       // write line out to terminal
+                       {
+                               int nic = ce-cs+1;
+                               char *out = sp+cs;
+
+                               while(nic-- > 0) {
+                                       putchar(*out);
+                                       out++;
+                               }
+                       }
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+                       last_row = li;
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+               }
+       }
+
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+       place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
+       last_row = crow;
+#else
+       place_cursor(crow, ccol, FALSE);
+#endif                                                 /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+
+       if (offset != old_offset)
+               old_offset = offset;
+}
+
+//---------------------------------------------------------------------
+//----- the Ascii Chart -----------------------------------------------
+//
+//  00 nul   01 soh   02 stx   03 etx   04 eot   05 enq   06 ack   07 bel
+//  08 bs    09 ht    0a nl    0b vt    0c np    0d cr    0e so    0f si
+//  10 dle   11 dc1   12 dc2   13 dc3   14 dc4   15 nak   16 syn   17 etb
+//  18 can   19 em    1a sub   1b esc   1c fs    1d gs    1e rs    1f us
+//  20 sp    21 !     22 "     23 #     24 $     25 %     26 &     27 '
+//  28 (     29 )     2a *     2b +     2c ,     2d -     2e .     2f /
+//  30 0     31 1     32 2     33 3     34 4     35 5     36 6     37 7
+//  38 8     39 9     3a :     3b ;     3c <     3d =     3e >     3f ?
+//  40 @     41 A     42 B     43 C     44 D     45 E     46 F     47 G
+//  48 H     49 I     4a J     4b K     4c L     4d M     4e N     4f O
+//  50 P     51 Q     52 R     53 S     54 T     55 U     56 V     57 W
+//  58 X     59 Y     5a Z     5b [     5c \     5d ]     5e ^     5f _
+//  60 `     61 a     62 b     63 c     64 d     65 e     66 f     67 g
+//  68 h     69 i     6a j     6b k     6c l     6d m     6e n     6f o
+//  70 p     71 q     72 r     73 s     74 t     75 u     76 v     77 w
+//  78 x     79 y     7a z     7b {     7c |     7d }     7e ~     7f del
+//---------------------------------------------------------------------
+
+//----- Execute a Vi Command -----------------------------------
+static void do_cmd(Byte c)
+{
+       Byte c1, *p, *q, *msg, buf[9], *save_dot;
+       int cnt, i, j, dir, yf;
+
+       c1 = c;                         // quiet the compiler
+       cnt = yf = dir = 0;     // quiet the compiler
+       p = q = save_dot = msg = buf;   // quiet the compiler
+       memset(buf, '\0', 9);   // clear buf
+
+       /* if this is a cursor key, skip these checks */
+       switch (c) {
+               case VI_K_UP:
+               case VI_K_DOWN:
+               case VI_K_LEFT:
+               case VI_K_RIGHT:
+               case VI_K_HOME:
+               case VI_K_END:
+               case VI_K_PAGEUP:
+               case VI_K_PAGEDOWN:
+                       goto key_cmd_mode;
+       }
+
+       if (cmd_mode == 2) {
+               //  flip-flop Insert/Replace mode
+               if (c == VI_K_INSERT) goto dc_i;
+               // we are 'R'eplacing the current *dot with new char
+               if (*dot == '\n') {
+                       // don't Replace past E-o-l
+                       cmd_mode = 1;   // convert to insert
+               } else {
+                       if (1 <= c || Isprint(c)) {
+                               if (c != 27)
+                                       dot = yank_delete(dot, dot, 0, YANKDEL);        // delete char
+                               dot = char_insert(dot, c);      // insert new char
+                       }
+                       goto dc1;
+               }
+       }
+       if (cmd_mode == 1) {
+               //  hitting "Insert" twice means "R" replace mode
+               if (c == VI_K_INSERT) goto dc5;
+               // insert the char c at "dot"
+               if (1 <= c || Isprint(c)) {
+                       dot = char_insert(dot, c);
+               }
+               goto dc1;
+       }
+
+key_cmd_mode:
+       switch (c) {
+               //case 0x01:    // soh
+               //case 0x09:    // ht
+               //case 0x0b:    // vt
+               //case 0x0e:    // so
+               //case 0x0f:    // si
+               //case 0x10:    // dle
+               //case 0x11:    // dc1
+               //case 0x13:    // dc3
+#ifdef CONFIG_FEATURE_VI_CRASHME
+       case 0x14:                      // dc4  ctrl-T
+               crashme = (crashme == 0) ? 1 : 0;
+               break;
+#endif                                                 /* CONFIG_FEATURE_VI_CRASHME */
+               //case 0x16:    // syn
+               //case 0x17:    // etb
+               //case 0x18:    // can
+               //case 0x1c:    // fs
+               //case 0x1d:    // gs
+               //case 0x1e:    // rs
+               //case 0x1f:    // us
+               //case '!':     // !-
+               //case '#':     // #-
+               //case '&':     // &-
+               //case '(':     // (-
+               //case ')':     // )-
+               //case '*':     // *-
+               //case ',':     // ,-
+               //case '=':     // =-
+               //case '@':     // @-
+               //case 'F':     // F-
+               //case 'K':     // K-
+               //case 'Q':     // Q-
+               //case 'S':     // S-
+               //case 'T':     // T-
+               //case 'V':     // V-
+               //case '[':     // [-
+               //case '\\':    // \-
+               //case ']':     // ]-
+               //case '_':     // _-
+               //case '`':     // `-
+               //case 'g':     // g-
+               //case 'u':     // u- FIXME- there is no undo
+               //case 'v':     // v-
+       default:                        // unrecognised command
+               buf[0] = c;
+               buf[1] = '\0';
+               if (c < ' ') {
+                       buf[0] = '^';
+                       buf[1] = c + '@';
+                       buf[2] = '\0';
+               }
+               ni((Byte *) buf);
+               end_cmd_q();    // stop adding to q
+       case 0x00:                      // nul- ignore
+               break;
+       case 2:                 // ctrl-B  scroll up   full screen
+       case VI_K_PAGEUP:       // Cursor Key Page Up
+               dot_scroll(rows - 2, -1);
+               break;
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+       case 0x03:                      // ctrl-C   interrupt
+               longjmp(restart, 1);
+               break;
+       case 26:                        // ctrl-Z suspend
+               suspend_sig(SIGTSTP);
+               break;
+#endif                                                 /* CONFIG_FEATURE_VI_USE_SIGNALS */
+       case 4:                 // ctrl-D  scroll down half screen
+               dot_scroll((rows - 2) / 2, 1);
+               break;
+       case 5:                 // ctrl-E  scroll down one line
+               dot_scroll(1, 1);
+               break;
+       case 6:                 // ctrl-F  scroll down full screen
+       case VI_K_PAGEDOWN:     // Cursor Key Page Down
+               dot_scroll(rows - 2, 1);
+               break;
+       case 7:                 // ctrl-G  show current status
+               edit_status();
+               break;
+       case 'h':                       // h- move left
+       case VI_K_LEFT: // cursor key Left
+       case 8:                 // ctrl-H- move left    (This may be ERASE char)
+       case 127:                       // DEL- move left   (This may be ERASE char)
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_left();
+               break;
+       case 10:                        // Newline ^J
+       case 'j':                       // j- goto next line, same col
+       case VI_K_DOWN: // cursor key Down
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_next();             // go to next B-o-l
+               dot = move_to_col(dot, ccol + offset);  // try stay in same col
+               break;
+       case 12:                        // ctrl-L  force redraw whole screen
+       case 18:                        // ctrl-R  force redraw
+               place_cursor(0, 0, FALSE);      // put cursor in correct place
+               clear_to_eos(); // tel terminal to erase display
+               (void) mysleep(10);
+               screen_erase(); // erase the internal screen buffer
+               refresh(TRUE);  // this will redraw the entire display
+               break;
+       case 13:                        // Carriage Return ^M
+       case '+':                       // +- goto next line
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_next();
+               dot_skip_over_ws();
+               break;
+       case 21:                        // ctrl-U  scroll up   half screen
+               dot_scroll((rows - 2) / 2, -1);
+               break;
+       case 25:                        // ctrl-Y  scroll up one line
+               dot_scroll(1, -1);
+               break;
+       case 27:                        // esc
+               if (cmd_mode == 0)
+                       indicate_error(c);
+               cmd_mode = 0;   // stop insrting
+               end_cmd_q();
+               *status_buffer = '\0';  // clear status buffer
+               break;
+       case ' ':                       // move right
+       case 'l':                       // move right
+       case VI_K_RIGHT:        // Cursor Key Right
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_right();
+               break;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       case '"':                       // "- name a register to use for Delete/Yank
+               c1 = get_one_char();
+               c1 = tolower(c1);
+               if (islower(c1)) {
+                       YDreg = c1 - 'a';
+               } else {
+                       indicate_error(c);
+               }
+               break;
+       case '\'':                      // '- goto a specific mark
+               c1 = get_one_char();
+               c1 = tolower(c1);
+               if (islower(c1)) {
+                       c1 = c1 - 'a';
+                       // get the b-o-l
+                       q = mark[(int) c1];
+                       if (text <= q && q < end) {
+                               dot = q;
+                               dot_begin();    // go to B-o-l
+                               dot_skip_over_ws();
+                       }
+               } else if (c1 == '\'') {        // goto previous context
+                       dot = swap_context(dot);        // swap current and previous context
+                       dot_begin();    // go to B-o-l
+                       dot_skip_over_ws();
+               } else {
+                       indicate_error(c);
+               }
+               break;
+       case 'm':                       // m- Mark a line
+               // this is really stupid.  If there are any inserts or deletes
+               // between text[0] and dot then this mark will not point to the
+               // correct location! It could be off by many lines!
+               // Well..., at least its quick and dirty.
+               c1 = get_one_char();
+               c1 = tolower(c1);
+               if (islower(c1)) {
+                       c1 = c1 - 'a';
+                       // remember the line
+                       mark[(int) c1] = dot;
+               } else {
+                       indicate_error(c);
+               }
+               break;
+       case 'P':                       // P- Put register before
+       case 'p':                       // p- put register after
+               p = reg[YDreg];
+               if (p == 0) {
+                       psbs("Nothing in register %c", what_reg());
+                       break;
+               }
+               // are we putting whole lines or strings
+               if (strchr((char *) p, '\n') != NULL) {
+                       if (c == 'P') {
+                               dot_begin();    // putting lines- Put above
+                       }
+                       if (c == 'p') {
+                               // are we putting after very last line?
+                               if (end_line(dot) == (end - 1)) {
+                                       dot = end;      // force dot to end of text[]
+                               } else {
+                                       dot_next();     // next line, then put before
+                               }
+                       }
+               } else {
+                       if (c == 'p')
+                               dot_right();    // move to right, can move to NL
+               }
+               dot = string_insert(dot, p);    // insert the string
+               end_cmd_q();    // stop adding to q
+               break;
+       case 'U':                       // U- Undo; replace current line with original version
+               if (reg[Ureg] != 0) {
+                       p = begin_line(dot);
+                       q = end_line(dot);
+                       p = text_hole_delete(p, q);     // delete cur line
+                       p = string_insert(p, reg[Ureg]);        // insert orig line
+                       dot = p;
+                       dot_skip_over_ws();
+               }
+               break;
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+       case '$':                       // $- goto end of line
+       case VI_K_END:          // Cursor Key End
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot = end_line(dot);
+               break;
+       case '%':                       // %- find matching char of pair () [] {}
+               for (q = dot; q < end && *q != '\n'; q++) {
+                       if (strchr("()[]{}", *q) != NULL) {
+                               // we found half of a pair
+                               p = find_pair(q, *q);
+                               if (p == NULL) {
+                                       indicate_error(c);
+                               } else {
+                                       dot = p;
+                               }
+                               break;
+                       }
+               }
+               if (*q == '\n')
+                       indicate_error(c);
+               break;
+       case 'f':                       // f- forward to a user specified char
+               last_forward_char = get_one_char();     // get the search char
+               //
+               // dont separate these two commands. 'f' depends on ';'
+               //
+               //**** fall thru to ... 'i'
+       case ';':                       // ;- look at rest of line for last forward char
+               if (cmdcnt-- > 1) {
+                       do_cmd(';');
+               }                               // repeat cnt
+               if (last_forward_char == 0) break;
+               q = dot + 1;
+               while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
+                       q++;
+               }
+               if (*q == last_forward_char)
+                       dot = q;
+               break;
+       case '-':                       // -- goto prev line
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_prev();
+               dot_skip_over_ws();
+               break;
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+       case '.':                       // .- repeat the last modifying command
+               // Stuff the last_modifying_cmd back into stdin
+               // and let it be re-executed.
+               if (last_modifying_cmd != 0) {
+                       ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
+               }
+               break;
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+       case '?':                       // /- search for a pattern
+       case '/':                       // /- search for a pattern
+               buf[0] = c;
+               buf[1] = '\0';
+               q = get_input_line(buf);        // get input line- use "status line"
+               if (strlen((char *) q) == 1)
+                       goto dc3;       // if no pat re-use old pat
+               if (strlen((char *) q) > 1) {   // new pat- save it and find
+                       // there is a new pat
+                       free(last_search_pattern);
+                       last_search_pattern = (Byte *) bb_xstrdup((char *) q);
+                       goto dc3;       // now find the pattern
+               }
+               // user changed mind and erased the "/"-  do nothing
+               break;
+       case 'N':                       // N- backward search for last pattern
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dir = BACK;             // assume BACKWARD search
+               p = dot - 1;
+               if (last_search_pattern[0] == '?') {
+                       dir = FORWARD;
+                       p = dot + 1;
+               }
+               goto dc4;               // now search for pattern
+               break;
+       case 'n':                       // n- repeat search for last pattern
+               // search rest of text[] starting at next char
+               // if search fails return orignal "p" not the "p+1" address
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+         dc3:
+               if (last_search_pattern == 0) {
+                       msg = (Byte *) "No previous regular expression";
+                       goto dc2;
+               }
+               if (last_search_pattern[0] == '/') {
+                       dir = FORWARD;  // assume FORWARD search
+                       p = dot + 1;
+               }
+               if (last_search_pattern[0] == '?') {
+                       dir = BACK;
+                       p = dot - 1;
+               }
+         dc4:
+               q = char_search(p, last_search_pattern + 1, dir, FULL);
+               if (q != NULL) {
+                       dot = q;        // good search, update "dot"
+                       msg = (Byte *) "";
+                       goto dc2;
+               }
+               // no pattern found between "dot" and "end"- continue at top
+               p = text;
+               if (dir == BACK) {
+                       p = end - 1;
+               }
+               q = char_search(p, last_search_pattern + 1, dir, FULL);
+               if (q != NULL) {        // found something
+                       dot = q;        // found new pattern- goto it
+                       msg = (Byte *) "search hit BOTTOM, continuing at TOP";
+                       if (dir == BACK) {
+                               msg = (Byte *) "search hit TOP, continuing at BOTTOM";
+                       }
+               } else {
+                       msg = (Byte *) "Pattern not found";
+               }
+         dc2:
+               psbs("%s", msg);
+               break;
+       case '{':                       // {- move backward paragraph
+               q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
+               if (q != NULL) {        // found blank line
+                       dot = next_line(q);     // move to next blank line
+               }
+               break;
+       case '}':                       // }- move forward paragraph
+               q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
+               if (q != NULL) {        // found blank line
+                       dot = next_line(q);     // move to next blank line
+               }
+               break;
+#endif                                                 /* CONFIG_FEATURE_VI_SEARCH */
+       case '0':                       // 0- goto begining of line
+       case '1':                       // 1-
+       case '2':                       // 2-
+       case '3':                       // 3-
+       case '4':                       // 4-
+       case '5':                       // 5-
+       case '6':                       // 6-
+       case '7':                       // 7-
+       case '8':                       // 8-
+       case '9':                       // 9-
+               if (c == '0' && cmdcnt < 1) {
+                       dot_begin();    // this was a standalone zero
+               } else {
+                       cmdcnt = cmdcnt * 10 + (c - '0');       // this 0 is part of a number
+               }
+               break;
+       case ':':                       // :- the colon mode commands
+               p = get_input_line((Byte *) ":");       // get input line- use "status line"
+#ifdef CONFIG_FEATURE_VI_COLON
+               colon(p);               // execute the command
+#else                                                  /* CONFIG_FEATURE_VI_COLON */
+               if (*p == ':')
+                       p++;                            // move past the ':'
+               cnt = strlen((char *) p);
+               if (cnt <= 0)
+                       break;
+               if (strncasecmp((char *) p, "quit", cnt) == 0 ||
+                       strncasecmp((char *) p, "q!", cnt) == 0) {      // delete lines
+                       if (file_modified && p[1] != '!') {
+                               psbs("No write since last change (:quit! overrides)");
+                       } else {
+                               editing = 0;
+                       }
+               } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
+                                  strncasecmp((char *) p, "wq", cnt) == 0 ||
+                                  strncasecmp((char *) p, "x", cnt) == 0) {
+                       cnt = file_write(cfn, text, end - 1);
+                       file_modified = FALSE;
+                       psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
+                       if (p[0] == 'x' || p[1] == 'q') {
+                               editing = 0;
+                       }
+               } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
+                       edit_status();                  // show current file status
+               } else if (sscanf((char *) p, "%d", &j) > 0) {
+                       dot = find_line(j);             // go to line # j
+                       dot_skip_over_ws();
+               } else {                // unrecognised cmd
+                       ni((Byte *) p);
+               }
+#endif                                                 /* CONFIG_FEATURE_VI_COLON */
+               break;
+       case '<':                       // <- Left  shift something
+       case '>':                       // >- Right shift something
+               cnt = count_lines(text, dot);   // remember what line we are on
+               c1 = get_one_char();    // get the type of thing to delete
+               find_range(&p, &q, c1);
+               (void) yank_delete(p, q, 1, YANKONLY);  // save copy before change
+               p = begin_line(p);
+               q = end_line(q);
+               i = count_lines(p, q);  // # of lines we are shifting
+               for ( ; i > 0; i--, p = next_line(p)) {
+                       if (c == '<') {
+                               // shift left- remove tab or 8 spaces
+                               if (*p == '\t') {
+                                       // shrink buffer 1 char
+                                       (void) text_hole_delete(p, p);
+                               } else if (*p == ' ') {
+                                       // we should be calculating columns, not just SPACE
+                                       for (j = 0; *p == ' ' && j < tabstop; j++) {
+                                               (void) text_hole_delete(p, p);
+                                       }
+                               }
+                       } else if (c == '>') {
+                               // shift right -- add tab or 8 spaces
+                               (void) char_insert(p, '\t');
+                       }
+               }
+               dot = find_line(cnt);   // what line were we on
+               dot_skip_over_ws();
+               end_cmd_q();    // stop adding to q
+               break;
+       case 'A':                       // A- append at e-o-l
+               dot_end();              // go to e-o-l
+               //**** fall thru to ... 'a'
+       case 'a':                       // a- append after current char
+               if (*dot != '\n')
+                       dot++;
+               goto dc_i;
+               break;
+       case 'B':                       // B- back a blank-delimited Word
+       case 'E':                       // E- end of a blank-delimited word
+       case 'W':                       // W- forward a blank-delimited word
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dir = FORWARD;
+               if (c == 'B')
+                       dir = BACK;
+               if (c == 'W' || isspace(dot[dir])) {
+                       dot = skip_thing(dot, 1, dir, S_TO_WS);
+                       dot = skip_thing(dot, 2, dir, S_OVER_WS);
+               }
+               if (c != 'W')
+                       dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
+               break;
+       case 'C':                       // C- Change to e-o-l
+       case 'D':                       // D- delete to e-o-l
+               save_dot = dot;
+               dot = dollar_line(dot); // move to before NL
+               // copy text into a register and delete
+               dot = yank_delete(save_dot, dot, 0, YANKDEL);   // delete to e-o-l
+               if (c == 'C')
+                       goto dc_i;      // start inserting
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+               if (c == 'D')
+                       end_cmd_q();    // stop adding to q
+#endif                                                 /* CONFIG_FEATURE_VI_DOT_CMD */
+               break;
+       case 'G':               // G- goto to a line number (default= E-O-F)
+               dot = end - 1;                          // assume E-O-F
+               if (cmdcnt > 0) {
+                       dot = find_line(cmdcnt);        // what line is #cmdcnt
+               }
+               dot_skip_over_ws();
+               break;
+       case 'H':                       // H- goto top line on screen
+               dot = screenbegin;
+               if (cmdcnt > (rows - 1)) {
+                       cmdcnt = (rows - 1);
+               }
+               if (cmdcnt-- > 1) {
+                       do_cmd('+');
+               }                               // repeat cnt
+               dot_skip_over_ws();
+               break;
+       case 'I':                       // I- insert before first non-blank
+               dot_begin();    // 0
+               dot_skip_over_ws();
+               //**** fall thru to ... 'i'
+       case 'i':                       // i- insert before current char
+       case VI_K_INSERT:       // Cursor Key Insert
+         dc_i:
+               cmd_mode = 1;   // start insrting
+               psb("-- Insert --");
+               break;
+       case 'J':                       // J- join current and next lines together
+               if (cmdcnt-- > 2) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_end();              // move to NL
+               if (dot < end - 1) {    // make sure not last char in text[]
+                       *dot++ = ' ';   // replace NL with space
+                       while (isblnk(*dot)) {  // delete leading WS
+                               dot_delete();
+                       }
+               }
+               end_cmd_q();    // stop adding to q
+               break;
+       case 'L':                       // L- goto bottom line on screen
+               dot = end_screen();
+               if (cmdcnt > (rows - 1)) {
+                       cmdcnt = (rows - 1);
+               }
+               if (cmdcnt-- > 1) {
+                       do_cmd('-');
+               }                               // repeat cnt
+               dot_begin();
+               dot_skip_over_ws();
+               break;
+       case 'M':                       // M- goto middle line on screen
+               dot = screenbegin;
+               for (cnt = 0; cnt < (rows-1) / 2; cnt++)
+                       dot = next_line(dot);
+               break;
+       case 'O':                       // O- open a empty line above
+               //    0i\n ESC -i
+               p = begin_line(dot);
+               if (p[-1] == '\n') {
+                       dot_prev();
+       case 'o':                       // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
+                       dot_end();
+                       dot = char_insert(dot, '\n');
+               } else {
+                       dot_begin();    // 0
+                       dot = char_insert(dot, '\n');   // i\n ESC
+                       dot_prev();     // -
+               }
+               goto dc_i;
+               break;
+       case 'R':                       // R- continuous Replace char
+         dc5:
+               cmd_mode = 2;
+               psb("-- Replace --");
+               break;
+       case 'X':                       // X- delete char before dot
+       case 'x':                       // x- delete the current char
+       case 's':                       // s- substitute the current char
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dir = 0;
+               if (c == 'X')
+                       dir = -1;
+               if (dot[dir] != '\n') {
+                       if (c == 'X')
+                               dot--;  // delete prev char
+                       dot = yank_delete(dot, dot, 0, YANKDEL);        // delete char
+               }
+               if (c == 's')
+                       goto dc_i;      // start insrting
+               end_cmd_q();    // stop adding to q
+               break;
+       case 'Z':                       // Z- if modified, {write}; exit
+               // ZZ means to save file (if necessary), then exit
+               c1 = get_one_char();
+               if (c1 != 'Z') {
+                       indicate_error(c);
+                       break;
+               }
+               if (file_modified
+#ifdef CONFIG_FEATURE_VI_READONLY
+                       && ! vi_readonly
+                       && ! readonly
+#endif                                                 /* CONFIG_FEATURE_VI_READONLY */
+                       ) {
+                       cnt = file_write(cfn, text, end - 1);
+                       if (cnt == (end - 1 - text + 1)) {
+                               editing = 0;
+                       }
+               } else {
+                       editing = 0;
+               }
+               break;
+       case '^':                       // ^- move to first non-blank on line
+               dot_begin();
+               dot_skip_over_ws();
+               break;
+       case 'b':                       // b- back a word
+       case 'e':                       // e- end of word
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dir = FORWARD;
+               if (c == 'b')
+                       dir = BACK;
+               if ((dot + dir) < text || (dot + dir) > end - 1)
+                       break;
+               dot += dir;
+               if (isspace(*dot)) {
+                       dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
+               }
+               if (isalnum(*dot) || *dot == '_') {
+                       dot = skip_thing(dot, 1, dir, S_END_ALNUM);
+               } else if (ispunct(*dot)) {
+                       dot = skip_thing(dot, 1, dir, S_END_PUNCT);
+               }
+               break;
+       case 'c':                       // c- change something
+       case 'd':                       // d- delete something
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       case 'y':                       // y- yank   something
+       case 'Y':                       // Y- Yank a line
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+               yf = YANKDEL;   // assume either "c" or "d"
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+               if (c == 'y' || c == 'Y')
+                       yf = YANKONLY;
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+               c1 = 'y';
+               if (c != 'Y')
+                       c1 = get_one_char();    // get the type of thing to delete
+               find_range(&p, &q, c1);
+               if (c1 == 27) { // ESC- user changed mind and wants out
+                       c = c1 = 27;    // Escape- do nothing
+               } else if (strchr("wW", c1)) {
+                       if (c == 'c') {
+                               // don't include trailing WS as part of word
+                               while (isblnk(*q)) {
+                                       if (q <= text || q[-1] == '\n')
+                                               break;
+                                       q--;
+                               }
+                       }
+                       dot = yank_delete(p, q, 0, yf); // delete word
+               } else if (strchr("^0bBeEft$", c1)) {
+                       // single line copy text into a register and delete
+                       dot = yank_delete(p, q, 0, yf); // delete word
+               } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
+                       // multiple line copy text into a register and delete
+                       dot = yank_delete(p, q, 1, yf); // delete lines
+                       if (c == 'c') {
+                               dot = char_insert(dot, '\n');
+                               // on the last line of file don't move to prev line
+                               if (dot != (end-1)) {
+                                       dot_prev();
+                               }
+                       } else if (c == 'd') {
+                               dot_begin();
+                               dot_skip_over_ws();
+                       }
+               } else {
+                       // could not recognize object
+                       c = c1 = 27;    // error-
+                       indicate_error(c);
+               }
+               if (c1 != 27) {
+                       // if CHANGING, not deleting, start inserting after the delete
+                       if (c == 'c') {
+                               strcpy((char *) buf, "Change");
+                               goto dc_i;      // start inserting
+                       }
+                       if (c == 'd') {
+                               strcpy((char *) buf, "Delete");
+                       }
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+                       if (c == 'y' || c == 'Y') {
+                               strcpy((char *) buf, "Yank");
+                       }
+                       p = reg[YDreg];
+                       q = p + strlen((char *) p);
+                       for (cnt = 0; p <= q; p++) {
+                               if (*p == '\n')
+                                       cnt++;
+                       }
+                       psb("%s %d lines (%d chars) using [%c]",
+                               buf, cnt, strlen((char *) reg[YDreg]), what_reg());
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+                       end_cmd_q();    // stop adding to q
+               }
+               break;
+       case 'k':                       // k- goto prev line, same col
+       case VI_K_UP:           // cursor key Up
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               dot_prev();
+               dot = move_to_col(dot, ccol + offset);  // try stay in same col
+               break;
+       case 'r':                       // r- replace the current char with user input
+               c1 = get_one_char();    // get the replacement char
+               if (*dot != '\n') {
+                       *dot = c1;
+                       file_modified = TRUE;   // has the file been modified
+               }
+               end_cmd_q();    // stop adding to q
+               break;
+       case 't':                       // t- move to char prior to next x
+                last_forward_char = get_one_char();
+                do_cmd(';');
+                if (*dot == last_forward_char)
+                        dot_left();
+                last_forward_char= 0;
+               break;
+       case 'w':                       // w- forward a word
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               if (isalnum(*dot) || *dot == '_') {     // we are on ALNUM
+                       dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
+               } else if (ispunct(*dot)) {     // we are on PUNCT
+                       dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
+               }
+               if (dot < end - 1)
+                       dot++;          // move over word
+               if (isspace(*dot)) {
+                       dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
+               }
+               break;
+       case 'z':                       // z-
+               c1 = get_one_char();    // get the replacement char
+               cnt = 0;
+               if (c1 == '.')
+                       cnt = (rows - 2) / 2;   // put dot at center
+               if (c1 == '-')
+                       cnt = rows - 2; // put dot at bottom
+               screenbegin = begin_line(dot);  // start dot at top
+               dot_scroll(cnt, -1);
+               break;
+       case '|':                       // |- move to column "cmdcnt"
+               dot = move_to_col(dot, cmdcnt - 1);     // try to move to column
+               break;
+       case '~':                       // ~- flip the case of letters   a-z -> A-Z
+               if (cmdcnt-- > 1) {
+                       do_cmd(c);
+               }                               // repeat cnt
+               if (islower(*dot)) {
+                       *dot = toupper(*dot);
+                       file_modified = TRUE;   // has the file been modified
+               } else if (isupper(*dot)) {
+                       *dot = tolower(*dot);
+                       file_modified = TRUE;   // has the file been modified
+               }
+               dot_right();
+               end_cmd_q();    // stop adding to q
+               break;
+               //----- The Cursor and Function Keys -----------------------------
+       case VI_K_HOME: // Cursor Key Home
+               dot_begin();
+               break;
+               // The Fn keys could point to do_macro which could translate them
+       case VI_K_FUN1: // Function Key F1
+       case VI_K_FUN2: // Function Key F2
+       case VI_K_FUN3: // Function Key F3
+       case VI_K_FUN4: // Function Key F4
+       case VI_K_FUN5: // Function Key F5
+       case VI_K_FUN6: // Function Key F6
+       case VI_K_FUN7: // Function Key F7
+       case VI_K_FUN8: // Function Key F8
+       case VI_K_FUN9: // Function Key F9
+       case VI_K_FUN10:        // Function Key F10
+       case VI_K_FUN11:        // Function Key F11
+       case VI_K_FUN12:        // Function Key F12
+               break;
+       }
+
+  dc1:
+       // if text[] just became empty, add back an empty line
+       if (end == text) {
+               (void) char_insert(text, '\n'); // start empty buf with dummy line
+               dot = text;
+       }
+       // it is OK for dot to exactly equal to end, otherwise check dot validity
+       if (dot != end) {
+               dot = bound_dot(dot);   // make sure "dot" is valid
+       }
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+       check_context(c);       // update the current context
+#endif                                                 /* CONFIG_FEATURE_VI_YANKMARK */
+
+       if (!isdigit(c))
+               cmdcnt = 0;             // cmd was not a number, reset cmdcnt
+       cnt = dot - begin_line(dot);
+       // Try to stay off of the Newline
+       if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
+               dot--;
+}
+
+#ifdef CONFIG_FEATURE_VI_CRASHME
+static int totalcmds = 0;
+static int Mp = 85;             // Movement command Probability
+static int Np = 90;             // Non-movement command Probability
+static int Dp = 96;             // Delete command Probability
+static int Ip = 97;             // Insert command Probability
+static int Yp = 98;             // Yank command Probability
+static int Pp = 99;             // Put command Probability
+static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
+char chars[20] = "\t012345 abcdABCD-=.$";
+char *words[20] = { "this", "is", "a", "test",
+       "broadcast", "the", "emergency", "of",
+       "system", "quick", "brown", "fox",
+       "jumped", "over", "lazy", "dogs",
+       "back", "January", "Febuary", "March"
+};
+char *lines[20] = {
+       "You should have received a copy of the GNU General Public License\n",
+       "char c, cm, *cmd, *cmd1;\n",
+       "generate a command by percentages\n",
+       "Numbers may be typed as a prefix to some commands.\n",
+       "Quit, discarding changes!\n",
+       "Forced write, if permission originally not valid.\n",
+       "In general, any ex or ed command (such as substitute or delete).\n",
+       "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
+       "Please get w/ me and I will go over it with you.\n",
+       "The following is a list of scheduled, committed changes.\n",
+       "1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
+       "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
+       "Any question about transactions please contact Sterling Huxley.\n",
+       "I will try to get back to you by Friday, December 31.\n",
+       "This Change will be implemented on Friday.\n",
+       "Let me know if you have problems accessing this;\n",
+       "Sterling Huxley recently added you to the access list.\n",
+       "Would you like to go to lunch?\n",
+       "The last command will be automatically run.\n",
+       "This is too much english for a computer geek.\n",
+};
+char *multilines[20] = {
+       "You should have received a copy of the GNU General Public License\n",
+       "char c, cm, *cmd, *cmd1;\n",
+       "generate a command by percentages\n",
+       "Numbers may be typed as a prefix to some commands.\n",
+       "Quit, discarding changes!\n",
+       "Forced write, if permission originally not valid.\n",
+       "In general, any ex or ed command (such as substitute or delete).\n",
+       "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
+       "Please get w/ me and I will go over it with you.\n",
+       "The following is a list of scheduled, committed changes.\n",
+       "1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
+       "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
+       "Any question about transactions please contact Sterling Huxley.\n",
+       "I will try to get back to you by Friday, December 31.\n",
+       "This Change will be implemented on Friday.\n",
+       "Let me know if you have problems accessing this;\n",
+       "Sterling Huxley recently added you to the access list.\n",
+       "Would you like to go to lunch?\n",
+       "The last command will be automatically run.\n",
+       "This is too much english for a computer geek.\n",
+};
+
+// create a random command to execute
+static void crash_dummy()
+{
+       static int sleeptime;   // how long to pause between commands
+       char c, cm, *cmd, *cmd1;
+       int i, cnt, thing, rbi, startrbi, percent;
+
+       // "dot" movement commands
+       cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
+
+       // is there already a command running?
+       if (readed_for_parse > 0)
+               goto cd1;
+  cd0:
+       startrbi = rbi = 0;
+       sleeptime = 0;          // how long to pause between commands
+       memset(readbuffer, '\0', BUFSIZ);   // clear the read buffer
+       // generate a command by percentages
+       percent = (int) lrand48() % 100;        // get a number from 0-99
+       if (percent < Mp) {     //  Movement commands
+               // available commands
+               cmd = cmd1;
+               M++;
+       } else if (percent < Np) {      //  non-movement commands
+               cmd = "mz<>\'\"";       // available commands
+               N++;
+       } else if (percent < Dp) {      //  Delete commands
+               cmd = "dx";             // available commands
+               D++;
+       } else if (percent < Ip) {      //  Inset commands
+               cmd = "iIaAsrJ";        // available commands
+               I++;
+       } else if (percent < Yp) {      //  Yank commands
+               cmd = "yY";             // available commands
+               Y++;
+       } else if (percent < Pp) {      //  Put commands
+               cmd = "pP";             // available commands
+               P++;
+       } else {
+               // We do not know how to handle this command, try again
+               U++;
+               goto cd0;
+       }
+       // randomly pick one of the available cmds from "cmd[]"
+       i = (int) lrand48() % strlen(cmd);
+       cm = cmd[i];
+       if (strchr(":\024", cm))
+               goto cd0;               // dont allow colon or ctrl-T commands
+       readbuffer[rbi++] = cm; // put cmd into input buffer
+
+       // now we have the command-
+       // there are 1, 2, and multi char commands
+       // find out which and generate the rest of command as necessary
+       if (strchr("dmryz<>\'\"", cm)) {        // 2-char commands
+               cmd1 = " \n\r0$^-+wWeEbBhjklHL";
+               if (cm == 'm' || cm == '\'' || cm == '\"') {    // pick a reg[]
+                       cmd1 = "abcdefghijklmnopqrstuvwxyz";
+               }
+               thing = (int) lrand48() % strlen(cmd1); // pick a movement command
+               c = cmd1[thing];
+               readbuffer[rbi++] = c;  // add movement to input buffer
+       }
+       if (strchr("iIaAsc", cm)) {     // multi-char commands
+               if (cm == 'c') {
+                       // change some thing
+                       thing = (int) lrand48() % strlen(cmd1); // pick a movement command
+                       c = cmd1[thing];
+                       readbuffer[rbi++] = c;  // add movement to input buffer
+               }
+               thing = (int) lrand48() % 4;    // what thing to insert
+               cnt = (int) lrand48() % 10;     // how many to insert
+               for (i = 0; i < cnt; i++) {
+                       if (thing == 0) {       // insert chars
+                               readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
+                       } else if (thing == 1) {        // insert words
+                               strcat((char *) readbuffer, words[(int) lrand48() % 20]);
+                               strcat((char *) readbuffer, " ");
+                               sleeptime = 0;  // how fast to type
+                       } else if (thing == 2) {        // insert lines
+                               strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
+                               sleeptime = 0;  // how fast to type
+                       } else {        // insert multi-lines
+                               strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
+                               sleeptime = 0;  // how fast to type
+                       }
+               }
+               strcat((char *) readbuffer, "\033");
+       }
+       readed_for_parse = strlen(readbuffer);
+  cd1:
+       totalcmds++;
+       if (sleeptime > 0)
+               (void) mysleep(sleeptime);      // sleep 1/100 sec
+}
+
+// test to see if there are any errors
+static void crash_test()
+{
+       static time_t oldtim;
+       time_t tim;
+       char d[2], msg[BUFSIZ];
+
+       msg[0] = '\0';
+       if (end < text) {
+               strcat((char *) msg, "end<text ");
+       }
+       if (end > textend) {
+               strcat((char *) msg, "end>textend ");
+       }
+       if (dot < text) {
+               strcat((char *) msg, "dot<text ");
+       }
+       if (dot > end) {
+               strcat((char *) msg, "dot>end ");
+       }
+       if (screenbegin < text) {
+               strcat((char *) msg, "screenbegin<text ");
+       }
+       if (screenbegin > end - 1) {
+               strcat((char *) msg, "screenbegin>end-1 ");
+       }
+
+       if (strlen(msg) > 0) {
+               alarm(0);
+               printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
+                       totalcmds, last_input_char, msg, SOs, SOn);
+               fflush(stdout);
+               while (read(0, d, 1) > 0) {
+                       if (d[0] == '\n' || d[0] == '\r')
+                               break;
+               }
+               alarm(3);
+       }
+       tim = (time_t) time((time_t *) 0);
+       if (tim >= (oldtim + 3)) {
+               sprintf((char *) status_buffer,
+                               "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
+                               totalcmds, M, N, I, D, Y, P, U, end - text + 1);
+               oldtim = tim;
+       }
+       return;
+}
+#endif                                                  /* CONFIG_FEATURE_VI_CRASHME */
diff --git a/busybox/examples/bootfloppy/bootfloppy.txt b/busybox/examples/bootfloppy/bootfloppy.txt
new file mode 100644 (file)
index 0000000..16f2c13
--- /dev/null
@@ -0,0 +1,180 @@
+Building a Busybox Boot Floppy
+==============================
+
+This document describes how to buid a boot floppy using the following
+components:
+
+ - Linux Kernel (http://www.kernel.org)
+ - uClibc: C library (http://cvs.uclinux.org/uClibc.html)
+ - Busybox: Unix utilities (http://busybox.net/)
+ - Syslinux: bootloader (http://syslinux.zytor.com)
+
+It is based heavily on a paper presented by Erik Andersen at the 2001 Embedded
+Systems Conference.
+
+
+
+Building The Software Components
+--------------------------------
+
+Detailed instructions on how to build Busybox, uClibc, or a working Linux
+kernel are beyond the scope of this document. The following guidelines will
+help though:
+
+       - Stock Busybox from CVS or a tarball will work with no modifications to
+         any files. Just extract and go.
+       - Ditto uClibc.
+       - Your Linux kernel must include support for initrd or else the floppy
+         won't be able to mount it's root file system.
+
+If you require further information on building Busybox uClibc or Linux, please
+refer to the web pages and documentation for those individual programs.
+
+
+
+Making a Root File System
+-------------------------
+
+The following steps will create a root file system.
+
+ - Create an empty file that you can format as a filesystem:
+
+       dd if=/dev/zero of=rootfs bs=1k count=4000
+
+ - Set up the rootfs file we just created to be used as a loop device (may not
+   be necessary)
+
+       losetup /dev/loop0 rootfs
+
+ - Format the rootfs file with a filesystem:
+
+       mkfs.ext2 -F -i 2000 rootfs
+
+ - Mount the file on a mountpoint so we can place files in it:
+
+       mkdir loop
+       mount -o loop rootfs loop/
+
+       (you will probably need to be root to do this)
+
+ - Copy on the C library, the dynamic linking library, and other necessary
+   libraries. For this example, we copy the following files from the uClibc
+   tree:
+
+       mkdir loop/lib
+       (chdir to uClibc directory)
+       cp -a libc.so* uClibc*.so \
+               ld.so-1/d-link/ld-linux-uclibc.so* \
+               ld.so-1/libdl/libdl.so* \
+               crypt/libcrypt.so* \
+               (path to)loop/lib
+
+ - Install the Busybox binary and accompanying symlinks:
+
+       (chdir to busybox directory)
+       make PREFIX=(path to)loop/ install
+
+ - Make device files in /dev:
+
+       This can be done by running the 'mkdevs.sh' script. If you want the gory
+       details, you can read the script.
+
+ - Make necessary files in /etc:
+
+       For this, just cp -a the etc/ directory onto rootfs. Again, if you want
+       all the details, you can just look at the files in the dir.
+
+ - Unmount the rootfs from the mountpoint:
+
+       umount loop
+
+ - Compress it:
+
+       gzip -9 rootfs
+
+
+Making a SYSLINUX boot floppy
+-----------------------------
+
+The following steps will create the boot floppy.
+
+Note: You will need to have the mtools package installed beforehand.
+
+ - Insert a floppy in the drive and format it with an MSDOS filesystem:
+
+       mformat a:
+
+       (if the system doesn't know what device 'a:' is, look at /etc/mtools.conf)
+
+ - Run syslinux on the floppy:
+
+       syslinux -s /dev/fd0
+
+       (the -s stands for "safe, slow, and stupid" and should work better with
+       buggy BIOSes; it can be omitted)
+
+ - Put on a syslinux.cfg file:
+
+       mcopy syslinux.cfg a:
+
+       (more on syslinux.cfg below)
+
+ - Copy the root file system you made onto the MSDOS formatted floppy
+
+       mcopy rootfs.gz a:
+
+ - Build a linux kernel and copy it onto the disk with the filename 'linux'
+
+       mcopy bzImage a:linux
+
+
+Sample syslinux.cfg
+~~~~~~~~~~~~~~~~~~~
+
+The following simple syslinux.cfg file should work. You can tweak it if you
+like.
+
+----begin-syslinux.cfg---------------
+DEFAULT linux
+APPEND initrd=rootfs.gz root=/dev/ram0
+TIMEOUT 10
+PROMPT 1
+----end-syslinux.cfg---------------
+
+Some changes you could make to syslinux.cfg:
+
+ - This value is the number seconds it will wait before booting. You can set
+   the timeout to 0 (or omit) to boot instantly, or you can set it as high as
+   10 to wait awhile.
+
+ - PROMPT can be set to 0 to disable the 'boot:' prompt.
+
+ - you can add this line to display the contents of a file as a welcome
+   message:
+
+       DISPLAY display.txt
+
+
+
+Additional Resources
+--------------------
+
+Other useful information on making a Linux bootfloppy is available at the
+following URLs:
+
+http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/index.html
+http://www.linux-embedded.com/howto/Embedded-Linux-Howto.html
+http://linux-embedded.org/howto/LFS-HOWTO.html
+http://linux-embedded.org/pmhowto.html
+http://recycle.lbl.gov/~ldoolitt/embedded/ (Larry Doolittle's stuff)
+
+
+
+Possible TODOs
+--------------
+
+The following features that we might want to add later:
+
+ - support for additional filesystems besides ext2, i.e. minix
+ - different libc, static vs dynamic loading
+ - maybe using an alternate bootloader
diff --git a/busybox/examples/bootfloppy/display.txt b/busybox/examples/bootfloppy/display.txt
new file mode 100644 (file)
index 0000000..399d326
--- /dev/null
@@ -0,0 +1,4 @@
+
+This boot floppy is made with Busybox, uClibc, and the Linux kernel.
+Hit RETURN to boot or enter boot parameters at the prompt below.
+
diff --git a/busybox/examples/bootfloppy/etc/fstab b/busybox/examples/bootfloppy/etc/fstab
new file mode 100644 (file)
index 0000000..ef14ca2
--- /dev/null
@@ -0,0 +1,2 @@
+proc           /proc   proc    defaults    0   0
+
diff --git a/busybox/examples/bootfloppy/etc/init.d/rcS b/busybox/examples/bootfloppy/etc/init.d/rcS
new file mode 100755 (executable)
index 0000000..4f29b92
--- /dev/null
@@ -0,0 +1,3 @@
+#! /bin/sh
+
+/bin/mount -a
diff --git a/busybox/examples/bootfloppy/etc/inittab b/busybox/examples/bootfloppy/etc/inittab
new file mode 100644 (file)
index 0000000..eb3e979
--- /dev/null
@@ -0,0 +1,5 @@
+::sysinit:/etc/init.d/rcS
+::respawn:-/bin/sh
+tty2::askfirst:-/bin/sh
+::ctrlaltdel:/bin/umount -a -r
+
diff --git a/busybox/examples/bootfloppy/etc/profile b/busybox/examples/bootfloppy/etc/profile
new file mode 100644 (file)
index 0000000..8a7c77d
--- /dev/null
@@ -0,0 +1,8 @@
+# /etc/profile: system-wide .profile file for the Bourne shells
+
+echo
+echo -n "Processing /etc/profile... "
+# no-op
+echo "Done"
+echo
+
diff --git a/busybox/examples/bootfloppy/mkdevs.sh b/busybox/examples/bootfloppy/mkdevs.sh
new file mode 100755 (executable)
index 0000000..03a1a85
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# makedev.sh - creates device files for a busybox boot floppy image
+
+
+# we do our work in the dev/ directory
+if [ -z "$1" ]; then
+       echo "usage: `basename $0` path/to/dev/dir"
+       exit 1
+fi
+
+cd $1
+
+
+# miscellaneous one-of-a-kind stuff
+mknod console c 5 1
+mknod full c 1 7
+mknod kmem c 1 2
+mknod mem c 1 1
+mknod null c 1 3
+mknod port c 1 4
+mknod random c 1 8
+mknod urandom c 1 9
+mknod zero c 1 5
+ln -s /proc/kcore core
+
+# IDE HD devs
+# note: not going to bother creating all concievable partitions; you can do
+# that yourself as you need 'em.
+mknod hda b 3 0
+mknod hdb b 3 64
+mknod hdc b 22 0
+mknod hdd b 22 64
+
+# loop devs
+for i in `seq 0 7`; do
+       mknod loop$i b 7 $i
+done
+
+# ram devs
+for i in `seq 0 9`; do
+       mknod ram$i b 1 $i
+done
+ln -s ram1 ram
+
+# ttys
+mknod tty c 5 0
+for i in `seq 0 9`; do
+       mknod tty$i c 4 $i
+done
+
+# virtual console screen devs
+for i in `seq 0 9`; do
+       mknod vcs$i b 7 $i
+done
+ln -s vcs0 vcs
+
+# virtual console screen w/ attributes devs
+for i in `seq 0 9`; do
+       mknod vcsa$i b 7 $i
+done
+ln -s vcsa0 vcsa
diff --git a/busybox/examples/bootfloppy/mkrootfs.sh b/busybox/examples/bootfloppy/mkrootfs.sh
new file mode 100755 (executable)
index 0000000..e79ed41
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/bash
+#
+# mkrootfs.sh - creates a root file system
+#
+
+# TODO: need to add checks here to verify that busybox, uClibc and bzImage
+# exist
+
+
+# command-line settable variables
+BUSYBOX_DIR=..
+UCLIBC_DIR=../../uClibc
+TARGET_DIR=./loop
+FSSIZE=4000
+CLEANUP=1
+MKFS='mkfs.ext2 -F'
+
+# don't-touch variables
+BASE_DIR=`pwd`
+
+
+while getopts 'b:u:s:t:Cm' opt
+do
+       case $opt in
+               b) BUSYBOX_DIR=$OPTARG ;;
+               u) UCLIBC_DIR=$OPTARG ;;
+               t) TARGET_DIR=$OPTARG ;;
+               s) FSSIZE=$OPTARG ;;
+               C) CLEANUP=0 ;;
+               m) MKFS='mkfs.minix' ;;
+               *)
+                       echo "usage: `basename $0` [-bu]"
+                       echo "  -b DIR  path to busybox direcory (default ..)"
+                       echo "  -u DIR  path to uClibc direcory (default ../../uClibc)"
+                       echo "  -t DIR  path to target direcory (default ./loop)"
+                       echo "  -s SIZE size of root filesystem in Kbytes (default 4000)"
+                       echo "  -C      don't perform cleanup (umount target dir, gzip rootfs, etc.)"
+                       echo "          (this allows you to 'chroot loop/ /bin/sh' to test it)"
+                       echo "  -m      use minix filesystem (default is ext2)"
+                       exit 1
+                       ;;
+       esac
+done
+
+
+
+
+# clean up from any previous work
+mount | grep -q loop
+[ $? -eq 0 ] && umount $TARGET_DIR
+[ -d $TARGET_DIR ] && rm -rf $TARGET_DIR/
+[ -f rootfs ] && rm -f rootfs
+[ -f rootfs.gz ] && rm -f rootfs.gz
+
+
+# prepare root file system and mount as loopback
+dd if=/dev/zero of=rootfs bs=1k count=$FSSIZE
+$MKFS -i 2000 rootfs
+mkdir $TARGET_DIR
+mount -o loop,exec rootfs $TARGET_DIR # must be root
+
+
+# install uClibc
+mkdir -p $TARGET_DIR/lib
+cd $UCLIBC_DIR
+make INSTALL_DIR=
+cp -a libc.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a uClibc*.so $BASE_DIR/$TARGET_DIR/lib
+cp -a ld.so-1/d-link/ld-linux-uclibc.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a ld.so-1/libdl/libdl.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a crypt/libcrypt.so* $BASE_DIR/$TARGET_DIR/lib
+cd $BASE_DIR
+
+
+# install busybox and components
+cd $BUSYBOX_DIR
+make distclean
+make CC=$BASE_DIR/$UCLIBC_DIR/extra/gcc-uClibc/i386-uclibc-gcc
+make PREFIX=$BASE_DIR/$TARGET_DIR install
+cd $BASE_DIR
+
+
+# make files in /dev
+mkdir $TARGET_DIR/dev
+./mkdevs.sh $TARGET_DIR/dev
+
+
+# make files in /etc
+cp -a etc $TARGET_DIR
+ln -s /proc/mounts $TARGET_DIR/etc/mtab
+
+
+# other miscellaneous setup
+mkdir $TARGET_DIR/initrd
+mkdir $TARGET_DIR/proc
+
+
+# Done. Maybe do cleanup.
+if [ $CLEANUP -eq 1 ]
+then
+       umount $TARGET_DIR
+       rmdir $TARGET_DIR
+       gzip -9 rootfs
+fi
+
diff --git a/busybox/examples/bootfloppy/mksyslinux.sh b/busybox/examples/bootfloppy/mksyslinux.sh
new file mode 100755 (executable)
index 0000000..e254173
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Formats a floppy to use Syslinux
+
+dummy=""
+
+
+# need to have mtools installed
+if [ -z `which mformat` -o -z `which mcopy` ]; then
+       echo "You must have the mtools package installed to run this script"
+       exit 1
+fi
+
+
+# need an arg for the location of the kernel
+if [ -z "$1" ]; then
+       echo "usage: `basename $0` path/to/linux/kernel"
+       exit 1
+fi
+
+
+# need to have a root file system built
+if [ ! -f rootfs.gz ]; then
+       echo "You need to have a rootfs built first."
+       echo "Hit RETURN to make one now or Control-C to quit."
+       read dummy
+       ./mkrootfs.sh
+fi
+
+
+# prepare the floppy
+echo "Please insert a blank floppy in the drive and press RETURN to format"
+echo "(WARNING: All data will be erased! Hit Control-C to abort)"
+read dummy
+
+echo "Formatting the floppy..."
+mformat a:
+echo "Making it bootable with Syslinux..."
+syslinux -s /dev/fd0
+echo "Copying Syslinux configuration files..."
+mcopy syslinux.cfg display.txt a:
+echo "Copying root filesystem file..."
+mcopy rootfs.gz a:
+# XXX: maybe check for "no space on device" errors here
+echo "Copying linux kernel..."
+mcopy $1 a:linux
+# XXX: maybe check for "no space on device" errors here too
+echo "Finished: boot floppy created"
diff --git a/busybox/examples/bootfloppy/quickstart.txt b/busybox/examples/bootfloppy/quickstart.txt
new file mode 100644 (file)
index 0000000..0d80908
--- /dev/null
@@ -0,0 +1,15 @@
+Quickstart on making the Busybox boot-floppy:
+
+  1) Download Busybox and uClibc from CVS or tarballs. Make sure they share a
+     common parent directory. (i.e. busybox/ and uclibc/ are both right off of
+        /tmp, or wherever.)
+
+  2) Build a Linux kernel. Make sure you include support for initrd.
+
+  3) Put a floppy in the drive. Make sure it is a floppy you don't care about
+     because the contents will be overwritten.
+
+  4) As root, type ./mksyslinux.sh path/to/linux/kernel from this directory.
+     Wait patiently while the magic happens.
+
+  5) Boot up on the floppy.
diff --git a/busybox/examples/bootfloppy/syslinux.cfg b/busybox/examples/bootfloppy/syslinux.cfg
new file mode 100644 (file)
index 0000000..fa2677c
--- /dev/null
@@ -0,0 +1,7 @@
+display display.txt
+default linux
+timeout 10
+prompt 1
+label linux
+       kernel linux
+       append initrd=rootfs.gz root=/dev/ram0
diff --git a/busybox/examples/busybox.spec b/busybox/examples/busybox.spec
new file mode 100644 (file)
index 0000000..3986436
--- /dev/null
@@ -0,0 +1,44 @@
+%define name   busybox
+%define epoch   0
+%define version        0.61.pre
+%define release        %(date -I | sed -e 's/-/_/g')
+%define serial  1
+
+Name:   %{name}
+#Epoch:   %{epoch}
+Version: %{version}
+Release: %{release}
+Serial:         %{serial}
+Copyright: GPL
+Group: System/Utilities
+Summary: BusyBox is a tiny suite of Unix utilities in a multi-call binary.
+URL:    http://busybox.net/
+Source:         ftp://busybox.net/busybox/%{name}-%{version}.tar.gz
+Buildroot: /var/tmp/%{name}-%{version}
+Packager : Erik Andersen <andersen@codepoet.org>
+
+%Description
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides minimalist replacements for most of the utilities
+you usually find in fileutils, shellutils, findutils, textutils, grep, gzip,
+tar, etc.  BusyBox provides a fairly complete POSIX environment for any small
+or emdedded system.  The utilities in BusyBox generally have fewer options then
+their full featured GNU cousins; however, the options that are provided behave
+very much like their GNU counterparts.
+
+%Prep
+%setup -q -n %{name}-%{version}
+
+%Build
+make
+
+%Install
+rm -rf $RPM_BUILD_ROOT
+make PREFIX=$RPM_BUILD_ROOT install
+
+%Clean
+rm -rf $RPM_BUILD_ROOT
+
+%Files
+%defattr(-,root,root)
+/
diff --git a/busybox/examples/depmod.pl b/busybox/examples/depmod.pl
new file mode 100755 (executable)
index 0000000..9af1922
--- /dev/null
@@ -0,0 +1,237 @@
+#!/usr/bin/perl -w
+# vi: set ts=4:
+# Copyright (c) 2001 David Schleef <ds@schleef.org>
+# Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
+# Copyright (c) 2001 Stuart Hughes <stuarth@lineo.com>
+# Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as Perl itself.
+
+# TODO -- use strict mode...
+#use strict;
+
+use Getopt::Long;
+use File::Find;
+
+
+# Set up some default values
+
+my $basedir="";
+my $kernel;
+my $kernelsyms;
+my $stdout=0;
+my $verbose=0;
+
+
+# get command-line options
+
+my %opt;
+
+GetOptions(
+       \%opt,
+       "help|h",
+       "basedir|b=s" => \$basedir,
+       "kernel|k=s" => \$kernel,
+       "kernelsyms|F=s" => \$kernelsyms,
+       "stdout|n" => \$stdout,
+       "verbose|v" => \$verbose,
+);
+
+if (defined $opt{help}) {
+       print
+       "  $0 [OPTION]... [basedir]\n",
+       "     -h --help\t\tShow this help screen\n",
+       "     -b --basedir\tModules base directory (defaults to /lib/modules)\n",
+       "     -k --kernel\tKernel binary for the target\n",
+       "     -F --kernelsyms\tKernel symbol file\n",
+       "     -n --stdout\tWrite to stdout instead of <basedir>/modules.dep\n",
+       "     -v --verbose\tPrint out lots of debugging stuff\n",
+       ;
+       exit 1;
+}
+
+if($basedir !~ m-/lib/modules-) {
+    warn "WARNING: base directory does not match ..../lib/modules\n";
+}
+
+# Find the list of .o files living under $basedir
+#if ($verbose) { printf "Locating all modules\n"; }
+my($ofile) = "";
+my($file) = "";
+my(@liblist) = ();
+find sub {
+       if ( -f $_  && ! -d $_ ) {
+               $file = $File::Find::name;
+               if ( $file =~ /.o$/ ) {
+                       push(@liblist, $file);
+                       if ($verbose) { printf "$file\n"; }
+               }
+       }
+}, $basedir;
+if ($verbose) { printf "Finished locating modules\n"; }
+
+foreach $obj ( @liblist, $kernel ){
+    # turn the input file name into a target tag name
+    # vmlinux is a special that is only used to resolve symbols
+    if($obj =~ /vmlinux/) {
+        $tgtname = "vmlinux";
+    } else {
+        ($tgtname) = $obj =~ m-(/lib/modules/.*)$-;
+    }
+
+    warn "MODULE = $tgtname\n" if $verbose;
+
+    # get a list of symbols
+       @output=`nm $obj`;
+       $ksymtab=grep m/ __ksymtab/, @output;
+
+    # gather the exported symbols
+       if($ksymtab){
+        # explicitly exported
+        foreach ( @output ) {
+            / __ksymtab_(.*)$/ and do {
+                warn "sym = $1\n" if $verbose;
+                $exp->{$1} = $tgtname;
+            };
+        }
+       } else {
+        # exporting all symbols
+        foreach ( @output) {
+            / [ABCDGRST] (.*)$/ and do {
+                warn "syma = $1\n" if $verbose;
+                $exp->{$1} = $tgtname;
+            };
+        }
+       }
+    # gather the unresolved symbols
+    foreach ( @output ) {
+        !/ __this_module/ && / U (.*)$/ and do {
+            warn "und = $1\n" if $verbose;
+            push @{$dep->{$tgtname}}, $1;
+        };
+    }
+}
+
+
+# reduce dependancies: remove unresolvable and resolved from vmlinux
+# remove duplicates
+foreach $module (keys %$dep) {
+    $mod->{$module} = {};
+    foreach (@{$dep->{$module}}) {
+        if( $exp->{$_} ) {
+            warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose;
+            next if $exp->{$_} =~ /vmlinux/;
+            $mod->{$module}{$exp->{$_}} = 1;
+        } else {
+            warn "unresolved symbol $_ in file $module\n";
+        }
+    }
+}
+
+# resolve the dependancies for each module
+if ($stdout == 1) {
+       foreach $module ( keys %$mod ) {
+               print "$module:\t";
+               @sorted = sort bydep keys %{$mod->{$module}};
+               print join(" \\\n\t",@sorted);
+               print "\n\n";
+       }
+} else {
+       open(OFILE, ">$basedir/modules.dep");
+       foreach $module ( keys %$mod ) {
+               print OFILE "$module:\t";
+               @sorted = sort bydep keys %{$mod->{$module}};
+               print OFILE join(" \\\n\t",@sorted);
+               print OFILE "\n\n";
+       }
+}
+
+
+sub bydep
+{
+    foreach my $f ( keys %{$mod->{$b}} ) {
+        if($f eq $a) {
+            return 1;
+        }
+    }
+    return -1;
+}
+
+
+
+__END__
+
+=head1 NAME
+
+depmod.pl - a cross platform script to generate kernel module dependency
+               lists which can then be used by modprobe.
+
+=head1 SYNOPSIS
+
+depmod.pl [OPTION]... [basedir]...
+
+Example:
+
+       depmod.pl -F linux/System.map target/lib/modules
+
+=head1 DESCRIPTION
+
+The purpose of this script is to automagically generate a list of of kernel
+module dependancies.  This script produces dependancy lists that should be
+identical to the depmod program from the modutils package.  Unlike the depmod
+binary, however, depmod.pl is designed to be run on your host system, not
+on your target system.
+
+This script was written by David Schleef <ds@schleef.org> to be used in
+conjunction with the BusyBox modprobe applet.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-h --help>
+
+This displays the help message.
+
+=item B<-b --basedir>
+
+The base directory uner which the target's modules will be found.  This
+defaults to the /lib/modules directory.
+
+=item B<-k --kernel>
+
+Kernel binary for the target.  You must either supply a kernel binary
+or a kernel symbol file (using the -F option).
+
+=item B<-F --kernelsyms>
+
+Kernel symbol file for the target.  You must supply either a kernel symbol file
+kernel binary for the target (using the -k option).
+
+=item B<-n --stdout>
+
+Write to stdout instead of modules.dep.  This is currently hard coded...
+kernel binary for the target (using the -k option).
+
+=item B<--verbose>
+
+Be verbose (not implemented)
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001 David Schleef <ds@schleef.org>
+Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
+Copyright (c) 2001 Stuart Hughes <stuarth@lineo.com>
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+David Schleef <ds@schleef.org>
+
+=cut
+
+# $Id: depmod.pl,v 1.4 2004/03/15 08:28:33 andersen Exp $
+
diff --git a/busybox/examples/devfsd.conf b/busybox/examples/devfsd.conf
new file mode 100644 (file)
index 0000000..e90e710
--- /dev/null
@@ -0,0 +1,133 @@
+# Sample /etc/devfsd.conf configuration file.
+# Richard Gooch  <rgooch@atnf.csiro.au>                17-FEB-2002
+#
+# adapted for busybox devfsd implementation by Tito <farmatito@tiscali.it>
+#
+# Enable full compatibility mode for old device names. You may comment these
+# out if you don't use the old device names. Make sure you know what you're
+# doing!
+REGISTER       .*              MKOLDCOMPAT
+UNREGISTER     .*              RMOLDCOMPAT
+
+# You may comment out the above and uncomment the following if you've
+# configured your system to use the original "new" devfs names or the really
+# new names
+#REGISTER      ^vc/            MKOLDCOMPAT
+#UNREGISTER    ^vc/            RMOLDCOMPAT
+#REGISTER      ^pty/           MKOLDCOMPAT
+#UNREGISTER    ^pty/           RMOLDCOMPAT
+#REGISTER      ^misc/          MKOLDCOMPAT
+#UNREGISTER    ^misc/          RMOLDCOMPAT
+
+# You may comment these out if you don't use the original "new" names
+REGISTER       .*              MKNEWCOMPAT
+UNREGISTER     .*              RMNEWCOMPAT
+
+# Enable module autoloading. You may comment this out if you don't use
+# autoloading
+# Supported by busybox when CONFIG_DEVFSD_MODLOAD is set.
+# This actually doesn't work with busybox  modutils but needs
+# the real modutils' modprobe
+LOOKUP         .*              MODLOAD
+
+# Uncomment the following if you want to set the group to "tty" for the
+# pseudo-tty devices. This is necessary so that mesg(1) can later be used to
+# enable/disable talk requests and wall(1) messages.
+REGISTER       ^pty/s.*        PERMISSIONS     -1.tty  0600
+#REGISTER      ^pts/.*         PERMISSIONS     -1.tty  0600
+
+# Restoring /dev/log on startup would trigger the minilogd/initlog deadlock
+# (minilogd falsely assuming syslogd has been started).
+REGISTER       ^log$           IGNORE
+CREATE         ^log$           IGNORE
+CHANGE         ^log$           IGNORE
+DELETE         ^log$           IGNORE
+
+#
+# Uncomment this if you want permissions to be saved and restored
+# Do not do this for pseudo-terminal devices
+REGISTER       ^pt[sy]         IGNORE
+CREATE         ^pt[sy]         IGNORE
+CHANGE         ^pt[sy]         IGNORE
+DELETE         ^pt[sy]         IGNORE
+REGISTER       .*              COPY    /lib/dev-state/$devname $devpath
+CREATE         .*              COPY    $devpath /lib/dev-state/$devname
+CHANGE         .*              COPY    $devpath /lib/dev-state/$devname
+#DELETE                .*              CFUNCTION GLOBAL unlink /lib/dev-state/$devname
+# Busybox
+DELETE         .*              EXECUTE /bin/rm -f              /lib/dev-state/$devname
+
+RESTORE                /lib/dev-state
+
+#
+# Uncomment this if you want the old /dev/cdrom symlink
+#REGISTER      ^cdroms/cdrom0$ CFUNCTION GLOBAL mksymlink $devname cdrom
+#UNREGISTER    ^cdroms/cdrom0$ CFUNCTION GLOBAL unlink cdrom
+# busybox
+REGISTER       ^cdroms/cdrom0$ EXECUTE /bin/ln -sf $devname cdrom
+UNREGISTER     ^cdroms/cdrom0$ EXECUTE /bin/rm -f cdrom
+
+#REGISTER      ^v4l/video0$    CFUNCTION GLOBAL mksymlink v4l/video0 video
+#UNREGISTER    ^v4l/video0$    CFUNCTION GLOBAL unlink video
+#REGISTER      ^radio0$        CFUNCTION GLOBAL mksymlink radio0 radio
+#UNREGISTER    ^radio0$        CFUNCTION GLOBAL unlink radio
+# Busybox
+REGISTER       ^v4l/video0$    EXECUTE /bin/ln -sf v4l/video0 video
+UNREGISTER     ^v4l/video0$    EXECUTE /bin/rm -f video
+REGISTER       ^radio0$                EXECUTE /bin/ln -sf  radio0 radio
+UNREGISTER     ^radio0$                EXECUTE /bin/rm -f radio
+
+# ALSA stuff
+#LOOKUP                snd             MODLOAD ACTION snd
+
+# Uncomment this to let PAM manage devfs
+# Not supported by busybox
+#REGISTER      .*              CFUNCTION /lib/security/pam_console_apply_devfsd.so pam_console_apply_single $devpath
+
+# Uncomment this to manage USB mouse
+# Not supported by busybox
+#REGISTER      ^input/mouse0$  CFUNCTION GLOBAL mksymlink $devname usbmouse
+#UNREGISTER    ^input/mouse0$  CFUNCTION GLOBAL unlink usbmouse
+# Busybox
+#REGISTER      ^input/mouse0$  EXECUTE /bin/ln -sf $devname usbmouse
+#UNREGISTER    ^input/mouse0$  EXECUTE /bin/rm -f usbmouse
+# Not supported by busybox
+#REGISTER      ^input/mice$    CFUNCTION GLOBAL mksymlink $devname usbmouse
+#UNREGISTER    ^input/mice$    CFUNCTION GLOBAL unlink usbmouse
+# Busybox
+REGISTER       ^input/mice$    EXECUTE /bin/ln -sf $devname usbmouse
+UNREGISTER     ^input/mice$    EXECUTE /bin/rm -f usbmouse
+
+# If you have removable media and want to force media revalidation when looking
+# up new or old compatibility names, uncomment the following lines
+# SCSI NEWCOMPAT  /dev/sd/* names
+LOOKUP         ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$      EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# SCSI OLDCOMPAT  /dev/sd?? names
+LOOKUP         ^(sd[a-z]+)[0-9]+$      EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE NEWCOMPAT   /dev/ide/hd/* names
+LOOKUP         ^(ide/hd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$  EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE OLDCOMPAT   /dev/hd?? names
+LOOKUP         ^(hd[a-z])[0-9]+$       EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE-SCSI NEWCOMPAT  /dev/sd/* names
+#LOOKUP                ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$      EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+#SCSI OLDCOMPAT  /dev/scd? names
+LOOKUP         ^(scd+)[0-9]+$  EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+
+
+REGISTER ^dvb/card[0-9]+/[^/]+$ PERMISSIONS root.video 0660
+# Not supported by busybox
+#REGISTER      ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$     CFUNCTION GLOBAL mksymlink /dev/$devname ost/\2\1
+#UNREGISTER    ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$     CFUNCTION GLOBAL unlink ost/\2\1
+# Busybox
+REGISTER       ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$     EXECUTE /bin/ln -sf /dev/$devname ost/\2\1
+UNREGISTER     ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$     EXECUTE /bin/rm -f ost/\2\1
+
+# Include package-generated files from /etc/devfs/conf.d
+# Supported by busybox
+# INCLUDE   /etc/devfs/conf.d/
+INCLUDE   /etc/devfs/busybox/
+# Busybox: just for testing
+#INCLUDE                       /etc/devfs/nothing/
+#INCLUDE                       /etc/devfs/nothing/nothing
+#OPTIONAL_INCLUDE      /etc/devfs/nothing/
+#OPTIONAL_INCLUDE      /etc/devfs/nothing/nothing
diff --git a/busybox/examples/inetd.conf b/busybox/examples/inetd.conf
new file mode 100644 (file)
index 0000000..ca7e3d8
--- /dev/null
@@ -0,0 +1,73 @@
+# /etc/inetd.conf:  see inetd(8) for further informations.
+#
+# Internet server configuration database
+#
+#
+# If you want to disable an entry so it isn't touched during
+# package updates just comment it out with a single '#' character.
+#
+# If you make changes to this file, either reboot your machine or
+# send the inetd process a HUP signal:
+# Do a "ps x" as root and look up the pid of inetd. Then do a
+#     kill -HUP <pid of inetd>
+# inetd will re-read this file whenever it gets that signal.
+# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
+#
+#:INTERNAL: Internal services
+# It is generally considered safer to keep these off.
+echo     stream  tcp   nowait  root    internal
+echo     dgram   udp   wait    root    internal
+#discard  stream  tcp  nowait  root    internal
+#discard  dgram   udp  wait    root    internal
+daytime  stream  tcp   nowait  root    internal
+daytime  dgram   udp   wait    root    internal
+#chargen  stream  tcp  nowait  root    internal
+#chargen  dgram   udp  wait    root    internal
+time     stream  tcp   nowait  root    internal
+time     dgram   udp   wait    root    internal
+
+# These are standard services.
+#
+#ftp   stream  tcp     nowait  root    /usr/sbin/tcpd  in.ftpd
+#telnet        stream  tcp     nowait  root    /sbin/telnetd   /sbin/telnetd
+#nntp  stream  tcp     nowait  root    tcpd    in.nntpd
+#smtp  stream  tcp     nowait  root    tcpd    sendmail -v
+#
+# Shell, login, exec and talk are BSD protocols.
+#
+# If you run an ntalk daemon (such as netkit-ntalk) on the old talk
+# port, that is, "talk" as opposed to "ntalk", it won't work and may
+# cause certain broken talk clients to malfunction.
+#
+# The talkd from netkit-ntalk 0.12 and higher, however, can speak the
+# old talk protocol and can be used safely.
+#
+#shell stream  tcp     nowait  root    /usr/sbin/tcpd  in.rshd -L
+#login stream  tcp     nowait  root    /usr/sbin/tcpd  in.rlogind -L
+#exec  stream  tcp     nowait  root    /usr/sbin/tcpd  in.rexecd
+#talk  dgram   udp     wait    root    /usr/sbin/tcpd  in.talkd
+#ntalk dgram   udp     wait    root    /usr/sbin/tcpd  in.talkd
+#
+# Pop et al
+# Leave these off unless you're using them.
+#pop2  stream  tcp     nowait  root    /usr/sbin/tcpd  in.pop2d
+#pop3  stream  tcp     nowait  root    /usr/sbin/tcpd  in.pop3d
+#
+# The Internet UUCP service.
+# uucp stream  tcp     nowait  uucp    /usr/sbin/tcpd  /usr/lib/uucp/uucico    -l
+#
+# Tftp service is provided primarily for booting.  Most sites
+# run this only on machines acting as "boot servers." If you don't
+# need it, don't use it.
+#
+#tftp  dgram   udp     wait    nobody  /usr/sbin/tcpd  in.tftpd
+#bootps        dgram   udp     wait    root    /usr/sbin/in.bootpd     in.bootpd
+#
+# Finger, systat and netstat give out user information which may be
+# valuable to potential "system crackers."  Many sites choose to disable
+# some or all of these services to improve security.
+#
+#finger        stream  tcp     nowait  nobody  /usr/sbin/tcpd  in.fingerd -w
+#systat        stream  tcp     nowait  nobody  /usr/sbin/tcpd  /bin/ps -auwwx
+#netstat       stream  tcp     nowait  root    /bin/netstat    /bin/netstat    -a
+#ident stream  tcp     nowait  root    /usr/sbin/in.identd     in.identd
diff --git a/busybox/examples/inittab b/busybox/examples/inittab
new file mode 100644 (file)
index 0000000..ce711ac
--- /dev/null
@@ -0,0 +1,90 @@
+# /etc/inittab init(8) configuration for BusyBox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+#
+# Note, BusyBox init doesn't support runlevels.  The runlevels field is
+# completely ignored by BusyBox init. If you want runlevels, use sysvinit.
+#
+#
+# Format for each entry: <id>:<runlevels>:<action>:<process>
+#
+# <id>: WARNING: This field has a non-traditional meaning for BusyBox init!
+#
+#      The id field is used by BusyBox init to specify the controlling tty for
+#      the specified process to run on.  The contents of this field are
+#      appended to "/dev/" and used as-is.  There is no need for this field to
+#      be unique, although if it isn't you may have strange results.  If this
+#      field is left blank, it is completely ignored.  Also note that if
+#      BusyBox detects that a serial console is in use, then all entries
+#      containing non-empty id fields will _not_ be run.  BusyBox init does
+#      nothing with utmp.  We don't need no stinkin' utmp.
+#
+# <runlevels>: The runlevels field is completely ignored.
+#
+# <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
+#                                  restart, ctrlaltdel, and shutdown.
+#
+#       Note: askfirst acts just like respawn, but before running the specified
+#       process it displays the line "Please press Enter to activate this
+#       console." and then waits for the user to press enter before starting
+#       the specified process.
+#
+#       Note: unrecognised actions (like initdefault) will cause init to emit
+#       an error message, and then go along with its business.
+#
+# <process>: Specifies the process to be executed and it's command line.
+#
+# Note: BusyBox init works just fine without an inittab. If no inittab is
+# found, it has the following default behavior:
+#         ::sysinit:/etc/init.d/rcS
+#         ::askfirst:/bin/sh
+#         ::ctrlaltdel:/sbin/reboot
+#         ::shutdown:/sbin/swapoff -a
+#         ::shutdown:/bin/umount -a -r
+#         ::restart:/sbin/init
+#
+# if it detects that /dev/console is _not_ a serial console, it will
+# also run:
+#         tty2::askfirst:/bin/sh
+#         tty3::askfirst:/bin/sh
+#         tty4::askfirst:/bin/sh
+#
+# Boot-time system configuration/initialization script.
+# This is run first except when booting in single-user mode.
+#
+::sysinit:/etc/init.d/rcS
+
+# /bin/sh invocations on selected ttys
+#
+# Note below that we prefix the shell commands with a "-" to indicate to the
+# shell that it is supposed to be a login shell.  Normally this is handled by
+# login, but since we are bypassing login in this case, BusyBox lets you do
+# this yourself...
+#
+# Start an "askfirst" shell on the console (whatever that may be)
+::askfirst:-/bin/sh
+# Start an "askfirst" shell on /dev/tty2-4
+tty2::askfirst:-/bin/sh
+tty3::askfirst:-/bin/sh
+tty4::askfirst:-/bin/sh
+
+# /sbin/getty invocations for selected ttys
+tty4::respawn:/sbin/getty 38400 tty5
+tty5::respawn:/sbin/getty 38400 tty6
+
+# Example of how to put a getty on a serial line (for a terminal)
+#::respawn:/sbin/getty -L ttyS0 9600 vt100
+#::respawn:/sbin/getty -L ttyS1 9600 vt100
+#
+# Example how to put a getty on a modem line.
+#::respawn:/sbin/getty 57600 ttyS2
+
+# Stuff to do when restarting the init process
+::restart:/sbin/init
+
+# Stuff to do before rebooting
+::ctrlaltdel:/sbin/reboot
+::shutdown:/bin/umount -a -r
+::shutdown:/sbin/swapoff -a
+
diff --git a/busybox/examples/mk2knr.pl b/busybox/examples/mk2knr.pl
new file mode 100755 (executable)
index 0000000..1259b84
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/perl -w
+#
+# @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style
+#
+# How to use this script:
+#  - In the busybox directory type 'examples/mk2knr.pl files-to-convert'
+#  - Review the 'convertme.pl' script generated and remove / edit any of the
+#    substitutions in there (please especially check for false positives)
+#  - Type './convertme.pl same-files-as-before'
+#  - Compile and see if it works
+#
+# BUGS: This script does not ignore strings inside comments or strings inside
+# quotes (it probably should).
+
+# set this to something else if you want
+$convertme = 'convertme.pl';
+
+# internal-use variables (don't touch)
+$convert = 0;
+%converted = ();
+
+# if no files were specified, print usage
+die "usage: $0 file.c | file.h\n" if scalar(@ARGV) == 0;
+
+# prepare the "convert me" file
+open(CM, ">$convertme") or die "convertme.pl $!";
+print CM "#!/usr/bin/perl -p -i\n\n";
+
+# process each file passed on the cmd line
+while (<>) {
+
+       # if the line says "getopt" in it anywhere, we don't want to muck with it
+       # because option lists tend to include strings like "cxtzvOf:" which get
+       # matched by the "check for mixed case" regexps below
+       next if /getopt/;
+
+       # tokenize the string into just the variables
+       while (/([a-zA-Z_][a-zA-Z0-9_]*)/g) {
+               $var = $1;
+
+               # ignore the word "BusyBox"
+               next if ($var =~ /BusyBox/);
+
+               # this checks for javaStyle or szHungarianNotation
+               $convert++ if ($var =~ /^[a-z]+[A-Z][a-z]+/);
+
+               # this checks for PascalStyle
+               $convert++ if ($var =~ /^[A-Z][a-z]+[A-Z][a-z]+/);
+
+               # if we want to add more checks, we can add 'em here, but the above
+               # checks catch "just enough" and not too much, so prolly not.
+
+               if ($convert) {
+                       $convert = 0;
+
+                       # skip ahead if we've already dealt with this one
+                       next if ($converted{$var});
+
+                       # record that we've dealt with this var
+                       $converted{$var} = 1;
+
+                       print CM "s/\\b$var\\b/"; # more to come in just a minute
+
+                       # change the first letter to lower-case
+                       $var = lcfirst($var);
+
+                       # put underscores before all remaining upper-case letters
+                       $var =~ s/([A-Z])/_$1/g;
+
+                       # now change the remaining characters to lower-case
+                       $var = lc($var);
+
+                       print CM "$var/g;\n";
+               }
+       }
+}
+
+# tidy up and make the $convertme script executable
+close(CM);
+chmod 0755, $convertme;
+
+# print a helpful help message
+print "Done. Scheduled name changes are in $convertme.\n";
+print "Please review/modify it and then type ./$convertme to do the search & replace.\n";
diff --git a/busybox/examples/udhcp/sample.bound b/busybox/examples/udhcp/sample.bound
new file mode 100755 (executable)
index 0000000..2a95d8b
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Sample udhcpc renew script
+
+RESOLV_CONF="/etc/udhcpc/resolv.conf"
+
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+if [ -n "$router" ]
+then
+       echo "deleting routers"
+       while /sbin/route del default gw 0.0.0.0 dev $interface
+       do :
+       done
+
+       metric=0
+       for i in $router
+       do
+               /sbin/route add default gw $i dev $interface metric $((metric++))
+       done
+fi
+
+echo -n > $RESOLV_CONF
+[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+for i in $dns
+do
+       echo adding dns $i
+       echo nameserver $i >> $RESOLV_CONF
+done
\ No newline at end of file
diff --git a/busybox/examples/udhcp/sample.deconfig b/busybox/examples/udhcp/sample.deconfig
new file mode 100755 (executable)
index 0000000..b221bcf
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Sample udhcpc deconfig script
+
+/sbin/ifconfig $interface 0.0.0.0
diff --git a/busybox/examples/udhcp/sample.nak b/busybox/examples/udhcp/sample.nak
new file mode 100755 (executable)
index 0000000..f4d08e6
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Sample udhcpc nak script
+
+echo Received a NAK: $message
diff --git a/busybox/examples/udhcp/sample.renew b/busybox/examples/udhcp/sample.renew
new file mode 100755 (executable)
index 0000000..842bafe
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Sample udhcpc bound script
+
+RESOLV_CONF="/etc/udhcpc/resolv.conf"
+
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+if [ -n "$router" ]
+then
+       echo "deleting routers"
+       while /sbin/route del default gw 0.0.0.0 dev $interface
+       do :
+       done
+
+       metric=0
+       for i in $router
+       do
+               /sbin/route add default gw $i dev $interface metric $((metric++))
+       done
+fi
+
+echo -n > $RESOLV_CONF
+[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+for i in $dns
+do
+       echo adding dns $i
+       echo nameserver $i >> $RESOLV_CONF
+done
\ No newline at end of file
diff --git a/busybox/examples/udhcp/sample.script b/busybox/examples/udhcp/sample.script
new file mode 100644 (file)
index 0000000..9b717ac
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Currently, we only dispatch according to command.  However, a more
+# elaborate system might dispatch by command and interface or do some
+# common initialization first, especially if more dhcp event notifications
+# are added.
+
+exec /usr/share/udhcpc/sample.$1
diff --git a/busybox/examples/udhcp/simple.script b/busybox/examples/udhcp/simple.script
new file mode 100644 (file)
index 0000000..98ebc15
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# udhcpc script edited by Tim Riker <Tim@Rikers.org>
+
+[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
+
+RESOLV_CONF="/etc/resolv.conf"
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+case "$1" in
+       deconfig)
+               /sbin/ifconfig $interface 0.0.0.0
+               ;;
+
+       renew|bound)
+               /sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+               if [ -n "$router" ] ; then
+                       echo "deleting routers"
+                       while route del default gw 0.0.0.0 dev $interface ; do
+                               :
+                       done
+
+                       metric=0
+                       for i in $router ; do
+                               route add default gw $i dev $interface metric $((metric++))
+                       done
+               fi
+
+               echo -n > $RESOLV_CONF
+               [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF
+               for i in $dns ; do
+                       echo adding dns $i
+                       echo nameserver $i >> $RESOLV_CONF
+               done
+               ;;
+esac
+
+exit 0
diff --git a/busybox/examples/udhcp/udhcpd.conf b/busybox/examples/udhcp/udhcpd.conf
new file mode 100644 (file)
index 0000000..f91fdde
--- /dev/null
@@ -0,0 +1,123 @@
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+
+# The start and end of the IP lease block
+
+start          192.168.0.20    #default: 192.168.0.20
+end            192.168.0.254   #default: 192.168.0.254
+
+
+# The interface that udhcpd will use
+
+interface      eth0            #default: eth0
+
+
+# The maximim number of leases (includes addressesd reserved
+# by OFFER's, DECLINE's, and ARP conficts
+
+#max_leases    254             #default: 254
+
+
+# If remaining is true (default), udhcpd will store the time
+# remaining for each lease in the udhcpd leases file. This is
+# for embedded systems that cannot keep time between reboots.
+# If you set remaining to no, the absolute time that the lease
+# expires at will be stored in the dhcpd.leases file.
+
+#remaining     yes             #default: yes
+
+
+# The time period at which udhcpd will write out a dhcpd.leases
+# file. If this is 0, udhcpd will never automatically write a
+# lease file. (specified in seconds)
+
+#auto_time     7200            #default: 7200 (2 hours)
+
+
+# The amount of time that an IP will be reserved (leased) for if a
+# DHCP decline message is received (seconds).
+
+#decline_time  3600            #default: 3600 (1 hour)
+
+
+# The amount of time that an IP will be reserved (leased) for if an
+# ARP conflct occurs. (seconds
+
+#conflict_time 3600            #default: 3600 (1 hour)
+
+
+# How long an offered address is reserved (leased) in seconds
+
+#offer_time    60              #default: 60 (1 minute)
+
+# If a lease to be given is below this value, the full lease time is
+# instead used (seconds).
+
+#min_lease     60              #defult: 60
+
+
+# The location of the leases file
+
+#lease_file    /var/lib/misc/udhcpd.leases     #defualt: /var/lib/misc/udhcpd.leases
+
+# The location of the pid file
+#pidfile       /var/run/udhcpd.pid     #default: /var/run/udhcpd.pid
+
+# Everytime udhcpd writes a leases file, the below script will be called.
+# Useful for writing the lease file to flash every few hours.
+
+#notify_file                           #default: (no script)
+
+#notify_file   dumpleases      # <--- usefull for debugging
+
+# The following are bootp specific options, setable by udhcpd.
+
+#siaddr                192.168.0.22            #default: 0.0.0.0
+
+#sname         zorak                   #default: (none)
+
+#boot_file     /var/nfs_root           #default: (none)
+
+# The remainer of options are DHCP options and can be specifed with the
+# keyword 'opt' or 'option'. If an option can take multiple items, such
+# as the dns option, they can be listed on the same line, or multiple
+# lines. The only option with a default is 'lease'.
+
+#Examles
+opt    dns     192.168.10.2 192.168.10.10
+option subnet  255.255.255.0
+opt    router  192.168.10.2
+opt    wins    192.168.10.10
+option dns     129.219.13.81   # appened to above DNS servers for a total of 3
+option domain  local
+option lease   864000          # 10 days of seconds
+
+
+# Currently supported options, for more info, see options.c
+#opt subnet
+#opt timezone
+#opt router
+#opt timesvr
+#opt namesvr
+#opt dns
+#opt logsvr
+#opt cookiesvr
+#opt lprsvr
+#opt bootsize
+#opt domain
+#opt swapsvr
+#opt rootpath
+#opt ipttl
+#opt mtu
+#opt broadcast
+#opt wins
+#opt lease
+#opt ntpsrv
+#opt tftp
+#opt bootfile
+
+
+# Static leases map
+#static_lease 00:60:08:11:CE:4E 192.168.0.54
+#static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+
diff --git a/busybox/examples/undeb b/busybox/examples/undeb
new file mode 100644 (file)
index 0000000..37104e9
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# This should work with the GNU version of tar and gzip!
+# This should work with the bash or ash shell!
+# Requires the programs (ar, tar, gzip, and the pager more or less).
+#
+usage() {
+echo "Usage: undeb -c package.deb            <Print control file info>"
+echo "       undeb -l package.deb            <List contents of deb package>"
+echo "       undeb -x package.deb /foo/boo   <Extract deb package to this directory,"
+echo "                                        put . for current directory>"
+exit
+}
+
+deb=$2
+
+exist() {
+if [ "$deb" = "" ]; then
+usage
+elif [ ! -s "$deb" ]; then
+echo "Can't find $deb!"
+exit
+fi
+}
+
+if [ "$1" = "" ]; then
+usage
+elif [ "$1" = "-l" ]; then
+exist
+type more >/dev/null 2>&1 && pager=more
+type less >/dev/null 2>&1 && pager=less
+[ "$pager" = "" ] && echo "No pager found!" && exit
+(ar -p $deb control.tar.gz | tar -xzO *control ; echo -e "\nPress enter to scroll, q to Quit!\n" ; ar -p $deb data.tar.gz | tar -tzv) | $pager
+exit
+elif [ "$1" = "-c" ]; then
+exist
+ar -p $deb control.tar.gz | tar -xzO *control
+exit
+elif [ "$1" = "-x" ]; then
+exist
+if [ "$3" = "" ]; then
+usage
+elif [ ! -d "$3" ]; then
+echo "No such directory $3!"
+exit
+fi
+ar -p $deb data.tar.gz | tar -xzvpf - -C $3 || exit
+echo
+echo "Extracted $deb to $3!"
+exit
+else
+usage
+fi
diff --git a/busybox/examples/unrpm b/busybox/examples/unrpm
new file mode 100644 (file)
index 0000000..7fd3676
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# This should work with the GNU version of cpio and gzip!
+# This should work with the bash or ash shell!
+# Requires the programs (cpio, gzip, and the pager more or less).
+#
+usage() {
+echo "Usage: unrpm -l package.rpm            <List contents of rpm package>"
+echo "       unrpm -x package.rpm /foo/boo   <Extract rpm package to this directory,"
+echo "                                        put . for current directory>"
+exit
+}
+
+rpm=$2
+
+exist() {
+if [ "$rpm" = "" ]; then
+usage
+elif [ ! -s "$rpm" ]; then
+echo "Can't find $rpm!"
+exit
+fi
+}
+
+if [ "$1" = "" ]; then
+usage
+elif [ "$1" = "-l" ]; then
+exist
+type more >/dev/null 2>&1 && pager=more
+type less >/dev/null 2>&1 && pager=less
+[ "$pager" = "" ] && echo "No pager found!" && exit
+(echo -e "\nPress enter to scroll, q to Quit!\n" ; rpm2cpio $rpm | cpio -tv --quiet) | $pager
+exit
+elif [ "$1" = "-x" ]; then
+exist
+if [ "$3" = "" ]; then
+usage
+elif [ ! -d "$3" ]; then
+echo "No such directory $3!"
+exit
+fi
+rpm2cpio $rpm | (umask 0 ; cd $3 ; cpio -idmuv) || exit
+echo
+echo "Extracted $rpm to $3!"
+exit
+else
+usage
+fi
diff --git a/busybox/findutils/Config.in b/busybox/findutils/Config.in
new file mode 100644 (file)
index 0000000..3143bd4
--- /dev/null
@@ -0,0 +1,133 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Finding Utilities"
+
+config CONFIG_FIND
+       bool "find"
+       default n
+       help
+         find is used to search your system to find specified files.
+
+config CONFIG_FEATURE_FIND_MTIME
+       bool "  Enable modified time matching (-mtime) option"
+       default y
+       depends on CONFIG_FIND
+       help
+         Allow searching based on the modification time of
+         files.
+
+config CONFIG_FEATURE_FIND_PERM
+       bool "  Enable permissions matching (-perm) option"
+       default y
+       depends on CONFIG_FIND
+       help
+         Enable searching based on file permissions.
+
+config CONFIG_FEATURE_FIND_TYPE
+       bool "  Enable filetype matching (-type) option"
+       default y
+       depends on CONFIG_FIND
+       help
+         Enable searching based on file type (file,
+         directory, socket, device, etc.).
+
+config CONFIG_FEATURE_FIND_XDEV
+       bool "  Enable stay in filesystem (-xdev) option"
+       default y
+       depends on CONFIG_FIND
+       help
+         This option will allow find to restrict searches to a single
+         filesystem.
+
+config CONFIG_FEATURE_FIND_NEWER
+       bool "  Enable -newer option for comparing file mtimes"
+       default y
+       depends on CONFIG_FIND
+       help
+         Support the 'find -newer' option for finding any files which have
+         a modified time that is more recent than the specified FILE.
+
+config CONFIG_FEATURE_FIND_INUM
+       bool "  Enable inode number matching (-inum) option"
+       default y
+       depends on CONFIG_FIND
+       help
+         Support the 'find -inum' option for searching by inode number.
+
+config CONFIG_GREP
+       bool "grep"
+       default n
+       help
+         grep is used to search files for a specified pattern.
+
+config CONFIG_FEATURE_GREP_EGREP_ALIAS
+       bool "  Support extended regular expressions (egrep & grep -E)"
+       default y
+       depends on CONFIG_GREP
+       help
+         Enabled support for extended regular expressions.  Extended
+         regular expressions allow for alternation (foo|bar), grouping,
+         and various repetition operators.
+
+config CONFIG_FEATURE_GREP_FGREP_ALIAS
+       bool "  Alias fgrep to grep -f"
+       default y
+       depends on CONFIG_GREP
+       help
+          fgrep sees the search pattern as a normal string rather than
+         regular expressions.
+         grep -f is always builtin, this just creates the fgrep alias.
+
+config CONFIG_FEATURE_GREP_CONTEXT
+       bool "  Enable before and after context flags (-A, -B and -C)"
+       default y
+       depends on CONFIG_GREP
+       help
+         Print the specified number of leading (-B) and/or trailing (-A)
+         context surrounding our matching lines.
+         Print the specified number of context lines (-C).
+
+config CONFIG_XARGS
+       bool "xargs"
+       default n
+       help
+         xargs is used to execute a specified command on
+         every item from standard input.
+
+config CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+       bool "  Enable prompt and confirmation option -p"
+       default n
+       depends on CONFIG_XARGS
+       help
+         Support prompt the user about whether to run each command
+         line and read a line from the terminal.
+
+config CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+       bool "  Enable support single and double quotes and backslash"
+       default n
+       depends on CONFIG_XARGS
+       help
+         Default xargs unsupport single and double quotes
+         and backslash for can use aruments with spaces.
+
+config CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+       bool "  Enable support options -x"
+       default n
+       depends on CONFIG_XARGS
+       help
+         Enable support exit if the size (see the -s or -n option)
+         is exceeded.
+
+config CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+       bool "  Enable options -0"
+       default n
+       depends on CONFIG_XARGS
+       help
+         Enable input filenames are terminated by a null character
+         instead of by whitespace, and the quotes and backslash
+         are not special.
+
+endmenu
diff --git a/busybox/findutils/Makefile b/busybox/findutils/Makefile
new file mode 100644 (file)
index 0000000..f3f8bb8
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/findutils
+FINDUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/findutils/Makefile.in b/busybox/findutils/Makefile.in
new file mode 100644 (file)
index 0000000..ae71070
--- /dev/null
@@ -0,0 +1,38 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+FINDUTILS_AR:=findutils.a
+ifndef $(FINDUTILS_DIR)
+FINDUTILS_DIR:=$(top_builddir)/findutils/
+endif
+srcdir=$(top_srcdir)/findutils
+
+FINDUTILS-y:=
+FINDUTILS-$(CONFIG_FIND)       += find.o
+FINDUTILS-$(CONFIG_GREP)       += grep.o
+FINDUTILS-$(CONFIG_XARGS)      += xargs.o
+
+libraries-y+=$(FINDUTILS_DIR)$(FINDUTILS_AR)
+
+$(FINDUTILS_DIR)$(FINDUTILS_AR): $(patsubst %,$(FINDUTILS_DIR)%, $(FINDUTILS-y))
+       $(AR) -ro $@ $(patsubst %,$(FINDUTILS_DIR)%, $(FINDUTILS-y))
+
+$(FINDUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/findutils/find.c b/busybox/findutils/find.c
new file mode 100644 (file)
index 0000000..11a838e
--- /dev/null
@@ -0,0 +1,280 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini find implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Reworked by David Douthitt <n9ubh@callsign.net> and
+ *  Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fnmatch.h>
+#include <time.h>
+#include <ctype.h>
+#include "busybox.h"
+
+//XXX just found out about libbb/messages.c . maybe move stuff there ? - ghoz
+const char msg_req_arg[] = "option `%s' requires an argument";
+const char msg_invalid_arg[] = "invalid argument `%s' to `%s'";
+
+static char *pattern;
+
+#ifdef CONFIG_FEATURE_FIND_TYPE
+static int type_mask = 0;
+#endif
+
+#ifdef CONFIG_FEATURE_FIND_PERM
+static char perm_char = 0;
+static int perm_mask = 0;
+#endif
+
+#ifdef CONFIG_FEATURE_FIND_MTIME
+static char mtime_char;
+static int mtime_days;
+#endif
+
+#ifdef CONFIG_FEATURE_FIND_XDEV
+static dev_t *xdev_dev;
+static int xdev_count = 0;
+#endif
+
+#ifdef CONFIG_FEATURE_FIND_NEWER
+time_t newer_mtime;
+#endif
+
+#ifdef CONFIG_FEATURE_FIND_INUM
+static ino_t inode_num;
+#endif
+
+static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
+{
+       if (pattern != NULL) {
+               const char *tmp = strrchr(fileName, '/');
+
+               if (tmp == NULL)
+                       tmp = fileName;
+               else
+                       tmp++;
+               if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0))
+                       goto no_match;
+       }
+#ifdef CONFIG_FEATURE_FIND_TYPE
+       if (type_mask != 0) {
+               if (!((statbuf->st_mode & S_IFMT) == type_mask))
+                       goto no_match;
+       }
+#endif
+#ifdef CONFIG_FEATURE_FIND_PERM
+       if (perm_mask != 0) {
+               if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) ||
+                        (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) ||
+                        (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0)))
+                       goto no_match;
+       }
+#endif
+#ifdef CONFIG_FEATURE_FIND_MTIME
+       if (mtime_char != 0) {
+               time_t file_age = time(NULL) - statbuf->st_mtime;
+               time_t mtime_secs = mtime_days * 24 * 60 * 60;
+               if (!((isdigit(mtime_char) && file_age >= mtime_secs &&
+                                               file_age < mtime_secs + 24 * 60 * 60) ||
+                               (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) ||
+                               (mtime_char == '-' && file_age < mtime_secs)))
+                       goto no_match;
+       }
+#endif
+#ifdef CONFIG_FEATURE_FIND_XDEV
+       if (xdev_count) {
+               int i;
+               for (i=0; i<xdev_count; i++) {
+                       if (xdev_dev[i] == statbuf-> st_dev)
+                               break;
+               }
+               if (i == xdev_count) {
+                       if(S_ISDIR(statbuf->st_mode))
+                               return SKIP;
+                       else
+                               goto no_match;
+               }
+       }
+#endif
+#ifdef CONFIG_FEATURE_FIND_NEWER
+       if (newer_mtime != 0) {
+               time_t file_age = newer_mtime - statbuf->st_mtime;
+               if (file_age >= 0)
+                       goto no_match;
+       }
+#endif
+#ifdef CONFIG_FEATURE_FIND_INUM
+       if (inode_num != 0) {
+               if (!(statbuf->st_ino == inode_num))
+                       goto no_match;
+       }
+#endif
+       puts(fileName);
+no_match:
+       return (TRUE);
+}
+
+#ifdef CONFIG_FEATURE_FIND_TYPE
+static int find_type(char *type)
+{
+       int mask = 0;
+
+       switch (type[0]) {
+               case 'b':
+                       mask = S_IFBLK;
+                       break;
+               case 'c':
+                       mask = S_IFCHR;
+                       break;
+               case 'd':
+                       mask = S_IFDIR;
+                       break;
+               case 'p':
+                       mask = S_IFIFO;
+                       break;
+               case 'f':
+                       mask = S_IFREG;
+                       break;
+               case 'l':
+                       mask = S_IFLNK;
+                       break;
+               case 's':
+                       mask = S_IFSOCK;
+                       break;
+       }
+
+       if (mask == 0 || type[1] != '\0')
+               bb_error_msg_and_die(msg_invalid_arg, type, "-type");
+
+       return mask;
+}
+#endif
+
+int find_main(int argc, char **argv)
+{
+       int dereference = FALSE;
+       int i, firstopt, status = EXIT_SUCCESS;
+
+       for (firstopt = 1; firstopt < argc; firstopt++) {
+               if (argv[firstopt][0] == '-')
+                       break;
+       }
+
+       /* Parse any options */
+       for (i = firstopt; i < argc; i++) {
+               if (strcmp(argv[i], "-follow") == 0)
+                       dereference = TRUE;
+               else if (strcmp(argv[i], "-print") == 0) {
+                       ;
+                       }
+               else if (strcmp(argv[i], "-name") == 0) {
+                       if (++i == argc)
+                               bb_error_msg_and_die(msg_req_arg, "-name");
+                       pattern = argv[i];
+#ifdef CONFIG_FEATURE_FIND_TYPE
+               } else if (strcmp(argv[i], "-type") == 0) {
+                       if (++i == argc)
+                               bb_error_msg_and_die(msg_req_arg, "-type");
+                       type_mask = find_type(argv[i]);
+#endif
+#ifdef CONFIG_FEATURE_FIND_PERM
+               } else if (strcmp(argv[i], "-perm") == 0) {
+                       char *end;
+                       if (++i == argc)
+                               bb_error_msg_and_die(msg_req_arg, "-perm");
+                       perm_mask = strtol(argv[i], &end, 8);
+                       if ((end[0] != '\0') || (perm_mask > 07777))
+                               bb_error_msg_and_die(msg_invalid_arg, argv[i], "-perm");
+                       if ((perm_char = argv[i][0]) == '-')
+                               perm_mask = -perm_mask;
+#endif
+#ifdef CONFIG_FEATURE_FIND_MTIME
+               } else if (strcmp(argv[i], "-mtime") == 0) {
+                       char *end;
+                       if (++i == argc)
+                               bb_error_msg_and_die(msg_req_arg, "-mtime");
+                       mtime_days = strtol(argv[i], &end, 10);
+                       if (end[0] != '\0')
+                               bb_error_msg_and_die(msg_invalid_arg, argv[i], "-mtime");
+                       if ((mtime_char = argv[i][0]) == '-')
+                               mtime_days = -mtime_days;
+#endif
+#ifdef CONFIG_FEATURE_FIND_XDEV
+               } else if (strcmp(argv[i], "-xdev") == 0) {
+                       struct stat stbuf;
+
+                       xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1;
+                       xdev_dev = xmalloc ( xdev_count * sizeof( dev_t ));
+
+                       if ( firstopt == 1 ) {
+                               if ( stat ( ".", &stbuf ) < 0 )
+                                       bb_error_msg_and_die("could not stat '.'" );
+                               xdev_dev [0] = stbuf. st_dev;
+                       }
+                       else {
+
+                               for (i = 1; i < firstopt; i++) {
+                                       if ( stat ( argv [i], &stbuf ) < 0 )
+                                               bb_error_msg_and_die("could not stat '%s'", argv [i] );
+                                       xdev_dev [i-1] = stbuf. st_dev;
+                               }
+                       }
+#endif
+#ifdef CONFIG_FEATURE_FIND_NEWER
+               } else if (strcmp(argv[i], "-newer") == 0) {
+                       struct stat stat_newer;
+                       if (++i == argc)
+                               bb_error_msg_and_die(msg_req_arg, "-newer");
+                   if (stat (argv[i], &stat_newer) != 0)
+                               bb_error_msg_and_die("file %s not found", argv[i]);
+                       newer_mtime = stat_newer.st_mtime;
+#endif
+#ifdef CONFIG_FEATURE_FIND_INUM
+               } else if (strcmp(argv[i], "-inum") == 0) {
+                       char *end;
+                       if (++i == argc)
+                               bb_error_msg_and_die(msg_req_arg, "-inum");
+                       inode_num = strtol(argv[i], &end, 10);
+                       if (end[0] != '\0')
+                               bb_error_msg_and_die(msg_invalid_arg, argv[i], "-inum");
+#endif
+               } else
+                       bb_show_usage();
+       }
+
+       if (firstopt == 1) {
+               if (! recursive_action(".", TRUE, dereference, FALSE, fileAction,
+                                       fileAction, NULL))
+                       status = EXIT_FAILURE;
+       } else {
+               for (i = 1; i < firstopt; i++) {
+                       if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction,
+                                               fileAction, NULL))
+                               status = EXIT_FAILURE;
+               }
+       }
+
+       return status;
+}
diff --git a/busybox/findutils/grep.c b/busybox/findutils/grep.c
new file mode 100644 (file)
index 0000000..29f4ecd
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Mini grep implementation for busybox using libc regex.
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
+ * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * Apr 2004 by Vladimir Oleynik <dzo@simtreas.ru> -
+ * correction "-e pattern1 -e pattern2" logic and more optimizations.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <regex.h>
+#include <string.h>
+#include <errno.h>
+#include "busybox.h"
+
+
+/* options */
+#define GREP_OPTS "lnqvscFiHhe:f:L"
+#define GREP_OPT_l (1<<0)
+static char print_files_with_matches;
+#define GREP_OPT_n (1<<1)
+static char print_line_num;
+#define GREP_OPT_q (1<<2)
+static char be_quiet;
+#define GREP_OPT_v (1<<3)
+typedef char invert_search_t;
+static invert_search_t invert_search;
+#define GREP_OPT_s (1<<4)
+static char suppress_err_msgs;
+#define GREP_OPT_c (1<<5)
+static char print_match_counts;
+#define GREP_OPT_F (1<<6)
+static char fgrep_flag;
+#define GREP_OPT_i (1<<7)
+#define GREP_OPT_H (1<<8)
+#define GREP_OPT_h (1<<9)
+#define GREP_OPT_e (1<<10)
+#define GREP_OPT_f (1<<11)
+#define GREP_OPT_L (1<<12)
+static char print_files_without_matches;
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+#define GREP_OPT_CONTEXT "A:B:C"
+#define GREP_OPT_A (1<<13)
+#define GREP_OPT_B (1<<14)
+#define GREP_OPT_C (1<<15)
+#define GREP_OPT_E (1<<16)
+#else
+#define GREP_OPT_CONTEXT ""
+#define GREP_OPT_E (1<<13)
+#endif
+#ifdef CONFIG_FEATURE_GREP_EGREP_ALIAS
+# define OPT_EGREP "E"
+#else
+# define OPT_EGREP ""
+#endif
+
+static int reflags;
+static int print_filename;
+
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+static int lines_before;
+static int lines_after;
+static char **before_buf;
+static int last_line_printed;
+#endif /* CONFIG_FEATURE_GREP_CONTEXT */
+
+/* globals used internally */
+static llist_t *pattern_head;   /* growable list of patterns to match */
+static char *cur_file;          /* the current file we are reading */
+
+
+static void print_line(const char *line, int linenum, char decoration)
+{
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+       /* possibly print the little '--' separator */
+       if ((lines_before || lines_after) && last_line_printed &&
+                       last_line_printed < linenum - 1) {
+               puts("--");
+       }
+       last_line_printed = linenum;
+#endif
+       if (print_filename)
+               printf("%s%c", cur_file, decoration);
+       if (print_line_num)
+               printf("%i%c", linenum, decoration);
+       puts(line);
+}
+
+extern void xregcomp(regex_t *preg, const char *regex, int cflags);
+
+
+static int grep_file(FILE *file)
+{
+       char *line;
+       invert_search_t ret;
+       int linenum = 0;
+       int nmatches = 0;
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+       int print_n_lines_after = 0;
+       int curpos = 0; /* track where we are in the circular 'before' buffer */
+       int idx = 0; /* used for iteration through the circular buffer */
+#endif /* CONFIG_FEATURE_GREP_CONTEXT */
+
+       while ((line = bb_get_chomped_line_from_file(file)) != NULL) {
+               llist_t *pattern_ptr = pattern_head;
+
+               linenum++;
+               ret = 0;
+               while (pattern_ptr) {
+                       if (fgrep_flag) {
+                               ret = strstr(line, pattern_ptr->data) != NULL;
+                       } else {
+                               /*
+                                * test for a postitive-assertion match (regexec returns success (0)
+                                * and the user did not specify invert search), or a negative-assertion
+                                * match (regexec returns failure (REG_NOMATCH) and the user specified
+                                * invert search)
+                                */
+                               regex_t regex;
+                               xregcomp(&regex, pattern_ptr->data, reflags);
+                               ret |= regexec(&regex, line, 0, NULL, 0) == 0;
+                               regfree(&regex);
+                       }
+                       pattern_ptr = pattern_ptr->link;
+               } /* while (pattern_ptr) */
+
+               if ((ret ^ invert_search)) {
+
+                       if (print_files_with_matches || be_quiet)
+                               free(line);
+
+                       /* if we found a match but were told to be quiet, stop here */
+                               if (be_quiet || print_files_without_matches)
+                               return -1;
+
+                               /* keep track of matches */
+                               nmatches++;
+
+                               /* if we're just printing filenames, we stop after the first match */
+                               if (print_files_with_matches)
+                                       break;
+
+                               /* print the matched line */
+                               if (print_match_counts == 0) {
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+                                       int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
+
+                                       /* if we were told to print 'before' lines and there is at least
+                                        * one line in the circular buffer, print them */
+                                       if (lines_before && before_buf[prevpos] != NULL) {
+                                               int first_buf_entry_line_num = linenum - lines_before;
+
+                                               /* advance to the first entry in the circular buffer, and
+                                                * figure out the line number is of the first line in the
+                                                * buffer */
+                                               idx = curpos;
+                                               while (before_buf[idx] == NULL) {
+                                                       idx = (idx + 1) % lines_before;
+                                                       first_buf_entry_line_num++;
+                                               }
+
+                                               /* now print each line in the buffer, clearing them as we go */
+                                               while (before_buf[idx] != NULL) {
+                                                       print_line(before_buf[idx], first_buf_entry_line_num, '-');
+                                                       free(before_buf[idx]);
+                                                       before_buf[idx] = NULL;
+                                                       idx = (idx + 1) % lines_before;
+                                                       first_buf_entry_line_num++;
+                                               }
+                                       }
+
+                                       /* make a note that we need to print 'after' lines */
+                                       print_n_lines_after = lines_after;
+#endif /* CONFIG_FEATURE_GREP_CONTEXT */
+                                       print_line(line, linenum, ':');
+                               }
+                       }
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+                       else { /* no match */
+                               /* Add the line to the circular 'before' buffer */
+                               if(lines_before) {
+                                       free(before_buf[curpos]);
+                                       before_buf[curpos] = bb_xstrdup(line);
+                                       curpos = (curpos + 1) % lines_before;
+                               }
+                       }
+
+                       /* if we need to print some context lines after the last match, do so */
+                       if (print_n_lines_after && (last_line_printed != linenum)) {
+                               print_line(line, linenum, '-');
+                               print_n_lines_after--;
+                       }
+#endif /* CONFIG_FEATURE_GREP_CONTEXT */
+               free(line);
+       }
+
+
+       /* special-case file post-processing for options where we don't print line
+        * matches, just filenames and possibly match counts */
+
+       /* grep -c: print [filename:]count, even if count is zero */
+       if (print_match_counts) {
+               if (print_filename)
+                       printf("%s:", cur_file);
+                   printf("%d\n", nmatches);
+       }
+
+       /* grep -l: print just the filename, but only if we grepped the line in the file  */
+       if (print_files_with_matches && nmatches > 0) {
+               puts(cur_file);
+       }
+
+       /* grep -L: print just the filename, but only if we didn't grep the line in the file  */
+       if (print_files_without_matches && nmatches == 0) {
+               puts(cur_file);
+       }
+
+       return nmatches;
+}
+
+static void load_regexes_from_file(llist_t *fopt)
+{
+       char *line;
+       FILE *f;
+
+       while(fopt) {
+               llist_t *cur = fopt;
+               char *ffile = cur->data;
+
+               fopt = cur->link;
+               free(cur);
+               f = bb_xfopen(ffile, "r");
+               while ((line = bb_get_chomped_line_from_file(f)) != NULL) {
+                       pattern_head = llist_add_to(pattern_head, line);
+               }
+       }
+}
+
+
+extern int grep_main(int argc, char **argv)
+{
+       FILE *file;
+       int matched;
+       unsigned long opt;
+       llist_t *fopt = NULL;
+
+       /* do normal option parsing */
+#ifdef CONFIG_FEATURE_GREP_CONTEXT
+  {
+       char *junk;
+       char *slines_after;
+       char *slines_before;
+       char *Copt;
+
+       bb_opt_complementaly = "H-h:e*:f*:C-AB";
+       opt = bb_getopt_ulflags(argc, argv,
+               GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP,
+               &pattern_head, &fopt,
+               &slines_after, &slines_before, &Copt);
+
+       if(opt & GREP_OPT_C) {
+               /* C option unseted A and B options, but next -A or -B
+                  may be ovewrite own option */
+               if(!(opt & GREP_OPT_A))         /* not overwtited */
+                       slines_after = Copt;
+               if(!(opt & GREP_OPT_B))         /* not overwtited */
+                       slines_before = Copt;
+               opt |= GREP_OPT_A|GREP_OPT_B;   /* set for parse now */
+       }
+       if(opt & GREP_OPT_A) {
+               lines_after = strtoul(slines_after, &junk, 10);
+                               if(*junk != '\0')
+                                       bb_error_msg_and_die("invalid context length argument");
+       }
+       if(opt & GREP_OPT_B) {
+               lines_before = strtoul(slines_before, &junk, 10);
+                               if(*junk != '\0')
+                                       bb_error_msg_and_die("invalid context length argument");
+               }
+       /* sanity checks after parse may be invalid numbers ;-) */
+       if ((opt & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L))) {
+               opt &= ~GREP_OPT_n;
+               lines_before = 0;
+               lines_after = 0;
+       } else if(lines_before > 0)
+               before_buf = (char **)xcalloc(lines_before, sizeof(char *));
+       }
+#else
+       /* with auto sanity checks */
+       bb_opt_complementaly = "H-h:e*:f*:c-n:q-n:l-n";
+       opt = bb_getopt_ulflags(argc, argv, GREP_OPTS OPT_EGREP,
+               &pattern_head, &fopt);
+
+#endif
+       print_files_with_matches = opt & GREP_OPT_l;
+       print_files_without_matches = (opt & GREP_OPT_L) != 0;
+       print_line_num = opt & GREP_OPT_n;
+       be_quiet = opt & GREP_OPT_q;
+       invert_search = (opt & GREP_OPT_v) != 0;        /* 0 | 1 */
+       suppress_err_msgs = opt & GREP_OPT_s;
+       print_match_counts = opt & GREP_OPT_c;
+       fgrep_flag = opt & GREP_OPT_F;
+       if(opt & GREP_OPT_H)
+               print_filename++;
+       if(opt & GREP_OPT_h)
+               print_filename--;
+       if(opt & GREP_OPT_f)
+               load_regexes_from_file(fopt);
+
+#ifdef CONFIG_FEATURE_GREP_EGREP_ALIAS
+       if(bb_applet_name[0] == 'e' || (opt & GREP_OPT_E))
+               reflags = REG_EXTENDED | REG_NOSUB;
+       else
+#endif
+               reflags = REG_NOSUB;
+
+       if(opt & GREP_OPT_i)
+               reflags |= REG_ICASE;
+
+       argv += optind;
+       argc -= optind;
+
+       /* if we didn't get a pattern from a -e and no command file was specified,
+        * argv[optind] should be the pattern. no pattern, no worky */
+       if (pattern_head == NULL) {
+               if (*argv == NULL)
+                       bb_show_usage();
+               else {
+                       pattern_head = llist_add_to(pattern_head, *argv++);
+                       argc--;
+               }
+       }
+
+       /* argv[(optind)..(argc-1)] should be names of file to grep through. If
+        * there is more than one file to grep, we will print the filenames */
+       if (argc > 1) {
+               print_filename++;
+
+       /* If no files were specified, or '-' was specified, take input from
+        * stdin. Otherwise, we grep through all the files specified. */
+       } else if (argc == 0) {
+               argc++;
+       }
+       matched = 0;
+       while (argc--) {
+               cur_file = *argv++;
+               if(!cur_file || (*cur_file == '-' && !cur_file[1])) {
+                       cur_file = "-";
+                       file = stdin;
+               } else {
+                       file = fopen(cur_file, "r");
+               }
+                       if (file == NULL) {
+                               if (!suppress_err_msgs)
+                                       bb_perror_msg("%s", cur_file);
+               } else {
+                       matched += grep_file(file);
+                       if(matched < 0) {
+                               /* we found a match but were told to be quiet, stop here and
+                               * return success */
+                               break;
+                       }
+                               fclose(file);
+                       }
+               }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       /* destroy all the elments in the pattern list */
+       while (pattern_head) {
+               llist_t *pattern_head_ptr = pattern_head;
+
+               pattern_head = pattern_head->link;
+               free(pattern_head_ptr);
+       }
+#endif
+
+       return !matched; /* invert return value 0 = success, 1 = failed */
+}
diff --git a/busybox/findutils/xargs.c b/busybox/findutils/xargs.c
new file mode 100644 (file)
index 0000000..1a43478
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * Mini xargs implementation for busybox
+ * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
+ *
+ * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Special thanks
+ * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
+ * - Mike Rendell <michael@cs.mun.ca>
+ * and David MacKenzie <djm@gnu.ai.mit.edu>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * xargs is described in the Single Unix Specification v3 at
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "busybox.h"
+
+/* COMPAT:  SYSV version defaults size (and has a max value of) to 470.
+   We try to make it as large as possible. */
+#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
+#define ARG_MAX sysconf (_SC_ARG_MAX)
+#endif
+#ifndef ARG_MAX
+#define ARG_MAX 470
+#endif
+
+
+#ifdef TEST
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+#  define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+# endif
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+#  define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+# endif
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+#  define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+# endif
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+#  define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+# endif
+#endif
+
+/*
+   This function have special algorithm.
+   Don`t use fork and include to main!
+*/
+static int xargs_exec(char *const *args)
+{
+       pid_t p;
+       volatile int exec_errno = 0;    /* shared vfork stack */
+
+       if ((p = vfork()) >= 0) {
+               if (p == 0) {
+                       /* vfork -- child */
+                       execvp(args[0], args);
+                       exec_errno = errno;     /* set error to shared stack */
+                       _exit(1);
+               } else {
+                       /* vfork -- parent */
+                       int status;
+
+                       while (wait(&status) == (pid_t) - 1)
+                               if (errno != EINTR)
+                                       break;
+                       if (exec_errno) {
+                               errno = exec_errno;
+                               bb_perror_msg("%s", args[0]);
+                               return exec_errno == ENOENT ? 127 : 126;
+                       } else {
+                               if (WEXITSTATUS(status) == 255) {
+                                       bb_error_msg("%s: exited with status 255; aborting", args[0]);
+                                       return 124;
+                               }
+                               if (WIFSTOPPED(status)) {
+                                       bb_error_msg("%s: stopped by signal %d",
+                                               args[0], WSTOPSIG(status));
+                                       return 125;
+                               }
+                               if (WIFSIGNALED(status)) {
+                                       bb_error_msg("%s: terminated by signal %d",
+                                               args[0], WTERMSIG(status));
+                                       return 125;
+                               }
+                               if (WEXITSTATUS(status) != 0)
+                                       return 123;
+                               return 0;
+                       }
+               }
+       } else {
+               bb_perror_msg_and_die("vfork");
+       }
+}
+
+
+typedef struct xlist_s {
+       char *data;
+       size_t lenght;
+       struct xlist_s *link;
+} xlist_t;
+
+static int eof_stdin_detected;
+
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
+                   || (c) == '\f' || (c) == '\v')
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+static xlist_t *process_stdin(xlist_t * list_arg,
+       const char *eof_str, size_t mc, char *buf)
+{
+#define NORM      0
+#define QUOTE     1
+#define BACKSLASH 2
+#define SPACE     4
+
+       char *s = NULL;         /* start word */
+       char *p = NULL;         /* pointer to end word */
+       char q = 0;             /* quote char */
+       char state = NORM;
+       char eof_str_detected = 0;
+       size_t line_l = 0;      /* size loaded args line */
+       int c;                  /* current char */
+       xlist_t *cur;
+       xlist_t *prev;
+
+       for (prev = cur = list_arg; cur; cur = cur->link) {
+               line_l += cur->lenght;  /* previous allocated */
+               if (prev != cur)
+                       prev = prev->link;
+       }
+
+       while (!eof_stdin_detected) {
+               c = getchar();
+               if (c == EOF) {
+                       eof_stdin_detected++;
+                       if (s)
+                               goto unexpected_eof;
+                       break;
+               }
+               if (eof_str_detected)
+                       continue;
+               if (state == BACKSLASH) {
+                       state = NORM;
+                       goto set;
+               } else if (state == QUOTE) {
+                       if (c == q) {
+                               q = 0;
+                               state = NORM;
+                       } else {
+                               goto set;
+                       }
+               } else { /* if(state == NORM) */
+
+                       if (ISSPACE(c)) {
+                               if (s) {
+unexpected_eof:
+                                       state = SPACE;
+                                       c = 0;
+                                       goto set;
+                               }
+                       } else {
+                               if (s == NULL)
+                                       s = p = buf;
+                               if (c == '\\') {
+                                       state = BACKSLASH;
+                               } else if (c == '\'' || c == '"') {
+                                       q = c;
+                                       state = QUOTE;
+                               } else {
+set:
+                                       if ((p - buf) >= mc)
+                                               bb_error_msg_and_die("argument line too long");
+                                       *p++ = c;
+                               }
+                       }
+               }
+               if (state == SPACE) {   /* word's delimiter or EOF detected */
+                       if (q) {
+                               bb_error_msg_and_die("unmatched %s quote",
+                                       q == '\'' ? "single" : "double");
+                       }
+                       /* word loaded */
+                       if (eof_str) {
+                               eof_str_detected = strcmp(s, eof_str) == 0;
+                       }
+                       if (!eof_str_detected) {
+                               size_t lenght = (p - buf);
+
+                               cur = xmalloc(sizeof(xlist_t) + lenght);
+                               cur->data = memcpy(cur + 1, s, lenght);
+                               cur->lenght = lenght;
+                               cur->link = NULL;
+                               if (prev == NULL) {
+                                       list_arg = cur;
+                               } else {
+                                       prev->link = cur;
+                               }
+                               prev = cur;
+                               line_l += lenght;
+                               if (line_l > mc) {
+                                       /* stop memory usage :-) */
+                                       break;
+                               }
+                       }
+                       s = NULL;
+                       state = NORM;
+               }
+       }
+       return list_arg;
+}
+#else
+/* The variant does not support single quotes, double quotes or backslash */
+static xlist_t *process_stdin(xlist_t * list_arg,
+       const char *eof_str, size_t mc, char *buf)
+{
+
+       int c;                  /* current char */
+       int eof_str_detected = 0;
+       char *s = NULL;         /* start word */
+       char *p = NULL;         /* pointer to end word */
+       size_t line_l = 0;      /* size loaded args line */
+       xlist_t *cur;
+       xlist_t *prev;
+
+       for (prev = cur = list_arg; cur; cur = cur->link) {
+               line_l += cur->lenght;  /* previous allocated */
+               if (prev != cur)
+                       prev = prev->link;
+       }
+
+       while (!eof_stdin_detected) {
+               c = getchar();
+               if (c == EOF) {
+                       eof_stdin_detected++;
+               }
+               if (eof_str_detected)
+                       continue;
+               if (c == EOF || ISSPACE(c)) {
+                       if (s == NULL)
+                               continue;
+                       c = EOF;
+               }
+               if (s == NULL)
+                       s = p = buf;
+               if ((p - buf) >= mc)
+                       bb_error_msg_and_die("argument line too long");
+               *p++ = c == EOF ? 0 : c;
+               if (c == EOF) { /* word's delimiter or EOF detected */
+                       /* word loaded */
+                       if (eof_str) {
+                               eof_str_detected = strcmp(s, eof_str) == 0;
+                       }
+                       if (!eof_str_detected) {
+                               size_t lenght = (p - buf);
+
+                               cur = xmalloc(sizeof(xlist_t) + lenght);
+                               cur->data = memcpy(cur + 1, s, lenght);
+                               cur->lenght = lenght;
+                               cur->link = NULL;
+                               if (prev == NULL) {
+                                       list_arg = cur;
+                               } else {
+                                       prev->link = cur;
+                               }
+                               prev = cur;
+                               line_l += lenght;
+                               if (line_l > mc) {
+                                       /* stop memory usage :-) */
+                                       break;
+                               }
+                               s = NULL;
+                       }
+               }
+       }
+       return list_arg;
+}
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
+
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+/* Prompt the user for a response, and
+   if the user responds affirmatively, return true;
+   otherwise, return false. Used "/dev/tty", not stdin. */
+static int xargs_ask_confirmation(void)
+{
+       static FILE *tty_stream;
+       int c, savec;
+
+       if (!tty_stream) {
+               tty_stream = fopen("/dev/tty", "r");
+               if (!tty_stream)
+                       bb_perror_msg_and_die("/dev/tty");
+               /* pranoidal security by vodz */
+               fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
+       }
+       fputs(" ?...", stderr);
+       fflush(stderr);
+       c = savec = getc(tty_stream);
+       while (c != EOF && c != '\n')
+               c = getc(tty_stream);
+       if (savec == 'y' || savec == 'Y')
+               return 1;
+       return 0;
+}
+
+# define OPT_INC_P 1
+#else
+# define OPT_INC_P 0
+# define xargs_ask_confirmation() 1
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+# define OPT_INC_X 1
+#else
+# define OPT_INC_X 0
+#endif
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str,
+                                                          size_t mc, char *buf)
+{
+       int c;                  /* current char */
+       char *s = NULL;         /* start word */
+       char *p = NULL;         /* pointer to end word */
+       size_t line_l = 0;      /* size loaded args line */
+       xlist_t *cur;
+       xlist_t *prev;
+
+       for (prev = cur = list_arg; cur; cur = cur->link) {
+               line_l += cur->lenght;  /* previous allocated */
+               if (prev != cur)
+                       prev = prev->link;
+       }
+
+       while (!eof_stdin_detected) {
+               c = getchar();
+               if (c == EOF) {
+                       eof_stdin_detected++;
+                       if (s == NULL)
+                               break;
+                       c = 0;
+               }
+               if (s == NULL)
+                       s = p = buf;
+               if ((p - buf) >= mc)
+                       bb_error_msg_and_die("argument line too long");
+               *p++ = c;
+               if (c == 0) {   /* word's delimiter or EOF detected */
+                       /* word loaded */
+                       size_t lenght = (p - buf);
+
+                       cur = xmalloc(sizeof(xlist_t) + lenght);
+                       cur->data = memcpy(cur + 1, s, lenght);
+                       cur->lenght = lenght;
+                       cur->link = NULL;
+                       if (prev == NULL) {
+                               list_arg = cur;
+                       } else {
+                               prev->link = cur;
+                       }
+                       prev = cur;
+                       line_l += lenght;
+                       if (line_l > mc) {
+                               /* stop memory usage :-) */
+                               break;
+                       }
+                       s = NULL;
+               }
+       }
+       return list_arg;
+}
+
+# define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc)
+# define OPT_INC_0 1   /* future use */
+#else
+# define OPT_INC_0 0   /* future use */
+# define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
+
+
+#define OPT_VERBOSE     (1<<0)
+#define OPT_NO_EMPTY    (1<<1)
+#define OPT_UPTO_NUMBER (1<<2)
+#define OPT_UPTO_SIZE   (1<<3)
+#define OPT_EOF_STRING  (1<<4)
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+#define OPT_INTERACTIVE (1<<5)
+#else
+#define OPT_INTERACTIVE (0)    /* require for algorithm &| */
+#endif
+#define OPT_TERMINATE   (1<<(5+OPT_INC_P))
+#define OPT_ZEROTERM    (1<<(5+OPT_INC_P+OPT_INC_X))
+/* next future
+#define OPT_NEXT_OTHER  (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0))
+*/
+
+int xargs_main(int argc, char **argv)
+{
+       char **args;
+       int i, a, n;
+       xlist_t *list = NULL;
+       xlist_t *cur;
+       int child_error = 0;
+       char *max_args, *max_chars;
+       int n_max_arg;
+       size_t n_chars = 0;
+       long orig_arg_max;
+       const char *eof_str = "_";
+       unsigned long opt;
+       size_t n_max_chars;
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+       xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin;
+#endif
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+       bb_opt_complementaly = "pt";
+#endif
+
+       opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::"
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+       "p"
+#endif
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+       "x"
+#endif
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+       "0"
+#endif
+       ,&max_args, &max_chars, &eof_str);
+
+       a = argc - optind;
+       argv += optind;
+       if (a == 0) {
+               /* default behavior is to echo all the filenames */
+               *argv = "echo";
+               a++;
+       }
+
+       orig_arg_max = ARG_MAX;
+       if (orig_arg_max == -1)
+               orig_arg_max = LONG_MAX;
+       orig_arg_max -= 2048;   /* POSIX.2 requires subtracting 2048.  */
+       if ((opt & OPT_UPTO_SIZE)) {
+               n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max);
+               for (i = 0; i < a; i++) {
+                       n_chars += strlen(*argv) + 1;
+               }
+               if (n_max_chars < n_chars) {
+                       bb_error_msg_and_die("can not fit single argument within argument list size limit");
+               }
+               n_max_chars -= n_chars;
+       } else {
+               /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
+                  have it at 1 meg).  Things will work fine with a large ARG_MAX but it
+                  will probably hurt the system more than it needs to; an array of this
+                  size is allocated.  */
+               if (orig_arg_max > 20 * 1024)
+                       orig_arg_max = 20 * 1024;
+               n_max_chars = orig_arg_max;
+       }
+       max_chars = xmalloc(n_max_chars);
+
+       if ((opt & OPT_UPTO_NUMBER)) {
+               n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX);
+       } else {
+               n_max_arg = n_max_chars;
+       }
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+       if (opt & OPT_ZEROTERM)
+               read_args = process0_stdin;
+#endif
+
+       while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||
+               (opt & OPT_NO_EMPTY) == 0)
+       {
+               opt |= OPT_NO_EMPTY;
+               n = 0;
+               n_chars = 0;
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+               for (cur = list; cur;) {
+                       n_chars += cur->lenght;
+                       n++;
+                       cur = cur->link;
+                       if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
+                               if (opt & OPT_TERMINATE)
+                                       bb_error_msg_and_die("argument list too long");
+                               break;
+                       }
+               }
+#else
+               for (cur = list; cur; cur = cur->link) {
+                       n_chars += cur->lenght;
+                       n++;
+                       if (n_chars > n_max_chars || n == n_max_arg) {
+                               break;
+                       }
+               }
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
+
+               /* allocating pointers for execvp:
+                  a*arg, n*arg from stdin, NULL */
+               args = xcalloc(n + a + 1, sizeof(char *));
+
+               /* Store the command to be executed
+                  (taken from the command line) */
+               for (i = 0; i < a; i++)
+                       args[i] = argv[i];
+               /* (taken from stdin) */
+               for (cur = list; n; cur = cur->link) {
+                       args[i++] = cur->data;
+                       n--;
+               }
+
+               if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) {
+                       for (i = 0; args[i]; i++) {
+                               if (i)
+                                       fputc(' ', stderr);
+                               fputs(args[i], stderr);
+                       }
+                       if ((opt & OPT_INTERACTIVE) == 0)
+                               fputc('\n', stderr);
+               }
+               if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
+                       child_error = xargs_exec(args);
+               }
+
+               /* clean up */
+               for (i = a; args[i]; i++) {
+                       cur = list;
+                       list = list->link;
+                       free(cur);
+               }
+               free(args);
+               if (child_error > 0 && child_error != 123) {
+                       break;
+               }
+       }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       free(max_chars);
+#endif
+       return child_error;
+}
+
+
+#ifdef TEST
+
+const char *bb_applet_name = "debug stuff usage";
+
+void bb_show_usage(void)
+{
+       fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
+               bb_applet_name);
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+       return xargs_main(argc, argv);
+}
+#endif /* TEST */
diff --git a/busybox/include/.cvsignore b/busybox/include/.cvsignore
new file mode 100644 (file)
index 0000000..9c68c95
--- /dev/null
@@ -0,0 +1,2 @@
+config
+config.h
diff --git a/busybox/include/applets.h b/busybox/include/applets.h
new file mode 100644 (file)
index 0000000..90d4195
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ * applets.h - a listing of all busybox applets.
+ *
+ * If you write a new applet, you need to add an entry to this list to make
+ * busybox aware of it.
+ *
+ * It is CRUCIAL that this listing be kept in ascii order, otherwise the binary
+ * search lookup contributed by Gaute B Strokkenes stops working. If you value
+ * your kneecaps, you'll be sure to *make sure* that any changes made to this
+ * file result in the listing remaining in ascii order. You have been warned.
+ */
+
+#undef APPLET
+#undef APPLET_ODDNAME
+#undef APPLET_NOUSAGE
+
+
+#if defined(PROTOTYPES)
+  #define APPLET(a,b,c,d) extern int b(int argc, char **argv);
+  #define APPLET_NOUSAGE(a,b,c,d) extern int b(int argc, char **argv);
+  #define APPLET_ODDNAME(a,b,c,d,e) extern int b(int argc, char **argv);
+  extern const char usage_messages[];
+#elif defined(MAKE_USAGE)
+  #ifdef CONFIG_FEATURE_VERBOSE_USAGE
+    #define APPLET(a,b,c,d) a##_trivial_usage "\n\n" a##_full_usage "\0"
+    #define APPLET_NOUSAGE(a,b,c,d) "\b\0"
+    #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\n\n" e##_full_usage "\0"
+  #else
+    #define APPLET(a,b,c,d) a##_trivial_usage "\0"
+    #define APPLET_NOUSAGE(a,b,c,d) "\b\0"
+    #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\0"
+  #endif
+#elif defined(MAKE_LINKS)
+#  define APPLET(a,b,c,d) LINK c a
+#  define APPLET_NOUSAGE(a,b,c,d) LINK c a
+#  define APPLET_ODDNAME(a,b,c,d,e) LINK c a
+#else
+  const struct BB_applet applets[] = {
+  #define APPLET(a,b,c,d) {#a,b,c,d},
+  #define APPLET_NOUSAGE(a,b,c,d) {a,b,c,d},
+  #define APPLET_ODDNAME(a,b,c,d,e) {a,b,c,d},
+#endif
+
+#ifdef CONFIG_INSTALL_NO_USR
+#define _BB_DIR_USR_BIN _BB_DIR_BIN
+#define _BB_DIR_USR_SBIN _BB_DIR_SBIN
+#endif
+
+
+
+#ifdef CONFIG_TEST
+       APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ADDGROUP
+       APPLET(addgroup, addgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ADDUSER
+       APPLET(adduser, adduser_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ADJTIMEX
+       APPLET(adjtimex, adjtimex_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_AR
+       APPLET(ar, ar_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ARPING
+       APPLET(arping, arping_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ASH
+       APPLET_NOUSAGE("ash", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_AWK
+       APPLET(awk, awk_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_BASENAME
+       APPLET(basename, basename_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_BUNZIP2
+       APPLET(bunzip2, bunzip2_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+       APPLET_NOUSAGE("busybox", busybox_main, _BB_DIR_BIN, _BB_SUID_MAYBE)
+#ifdef CONFIG_BUNZIP2
+       APPLET(bzcat, bunzip2_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CAL
+       APPLET(cal, cal_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CAT
+       APPLET(cat, cat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CHGRP
+       APPLET(chgrp, chgrp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CHMOD
+       APPLET(chmod, chmod_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CHOWN
+       APPLET(chown, chown_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CHROOT
+       APPLET(chroot, chroot_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CHVT
+       APPLET(chvt, chvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CLEAR
+       APPLET(clear, clear_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CMP
+       APPLET(cmp, cmp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CP
+       APPLET(cp, cp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CPIO
+       APPLET(cpio, cpio_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CROND
+       APPLET(crond, crond_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_CRONTAB
+       APPLET(crontab, crontab_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
+#endif
+#ifdef CONFIG_CUT
+       APPLET(cut, cut_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DATE
+       APPLET(date, date_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DC
+       APPLET(dc, dc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DD
+       APPLET(dd, dd_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DEALLOCVT
+       APPLET(deallocvt, deallocvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DELGROUP
+       APPLET(delgroup, delgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DELUSER
+       APPLET(deluser, deluser_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DEVFSD
+       APPLET(devfsd, devfsd_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DF
+       APPLET(df, df_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DIRNAME
+       APPLET(dirname, dirname_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DMESG
+       APPLET(dmesg, dmesg_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DOS2UNIX
+       APPLET(dos2unix, dos2unix_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DPKG
+       APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DPKG_DEB
+       APPLET_ODDNAME("dpkg-deb", dpkg_deb_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb)
+#endif
+#ifdef CONFIG_DU
+       APPLET(du, du_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DUMPKMAP
+       APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_DUMPLEASES
+        APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ECHO
+       APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
+       APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ENV
+       APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_EXPR
+       APPLET(expr, expr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FALSE
+       APPLET(false, false_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FBSET
+       APPLET(fbset, fbset_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FDFLUSH
+       APPLET(fdflush, fdflush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FDFORMAT
+       APPLET(fdformat, fdformat_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FDISK
+       APPLET(fdisk, fdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#if defined(CONFIG_FEATURE_GREP_FGREP_ALIAS)
+       APPLET_NOUSAGE("fgrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FIND
+       APPLET(find, find_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FOLD
+       APPLET(fold, fold_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FREE
+       APPLET(free, free_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FREERAMDISK
+       APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FSCK_MINIX
+       APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix)
+#endif
+#ifdef CONFIG_FTPGET
+       APPLET(ftpget, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FTPPUT
+       APPLET(ftpput, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_GETOPT
+       APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_GETTY
+       APPLET(getty, getty_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_GREP
+       APPLET(grep, grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_GUNZIP
+       APPLET(gunzip, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_GZIP
+       APPLET(gzip, gzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HALT
+       APPLET(halt, halt_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HDPARM
+       APPLET(hdparm, hdparm_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HEAD
+       APPLET(head, head_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HEXDUMP
+       APPLET(hexdump, hexdump_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HOSTID
+       APPLET(hostid, hostid_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HOSTNAME
+       APPLET(hostname, hostname_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HTTPD
+       APPLET(httpd, httpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HUSH
+       APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_HWCLOCK
+       APPLET(hwclock, hwclock_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ID
+       APPLET(id, id_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IFCONFIG
+       APPLET(ifconfig, ifconfig_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IFUPDOWN
+       APPLET(ifdown, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IFUPDOWN
+       APPLET(ifup, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_INETD
+       APPLET(inetd, inetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_INIT
+       APPLET(init, init_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_INSMOD
+       APPLET(insmod, insmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_INSTALL
+       APPLET(install, install_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IP
+       APPLET(ip, ip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IPADDR
+       APPLET(ipaddr, ipaddr_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IPCALC
+       APPLET(ipcalc, ipcalc_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IPLINK
+       APPLET(iplink, iplink_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IPROUTE
+       APPLET(iproute, iproute_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_IPTUNNEL
+       APPLET(iptunnel, iptunnel_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_KILL
+       APPLET(kill, kill_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_KILLALL
+       APPLET(killall, kill_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_KLOGD
+       APPLET(klogd, klogd_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LASH
+       APPLET(lash, lash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LAST
+       APPLET(last, last_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LENGTH
+       APPLET(length, length_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_FEATURE_INITRD
+       APPLET_NOUSAGE("linuxrc", init_main, _BB_DIR_ROOT, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LN
+       APPLET(ln, ln_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LOADFONT
+       APPLET(loadfont, loadfont_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LOADKMAP
+       APPLET(loadkmap, loadkmap_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LOGGER
+       APPLET(logger, logger_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LOGIN
+       APPLET(login, login_main, _BB_DIR_BIN, _BB_SUID_ALWAYS)
+#endif
+#ifdef CONFIG_LOGNAME
+       APPLET(logname, logname_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LOGREAD
+       APPLET(logread, logread_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LOSETUP
+       APPLET(losetup, losetup_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LS
+       APPLET(ls, ls_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_LSMOD
+       APPLET(lsmod, lsmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MAKEDEVS
+       APPLET(makedevs, makedevs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MD5SUM
+       APPLET(md5sum, md5sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MESG
+       APPLET(mesg, mesg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MKDIR
+       APPLET(mkdir, mkdir_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MKFIFO
+       APPLET(mkfifo, mkfifo_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MKFS_MINIX
+       APPLET_ODDNAME("mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, mkfs_minix)
+#endif
+#ifdef CONFIG_MKNOD
+       APPLET(mknod, mknod_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MKSWAP
+       APPLET(mkswap, mkswap_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MKTEMP
+       APPLET(mktemp, mktemp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MODPROBE
+       APPLET(modprobe, modprobe_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MORE
+       APPLET(more, more_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MOUNT
+       APPLET(mount, mount_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MSH
+       APPLET_NOUSAGE("msh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MT
+       APPLET(mt, mt_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_MV
+       APPLET(mv, mv_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_NAMEIF
+       APPLET(nameif, nameif_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_NC
+       APPLET(nc, nc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_NETSTAT
+       APPLET(netstat, netstat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_NSLOOKUP
+       APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_OD
+       APPLET(od, od_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_OPENVT
+       APPLET(openvt, openvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PASSWD
+       APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
+#endif
+#ifdef CONFIG_PATCH
+       APPLET(patch, patch_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PIDOF
+       APPLET(pidof, pidof_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PING
+       APPLET(ping, ping_main, _BB_DIR_BIN, _BB_SUID_MAYBE)
+#endif
+#ifdef CONFIG_PING6
+       APPLET(ping6, ping6_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PIPE_PROGRESS
+       APPLET_NOUSAGE("pipe_progress", pipe_progress_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PIVOT_ROOT
+       APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_POWEROFF
+       APPLET(poweroff, poweroff_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PRINTF
+       APPLET(printf, printf_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PS
+       APPLET(ps, ps_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_PWD
+       APPLET(pwd, pwd_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RDATE
+       APPLET(rdate, rdate_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_READLINK
+       APPLET(readlink, readlink_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_REALPATH
+       APPLET(realpath, realpath_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_REBOOT
+       APPLET(reboot, reboot_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RENICE
+       APPLET(renice, renice_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RESET
+       APPLET(reset, reset_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RM
+       APPLET(rm, rm_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RMDIR
+       APPLET(rmdir, rmdir_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RMMOD
+       APPLET(rmmod, rmmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_ROUTE
+       APPLET(route, route_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RPM
+       APPLET(rpm, rpm_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RPM2CPIO
+       APPLET(rpm2cpio, rpm2cpio_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_RUN_PARTS
+       APPLET_ODDNAME("run-parts", run_parts_main, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts)
+#endif
+#ifdef CONFIG_RX
+       APPLET(rx, rx_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SED
+       APPLET(sed, sed_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SEQ
+       APPLET(seq, seq_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SETKEYCODES
+       APPLET(setkeycodes, setkeycodes_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#if defined(CONFIG_FEATURE_SH_IS_ASH) && defined(CONFIG_ASH)
+       APPLET_NOUSAGE("sh", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#elif defined(CONFIG_FEATURE_SH_IS_HUSH) && defined(CONFIG_HUSH)
+       APPLET_NOUSAGE("sh", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#elif defined(CONFIG_FEATURE_SH_IS_LASH) && defined(CONFIG_LASH)
+       APPLET_NOUSAGE("sh", lash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#elif defined(CONFIG_FEATURE_SH_IS_MSH) && defined(CONFIG_MSH)
+       APPLET_NOUSAGE("sh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SHA1SUM
+       APPLET(sha1sum, sha1sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SLEEP
+       APPLET(sleep, sleep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SORT
+       APPLET(sort, sort_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_START_STOP_DAEMON
+    APPLET_ODDNAME("start-stop-daemon", start_stop_daemon_main, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon)
+#endif
+#ifdef CONFIG_STRINGS
+       APPLET(strings, strings_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_STTY
+       APPLET(stty, stty_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SU
+       APPLET(su, su_main, _BB_DIR_BIN, _BB_SUID_ALWAYS)
+#endif
+#ifdef CONFIG_SULOGIN
+       APPLET(sulogin, sulogin_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SWAPONOFF
+       APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SWAPONOFF
+       APPLET(swapon, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SYNC
+       APPLET(sync, sync_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SYSCTL
+       APPLET(sysctl, sysctl_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_SYSLOGD
+       APPLET(syslogd, syslogd_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TAIL
+       APPLET(tail, tail_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TAR
+       APPLET(tar, tar_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TEE
+       APPLET(tee, tee_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TELNET
+       APPLET(telnet, telnet_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TELNETD
+       APPLET(telnetd, telnetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TEST
+       APPLET(test, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TFTP
+       APPLET(tftp, tftp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TIME
+       APPLET(time, time_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TOP
+       APPLET(top, top_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TOUCH
+       APPLET(touch, touch_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TR
+       APPLET(tr, tr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TRACEROUTE
+       APPLET(traceroute, traceroute_main, _BB_DIR_USR_BIN, _BB_SUID_MAYBE)
+#endif
+#ifdef CONFIG_TRUE
+       APPLET(true, true_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_TTY
+       APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UDHCPC
+       APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UDHCPD
+        APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UMOUNT
+       APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UNAME
+       APPLET(uname, uname_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UNCOMPRESS
+       APPLET(uncompress, uncompress_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UNIQ
+       APPLET(uniq, uniq_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UNIX2DOS
+       APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UNZIP
+       APPLET(unzip, unzip_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UPTIME
+       APPLET(uptime, uptime_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_USLEEP
+       APPLET(usleep, usleep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UUDECODE
+       APPLET(uudecode, uudecode_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_UUENCODE
+       APPLET(uuencode, uuencode_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_VCONFIG
+       APPLET(vconfig, vconfig_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_VI
+       APPLET(vi, vi_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_VLOCK
+       APPLET(vlock, vlock_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
+#endif
+#ifdef CONFIG_WATCH
+       APPLET(watch, watch_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_WATCHDOG
+       APPLET(watchdog, watchdog_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_WC
+       APPLET(wc, wc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_WGET
+       APPLET(wget, wget_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_WHICH
+       APPLET(which, which_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_WHO
+       APPLET(who, who_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_WHOAMI
+       APPLET(whoami, whoami_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_XARGS
+       APPLET(xargs, xargs_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_YES
+       APPLET(yes, yes_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
+#ifdef CONFIG_GUNZIP
+       APPLET(zcat, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+#endif
+
+#if !defined(PROTOTYPES) && !defined(MAKE_USAGE)
+       { 0,NULL,0 }
+};
+
+#endif
diff --git a/busybox/include/busybox.h b/busybox/include/busybox.h
new file mode 100644 (file)
index 0000000..f6f5759
--- /dev/null
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox main internal header file
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ */
+#ifndef        _BB_INTERNAL_H_
+#define        _BB_INTERNAL_H_    1
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if __GNU_LIBRARY__ < 5
+#ifndef __dietlibc__
+#error "Sorry, libc5 is not supported"
+#endif
+#endif
+
+#ifndef BB_EXTRA_VERSION
+#define BB_BANNER "BusyBox v" BB_VER " (" BB_BT ")"
+#else
+#define BB_BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")"
+#endif
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#include <features.h>
+
+/* Pull in the utility routines from libbb */
+#include "libbb.h"
+
+enum Location {
+       _BB_DIR_ROOT = 0,
+       _BB_DIR_BIN,
+       _BB_DIR_SBIN,
+       _BB_DIR_USR_BIN,
+       _BB_DIR_USR_SBIN
+};
+
+enum SUIDRoot {
+       _BB_SUID_NEVER = 0,
+       _BB_SUID_MAYBE,
+       _BB_SUID_ALWAYS
+};
+
+struct BB_applet {
+       const char *name;
+       int (*main) (int argc, char **argv);
+       enum Location location:4;
+       enum SUIDRoot need_suid:4;
+};
+
+/* From busybox.c */
+extern const struct BB_applet applets[];
+
+/* Automagically pull in all the applet function prototypes and
+ * applet usage strings.  These are all of the form:
+ *             extern int foo_main(int argc, char **argv);
+ *             extern const char foo_usage[];
+ * These are all autogenerated from the set of currently defined applets.
+ */
+#define PROTOTYPES
+#include "applets.h"
+#undef PROTOTYPES
+
+#ifdef CONFIG_FEATURE_BUFFERS_GO_ON_STACK
+#define RESERVE_CONFIG_BUFFER(buffer,len)           char buffer[len]
+#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len]
+#define RELEASE_CONFIG_BUFFER(buffer)      ((void)0)
+#else
+#ifdef CONFIG_FEATURE_BUFFERS_GO_IN_BSS
+#define RESERVE_CONFIG_BUFFER(buffer,len)  static          char buffer[len]
+#define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len]
+#define RELEASE_CONFIG_BUFFER(buffer)      ((void)0)
+#else
+#define RESERVE_CONFIG_BUFFER(buffer,len)           char *buffer=xmalloc(len)
+#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer=xmalloc(len)
+#define RELEASE_CONFIG_BUFFER(buffer)      free (buffer)
+#endif
+#endif
+
+
+#ifndef RB_POWER_OFF
+/* Stop system and switch power off if possible.  */
+#define RB_POWER_OFF   0x4321fedc
+#endif
+
+/* Try to pull in PATH_MAX */
+#include <limits.h>
+
+/* for PATH_MAX on systems that don't have it in limits.h */
+#include <sys/param.h>
+#ifndef PATH_MAX
+#define  PATH_MAX         256
+#endif
+
+#endif                                                 /* _BB_INTERNAL_H_ */
diff --git a/busybox/include/dump.h b/busybox/include/dump.h
new file mode 100644 (file)
index 0000000..3f4b480
--- /dev/null
@@ -0,0 +1,49 @@
+#define        F_IGNORE        0x01            /* %_A */
+#define        F_SETREP        0x02            /* rep count set, not default */
+#define        F_ADDRESS       0x001           /* print offset */
+#define        F_BPAD          0x002           /* blank pad */
+#define        F_C             0x004           /* %_c */
+#define        F_CHAR          0x008           /* %c */
+#define        F_DBL           0x010           /* %[EefGf] */
+#define        F_INT           0x020           /* %[di] */
+#define        F_P             0x040           /* %_p */
+#define        F_STR           0x080           /* %s */
+#define        F_U             0x100           /* %_u */
+#define        F_UINT          0x200           /* %[ouXx] */
+#define        F_TEXT          0x400           /* no conversions */
+
+enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */
+
+typedef struct _pr {
+       struct _pr *nextpr;             /* next print unit */
+       unsigned int 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 int 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;
+
+extern void bb_dump_add(const char *fmt);
+extern int bb_dump_dump (char **argv);
+extern int bb_dump_size(FS * fs);
+
+extern FS *bb_dump_fshead;             /* head of format strings */
+extern int bb_dump_blocksize;                          /* data block size */
+extern int bb_dump_length;                     /* max bytes to read */
+extern enum _vflag bb_dump_vflag;
+extern off_t bb_dump_skip;                      /* bytes to skip */
diff --git a/busybox/include/grp_.h b/busybox/include/grp_.h
new file mode 100644 (file)
index 0000000..b212b0b
--- /dev/null
@@ -0,0 +1,116 @@
+/* Copyright (C) 1991,92,95,96,97,98,99,2000,01 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/*
+ *     POSIX Standard: 9.2.1 Group Database Access     <grp.h>
+ */
+
+
+#if !defined CONFIG_USE_BB_PWD_GRP
+#include <grp.h>
+
+#else
+
+#ifndef        _GRP_H
+#define        _GRP_H  1
+
+
+#include <sys/types.h>
+#include <features.h>
+#include <stdio.h>
+
+
+/* The group structure.         */
+struct group
+{
+    char *gr_name;             /* Group name.  */
+    char *gr_passwd;           /* Password.    */
+    gid_t gr_gid;              /* Group ID.    */
+    char **gr_mem;             /* Member list. */
+};
+
+
+/* Rewind the group-file stream.  */
+extern void setgrent (void);
+
+/* Close the group-file stream.  */
+extern void endgrent (void);
+
+/* Read an entry from the group-file stream, opening it if necessary.  */
+extern struct group *getgrent (void);
+
+/* Read a group entry from STREAM.  */
+extern struct group *fgetgrent (FILE *__stream);
+
+/* Write the given entry onto the given stream.  */
+extern int putgrent (__const struct group *__restrict __p,
+                    FILE *__restrict __f);
+
+/* Search for an entry with a matching group ID.  */
+extern struct group *getgrgid (gid_t __gid);
+
+/* Search for an entry with a matching group name.  */
+extern struct group *getgrnam (__const char *__name);
+
+/* Reentrant versions of some of the functions above.
+
+   PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
+   The interface may change in later versions of this library.  But
+   the interface is designed following the principals used for the
+   other reentrant functions so the chances are good this is what the
+   POSIX people would choose.  */
+
+extern int getgrent_r (struct group *__restrict __resultbuf,
+                      char *__restrict __buffer, size_t __buflen,
+                      struct group **__restrict __result);
+
+/* Search for an entry with a matching group ID.  */
+extern int getgrgid_r (gid_t __gid, struct group *__restrict __resultbuf,
+                      char *__restrict __buffer, size_t __buflen,
+                      struct group **__restrict __result);
+
+/* Search for an entry with a matching group name.  */
+extern int getgrnam_r (__const char *__restrict __name,
+                      struct group *__restrict __resultbuf,
+                      char *__restrict __buffer, size_t __buflen,
+                      struct group **__restrict __result);
+
+/* Read a group entry from STREAM.  This function is not standardized
+   an probably never will.  */
+extern int fgetgrent_r (FILE *__restrict __stream,
+                       struct group *__restrict __resultbuf,
+                       char *__restrict __buffer, size_t __buflen,
+                       struct group **__restrict __result);
+
+/* Set the group set for the current user to GROUPS (N of them).  */
+extern int setgroups (size_t __n, __const gid_t *__groups);
+
+/* Store at most *NGROUPS members of the group set for USER into
+   *GROUPS.  Also include GROUP.  The actual number of groups found is
+   returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.  */
+extern int getgrouplist (__const char *__user, gid_t __group,
+                        gid_t *__groups, int *__ngroups);
+
+/* Initialize the group set for the current user
+   by reading the group database and using all groups
+   of which USER is a member.  Also include GROUP.  */
+extern int initgroups (__const char *__user, gid_t __group);
+
+
+#endif /* grp.h  */
+#endif
diff --git a/busybox/include/inet_common.h b/busybox/include/inet_common.h
new file mode 100644 (file)
index 0000000..afea5de
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ *                      Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III       Mar 12, 2001
+ *
+ * Version:     $Id: inet_common.h,v 1.4 2004/03/10 07:42:37 mjn3 Exp $
+ *
+ */
+
+#include <features.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+
+extern const char bb_INET_default[];    /* = "default" */
+
+/* hostfirst!=0 If we expect this to be a hostname,
+   try hostname database first
+ */
+extern int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst);
+
+
+/* numeric: & 0x8000: default instead of *,
+ *          & 0x4000: host instead of net,
+ *          & 0x0fff: don't resolve
+ */
+extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
+                        int numeric, unsigned int netmask);
+
+extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6);
+extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric);
diff --git a/busybox/include/libbb.h b/busybox/include/libbb.h
new file mode 100644 (file)
index 0000000..93ab537
--- /dev/null
@@ -0,0 +1,487 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox main internal header file
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ */
+#ifndef        __LIBCONFIG_H__
+#define        __LIBCONFIG_H__    1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <termios.h>
+#include <stdint.h>
+
+#include <netdb.h>
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#include <features.h>
+
+#include "config.h"
+#ifdef CONFIG_SELINUX
+#include <proc_secure.h>
+#endif
+
+#include "pwd_.h"
+#include "grp_.h"
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+#include "shadow_.h"
+#endif
+#ifdef CONFIG_FEATURE_SHA1_PASSWORDS
+# include "sha1.h"
+#endif
+
+/* Convenience macros to test the version of gcc. */
+#if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) \
+        ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define __GNUC_PREREQ(maj, min) 0
+#endif
+
+/* __restrict is known in EGCS 1.2 and above. */
+#if !__GNUC_PREREQ (2,92)
+# define __restrict     /* Ignore */
+#endif
+
+/* Some useful definitions */
+#define FALSE   ((int) 0)
+#define TRUE    ((int) 1)
+#define SKIP   ((int) 2)
+
+/* for mtab.c */
+#define MTAB_GETMOUNTPT '1'
+#define MTAB_GETDEVICE  '2'
+
+#define BUF_SIZE        8192
+#define EXPAND_ALLOC    1024
+
+/* Macros for min/max.  */
+#ifndef MIN
+#define        MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+#define        MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+extern void bb_show_usage(void) __attribute__ ((noreturn));
+extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+extern void bb_error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+extern void bb_perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+extern void bb_perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+extern void bb_vherror_msg(const char *s, va_list p);
+extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+
+extern void bb_perror_nomsg_and_die(void) __attribute__ ((noreturn));
+extern void bb_perror_nomsg(void);
+
+/* These two are used internally -- you shouldn't need to use them */
+extern void bb_verror_msg(const char *s, va_list p) __attribute__ ((format (printf, 1, 0)));
+extern void bb_vperror_msg(const char *s, va_list p)  __attribute__ ((format (printf, 1, 0)));
+
+extern const char *bb_mode_string(int mode);
+extern int is_directory(const char *name, int followLinks, struct stat *statBuf);
+
+extern int remove_file(const char *path, int flags);
+extern int copy_file(const char *source, const char *dest, int flags);
+extern ssize_t safe_read(int fd, void *buf, size_t count);
+extern ssize_t bb_full_read(int fd, void *buf, size_t len);
+extern ssize_t safe_write(int fd, const void *buf, size_t count);
+extern ssize_t bb_full_write(int fd, const void *buf, size_t len);
+extern int recursive_action(const char *fileName, int recurse,
+         int followLinks, int depthFirst,
+         int (*fileAction) (const char *fileName, struct stat* statbuf, void* userData),
+         int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData),
+         void* userData);
+
+extern int bb_parse_mode( const char* s, mode_t* theMode);
+extern long bb_xgetlarg(const char *arg, int base, long lower, long upper);
+
+extern unsigned long bb_baud_to_value(speed_t speed);
+extern speed_t bb_value_to_baud(unsigned long value);
+
+extern int get_kernel_revision(void);
+
+extern int get_console_fd(void);
+extern struct mntent *find_mount_point(const char *name, const char *table);
+extern void write_mtab(char* blockDevice, char* directory,
+                                          char* filesystemType, long flags, char* string_flags);
+extern void erase_mtab(const char * name);
+extern long *find_pid_by_name( const char* pidName);
+extern char *find_real_root_device_name(void);
+extern char *bb_get_line_from_file(FILE *file);
+extern char *bb_get_chomped_line_from_file(FILE *file);
+extern int bb_copyfd_size(int fd1, int fd2, const off_t size);
+extern int bb_copyfd_eof(int fd1, int fd2);
+extern void  bb_xprint_and_close_file(FILE *file);
+extern int   bb_xprint_file_by_name(const char *filename);
+extern char  bb_process_escape_sequence(const char **ptr);
+extern char *bb_get_last_path_component(char *path);
+extern FILE *bb_wfopen(const char *path, const char *mode);
+extern FILE *bb_xfopen(const char *path, const char *mode);
+
+//#warning rename?
+extern int   bb_fclose_nonstdin(FILE *f);
+extern void  bb_fflush_stdout_and_exit(int retval) __attribute__ ((noreturn));
+
+extern const char *bb_opt_complementaly;
+extern const struct option *bb_applet_long_options;
+extern unsigned long bb_getopt_ulflags(int argc, char **argv, const char *applet_opts, ...);
+
+//#warning rename?
+extern FILE *bb_wfopen_input(const char *filename);
+
+extern int bb_vfprintf(FILE * __restrict stream, const char * __restrict format,
+                                          va_list arg) __attribute__ ((format (printf, 2, 0)));
+extern int bb_vprintf(const char * __restrict format, va_list arg)
+       __attribute__ ((format (printf, 1, 0)));
+extern int bb_fprintf(FILE * __restrict stream, const char * __restrict format, ...)
+       __attribute__ ((format (printf, 2, 3)));
+extern int bb_printf(const char * __restrict format, ...)
+       __attribute__ ((format (printf, 1, 2)));
+
+//#warning rename to xferror_filename?
+extern void bb_xferror(FILE *fp, const char *fn);
+extern void bb_xferror_stdout(void);
+extern void bb_xfflush_stdout(void);
+
+extern void bb_warn_ignoring_args(int n);
+
+extern void chomp(char *s);
+extern void trim(char *s);
+extern const char *bb_skip_whitespace(const char *);
+
+extern struct BB_applet *find_applet_by_name(const char *name);
+void run_applet_by_name(const char *name, int argc, char **argv);
+
+//#warning is this needed anymore?
+#ifndef DMALLOC
+extern void *xmalloc (size_t size);
+extern void *xrealloc(void *old, size_t size);
+extern void *xcalloc(size_t nmemb, size_t size);
+#endif
+extern char *bb_xstrdup (const char *s);
+extern char *bb_xstrndup (const char *s, int n);
+extern char *safe_strncpy(char *dst, const char *src, size_t size);
+extern int safe_strtoi(char *arg, int* value);
+extern int safe_strtod(char *arg, double* value);
+extern int safe_strtol(char *arg, long* value);
+extern int safe_strtoul(char *arg, unsigned long* value);
+
+struct suffix_mult {
+       const char *suffix;
+       unsigned int mult;
+};
+
+extern unsigned long bb_xgetularg_bnd_sfx(const char *arg, int base,
+                                                                                 unsigned long lower,
+                                                                                 unsigned long upper,
+                                                                                 const struct suffix_mult *suffixes);
+extern unsigned long bb_xgetularg_bnd(const char *arg, int base,
+                                                                         unsigned long lower,
+                                                                         unsigned long upper);
+extern unsigned long bb_xgetularg10_bnd(const char *arg,
+                                                                               unsigned long lower,
+                                                                               unsigned long upper);
+extern unsigned long bb_xgetularg10(const char *arg);
+
+extern long bb_xgetlarg_bnd_sfx(const char *arg, int base,
+                                                               long lower,
+                                                               long upper,
+                                                               const struct suffix_mult *suffixes);
+extern long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes);
+
+
+//#warning pitchable now?
+extern unsigned long bb_xparse_number(const char *numstr,
+               const struct suffix_mult *suffixes);
+
+
+//#warning change names?
+
+/* These parse entries in /etc/passwd and /etc/group.  This is desirable
+ * for BusyBox since we want to avoid using the glibc NSS stuff, which
+ * increases target size and is often not needed embedded systems.  */
+extern long my_getpwnam(const char *name);
+extern long my_getgrnam(const char *name);
+extern char * my_getug(char *buffer, char *idname, long id, int bufsize, char prefix);
+extern char * my_getpwuid(char *name, long uid, int bufsize);
+extern char * my_getgrgid(char *group, long gid, int bufsize);
+extern char *bb_askpass(int timeout, const char * prompt);
+
+extern int device_open(const char *device, int mode);
+
+extern int del_loop(const char *device);
+extern int set_loop(const char *device, const char *file, int offset, int *loopro);
+extern char *find_unused_loop_device (void);
+
+
+#if (__GLIBC__ < 2)
+extern int vdprintf(int d, const char *format, va_list ap);
+#endif
+
+int nfsmount(const char *spec, const char *node, int *flags,
+            char **extra_opts, char **mount_opts, int running_bg);
+
+/* Include our own copy of struct sysinfo to avoid binary compatability
+ * problems with Linux 2.4, which changed things.  Grumble, grumble. */
+struct sysinfo {
+       long uptime;                    /* Seconds since boot */
+       unsigned long loads[3];         /* 1, 5, and 15 minute load averages */
+       unsigned long totalram;         /* Total usable main memory size */
+       unsigned long freeram;          /* Available memory size */
+       unsigned long sharedram;        /* Amount of shared memory */
+       unsigned long bufferram;        /* Memory used by buffers */
+       unsigned long totalswap;        /* Total swap space size */
+       unsigned long freeswap;         /* swap space still available */
+       unsigned short procs;           /* Number of current processes */
+       unsigned short pad;                     /* Padding needed for m68k */
+       unsigned long totalhigh;        /* Total high memory size */
+       unsigned long freehigh;         /* Available high memory size */
+       unsigned int mem_unit;          /* Memory unit size in bytes */
+       char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+};
+extern int sysinfo (struct sysinfo* info);
+
+enum {
+       KILOBYTE = 1024,
+       MEGABYTE = (KILOBYTE*1024),
+       GIGABYTE = (MEGABYTE*1024)
+};
+const char *make_human_readable_str(unsigned long long size,
+               unsigned long block_size, unsigned long display_unit);
+
+int bb_ask_confirmation(void);
+int klogctl(int type, char * b, int len);
+
+char *xgetcwd(char *cwd);
+char *xreadlink(const char *path);
+char *concat_path_file(const char *path, const char *filename);
+char *concat_subpath_file(const char *path, const char *filename);
+char *last_char_is(const char *s, int c);
+
+int read_package_field(const char *package_buffer, char **field_name, char **field_value);
+//#warning yuk!
+char *fgets_str(FILE *file, const char *terminating_string);
+
+extern int uncompress(int fd_in, int fd_out);
+extern int inflate(int in, int out);
+
+extern struct hostent *xgethostbyname(const char *name);
+extern struct hostent *xgethostbyname2(const char *name, int af);
+extern int create_icmp_socket(void);
+extern int create_icmp6_socket(void);
+extern int xconnect(struct sockaddr_in *s_addr);
+extern unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port);
+extern void bb_lookup_host(struct sockaddr_in *s_in, const char *host);
+
+//#warning wrap this?
+char *dirname (char *path);
+
+int bb_make_directory (char *path, long mode, int flags);
+
+const char *u_signal_names(const char *str_sig, int *signo, int startnum);
+char *bb_simplify_path(const char *path);
+
+enum { /* DO NOT CHANGE THESE VALUES!  cp.c depends on them. */
+       FILEUTILS_PRESERVE_STATUS = 1,
+       FILEUTILS_DEREFERENCE = 2,
+       FILEUTILS_RECUR = 4,
+       FILEUTILS_FORCE = 8,
+       FILEUTILS_INTERACTIVE = 16
+};
+
+extern const char *bb_applet_name;
+
+extern const char * const bb_msg_full_version;
+extern const char * const bb_msg_memory_exhausted;
+extern const char * const bb_msg_invalid_date;
+extern const char * const bb_msg_io_error;
+extern const char * const bb_msg_write_error;
+extern const char * const bb_msg_name_longer_than_foo;
+extern const char * const bb_msg_unknown;
+extern const char * const bb_msg_can_not_create_raw_socket;
+extern const char * const bb_msg_perm_denied_are_you_root;
+extern const char * const bb_msg_standard_input;
+extern const char * const bb_msg_standard_output;
+
+extern const char * const bb_path_nologin_file;
+extern const char * const bb_path_passwd_file;
+extern const char * const bb_path_shadow_file;
+extern const char * const bb_path_gshadow_file;
+extern const char * const bb_path_group_file;
+extern const char * const bb_path_securetty_file;
+extern const char * const bb_path_motd_file;
+
+/*
+ * You can change LIBBB_DEFAULT_LOGIN_SHELL, but don`t use,
+ * use bb_default_login_shell and next defines,
+ * if you LIBBB_DEFAULT_LOGIN_SHELL change,
+ * don`t lose change increment constant!
+ */
+#define LIBBB_DEFAULT_LOGIN_SHELL      "-/bin/sh"
+
+extern const char * const bb_default_login_shell;
+/* "/bin/sh" */
+#define DEFAULT_SHELL     (bb_default_login_shell+1)
+/* "sh" */
+#define DEFAULT_SHELL_SHORT_NAME     (bb_default_login_shell+6)
+
+
+extern const char bb_path_mtab_file[];
+
+extern int bb_default_error_retval;
+
+#ifdef CONFIG_FEATURE_DEVFS
+# define CURRENT_VC "/dev/vc/0"
+# define VC_1 "/dev/vc/1"
+# define VC_2 "/dev/vc/2"
+# define VC_3 "/dev/vc/3"
+# define VC_4 "/dev/vc/4"
+# define VC_5 "/dev/vc/5"
+#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
+/* Yes, this sucks, but both SH (including sh64) and H8 have a SCI(F) for their
+   respective serial ports .. as such, we can't use the common device paths for
+   these. -- PFM */
+#  define SC_0 "/dev/ttsc/0"
+#  define SC_1 "/dev/ttsc/1"
+#  define SC_FORMAT "/dev/ttsc/%d"
+#else
+#  define SC_0 "/dev/tts/0"
+#  define SC_1 "/dev/tts/1"
+#  define SC_FORMAT "/dev/tts/%d"
+#endif
+# define VC_FORMAT "/dev/vc/%d"
+# define LOOP_FORMAT "/dev/loop/%d"
+# define FB_0 "/dev/fb/0"
+#else
+# define CURRENT_VC "/dev/tty0"
+# define VC_1 "/dev/tty1"
+# define VC_2 "/dev/tty2"
+# define VC_3 "/dev/tty3"
+# define VC_4 "/dev/tty4"
+# define VC_5 "/dev/tty5"
+#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
+#  define SC_0 "/dev/ttySC0"
+#  define SC_1 "/dev/ttySC1"
+#  define SC_FORMAT "/dev/ttySC%d"
+#else
+#  define SC_0 "/dev/ttyS0"
+#  define SC_1 "/dev/ttyS1"
+#  define SC_FORMAT "/dev/ttyS%d"
+#endif
+# define VC_FORMAT "/dev/tty%d"
+# define LOOP_FORMAT "/dev/loop%d"
+# define FB_0 "/dev/fb0"
+#endif
+
+//#warning put these in .o files
+
+/* The following devices are the same on devfs and non-devfs systems.  */
+#define CURRENT_TTY "/dev/tty"
+#define CONSOLE_DEV "/dev/console"
+
+int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name);
+void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name);
+void reset_ino_dev_hashtable(void);
+
+/* Stupid gcc always includes its own builtin strlen()... */
+extern size_t bb_strlen(const char *string);
+#define strlen(x)   bb_strlen(x)
+
+void bb_xasprintf(char **string_ptr, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+
+
+#define FAIL_DELAY    3
+extern void change_identity ( const struct passwd *pw );
+extern const char *change_identity_e2str ( const struct passwd *pw );
+extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args
+#ifdef CONFIG_SELINUX
+       , security_id_t sid
+#endif
+);
+extern int run_parts(char **args, const unsigned char test_mode, char **env);
+extern int restricted_shell ( const char *shell );
+extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw );
+extern int correct_password ( const struct passwd *pw );
+extern char *pw_encrypt(const char *clear, const char *salt);
+extern struct spwd *pwd_to_spwd(const struct passwd *pw);
+extern int obscure(const char *old, const char *newval, const struct passwd *pwdp);
+
+extern int bb_xopen(const char *pathname, int flags);
+extern ssize_t bb_xread(int fd, void *buf, size_t count);
+extern void bb_xread_all(int fd, void *buf, size_t count);
+extern unsigned char bb_xread_char(int fd);
+
+typedef struct {
+       int pid;
+       char user[9];
+       char state[4];
+       unsigned long rss;
+       int ppid;
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       unsigned pcpu;
+       unsigned long stime, utime;
+#endif
+       char *cmd;
+
+       /* basename of executable file in call to exec(2),
+               size from kernel headers */
+       char short_cmd[16];
+} procps_status_t;
+
+extern procps_status_t * procps_scan(int save_user_arg0
+#ifdef CONFIG_SELINUX
+       , int use_selinux, security_id_t *sid
+#endif
+);
+extern unsigned short compare_string_array(const char *string_array[], const char *key);
+
+extern int my_query_module(const char *name, int which, void **buf, size_t *bufsize, size_t *ret);
+
+typedef struct llist_s {
+       char *data;
+       struct llist_s *link;
+} llist_t;
+extern llist_t *llist_add_to(llist_t *old_head, char *new_item);
+
+extern void print_login_issue(const char *issue_file, const char *tty);
+extern void print_login_prompt(void);
+
+extern void vfork_daemon_rexec(int nochdir, int noclose,
+               int argc, char **argv, char *foreground_opt);
+extern void get_terminal_width_height(int fd, int *width, int *height);
+extern unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *));
+extern void xregcomp(regex_t *preg, const char *regex, int cflags);
+
+#define HASH_SHA1      1
+#define HASH_MD5       2
+extern int hash_fd(int fd, const size_t size, const uint8_t hash_algo, uint8_t *hashval);
+
+#endif /* __LIBCONFIG_H__ */
diff --git a/busybox/include/pwd_.h b/busybox/include/pwd_.h
new file mode 100644 (file)
index 0000000..7215120
--- /dev/null
@@ -0,0 +1,106 @@
+/* Copyright (C) 1991,92,95,96,97,98,99,2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/*
+ *     POSIX Standard: 9.2.2 User Database Access      <pwd.h>
+ */
+
+#if !defined CONFIG_USE_BB_PWD_GRP
+#include <pwd.h>
+
+#else
+
+#ifndef        _PWD_H
+#define        _PWD_H  1
+
+#include <sys/types.h>
+#include <features.h>
+#include <stdio.h>
+
+/* The passwd structure.  */
+struct passwd
+{
+    char *pw_name;             /* Username.  */
+    char *pw_passwd;           /* Password.  */
+    uid_t pw_uid;                      /* User ID.  */
+    gid_t pw_gid;                      /* Group ID.  */
+    char *pw_gecos;            /* Real name.  */
+    char *pw_dir;                      /* Home directory.  */
+    char *pw_shell;            /* Shell program.  */
+};
+
+
+/* Rewind the password-file stream.  */
+extern void setpwent (void);
+
+/* Close the password-file stream.  */
+extern void endpwent (void);
+
+/* Read an entry from the password-file stream, opening it if necessary.  */
+extern struct passwd *getpwent (void);
+
+/* Read an entry from STREAM.  */
+extern struct passwd *fgetpwent (FILE *__stream);
+
+/* Write the given entry onto the given stream.  */
+extern int putpwent (__const struct passwd *__restrict __p,
+                    FILE *__restrict __f);
+
+/* Search for an entry with a matching user ID.  */
+extern struct passwd *getpwuid (uid_t __uid);
+
+/* Search for an entry with a matching username.  */
+extern struct passwd *getpwnam (__const char *__name);
+
+/* Reentrant versions of some of the functions above.
+
+   PLEASE NOTE: the `getpwent_r' function is not (yet) standardized.
+   The interface may change in later versions of this library.  But
+   the interface is designed following the principals used for the
+   other reentrant functions so the chances are good this is what the
+   POSIX people would choose.  */
+
+extern int getpwent_r (struct passwd *__restrict __resultbuf,
+                      char *__restrict __buffer, size_t __buflen,
+                      struct passwd **__restrict __result);
+
+extern int getpwuid_r (uid_t __uid,
+                      struct passwd *__restrict __resultbuf,
+                      char *__restrict __buffer, size_t __buflen,
+                      struct passwd **__restrict __result);
+
+extern int getpwnam_r (__const char *__restrict __name,
+                      struct passwd *__restrict __resultbuf,
+                      char *__restrict __buffer, size_t __buflen,
+                      struct passwd **__restrict __result);
+
+
+/* Read an entry from STREAM.  This function is not standardized and
+   probably never will.  */
+extern int fgetpwent_r (FILE *__restrict __stream,
+                       struct passwd *__restrict __resultbuf,
+                       char *__restrict __buffer, size_t __buflen,
+                       struct passwd **__restrict __result);
+
+/* Re-construct the password-file line for the given uid
+   in the given buffer.  This knows the format that the caller
+   will expect, but this need not be the format of the password file.  */
+extern int getpw (uid_t __uid, char *__buffer);
+
+#endif /* pwd.h  */
+#endif
diff --git a/busybox/include/shadow_.h b/busybox/include/shadow_.h
new file mode 100644 (file)
index 0000000..1b14f0a
--- /dev/null
@@ -0,0 +1,98 @@
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* Declaration of types and functions for shadow password suite.  */
+
+#if !defined CONFIG_USE_BB_SHADOW
+#include <shadow.h>
+#else
+
+#ifndef _SHADOW_H
+#define _SHADOW_H      1
+
+#include <stdio.h>
+
+/* Paths to the user database files.  */
+#ifndef _PATH_SHADOW
+#define        _PATH_SHADOW    "/etc/shadow"
+#endif
+#define        SHADOW _PATH_SHADOW
+
+
+/* Structure of the password file.  */
+struct spwd
+{
+    char *sp_namp;             /* Login name.  */
+    char *sp_pwdp;             /* Encrypted password.  */
+    long int sp_lstchg;                /* Date of last change.  */
+    long int sp_min;           /* Minimum number of days between changes.  */
+    long int sp_max;           /* Maximum number of days between changes.  */
+    long int sp_warn;          /* Number of days to warn user to change
+                                  the password.  */
+    long int sp_inact;         /* Number of days the account may be
+                                  inactive.  */
+    long int sp_expire;                /* Number of days since 1970-01-01 until
+                                  account expires.  */
+    unsigned long int sp_flag; /* Reserved.  */
+};
+
+
+/* Open database for reading.  */
+extern void setspent (void);
+
+/* Close database.  */
+extern void endspent (void);
+
+/* Get next entry from database, perhaps after opening the file.  */
+extern struct spwd *getspent (void);
+
+/* Get shadow entry matching NAME.  */
+extern struct spwd *getspnam (__const char *__name);
+
+/* Read shadow entry from STRING.  */
+extern struct spwd *sgetspent (__const char *__string);
+
+/* Read next shadow entry from STREAM.  */
+extern struct spwd *fgetspent (FILE *__stream);
+
+/* Write line containing shadow password entry to stream.  */
+extern int putspent (__const struct spwd *__p, FILE *__stream);
+
+/* Reentrant versions of some of the functions above.  */
+extern int getspent_r (struct spwd *__result_buf, char *__buffer,
+                      size_t __buflen, struct spwd **__result);
+
+extern int getspnam_r (__const char *__name, struct spwd *__result_buf,
+                      char *__buffer, size_t __buflen,
+                      struct spwd **__result)__THROW;
+
+extern int sgetspent_r (__const char *__string, struct spwd *__result_buf,
+                       char *__buffer, size_t __buflen,
+                       struct spwd **__result);
+
+extern int fgetspent_r (FILE *__stream, struct spwd *__result_buf,
+                       char *__buffer, size_t __buflen,
+                       struct spwd **__result);
+/* Protect password file against multi writers.  */
+extern int lckpwdf (void);
+
+/* Unlock password file.  */
+extern int ulckpwdf (void);
+
+#endif /* shadow.h */
+#endif
diff --git a/busybox/include/unarchive.h b/busybox/include/unarchive.h
new file mode 100644 (file)
index 0000000..1679b73
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef        __UNARCHIVE_H__
+#define        __UNARCHIVE_H__
+
+#define ARCHIVE_PRESERVE_DATE  1
+#define ARCHIVE_CREATE_LEADING_DIRS            2
+#define ARCHIVE_EXTRACT_UNCONDITIONAL  4
+#define ARCHIVE_EXTRACT_QUIET  8
+#define ARCHIVE_EXTRACT_NEWER  16
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "libbb.h"
+
+typedef struct file_headers_s {
+       char *name;
+       char *link_name;
+       off_t size;
+       uid_t uid;
+       gid_t gid;
+       mode_t mode;
+       time_t mtime;
+       dev_t device;
+} file_header_t;
+
+typedef struct archive_handle_s {
+       /* define if the header and data compenent should processed */
+       char (*filter)(struct archive_handle_s *);
+       llist_t *accept;
+       llist_t *reject;
+       llist_t *passed;        /* List of files that have successfully been worked on */
+
+       /* Contains the processed header entry */
+       file_header_t *file_header;
+
+       /* process the header component, e.g. tar -t */
+       void (*action_header)(const file_header_t *);
+
+       /* process the data component, e.g. extract to filesystem */
+       void (*action_data)(struct archive_handle_s *);
+
+       /* How to process any sub archive, e.g. get_header_tar_gz */
+       char (*action_data_subarchive)(struct archive_handle_s *);
+
+       /* Contains the handle to a sub archive */
+       struct archive_handle_s *sub_archive;
+
+       /* The raw stream as read from disk or stdin */
+       int src_fd;
+
+       /* Count the number of bytes processed */
+       off_t offset;
+
+       /* Function that skips data: read_by_char or read_by_skip */
+       void (*seek)(const struct archive_handle_s *archive_handle, const unsigned int amount);
+
+       /* Temporary storage */
+       char *buffer;
+
+       /* Misc. stuff */
+       unsigned char flags;
+
+} archive_handle_t;
+
+extern archive_handle_t *init_handle(void);
+
+extern char filter_accept_all(archive_handle_t *archive_handle);
+extern char filter_accept_list(archive_handle_t *archive_handle);
+extern char filter_accept_list_reassign(archive_handle_t *archive_handle);
+extern char filter_accept_reject_list(archive_handle_t *archive_handle);
+
+extern void unpack_ar_archive(archive_handle_t *ar_archive);
+
+extern void data_skip(archive_handle_t *archive_handle);
+extern void data_extract_all(archive_handle_t *archive_handle);
+extern void data_extract_to_stdout(archive_handle_t *archive_handle);
+extern void data_extract_to_buffer(archive_handle_t *archive_handle);
+
+extern void header_skip(const file_header_t *file_header);
+extern void header_list(const file_header_t *file_header);
+extern void header_verbose_list(const file_header_t *file_header);
+
+extern void check_header_gzip(int src_fd);
+
+extern char get_header_ar(archive_handle_t *archive_handle);
+extern char get_header_cpio(archive_handle_t *archive_handle);
+extern char get_header_tar(archive_handle_t *archive_handle);
+extern char get_header_tar_bz2(archive_handle_t *archive_handle);
+extern char get_header_tar_gz(archive_handle_t *archive_handle);
+
+extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount);
+extern void seek_by_char(const archive_handle_t *archive_handle, const unsigned int amount);
+
+extern void archive_xread_all(const archive_handle_t *archive_handle, void *buf, const size_t count);
+extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count);
+
+extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary);
+extern const llist_t *find_list_entry(const llist_t *list, const char *filename);
+
+extern int uncompressStream(int src_fd, int dst_fd);
+extern void inflate_init(unsigned int bufsize);
+extern int inflate_unzip(int in, int out);
+extern int inflate_gunzip(int in, int out);
+
+extern int open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd));
+
+
+#endif
diff --git a/busybox/include/usage.h b/busybox/include/usage.h
new file mode 100644 (file)
index 0000000..377eb10
--- /dev/null
@@ -0,0 +1,2885 @@
+#ifndef __BB_USAGE_H__
+#define __BB_USAGE_H__
+
+#define addgroup_trivial_usage \
+       "[-g GID] group_name [user_name]"
+#define addgroup_full_usage \
+       "Adds a group to the system\n\n" \
+       "Options:\n" \
+       "\t-g GID\t\tspecify gid"
+
+#define adduser_trivial_usage \
+       "[OPTIONS] user_name"
+#define adduser_full_usage \
+       "Adds a user to the system\n\n" \
+       "Options:\n" \
+       "\t-h DIR\t\tAssign home directory DIR\n" \
+       "\t-g GECOS\tAssign gecos field GECOS\n" \
+       "\t-s SHELL\tAssign login shell SHELL\n" \
+       "\t-G\t\tAdd the user to existing group GROUP\n" \
+       "\t-S\t\tcreate a system user (ignored)\n" \
+       "\t-D\t\tDo not assign a password (logins still possible via ssh)\n" \
+       "\t-H\t\tDo not create the home directory"
+
+#define adjtimex_trivial_usage \
+       "[-q] [-o offset] [-f frequency] [-p timeconstant] [-t tick]"
+#define adjtimex_full_usage \
+       "Reads and optionally sets system timebase parameters.\n" \
+       "See adjtimex(2).\n\n" \
+       "Options:\n" \
+       "\t-q\t\tquiet mode - do not print\n" \
+       "\t-o offset\ttime offset, microseconds\n" \
+       "\t-f frequency\tfrequency adjust, integer kernel units (65536 is 1ppm)\n" \
+       "\t\t\t(positive values make the system clock run fast)\n" \
+       "\t-t tick\t\tmicroseconds per tick, usually 10000\n" \
+       "\t-p timeconstant"
+
+#define ar_trivial_usage \
+       "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
+#define ar_full_usage \
+       "Extract or list FILES from an ar archive.\n\n" \
+       "Options:\n" \
+       "\t-o\t\tpreserve original dates\n" \
+       "\t-p\t\textract to stdout\n" \
+       "\t-t\t\tlist\n" \
+       "\t-x\t\textract\n" \
+       "\t-v\t\tverbosely list files processed"
+
+#define arping_trivial_usage \
+       "[-fqbDUA] [-c count] [-w timeout] [-I device] [-s sender] target\n"
+#define arping_full_usage \
+       "Ping hosts by ARP requests/replies.\n\n" \
+       "Options:\n" \
+       "\t-f\t\tQuit on first ARP reply\n" \
+       "\t-q\t\tBe quiet\n" \
+       "\t-b\t\tKeep broadcasting, don't go unicast\n" \
+       "\t-D\t\tDuplicated address detection mode\n" \
+       "\t-U\t\tUnsolicited ARP mode, update your neighbours\n" \
+       "\t-A\t\tARP answer mode, update your neighbours\n" \
+       "\t-c count\tStop after sending count ARP request packets\n" \
+       "\t-w timeout\tTime to wait for ARP reply, in seconds\n" \
+       "\t-I device\tOutgoing interface name, default is eth0\n" \
+       "\t-s sender\tSet specific sender IP address\n" \
+       "\ttarget\t\tTarget IP address of ARP request"
+
+#define ash_trivial_usage \
+       "[FILE]...\n" \
+       "or: ash -c command [args]...\n"
+#define ash_full_usage \
+       "The ash shell (command interpreter)"
+
+#define awk_trivial_usage \
+       "[OPTION]... [program-text] [FILE ...]"
+#define awk_full_usage \
+       "Options:\n" \
+       "\t-v var=val\t\tassign value 'val' to variable 'var'\n" \
+       "\t-F sep\t\tuse 'sep' as field separator\n" \
+       "\t-f progname\t\tread program source from file 'progname'"
+
+#define basename_trivial_usage \
+       "FILE [SUFFIX]"
+#define basename_full_usage \
+       "Strips directory path and suffixes from FILE.\n" \
+       "If specified, also removes any trailing SUFFIX."
+#define basename_example_usage \
+       "$ basename /usr/local/bin/foo\n" \
+       "foo\n" \
+       "$ basename /usr/local/bin/\n" \
+       "bin\n" \
+       "$ basename /foo/bar.txt .txt\n" \
+       "bar"
+
+#define bunzip2_trivial_usage \
+       "[OPTION]... [FILE]"
+#define bunzip2_full_usage \
+       "Uncompress FILE (or standard input if FILE is '-' or omitted).\n\n" \
+       "Options:\n" \
+       "\t-c\tWrite output to standard output\n" \
+       "\t-f\tForce"
+
+#define bzcat_trivial_usage \
+       "FILE"
+#define bzcat_full_usage \
+       "Uncompress to stdout."
+
+#define cal_trivial_usage \
+       "[-jy] [[month] year]"
+#define cal_full_usage \
+       "Display a calendar.\n" \
+       "\nOptions:\n" \
+       "\t-j\tUse julian dates.\n" \
+       "\t-y\tDisplay the entire year."
+
+#define cat_trivial_usage \
+       "[-u] [FILE]..."
+#define cat_full_usage \
+       "Concatenates FILE(s) and prints them to stdout.\n\n" \
+       "Options:\n" \
+       "\t-u\tignored since unbuffered i/o is always used"
+#define cat_example_usage \
+       "$ cat /proc/uptime\n" \
+       "110716.72 17.67"
+
+#define chgrp_trivial_usage \
+       "[OPTION]... GROUP FILE..."
+#define chgrp_full_usage \
+       "Change the group membership of each FILE to GROUP.\n" \
+       "\nOptions:\n" \
+       "\t-R\tChanges files and directories recursively."
+#define chgrp_example_usage \
+       "$ ls -l /tmp/foo\n" \
+       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n" \
+       "$ chgrp root /tmp/foo\n" \
+       "$ ls -l /tmp/foo\n" \
+       "-r--r--r--    1 andersen root            0 Apr 12 18:25 /tmp/foo\n"
+
+#define chmod_trivial_usage \
+       "[-R] MODE[,MODE]... FILE..."
+#define chmod_full_usage \
+       "Each MODE is one or more of the letters ugoa, one of the\n" \
+       "symbols +-= and one or more of the letters rwxst.\n\n" \
+       "Options:\n" \
+       "\t-R\tChanges files and directories recursively."
+#define chmod_example_usage \
+       "$ ls -l /tmp/foo\n" \
+       "-rw-rw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n" \
+       "$ chmod u+x /tmp/foo\n" \
+       "$ ls -l /tmp/foo\n" \
+       "-rwxrw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo*\n" \
+       "$ chmod 444 /tmp/foo\n" \
+       "$ ls -l /tmp/foo\n" \
+       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
+
+#define chown_trivial_usage \
+       "[ -Rh ]...  OWNER[<.|:>[GROUP]] FILE..."
+#define chown_full_usage \
+       "Change the owner and/or group of each FILE to OWNER and/or GROUP.\n" \
+       "\nOptions:\n" \
+       "\t-R\tChanges files and directories recursively.\n" \
+       "\t-h\tDo not dereference symbolic links."
+#define chown_example_usage \
+       "$ ls -l /tmp/foo\n" \
+       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n" \
+       "$ chown root /tmp/foo\n" \
+       "$ ls -l /tmp/foo\n" \
+       "-r--r--r--    1 root     andersen        0 Apr 12 18:25 /tmp/foo\n" \
+       "$ chown root.root /tmp/foo\n" \
+       "ls -l /tmp/foo\n" \
+       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
+
+#define chroot_trivial_usage \
+       "NEWROOT [COMMAND...]"
+#define chroot_full_usage \
+       "Run COMMAND with root directory set to NEWROOT."
+#define chroot_example_usage \
+       "$ ls -l /bin/ls\n" \
+       "lrwxrwxrwx    1 root     root          12 Apr 13 00:46 /bin/ls -> /BusyBox\n" \
+       "# mount /dev/hdc1 /mnt -t minix\n" \
+       "# chroot /mnt\n" \
+       "# ls -l /bin/ls\n" \
+       "-rwxr-xr-x    1 root     root        40816 Feb  5 07:45 /bin/ls*\n"
+
+#define chvt_trivial_usage \
+       "N"
+#define chvt_full_usage \
+       "Changes the foreground virtual terminal to /dev/ttyN"
+
+#define clear_trivial_usage \
+       ""
+#define clear_full_usage \
+       "Clear screen."
+
+#define cmp_trivial_usage \
+       "[-l] [-s] FILE1 [FILE2]"
+#define cmp_full_usage \
+       "Compare files.  Compares FILE1 vs stdin if FILE2 is not specified.\n\n" \
+       "Options:\n" \
+       "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \
+       "\t\t  for all differing bytes.\n" \
+       "\t-s\tquiet mode - do not print"
+
+#define cp_trivial_usage \
+       "[OPTION]... SOURCE DEST"
+#define cp_full_usage \
+       "Copies SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n" \
+       "\n" \
+       "\t-a\tSame as -dpR\n" \
+       "\t-d\tPreserves links\n" \
+       "\t-p\tPreserves file attributes if possible\n" \
+       "\t-f\tforce (implied; ignored) - always set\n" \
+       "\t-i\tinteractive, prompt before overwrite\n" \
+       "\t-R,-r\tCopies directories recursively"
+
+#define cpio_trivial_usage \
+       "-[dimtuv][F cpiofile]"
+#define cpio_full_usage \
+       "Extract or list files from a cpio archive\n" \
+       "Main operation mode:\n" \
+       "\td\t\tmake leading directories\n" \
+       "\ti\t\textract\n" \
+       "\tm\t\tpreserve mtime\n" \
+       "\tt\t\tlist\n" \
+       "\tv\t\tverbose\n" \
+       "\tu\t\tunconditional overwrite\n" \
+       "\tF\t\tinput from file"
+
+#define crond_trivial_usage \
+       "-d[#] -c <crondir> -f -b"
+#define crond_full_usage \
+       "\t-d [#] -l [#] -S -L logfile -f -b -c dir\n" \
+       "\t-d num\tdebug level\n" \
+       "\t-l num\tlog level (8 - default)\n" \
+       "\t-S\tlog to syslogd (default)\n" \
+       "\t-L file\tlog to file\n" \
+       "\t-f\trun in fordeground\n" \
+       "\t-b\trun in background (default)\n" \
+       "\t-c dir\tworking dir"
+
+#define crontab_trivial_usage \
+       "[-c dir] {file|-}|[-u|-l|-e|-d user]"
+#define crontab_full_usage \
+       "\tfile <opts>  replace crontab from file\n" \
+       "\t-    <opts>  replace crontab from stdin\n" \
+       "\t-u user      specify user\n" \
+       "\t-l [user]    list crontab for user\n" \
+       "\t-e [user]    edit crontab for user\n" \
+       "\t-d [user]    delete crontab for user\n" \
+       "\t-c dir       specify crontab directory"
+
+
+#define cut_trivial_usage \
+       "[OPTION]... [FILE]..."
+#define cut_full_usage \
+       "Prints selected fields from each input FILE to standard output.\n\n" \
+       "Options:\n" \
+       "\t-b LIST\t\tOutput only bytes from LIST\n" \
+       "\t-c LIST\t\tOutput only characters from LIST\n" \
+       "\t-d CHAR\t\tUse CHAR instead of tab as the field delimiter\n" \
+       "\t-s\t\tOutput only the lines containing delimiter\n" \
+       "\t-f N\t\tPrint only these fields\n" \
+       "\t-n\t\tIgnored"
+#define cut_example_usage \
+       "$ echo "Hello world" | cut -f 1 -d ' '\n" \
+       "Hello\n" \
+       "$ echo "Hello world" | cut -f 2 -d ' '\n" \
+       "world\n"
+
+#ifdef CONFIG_FEATURE_DATE_ISOFMT
+#define USAGE_DATE_ISOFMT(a) a
+#else
+#define USAGE_DATE_ISOFMT(a)
+#endif
+
+#define date_trivial_usage \
+       "[OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]"
+#define date_full_usage \
+       "Displays the current time in the given FORMAT, or sets the system date.\n" \
+       "\nOptions:\n" \
+       "\t-R\t\tOutputs RFC-822 compliant date string\n" \
+       "\t-d STRING\tDisplays time described by STRING, not `now'\n" \
+       USAGE_DATE_ISOFMT("\t-I[TIMESPEC]\tOutputs an ISO-8601 compliant date/time string.\n" \
+       "\t\t\tTIMESPEC=`date' (or missing) for date only,\n" \
+       "\t\t\t`hours', `minutes', or `seconds' for date and,\n" \
+       "\t\t\ttime to the indicated precision.\n") \
+       "\t-s\t\tSets time described by STRING\n" \
+       "\t-r FILE\t\tDisplays the last modification time of FILE\n" \
+       "\t-u\t\tPrints or sets Coordinated Universal Time"
+#define date_example_usage \
+       "$ date\n" \
+       "Wed Apr 12 18:52:41 MDT 2000\n"
+
+#define dc_trivial_usage \
+       "expression ..."
+#define dc_full_usage \
+       "This is a Tiny RPN calculator that understands the\n" \
+       "following operations: +, add, -, sub, *, mul, /, div, %, mod, "\
+       "**, exp, and, or, not, eor.\n" \
+       "For example: 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16.\n" \
+       "\nOptions:\n" \
+       "p - Prints the value on the top of the stack, without altering the stack.\n" \
+       "f - Prints the entire contents of the stack without altering anything.\n" \
+       "o - Pops the value off the top of the stack and uses it to set the output radix.\n" \
+       "    Only 10 and 16 are supported."
+#define dc_example_usage \
+       "$ dc 2 2 + p\n" \
+       "4\n" \
+       "$ dc 8 8 \\* 2 2 + / p\n" \
+       "16\n" \
+       "$ dc 0 1 and p\n" \
+       "0\n" \
+       "$ dc 0 1 or p\n" \
+       "1\n" \
+       "$ echo 72 9 div 8 mul p | dc\n" \
+       "64\n"
+
+#define dd_trivial_usage \
+       "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \
+       "\t  [seek=N] [conv=notrunc|noerror|sync]"
+#define dd_full_usage \
+       "Copy a file, converting and formatting according to options\n\n" \
+       "\tif=FILE\t\tread from FILE instead of stdin\n" \
+       "\tof=FILE\t\twrite to FILE instead of stdout\n" \
+       "\tbs=N\t\tread and write N bytes at a time\n" \
+       "\tcount=N\t\tcopy only N input blocks\n" \
+       "\tskip=N\t\tskip N input blocks\n" \
+       "\tseek=N\t\tskip N output blocks\n" \
+       "\tconv=notrunc\tdon't truncate output file\n" \
+       "\tconv=noerror\tcontinue after read errors\n" \
+       "\tconv=sync\tpad blocks with zeros\n" \
+       "\n" \
+       "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \
+       "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)."
+#define dd_example_usage \
+       "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" \
+       "4+0 records in\n" \
+       "4+0 records out\n"
+
+#define deallocvt_trivial_usage \
+       "[N]"
+#define deallocvt_full_usage \
+        "Deallocate unused virtual terminal /dev/ttyN"
+
+#define delgroup_trivial_usage \
+       "GROUP"
+#define delgroup_full_usage \
+        "Deletes group GROUP from the system"
+
+#define deluser_trivial_usage \
+       "USER"
+#define deluser_full_usage \
+        "Deletes user USER from the system"
+
+#ifdef CONFIG_DEVFSD_FG_NP
+  #define USAGE_DEVFSD_FG_NP(a) a
+#else
+  #define USAGE_DEVFSD_FG_NP(a)
+#endif
+
+#define devfsd_trivial_usage \
+       "mntpnt [-v]"\
+       USAGE_DEVFSD_FG_NP("[-fg][-np]" )
+#define devfsd_full_usage \
+       "Optional daemon for managing devfs permissions and old device name symlinks.\n" \
+       "\nOptions:\n" \
+       "\tmntpnt\tThe mount point where devfs is mounted.\n\n" \
+       "\t-v\tPrint the protocol version numbers for devfsd\n" \
+       "\t\tand the kernel-side protocol version and exits." \
+       USAGE_DEVFSD_FG_NP( "\n\n\t-fg\tRun the daemon in the foreground.\n\n" \
+       "\t-np\tExit  after  parsing  the configuration file\n" \
+       "\t\tand processing synthetic REGISTER events.\n" \
+       "\t\tDo not poll for events.")
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+  #define USAGE_HUMAN_READABLE(a) a
+  #define USAGE_NOT_HUMAN_READABLE(a)
+#else
+  #define USAGE_HUMAN_READABLE(a)
+  #define USAGE_NOT_HUMAN_READABLE(a) a
+#endif
+#define df_trivial_usage \
+       "[-" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILESYSTEM ...]"
+#define df_full_usage \
+       "Print the filesystem space used and space available.\n\n" \
+       "Options:\n" \
+       USAGE_HUMAN_READABLE( \
+       "\n\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n" \
+       "\t-m\tprint sizes in megabytes\n" \
+       "\t-k\tprint sizes in kilobytes(default)") USAGE_NOT_HUMAN_READABLE( \
+       "\n\t-k\tprint sizes in kilobytes(compatibility)")
+#define df_example_usage \
+       "$ df\n" \
+       "Filesystem           1k-blocks      Used Available Use% Mounted on\n" \
+       "/dev/sda3              8690864   8553540    137324  98% /\n" \
+       "/dev/sda1                64216     36364     27852  57% /boot\n" \
+       "$ df /dev/sda3\n" \
+       "Filesystem           1k-blocks      Used Available Use% Mounted on\n" \
+       "/dev/sda3              8690864   8553540    137324  98% /\n"
+
+#define dirname_trivial_usage \
+       "FILENAME"
+#define dirname_full_usage \
+       "Strips non-directory suffix from FILENAME"
+#define dirname_example_usage \
+       "$ dirname /tmp/foo\n" \
+       "/tmp\n" \
+       "$ dirname /tmp/foo/\n" \
+       "/tmp\n"
+
+#define dmesg_trivial_usage \
+       "[-c] [-n LEVEL] [-s SIZE]"
+#define dmesg_full_usage \
+       "Prints or controls the kernel ring buffer\n\n" \
+       "Options:\n" \
+       "\t-c\t\tClears the ring buffer's contents after printing\n" \
+       "\t-n LEVEL\tSets console logging level\n" \
+       "\t-s SIZE\t\tUse a buffer of size SIZE"
+
+#define dos2unix_trivial_usage \
+       "[option] [FILE]"
+#define dos2unix_full_usage \
+       "Converts FILE from dos format to unix format.  When no option\n" \
+       "is given, the input is converted to the opposite output format.\n" \
+       "When no file is given, uses stdin for input and stdout for output.\n\n" \
+       "Options:\n" \
+       "\t-u\toutput will be in UNIX format\n" \
+       "\t-d\toutput will be in DOS format"
+
+#define dpkg_trivial_usage \
+       "[-ilCPru] [-F option] package_name"
+#define dpkg_full_usage \
+       "dpkg is a utility to install, remove and manage Debian packages.\n\n" \
+       "Options:\n" \
+       "\t-i\t\tInstall the package\n" \
+       "\t-l\t\tList of installed packages\n" \
+       "\t-C\t\tConfigure an unpackaged package\n" \
+       "\t-F depends\tIgnore depency problems\n" \
+       "\t-P\t\tPurge all files of a package\n" \
+       "\t-r\t\tRemove all but the configuration files for a package\n" \
+       "\t-u\t\tUnpack a package, but don't configure it"
+
+#define dpkg_deb_trivial_usage \
+       "[-cefxX] FILE [argument]"
+#define dpkg_deb_full_usage \
+       "Perform actions on Debian packages (.debs)\n\n" \
+       "Options:\n" \
+       "\t-c\tList contents of filesystem tree\n" \
+       "\t-e\tExtract control files to [argument] directory\n" \
+       "\t-f\tDisplay control field name starting with [argument]\n" \
+       "\t-x\tExtract packages filesystem tree to directory\n" \
+       "\t-X\tVerbose extract"
+#define dpkg_deb_example_usage \
+       "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n"
+
+#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
+#define USAGE_DU_DEFALT_BLOCKSIZE_1k(a) a
+#define USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k(a)
+#else
+#define USAGE_DU_DEFALT_BLOCKSIZE_1k(a)
+#define USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k(a) a
+#endif
+
+#define du_trivial_usage \
+       "[-aHLdclsx" USAGE_HUMAN_READABLE("hm") "k] [FILE]..."
+#define du_full_usage \
+       "Summarizes disk space used for each FILE and/or directory.\n" \
+       "Disk space is printed in units of " \
+       USAGE_DU_DEFALT_BLOCKSIZE_1k("1024") USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k("512") \
+       " bytes.\n\n" \
+       "Options:\n" \
+       "\t-a\tshow sizes of files in addition to directories\n" \
+       "\t-H\tfollow symbolic links that are FILE command line args\n" \
+       "\t-L\tfollow all symbolic links encountered\n" \
+       "\t-d N\tlimit output to directories (and files with -a) of depth < N\n" \
+       "\t-c\toutput a grand total\n" \
+       "\t-l\tcount sizes many times if hard linked\n" \
+       "\t-s\tdisplay only a total for each argument\n" \
+       "\t-x\tskip directories on different filesystems\n" \
+       USAGE_HUMAN_READABLE( \
+       "\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n" \
+       "\t-m\tprint sizes in megabytes\n" ) \
+       "\t-k\tprint sizes in kilobytes" USAGE_DU_DEFALT_BLOCKSIZE_1k("(default)")
+#define du_example_usage \
+       "$ du\n" \
+       "16      ./CVS\n" \
+       "12      ./kernel-patches/CVS\n" \
+       "80      ./kernel-patches\n" \
+       "12      ./tests/CVS\n" \
+       "36      ./tests\n" \
+       "12      ./scripts/CVS\n" \
+       "16      ./scripts\n" \
+       "12      ./docs/CVS\n" \
+       "104     ./docs\n" \
+       "2417    .\n"
+
+#define dumpkmap_trivial_usage \
+       "> keymap"
+#define dumpkmap_full_usage \
+       "Prints out a binary keyboard translation table to standard output."
+#define dumpkmap_example_usage \
+       "$ dumpkmap > keymap\n"
+
+#define dumpleases_trivial_usage \
+       "[-r|-a] [-f LEASEFILE]"
+#define dumpleases_full_usage \
+       "Displays the DHCP leases granted by udhcpd.\n\n" \
+       "Options:\n" \
+       "\t-f,\t--file=FILENAME\tLeases file to load\n" \
+       "\t-r,\t--remaining\tInterpret lease times as time remaing\n" \
+       "\t-a,\t--absolute\tInterpret lease times as expire time"
+
+#ifdef CONFIG_FEATURE_FANCY_ECHO
+  #define USAGE_FANCY_ECHO(a) a
+#else
+  #define USAGE_FANCY_ECHO(a)
+#endif
+
+#define echo_trivial_usage \
+       USAGE_FANCY_ECHO("[-neE] ") "[ARG ...]"
+#define echo_full_usage \
+       "Prints the specified ARGs to stdout\n\n" \
+       USAGE_FANCY_ECHO("Options:\n" \
+       "\t-n\tsuppress trailing newline\n" \
+       "\t-e\tinterpret backslash-escaped characters (i.e., \\t=tab)\n" \
+       "\t-E\tdisable interpretation of backslash-escaped characters")
+#define echo_example_usage \
+       "$ echo "Erik is cool"\n" \
+       "Erik is cool\n" \
+       USAGE_FANCY_ECHO("$  echo -e "Erik\\nis\\ncool"\n" \
+       "Erik\n" \
+       "is\n" \
+       "cool\n" \
+       "$ echo "Erik\\nis\\ncool"\n" \
+       "Erik\\nis\\ncool\n")
+
+#define env_trivial_usage \
+       "[-iu] [-] [name=value]... [command]"
+#define env_full_usage \
+       "Prints the current environment or runs a program after setting\n" \
+       "up the specified environment.\n\n" \
+       "Options:\n" \
+       "\t-, -i\tstart with an empty environment\n" \
+       "\t-u\tremove variable from the environment"
+
+#define expr_trivial_usage \
+       "EXPRESSION"
+#define expr_full_usage \
+       "Prints the value of EXPRESSION to standard output.\n\n" \
+       "EXPRESSION may be:\n" \
+       "\tARG1 |  ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" \
+       "\tARG1 &  ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" \
+       "\tARG1 <  ARG2 ARG1 is less than ARG2\n" \
+       "\tARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" \
+       "\tARG1 =  ARG2 ARG1 is equal to ARG2\n" \
+       "\tARG1 != ARG2 ARG1 is unequal to ARG2\n" \
+       "\tARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" \
+       "\tARG1 >  ARG2 ARG1 is greater than ARG2\n" \
+       "\tARG1 +  ARG2 arithmetic sum of ARG1 and ARG2\n" \
+       "\tARG1 -  ARG2 arithmetic difference of ARG1 and ARG2\n" \
+       "\tARG1 *  ARG2 arithmetic product of ARG1 and ARG2\n" \
+       "\tARG1 /  ARG2 arithmetic quotient of ARG1 divided by ARG2\n" \
+       "\tARG1 %  ARG2 arithmetic remainder of ARG1 divided by ARG2\n" \
+       "\tSTRING : REGEXP             anchored pattern match of REGEXP in STRING\n" \
+       "\tmatch STRING REGEXP         same as STRING : REGEXP\n" \
+       "\tsubstr STRING POS LENGTH    substring of STRING, POS counted from 1\n" \
+       "\tindex STRING CHARS          index in STRING where any CHARS is found,\n" \
+       "\t                            or 0\n" \
+       "\tlength STRING               length of STRING\n" \
+       "\tquote TOKEN                 interpret TOKEN as a string, even if\n" \
+       "\t                            it is a keyword like `match' or an\n" \
+       "\t                            operator like `/'\n" \
+       "\t( EXPRESSION )              value of EXPRESSION\n\n" \
+       "Beware that many operators need to be escaped or quoted for shells.\n" \
+       "Comparisons are arithmetic if both ARGs are numbers, else\n" \
+       "lexicographical.  Pattern matches return the string matched between \n" \
+       "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" \
+       "of characters matched or 0."
+
+#define false_trivial_usage \
+       ""
+#define false_full_usage \
+       "Return an exit code of FALSE (1)."
+#define false_example_usage \
+       "$ false\n" \
+       "$ echo $?\n" \
+       "1\n"
+
+#define fbset_trivial_usage \
+       "[options] [mode]"
+#define fbset_full_usage \
+       "Show and modify frame buffer settings"
+#define fbset_example_usage \
+       "$ fbset\n" \
+       "mode "1024x768-76"\n" \
+       "\t# D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" \
+       "\tgeometry 1024 768 1024 768 16\n" \
+       "\ttimings 12714 128 32 16 4 128 4\n" \
+       "\taccel false\n" \
+       "\trgba 5/11,6/5,5/0,0/0\n" \
+       "endmode\n"
+
+#define fdflush_trivial_usage \
+       "DEVICE"
+#define fdflush_full_usage \
+       "Forces floppy disk drive to detect disk change"
+
+#define fdformat_trivial_usage \
+       "[-n] DEVICE"
+#define fdformat_full_usage \
+       "Low-level formats a floppy disk\n\n" \
+       "Options:\n" \
+       "\t-n\tDon't verify after format"
+
+#define fdisk_trivial_usage \
+       "[-luv] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK"
+#define fdisk_full_usage \
+       "Change partition table\n" \
+       "Options:\n" \
+       "\t-l  List partition table(s)\n" \
+       "\t-u  Give Start and End in sector (instead of cylinder) units\n" \
+       "\t-s PARTITION  Give partition size(s) in blocks\n" \
+       "\t-b 2048: (for certain MO disks) use 2048-byte sectors\n" \
+       "\t-C CYLINDERS  Set the number of cylinders\n" \
+       "\t-H HEADS  Set the number of heads\n" \
+       "\t-S SECTORS  Set the number of sectors\n" \
+       "\t-v  Give fdisk version"
+
+#ifdef CONFIG_FEATURE_FIND_TYPE
+  #define USAGE_FIND_TYPE(a) a
+#else
+  #define USAGE_FIND_TYPE(a)
+#endif
+#ifdef CONFIG_FEATURE_FIND_PERM
+  #define USAGE_FIND_PERM(a) a
+#else
+  #define USAGE_FIND_PERM(a)
+#endif
+#ifdef CONFIG_FEATURE_FIND_MTIME
+  #define USAGE_FIND_MTIME(a) a
+#else
+  #define USAGE_FIND_MTIME(a)
+#endif
+#ifdef CONFIG_FEATURE_FIND_NEWER
+  #define USAGE_FIND_NEWER(a) a
+#else
+  #define USAGE_FIND_NEWER(a)
+#endif
+#ifdef CONFIG_FEATURE_FIND_INUM
+  #define USAGE_FIND_INUM(a) a
+#else
+  #define USAGE_FIND_INUM(a)
+#endif
+
+#define find_trivial_usage \
+       "[PATH...] [EXPRESSION]"
+#define find_full_usage \
+       "Search for files in a directory hierarchy.  The default PATH is\n" \
+       "the current directory; default EXPRESSION is '-print'\n" \
+       "\nEXPRESSION may consist of:\n" \
+       "\t-follow\t\tDereference symbolic links.\n" \
+       "\t-name PATTERN\tFile name (leading directories removed) matches PATTERN.\n" \
+       "\t-print\t\tPrint (default and assumed).\n" \
+       USAGE_FIND_TYPE( \
+       "\n\t-type X\t\tFiletype matches X (where X is one of: f,d,l,b,c,...)" \
+) USAGE_FIND_PERM( \
+       "\n\t-perm PERMS\tPermissions match any of (+NNN); all of (-NNN);\n\t\t\tor exactly (NNN)" \
+) USAGE_FIND_MTIME( \
+       "\n\t-mtime TIME\tModified time is greater than (+N); less than (-N);\n\t\t\tor exactly (N) days" \
+) USAGE_FIND_NEWER( \
+       "\n\t-newer FILE\tModified time is more recent than FILE's" \
+) USAGE_FIND_INUM( \
+       "\n\t-inum N\t\tFile has inode number N")
+#define find_example_usage \
+       "$ find / -name passwd\n" \
+       "/etc/passwd\n"
+
+#define fold_trivial_usage \
+       "[-bsw] [FILE]"
+#define fold_full_usage \
+       "Wrap input lines in each FILE (standard input by default), writing to\n" \
+       "standard output.\n\n" \
+       "Options:\n" \
+       "\t-b\tcount bytes rather than columns\n" \
+       "\t-s\tbreak at spaces\n" \
+       "\t-w\tuse WIDTH columns instead of 80"
+
+#define free_trivial_usage \
+       ""
+#define free_full_usage \
+       "Displays the amount of free and used system memory"
+#define free_example_usage \
+       "$ free\n" \
+       "              total         used         free       shared      buffers\n" \
+       "  Mem:       257628       248724         8904        59644        93124\n" \
+       " Swap:       128516         8404       120112\n" \
+       "Total:       386144       257128       129016\n" \
+
+#define freeramdisk_trivial_usage \
+       "DEVICE"
+#define freeramdisk_full_usage \
+       "Frees all memory used by the specified ramdisk."
+#define freeramdisk_example_usage \
+       "$ freeramdisk /dev/ram2\n"
+
+#define fsck_minix_trivial_usage \
+       "[-larvsmf] /dev/name"
+#define fsck_minix_full_usage \
+       "Performs a consistency check for MINIX filesystems.\n\n" \
+       "Options:\n" \
+       "\t-l\tLists all filenames\n" \
+       "\t-r\tPerform interactive repairs\n" \
+       "\t-a\tPerform automatic repairs\n" \
+       "\t-v\tverbose\n" \
+       "\t-s\tOutputs super-block information\n" \
+       "\t-m\tActivates MINIX-like \"mode not cleared\" warnings\n" \
+       "\t-f\tForce file system check."
+
+#define ftpget_trivial_usage \
+       "[options] remote-host local-file remote-file"
+#define ftpget_full_usage \
+       "Retrieve a remote file via FTP.\n\n" \
+       "Options:\n" \
+       "\t-c, --continue         Continue a previous transfer\n" \
+       "\t-v, --verbose          Verbose\n" \
+       "\t-u, --username         Username to be used\n" \
+       "\t-p, --password         Password to be used\n" \
+       "\t-P, --port             Port number to be used"
+
+#define ftpput_trivial_usage \
+       "[options] remote-host remote-file local-file"
+#define ftpput_full_usage \
+       "Store a local file on a remote machine via FTP.\n\n" \
+       "Options:\n" \
+       "\t-v, --verbose          Verbose\n" \
+       "\t-u, --username         Username to be used\n" \
+       "\t-p, --password         Password to be used\n" \
+       "\t-P, --port             Port number to be used"
+
+#define getopt_trivial_usage \
+       "[OPTIONS]..."
+#define getopt_full_usage \
+       "Parse command options\n" \
+       "\t-a, --alternative            Allow long options starting with single -\n" \
+       "\t-l, --longoptions=longopts   Long options to be recognized\n" \
+       "\t-n, --name=progname          The name under which errors are reported\n" \
+       "\t-o, --options=optstring      Short options to be recognized\n" \
+       "\t-q, --quiet                  Disable error reporting by getopt(3)\n" \
+       "\t-Q, --quiet-output           No normal output\n" \
+       "\t-s, --shell=shell            Set shell quoting conventions\n" \
+       "\t-T, --test                   Test for getopt(1) version\n" \
+       "\t-u, --unquoted               Do not quote the output"
+#define getopt_example_usage \
+        "$ cat getopt.test\n" \
+        "#!/bin/sh\n" \
+        "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \
+        "       -n 'example.busybox' -- "$@"`\n" \
+        "if [ $? != 0 ] ; then  exit 1 ; fi\n" \
+        "eval set -- "$GETOPT"\n" \
+        "while true ; do\n" \
+        " case $1 in\n" \
+        "   -a|--a-long) echo \"Option a\" ; shift ;;\n" \
+        "   -b|--b-long) echo \"Option b, argument `$2'\" ; shift 2 ;;\n" \
+        "   -c|--c-long)\n" \
+        "     case "$2" in\n" \
+        "       \"\") echo \"Option c, no argument\"; shift 2 ;;\n" \
+        "       *)  echo \"Option c, argument `$2'\" ; shift 2 ;;\n" \
+        "     esac ;;\n" \
+        "   --) shift ; break ;;\n" \
+        "   *) echo \"Internal error!\" ; exit 1 ;;\n" \
+        " esac\n" \
+        "done\n"
+
+#define getty_trivial_usage \
+       "[OPTIONS]... baud_rate,... line [termtype]"
+#define getty_full_usage \
+       "Opens a tty, prompts for a login name, then invokes /bin/login\n\n" \
+       "Options:\n" \
+       "\t-h\t\tEnable hardware (RTS/CTS) flow control.\n" \
+       "\t-i\t\tDo not display /etc/issue before running login.\n" \
+       "\t-L\t\tLocal line, so do not do carrier detect.\n" \
+       "\t-m\t\tGet baud rate from modem's CONNECT status message.\n" \
+       "\t-w\t\tWait for a CR or LF before sending /etc/issue.\n" \
+       "\t-n\t\tDo not prompt the user for a login name.\n" \
+       "\t-f issue_file\tDisplay issue_file instead of /etc/issue.\n" \
+       "\t-l login_app\tInvoke login_app instead of /bin/login.\n" \
+       "\t-t timeout\tTerminate after timeout if no username is read.\n" \
+       "\t-I initstring\tSets the init string to send before anything else.\n" \
+       "\t-H login_host\tLog login_host into the utmp file as the hostname."
+
+
+#define grep_trivial_usage \
+       "[-ihHnqvs] PATTERN [FILEs...]"
+#define grep_full_usage \
+       "Search for PATTERN in each FILE or standard input.\n\n" \
+       "Options:\n" \
+       "\t-H\tprefix output lines with filename where match was found\n" \
+       "\t-h\tsuppress the prefixing filename on output\n" \
+       "\t-i\tignore case distinctions\n" \
+       "\t-l\tlist names of files that match\n" \
+       "\t-n\tprint line number with output lines\n" \
+       "\t-q\tbe quiet. Returns 0 if result was found, 1 otherwise\n" \
+       "\t-v\tselect non-matching lines\n" \
+       "\t-s\tsuppress file open/read error messages"
+#define grep_example_usage \
+       "$ grep root /etc/passwd\n" \
+       "root:x:0:0:root:/root:/bin/bash\n" \
+       "$ grep ^[rR]oo. /etc/passwd\n" \
+       "root:x:0:0:root:/root:/bin/bash\n"
+
+#define gunzip_trivial_usage \
+       "[OPTION]... FILE"
+#define gunzip_full_usage \
+       "Uncompress FILE (or standard input if FILE is '-').\n\n" \
+       "Options:\n" \
+       "\t-c\tWrite output to standard output\n" \
+       "\t-f\tForce read when source is a terminal\n" \
+       "\t-t\tTest compressed file integrity"
+#define gunzip_example_usage \
+       "$ ls -la /tmp/BusyBox*\n" \
+       "-rw-rw-r--    1 andersen andersen   557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" \
+       "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" \
+       "$ ls -la /tmp/BusyBox*\n" \
+       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
+
+#define gzip_trivial_usage \
+       "[OPTION]... [FILE]..."
+#define gzip_full_usage \
+       "Compress FILE(s) with maximum compression.\n" \
+       "When FILE is '-' or unspecified, reads standard input.  Implies -c.\n\n" \
+       "Options:\n" \
+       "\t-c\tWrite output to standard output instead of FILE.gz\n" \
+       "\t-d\tDecompress\n" \
+       "\t-f\tForce write when destination is a terminal"
+#define gzip_example_usage \
+       "$ ls -la /tmp/busybox*\n" \
+       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/busybox.tar\n" \
+       "$ gzip /tmp/busybox.tar\n" \
+       "$ ls -la /tmp/busybox*\n" \
+       "-rw-rw-r--    1 andersen andersen   554058 Apr 14 17:49 /tmp/busybox.tar.gz\n"
+
+#define halt_trivial_usage \
+       "[-d<delay>]"
+#define halt_full_usage \
+       "Halt the system.\n" \
+       "Options:\n" \
+       "\t-d\t\tdelay interval for halting."
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+#define USAGE_HDPARM_IDENT(a) a
+#else
+#define USAGE_HDPARM_IDENT(a)
+#endif
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
+#define USAGE_SCAN_HWIF(a) a
+#else
+#define USAGE_SCAN_HWIF(a)
+#endif
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+#define USAGE_UNREGISTER_HWIF(a) a
+#else
+#define USAGE_UNREGISTER_HWIF(a)
+#endif
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
+#define USAGE_DRIVE_RESET(a) a
+#else
+#define USAGE_DRIVE_RESET(a)
+#endif
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+#define USAGE_TRISTATE_HWIF(a) a
+#else
+#define USAGE_TRISTATE_HWIF(a)
+#endif
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
+#define USAGE_GETSET_DMA(a) a
+#else
+#define USAGE_GETSET_DMA(a)
+#endif
+
+#define hdparm_trivial_usage \
+       "[options] [device] .."
+#define hdparm_full_usage \
+       "Options:" \
+       "\t-a   get/set fs readahead\n" \
+       "\t-A   set drive read-lookahead flag (0/1)\n" \
+       "\t-b   get/set bus state (0 == off, 1 == on, 2 == tristate)\n" \
+       "\t-B   set Advanced Power Management setting (1-255)\n" \
+       "\t-c   get/set IDE 32-bit IO setting\n" \
+       "\t-C   check IDE power mode status\n" \
+       USAGE_GETSET_DMA("\t-d   get/set using_dma flag\n") \
+       "\t-D   enable/disable drive defect-mgmt\n" \
+       "\t-f   flush buffer cache for device on exit\n" \
+       "\t-g   display drive geometry\n" \
+       "\t-h   display terse usage information\n" \
+       "\t-i   display drive identification\n" \
+       USAGE_HDPARM_IDENT("\t-I   detailed/current information directly from drive\n") \
+       USAGE_HDPARM_IDENT("\t-Istdin  similar to -I, but wants /proc/ide/" "*" "/hd?/identify as input\n") \
+       "\t-k   get/set keep_settings_over_reset flag (0/1)\n" \
+       "\t-K   set drive keep_features_over_reset flag (0/1)\n" \
+       "\t-L   set drive doorlock (0/1) (removable harddisks only)\n" \
+       "\t-m   get/set multiple sector count\n" \
+       "\t-n   get/set ignore-write-errors flag (0/1)\n" \
+       "\t-p   set PIO mode on IDE interface chipset (0,1,2,3,4,...)\n" \
+       "\t-P   set drive prefetch count\n" \
+       "\t-q   change next setting quietly\n" \
+       "\t-Q   get/set DMA tagged-queuing depth (if supported)\n" \
+       "\t-r   get/set readonly flag (DANGEROUS to set)\n" \
+       USAGE_SCAN_HWIF("\t-R   register an IDE interface (DANGEROUS)\n") \
+       "\t-S   set standby (spindown) timeout\n" \
+       "\t-t   perform device read timings\n" \
+       "\t-T   perform cache read timings\n" \
+       "\t-u   get/set unmaskirq flag (0/1)\n" \
+       USAGE_UNREGISTER_HWIF("\t-U   un-register an IDE interface (DANGEROUS)\n") \
+       "\t-v   defaults; same as -mcudkrag for IDE drives\n" \
+       "\t-V   display program version and exit immediately\n" \
+       USAGE_DRIVE_RESET("\t-w   perform device reset (DANGEROUS)\n") \
+       "\t-W   set drive write-caching flag (0/1) (DANGEROUS)\n" \
+       USAGE_TRISTATE_HWIF("\t-x   tristate device for hotswap (0/1) (DANGEROUS)\n") \
+       "\t-X   set IDE xfer mode (DANGEROUS)\n" \
+       "\t-y   put IDE drive in standby mode\n" \
+       "\t-Y   put IDE drive to sleep\n" \
+       "\t-Z   disable Seagate auto-powersaving mode\n" \
+       "\t-z   re-read partition table"
+
+#ifdef CONFIG_FEATURE_FANCY_HEAD
+#define USAGE_FANCY_HEAD(a) a
+#else
+#define USAGE_FANCY_HEAD(a)
+#endif
+
+#define head_trivial_usage \
+       "[OPTION]... [FILE]..."
+#define head_full_usage \
+       "Print first 10 lines of each FILE to standard output.\n" \
+       "With more than one FILE, precede each with a header giving the\n" \
+       "file name. With no FILE, or when FILE is -, read standard input.\n\n" \
+       "Options:\n" \
+       "\t-n NUM\t\tPrint first NUM lines instead of first 10" \
+       USAGE_FANCY_HEAD( \
+       "\n\t-c NUM\t\toutput the first NUM bytes\n" \
+       "\t-q\t\tnever output headers giving file names\n" \
+       "\t-v\t\talways output headers giving file names" )
+#define head_example_usage \
+       "$ head -n 2 /etc/passwd\n" \
+       "root:x:0:0:root:/root:/bin/bash\n" \
+       "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
+
+#define hexdump_trivial_usage \
+       "[-[bcdefnosvx]] [OPTION] FILE"
+#define hexdump_full_usage \
+       "The hexdump utility is a filter which displays the specified files,\n" \
+       "or the standard input, if no files are specified, in a user specified\n"\
+       "format\n" \
+       "\t-b\t\tOne-byte octal display\n" \
+       "\t-c\t\tOne-byte character display\n" \
+       "\t-d\t\tTwo-byte decimal display\n" \
+       "\t-e FORMAT STRING\n" \
+       "\t-f FORMAT FILE\n" \
+       "\t-n LENGTH\tInterpret only length bytes of input\n" \
+       "\t-o\t\tTwo-byte octal display\n" \
+       "\t-s OFFSET\tSkip offset byte\n" \
+       "\t-v\t\tdisplay all input data\n" \
+       "\t-x\t\tTwo-byte hexadecimal display"
+
+#define hostid_trivial_usage \
+       ""
+#define hostid_full_usage \
+       "Print out a unique 32-bit identifier for the machine."
+
+#define hostname_trivial_usage \
+       "[OPTION] {hostname | -F FILE}"
+#define hostname_full_usage \
+       "Get or set the hostname or DNS domain name. If a hostname is given\n" \
+       "(or FILE with the -F parameter), the host name will be set.\n\n" \
+       "Options:\n" \
+       "\t-s\tShort\n" \
+       "\t-i\tAddresses for the hostname\n" \
+       "\t-d\tDNS domain name\n" \
+       "\t-f\tFully qualified domain name\n" \
+       "\t-F FILE\tUse the contents of FILE to specify the hostname"
+#define hostname_example_usage \
+       "$ hostname\n" \
+       "sage\n"
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  #define USAGE_HTTPD_BASIC_AUTH(a) a
+  #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+    #define USAGE_HTTPD_AUTH_MD5(a) a
+  #else
+    #define USAGE_HTTPD_AUTH_MD5(a)
+  #endif
+#else
+  #define USAGE_HTTPD_BASIC_AUTH(a)
+  #define USAGE_HTTPD_AUTH_MD5(a)
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  #define USAGE_HTTPD_STANDALONE(a)
+  #define USAGE_HTTPD_SETUID(a)
+#else
+  #define USAGE_HTTPD_STANDALONE(a) a
+  #ifdef CONFIG_FEATURE_HTTPD_SETUID
+    #define USAGE_HTTPD_SETUID(a) a
+  #else
+    #define USAGE_HTTPD_SETUID(a)
+  #endif
+#endif
+#define httpd_trivial_usage \
+       "[-c <conf file>]" \
+       USAGE_HTTPD_STANDALONE(" [-p <port>]") \
+       USAGE_HTTPD_SETUID(" [-u user]") \
+       USAGE_HTTPD_BASIC_AUTH(" [-r <realm>]") \
+       USAGE_HTTPD_AUTH_MD5(" [-m pass]") \
+       " [-h home]" \
+       " [-d/-e <string>]"
+#define httpd_full_usage \
+       "Listens for incoming http server requests.\n\n"\
+       "Options:\n" \
+       "\t-c FILE\t\tSpecifies configuration file. (default httpd.conf)\n" \
+       USAGE_HTTPD_STANDALONE("\t-p PORT\tServer port (default 80)\n") \
+       USAGE_HTTPD_SETUID("\t-u USER\tSet uid to USER after listening privileges port\n") \
+       USAGE_HTTPD_BASIC_AUTH("\t-r REALM\tAuthentication Realm for Basic Authentication\n") \
+       USAGE_HTTPD_AUTH_MD5("\t-m PASS\t\tCrypt PASS with md5 algorithm\n") \
+       "\t-h HOME  \tSpecifies http HOME directory (default ./)\n" \
+       "\t-e STRING\tHtml encode STRING\n" \
+       "\t-d STRING\tURL decode STRING"
+
+#define hwclock_trivial_usage \
+       "[-r|--show] [-s|--hctosys] [-w|--systohc] [-l|--localtime] [-u|--utc]"
+#define hwclock_full_usage \
+       "Query and set the hardware clock (RTC)\n\n" \
+       "Options:\n" \
+       "\t-r\tread hardware clock and print result\n" \
+       "\t-s\tset the system time from the hardware clock\n" \
+       "\t-w\tset the hardware clock to the current system time\n" \
+       "\t-u\tthe hardware clock is kept in coordinated universal time\n" \
+       "\t-l\tthe hardware clock is kept in local time"
+
+#ifdef CONFIG_SELINUX
+  #define USAGE_SELINUX(a) a
+#else
+  #define USAGE_SELINUX(a)
+#endif
+
+#define id_trivial_usage \
+       "[OPTIONS]... [USERNAME]"
+#define id_full_usage \
+       "Print information for USERNAME or the current user\n\n" \
+       "Options:\n" \
+       USAGE_SELINUX("\t-c\tprints only the security context\n") \
+       "\t-g\tprints only the group ID\n" \
+       "\t-u\tprints only the user ID\n" \
+       "\t-n\tprint a name instead of a number\n" \
+       "\t-r\tprints the real user ID instead of the effective ID"
+#define id_example_usage \
+       "$ id\n" \
+       "uid=1000(andersen) gid=1000(andersen)\n"
+
+#ifdef CONFIG_FEATURE_IFCONFIG_SLIP
+  #define USAGE_SIOCSKEEPALIVE(a) a
+#else
+  #define USAGE_SIOCSKEEPALIVE(a)
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+  #define USAGE_IFCONFIG_MII(a) a
+#else
+  #define USAGE_IFCONFIG_MII(a)
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+  #define USAGE_IFCONFIG_HW(a) a
+#else
+  #define USAGE_IFCONFIG_HW(a)
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+  #define USAGE_IFCONFIG_OPT_A(a) a
+#else
+  #define USAGE_IFCONFIG_OPT_A(a)
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+  #define USAGE_IPV6(a) a
+#else
+  #define USAGE_IPV6(a)
+#endif
+
+#define ifconfig_trivial_usage \
+       USAGE_IFCONFIG_OPT_A("[-a]") " <interface> [<address>]"
+#define ifconfig_full_usage \
+       "configure a network interface\n\n" \
+       "Options:\n" \
+       USAGE_IPV6("[add <address>[/<prefixlen>]]\n") \
+       USAGE_IPV6("[del <address>[/<prefixlen>]]\n") \
+       "\t[[-]broadcast [<address>]]  [[-]pointopoint [<address>]]\n" \
+       "\t[netmask <address>]  [dstaddr <address>]\n" \
+       USAGE_SIOCSKEEPALIVE("\t[outfill <NN>] [keepalive <NN>]\n") \
+       "\t" USAGE_IFCONFIG_HW("[hw ether <address>]  ") \
+    "[metric <NN>]  [mtu <NN>]\n" \
+       "\t[[-]trailers]  [[-]arp]  [[-]allmulti]\n" \
+       "\t[multicast]  [[-]promisc]  [txqueuelen <NN>]  [[-]dynamic]\n" \
+       USAGE_IFCONFIG_MII("\t[mem_start <NN>]  [io_addr <NN>]  [irq <NN>]\n") \
+       "\t[up|down] ..."
+
+#define ifup_trivial_usage \
+       "<-ahinv> <ifaces...>"
+#define ifup_full_usage \
+       "ifup <options> <ifaces...>\n\n" \
+       "Options:\n" \
+       "\t-h\tthis help\n" \
+       "\t-a\tde/configure all interfaces automatically\n" \
+       "\t-i FILE\tuse FILE for interface definitions\n" \
+       "\t-n\tprint out what would happen, but don't do it\n" \
+       "\t\t\t(note that this option doesn't disable mappings)\n" \
+       "\t-v\tprint out what would happen before doing it\n" \
+       "\t-m\tdon't run any mappings\n" \
+       "\t-f\tforce de/configuration"
+
+#define ifdown_trivial_usage \
+       "<-ahinv> <ifaces...>"
+#define ifdown_full_usage \
+       "ifdown <options> <ifaces...>\n\n" \
+       "Options:\n" \
+       "\t-h\tthis help\n" \
+       "\t-a\tde/configure all interfaces automatically\n" \
+       "\t-i FILE\tuse FILE for interface definitions\n" \
+       "\t-n\tprint out what would happen, but don't do it\n" \
+       "\t\t(note that this option doesn't disable mappings)\n" \
+       "\t-v\tprint out what would happen before doing it\n" \
+       "\t-m\tdon't run any mappings\n" \
+       "\t-f\tforce de/configuration"
+
+#define inetd_trivial_usage \
+       "[-q len] [conf]"
+#define inetd_full_usage \
+       "Listens for network connections and launches programs\n\n" \
+       "Option:\n" \
+       "\t-q\tSets the size of the socket listen queue to\n" \
+       "\t\tthe specified value. Default is 128."
+
+#define init_trivial_usage \
+       ""
+#define init_full_usage \
+       "Init is the parent of all processes."
+#define init_notes_usage \
+"This version of init is designed to be run only by the kernel.\n" \
+"\n" \
+"BusyBox init doesn't support multiple runlevels.  The runlevels field of\n" \
+"the /etc/inittab file is completely ignored by BusyBox init. If you want \n" \
+"runlevels, use sysvinit.\n" \
+"\n" \
+"BusyBox init works just fine without an inittab.  If no inittab is found, \n" \
+"it has the following default behavior:\n" \
+"\n" \
+"      ::sysinit:/etc/init.d/rcS\n" \
+"      ::askfirst:/bin/sh\n" \
+"      ::ctrlaltdel:/sbin/reboot\n" \
+"      ::shutdown:/sbin/swapoff -a\n" \
+"      ::shutdown:/bin/umount -a -r\n" \
+"      ::restart:/sbin/init\n" \
+"\n" \
+"if it detects that /dev/console is _not_ a serial console, it will also run:\n" \
+"\n" \
+"      tty2::askfirst:/bin/sh\n" \
+"      tty3::askfirst:/bin/sh\n" \
+"      tty4::askfirst:/bin/sh\n" \
+"\n" \
+"If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n" \
+"\n" \
+"      <id>:<runlevels>:<action>:<process>\n" \
+"\n" \
+"      <id>:\n" \
+"\n" \
+"              WARNING: This field has a non-traditional meaning for BusyBox init!\n" \
+"              The id field is used by BusyBox init to specify the controlling tty for\n" \
+"              the specified process to run on.  The contents of this field are\n" \
+"              appended to "/dev/" and used as-is.  There is no need for this field to\n" \
+"              be unique, although if it isn't you may have strange results.  If this\n" \
+"              field is left blank, the controlling tty is set to the console.  Also\n" \
+"              note that if BusyBox detects that a serial console is in use, then only\n" \
+"              entries whose controlling tty is either the serial console or /dev/null\n" \
+"              will be run.  BusyBox init does nothing with utmp.  We don't need no\n" \
+"              stinkin' utmp.\n" \
+"\n" \
+"      <runlevels>:\n" \
+"\n" \
+"              The runlevels field is completely ignored.\n" \
+"\n" \
+"      <action>:\n" \
+"\n" \
+"              Valid actions include: sysinit, respawn, askfirst, wait,\n" \
+"              once, restart, ctrlaltdel, and shutdown.\n" \
+"\n" \
+"              The available actions can be classified into two groups: actions\n" \
+"              that are run only once, and actions that are re-run when the specified\n" \
+"              process exits.\n" \
+"\n" \
+"              Run only-once actions:\n" \
+"\n" \
+"                      'sysinit' is the first item run on boot.  init waits until all\n" \
+"                      sysinit actions are completed before continuing.  Following the\n" \
+"                      completion of all sysinit actions, all 'wait' actions are run.\n" \
+"                      'wait' actions, like  'sysinit' actions, cause init to wait until\n" \
+"                      the specified task completes.  'once' actions are asynchronous,\n" \
+"                      therefore, init does not wait for them to complete.  'restart' is\n" \
+"                      the action taken to restart the init process.  By default this should\n" \
+"                      simply run /sbin/init, but can be a script which runs pivot_root or it\n" \
+"                      can do all sorts of other interesting things.  The 'ctrlaltdel' init\n" \
+"                      actions are run when the system detects that someone on the system\n" \
+"                       console has pressed the CTRL-ALT-DEL key combination.  Typically one\n" \
+"                       wants to run 'reboot' at this point to cause the system to reboot.\n" \
+"                      Finally the 'shutdown' action specifies the actions to taken when\n" \
+"                       init is told to reboot.  Unmounting filesystems and disabling swap\n" \
+"                       is a very good here\n" \
+"\n" \
+"              Run repeatedly actions:\n" \
+"\n" \
+"                      'respawn' actions are run after the 'once' actions.  When a process\n" \
+"                      started with a 'respawn' action exits, init automatically restarts\n" \
+"                      it.  Unlike sysvinit, BusyBox init does not stop processes from\n" \
+"                      respawning out of control.  The 'askfirst' actions acts just like\n" \
+"                      respawn, except that before running the specified process it\n" \
+"                      displays the line "Please press Enter to activate this console."\n" \
+"                      and then waits for the user to press enter before starting the\n" \
+"                      specified process.\n" \
+"\n" \
+"              Unrecognized actions (like initdefault) will cause init to emit an\n" \
+"              error message, and then go along with its business.  All actions are\n" \
+"              run in the order they appear in /etc/inittab.\n" \
+"\n" \
+"      <process>:\n" \
+"\n" \
+"              Specifies the process to be executed and its command line.\n" \
+"\n" \
+"Example /etc/inittab file:\n" \
+"\n" \
+"      # This is run first except when booting in single-user mode.\n" \
+"      #\n" \
+"      ::sysinit:/etc/init.d/rcS\n" \
+"      \n" \
+"      # /bin/sh invocations on selected ttys\n" \
+"      #\n" \
+"      # Start an "askfirst" shell on the console (whatever that may be)\n" \
+"      ::askfirst:-/bin/sh\n" \
+"      # Start an "askfirst" shell on /dev/tty2-4\n" \
+"      tty2::askfirst:-/bin/sh\n" \
+"      tty3::askfirst:-/bin/sh\n" \
+"      tty4::askfirst:-/bin/sh\n" \
+"      \n" \
+"      # /sbin/getty invocations for selected ttys\n" \
+"      #\n" \
+"      tty4::respawn:/sbin/getty 38400 tty4\n" \
+"      tty5::respawn:/sbin/getty 38400 tty5\n" \
+"      \n" \
+"      \n" \
+"      # Example of how to put a getty on a serial line (for a terminal)\n" \
+"      #\n" \
+"      #::respawn:/sbin/getty -L ttyS0 9600 vt100\n" \
+"      #::respawn:/sbin/getty -L ttyS1 9600 vt100\n" \
+"      #\n" \
+"      # Example how to put a getty on a modem line.\n" \
+"      #::respawn:/sbin/getty 57600 ttyS2\n" \
+"      \n" \
+"      # Stuff to do when restarting the init process\n" \
+"      ::restart:/sbin/init\n" \
+"      \n" \
+"      # Stuff to do before rebooting\n" \
+"      ::ctrlaltdel:/sbin/reboot\n" \
+"      ::shutdown:/bin/umount -a -r\n" \
+"      ::shutdown:/sbin/swapoff -a\n"
+
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+  #define USAGE_INSMOD_MAP(a) a
+#else
+  #define USAGE_INSMOD_MAP(a)
+#endif
+#define insmod_trivial_usage \
+       "[OPTION]... MODULE [symbol=value]..."
+#define insmod_full_usage \
+       "Loads the specified kernel modules into the kernel.\n\n" \
+       "Options:\n" \
+       "\t-f\tForce module to load into the wrong kernel version.\n" \
+       "\t-k\tMake module autoclean-able.\n" \
+       "\t-v\tverbose output\n"  \
+       "\t-q\tquiet output\n" \
+       "\t-L\tLock to prevent simultaneous loads of a module\n" \
+       USAGE_INSMOD_MAP("\t-m\tOutput load map to stdout\n") \
+       "\t-o NAME\tSet internal module name to NAME\n" \
+       "\t-x\tdo not export externs"
+
+#define install_trivial_usage \
+       "[-cgmops] [sources] <dest|directory>"
+#define install_full_usage \
+       "Copies files and set attributes\n\n" \
+       "Options:\n" \
+       "\t-c\tcopy the file, default\n" \
+       "\t-d\tcreate directories\n" \
+       "\t-g\tset group ownership\n" \
+       "\t-m\tset permission modes\n" \
+       "\t-o\tset ownership\n" \
+       "\t-p\tpreserve date\n" \
+       "\t-s\tstrip symbol tables"
+
+#define ip_trivial_usage \
+       "[ OPTIONS ] { address | link | route | tunnel } { COMMAND | help }"
+#define ip_full_usage \
+       "ip [ OPTIONS ] OBJECT { COMMAND | help }\n" \
+       "where  OBJECT := { link | addr | route | tunnel }\n" \
+       "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }"
+
+#define ipaddr_trivial_usage \
+       "{ {add|del} IFADDR dev STRING | {show|flush}\n" \
+       "\t\t[ dev STRING ] [ to PREFIX ] }"
+#define ipaddr_full_usage \
+       "ipaddr {add|del} IFADDR dev STRING\n" \
+       "ipaddr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n" \
+       "\t\t\t[ to PREFIX ] [ label PATTERN ]\n" \
+       "\t\t\tIFADDR := PREFIX | ADDR peer PREFIX\n" \
+       "\t\t\t[ broadcast ADDR ] [ anycast ADDR ]\n" \
+       "\t\t\t[ label STRING ] [ scope SCOPE-ID ]\n" \
+       "\t\t\tSCOPE-ID := [ host | link | global | NUMBER ]"
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+  #define XUSAGE_IPCALC_FANCY(a) a
+#else
+  #define XUSAGE_IPCALC_FANCY(a)
+#endif
+#define ipcalc_trivial_usage \
+       "[OPTION]... <ADDRESS>[[/]<NETMASK>] [NETMASK]"
+#define ipcalc_full_usage \
+       "Calculate IP network settings from a IP address\n\n" \
+       "Options:\n" \
+       "\t-b\t--broadcast\tDisplay calculated broadcast address.\n" \
+       "\t-n\t--network\tDisplay calculated network address.\n" \
+       "\t-m\t--netmask\tDisplay default netmask for IP." \
+       XUSAGE_IPCALC_FANCY(\
+       "\n\t-p\t--prefix\tDisplay the prefix for IP/NETMASK." \
+       "\t-h\t--hostname\tDisplay first resolved host name.\n" \
+       "\t-s\t--silent\tDon't ever display error messages.")
+
+#define iplink_trivial_usage \
+       "{ set DEVICE { up | down | arp { on | off } | show [ DEVICE ] }"
+#define iplink_full_usage \
+       "iplink set DEVICE { up | down | arp { on | off } |\n" \
+       "\t\t\tdynamic { on | off } |\n" \
+       "\t\t\tmtu MTU }\n" \
+       "\tiplink show [ DEVICE ]"
+
+#define iproute_trivial_usage \
+       "{ list | flush | { add | del | change | append |\n" \
+       "\t\treplace | monitor } ROUTE }"
+#define iproute_full_usage \
+       "iproute { list | flush } SELECTOR\n" \
+       "iproute get ADDRESS [ from ADDRESS iif STRING ]\n" \
+       "\t\t\t[ oif STRING ]  [ tos TOS ]\n" \
+       "\tiproute { add | del | change | append | replace | monitor } ROUTE\n" \
+       "\t\t\tSELECTOR := [ root PREFIX ] [ match PREFIX ] [ proto RTPROTO ]\n" \
+       "\t\t\tROUTE := [ TYPE ] PREFIX [ tos TOS ] [ proto RTPROTO ]"
+
+#define iptunnel_trivial_usage \
+       "{ add | change | del | show } [ NAME ]\n" \
+       "\t\t[ mode { ipip | gre | sit } ]\n" \
+       "\t\t[ remote ADDR ] [ local ADDR ] [ ttl TTL ]"
+#define iptunnel_full_usage \
+       "iptunnel { add | change | del | show } [ NAME ]\n" \
+       "\t\t\t[ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n" \
+       "\t\t\t[ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n" \
+       "\t\t\t[ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]"
+
+#define kill_trivial_usage \
+       "[-signal] process-id [process-id ...]"
+#define kill_full_usage \
+       "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\
+       "Options:\n" \
+       "\t-l\tList all signal names and numbers."
+#define kill_example_usage \
+       "$ ps | grep apache\n" \
+       "252 root     root     S [apache]\n" \
+       "263 www-data www-data S [apache]\n" \
+       "264 www-data www-data S [apache]\n" \
+       "265 www-data www-data S [apache]\n" \
+       "266 www-data www-data S [apache]\n" \
+       "267 www-data www-data S [apache]\n" \
+       "$ kill 252\n"
+
+#define killall_trivial_usage \
+       "[-q] [-signal] process-name [process-name ...]"
+#define killall_full_usage \
+       "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\
+       "Options:\n" \
+       "\t-l\tList all signal names and numbers.\n"\
+       "\t-q\tDo not complain if no processes were killed."
+#define killall_example_usage \
+       "$ killall apache\n"
+
+#define klogd_trivial_usage \
+       "[-c n] [-n]"
+#define klogd_full_usage \
+       "Kernel logger.\n"\
+       "Options:\n"\
+       "\t-c n\tSets the default log level of console messages to n.\n"\
+       "\t-n\tRun as a foreground process."
+
+#define length_trivial_usage \
+       "STRING"
+#define length_full_usage \
+       "Prints out the length of the specified STRING."
+#define length_example_usage \
+       "$ length Hello\n" \
+       "5\n"
+
+#define ln_trivial_usage \
+       "[OPTION] TARGET... LINK_NAME|DIRECTORY"
+#define ln_full_usage \
+       "Create a link named LINK_NAME or DIRECTORY to the specified TARGET\n"\
+       "\nYou may use '--' to indicate that all following arguments are non-options.\n\n" \
+       "Options:\n" \
+       "\t-s\tmake symbolic links instead of hard links\n" \
+       "\t-f\tremove existing destination files\n" \
+       "\t-n\tno dereference symlinks - treat like normal file"
+#define ln_example_usage \
+       "$ ln -s BusyBox /tmp/ls\n" \
+       "$ ls -l /tmp/ls\n" \
+       "lrwxrwxrwx    1 root     root            7 Apr 12 18:39 ls -> BusyBox*\n"
+
+#define loadfont_trivial_usage \
+       "< font"
+#define loadfont_full_usage \
+       "Loads a console font from standard input."
+#define loadfont_example_usage \
+       "$ loadfont < /etc/i18n/fontname\n"
+
+#define loadkmap_trivial_usage \
+       "< keymap"
+#define loadkmap_full_usage \
+       "Loads a binary keyboard translation table from standard input."
+#define loadkmap_example_usage \
+       "$ loadkmap < /etc/i18n/lang-keymap\n"
+
+#define logger_trivial_usage \
+       "[OPTION]... [MESSAGE]"
+#define logger_full_usage \
+       "Write MESSAGE to the system log.  If MESSAGE is omitted, log stdin.\n\n" \
+       "Options:\n" \
+       "\t-s\tLog to stderr as well as the system log.\n" \
+       "\t-t TAG\tLog using the specified tag (defaults to user name).\n" \
+       "\t-p PRIORITY\tEnter the message with the specified priority.\n" \
+       "\t\tThis may be numerical or a ``facility.level'' pair."
+#define logger_example_usage \
+       "$ logger "hello"\n"
+
+#define login_trivial_usage \
+       "[OPTION]... [username] [ENV=VAR ...]"
+#define login_full_usage \
+       "Begin a new session on the system\n\n" \
+       "Options:\n" \
+       "\t-f\tDo not authenticate (user already authenticated)\n" \
+       "\t-h\tName of the remote host for this login.\n" \
+       "\t-p\tPreserve environment."
+
+#define logname_trivial_usage \
+       ""
+#define logname_full_usage \
+       "Print the name of the current user."
+#define logname_example_usage \
+       "$ logname\n" \
+       "root\n"
+
+#define logread_trivial_usage \
+       "[OPTION]..."
+#define logread_full_usage \
+        "Shows the messages from syslogd (using circular buffer).\n\n" \
+       "Options:\n" \
+       "\t-f\t\toutput data as the log grows"
+
+#define losetup_trivial_usage \
+       "[OPTION]... LOOPDEVICE FILE\n" \
+       "or: losetup [OPTION]... -d LOOPDEVICE"
+#define losetup_full_usage \
+       "Associate LOOPDEVICE with FILE.\n\n" \
+       "Options:\n" \
+       "\t-d\t\tDisassociate LOOPDEVICE.\n" \
+       "\t-o OFFSET\tStart OFFSET bytes into FILE."
+
+#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+  #define USAGE_LS_TIMESTAMPS(a) a
+#else
+  #define USAGE_LS_TIMESTAMPS(a)
+#endif
+#ifdef CONFIG_FEATURE_LS_FILETYPES
+  #define USAGE_LS_FILETYPES(a) a
+#else
+  #define USAGE_LS_FILETYPES(a)
+#endif
+#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+  #define USAGE_LS_FOLLOWLINKS(a) a
+#else
+  #define USAGE_LS_FOLLOWLINKS(a)
+#endif
+#ifdef CONFIG_FEATURE_LS_RECURSIVE
+  #define USAGE_LS_RECURSIVE(a) a
+#else
+  #define USAGE_LS_RECURSIVE(a)
+#endif
+#ifdef CONFIG_FEATURE_LS_SORTFILES
+  #define USAGE_LS_SORTFILES(a) a
+#else
+  #define USAGE_LS_SORTFILES(a)
+#endif
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+  #define USAGE_AUTOWIDTH(a) a
+#else
+  #define USAGE_AUTOWIDTH(a)
+#endif
+
+#define ls_trivial_usage \
+       "[-1Aa" USAGE_LS_TIMESTAMPS("c") "Cd" USAGE_LS_TIMESTAMPS("e") USAGE_LS_FILETYPES("F") "iln" USAGE_LS_FILETYPES("p") USAGE_LS_FOLLOWLINKS("L") USAGE_LS_RECURSIVE("R") USAGE_LS_SORTFILES("rS") "s" USAGE_AUTOWIDTH("T") USAGE_LS_TIMESTAMPS("tu") USAGE_LS_SORTFILES("v") USAGE_AUTOWIDTH("w") "x" USAGE_LS_SORTFILES("X") USAGE_HUMAN_READABLE("h") USAGE_NOT_HUMAN_READABLE("") "k" USAGE_SELINUX("K") "] [filenames...]"
+#define ls_full_usage \
+       "List directory contents\n\n" \
+       "Options:\n" \
+       "\t-1\tlist files in a single column\n" \
+       "\t-A\tdo not list implied . and ..\n" \
+       "\t-a\tdo not hide entries starting with .\n" \
+       "\t-C\tlist entries by columns\n" \
+       USAGE_LS_TIMESTAMPS("\t-c\twith -l: show ctime\n") \
+       "\t-d\tlist directory entries instead of contents\n" \
+       USAGE_LS_TIMESTAMPS("\t-e\tlist both full date and full time\n") \
+       USAGE_LS_FILETYPES("\t-F\tappend indicator (one of */=@|) to entries\n") \
+       "\t-i\tlist the i-node for each file\n" \
+       "\t-l\tuse a long listing format\n" \
+       "\t-n\tlist numeric UIDs and GIDs instead of names\n" \
+       USAGE_LS_FILETYPES("\t-p\tappend indicator (one of /=@|) to entries\n") \
+       USAGE_LS_FOLLOWLINKS("\t-L\tlist entries pointed to by symbolic links\n") \
+       USAGE_LS_RECURSIVE("\t-R\tlist subdirectories recursively\n") \
+       USAGE_LS_SORTFILES("\t-r\tsort the listing in reverse order\n") \
+       USAGE_LS_SORTFILES("\t-S\tsort the listing by file size\n") \
+       "\t-s\tlist the size of each file, in blocks\n" \
+       USAGE_AUTOWIDTH("\t-T NUM\tassume Tabstop every NUM columns\n") \
+       USAGE_LS_TIMESTAMPS("\t-t\twith -l: show modification time\n") \
+       USAGE_LS_TIMESTAMPS("\t-u\twith -l: show access time\n") \
+       USAGE_LS_SORTFILES("\t-v\tsort the listing by version\n") \
+       USAGE_AUTOWIDTH("\t-w NUM\tassume the terminal is NUM columns wide\n") \
+       "\t-x\tlist entries by lines instead of by columns\n" \
+       USAGE_LS_SORTFILES("\t-X\tsort the listing by extension\n") \
+       USAGE_HUMAN_READABLE( \
+       "\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n") \
+       USAGE_SELINUX("\t-k\tprint security context\n") \
+       USAGE_SELINUX("\t-K\tprint security context in long format\n")
+
+#define lsmod_trivial_usage \
+       ""
+#define lsmod_full_usage \
+       "List the currently loaded kernel modules."
+
+#define makedevs_trivial_usage \
+       "NAME TYPE MAJOR MINOR FIRST LAST [s]"
+#define makedevs_full_usage \
+       "Creates a range of block or character special files\n\n" \
+       "TYPEs include:\n" \
+       "\tb:\tMake a block (buffered) device.\n" \
+       "\tc or u:\tMake a character (un-buffered) device.\n" \
+       "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes.\n\n" \
+       "FIRST specifies the number appended to NAME to create the first device.\n" \
+       "LAST specifies the number of the last item that should be created.\n" \
+       "If 's' is the last argument, the base device is created as well.\n\n" \
+       "For example:\n" \
+       "\tmakedevs /dev/ttyS c 4 66 2 63   ->  ttyS2-ttyS63\n" \
+       "\tmakedevs /dev/hda b 3 0 0 8 s    ->  hda,hda1-hda8"
+#define makedevs_example_usage \
+       "# makedevs /dev/ttyS c 4 66 2 63\n" \
+       "[creates ttyS2-ttyS63]\n" \
+       "# makedevs /dev/hda b 3 0 0 8 s\n" \
+       "[creates hda,hda1-hda8]\n"
+
+#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+#define USAGE_MD5_SHA1_SUM_CHECK(a) a
+#else
+#define USAGE_MD5_SHA1_SUM_CHECK(a)
+#endif
+
+#define md5sum_trivial_usage \
+       "[OPTION] [FILEs...]" \
+       USAGE_MD5_SHA1_SUM_CHECK("\n   or: md5sum [OPTION] -c [FILE]")
+#define md5sum_full_usage \
+       "Print" USAGE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums.\n\n" \
+       "Options:\n" \
+       "With no FILE, or when FILE is -, read standard input." \
+       USAGE_MD5_SHA1_SUM_CHECK("\n\n" \
+       "\t-c\tcheck MD5 sums against given list\n" \
+       "\nThe following two options are useful only when verifying checksums:\n" \
+       "\t-s\tdon't output anything, status code shows success\n" \
+       "\t-w\twarn about improperly formated MD5 checksum lines")
+#define md5sum_example_usage \
+       "$ md5sum < busybox\n" \
+       "6fd11e98b98a58f64ff3398d7b324003\n" \
+       "$ md5sum busybox\n" \
+       "6fd11e98b98a58f64ff3398d7b324003  busybox\n" \
+       "$ md5sum -c -\n" \
+       "6fd11e98b98a58f64ff3398d7b324003  busybox\n" \
+       "busybox: OK\n" \
+       "^D\n"
+
+#define mesg_trivial_usage \
+       "[y|n]"
+#define mesg_full_usage \
+       "mesg controls write access to your terminal\n" \
+       "\ty\tAllow write access to your terminal.\n" \
+       "\tn\tDisallow write access to your terminal.\n"
+
+#define mkdir_trivial_usage \
+       "[OPTION] DIRECTORY..."
+#define mkdir_full_usage \
+       "Create the DIRECTORY(ies) if they do not already exist\n\n" \
+       "Options:\n" \
+       "\t-m\tset permission mode (as in chmod), not rwxrwxrwx - umask\n" \
+       "\t-p\tno error if existing, make parent directories as needed"
+#define mkdir_example_usage \
+       "$ mkdir /tmp/foo\n" \
+       "$ mkdir /tmp/foo\n" \
+       "/tmp/foo: File exists\n" \
+       "$ mkdir /tmp/foo/bar/baz\n" \
+       "/tmp/foo/bar/baz: No such file or directory\n" \
+       "$ mkdir -p /tmp/foo/bar/baz\n"
+
+#define mkfifo_trivial_usage \
+       "[OPTIONS] name"
+#define mkfifo_full_usage \
+       "Creates a named pipe (identical to 'mknod name p')\n\n" \
+       "Options:\n" \
+       "\t-m\tcreate the pipe using the specified mode (default a=rw)"
+
+#define mkfs_minix_trivial_usage \
+       "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]"
+#define mkfs_minix_full_usage \
+       "Make a MINIX filesystem.\n\n" \
+       "Options:\n" \
+       "\t-c\t\tCheck the device for bad blocks\n" \
+       "\t-n [14|30]\tSpecify the maximum length of filenames\n" \
+       "\t-i INODES\tSpecify the number of inodes for the filesystem\n" \
+       "\t-l FILENAME\tRead the bad blocks list from FILENAME\n" \
+       "\t-v\t\tMake a Minix version 2 filesystem"
+
+#define mknod_trivial_usage \
+       "[OPTIONS] NAME TYPE MAJOR MINOR"
+#define mknod_full_usage \
+       "Create a special file (block, character, or pipe).\n\n" \
+       "Options:\n" \
+       "\t-m\tcreate the special file using the specified mode (default a=rw)\n\n" \
+       "TYPEs include:\n" \
+       "\tb:\tMake a block (buffered) device.\n" \
+       "\tc or u:\tMake a character (un-buffered) device.\n" \
+       "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes."
+#define mknod_example_usage \
+       "$ mknod /dev/fd0 b 2 0\n" \
+       "$ mknod -m 644 /tmp/pipe p\n"
+
+#define mkswap_trivial_usage \
+       "[-c] [-v0|-v1] device [block-count]"
+#define mkswap_full_usage \
+       "Prepare a disk partition to be used as a swap partition.\n\n" \
+       "Options:\n" \
+       "\t-c\t\tCheck for read-ability.\n" \
+       "\t-v0\t\tMake version 0 swap [max 128 Megs].\n" \
+       "\t-v1\t\tMake version 1 swap [big!] (default for kernels >\n\t\t\t2.1.117).\n" \
+       "\tblock-count\tNumber of block to use (default is entire partition)."
+
+#define mktemp_trivial_usage \
+       "[-dq] TEMPLATE"
+#define mktemp_full_usage \
+       "Creates a temporary file with its name based on TEMPLATE.\n" \
+       "TEMPLATE is any name with six `Xs' (i.e., /tmp/temp.XXXXXX).\n\n" \
+       "Options:\n" \
+       "\t-d\t\tMake a directory instead of a file\n" \
+       "\t-q\t\tFail silently if an error occurs"
+#define mktemp_example_usage \
+       "$ mktemp /tmp/temp.XXXXXX\n" \
+       "/tmp/temp.mWiLjM\n" \
+       "$ ls -la /tmp/temp.mWiLjM\n" \
+       "-rw-------    1 andersen andersen        0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
+
+#define modprobe_trivial_usage \
+       "[-knqrsv] [MODULE ...]"
+#define modprobe_full_usage \
+       "Used for high level module loading and unloading.\n\n" \
+       "Options:\n" \
+       "\t-k\tMake module autoclean-able.\n" \
+       "\t-n\tJust show what would be done.\n" \
+       "\t-q\tQuiet output.\n" \
+       "\t-r\tRemove module (stacks) or do autoclean.\n" \
+       "\t-s\tReport via syslog instead of stderr.\n" \
+       "\t-v\tVerbose output."
+#define modprobe_example_usage \
+       "$ modprobe cdrom\n"
+
+#define more_trivial_usage \
+       "[FILE ...]"
+#define more_full_usage \
+       "More is a filter for viewing FILE one screenful at a time."
+#define more_example_usage \
+       "$ dmesg | more\n"
+
+#ifdef CONFIG_FEATURE_MOUNT_LOOP
+  #define USAGE_MOUNT_LOOP(a) a
+#else
+  #define USAGE_MOUNT_LOOP(a)
+#endif
+#ifdef CONFIG_FEATURE_MTAB_SUPPORT
+  #define USAGE_MTAB(a) a
+#else
+  #define USAGE_MTAB(a)
+#endif
+#define mount_trivial_usage \
+       "[flags] DEVICE NODE [-o options,more-options]"
+#define mount_full_usage \
+       "Mount a filesystem.  Autodetection of filesystem type requires the\n" \
+       "/proc filesystem be already mounted.\n\n" \
+       "Flags:\n"  \
+       "\t-a:\t\tMount all filesystems in fstab.\n" \
+       USAGE_MTAB( \
+       "\t-f:\t\t\"Fake\" Add entry to mount table but don't mount it.\n" \
+       "\t-n:\t\tDon't write a mount table entry.\n" \
+       ) \
+       "\t-o option:\tOne of many filesystem options, listed below.\n" \
+       "\t-r:\t\tMount the filesystem read-only.\n" \
+       "\t-t fs-type:\tSpecify the filesystem type.\n" \
+       "\t-w:\t\tMount for reading and writing (default).\n" \
+       "\n" \
+       "Options for use with the \"-o\" flag:\n" \
+       "\tasync/sync:\tWrites are asynchronous / synchronous.\n" \
+       "\tatime/noatime:\tEnable / disable updates to inode access times.\n" \
+       "\tdev/nodev:\tAllow use of special device files / disallow them.\n" \
+       "\texec/noexec:\tAllow use of executable files / disallow them.\n" \
+       USAGE_MOUNT_LOOP( \
+       "\tloop:\t\tMounts a file via loop device.\n" \
+       ) \
+       "\tsuid/nosuid:\tAllow set-user-id-root programs / disallow them.\n" \
+       "\tremount:\tRe-mount a mounted filesystem, changing its flags.\n" \
+       "\tro/rw:\t\tMount for read-only / read-write.\n" \
+       "\tbind:\t\tUse the linux 2.4.x \"bind\" feature.\n" \
+       "\nThere are EVEN MORE flags that are specific to each filesystem.\n" \
+       "You'll have to see the written documentation for those filesystems."
+#define mount_example_usage \
+       "$ mount\n" \
+       "/dev/hda3 on / type minix (rw)\n" \
+       "proc on /proc type proc (rw)\n" \
+       "devpts on /dev/pts type devpts (rw)\n" \
+       "$ mount /dev/fd0 /mnt -t msdos -o ro\n" \
+       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
+
+#define mt_trivial_usage \
+       "[-f device] opcode value"
+#define mt_full_usage \
+       "Control magnetic tape drive operation\n" \
+       "\nAvailable Opcodes:\n\n" \
+       "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" \
+       "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" \
+       "ras3 reset retension rewind rewoffline seek setblk setdensity\n" \
+       "setpart tell unload unlock weof wset"
+
+#define mv_trivial_usage \
+       "[OPTION]... SOURCE DEST\n" \
+       "or: mv [OPTION]... SOURCE... DIRECTORY"
+#define mv_full_usage \
+       "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\n" \
+       "Options:\n" \
+       "\t-f\tdon't prompt before overwriting\n" \
+       "\t-i\tinteractive, prompt before overwrite"
+#define mv_example_usage \
+       "$ mv /tmp/foo /bin/bar\n"
+
+#define nameif_trivial_usage \
+       "[-s] [-c FILE] [{IFNAME MACADDR}]"
+#define nameif_full_usage \
+       "Nameif renaming network interface while it in the down state.\n\n" \
+       "Options:\n" \
+       "\t-c FILE\t\tUse configuration file (default is /etc/mactab)\n" \
+       "\t-s\t\tUse syslog (LOCAL0 facility).\n" \
+       "\tIFNAME MACADDR\tnew_interface_name interface_mac_address"
+#define nameif_example_usage \
+       "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n" \
+       " or\n" \
+       "$ nameif -c /etc/my_mactab_file\n" \
+
+#define nc_trivial_usage \
+       "[OPTIONS] [IP] [port]"
+#define nc_full_usage \
+       "Netcat opens a pipe to IP:port\n\n" \
+       "Options:\n" \
+       "\t-l\t\tlisten mode, for inbound connects\n" \
+       "\t-p PORT\t\tlocal port number\n" \
+       "\t-i SECS\t\tdelay interval for lines sent\n" \
+       "\t-e PROG\t\tprogram to exec after connect (dangerous!)"
+#define nc_example_usage \
+       "$ nc foobar.somedomain.com 25\n" \
+       "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" \
+       "help\n" \
+       "214-Commands supported:\n" \
+       "214-    HELO EHLO MAIL RCPT DATA AUTH\n" \
+       "214     NOOP QUIT RSET HELP\n" \
+       "quit\n" \
+       "221 foobar closing connection\n"
+
+#define netstat_trivial_usage \
+       "[-laenrtuwx]"
+#define netstat_full_usage \
+       "Netstat displays Linux networking information.\n\n" \
+       "Options:\n" \
+       "\t-l display listening server sockets\n" \
+       "\t-a display all sockets (default: connected)\n" \
+       "\t-e display other/more information\n" \
+       "\t-n don't resolve names\n" \
+       "\t-r display routing table\n" \
+       "\t-t tcp sockets\n" \
+       "\t-u udp sockets\n" \
+       "\t-w raw sockets\n" \
+       "\t-x unix sockets"
+
+#define nslookup_trivial_usage \
+       "[HOST] [SERVER]"
+#define nslookup_full_usage \
+       "Queries the nameserver for the IP address of the given HOST\n" \
+       "optionally using a specified DNS server"
+#define nslookup_example_usage \
+       "$ nslookup localhost\n" \
+       "Server:     default\n" \
+       "Address:    default\n" \
+       "\n" \
+       "Name:       debian\n" \
+       "Address:    127.0.0.1\n"
+
+#define od_trivial_usage \
+       "[-aBbcDdeFfHhIiLlOovXx] [FILE]"
+#define od_full_usage \
+       "Write an unambiguous representation, octal bytes by default, of FILE\n"\
+       "to standard output.  With no FILE, or when FILE is -, read standard input."
+
+#define openvt_trivial_usage \
+       "<vtnum> <COMMAND> [ARGS...]"
+#define openvt_full_usage \
+       "Start a command on a new virtual terminal"
+#define openvt_example_usage \
+       "openvt 2 /bin/ash\n"
+
+#ifdef CONFIG_FEATURE_SHA1_PASSWORDS
+  #define PASSWORD_ALG_TYPES(a) a
+#else
+  #define PASSWORD_ALG_TYPES(a)
+#endif
+#define passwd_trivial_usage \
+       "[OPTION] [name]"
+#define passwd_full_usage \
+       "Change a user password. If no name is specified,\n" \
+       "changes the password for the current user.\n" \
+       "Options:\n" \
+       "\t-a\tDefine which algorithm shall be used for the password.\n" \
+       "\t\t\t(Choices: des, md5" \
+       PASSWORD_ALG_TYPES(", sha1") \
+       ")\n\t-d\tDelete the password for the specified user account.\n" \
+       "\t-l\tLocks (disables) the specified user account.\n" \
+       "\t-u\tUnlocks (re-enables) the specified user account."
+
+#define patch_trivial_usage \
+       "[-p<num>]"
+#define patch_full_usage \
+       "[-p<num>]"
+#define patch_example_usage \
+       "$ patch -p1 <example.diff"
+
+#define pidof_trivial_usage \
+       "process-name [OPTION] [process-name ...]"
+#define pidof_full_usage \
+       "Lists the PIDs of all processes with names that match the\n" \
+       "names on the command line.\n" \
+       "Options:\n" \
+       "\t-s\t\tdisplay only a single PID."
+#define pidof_example_usage \
+       "$ pidof init\n" \
+       "1\n"
+
+#ifndef CONFIG_FEATURE_FANCY_PING
+#define ping_trivial_usage "host"
+#define ping_full_usage    "Send ICMP ECHO_REQUEST packets to network hosts"
+#else
+#define ping_trivial_usage \
+       "[OPTION]... host"
+#define ping_full_usage \
+       "Send ICMP ECHO_REQUEST packets to network hosts.\n\n" \
+       "Options:\n" \
+       "\t-c COUNT\tSend only COUNT pings.\n" \
+       "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n" \
+       "\t-q\t\tQuiet mode, only displays output at start\n" \
+       "\t\t\tand when finished."
+#endif
+#define ping_example_usage \
+       "$ ping localhost\n" \
+       "PING slag (127.0.0.1): 56 data bytes\n" \
+       "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n" \
+       "\n" \
+       "--- debian ping statistics ---\n" \
+       "1 packets transmitted, 1 packets received, 0% packet loss\n" \
+       "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
+
+#ifndef CONFIG_FEATURE_FANCY_PING6
+#define ping6_trivial_usage "host"
+#define ping6_full_usage    "Send ICMP ECHO_REQUEST packets to network hosts"
+#else
+#define ping6_trivial_usage \
+       "[OPTION]... host"
+#define ping6_full_usage \
+       "Send ICMP ECHO_REQUEST packets to network hosts.\n\n" \
+       "Options:\n" \
+       "\t-c COUNT\tSend only COUNT pings.\n" \
+       "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n" \
+       "\t-q\t\tQuiet mode, only displays output at start\n" \
+       "\t\t\tand when finished."
+#endif
+#define ping6_example_usage \
+       "$ ping6 ip6-localhost\n" \
+       "PING ip6-localhost (::1): 56 data bytes\n" \
+       "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n" \
+       "\n" \
+       "--- ip6-localhost ping statistics ---\n" \
+       "1 packets transmitted, 1 packets received, 0% packet loss\n" \
+       "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
+
+#define pivot_root_trivial_usage \
+       "NEW_ROOT PUT_OLD"
+#define pivot_root_full_usage \
+       "Move the current root file system to PUT_OLD and make NEW_ROOT\n" \
+       "the new root file system."
+
+#define poweroff_trivial_usage \
+       "[-d<delay>]"
+#define poweroff_full_usage \
+       "Halt the system and request that the kernel shut off the power.\n" \
+       "Options:\n" \
+       "\t-d\t\tdelay interval for shutting off."
+
+#define printf_trivial_usage \
+       "FORMAT [ARGUMENT...]"
+#define printf_full_usage \
+       "Formats and prints ARGUMENT(s) according to FORMAT,\n" \
+       "Where FORMAT controls the output exactly as in C printf."
+#define printf_example_usage \
+       "$ printf "Val=%d\\n" 5\n" \
+       "Val=5\n"
+
+#ifdef CONFIG_SELINUX
+#define USAGE_NONSELINUX(a)
+#else
+#define USAGE_NONSELINUX(a) a
+#endif
+
+#define ps_trivial_usage \
+       ""
+#define ps_full_usage \
+       "Report process status\n" \
+       USAGE_NONSELINUX("\n\tThis version of ps accepts no options.") \
+       USAGE_SELINUX("\nOptions:\n\t-c\tshow SE Linux context")
+
+#define ps_example_usage \
+       "$ ps\n" \
+       "  PID  Uid      Gid State Command\n" \
+       "    1 root     root     S init\n" \
+       "    2 root     root     S [kflushd]\n" \
+       "    3 root     root     S [kupdate]\n" \
+       "    4 root     root     S [kpiod]\n" \
+       "    5 root     root     S [kswapd]\n" \
+       "  742 andersen andersen S [bash]\n" \
+       "  743 andersen andersen S -bash\n" \
+       "  745 root     root     S [getty]\n" \
+       " 2990 andersen andersen R ps\n"
+
+#define pwd_trivial_usage \
+       ""
+#define pwd_full_usage \
+       "Print the full filename of the current working directory."
+#define pwd_example_usage \
+       "$ pwd\n" \
+       "/root\n"
+
+#define rdate_trivial_usage \
+       "[-sp] HOST"
+#define rdate_full_usage \
+       "Get and possibly set the system date and time from a remote HOST.\n\n" \
+       "Options:\n" \
+       "\t-s\tSet the system date and time (default).\n" \
+       "\t-p\tPrint the date and time."
+
+#define readlink_trivial_usage \
+       ""
+#define readlink_full_usage \
+       "Displays the value of a symbolic link."
+
+#define realpath_trivial_usage \
+       "pathname  ..."
+#define realpath_full_usage \
+       "Returns the absolute pathnames of given argument."
+
+#define reboot_trivial_usage \
+       "[-d<delay>]"
+#define reboot_full_usage \
+       "Reboot the system.\n" \
+       "Options:\n" \
+       "\t-d\t\tdelay interval for rebooting."
+
+#define renice_trivial_usage \
+       "priority pid [pid ...]"
+#define renice_full_usage \
+       "Changes priority of running processes. Allowed priorities range\n" \
+       "from 20 (the process runs only when nothing else is running) to 0\n" \
+       "(default priority) to -20 (almost nothing else ever gets to run)."
+
+#define reset_trivial_usage \
+       ""
+#define reset_full_usage \
+       "Resets the screen."
+
+#define rm_trivial_usage \
+       "[OPTION]... FILE..."
+#define rm_full_usage \
+       "Remove (unlink) the FILE(s).  You may use '--' to\n" \
+       "indicate that all following arguments are non-options.\n\n" \
+       "Options:\n" \
+       "\t-i\t\talways prompt before removing each destination\n" \
+       "\t-f\t\tremove existing destinations, never prompt\n" \
+       "\t-r or -R\tremove the contents of directories recursively"
+#define rm_example_usage \
+       "$ rm -rf /tmp/foo\n"
+
+#define rmdir_trivial_usage \
+       "[OPTION]... DIRECTORY..."
+#define rmdir_full_usage \
+       "Remove the DIRECTORY(ies), if they are empty."
+#define rmdir_example_usage \
+       "# rmdir /tmp/foo\n"
+
+#define rmmod_trivial_usage \
+       "[OPTION]... [MODULE]..."
+#define rmmod_full_usage \
+       "Unloads the specified kernel modules from the kernel.\n\n" \
+       "Options:\n" \
+       "\t-a\tRemove all unused modules (recursively)"
+#define rmmod_example_usage \
+       "$ rmmod tulip\n"
+
+#ifdef CONFIG_FEATURE_IPV6
+  #define USAGE_ROUTE_IPV6(a) a
+#else
+  #define USAGE_ROUTE_IPV6(a) "\t"
+#endif
+
+
+#define route_trivial_usage \
+       "[{add|del|delete}]"
+#define route_full_usage \
+       "Edit the kernel's routing tables.\n\n" \
+       "Options:\n" \
+       "\t-n\t\tDont resolve names.\n" \
+       "\t-e\t\tDisplay other/more information.\n" \
+       "\t-A inet" USAGE_ROUTE_IPV6("{6}") "\tSelect address family."
+
+#define rpm_trivial_usage \
+       "-i -q[ildc]p package.rpm"
+#define rpm_full_usage \
+       "Manipulates RPM packages" \
+       "\n\nOptions:" \
+       "\n\t-i Install package" \
+       "\n\t-q Query package" \
+       "\n\t-p Query uninstalled package" \
+       "\n\t-i Show information" \
+       "\n\t-l List contents" \
+       "\n\t-d List documents" \
+       "\n\t-c List config files"
+
+#define rpm2cpio_trivial_usage \
+       "package.rpm"
+#define rpm2cpio_full_usage \
+       "Outputs a cpio archive of the rpm file."
+
+#define run_parts_trivial_usage \
+       "[-t] [-a ARG] [-u MASK] DIRECTORY"
+#define run_parts_full_usage \
+       "Run a bunch of scripts in a directory.\n\n" \
+       "Options:\n" \
+       "\t-t\tPrints what would be run, but does not actually run anything.\n" \
+       "\t-a ARG\tPass ARG as an argument for every program invoked.\n" \
+       "\t-u MASK\tSet the umask to MASK before executing every program."
+
+#define rx_trivial_usage \
+       "FILE"
+#define rx_full_usage \
+       "Receive a file using the xmodem protocol."
+#define rx_example_usage \
+       "$ rx /tmp/foo\n"
+
+#define sed_trivial_usage \
+       "[-efinr] pattern [files...]"
+#define sed_full_usage \
+       "Options:\n" \
+       "\t-e script\tadd the script to the commands to be executed\n" \
+       "\t-f scriptfile\tadd script-file contents to the\n" \
+           "\t\t\tcommands to be executed\n" \
+       "\t-i\t\tedit files in-place\n" \
+       "\t-n\t\tsuppress automatic printing of pattern space\n" \
+       "\t-r\t\tuse extended regular expression syntax\n" \
+       "\n" \
+       "If no -e or -f is given, the first non-option argument is taken as the sed\n"\
+       "script to interpret. All remaining arguments are names of input files; if no\n"\
+       "input files are specified, then the standard input is read.  Source files\n" \
+       "will not be modified unless -i option is given."
+
+#define sed_example_usage \
+       "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \
+       "bar\n"
+
+#define seq_trivial_usage \
+       "[first [increment]] last"
+#define seq_full_usage \
+       "Print numbers from FIRST to LAST, in steps of INCREMENT.\n" \
+       "FIRST, INCREMENT default to 1\n" \
+       "Arguments:\n" \
+       "\tLAST\n" \
+       "\tFIRST\tLAST\n" \
+       "\tFIRST\tINCREMENT\tLAST"
+
+#define setkeycodes_trivial_usage \
+       "SCANCODE KEYCODE ..."
+#define setkeycodes_full_usage \
+       "Set entries into the kernel's scancode-to-keycode map,\n" \
+       "allowing unusual keyboards to generate usable keycodes.\n\n" \
+       "SCANCODE may be either xx or e0xx (hexadecimal),\n" \
+       "and KEYCODE is given in decimal"
+#define setkeycodes_example_usage \
+       "$ setkeycodes e030 127\n"
+
+#define lash_trivial_usage \
+       "[FILE]...\n" \
+       "or: sh -c command [args]..."
+#define lash_full_usage \
+       "The BusyBox LAme SHell (command interpreter)"
+#define lash_notes_usage \
+       "This command does not yet have proper documentation.\n\n" \
+       "Use lash just as you would use any other shell.  It properly handles pipes,\n" \
+       "redirects, job control, can be used as the shell for scripts, and has a\n" \
+       "sufficient set of builtins to do what is needed.  It does not (yet) support\n" \
+       "Bourne Shell syntax.  If you need things like "if-then-else", "while", and such\n" \
+       "use ash or bash.  If you just need a very simple and extremely small shell,\n" \
+       "this will do the job."
+
+#define last_trivial_usage \
+       ""
+#define last_full_usage \
+       "Shows listing of the last users that logged into the system"
+
+#define sha1sum_trivial_usage \
+       "[OPTION] [FILEs...]" \
+       USAGE_MD5_SHA1_SUM_CHECK("\n   or: sha1sum [OPTION] -c [FILE]")
+#define sha1sum_full_usage \
+       "Print" USAGE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums.\n\n" \
+       "Options:\n" \
+       "With no FILE, or when FILE is -, read standard input." \
+       USAGE_MD5_SHA1_SUM_CHECK("\n\n" \
+       "\t-c\tcheck SHA1 sums against given list\n" \
+       "\nThe following two options are useful only when verifying checksums:\n" \
+       "\t-s\tdon't output anything, status code shows success\n" \
+       "\t-w\twarn about improperly formated SHA1 checksum lines")
+
+#ifdef CONFIG_FEATURE_FANCY_SLEEP
+  #define USAGE_FANCY_SLEEP(a) a
+  #define USAGE_NOT_FANCY_SLEEP(a)
+#else
+  #define USAGE_FANCY_SLEEP(a)
+  #define USAGE_NOT_FANCY_SLEEP(a) a
+#endif
+
+#define sleep_trivial_usage \
+       USAGE_FANCY_SLEEP("[") "N" USAGE_FANCY_SLEEP("]...")
+#define sleep_full_usage \
+       USAGE_NOT_FANCY_SLEEP("Pause for N seconds.") \
+       USAGE_FANCY_SLEEP( \
+       "Pause for a time equal to the total of the args given, where each arg can\n" \
+       "\t\thave an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays.")
+#define sleep_example_usage \
+       "$ sleep 2\n" \
+       "[2 second delay results]\n" \
+       USAGE_FANCY_SLEEP("$ sleep 1d 3h 22m 8s\n" \
+       "[98528 second delay results]\n")
+
+#ifdef CONFIG_FEATURE_SORT_UNIQUE
+  #define USAGE_SORT_UNIQUE(a) a
+#else
+  #define USAGE_SORT_UNIQUE(a)
+#endif
+#ifdef CONFIG_FEATURE_SORT_REVERSE
+  #define USAGE_SORT_REVERSE(a) a
+#else
+  #define USAGE_SORT_REVERSE(a)
+#endif
+#define sort_trivial_usage \
+       "[-n" USAGE_SORT_REVERSE("r") USAGE_SORT_UNIQUE("u") "] [FILE]..."
+#define sort_full_usage \
+       "Sorts lines of text in the specified files\n\n"\
+       "Options:\n" \
+       USAGE_SORT_UNIQUE("\t-u\tsuppress duplicate lines\n") \
+       USAGE_SORT_REVERSE("\t-r\tsort in reverse order\n") \
+       "\t-n\tsort numerics"
+#define sort_example_usage \
+       "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" \
+       "a\n" \
+       "b\n" \
+       "c\n" \
+       "d\n" \
+       "e\n" \
+       "f\n"
+
+#define start_stop_daemon_trivial_usage \
+       "[OPTIONS] [--start|--stop] ... [-- arguments...]\n"
+#define start_stop_daemon_full_usage \
+       "Program to start and stop services."\
+       "\n\nOptions:"\
+       "\n\t-S|--start\t\t\tstart"\
+       "\n\t-K|--stop\t\t\tstop"\
+       "\n\t-a|--startas <pathname>\t\tstarts process specified by pathname"\
+       "\n\t-b|--background\t\t\tforce process into background"\
+       "\n\t-u|--user <username>|<uid>\tstop this user's processes"\
+       "\n\t-x|--exec <executable>\t\tprogram to either start or check"\
+       "\n\t-m|--make-pidfile <filename>\tcreate the -p file and enter pid in it"\
+       "\n\t-n|--name <process-name>\tstop processes with this name"\
+       "\n\t-p|--pidfile <pid-file>\t\tsave or load pid using a pid-file"\
+       "\n\t-q|--quiet\t\t\tbe quiet" \
+       "\n\t-s|--signal <signal>\t\tsignal to send (default TERM)"
+
+#define strings_trivial_usage \
+       "[-afo] [-n length] [file ... ]"
+#define strings_full_usage \
+       "Display printable strings in a binary file." \
+       "\n\nOptions:" \
+       "\n\t-a\tScan the whole files (this is the default)."\
+       "\n\t-f\tPrecede each string with the name of the file where it was found." \
+       "\n\t-n N\tSpecifies that at least N characters forms a sequence (default 4)" \
+       "\n\t-o\tEach string is preceded by its decimal offset in the file."
+
+#define stty_trivial_usage \
+       "[-a|g] [-F DEVICE] [SETTING]..."
+#define stty_full_usage \
+       "Without arguments, prints baud rate, line discipline," \
+       "\nand deviations from stty sane." \
+       "\n\nOptions:" \
+       "\n\t-F DEVICE\topen device instead of stdin" \
+       "\n\t-a\t\tprint all current settings in human-readable form" \
+       "\n\t-g\t\tprint in stty-readable form" \
+       "\n\t[SETTING]\tsee manpage"
+
+#define su_trivial_usage \
+       "[OPTION]... [-] [username]"
+#define su_full_usage \
+       "Change user id or become root.\n" \
+       "Options:\n" \
+       "\t-p\tPreserve environment"
+
+#define sulogin_trivial_usage \
+       "[OPTION]... [tty-device]"
+#define sulogin_full_usage \
+       "Single user login\n" \
+       "Options:\n" \
+       "\t-f\tDo not authenticate (user already authenticated)\n" \
+       "\t-h\tName of the remote host for this login.\n" \
+       "\t-p\tPreserve environment."
+
+#define swapoff_trivial_usage \
+       "[OPTION] [DEVICE]"
+#define swapoff_full_usage \
+       "Stop swapping virtual memory pages on DEVICE.\n\n" \
+       "Options:\n" \
+       "\t-a\tStop swapping on all swap devices"
+
+#define swapon_trivial_usage \
+       "[OPTION] [DEVICE]"
+#define swapon_full_usage \
+       "Start swapping virtual memory pages on DEVICE.\n\n" \
+       "Options:\n" \
+       "\t-a\tStart swapping on all swap devices"
+
+#define sync_trivial_usage \
+       ""
+#define sync_full_usage \
+       "Write all buffered filesystem blocks to disk."
+
+
+#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
+       #define USAGE_ROTATE_LOGFILE(a) a
+#else
+       #define USAGE_ROTATE_LOGFILE(a)
+#endif
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+  #define USAGE_REMOTE_LOG(a) a
+#else
+  #define USAGE_REMOTE_LOG(a)
+#endif
+#ifdef CONFIG_FEATURE_IPC_SYSLOG
+  #define USAGE_IPC_LOG(a) a
+#else
+  #define USAGE_IPC_LOG(a)
+#endif
+
+#ifdef CONFIG_SYSCTL
+#define sysctl_trivial_usage \
+       "[OPTIONS]... [VALUE]...\n"
+#define sysctl_full_usage
+       "sysctl - configure kernel parameters at runtime\n\n" \
+       "Options:\n" \
+       "\t-n\tUse this option to disable printing of the key name when printing values.\n" \
+       "\t-w\tUse this option when you want to change a sysctl setting.\n" \
+       "\t-p\tLoad in sysctl settings from the file specified or /etc/sysctl.conf if none given.\n" \
+       "\t-a\tDisplay all values currently available.\n" \
+       "\t-A\tDisplay all values currently available in table form."
+#define sysctl_example_usage
+       "sysctl [-n] variable ...\n" \
+       "sysctl [-n] -w variable=value ...\n" \
+       "sysctl [-n] -a\n" \
+       "sysctl [-n] -p <file>\t(default /etc/sysctl.conf)\n" \
+       "sysctl [-n] -A\n"
+#endif
+
+#define syslogd_trivial_usage \
+       "[OPTION]..."
+#define syslogd_full_usage \
+       "Linux system and kernel logging utility.\n" \
+       "Note that this version of syslogd ignores /etc/syslog.conf.\n\n" \
+       "Options:\n" \
+       "\t-m MIN\t\tMinutes between MARK lines (default=20, 0=off)\n" \
+       "\t-n\t\tRun as a foreground process\n" \
+       "\t-O FILE\t\tUse an alternate log file (default=/var/log/messages)\n" \
+       "\t-S\t\tMake logging output smaller." \
+       USAGE_ROTATE_LOGFILE( \
+       "\n\t-s SIZE\t\tMax size (KB) before rotate (default=200KB, 0=off)\n" \
+       "\t-b NUM\t\tNumber of rotated logs to keep (default=1, max=99, 0=purge)") \
+       USAGE_REMOTE_LOG( \
+       "\n\t-R HOST[:PORT]\tLog to IP or hostname on PORT (default PORT=514/UDP)\n" \
+       "\t-L\t\tLog locally and via network logging (default is network only)") \
+       USAGE_IPC_LOG( \
+       "\n\t-C [size(KiB)]\tLog to a circular buffer (read the buffer using logread)")
+#define syslogd_example_usage \
+       "$ syslogd -R masterlog:514\n" \
+       "$ syslogd -R 192.168.1.1:601\n"
+
+
+#ifndef CONFIG_FEATURE_FANCY_TAIL
+  #define USAGE_UNSIMPLE_TAIL(a)
+#else
+  #define USAGE_UNSIMPLE_TAIL(a) a
+#endif
+#define tail_trivial_usage \
+       "[OPTION]... [FILE]..."
+#define tail_full_usage \
+       "Print last 10 lines of each FILE to standard output.\n" \
+       "With more than one FILE, precede each with a header giving the\n" \
+       "file name. With no FILE, or when FILE is -, read standard input.\n\n" \
+       "Options:\n" \
+       USAGE_UNSIMPLE_TAIL("\t-c N[kbm]\toutput the last N bytes\n") \
+       "\t-n N[kbm]\tprint last N lines instead of last 10\n" \
+       "\t-f\t\toutput data as the file grows" \
+       USAGE_UNSIMPLE_TAIL( "\n\t-q\t\tnever output headers giving file names\n" \
+       "\t-s SEC\t\twait SEC seconds between reads with -f\n" \
+       "\t-v\t\talways output headers giving file names\n\n" \
+       "If the first character of N (bytes or lines) is a '+', output begins with \n" \
+       "the Nth item from the start of each file, otherwise, print the last N items\n" \
+       "in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2)." )
+#define tail_example_usage \
+       "$ tail -n 1 /etc/resolv.conf\n" \
+       "nameserver 10.0.0.1\n"
+
+#ifdef CONFIG_FEATURE_TAR_CREATE
+  #define USAGE_TAR_CREATE(a) a
+#else
+  #define USAGE_TAR_CREATE(a)
+#endif
+#ifdef CONFIG_FEATURE_TAR_EXCLUDE
+  #define USAGE_TAR_EXCLUDE(a) a
+#else
+  #define USAGE_TAR_EXCLUDE(a)
+#endif
+#ifdef CONFIG_FEATURE_TAR_GZIP
+  #define USAGE_TAR_GZIP(a) a
+#else
+  #define USAGE_TAR_GZIP(a)
+#endif
+#ifdef CONFIG_FEATURE_TAR_BZIP2
+  #define USAGE_TAR_BZIP2(a) a
+#else
+  #define USAGE_TAR_BZIP2(a)
+#endif
+#ifdef CONFIG_FEATURE_TAR_COMPRESS
+  #define USAGE_TAR_COMPRESS(a) a
+#else
+  #define USAGE_TAR_COMPRESS(a)
+#endif
+
+#define tar_trivial_usage \
+       "-[" USAGE_TAR_CREATE("c") USAGE_TAR_GZIP("z") USAGE_TAR_BZIP2("j") USAGE_TAR_COMPRESS("Z") "xtvO] " \
+       USAGE_TAR_EXCLUDE("[-X FILE]") \
+       "[-f TARFILE] [-C DIR] [FILE(s)] ..."
+#define tar_full_usage \
+       "Create, extract, or list files from a tar file.\n\n" \
+       "Options:\n" \
+       USAGE_TAR_CREATE("\tc\t\tcreate\n") \
+       "\tx\t\textract\n" \
+       "\tt\t\tlist\n" \
+       "\nArchive format selection:\n" \
+       USAGE_TAR_GZIP("\tz\t\tFilter the archive through gzip\n") \
+       USAGE_TAR_BZIP2("\tj\t\tFilter the archive through bzip2\n") \
+       USAGE_TAR_COMPRESS("\tZ\t\tFilter the archive through compress\n") \
+       "\nFile selection:\n" \
+       "\tf\t\tname of TARFILE or \"-\" for stdin\n" \
+       "\tO\t\textract to stdout\n" \
+       USAGE_TAR_EXCLUDE( \
+       "\texclude\t\tfile to exclude\n" \
+        "\tX\t\tfile with names to exclude\n" \
+       ) \
+       "\tC\t\tchange to directory DIR before operation\n" \
+       "\tv\t\tverbosely list files processed"
+#define tar_example_usage \
+       "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" \
+       "$ tar -cf /tmp/tarball.tar /usr/local\n"
+
+#define tee_trivial_usage \
+       "[OPTION]... [FILE]..."
+#define tee_full_usage \
+       "Copy standard input to each FILE, and also to standard output.\n\n" \
+       "Options:\n" \
+       "\t-a\tappend to the given FILEs, do not overwrite\n" \
+       "\t-i\tignore interrupt signals (SIGINT)"
+#define tee_example_usage \
+       "$ echo "Hello" | tee /tmp/foo\n" \
+       "$ cat /tmp/foo\n" \
+       "Hello\n"
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+#define telnet_trivial_usage \
+       "[-a] [-l USER] HOST [PORT]"
+#define telnet_full_usage \
+       "Telnet is used to establish interactive communication with another\n" \
+       "computer over a network using the TELNET protocol.\n\n" \
+       "Options:\n" \
+       "\t-a\t\tAttempt an automatic login with the USER variable.\n" \
+       "\t-l USER\t\tAttempt an automatic login with the USER argument.\n" \
+       "\tHOST\t\tThe official name, alias or the IP address of the\n" \
+       "\t\t\tremote host.\n" \
+       "\tPORT\t\tThe remote port number to connect to. If it is not\n" \
+       "\t\t\tspecified, the default telnet (23) port is used."
+#else
+#define telnet_trivial_usage \
+       "HOST [PORT]"
+#define telnet_full_usage \
+       "Telnet is used to establish interactive communication with another\n"\
+       "computer over a network using the TELNET protocol."
+#endif
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+#define telnetd_trivial_usage \
+       "(inetd mode) [OPTION]"
+#define telnetd_full_usage \
+       "Telnetd uses incoming TELNET connections via inetd.\n"\
+       "Options:\n" \
+       "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \
+       "\t-f issue_file\tDisplay issue_file instead of /etc/issue."
+#else
+#define telnetd_trivial_usage \
+       "[OPTION]"
+#define telnetd_full_usage \
+       "Telnetd listens for incoming TELNET connections on PORT.\n"\
+       "Options:\n" \
+       "\t-p PORT\tlisten for connections on PORT (default 23)\n"\
+       "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n"\
+       "\t-f issue_file\tDisplay issue_file instead of /etc/issue."
+#endif
+
+#define test_trivial_usage \
+       "EXPRESSION\n  or   [ EXPRESSION ]"
+#define test_full_usage \
+       "Checks file types and compares values returning an exit\n" \
+       "code determined by the value of EXPRESSION."
+#define test_example_usage \
+       "$ test 1 -eq 2\n" \
+       "$ echo $?\n" \
+       "1\n" \
+       "$ test 1 -eq 1\n" \
+       "$ echo $?\n" \
+       "0\n" \
+       "$ [ -d /etc ]\n" \
+       "$ echo $?\n" \
+       "0\n" \
+       "$ [ -d /junk ]\n" \
+       "$ echo $?\n" \
+       "1\n"
+
+#ifdef CONFIG_FEATURE_TFTP_GET
+  #define USAGE_TFTP_GET(a) a
+#else
+  #define USAGE_TFTP_GET(a)
+#endif
+#ifdef CONFIG_FEATURE_TFTP_PUT
+  #define USAGE_TFTP_PUT(a) a
+#else
+  #define USAGE_TFTP_PUT(a)
+#endif
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+  #define USAGE_TFTP_BS(a) a
+#else
+  #define USAGE_TFTP_BS(a)
+#endif
+
+#define tftp_trivial_usage \
+       "[OPTION]... HOST [PORT]"
+#define tftp_full_usage \
+       "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \
+       "Options:\n" \
+       "\t-l FILE\tLocal FILE.\n" \
+       "\t-r FILE\tRemote FILE." \
+        USAGE_TFTP_GET(        \
+        "\n\t-g\tGet file." \
+        ) \
+        USAGE_TFTP_PUT(        \
+       "\n\t-p\tPut file." \
+       ) \
+       USAGE_TFTP_BS( \
+       "\n\t-b SIZE\tTransfer blocks of SIZE octets." \
+       )
+#define time_trivial_usage \
+       "[OPTION]... COMMAND [ARGS...]"
+#define time_full_usage \
+       "Runs the program COMMAND with arguments ARGS.  When COMMAND finishes,\n" \
+       "COMMAND's resource usage information is displayed\n\n" \
+       "Options:\n" \
+       "\t-v\tDisplays verbose resource usage information."
+
+#define top_trivial_usage \
+       "[-d <seconds>]"
+#define top_full_usage \
+       "top provides an view of processor activity in real time.\n" \
+       "This utility reads the status for all processes in /proc each <seconds>\n" \
+       "and shows the status for however many processes will fit on the screen.\n" \
+       "This utility will not show processes that are started after program startup,\n" \
+       "but it will show the EXIT status for and PIDs that exit while it is running."
+
+#define touch_trivial_usage \
+       "[-c] FILE [FILE ...]"
+#define touch_full_usage \
+       "Update the last-modified date on the given FILE[s].\n\n" \
+       "Options:\n" \
+       "\t-c\tDo not create any files"
+#define touch_example_usage \
+       "$ ls -l /tmp/foo\n" \
+       "/bin/ls: /tmp/foo: No such file or directory\n" \
+       "$ touch /tmp/foo\n" \
+       "$ ls -l /tmp/foo\n" \
+       "-rw-rw-r--    1 andersen andersen        0 Apr 15 01:11 /tmp/foo\n"
+
+#define tr_trivial_usage \
+       "[-cds] STRING1 [STRING2]"
+#define tr_full_usage \
+       "Translate, squeeze, and/or delete characters from\n" \
+       "standard input, writing to standard output.\n\n" \
+       "Options:\n" \
+       "\t-c\ttake complement of STRING1\n" \
+       "\t-d\tdelete input characters coded STRING1\n" \
+       "\t-s\tsqueeze multiple output characters of STRING2 into one character"
+#define tr_example_usage \
+       "$ echo "gdkkn vnqkc" | tr [a-y] [b-z]\n" \
+       "hello world\n"
+
+#define traceroute_trivial_usage \
+       "[-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n"\
+       "\t[-s src_addr] [-t tos] [-w wait] host [data size]"
+#define traceroute_full_usage \
+       "trace the route ip packets follow going to \"host\"\n" \
+       "Options:\n" \
+       "\t-d\tset SO_DEBUG options to socket\n" \
+       "\t-n\tPrint hop addresses numerically rather than symbolically\n" \
+       "\t-r\tBypass the normal routing tables and send directly to a host\n" \
+       "\t-v\tVerbose output\n" \
+       "\t-m max_ttl\tSet the max time-to-live (max number of hops)\n" \
+       "\t-p port#\tSet the base UDP port number used in probes\n" \
+       "\t\t(default is 33434)\n" \
+       "\t-q nqueries\tSet the number of probes per ``ttl'' to nqueries\n" \
+       "\t\t(default is 3)\n" \
+       "\t-s src_addr\tUse the following IP address as the source address\n" \
+       "\t-t tos\tSet the type-of-service in probe packets to the following value\n" \
+       "\t\t(default 0)\n" \
+       "\t-w wait\tSet the time (in seconds) to wait for a response to a probe\n" \
+       "\t\t(default 3 sec.)."
+
+
+#define true_trivial_usage \
+       ""
+#define true_full_usage \
+       "Return an exit code of TRUE (0)."
+#define true_example_usage \
+       "$ true\n" \
+       "$ echo $?\n" \
+       "0\n"
+
+#define tty_trivial_usage \
+       ""
+#define tty_full_usage \
+       "Print the file name of the terminal connected to standard input.\n\n"\
+       "Options:\n" \
+       "\t-s\tprint nothing, only return an exit status"
+#define tty_example_usage \
+       "$ tty\n" \
+       "/dev/tty2\n"
+
+#define udhcpc_trivial_usage \
+       "[-fbnqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p pidfile] [-r IP] [-s script]"
+#define udhcpc_full_usage \
+       "\t-c,\t--clientid=CLIENTID\tClient identifier\n" \
+       "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \
+       "\t-h,\t                   \tAlias for -H\n" \
+       "\t-f,\t--foreground\tDo not fork after getting lease\n" \
+       "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated.\n" \
+       "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \
+       "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \
+       "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
+       "\t-q,\t--quit\tQuit after obtaining lease\n" \
+       "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
+       "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
+       "\t-v,\t--version\tDisplay version"
+
+#define udhcpd_trivial_usage \
+       "[configfile]\n" \
+
+#define udhcpd_full_usage \
+       ""
+
+#ifdef CONFIG_FEATURE_MOUNT_FORCE
+  #define USAGE_MOUNT_FORCE(a) a
+#else
+  #define USAGE_MOUNT_FORCE(a)
+#endif
+#define umount_trivial_usage \
+       "[flags] FILESYSTEM|DIRECTORY"
+#define umount_full_usage \
+       "Unmount file systems\n" \
+       "\nFlags:\n" "\t-a\tUnmount all file systems" \
+       USAGE_MTAB(" in /etc/mtab\n\t-n\tDon't erase /etc/mtab entries") \
+       "\n\t-r\tTry to remount devices as read-only if mount is busy" \
+       USAGE_MOUNT_FORCE("\n\t-f\tForce umount (i.e., unreachable NFS server)") \
+       USAGE_MOUNT_LOOP("\n\t-l\tDo not free loop device (if a loop device has been used)")
+#define umount_example_usage \
+       "$ umount /dev/hdc1 \n"
+
+#define uname_trivial_usage \
+       "[OPTION]..."
+#define uname_full_usage \
+       "Print certain system information.  With no OPTION, same as -s.\n\n" \
+       "Options:\n" \
+       "\t-a\tprint all information\n" \
+       "\t-m\tthe machine (hardware) type\n" \
+       "\t-n\tprint the machine's network node hostname\n" \
+       "\t-r\tprint the operating system release\n" \
+       "\t-s\tprint the operating system name\n" \
+       "\t-p\tprint the host processor type\n" \
+       "\t-v\tprint the operating system version"
+#define uname_example_usage \
+       "$ uname -a\n" \
+       "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n"
+
+#define uncompress_trivial_usage \
+       "[-c] [-f] [ name ... ]"
+#define uncompress_full_usage \
+       "Uncompress .Z file[s]\n" \
+       "Options:\n" \
+       "\t-c\textract to stdout\n" \
+       "\t-f\tforce overwrite an existing file"
+
+#define uniq_trivial_usage \
+       "[OPTION]... [INPUT [OUTPUT]]"
+#define uniq_full_usage \
+       "Discard all but one of successive identical lines from INPUT\n" \
+       "(or standard input), writing to OUTPUT (or standard output).\n\n" \
+       "Options:\n" \
+       "\t-c\tprefix lines by the number of occurrences\n" \
+       "\t-d\tonly print duplicate lines\n" \
+       "\t-u\tonly print unique lines\n" \
+       "\t-f N\tskip the first N fields\n" \
+       "\t-s N\tskip the first N chars (after any skipped fields)"
+#define uniq_example_usage \
+       "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" \
+       "a\n" \
+       "b\n" \
+       "c\n"
+
+#define unix2dos_trivial_usage \
+       "[option] [FILE]"
+#define unix2dos_full_usage \
+       "Converts FILE from unix format to dos format.  When no option\n" \
+       "is given, the input is converted to the opposite output format.\n" \
+       "When no file is given, uses stdin for input and stdout for output.\n" \
+       "Options:\n" \
+       "\t-u\toutput will be in UNIX format\n" \
+       "\t-d\toutput will be in DOS format"
+
+#define unzip_trivial_usage \
+       "[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]"
+#define unzip_full_usage \
+       "Extracts files from ZIP archives.\n\n" \
+       "Options:\n" \
+       "\t-l\tlist archive contents (short form)\n" \
+       "\t-n\tnever overwrite existing files (default)\n" \
+       "\t-o\toverwrite files without prompting\n" \
+       "\t-p\tsend output to stdout\n" \
+       "\t-q\tbe quiet\n" \
+       "\t-x\texclude these files\n" \
+       "\t-d\textract files into this directory"
+
+#define uptime_trivial_usage \
+       ""
+#define uptime_full_usage \
+       "Display the time since the last boot."
+#define uptime_example_usage \
+       "$ uptime\n" \
+       "  1:55pm  up  2:30, load average: 0.09, 0.04, 0.00\n"
+
+#define usleep_trivial_usage \
+       "N"
+#define usleep_full_usage \
+       "Pause for N microseconds."
+#define usleep_example_usage \
+       "$ usleep 1000000\n" \
+       "[pauses for 1 second]\n"
+
+#define uudecode_trivial_usage \
+       "[FILE]..."
+#define uudecode_full_usage \
+       "Uudecode a file that is uuencoded.\n\n" \
+       "Options:\n" \
+       "\t-o FILE\tdirect output to FILE"
+#define uudecode_example_usage \
+       "$ uudecode -o busybox busybox.uu\n" \
+       "$ ls -l busybox\n" \
+       "-rwxr-xr-x   1 ams      ams        245264 Jun  7 21:35 busybox\n"
+
+#define uuencode_trivial_usage \
+       "[OPTION] [INFILE] REMOTEFILE"
+#define uuencode_full_usage \
+       "Uuencode a file.\n\n" \
+       "Options:\n" \
+       "\t-m\tuse base64 encoding per RFC1521"
+#define uuencode_example_usage \
+       "$ uuencode busybox busybox\n" \
+       "begin 755 busybox\n" \
+       "<encoded file snipped>\n" \
+       "$ uudecode busybox busybox > busybox.uu\n" \
+       "$\n"
+
+#define vconfig_trivial_usage \
+       "COMMAND [OPTIONS] ..."
+#define vconfig_full_usage \
+       "vconfig lets you create and remove virtual ethernet devices.\n\n" \
+       "Options:\n" \
+       "\tadd             [interface-name] [vlan_id]\n" \
+       "\trem             [vlan-name]\n" \
+       "\tset_flag        [interface-name] [flag-num]       [0 | 1]\n" \
+       "\tset_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]\n" \
+       "\tset_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]\n" \
+       "\tset_name_type   [name-type]"
+
+#define vi_trivial_usage \
+       "[OPTION] [FILE]..."
+#define vi_full_usage \
+       "edit FILE.\n\n" \
+       "Options:\n" \
+       "\t-R\tRead-only- do not write to the file."
+
+#define vlock_trivial_usage \
+       "[OPTIONS]"
+#define vlock_full_usage \
+       "Lock a virtual terminal.  A password is required to unlock\n" \
+       "Options:\n" \
+       "\t-a\tLock all VTs"
+
+#define watch_trivial_usage \
+       "[-n <seconds>] COMMAND..."
+#define watch_full_usage \
+       "Executes a program periodically.\n" \
+       "Options:\n" \
+       "\t-n\tLoop period in seconds - default is 2."
+#define watch_example_usage \
+       "$ watch date\n" \
+       "Mon Dec 17 10:31:40 GMT 2000\n" \
+       "Mon Dec 17 10:31:42 GMT 2000\n" \
+       "Mon Dec 17 10:31:44 GMT 2000"
+
+#define watchdog_trivial_usage \
+       "[-t <seconds>] DEV"
+#define watchdog_full_usage \
+       "Periodically write to watchdog device DEV.\n" \
+       "Options:\n" \
+       "\t-t\tTimer period in seconds - default is 30."
+
+#define wc_trivial_usage \
+       "[OPTION]... [FILE]..."
+#define wc_full_usage \
+       "Print line, word, and byte counts for each FILE, and a total line if\n" \
+       "more than one FILE is specified.  With no FILE, read standard input.\n\n" \
+       "Options:\n" \
+       "\t-c\tprint the byte counts\n" \
+       "\t-l\tprint the newline counts\n" \
+       "\t-L\tprint the length of the longest line\n" \
+       "\t-w\tprint the word counts"
+#define wc_example_usage \
+       "$ wc /etc/passwd\n" \
+       "     31      46    1365 /etc/passwd\n"
+
+#define wget_trivial_usage \
+       "[-c|--continue] [-q|--quiet] [-O|--output-document file]\n" \
+       "\t\t[--header 'header: value'] [-Y|--proxy on/off] [-P DIR] url"
+#define wget_full_usage \
+       "wget retrieves files via HTTP or FTP\n\n" \
+       "Options:\n" \
+       "\t-c\tcontinue retrieval of aborted transfers\n" \
+       "\t-q\tquiet mode - do not print\n" \
+       "\t-P\tSet directory prefix to DIR\n" \
+       "\t-O\tsave to filename ('-' for stdout)\n" \
+       "\t-Y\tuse proxy ('on' or 'off')"
+
+#define which_trivial_usage \
+       "[COMMAND ...]"
+#define which_full_usage \
+       "Locates a COMMAND."
+#define which_example_usage \
+       "$ which login\n" \
+       "/bin/login\n"
+
+#define who_trivial_usage \
+        " "
+#define who_full_usage \
+        "Prints the current user names and related information"
+
+#define whoami_trivial_usage \
+       ""
+#define whoami_full_usage \
+       "Prints the user name associated with the current effective user id."
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+#define USAGE_XARGS_CONFIRMATION(a) a
+#else
+#define USAGE_XARGS_CONFIRMATION(a)
+#endif
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+#define USAGE_XARGS_TERMOPT(a) a
+#else
+#define USAGE_XARGS_TERMOPT(a)
+#endif
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+#define USAGE_XARGS_ZERO_TERM(a) a
+#else
+#define USAGE_XARGS_ZERO_TERM(a)
+#endif
+
+
+#define xargs_trivial_usage \
+       "[COMMAND] [OPTIONS] [ARGS...]"
+#define xargs_full_usage \
+       "Executes COMMAND on every item given by standard input.\n\n" \
+       "Options:\n" \
+       USAGE_XARGS_CONFIRMATION("\t-p\tPrompt the user about whether to run each command\n") \
+       "\t-r\tDo not run command for empty readed lines\n" \
+       USAGE_XARGS_TERMOPT("\t-x\tExit if the size is exceeded\n") \
+       USAGE_XARGS_ZERO_TERM("\t-0\tInput filenames are terminated by a null character\n") \
+       "\t-t\tPrint the command line on stderr before executing it."
+#define xargs_example_usage \
+       "$ ls | xargs gzip\n" \
+       "$ find . -name '*.c' -print | xargs rm\n"
+
+#define yes_trivial_usage \
+       "[OPTION]... [STRING]..."
+#define yes_full_usage \
+       "Repeatedly outputs a line with all specified STRING(s), or 'y'."
+
+#define zcat_trivial_usage \
+       "FILE"
+#define zcat_full_usage \
+       "Uncompress to stdout."
+
+#endif /* __BB_USAGE_H__ */
diff --git a/busybox/init/Config.in b/busybox/init/Config.in
new file mode 100644 (file)
index 0000000..4465e75
--- /dev/null
@@ -0,0 +1,72 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Init Utilities"
+
+config CONFIG_INIT
+       bool "init"
+       default n
+       help
+         init is the first program run when the system boots.
+
+config CONFIG_FEATURE_USE_INITTAB
+       bool "  Support reading an inittab file?"
+       default y
+       depends on CONFIG_INIT
+       help
+         Allow init to read an inittab file when the system boot.
+
+config CONFIG_FEATURE_INITRD
+       bool "  Support running init from within an initrd?"
+       default y
+       depends on CONFIG_INIT
+       help
+         Allow init to be called from an initrd as linuxrc.
+
+config CONFIG_FEATURE_INIT_COREDUMPS
+       bool "  Support dumping core for child processes (debugging only)?"
+       default n
+       depends on CONFIG_INIT
+       help
+         If this option is enabled and the file /.init_enable_core
+         exists, then init will call setrlimit() to allow unlimited
+         core file sizes.  If this option is disabled, processes
+         will not generate any core files.
+
+config CONFIG_FEATURE_EXTRA_QUIET
+       bool "  Should init be _extra_ quiet on boot?"
+       default y
+       depends on CONFIG_INIT
+       help
+         Prevent init from logging some messages to the console
+         during boot.
+
+config CONFIG_HALT
+       bool "halt"
+       default y
+       help
+         Stop all processes and halt the system.
+
+config CONFIG_POWEROFF
+       bool "poweroff"
+       default y
+       help
+         Stop all processes and (try to) power off the system.
+
+config CONFIG_REBOOT
+       bool "reboot"
+       default y
+       help
+         Stop all processes and reboot the system.
+
+config CONFIG_MESG
+       bool "mesg"
+       default y
+       help
+         Mesg controls access to your terminal by others.  It is typically
+         used to allow or disallow other users to write to your terminal
+
+endmenu
+
diff --git a/busybox/init/Makefile b/busybox/init/Makefile
new file mode 100644 (file)
index 0000000..9b0a1d1
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/init
+INIT_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include $(srcdir)/Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/init/Makefile.in b/busybox/init/Makefile.in
new file mode 100644 (file)
index 0000000..807259d
--- /dev/null
@@ -0,0 +1,62 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+INIT_AR:=init.a
+ifndef $(INIT_DIR)
+INIT_DIR:=$(top_builddir)/init/
+endif
+srcdir=$(top_srcdir)/init
+
+INIT-y:=
+INIT-$(CONFIG_HALT)                    += halt.o
+INIT-$(CONFIG_INIT)                    += init.o
+INIT-$(CONFIG_MESG)                    += mesg.o
+INIT-$(CONFIG_POWEROFF)                        += poweroff.o
+INIT-$(CONFIG_REBOOT)                  += reboot.o
+
+ifeq ($(CONFIG_HALT), y)
+CONFIG_INIT_SHARED=y
+else
+ifeq ($(CONFIG_INIT), y)
+CONFIG_INIT_SHARED=y
+else
+ifeq ($(CONFIG_POWEROFF), y)
+CONFIG_INIT_SHARED=y
+else
+ifeq ($(CONFIG_REBOOT), y)
+CONFIG_INIT_SHARED=y
+else
+CONFIG_INIT_SHARED=n
+endif
+endif
+endif
+endif
+
+ifeq ($(CONFIG_INIT_SHARED), y)
+INIT-$(CONFIG_INIT_SHARED)        += init_shared.o
+endif
+
+libraries-y+=$(INIT_DIR)$(INIT_AR)
+
+$(INIT_DIR)$(INIT_AR): $(patsubst %,$(INIT_DIR)%, $(INIT-y))
+       $(AR) -ro $@ $(patsubst %,$(INIT_DIR)%, $(INIT-y))
+
+$(INIT_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/init/halt.c b/busybox/init/halt.c
new file mode 100644 (file)
index 0000000..bfc0042
--- /dev/null
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini halt implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/reboot.h>
+#include "busybox.h"
+#include "init_shared.h"
+
+
+extern int halt_main(int argc, char **argv)
+{
+       char *delay; /* delay in seconds before rebooting */
+
+       if(bb_getopt_ulflags(argc, argv, "d:", &delay)) {
+               sleep(atoi(delay));
+       }
+
+#ifndef CONFIG_INIT
+#ifndef RB_HALT_SYSTEM
+#define RB_HALT_SYSTEM         0xcdef0123
+#endif
+       return(bb_shutdown_system(RB_HALT_SYSTEM));
+#else
+       return kill_init(SIGUSR1);
+#endif
+}
diff --git a/busybox/init/init.c b/busybox/init/init.c
new file mode 100644 (file)
index 0000000..0c8dc89
--- /dev/null
@@ -0,0 +1,1214 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini init implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Adjusted by so many folks, it's impossible to keep track.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Turn this on to disable all the dangerous
+   rebooting stuff when debugging.
+#define DEBUG_INIT
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/reboot.h>
+#include "busybox.h"
+
+#include "init_shared.h"
+
+
+#ifdef CONFIG_SYSLOGD
+# include <sys/syslog.h>
+#endif
+
+
+#define INIT_BUFFS_SIZE 256
+
+/* From <linux/vt.h> */
+struct vt_stat {
+       unsigned short v_active;        /* active vt */
+       unsigned short v_signal;        /* signal to send */
+       unsigned short v_state; /* vt bitmask */
+};
+static const int VT_GETSTATE = 0x5603; /* get global vt state info */
+
+/* From <linux/serial.h> */
+struct serial_struct {
+       int     type;
+       int     line;
+       unsigned int    port;
+       int     irq;
+       int     flags;
+       int     xmit_fifo_size;
+       int     custom_divisor;
+       int     baud_base;
+       unsigned short  close_delay;
+       char    io_type;
+       char    reserved_char[1];
+       int     hub6;
+       unsigned short  closing_wait; /* time to wait before closing */
+       unsigned short  closing_wait2; /* no longer used... */
+       unsigned char   *iomem_base;
+       unsigned short  iomem_reg_shift;
+       unsigned int    port_high;
+       unsigned long   iomap_base;     /* cookie passed into ioremap */
+       int     reserved[1];
+};
+
+
+#ifndef _PATH_STDPATH
+#define _PATH_STDPATH  "/usr/bin:/bin:/usr/sbin:/sbin"
+#endif
+
+#if defined CONFIG_FEATURE_INIT_COREDUMPS
+/*
+ * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called
+ * before processes are spawned to set core file size as unlimited.
+ * This is for debugging only.  Don't use this is production, unless
+ * you want core dumps lying about....
+ */
+#define CORE_ENABLE_FLAG_FILE "/.init_enable_core"
+#include <sys/resource.h>
+#include <sys/time.h>
+#endif
+
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+#define INITTAB      "/etc/inittab"    /* inittab file location */
+#ifndef INIT_SCRIPT
+#define INIT_SCRIPT  "/etc/init.d/rcS" /* Default sysinit script. */
+#endif
+
+#define MAXENV 16              /* Number of env. vars */
+
+#define CONSOLE_BUFF_SIZE 32
+
+/* Allowed init action types */
+#define SYSINIT     0x001
+#define RESPAWN     0x002
+#define ASKFIRST    0x004
+#define WAIT        0x008
+#define ONCE        0x010
+#define CTRLALTDEL  0x020
+#define SHUTDOWN    0x040
+#define RESTART     0x080
+
+/* A mapping between "inittab" action name strings and action type codes. */
+struct init_action_type {
+       const char *name;
+       int action;
+};
+
+static const struct init_action_type actions[] = {
+       {"sysinit", SYSINIT},
+       {"respawn", RESPAWN},
+       {"askfirst", ASKFIRST},
+       {"wait", WAIT},
+       {"once", ONCE},
+       {"ctrlaltdel", CTRLALTDEL},
+       {"shutdown", SHUTDOWN},
+       {"restart", RESTART},
+       {0, 0}
+};
+
+/* Set up a linked list of init_actions, to be read from inittab */
+struct init_action {
+       pid_t pid;
+       char command[INIT_BUFFS_SIZE];
+       char terminal[CONSOLE_BUFF_SIZE];
+       struct init_action *next;
+       int action;
+};
+
+/* Static variables */
+static struct init_action *init_action_list = NULL;
+static char console[CONSOLE_BUFF_SIZE] = _PATH_CONSOLE;
+
+#ifndef CONFIG_SYSLOGD
+static char *log = VC_5;
+#endif
+static sig_atomic_t got_cont = 0;
+static const int LOG = 0x1;
+static const int CONSOLE = 0x2;
+
+#if defined CONFIG_FEATURE_EXTRA_QUIET
+static const int MAYBE_CONSOLE = 0x0;
+#else
+#define MAYBE_CONSOLE  CONSOLE
+#endif
+#ifndef RB_HALT_SYSTEM
+static const int RB_HALT_SYSTEM = 0xcdef0123;
+static const int RB_ENABLE_CAD = 0x89abcdef;
+static const int RB_DISABLE_CAD = 0;
+
+#define RB_POWER_OFF    0x4321fedc
+static const int RB_AUTOBOOT = 0x01234567;
+#endif
+
+static const char * const environment[] = {
+       "HOME=/",
+       "PATH=" _PATH_STDPATH,
+       "SHELL=/bin/sh",
+       "USER=root",
+       NULL
+};
+
+/* Function prototypes */
+static void delete_init_action(struct init_action *a);
+static int waitfor(const struct init_action *a);
+static void halt_signal(int sig);
+
+
+static void loop_forever(void)
+{
+       while (1)
+               sleep(1);
+}
+
+/* Print a message to the specified device.
+ * Device may be bitwise-or'd from LOG | CONSOLE */
+#ifndef DEBUG_INIT
+static inline void messageD(int device, const char *fmt, ...)
+{
+}
+#else
+#define messageD message
+#endif
+static void message(int device, const char *fmt, ...)
+       __attribute__ ((format(printf, 2, 3)));
+static void message(int device, const char *fmt, ...)
+{
+       va_list arguments;
+       int l;
+       char msg[1024];
+#ifndef CONFIG_SYSLOGD
+       static int log_fd = -1;
+#endif
+
+       msg[0] = '\r';
+               va_start(arguments, fmt);
+       l = vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments) + 1;
+               va_end(arguments);
+
+#ifdef CONFIG_SYSLOGD
+       /* Log the message to syslogd */
+       if (device & LOG) {
+               /* don`t out "\r\n" */
+               openlog(bb_applet_name, 0, LOG_DAEMON);
+               syslog(LOG_INFO, "%s", msg);
+               closelog();
+       }
+
+       msg[l++] = '\n';
+       msg[l] = 0;
+#else
+
+       msg[l++] = '\n';
+       msg[l] = 0;
+       /* Take full control of the log tty, and never close it.
+        * It's mine, all mine!  Muhahahaha! */
+       if (log_fd < 0) {
+               if ((log_fd = device_open(log, O_RDWR | O_NDELAY | O_NOCTTY)) < 0) {
+                       log_fd = -2;
+                       bb_error_msg("Bummer, can't write to log on %s!", log);
+                       device = CONSOLE;
+               } else {
+                       fcntl(log_fd, F_SETFD, FD_CLOEXEC);
+               }
+       }
+       if ((device & LOG) && (log_fd >= 0)) {
+               bb_full_write(log_fd, msg, l);
+       }
+#endif
+
+       if (device & CONSOLE) {
+               int fd = device_open(_PATH_CONSOLE,
+                                       O_WRONLY | O_NOCTTY | O_NDELAY);
+               /* Always send console messages to /dev/console so people will see them. */
+               if (fd >= 0) {
+                       bb_full_write(fd, msg, l);
+                       close(fd);
+#ifdef DEBUG_INIT
+               /* all descriptors may be closed */
+               } else {
+                       bb_error_msg("Bummer, can't print: ");
+                       va_start(arguments, fmt);
+                       vfprintf(stderr, fmt, arguments);
+                       va_end(arguments);
+#endif
+               }
+       }
+}
+
+/* Set terminal settings to reasonable defaults */
+static void set_term(int fd)
+{
+       struct termios tty;
+
+       tcgetattr(fd, &tty);
+
+       /* set control chars */
+       tty.c_cc[VINTR] = 3;    /* C-c */
+       tty.c_cc[VQUIT] = 28;   /* C-\ */
+       tty.c_cc[VERASE] = 127; /* C-? */
+       tty.c_cc[VKILL] = 21;   /* C-u */
+       tty.c_cc[VEOF] = 4;     /* C-d */
+       tty.c_cc[VSTART] = 17;  /* C-q */
+       tty.c_cc[VSTOP] = 19;   /* C-s */
+       tty.c_cc[VSUSP] = 26;   /* C-z */
+
+       /* use line dicipline 0 */
+       tty.c_line = 0;
+
+       /* Make it be sane */
+       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
+       tty.c_cflag |= CREAD | HUPCL | CLOCAL;
+
+
+       /* input modes */
+       tty.c_iflag = ICRNL | IXON | IXOFF;
+
+       /* output modes */
+       tty.c_oflag = OPOST | ONLCR;
+
+       /* local modes */
+       tty.c_lflag =
+               ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
+
+       tcsetattr(fd, TCSANOW, &tty);
+}
+
+/* How much memory does this machine have?
+   Units are kBytes to avoid overflow on 4GB machines */
+static unsigned int check_free_memory(void)
+{
+       struct sysinfo info;
+       unsigned int result, u, s = 10;
+
+       if (sysinfo(&info) != 0) {
+               bb_perror_msg("Error checking free memory");
+               return -1;
+       }
+
+       /* Kernels 2.0.x and 2.2.x return info.mem_unit==0 with values in bytes.
+        * Kernels 2.4.0 return info.mem_unit in bytes. */
+       u = info.mem_unit;
+       if (u == 0)
+               u = 1;
+       while ((u & 1) == 0 && s > 0) {
+               u >>= 1;
+               s--;
+       }
+       result = (info.totalram >> s) + (info.totalswap >> s);
+       if (((unsigned long long)result * (unsigned long long)u) > UINT_MAX) {
+               return(UINT_MAX);
+       } else {
+               return(result * u);
+       }
+}
+
+static void console_init(void)
+{
+       int fd;
+       int tried = 0;
+       struct vt_stat vt;
+       struct serial_struct sr;
+       char *s;
+
+       if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) {
+               safe_strncpy(console, s, sizeof(console));
+#if #cpu(sparc)
+       /* sparc kernel supports console=tty[ab] parameter which is also
+        * passed to init, so catch it here */
+               /* remap tty[ab] to /dev/ttyS[01] */
+               if (strcmp(s, "ttya") == 0)
+                       safe_strncpy(console, SC_0, sizeof(console));
+               else if (strcmp(s, "ttyb") == 0)
+                       safe_strncpy(console, SC_1, sizeof(console));
+#endif
+       } else {
+               /* 2.2 kernels: identify the real console backend and try to use it */
+               if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
+                       /* this is a serial console */
+                       snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line);
+               } else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
+                       /* this is linux virtual tty */
+                       snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active);
+               } else {
+                       safe_strncpy(console, _PATH_CONSOLE, sizeof(console));
+                       tried++;
+               }
+       }
+
+       while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) {
+               /* Can't open selected console -- try
+                       logical system console and VT_MASTER */
+               safe_strncpy(console, (tried == 0 ? _PATH_CONSOLE : CURRENT_VC),
+                                                       sizeof(console));
+               tried++;
+       }
+       if (fd < 0) {
+               /* Perhaps we should panic here? */
+#ifndef CONFIG_SYSLOGD
+               log =
+#endif
+               safe_strncpy(console, "/dev/null", sizeof(console));
+       } else {
+               s = getenv("TERM");
+               /* check for serial console */
+               if (ioctl(fd, TIOCGSERIAL, &sr) == 0) {
+                       /* Force the TERM setting to vt102 for serial console --
+                        * if TERM is set to linux (the default) */
+                       if (s == NULL || strcmp(s, "linux") == 0)
+                               putenv("TERM=vt102");
+#ifndef CONFIG_SYSLOGD
+                       log = console;
+#endif
+               } else {
+                       if (s == NULL)
+                               putenv("TERM=linux");
+               }
+               close(fd);
+       }
+       messageD(LOG, "console=%s", console);
+}
+
+static void fixup_argv(int argc, char **argv, char *new_argv0)
+{
+       int len;
+
+       /* Fix up argv[0] to be certain we claim to be init */
+       len = strlen(argv[0]);
+       memset(argv[0], 0, len);
+       safe_strncpy(argv[0], new_argv0, len + 1);
+
+       /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
+       len = 1;
+       while (argc > len) {
+               memset(argv[len], 0, strlen(argv[len]));
+               len++;
+       }
+}
+
+static pid_t run(const struct init_action *a)
+{
+       struct stat sb;
+       int i, junk;
+       pid_t pid, pgrp, tmp_pid;
+       char *s, *tmpCmd, *cmd[INIT_BUFFS_SIZE], *cmdpath;
+       char buf[INIT_BUFFS_SIZE + 6];  /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
+       sigset_t nmask, omask;
+       static const char press_enter[] =
+#ifdef CUSTOMIZED_BANNER
+#include CUSTOMIZED_BANNER
+#endif
+               "\nPlease press Enter to activate this console. ";
+
+       /* Block sigchild while forking.  */
+       sigemptyset(&nmask);
+       sigaddset(&nmask, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+       if ((pid = fork()) == 0) {
+               /* Clean up */
+               close(0);
+               close(1);
+               close(2);
+               sigprocmask(SIG_SETMASK, &omask, NULL);
+
+               /* Reset signal handlers that were set by the parent process */
+               signal(SIGUSR1, SIG_DFL);
+               signal(SIGUSR2, SIG_DFL);
+               signal(SIGINT, SIG_DFL);
+               signal(SIGTERM, SIG_DFL);
+               signal(SIGHUP, SIG_DFL);
+               signal(SIGCONT, SIG_DFL);
+               signal(SIGSTOP, SIG_DFL);
+               signal(SIGTSTP, SIG_DFL);
+
+               /* Create a new session and make ourself the process
+                * group leader */
+               setsid();
+
+               /* Open the new terminal device */
+               if ((device_open(a->terminal, O_RDWR)) < 0) {
+                       if (stat(a->terminal, &sb) != 0) {
+                               message(LOG | CONSOLE, "device '%s' does not exist.",
+                                               a->terminal);
+                               _exit(1);
+                       }
+                       message(LOG | CONSOLE, "Bummer, can't open %s", a->terminal);
+                       _exit(1);
+               }
+
+               /* Make sure the terminal will act fairly normal for us */
+               set_term(0);
+               /* Setup stdout, stderr for the new process so
+                * they point to the supplied terminal */
+               dup(0);
+               dup(0);
+
+               /* If the init Action requires us to wait, then force the
+                * supplied terminal to be the controlling tty. */
+               if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
+
+                       /* Now fork off another process to just hang around */
+                       if ((pid = fork()) < 0) {
+                               message(LOG | CONSOLE, "Can't fork!");
+                               _exit(1);
+                       }
+
+                       if (pid > 0) {
+
+                               /* We are the parent -- wait till the child is done */
+                               signal(SIGINT, SIG_IGN);
+                               signal(SIGTSTP, SIG_IGN);
+                               signal(SIGQUIT, SIG_IGN);
+                               signal(SIGCHLD, SIG_DFL);
+
+                               /* Wait for child to exit */
+                               while ((tmp_pid = waitpid(pid, &junk, 0)) != pid) {
+                                       if (tmp_pid == -1 && errno == ECHILD) {
+                                               break;
+                                       }
+                                       /* FIXME handle other errors */
+                }
+
+                               /* See if stealing the controlling tty back is necessary */
+                               pgrp = tcgetpgrp(0);
+                               if (pgrp != getpid())
+                                       _exit(0);
+
+                               /* Use a temporary process to steal the controlling tty. */
+                               if ((pid = fork()) < 0) {
+                                       message(LOG | CONSOLE, "Can't fork!");
+                                       _exit(1);
+                               }
+                               if (pid == 0) {
+                                       setsid();
+                                       ioctl(0, TIOCSCTTY, 1);
+                                       _exit(0);
+                               }
+                               while ((tmp_pid = waitpid(pid, &junk, 0)) != pid) {
+                                       if (tmp_pid < 0 && errno == ECHILD)
+                                               break;
+                               }
+                               _exit(0);
+                       }
+
+                       /* Now fall though to actually execute things */
+               }
+
+               /* See if any special /bin/sh requiring characters are present */
+               if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
+                       cmd[0] = (char *)DEFAULT_SHELL;
+                       cmd[1] = "-c";
+                       cmd[2] = strcat(strcpy(buf, "exec "), a->command);
+                       cmd[3] = NULL;
+               } else {
+                       /* Convert command (char*) into cmd (char**, one word per string) */
+                       strcpy(buf, a->command);
+                       s = buf;
+                       for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) {
+                               if (*tmpCmd != '\0') {
+                                       cmd[i] = tmpCmd;
+                                       i++;
+                               }
+                       }
+                       cmd[i] = NULL;
+               }
+
+               cmdpath = cmd[0];
+
+               /*
+                  Interactive shells want to see a dash in argv[0].  This
+                  typically is handled by login, argv will be setup this
+                  way if a dash appears at the front of the command path
+                  (like "-/bin/sh").
+                */
+
+               if (*cmdpath == '-') {
+
+                       /* skip over the dash */
+                       ++cmdpath;
+
+                       /* find the last component in the command pathname */
+                       s = bb_get_last_path_component(cmdpath);
+
+                       /* make a new argv[0] */
+                       if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) {
+                               message(LOG | CONSOLE, bb_msg_memory_exhausted);
+                               cmd[0] = cmdpath;
+                       } else {
+                               cmd[0][0] = '-';
+                               strcpy(cmd[0] + 1, s);
+                       }
+               }
+
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+               if (a->action & ASKFIRST) {
+                       char c;
+                       /*
+                        * Save memory by not exec-ing anything large (like a shell)
+                        * before the user wants it. This is critical if swap is not
+                        * enabled and the system has low memory. Generally this will
+                        * be run on the second virtual console, and the first will
+                        * be allowed to start a shell or whatever an init script
+                        * specifies.
+                        */
+                       messageD(LOG, "Waiting for enter to start '%s'"
+                                               "(pid %d, terminal %s)\n",
+                                         cmdpath, getpid(), a->terminal);
+                       bb_full_write(1, press_enter, sizeof(press_enter) - 1);
+                       while(read(0, &c, 1) == 1 && c != '\n')
+                               ;
+               }
+#endif
+
+               /* Log the process name and args */
+               message(LOG, "Starting pid %d, console %s: '%s'",
+                                 getpid(), a->terminal, cmdpath);
+
+#if defined CONFIG_FEATURE_INIT_COREDUMPS
+               if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
+                       struct rlimit limit;
+
+                       limit.rlim_cur = RLIM_INFINITY;
+                       limit.rlim_max = RLIM_INFINITY;
+                       setrlimit(RLIMIT_CORE, &limit);
+               }
+#endif
+
+               /* Now run it.  The new program will take over this PID,
+                * so nothing further in init.c should be run. */
+               execv(cmdpath, cmd);
+
+               /* We're still here?  Some error happened. */
+               message(LOG | CONSOLE, "Bummer, could not run '%s': %m", cmdpath);
+               _exit(-1);
+       }
+       sigprocmask(SIG_SETMASK, &omask, NULL);
+       return pid;
+}
+
+static int waitfor(const struct init_action *a)
+{
+       int pid;
+       int status, wpid;
+
+       pid = run(a);
+       while (1) {
+               wpid = waitpid(pid,&status,0);
+               if (wpid == pid)
+                       break;
+               if (wpid == -1 && errno == ECHILD) {
+                       /* we missed its termination */
+                       break;
+               }
+               /* FIXME other errors should maybe trigger an error, but allow
+                * the program to continue */
+       }
+       return wpid;
+}
+
+/* Run all commands of a particular type */
+static void run_actions(int action)
+{
+       struct init_action *a, *tmp;
+
+       for (a = init_action_list; a; a = tmp) {
+               tmp = a->next;
+               if (a->action == action) {
+                       if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
+                               waitfor(a);
+                               delete_init_action(a);
+                       } else if (a->action & ONCE) {
+                               run(a);
+                               delete_init_action(a);
+                       } else if (a->action & (RESPAWN | ASKFIRST)) {
+                               /* Only run stuff with pid==0.  If they have
+                                * a pid, that means it is still running */
+                               if (a->pid == 0) {
+                                       a->pid = run(a);
+                               }
+                       }
+               }
+       }
+}
+
+#ifndef DEBUG_INIT
+static void init_reboot(unsigned long magic)
+{
+       pid_t pid;
+       /* We have to fork here, since the kernel calls do_exit(0) in
+        * linux/kernel/sys.c, which can cause the machine to panic when
+        * the init process is killed.... */
+       if ((pid = fork()) == 0) {
+               reboot(magic);
+               _exit(0);
+       }
+       waitpid (pid, NULL, 0);
+}
+
+static void shutdown_system(void)
+{
+       sigset_t block_signals;
+
+       /* run everything to be run at "shutdown".  This is done _prior_
+        * to killing everything, in case people wish to use scripts to
+        * shut things down gracefully... */
+       run_actions(SHUTDOWN);
+
+       /* first disable all our signals */
+       sigemptyset(&block_signals);
+       sigaddset(&block_signals, SIGHUP);
+       sigaddset(&block_signals, SIGCHLD);
+       sigaddset(&block_signals, SIGUSR1);
+       sigaddset(&block_signals, SIGUSR2);
+       sigaddset(&block_signals, SIGINT);
+       sigaddset(&block_signals, SIGTERM);
+       sigaddset(&block_signals, SIGCONT);
+       sigaddset(&block_signals, SIGSTOP);
+       sigaddset(&block_signals, SIGTSTP);
+       sigprocmask(SIG_BLOCK, &block_signals, NULL);
+
+       /* Allow Ctrl-Alt-Del to reboot system. */
+       init_reboot(RB_ENABLE_CAD);
+
+       message(CONSOLE | LOG, "The system is going down NOW !!");
+       sync();
+
+       /* Send signals to every process _except_ pid 1 */
+       message(CONSOLE | LOG, "Sending SIGTERM to all processes.");
+       kill(-1, SIGTERM);
+       sleep(1);
+       sync();
+
+       message(CONSOLE | LOG, "Sending SIGKILL to all processes.");
+       kill(-1, SIGKILL);
+       sleep(1);
+
+       sync();
+}
+
+static void exec_signal(int sig)
+{
+       struct init_action *a, *tmp;
+       sigset_t unblock_signals;
+
+       for (a = init_action_list; a; a = tmp) {
+               tmp = a->next;
+               if (a->action & RESTART) {
+                       struct stat sb;
+
+                       shutdown_system();
+
+                       /* unblock all signals, blocked in shutdown_system() */
+                       sigemptyset(&unblock_signals);
+                       sigaddset(&unblock_signals, SIGHUP);
+                       sigaddset(&unblock_signals, SIGCHLD);
+                       sigaddset(&unblock_signals, SIGUSR1);
+                       sigaddset(&unblock_signals, SIGUSR2);
+                       sigaddset(&unblock_signals, SIGINT);
+                       sigaddset(&unblock_signals, SIGTERM);
+                       sigaddset(&unblock_signals, SIGCONT);
+                       sigaddset(&unblock_signals, SIGSTOP);
+                       sigaddset(&unblock_signals, SIGTSTP);
+                       sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
+
+                       /* Close whatever files are open. */
+                       close(0);
+                       close(1);
+                       close(2);
+
+                       /* Open the new terminal device */
+                       if ((device_open(a->terminal, O_RDWR)) < 0) {
+                               if (stat(a->terminal, &sb) != 0) {
+                                       message(LOG | CONSOLE, "device '%s' does not exist.", a->terminal);
+                               } else {
+                                       message(LOG | CONSOLE, "Bummer, can't open %s", a->terminal);
+                               }
+                               halt_signal(SIGUSR1);
+                       }
+
+                       /* Make sure the terminal will act fairly normal for us */
+                       set_term(0);
+                       /* Setup stdout, stderr on the supplied terminal */
+                       dup(0);
+                       dup(0);
+
+                       messageD(CONSOLE | LOG, "Trying to re-exec %s", a->command);
+                       execl(a->command, a->command, NULL);
+
+                       message(CONSOLE | LOG, "exec of '%s' failed: %m",
+                                       a->command);
+                       sync();
+                       sleep(2);
+                       init_reboot(RB_HALT_SYSTEM);
+                       loop_forever();
+               }
+       }
+}
+
+static void halt_signal(int sig)
+{
+       shutdown_system();
+       message(CONSOLE | LOG,
+#if #cpu(s390)
+                       /* Seems the s390 console is Wierd(tm). */
+                       "The system is halted. You may reboot now."
+#else
+                       "The system is halted. Press Reset or turn off power"
+#endif
+               );
+       sync();
+
+       /* allow time for last message to reach serial console */
+       sleep(2);
+
+       if (sig == SIGUSR2)
+               init_reboot(RB_POWER_OFF);
+       else
+               init_reboot(RB_HALT_SYSTEM);
+
+       loop_forever();
+}
+
+static void reboot_signal(int sig)
+{
+       shutdown_system();
+       message(CONSOLE | LOG, "Please stand by while rebooting the system.");
+       sync();
+
+       /* allow time for last message to reach serial console */
+       sleep(2);
+
+       init_reboot(RB_AUTOBOOT);
+
+       loop_forever();
+}
+
+static void ctrlaltdel_signal(int sig)
+{
+       run_actions(CTRLALTDEL);
+}
+
+/* The SIGSTOP & SIGTSTP handler */
+static void stop_handler(int sig)
+{
+       int saved_errno = errno;
+
+       got_cont = 0;
+       while (!got_cont)
+               pause();
+       got_cont = 0;
+       errno = saved_errno;
+}
+
+/* The SIGCONT handler */
+static void cont_handler(int sig)
+{
+       got_cont = 1;
+}
+
+#endif                                                 /* ! DEBUG_INIT */
+
+static void new_init_action(int action, const char *command, const char *cons)
+{
+       struct init_action *new_action, *a, *last;
+
+       if (*cons == '\0')
+               cons = console;
+
+       /* do not run entries if console device is not available */
+       if (access(cons, R_OK | W_OK))
+               return;
+       if (strcmp(cons, "/dev/null") == 0 && (action & ASKFIRST))
+               return;
+
+       new_action = calloc((size_t) (1), sizeof(struct init_action));
+       if (!new_action) {
+               message(LOG | CONSOLE, "Memory allocation failure");
+               loop_forever();
+       }
+
+       /* Append to the end of the list */
+       for (a = last = init_action_list; a; a = a->next) {
+               /* don't enter action if it's already in the list,
+                * but do overwrite existing actions */
+               if ((strcmp(a->command, command) == 0) &&
+                   (strcmp(a->terminal, cons) ==0)) {
+                       a->action = action;
+                       free(new_action);
+                       return;
+               }
+               last = a;
+       }
+       if (last) {
+               last->next = new_action;
+       } else {
+               init_action_list = new_action;
+       }
+       strcpy(new_action->command, command);
+       new_action->action = action;
+       strcpy(new_action->terminal, cons);
+#if 0   /* calloc zeroed always */
+       new_action->pid = 0;
+#endif
+       messageD(LOG|CONSOLE, "command='%s' action='%d' terminal='%s'\n",
+               new_action->command, new_action->action, new_action->terminal);
+}
+
+static void delete_init_action(struct init_action *action)
+{
+       struct init_action *a, *b = NULL;
+
+       for (a = init_action_list; a; b = a, a = a->next) {
+               if (a == action) {
+                       if (b == NULL) {
+                               init_action_list = a->next;
+                       } else {
+                               b->next = a->next;
+                       }
+                       free(a);
+                       break;
+               }
+       }
+}
+
+/* Make sure there is enough memory to do something useful. *
+ * Calls "swapon -a" if needed so be sure /etc/fstab is present... */
+static void check_memory(void)
+{
+       struct stat statBuf;
+
+       if (check_free_memory() > 1000)
+               return;
+
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+       if (stat("/etc/fstab", &statBuf) == 0) {
+               /* swapon -a requires /proc typically */
+               new_init_action(SYSINIT, "/bin/mount -t proc proc /proc", "");
+               /* Try to turn on swap */
+               new_init_action(SYSINIT, "/sbin/swapon -a", "");
+               run_actions(SYSINIT);   /* wait and removing */
+               if (check_free_memory() < 1000)
+                       goto goodnight;
+       } else
+               goto goodnight;
+       return;
+#endif
+
+  goodnight:
+       message(CONSOLE, "Sorry, your computer does not have enough memory.");
+       loop_forever();
+}
+
+/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
+ * then parse_inittab() simply adds in some default
+ * actions(i.e., runs INIT_SCRIPT and then starts a pair
+ * of "askfirst" shells).  If CONFIG_FEATURE_USE_INITTAB
+ * _is_ defined, but /etc/inittab is missing, this
+ * results in the same set of default behaviors.
+ */
+static void parse_inittab(void)
+{
+#ifdef CONFIG_FEATURE_USE_INITTAB
+       FILE *file;
+       char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
+       char tmpConsole[CONSOLE_BUFF_SIZE];
+       char *id, *runlev, *action, *command, *eol;
+       const struct init_action_type *a = actions;
+
+
+       file = fopen(INITTAB, "r");
+       if (file == NULL) {
+               /* No inittab file -- set up some default behavior */
+#endif
+               /* Reboot on Ctrl-Alt-Del */
+               new_init_action(CTRLALTDEL, "/sbin/reboot", "");
+               /* Umount all filesystems on halt/reboot */
+               new_init_action(SHUTDOWN, "/bin/umount -a -r", "");
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+               /* Swapoff on halt/reboot */
+               new_init_action(SHUTDOWN, "/sbin/swapoff -a", "");
+#endif
+               /* Prepare to restart init when a HUP is received */
+               new_init_action(RESTART, "/sbin/init", "");
+               /* Askfirst shell on tty1-4 */
+               new_init_action(ASKFIRST, bb_default_login_shell, "");
+               new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
+               new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
+               new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
+               /* sysinit */
+               new_init_action(SYSINIT, INIT_SCRIPT, "");
+
+               return;
+#ifdef CONFIG_FEATURE_USE_INITTAB
+       }
+
+       while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
+               /* Skip leading spaces */
+               for (id = buf; *id == ' ' || *id == '\t'; id++);
+
+               /* Skip the line if it's a comment */
+               if (*id == '#' || *id == '\n')
+                       continue;
+
+               /* Trim the trailing \n */
+               eol = strrchr(id, '\n');
+               if (eol != NULL)
+                       *eol = '\0';
+
+               /* Keep a copy around for posterity's sake (and error msgs) */
+               strcpy(lineAsRead, buf);
+
+               /* Separate the ID field from the runlevels */
+               runlev = strchr(id, ':');
+               if (runlev == NULL || *(runlev + 1) == '\0') {
+                       message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
+                       continue;
+               } else {
+                       *runlev = '\0';
+                       ++runlev;
+               }
+
+               /* Separate the runlevels from the action */
+               action = strchr(runlev, ':');
+               if (action == NULL || *(action + 1) == '\0') {
+                       message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
+                       continue;
+               } else {
+                       *action = '\0';
+                       ++action;
+               }
+
+               /* Separate the action from the command */
+               command = strchr(action, ':');
+               if (command == NULL || *(command + 1) == '\0') {
+                       message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
+                       continue;
+               } else {
+                       *command = '\0';
+                       ++command;
+               }
+
+               /* Ok, now process it */
+               for (a = actions; a->name != 0; a++) {
+                       if (strcmp(a->name, action) == 0) {
+                               if (*id != '\0') {
+                                       if(strncmp(id, "/dev/", 5) == 0)
+                                               id += 5;
+                                       strcpy(tmpConsole, "/dev/");
+                                       safe_strncpy(tmpConsole + 5, id,
+                                               CONSOLE_BUFF_SIZE - 5);
+                                       id = tmpConsole;
+                               }
+                               new_init_action(a->action, command, id);
+                               break;
+                       }
+               }
+               if (a->name == 0) {
+                       /* Choke on an unknown action */
+                       message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
+               }
+       }
+       fclose(file);
+       return;
+#endif                                                 /* CONFIG_FEATURE_USE_INITTAB */
+}
+
+#ifdef CONFIG_FEATURE_USE_INITTAB
+static void reload_signal(int sig)
+{
+       struct init_action *a, *tmp;
+
+       message(LOG, "Reloading /etc/inittab");
+
+       /* disable old entrys */
+       for (a = init_action_list; a; a = a->next ) {
+               a->action = ONCE;
+       }
+
+       parse_inittab();
+
+       /* remove unused entrys */
+       for (a = init_action_list; a; a = tmp) {
+               tmp = a->next;
+               if (a->action & (ONCE | SYSINIT | WAIT ) &&
+                               a->pid == 0 ) {
+                       delete_init_action(a);
+               }
+       }
+       run_actions(RESPAWN);
+       return;
+}
+#endif                                                 /* CONFIG_FEATURE_USE_INITTAB */
+
+extern int init_main(int argc, char **argv)
+{
+       struct init_action *a;
+       pid_t wpid;
+       int status;
+
+       if (argc > 1 && !strcmp(argv[1], "-q")) {
+               return kill_init(SIGHUP);
+       }
+#ifndef DEBUG_INIT
+       /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
+       if (getpid() != 1
+#ifdef CONFIG_FEATURE_INITRD
+               && strstr(bb_applet_name, "linuxrc") == NULL
+#endif
+               ) {
+               bb_show_usage();
+       }
+       /* Set up sig handlers  -- be sure to
+        * clear all of these in run() */
+       signal(SIGHUP, exec_signal);
+       signal(SIGUSR1, halt_signal);
+       signal(SIGUSR2, halt_signal);
+       signal(SIGINT, ctrlaltdel_signal);
+       signal(SIGTERM, reboot_signal);
+       signal(SIGCONT, cont_handler);
+       signal(SIGSTOP, stop_handler);
+       signal(SIGTSTP, stop_handler);
+
+       /* Turn off rebooting via CTL-ALT-DEL -- we get a
+        * SIGINT on CAD so we can shut things down gracefully... */
+       init_reboot(RB_DISABLE_CAD);
+#endif
+
+       /* Figure out where the default console should be */
+       console_init();
+
+       /* Close whatever files are open, and reset the console. */
+       close(0);
+       close(1);
+       close(2);
+
+       if (device_open(console, O_RDWR | O_NOCTTY) == 0) {
+               set_term(0);
+               close(0);
+       }
+
+       chdir("/");
+       setsid();
+       {
+               const char * const *e;
+               /* Make sure environs is set to something sane */
+               for(e = environment; *e; e++)
+                       putenv((char *) *e);
+       }
+       /* Hello world */
+       message(MAYBE_CONSOLE | LOG, "init started:  %s", bb_msg_full_version);
+
+       /* Make sure there is enough memory to do something useful. */
+       check_memory();
+
+       /* Check if we are supposed to be in single user mode */
+       if (argc > 1 && (!strcmp(argv[1], "single") ||
+                                        !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) {
+               /* Start a shell on console */
+               new_init_action(RESPAWN, bb_default_login_shell, "");
+       } else {
+               /* Not in single user mode -- see what inittab says */
+
+               /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
+                * then parse_inittab() simply adds in some default
+                * actions(i.e., runs INIT_SCRIPT and then starts a pair
+                * of "askfirst" shells */
+               parse_inittab();
+       }
+
+       /* Make the command line just say "init"  -- thats all, nothing else */
+       fixup_argv(argc, argv, "init");
+
+       /* Now run everything that needs to be run */
+
+       /* First run the sysinit command */
+       run_actions(SYSINIT);
+
+       /* Next run anything that wants to block */
+       run_actions(WAIT);
+
+       /* Next run anything to be run only once */
+       run_actions(ONCE);
+
+#ifdef CONFIG_FEATURE_USE_INITTAB
+       /* Redefine SIGHUP to reread /etc/inittab */
+       signal(SIGHUP, reload_signal);
+#else
+       signal(SIGHUP, SIG_IGN);
+#endif /* CONFIG_FEATURE_USE_INITTAB */
+
+
+       /* Now run the looping stuff for the rest of forever */
+       while (1) {
+               /* run the respawn stuff */
+               run_actions(RESPAWN);
+
+               /* run the askfirst stuff */
+               run_actions(ASKFIRST);
+
+               /* Don't consume all CPU time -- sleep a bit */
+               sleep(1);
+
+               /* Wait for a child process to exit */
+               wpid = wait(&status);
+               while (wpid > 0) {
+                       /* Find out who died and clean up their corpse */
+                       for (a = init_action_list; a; a = a->next) {
+                               if (a->pid == wpid) {
+                                       /* Set the pid to 0 so that the process gets
+                                        * restarted by run_actions() */
+                                       a->pid = 0;
+                                       message(LOG, "Process '%s' (pid %d) exited.  "
+                                                       "Scheduling it for restart.",
+                                                       a->command, wpid);
+                               }
+                       }
+                       /* see if anyone else is waiting to be reaped */
+                       wpid = waitpid (-1, &status, WNOHANG);
+               }
+       }
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/init/init_shared.c b/busybox/init/init_shared.c
new file mode 100644 (file)
index 0000000..0ad55a4
--- /dev/null
@@ -0,0 +1,95 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Stuff shared between init, reboot, halt, and poweroff
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/reboot.h>
+#include <sys/syslog.h>
+#include "busybox.h"
+#include "init_shared.h"
+
+extern int kill_init(int sig)
+{
+#ifdef CONFIG_FEATURE_INITRD
+       /* don't assume init's pid == 1 */
+       long *pid = find_pid_by_name("init");
+       if (!pid || *pid<=0) {
+               pid = find_pid_by_name("linuxrc");
+               if (!pid || *pid<=0)
+                       bb_error_msg_and_die("no process killed");
+       }
+       return(kill(*pid, sig));
+#else
+       return(kill(1, sig));
+#endif
+}
+
+#ifndef CONFIG_INIT
+const char * const bb_shutdown_format = "\r%s\n";
+extern int bb_shutdown_system(unsigned long magic)
+{
+       int pri = LOG_KERN|LOG_NOTICE|LOG_FACMASK;
+       const char *message;
+
+       /* Don't kill ourself */
+       signal(SIGTERM,SIG_IGN);
+       signal(SIGHUP,SIG_IGN);
+       setpgrp();
+
+       /* Allow Ctrl-Alt-Del to reboot system. */
+#ifndef RB_ENABLE_CAD
+#define RB_ENABLE_CAD  0x89abcdef
+#endif
+       reboot(RB_ENABLE_CAD);
+
+       openlog(bb_applet_name, 0, pri);
+
+       message = "\nThe system is going down NOW !!";
+       syslog(pri, "%s", message);
+       printf(bb_shutdown_format, message);
+
+       sync();
+
+       /* Send signals to every process _except_ pid 1 */
+       message = "Sending SIGTERM to all processes.";
+       syslog(pri, "%s", message);
+       printf(bb_shutdown_format, message);
+
+       kill(-1, SIGTERM);
+       sleep(1);
+       sync();
+
+       message = "Sending SIGKILL to all processes.";
+       syslog(pri, "%s", message);
+       printf(bb_shutdown_format, message);
+
+       kill(-1, SIGKILL);
+       sleep(1);
+
+       sync();
+
+       reboot(magic);
+       return 0; /* Shrug */
+}
+#endif
diff --git a/busybox/init/init_shared.h b/busybox/init/init_shared.h
new file mode 100644 (file)
index 0000000..1e4cfac
--- /dev/null
@@ -0,0 +1,3 @@
+extern int kill_init(int sig);
+extern int bb_shutdown_system(unsigned long magic);
+
diff --git a/busybox/init/mesg.c b/busybox/init/mesg.c
new file mode 100644 (file)
index 0000000..7fd9d24
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  mesg implementation for busybox
+ *
+ *  Copyright (c) 2002 Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+#ifdef USE_TTY_GROUP
+#define S_IWGRP_OR_S_IWOTH     S_IWGRP
+#else
+#define S_IWGRP_OR_S_IWOTH     (S_IWGRP | S_IWOTH)
+#endif
+
+extern int mesg_main(int argc, char *argv[])
+{
+       struct stat sb;
+       char *tty;
+       char c = 0;
+
+       if ((--argc == 0)
+               || ((argc == 1) && (((c = **++argv) == 'y') || (c == 'n')))) {
+               if ((tty = ttyname(STDERR_FILENO)) == NULL) {
+                       tty = "ttyname";
+               } else if (stat(tty, &sb) == 0) {
+                       if (argc == 0) {
+                               puts(((sb.st_mode & (S_IWGRP | S_IWOTH)) ==
+                                         0) ? "is n" : "is y");
+                               return EXIT_SUCCESS;
+                       }
+                       if (chmod
+                               (tty,
+                                (c ==
+                                 'y') ? sb.st_mode | (S_IWGRP_OR_S_IWOTH) : sb.
+                                st_mode & ~(S_IWGRP | S_IWOTH)) == 0) {
+                               return EXIT_SUCCESS;
+                       }
+               }
+               bb_perror_msg_and_die("%s", tty);
+       }
+       bb_show_usage();
+}
diff --git a/busybox/init/poweroff.c b/busybox/init/poweroff.c
new file mode 100644 (file)
index 0000000..8169508
--- /dev/null
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini poweroff implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/reboot.h>
+#include "busybox.h"
+#include "init_shared.h"
+
+
+extern int poweroff_main(int argc, char **argv)
+{
+       char *delay; /* delay in seconds before rebooting */
+
+       if(bb_getopt_ulflags(argc, argv, "d:", &delay)) {
+               sleep(atoi(delay));
+       }
+
+#ifndef CONFIG_INIT
+#ifndef RB_POWER_OFF
+#define RB_POWER_OFF           0x4321fedc
+#endif
+       return(bb_shutdown_system(RB_POWER_OFF));
+#else
+       return kill_init(SIGUSR2);
+#endif
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/init/reboot.c b/busybox/init/reboot.c
new file mode 100644 (file)
index 0000000..ca4e9a2
--- /dev/null
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini reboot implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/reboot.h>
+#include "busybox.h"
+#include "init_shared.h"
+
+
+extern int reboot_main(int argc, char **argv)
+{
+       char *delay; /* delay in seconds before rebooting */
+
+       if(bb_getopt_ulflags(argc, argv, "d:", &delay)) {
+               sleep(atoi(delay));
+       }
+
+#ifndef CONFIG_INIT
+#ifndef RB_AUTOBOOT
+#define RB_AUTOBOOT            0x01234567
+#endif
+       return(bb_shutdown_system(RB_AUTOBOOT));
+#else
+       return kill_init(SIGTERM);
+#endif
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/.cvsignore b/busybox/libbb/.cvsignore
new file mode 100644 (file)
index 0000000..2bbe016
--- /dev/null
@@ -0,0 +1 @@
+loop.h
diff --git a/busybox/libbb/Makefile b/busybox/libbb/Makefile
new file mode 100644 (file)
index 0000000..e94c052
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/libbb
+LIBBB_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/libbb/Makefile.in b/busybox/libbb/Makefile.in
new file mode 100644 (file)
index 0000000..85d4a96
--- /dev/null
@@ -0,0 +1,107 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+LIBBB_AR:=libbb.a
+ifndef $(LIBBB_DIR)
+LIBBB_DIR:=$(top_builddir)/libbb/
+endif
+srcdir=$(top_srcdir)/libbb
+
+LIBBB_SRC:= \
+       bb_asprintf.c ask_confirmation.c change_identity.c chomp.c \
+       compare_string_array.c concat_path_file.c copy_file.c copyfd.c \
+       correct_password.c create_icmp_socket.c create_icmp6_socket.c \
+       device_open.c dump.c error_msg.c error_msg_and_die.c find_mount_point.c \
+       find_pid_by_name.c find_root_device.c fgets_str.c full_read.c \
+       full_write.c get_last_path_component.c get_line_from_file.c get_ug_id.c \
+       get_terminal_width_height.c hash_fd.c herror_msg.c herror_msg_and_die.c \
+       human_readable.c inet_common.c inode_hash.c interface.c isdirectory.c \
+       kernel_version.c last_char_is.c llist_add_to.c login.c loop.c \
+       make_directory.c mode_string.c module_syscalls.c mtab.c mtab_file.c \
+       my_getgrgid.c my_getgrnam.c my_getpwnam.c my_getug.c\
+       my_getpwuid.c obscure.c parse_mode.c parse_number.c perror_msg.c \
+       perror_msg_and_die.c print_file.c get_console.c \
+       process_escape_sequence.c procps.c pwd2spwd.c pw_encrypt.c qmodule.c \
+       read_package_field.c recursive_action.c remove_file.c \
+       restricted_shell.c run_parts.c run_shell.c safe_read.c safe_write.c \
+       safe_strncpy.c setup_environment.c simplify_path.c syscalls.c \
+       trim.c u_signal_names.c vdprintf.c verror_msg.c \
+       vherror_msg.c vperror_msg.c wfopen.c xconnect.c xgetcwd.c \
+       xgethostbyname.c xgethostbyname2.c xreadlink.c xregcomp.c xgetlarg.c \
+       get_terminal_width_height.c fclose_nonstdin.c fflush_stdout_and_exit.c \
+       getopt_ulflags.c default_error_retval.c wfopen_input.c speed_table.c \
+       perror_nomsg_and_die.c perror_nomsg.c skip_whitespace.c bb_askpass.c \
+       warn_ignoring_args.c concat_subpath_file.c vfork_daemon_rexec.c
+
+LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC))
+
+LIBBB_MSRC0:=$(srcdir)/messages.c
+LIBBB_MOBJ0:=full_version.o \
+       memory_exhausted.o invalid_date.o io_error.o \
+       write_error.o name_longer_than_foo.o unknown.o \
+       can_not_create_raw_socket.o perm_denied_are_you_root.o \
+       shadow_file.o passwd_file.o group_file.o gshadow_file.o nologin_file.o \
+       securetty_file.o motd_file.o \
+       msg_standard_input.o msg_standard_output.o shell_file.o
+
+LIBBB_MSRC1:=$(srcdir)/xfuncs.c
+LIBBB_MOBJ1:=xmalloc.o xrealloc.o xcalloc.o xstrdup.o xstrndup.o \
+       xfopen.o xopen.o xread.o xread_all.o xread_char.o \
+       xferror.o xferror_stdout.o xfflush_stdout.o strlen.o
+
+LIBBB_MSRC2:=$(srcdir)/printf.c
+LIBBB_MOBJ2:=bb_vfprintf.o bb_vprintf.o bb_fprintf.o bb_printf.o
+
+LIBBB_MSRC3:=$(srcdir)/xgetularg.c
+LIBBB_MOBJ3:=xgetularg_bnd_sfx.o xgetlarg_bnd_sfx.o getlarg10_sfx.o \
+       xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o
+
+LIBBB_MSRC4:=$(srcdir)/safe_strtol.c
+LIBBB_MOBJ4:=safe_strtoi.o safe_strtod.o safe_strtol.o safe_strtoul.o
+
+LIBBB_MOBJS0=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ0))
+LIBBB_MOBJS1=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ1))
+LIBBB_MOBJS2=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ2))
+LIBBB_MOBJS3=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ3))
+LIBBB_MOBJS4=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ4))
+
+libraries-y+=$(LIBBB_DIR)$(LIBBB_AR)
+
+$(LIBBB_DIR)$(LIBBB_AR): $(LIBBB_OBJS) $(LIBBB_MOBJS0) $(LIBBB_MOBJS1) \
+       $(LIBBB_MOBJS2) $(LIBBB_MOBJS3) $(LIBBB_MOBJS4)
+       $(AR) -ro $@ $(LIBBB_OBJS) $(LIBBB_MOBJS0) $(LIBBB_MOBJS1) \
+               $(LIBBB_MOBJS2) $(LIBBB_MOBJS3) $(LIBBB_MOBJS4)
+
+$(LIBBB_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
+$(LIBBB_MOBJS0): $(LIBBB_MSRC0)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
+$(LIBBB_MOBJS1): $(LIBBB_MSRC1)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
+$(LIBBB_MOBJS2): $(LIBBB_MSRC2)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
+$(LIBBB_MOBJS3): $(LIBBB_MSRC3)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
+$(LIBBB_MOBJS4): $(LIBBB_MSRC4)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
diff --git a/busybox/libbb/README b/busybox/libbb/README
new file mode 100644 (file)
index 0000000..4f28f7e
--- /dev/null
@@ -0,0 +1,11 @@
+Please see the LICENSE file for copyright information (GPLv2)
+
+libbb is BusyBox's utility library.  All of this stuff used to be stuffed into
+a single file named utility.c.  When I split utility.c to create libbb, some of
+the very oldest stuff ended up without their original copyright and licensing
+information (which is now lost in the mists of time).  If you see something
+that you wrote that is mis-attributed, do let me know so we can fix that up.
+
+       Erik Andersen
+       <andersen@codepoet.org>
+
diff --git a/busybox/libbb/ask_confirmation.c b/busybox/libbb/ask_confirmation.c
new file mode 100644 (file)
index 0000000..a99a4e7
--- /dev/null
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_ask_confirmation implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Read a line from stdin.  If the first non-whitespace char is 'y' or 'Y',
+ * return 1.  Otherwise return 0.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "libbb.h"
+
+int bb_ask_confirmation(void)
+{
+       int retval = 0;
+       int first = 1;
+       int c;
+
+       while (((c = getchar()) != EOF) && (c != '\n')) {
+               /* Make sure we get the actual function call for isspace,
+                * as speed is not critical here. */
+               if (first && !(isspace)(c)) {
+                       --first;
+                       if ((c == 'y') || (c == 'Y')) {
+                               ++retval;
+                       }
+               }
+       }
+
+       return retval;
+}
diff --git a/busybox/libbb/bb_askpass.c b/busybox/libbb/bb_askpass.c
new file mode 100644 (file)
index 0000000..1ae1520
--- /dev/null
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Ask for a password
+ * I use a static buffer in this function.  Plan accordingly.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#define PWD_BUFFER_SIZE 256
+
+
+/* do nothing signal handler */
+static void askpass_timeout(int ignore)
+{
+}
+
+char *bb_askpass(int timeout, const char * prompt)
+{
+       char *ret;
+       int i, size;
+       struct sigaction sa;
+       struct termios old, new;
+       static char passwd[PWD_BUFFER_SIZE];
+
+       tcgetattr(STDIN_FILENO, &old);
+
+       size = sizeof(passwd);
+       ret = passwd;
+       memset(passwd, 0, size);
+
+       fputs(prompt, stdout);
+       fflush(stdout);
+
+       tcgetattr(STDIN_FILENO, &new);
+       new.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
+       new.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
+       tcsetattr(STDIN_FILENO, TCSANOW, &new);
+
+       if (timeout) {
+               sa.sa_flags = 0;
+               sa.sa_handler = askpass_timeout;
+               sigaction(SIGALRM, &sa, NULL);
+               alarm(timeout);
+       }
+
+       if (read(STDIN_FILENO, passwd, size-1) <= 0) {
+               ret = NULL;
+       } else {
+               for(i = 0; i < size && passwd[i]; i++) {
+                       if (passwd[i]== '\r' || passwd[i] == '\n') {
+                               passwd[i]= 0;
+                               break;
+                       }
+               }
+       }
+
+       if (timeout) {
+               alarm(0);
+       }
+
+       tcsetattr(STDIN_FILENO, TCSANOW, &old);
+       fputs("\n", stdout);
+       fflush(stdout);
+       return ret;
+}
+
diff --git a/busybox/libbb/bb_asprintf.c b/busybox/libbb/bb_asprintf.c
new file mode 100644 (file)
index 0000000..a3ba424
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+   Copyright (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "libbb.h"
+
+void bb_xasprintf(char **string_ptr, const char *format, ...)
+{
+       va_list p;
+       int r;
+
+       va_start(p, format);
+       r = vasprintf(string_ptr, format, p);
+       va_end(p);
+
+       if (r < 0) {
+               bb_perror_msg_and_die("bb_xasprintf");
+       }
+}
diff --git a/busybox/libbb/change_identity.c b/busybox/libbb/change_identity.c
new file mode 100644 (file)
index 0000000..adebad8
--- /dev/null
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+
+#include "libbb.h"
+
+
+/* Become the user and group(s) specified by PW.  */
+const char *change_identity_e2str ( const struct passwd *pw )
+{
+       if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 )
+               return "cannot set groups";
+       endgrent ( );
+
+       if ( setgid ( pw-> pw_gid ))
+               return "cannot set group id";
+       if ( setuid ( pw->pw_uid ))
+               return "cannot set user id";
+       return NULL;
+}
+
+void change_identity ( const struct passwd *pw )
+{
+       const char *err_msg = change_identity_e2str(pw);
+
+       if(err_msg)
+               bb_perror_msg_and_die ( "%s", err_msg );
+}
diff --git a/busybox/libbb/chomp.c b/busybox/libbb/chomp.c
new file mode 100644 (file)
index 0000000..774e533
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "libbb.h"
+
+
+void chomp(char *s)
+{
+       char *lc = last_char_is(s, '\n');
+
+       if(lc)
+               *lc = 0;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/compare_string_array.c b/busybox/libbb/compare_string_array.c
new file mode 100644 (file)
index 0000000..993b462
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+/* returns the array number of the string */
+extern unsigned short compare_string_array(const char *string_array[], const char *key)
+{
+       unsigned short i;
+
+       for (i = 0; string_array[i] != 0; i++) {
+               if (strcmp(string_array[i], key) == 0) {
+                       break;
+               }
+       }
+       return(i);
+}
+
diff --git a/busybox/libbb/concat_path_file.c b/busybox/libbb/concat_path_file.c
new file mode 100644 (file)
index 0000000..77c0545
--- /dev/null
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/* concatenate path and file name to new allocation buffer,
+ * not addition '/' if path name already have '/'
+*/
+
+#include <string.h>
+#include "libbb.h"
+
+extern char *concat_path_file(const char *path, const char *filename)
+{
+       char *outbuf;
+       char *lc;
+
+       if (!path)
+           path="";
+       lc = last_char_is(path, '/');
+       while (*filename == '/')
+               filename++;
+       bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename);
+
+       return outbuf;
+}
diff --git a/busybox/libbb/concat_subpath_file.c b/busybox/libbb/concat_subpath_file.c
new file mode 100644 (file)
index 0000000..6d86f5e
--- /dev/null
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) (C) 2003  Vladimir Oleynik  <dzo@simtreas.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+   This function make special for recursive actions with usage
+   concat_path_file(path, filename)
+   and skiping "." and ".." directory entries
+*/
+
+#include "libbb.h"
+
+extern char *concat_subpath_file(const char *path, const char *f)
+{
+       if(f && *f == '.' && (!f[1] || (f[1] == '.' && !f[2])))
+               return NULL;
+       return concat_path_file(path, f);
+}
diff --git a/busybox/libbb/copy_file.c b/busybox/libbb/copy_file.c
new file mode 100644 (file)
index 0000000..68a1ded
--- /dev/null
@@ -0,0 +1,268 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini copy_file implementation for busybox
+ *
+ * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <errno.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "busybox.h"
+
+int copy_file(const char *source, const char *dest, int flags)
+{
+       struct stat source_stat;
+       struct stat dest_stat;
+       int dest_exists = 0;
+       int status = 0;
+
+       if ((!(flags & FILEUTILS_DEREFERENCE) &&
+                       lstat(source, &source_stat) < 0) ||
+                       ((flags & FILEUTILS_DEREFERENCE) &&
+                        stat(source, &source_stat) < 0)) {
+               bb_perror_msg("%s", source);
+               return -1;
+       }
+
+       if (lstat(dest, &dest_stat) < 0) {
+               if (errno != ENOENT) {
+                       bb_perror_msg("unable to stat `%s'", dest);
+                       return -1;
+               }
+       } else {
+               if (source_stat.st_dev == dest_stat.st_dev &&
+                       source_stat.st_ino == dest_stat.st_ino) {
+               bb_error_msg("`%s' and `%s' are the same file", source, dest);
+               return -1;
+       }
+               dest_exists = 1;
+       }
+
+       if (S_ISDIR(source_stat.st_mode)) {
+               DIR *dp;
+               struct dirent *d;
+               mode_t saved_umask = 0;
+
+               if (!(flags & FILEUTILS_RECUR)) {
+                       bb_error_msg("%s: omitting directory", source);
+                       return -1;
+               }
+
+               /* Create DEST.  */
+               if (dest_exists) {
+                       if (!S_ISDIR(dest_stat.st_mode)) {
+                               bb_error_msg("`%s' is not a directory", dest);
+                               return -1;
+                       }
+               } else {
+                       mode_t mode;
+                       saved_umask = umask(0);
+
+                       mode = source_stat.st_mode;
+                       if (!(flags & FILEUTILS_PRESERVE_STATUS))
+                               mode = source_stat.st_mode & ~saved_umask;
+                       mode |= S_IRWXU;
+
+                       if (mkdir(dest, mode) < 0) {
+                               umask(saved_umask);
+                               bb_perror_msg("cannot create directory `%s'", dest);
+                               return -1;
+                       }
+
+                       umask(saved_umask);
+               }
+
+               /* Recursively copy files in SOURCE.  */
+               if ((dp = opendir(source)) == NULL) {
+                       bb_perror_msg("unable to open directory `%s'", source);
+                       status = -1;
+                       goto end;
+               }
+
+               while ((d = readdir(dp)) != NULL) {
+                       char *new_source, *new_dest;
+
+                       new_source = concat_subpath_file(source, d->d_name);
+                       if(new_source == NULL)
+                               continue;
+                       new_dest = concat_path_file(dest, d->d_name);
+                       if (copy_file(new_source, new_dest, flags) < 0)
+                               status = -1;
+                       free(new_source);
+                       free(new_dest);
+               }
+               /* closedir have only EBADF error, but "dp" not changes */
+               closedir(dp);
+
+               if (!dest_exists &&
+                               chmod(dest, source_stat.st_mode & ~saved_umask) < 0) {
+                       bb_perror_msg("unable to change permissions of `%s'", dest);
+                       status = -1;
+               }
+       } else if (S_ISREG(source_stat.st_mode)) {
+               int src_fd;
+               int dst_fd;
+#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
+               char *link_name;
+
+               if (!(flags & FILEUTILS_DEREFERENCE) &&
+                               is_in_ino_dev_hashtable(&source_stat, &link_name)) {
+                       if (link(link_name, dest) < 0) {
+                               bb_perror_msg("unable to link `%s'", dest);
+                               return -1;
+                       }
+
+                       return 0;
+               }
+#endif
+               src_fd = open(source, O_RDONLY);
+               if (src_fd == -1) {
+                       bb_perror_msg("unable to open `%s'", source);
+                       return(-1);
+               }
+
+               if (dest_exists) {
+                       if (flags & FILEUTILS_INTERACTIVE) {
+                               bb_error_msg("overwrite `%s'? ", dest);
+                               if (!bb_ask_confirmation()) {
+                                       close (src_fd);
+                                       return 0;
+                               }
+                       }
+
+                       dst_fd = open(dest, O_WRONLY|O_TRUNC);
+                       if (dst_fd == -1) {
+                               if (!(flags & FILEUTILS_FORCE)) {
+                                       bb_perror_msg("unable to open `%s'", dest);
+                                       close(src_fd);
+                                       return -1;
+                               }
+
+                               if (unlink(dest) < 0) {
+                                       bb_perror_msg("unable to remove `%s'", dest);
+                                       close(src_fd);
+                                       return -1;
+                               }
+
+                               dest_exists = 0;
+                       }
+               }
+
+               if (!dest_exists) {
+                       dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode);
+                       if (dst_fd == -1) {
+                               bb_perror_msg("unable to open `%s'", dest);
+                               close(src_fd);
+                               return(-1);
+                       }
+               }
+
+               if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+                       status = -1;
+
+               if (close(dst_fd) < 0) {
+                       bb_perror_msg("unable to close `%s'", dest);
+                       status = -1;
+               }
+
+               if (close(src_fd) < 0) {
+                       bb_perror_msg("unable to close `%s'", source);
+                       status = -1;
+               }
+                       }
+       else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
+           S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ||
+           S_ISLNK(source_stat.st_mode)) {
+
+               if (dest_exists &&
+                      ((flags & FILEUTILS_FORCE) == 0 || unlink(dest) < 0)) {
+                               bb_perror_msg("unable to remove `%s'", dest);
+                               return -1;
+
+                       }
+       } else {
+               bb_error_msg("internal error: unrecognized file type");
+               return -1;
+               }
+       if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
+           S_ISSOCK(source_stat.st_mode)) {
+               if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
+                       bb_perror_msg("unable to create `%s'", dest);
+                       return -1;
+               }
+       } else if (S_ISFIFO(source_stat.st_mode)) {
+               if (mkfifo(dest, source_stat.st_mode) < 0) {
+                       bb_perror_msg("cannot create fifo `%s'", dest);
+                       return -1;
+               }
+       } else if (S_ISLNK(source_stat.st_mode)) {
+               char *lpath;
+
+               lpath = xreadlink(source);
+               if (symlink(lpath, dest) < 0) {
+                       bb_perror_msg("cannot create symlink `%s'", dest);
+                       return -1;
+               }
+               free(lpath);
+
+#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
+               if (flags & FILEUTILS_PRESERVE_STATUS)
+                       if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
+                               bb_perror_msg("unable to preserve ownership of `%s'", dest);
+#endif
+
+#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
+               add_to_ino_dev_hashtable(&source_stat, dest);
+#endif
+
+               return 0;
+       }
+
+#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
+       if (! S_ISDIR(source_stat.st_mode)) {
+               add_to_ino_dev_hashtable(&source_stat, dest);
+       }
+#endif
+
+end:
+
+       if (flags & FILEUTILS_PRESERVE_STATUS) {
+               struct utimbuf times;
+
+               times.actime = source_stat.st_atime;
+               times.modtime = source_stat.st_mtime;
+               if (utime(dest, &times) < 0)
+                       bb_perror_msg("unable to preserve times of `%s'", dest);
+               if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
+                       source_stat.st_mode &= ~(S_ISUID | S_ISGID);
+                       bb_perror_msg("unable to preserve ownership of `%s'", dest);
+               }
+               if (chmod(dest, source_stat.st_mode) < 0)
+                       bb_perror_msg("unable to preserve permissions of `%s'", dest);
+       }
+
+       return status;
+}
diff --git a/busybox/libbb/copyfd.c b/busybox/libbb/copyfd.c
new file mode 100644 (file)
index 0000000..bf0a390
--- /dev/null
@@ -0,0 +1,90 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "busybox.h"
+
+
+#if BUFSIZ < 4096
+#undef BUFSIZ
+#define BUFSIZ 4096
+#endif
+
+
+/* If size is 0 copy until EOF */
+static size_t bb_full_fd_action(int src_fd, int dst_fd, const size_t size)
+{
+       size_t read_total = 0;
+       RESERVE_CONFIG_BUFFER(buffer,BUFSIZ);
+
+       while ((size == 0) || (read_total < size)) {
+               size_t read_try;
+               ssize_t read_actual;
+
+               if ((size == 0) || (size - read_total > BUFSIZ)) {
+                       read_try = BUFSIZ;
+               } else {
+                       read_try = size - read_total;
+               }
+
+               read_actual = safe_read(src_fd, buffer, read_try);
+               if (read_actual > 0) {
+                       if ((dst_fd >= 0) && (bb_full_write(dst_fd, buffer, (size_t) read_actual) != read_actual)) {
+                               bb_perror_msg(bb_msg_write_error);      /* match Read error below */
+                               break;
+                       }
+               }
+               else if (read_actual == 0) {
+                       if (size) {
+                               bb_error_msg("Unable to read all data");
+                       }
+                       break;
+               } else {
+                       /* read_actual < 0 */
+                       bb_perror_msg("Read error");
+                       break;
+               }
+
+               read_total += read_actual;
+       }
+
+       RELEASE_CONFIG_BUFFER(buffer);
+
+       return(read_total);
+}
+
+
+extern int bb_copyfd_size(int fd1, int fd2, const off_t size)
+{
+       if (size) {
+               return(bb_full_fd_action(fd1, fd2, size));
+       }
+       return(0);
+}
+
+extern int bb_copyfd_eof(int fd1, int fd2)
+{
+       return(bb_full_fd_action(fd1, fd2, 0));
+}
diff --git a/busybox/libbb/correct_password.c b/busybox/libbb/correct_password.c
new file mode 100644 (file)
index 0000000..570aa7e
--- /dev/null
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <crypt.h>
+
+#include "libbb.h"
+
+
+
+/* Ask the user for a password.
+   Return 1 if the user gives the correct password for entry PW,
+   0 if not.  Return 1 without asking for a password if run by UID 0
+   or if PW has an empty password.  */
+
+int correct_password ( const struct passwd *pw )
+{
+       char *unencrypted, *encrypted, *correct;
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) {
+               struct spwd *sp = getspnam ( pw-> pw_name );
+
+               if ( !sp )
+                       bb_error_msg_and_die ( "no valid shadow password" );
+
+               correct = sp-> sp_pwdp;
+       }
+       else
+#endif
+       correct = pw-> pw_passwd;
+
+       if ( correct == 0 || correct[0] == '\0' )
+               return 1;
+
+       unencrypted = bb_askpass ( 0, "Password: " );
+       if ( !unencrypted )
+       {
+               return 0;
+       }
+       encrypted = crypt ( unencrypted, correct );
+       memset ( unencrypted, 0, bb_strlen ( unencrypted ));
+       return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0;
+}
diff --git a/busybox/libbb/create_icmp6_socket.c b/busybox/libbb/create_icmp6_socket.c
new file mode 100644 (file)
index 0000000..d8ff35a
--- /dev/null
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * create raw socket for icmp (IPv6 version) protocol test permission
+ * and drop root privileges if running setuid
+ *
+ */
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <unistd.h>
+#include "libbb.h"
+
+#ifdef CONFIG_FEATURE_IPV6
+int create_icmp6_socket(void)
+{
+       struct protoent *proto;
+       int sock;
+
+       proto = getprotobyname("ipv6-icmp");
+       /* if getprotobyname failed, just silently force
+        * proto->p_proto to have the correct value for "ipv6-icmp" */
+       if ((sock = socket(AF_INET6, SOCK_RAW,
+                       (proto ? proto->p_proto : IPPROTO_ICMPV6))) < 0) {
+               if (errno == EPERM)
+                       bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+               else
+                       bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+       }
+
+       /* drop root privs if running setuid */
+       setuid(getuid());
+
+       return sock;
+}
+#endif
diff --git a/busybox/libbb/create_icmp_socket.c b/busybox/libbb/create_icmp_socket.c
new file mode 100644 (file)
index 0000000..26120a6
--- /dev/null
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * create raw socket for icmp protocol test permission
+ * and drop root privileges if running setuid
+ *
+ */
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <unistd.h>
+#include "libbb.h"
+
+int create_icmp_socket(void)
+{
+       struct protoent *proto;
+       int sock;
+
+       proto = getprotobyname("icmp");
+       /* if getprotobyname failed, just silently force
+        * proto->p_proto to have the correct value for "icmp" */
+       if ((sock = socket(AF_INET, SOCK_RAW,
+                       (proto ? proto->p_proto : 1))) < 0) {        /* 1 == ICMP */
+               if (errno == EPERM)
+                       bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+               else
+                       bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+       }
+
+       /* drop root privs if running setuid */
+       setuid(getuid());
+
+       return sock;
+}
diff --git a/busybox/libbb/default_error_retval.c b/busybox/libbb/default_error_retval.c
new file mode 100644 (file)
index 0000000..35c34b9
--- /dev/null
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Seems silly to copyright a global variable.  ;-)  Oh well.
+ *
+ * At least one applet (cmp) returns a value different from the typical
+ * EXIT_FAILURE values (1) when an error occurs.  So, make it configurable
+ * by the applet.  I suppose we could use a wrapper function to set it, but
+ * that too seems silly.
+ */
+
+#include <stdlib.h>
+#include "libbb.h"
+
+int bb_default_error_retval = EXIT_FAILURE;
diff --git a/busybox/libbb/device_open.c b/busybox/libbb/device_open.c
new file mode 100644 (file)
index 0000000..61f954f
--- /dev/null
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include "libbb.h"
+
+
+/* try to open up the specified device */
+extern int device_open(const char *device, int mode)
+{
+       int m, f, fd = -1;
+
+       m = mode | O_NONBLOCK;
+
+       /* Retry up to 5 times */
+       for (f = 0; f < 5; f++)
+               if ((fd = open(device, m, 0600)) >= 0)
+                       break;
+       if (fd < 0)
+               return fd;
+       /* Reset original flags. */
+       if (m != mode)
+               fcntl(fd, F_SETFL, mode);
+       return fd;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/dump.c b/busybox/libbb/dump.c
new file mode 100644 (file)
index 0000000..98f004f
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * Support code for the hexdump and od applets,
+ * based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>             /* for isdigit() */
+#include "libbb.h"
+#include "dump.h"
+
+enum _vflag bb_dump_vflag = FIRST;
+FS *bb_dump_fshead;                            /* head of format strings */
+static FU *endfu;
+static char **_argv;
+static off_t savaddress;       /* saved address/offset in stream */
+static off_t eaddress; /* end address */
+static off_t address;  /* address/offset in stream */
+off_t bb_dump_skip;                            /* bytes to skip */
+static int exitval;                    /* final exit value */
+int bb_dump_blocksize;                 /* data block size */
+int bb_dump_length = -1;               /* max bytes to read */
+
+static const char index_str[] = ".#-+ 0123456789";
+
+static const char size_conv_str[] =
+"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
+
+static const char lcc[] = "diouxX";
+
+int bb_dump_size(FS * fs)
+{
+       register FU *fu;
+       register int bcnt, cur_size;
+       register char *fmt;
+       const char *p;
+       int prec;
+
+       /* figure out the data block bb_dump_size needed for each format unit */
+       for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+               if (fu->bcnt) {
+                       cur_size += fu->bcnt * fu->reps;
+                       continue;
+               }
+               for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+                       if (*fmt != '%')
+                               continue;
+                       /*
+                        * bb_dump_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));
+                       }
+                       if (!(p = strchr(size_conv_str + 12, *fmt))) {
+                               if (*fmt == 's') {
+                                       bcnt += prec;
+                               } else if (*fmt == '_') {
+                                       ++fmt;
+                                       if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
+                                               bcnt += 1;
+                                       }
+                               }
+                       } else {
+                               bcnt += size_conv_str[p - (size_conv_str + 12)];
+                       }
+               }
+               cur_size += bcnt * fu->reps;
+       }
+       return (cur_size);
+}
+
+static void rewrite(FS * fs)
+{
+       enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+       register PR *pr, **nextpr = NULL;
+       register FU *fu;
+       register char *p1, *p2, *p3;
+       char savech, *fmtp;
+       const char *byte_count_str;
+       int nconv, prec = 0;
+
+       for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+               /*
+                * break each format unit into print units; each
+                * conversion character gets its own.
+                */
+               for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+                       /* NOSTRICT */
+                       /* DBU:[dvae@cray.com] calloc so that forward ptrs start out NULL*/
+                       pr = (PR *) xcalloc(1,sizeof(PR));
+                       if (!fu->nextpr)
+                               fu->nextpr = pr;
+                       /* ignore nextpr -- its unused inside the loop and is
+                        * uninitialized 1st time thru.
+                        */
+
+                       /* bb_dump_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;
+                               /* bb_dump_skip to conversion character */
+                               for (++p1; strchr(index_str, *p1); ++p1);
+                       } else {
+                               /* bb_dump_skip any special chars, field width */
+                               while (strchr(index_str + 1, *++p1));
+                               if (*p1 == '.' && isdigit(*++p1)) {
+                                       sokay = USEPREC;
+                                       prec = atoi(p1);
+                                       while (isdigit(*++p1));
+                               } else
+                                       sokay = NOTOKAY;
+                       }
+
+                       p2 = p1 + 1;    /* set end pointer */
+
+                       /*
+                        * figure out the byte count for each conversion;
+                        * rewrite the format as necessary, set up blank-
+                        * pbb_dump_adding for end of data.
+                        */
+
+                       if (*p1 == 'c') {
+                               pr->flags = F_CHAR;
+                       DO_BYTE_COUNT_1:
+                               byte_count_str = "\001";
+                       DO_BYTE_COUNT:
+                               if (fu->bcnt) {
+                                       do {
+                                               if (fu->bcnt == *byte_count_str) {
+                                                       break;
+                                               }
+                                       } while (*++byte_count_str);
+                               }
+                               /* Unlike the original, output the remainder of the format string. */
+                               if (!*byte_count_str) {
+                                       bb_error_msg_and_die("bad byte count for conversion character %s.", p1);
+                               }
+                               pr->bcnt = *byte_count_str;
+                       } else if (*p1 == 'l') {
+                               ++p2;
+                               ++p1;
+                       DO_INT_CONV:
+                               {
+                                       const char *e;
+                                       if (!(e = strchr(lcc, *p1))) {
+                                               goto DO_BAD_CONV_CHAR;
+                                       }
+                                       pr->flags = F_INT;
+                                       if (e > lcc + 1) {
+                                               pr->flags = F_UINT;
+                                       }
+                                       byte_count_str = "\004\002\001";
+                                       goto DO_BYTE_COUNT;
+                               }
+                               /* NOTREACHED */
+                       } else if (strchr(lcc, *p1)) {
+                               goto DO_INT_CONV;
+                       } else if (strchr("eEfgG", *p1)) {
+                               pr->flags = F_DBL;
+                               byte_count_str = "\010\004";
+                               goto DO_BYTE_COUNT;
+                       } else if (*p1 == 's') {
+                               pr->flags = F_STR;
+                               if (sokay == USEBCNT) {
+                                       pr->bcnt = fu->bcnt;
+                               } else if (sokay == USEPREC) {
+                                       pr->bcnt = prec;
+                               } else {        /* NOTOKAY */
+                                       bb_error_msg_and_die("%%s requires a precision or a byte count.");
+                               }
+                       } else if (*p1 == '_') {
+                               ++p2;
+                               switch (p1[1]) {
+                               case 'A':
+                                       endfu = fu;
+                                       fu->flags |= F_IGNORE;
+                                       /* FALLTHROUGH */
+                               case 'a':
+                                       pr->flags = F_ADDRESS;
+                                       ++p2;
+                                       if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
+                                               goto DO_BAD_CONV_CHAR;
+                                       }
+                                       *p1 = p1[2];
+                                       break;
+                               case 'c':
+                                       pr->flags = F_C;
+                                       /* *p1 = 'c';   set in conv_c */
+                                       goto DO_BYTE_COUNT_1;
+                               case 'p':
+                                       pr->flags = F_P;
+                                       *p1 = 'c';
+                                       goto DO_BYTE_COUNT_1;
+                               case 'u':
+                                       pr->flags = F_U;
+                                       /* *p1 = 'c';   set in conv_u */
+                                       goto DO_BYTE_COUNT_1;
+                               default:
+                                       goto DO_BAD_CONV_CHAR;
+                               }
+                       } else {
+                       DO_BAD_CONV_CHAR:
+                               bb_error_msg_and_die("bad conversion character %%%s.\n", p1);
+                       }
+
+                       /*
+                        * copy to PR format string, set conversion character
+                        * pointer, update original.
+                        */
+                       savech = *p2;
+                       p1[1] = '\0';
+                       pr->fmt = bb_xstrdup(fmtp);
+                       *p2 = savech;
+                       pr->cchar = pr->fmt + (p1 - fmtp);
+
+                       /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
+                        * Skip subsequent text and up to the next % sign and tack the
+                        * additional text onto fmt: eg. if fmt is "%x is a HEX number",
+                        * we lose the " is a HEX number" part of fmt.
+                        */
+                       for (p3 = p2; *p3 && *p3 != '%'; p3++);
+                       if (p3 > p2)
+                       {
+                               savech = *p3;
+                               *p3 = '\0';
+                               if (!(pr->fmt = realloc(pr->fmt, strlen(pr->fmt)+(p3-p2)+1)))
+                                       bb_perror_msg_and_die("hexdump");
+                               strcat(pr->fmt, p2);
+                               *p3 = savech;
+                               p2 = p3;
+                       }
+
+                       fmtp = p2;
+
+                       /* only one conversion character if byte count */
+                       if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
+                               bb_error_msg_and_die("byte count with multiple conversion characters.\n");
+                       }
+               }
+               /*
+                * 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 bb_dump_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->nextfu) {
+               if (!fu->nextfu && fs->bcnt < bb_dump_blocksize &&
+                       !(fu->flags & F_SETREP) && fu->bcnt)
+                       fu->reps += (bb_dump_blocksize - fs->bcnt) / fu->bcnt;
+               if (fu->reps > 1) {
+                       for (pr = fu->nextpr;; pr = pr->nextpr)
+                               if (!pr->nextpr)
+                                       break;
+                       for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+                               p2 = isspace(*p1) ? p1 : NULL;
+                       if (p2)
+                               pr->nospace = p2;
+               }
+               if (!fu->nextfu)
+                       break;
+       }
+}
+
+static void do_skip(char *fname, int statok)
+{
+       struct stat sbuf;
+
+       if (statok) {
+               if (fstat(STDIN_FILENO, &sbuf)) {
+                       bb_perror_msg_and_die("%s", fname);
+               }
+               if ((!(S_ISCHR(sbuf.st_mode) ||
+                          S_ISBLK(sbuf.st_mode) ||
+                          S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) {
+                       /* If bb_dump_size valid and bb_dump_skip >= size */
+                       bb_dump_skip -= sbuf.st_size;
+                       address += sbuf.st_size;
+                       return;
+               }
+       }
+       if (fseek(stdin, bb_dump_skip, SEEK_SET)) {
+               bb_perror_msg_and_die("%s", fname);
+       }
+       savaddress = address += bb_dump_skip;
+       bb_dump_skip = 0;
+}
+
+static int next(char **argv)
+{
+       static int done;
+       int statok;
+
+       if (argv) {
+               _argv = argv;
+               return (1);
+       }
+       for (;;) {
+               if (*_argv) {
+                       if (!(freopen(*_argv, "r", stdin))) {
+                               bb_perror_msg("%s", *_argv);
+                               exitval = 1;
+                               ++_argv;
+                               continue;
+                       }
+                       statok = done = 1;
+               } else {
+                       if (done++)
+                               return (0);
+                       statok = 0;
+               }
+               if (bb_dump_skip)
+                       do_skip(statok ? *_argv : "stdin", statok);
+               if (*_argv)
+                       ++_argv;
+               if (!bb_dump_skip)
+                       return (1);
+       }
+       /* NOTREACHED */
+}
+
+static u_char *get(void)
+{
+       static int ateof = 1;
+       static u_char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */
+       register int n;
+       int need, nread;
+       u_char *tmpp;
+
+       if (!curp) {
+               address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
+               curp = (u_char *) xmalloc(bb_dump_blocksize);
+               savp = (u_char *) xmalloc(bb_dump_blocksize);
+       } else {
+               tmpp = curp;
+               curp = savp;
+               savp = tmpp;
+               address = savaddress += bb_dump_blocksize;
+       }
+       for (need = bb_dump_blocksize, nread = 0;;) {
+               /*
+                * 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 (!bb_dump_length || (ateof && !next((char **) NULL))) {
+                       if (need == bb_dump_blocksize) {
+                               return ((u_char *) NULL);
+                       }
+                       if (bb_dump_vflag != ALL && !bcmp(curp, savp, nread)) {
+                               if (bb_dump_vflag != DUP) {
+                                       printf("*\n");
+                               }
+                               return ((u_char *) NULL);
+                       }
+                       bzero((char *) curp + nread, need);
+                       eaddress = address + nread;
+                       return (curp);
+               }
+               n = fread((char *) curp + nread, sizeof(u_char),
+                                 bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin);
+               if (!n) {
+                       if (ferror(stdin)) {
+                               bb_perror_msg("%s", _argv[-1]);
+                       }
+                       ateof = 1;
+                       continue;
+               }
+               ateof = 0;
+               if (bb_dump_length != -1) {
+                       bb_dump_length -= n;
+               }
+               if (!(need -= n)) {
+                       if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST
+                               || bcmp(curp, savp, bb_dump_blocksize)) {
+                               if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) {
+                                       bb_dump_vflag = WAIT;
+                               }
+                               return (curp);
+                       }
+                       if (bb_dump_vflag == WAIT) {
+                               printf("*\n");
+                       }
+                       bb_dump_vflag = DUP;
+                       address = savaddress += bb_dump_blocksize;
+                       need = bb_dump_blocksize;
+                       nread = 0;
+               } else {
+                       nread += n;
+               }
+       }
+}
+
+static void bpad(PR * pr)
+{
+       register char *p1, *p2;
+
+       /*
+        * remove all conversion flags; '-' is the only one valid
+        * with %s, and it's not useful here.
+        */
+       pr->flags = F_BPAD;
+       *pr->cchar = 's';
+       for (p1 = pr->fmt; *p1 != '%'; ++p1);
+       for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1);
+       while ((*p2++ = *p1++) != 0);
+}
+
+static const char conv_str[] =
+       "\0\\0\0"
+       "\007\\a\0"                             /* \a */
+       "\b\\b\0"
+       "\f\\b\0"
+       "\n\\n\0"
+       "\r\\r\0"
+       "\t\\t\0"
+       "\v\\v\0"
+       "\0";
+
+
+static void conv_c(PR * pr, u_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';
+               (void) 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\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
+               "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
+               "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
+               "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
+
+       /* od used nl, not lf */
+       if (*p <= 0x1f) {
+               *pr->cchar = 's';
+               printf(pr->fmt, list + (4 * (int)*p));
+       } else if (*p == 0x7f) {
+               *pr->cchar = 's';
+               printf(pr->fmt, "del");
+       } else if (isprint(*p)) {
+               *pr->cchar = 'c';
+               printf(pr->fmt, *p);
+       } else {
+               *pr->cchar = 'x';
+               printf(pr->fmt, (int) *p);
+       }
+}
+
+static void display(void)
+{
+/*  extern FU *endfu; */
+       register FS *fs;
+       register FU *fu;
+       register PR *pr;
+       register int cnt;
+       register u_char *bp;
+
+       off_t saveaddress;
+       u_char savech = 0, *savebp;
+
+       while ((bp = get()) != NULL) {
+               for (fs = bb_dump_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, (unsigned int) address);
+                                                       break;
+                                               case F_BPAD:
+                                                       printf(pr->fmt, "");
+                                                       break;
+                                               case F_C:
+                                                       conv_c(pr, bp);
+                                                       break;
+                                               case F_CHAR:
+                                                       printf(pr->fmt, *bp);
+                                                       break;
+                                               case F_DBL:{
+                                                       double dval;
+                                                       float fval;
+
+                                                       switch (pr->bcnt) {
+                                                       case 4:
+                                                               bcopy((char *) bp, (char *) &fval,
+                                                                         sizeof(fval));
+                                                               printf(pr->fmt, fval);
+                                                               break;
+                                                       case 8:
+                                                               bcopy((char *) bp, (char *) &dval,
+                                                                         sizeof(dval));
+                                                               printf(pr->fmt, dval);
+                                                               break;
+                                                       }
+                                                       break;
+                                               }
+                                               case F_INT:{
+                                                       int ival;
+                                                       short sval;
+
+                                                       switch (pr->bcnt) {
+                                                       case 1:
+                                                               printf(pr->fmt, (int) *bp);
+                                                               break;
+                                                       case 2:
+                                                               bcopy((char *) bp, (char *) &sval,
+                                                                         sizeof(sval));
+                                                               printf(pr->fmt, (int) sval);
+                                                               break;
+                                                       case 4:
+                                                               bcopy((char *) bp, (char *) &ival,
+                                                                         sizeof(ival));
+                                                               printf(pr->fmt, ival);
+                                                               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(pr->fmt);
+                                                       break;
+                                               case F_U:
+                                                       conv_u(pr, bp);
+                                                       break;
+                                               case F_UINT:{
+                                                       unsigned int ival;
+                                                       unsigned short sval;
+
+                                                       switch (pr->bcnt) {
+                                                       case 1:
+                                                               printf(pr->fmt, (unsigned int) * bp);
+                                                               break;
+                                                       case 2:
+                                                               bcopy((char *) bp, (char *) &sval,
+                                                                         sizeof(sval));
+                                                               printf(pr->fmt, (unsigned int) sval);
+                                                               break;
+                                                       case 4:
+                                                               bcopy((char *) bp, (char *) &ival,
+                                                                         sizeof(ival));
+                                                               printf(pr->fmt, ival);
+                                                               break;
+                                                       }
+                                                       break;
+                                               }
+                                               }
+                                               if (cnt == 1 && pr->nospace) {
+                                                       *pr->nospace = savech;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       if (endfu) {
+               /*
+                * if eaddress not set, error or file bb_dump_size was multiple of
+                * bb_dump_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:
+                               (void) printf(pr->fmt, (unsigned int) eaddress);
+                               break;
+                       case F_TEXT:
+                               (void) printf(pr->fmt);
+                               break;
+                       }
+               }
+       }
+}
+
+int bb_dump_dump(char **argv)
+{
+       register FS *tfs;
+
+       /* figure out the data block bb_dump_size */
+       for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
+               tfs->bcnt = bb_dump_size(tfs);
+               if (bb_dump_blocksize < tfs->bcnt) {
+                       bb_dump_blocksize = tfs->bcnt;
+               }
+       }
+       /* rewrite the rules, do syntax checking */
+       for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
+               rewrite(tfs);
+       }
+
+       next(argv);
+       display();
+
+       return (exitval);
+}
+
+void bb_dump_add(const char *fmt)
+{
+       register const char *p;
+       register char *p1;
+       register char *p2;
+       static FS **nextfs;
+       FS *tfs;
+       FU *tfu, **nextfu;
+       const char *savep;
+
+       /* start new linked list of format units */
+       /* NOSTRICT */
+       tfs = (FS *) xcalloc(1,sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
+       if (!bb_dump_fshead) {
+               bb_dump_fshead = tfs;
+       } else {
+               *nextfs = tfs;
+       }
+       nextfs = &tfs->nextfs;
+       nextfu = &tfs->nextfu;
+
+       /* take the format string and break it up into format units */
+       for (p = fmt;;) {
+               /* bb_dump_skip leading white space */
+               p = bb_skip_whitespace(p);
+               if (!*p) {
+                       break;
+               }
+
+               /* allocate a new format unit and link it in */
+               /* NOSTRICT */
+               /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */
+               tfu = (FU *) xcalloc(1,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 != '/') {
+                               bb_error_msg_and_die("bad format {%s}", fmt);
+                       }
+                       /* may overwrite either white space or slash */
+                       tfu->reps = atoi(savep);
+                       tfu->flags = F_SETREP;
+                       /* bb_dump_skip trailing white space */
+                       p = bb_skip_whitespace(++p);
+               }
+
+               /* bb_dump_skip slash and trailing white space */
+               if (*p == '/') {
+                       p = bb_skip_whitespace(++p);
+               }
+
+               /* byte count */
+               if (isdigit(*p)) {
+                       for (savep = p; isdigit(*p); ++p);
+                       if (!isspace(*p)) {
+                               bb_error_msg_and_die("bad format {%s}", fmt);
+                       }
+                       tfu->bcnt = atoi(savep);
+                       /* bb_dump_skip trailing white space */
+                       p = bb_skip_whitespace(++p);
+               }
+
+               /* format */
+               if (*p != '"') {
+                       bb_error_msg_and_die("bad format {%s}", fmt);
+               }
+               for (savep = ++p; *p != '"';) {
+                       if (*p++ == 0) {
+                               bb_error_msg_and_die("bad format {%s}", fmt);
+                       }
+               }
+               tfu->fmt = xmalloc(p - savep + 1);
+               strncpy(tfu->fmt, savep, p - savep);
+               tfu->fmt[p - savep] = '\0';
+/*      escape(tfu->fmt); */
+
+               p1 = tfu->fmt;
+
+               /* alphabetic escape sequences have to be done in place */
+               for (p2 = p1;; ++p1, ++p2) {
+                       if (!*p1) {
+                               *p2 = *p1;
+                               break;
+                       }
+                       if (*p1 == '\\') {
+                               const char *cs = conv_str + 4;
+                               ++p1;
+                               *p2 = *p1;
+                               do {
+                                       if (*p1 == cs[2]) {
+                                               *p2 = cs[0];
+                                               break;
+                                       }
+                                       cs += 4;
+                               } while (*cs);
+                       }
+               }
+
+               p++;
+       }
+}
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/libbb/error_msg.c b/busybox/libbb/error_msg.c
new file mode 100644 (file)
index 0000000..18811b8
--- /dev/null
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+extern void bb_error_msg(const char *s, ...)
+{
+       va_list p;
+
+       va_start(p, s);
+       bb_verror_msg(s, p);
+       va_end(p);
+       putc('\n', stderr);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/error_msg_and_die.c b/busybox/libbb/error_msg_and_die.c
new file mode 100644 (file)
index 0000000..0937658
--- /dev/null
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+extern void bb_error_msg_and_die(const char *s, ...)
+{
+       va_list p;
+
+       va_start(p, s);
+       bb_verror_msg(s, p);
+       va_end(p);
+       putc('\n', stderr);
+       exit(bb_default_error_retval);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/fclose_nonstdin.c b/busybox/libbb/fclose_nonstdin.c
new file mode 100644 (file)
index 0000000..8f489c8
--- /dev/null
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fclose_nonstdin implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* A number of standard utilities can accept multiple command line args
+ * of '-' for stdin, according to SUSv3.  So we encapsulate the check
+ * here to save a little space.
+ */
+
+#include <stdio.h>
+#include <libbb.h>
+
+int bb_fclose_nonstdin(FILE *f)
+{
+       if (f != stdin) {
+               return fclose(f);
+       }
+       return 0;
+}
diff --git a/busybox/libbb/fflush_stdout_and_exit.c b/busybox/libbb/fflush_stdout_and_exit.c
new file mode 100644 (file)
index 0000000..cbba042
--- /dev/null
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fflush_stdout_and_exit implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Attempt to fflush(stdout), and exit with an error code if stdout is
+ * in an error state.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libbb.h>
+
+void bb_fflush_stdout_and_exit(int retval)
+{
+       if (fflush(stdout)) {
+               retval = bb_default_error_retval;
+       }
+       exit(retval);
+}
diff --git a/busybox/libbb/fgets_str.c b/busybox/libbb/fgets_str.c
new file mode 100644 (file)
index 0000000..bf828be
--- /dev/null
@@ -0,0 +1,67 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libbb.h"
+
+/* Read up to (and including) TERMINATING_STRING from FILE and return it.
+ * Return NULL on EOF.  */
+
+char *fgets_str(FILE *file, const char *terminating_string)
+{
+       char *linebuf = NULL;
+       const int term_length = strlen(terminating_string);
+       int end_string_offset;
+       int linebufsz = 0;
+       int idx = 0;
+       int ch;
+
+       while (1) {
+               ch = fgetc(file);
+               if (ch == EOF) {
+                       free(linebuf);
+                       return NULL;
+               }
+
+               /* grow the line buffer as necessary */
+               while (idx > linebufsz - 2) {
+                       linebuf = xrealloc(linebuf, linebufsz += 1000);
+               }
+
+               linebuf[idx] = ch;
+               idx++;
+
+               /* Check for terminating string */
+               end_string_offset = idx - term_length;
+               if ((end_string_offset > 0) && (memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0)) {
+                       idx -= term_length;
+                       break;
+               }
+       }
+       linebuf[idx] = '\0';
+       return(linebuf);
+}
+
diff --git a/busybox/libbb/find_mount_point.c b/busybox/libbb/find_mount_point.c
new file mode 100644 (file)
index 0000000..83824de
--- /dev/null
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "libbb.h"
+
+
+#include <mntent.h>
+/*
+ * Given a block device, find the mount table entry if that block device
+ * is mounted.
+ *
+ * Given any other file (or directory), find the mount table entry for its
+ * filesystem.
+ */
+extern struct mntent *find_mount_point(const char *name, const char *table)
+{
+       struct stat s;
+       dev_t mountDevice;
+       FILE *mountTable;
+       struct mntent *mountEntry;
+
+       if (stat(name, &s) != 0)
+               return 0;
+
+       if ((s.st_mode & S_IFMT) == S_IFBLK)
+               mountDevice = s.st_rdev;
+       else
+               mountDevice = s.st_dev;
+
+
+       if ((mountTable = setmntent(table, "r")) == 0)
+               return 0;
+
+       while ((mountEntry = getmntent(mountTable)) != 0) {
+               if (strcmp(name, mountEntry->mnt_dir) == 0
+                       || strcmp(name, mountEntry->mnt_fsname) == 0)   /* String match. */
+                       break;
+               if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == mountDevice)  /* Match the device. */
+                       break;
+               if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == mountDevice)      /* Match the directory's mount point. */
+                       break;
+       }
+       endmntent(mountTable);
+       return mountEntry;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/find_pid_by_name.c b/busybox/libbb/find_pid_by_name.c
new file mode 100644 (file)
index 0000000..930710f
--- /dev/null
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+#define COMM_LEN 16 /* synchronize with size of comm in struct task_struct
+                                          in /usr/include/linux/sched.h */
+
+
+/* find_pid_by_name()
+ *
+ *  Modified by Vladimir Oleynik for use with libbb/procps.c
+ *  This finds the pid of the specified process.
+ *  Currently, it's implemented by rummaging through
+ *  the proc filesystem.
+ *
+ *  Returns a list of all matching PIDs
+ */
+extern long* find_pid_by_name( const char* pidName)
+{
+       long* pidList;
+       int i=0;
+       procps_status_t * p;
+
+       pidList = xmalloc(sizeof(long));
+#ifdef CONFIG_SELINUX
+       while ((p = procps_scan(0, 0, NULL)) != 0) {
+#else
+       while ((p = procps_scan(0)) != 0) {
+#endif
+               if (strncmp(p->short_cmd, pidName, COMM_LEN-1) == 0) {
+                       pidList=xrealloc( pidList, sizeof(long) * (i+2));
+                       pidList[i++]=p->pid;
+               }
+       }
+
+       pidList[i] = i==0 ? -1 : 0;
+       return pidList;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/find_root_device.c b/busybox/libbb/find_root_device.c
new file mode 100644 (file)
index 0000000..2600ce5
--- /dev/null
@@ -0,0 +1,89 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+
+
+extern char *find_real_root_device_name(void)
+{
+       DIR *dir;
+       struct dirent *entry;
+       struct stat statBuf, rootStat;
+       char *fileName = NULL;
+       dev_t dev;
+
+       if (stat("/", &rootStat) != 0)
+               bb_perror_msg("could not stat '/'");
+       else {
+               /* This check is here in case they pass in /dev name */
+               if ((rootStat.st_mode & S_IFMT) == S_IFBLK)
+                       dev = rootStat.st_rdev;
+               else
+                       dev = rootStat.st_dev;
+
+               dir = opendir("/dev");
+               if (!dir)
+                       bb_perror_msg("could not open '/dev'");
+               else {
+                       while((entry = readdir(dir)) != NULL) {
+                               const char *myname = entry->d_name;
+                               /* Must skip ".." since that is "/", and so we
+                                * would get a false positive on ".."  */
+                               if (myname[0] == '.' && myname[1] == '.' && !myname[2])
+                                       continue;
+#ifdef CONFIG_FEATURE_DEVFS
+                               /* if there is a link named /dev/root skip that too */
+                               if (strcmp(myname, "root")==0)
+                                       continue;
+#endif
+                               fileName = concat_path_file("/dev", myname);
+
+                               /* Some char devices have the same dev_t as block
+                                * devices, so make sure this is a block device */
+                               if (stat(fileName, &statBuf) == 0 &&
+                                               S_ISBLK(statBuf.st_mode)!=0 &&
+                                               statBuf.st_rdev == dev)
+                                       break;
+                               free(fileName);
+                               fileName=NULL;
+                       }
+                       closedir(dir);
+               }
+       }
+       if(fileName==NULL)
+               fileName = bb_xstrdup("/dev/root");
+       return fileName;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/full_read.c b/busybox/libbb/full_read.c
new file mode 100644 (file)
index 0000000..221fc94
--- /dev/null
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include "libbb.h"
+
+/*
+ * Read all of the supplied buffer from a file.
+ * This does multiple reads as necessary.
+ * Returns the amount read, or -1 on an error.
+ * A short read is returned on an end of file.
+ */
+ssize_t bb_full_read(int fd, void *buf, size_t len)
+{
+       ssize_t cc;
+       ssize_t total;
+
+       total = 0;
+
+       while (len > 0) {
+               cc = safe_read(fd, buf, len);
+
+               if (cc < 0)
+                       return cc;      /* read() returns -1 on failure. */
+
+               if (cc == 0)
+                       break;
+
+               buf = ((char *)buf) + cc;
+               total += cc;
+               len -= cc;
+       }
+
+       return total;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/full_write.c b/busybox/libbb/full_write.c
new file mode 100644 (file)
index 0000000..30de407
--- /dev/null
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include "libbb.h"
+
+/*
+ * Write all of the supplied buffer out to a file.
+ * This does multiple writes as necessary.
+ * Returns the amount written, or -1 on an error.
+ */
+ssize_t bb_full_write(int fd, const void *buf, size_t len)
+{
+       ssize_t cc;
+       ssize_t total;
+
+       total = 0;
+
+       while (len > 0) {
+               cc = safe_write(fd, buf, len);
+
+               if (cc < 0)
+                       return cc;              /* write() returns -1 on failure. */
+
+               total += cc;
+               buf = ((const char *)buf) + cc;
+               len -= cc;
+       }
+
+       return total;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/get_console.c b/busybox/libbb/get_console.c
new file mode 100644 (file)
index 0000000..bfb7468
--- /dev/null
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.  If you wrote this, please
+ * acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "libbb.h"
+
+
+
+/* From <linux/kd.h> */
+static const int KDGKBTYPE = 0x4B33;  /* get keyboard type */
+
+
+static int open_a_console(const char *fnam)
+{
+       int fd;
+
+       /* try read-write */
+       fd = open(fnam, O_RDWR);
+
+       /* if failed, try read-only */
+       if (fd < 0 && errno == EACCES)
+               fd = open(fnam, O_RDONLY);
+
+       /* if failed, try write-only */
+       if (fd < 0 && errno == EACCES)
+               fd = open(fnam, O_WRONLY);
+
+       return fd;
+}
+
+/*
+ * Get an fd for use with kbd/console ioctls.
+ * We try several things because opening /dev/console will fail
+ * if someone else used X (which does a chown on /dev/console).
+ */
+
+int get_console_fd(void)
+{
+       int fd;
+
+       static const char * const choise_console_names[] = {
+               CONSOLE_DEV, CURRENT_VC, CURRENT_TTY
+       };
+
+       for (fd = 2; fd >= 0; fd--) {
+               int fd4name;
+               int choise_fd;
+               char arg;
+
+               fd4name = open_a_console(choise_console_names[fd]);
+       chk_std:
+               choise_fd = fd4name >= 0 ? fd4name : fd;
+
+               arg = 0;
+               if (ioctl(choise_fd, KDGKBTYPE, &arg) == 0)
+                       return choise_fd;
+               if(fd4name >= 0) {
+                       close(fd4name);
+                       fd4name = -1;
+                       goto chk_std;
+               }
+       }
+
+       bb_error_msg("Couldn't get a file descriptor referring to the console");
+       return fd;                      /* total failure */
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/get_last_path_component.c b/busybox/libbb/get_last_path_component.c
new file mode 100644 (file)
index 0000000..497d6ae
--- /dev/null
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_get_last_path_component implementation for busybox
+ *
+ * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Set to 1 if you want basename() behavior for NULL or "". */
+/* WARNING!!! Doing so will break basename applet at least! */
+#define EMULATE_BASENAME       0
+
+char *bb_get_last_path_component(char *path)
+{
+#if EMULATE_BASENAME
+       static const char null_or_empty[] = ".";
+#endif
+       char *first = path;
+       char *last;
+
+#if EMULATE_BASENAME
+       if (!path || !*path) {
+               return (char *) null_or_empty;
+       }
+#endif
+
+       last = path - 1;
+
+       while (*path) {
+               if ((*path != '/') && (path > ++last)) {
+                       last = first = path;
+               }
+               ++path;
+       }
+
+       if (*first == '/') {
+               last = first;
+       }
+       last[1] = 0;
+
+       return first;
+}
diff --git a/busybox/libbb/get_line_from_file.c b/busybox/libbb/get_line_from_file.c
new file mode 100644 (file)
index 0000000..6d12b21
--- /dev/null
@@ -0,0 +1,82 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+/* get_line_from_file() - This function reads an entire line from a text file,
+ * up to a newline. It returns a malloc'ed char * which must be stored and
+ * free'ed  by the caller.  If 'c' is nonzero, the trailing '\n' (if any)
+ * is removed.  In event of a read error or EOF, NULL is returned. */
+
+static char *private_get_line_from_file(FILE *file, int c)
+{
+#define GROWBY (80)            /* how large we will grow strings by */
+
+       int ch;
+       int idx = 0;
+       char *linebuf = NULL;
+       int linebufsz = 0;
+
+       while ((ch = getc(file)) != EOF) {
+               /* grow the line buffer as necessary */
+               if (idx > linebufsz - 2) {
+                       linebuf = xrealloc(linebuf, linebufsz += GROWBY);
+               }
+               linebuf[idx++] = (char)ch;
+               if (ch == '\n' || ch == '\0') {
+                       if (c) {
+                               --idx;
+                       }
+                       break;
+               }
+       }
+       if (linebuf) {
+               if (ferror(file)) {
+                       free(linebuf);
+                       return NULL;
+               }
+               linebuf[idx] = 0;
+       }
+       return linebuf;
+}
+
+extern char *bb_get_line_from_file(FILE *file)
+{
+       return private_get_line_from_file(file, 0);
+}
+
+extern char *bb_get_chomped_line_from_file(FILE *file)
+{
+       return private_get_line_from_file(file, 1);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/get_terminal_width_height.c b/busybox/libbb/get_terminal_width_height.c
new file mode 100644 (file)
index 0000000..7a1af6d
--- /dev/null
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Determine the width and height of the terminal.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+/* It is perfectly ok to pass in a NULL for either width or for
+ * height, in which case that value will not be set.  It is also
+ * perfectly ok to have CONFIG_FEATURE_AUTOWIDTH disabled, in
+ * which case you will always get 80x24 */
+void get_terminal_width_height(int fd, int *width, int *height)
+{
+       struct winsize win = { 0, 0, 0, 0 };
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+       if (ioctl(fd, TIOCGWINSZ, &win) != 0) {
+               win.ws_row = 24;
+               win.ws_col = 80;
+       }
+#endif
+       if (win.ws_row <= 1) {
+               win.ws_row = 24;
+       }
+       if (win.ws_col <= 1) {
+               win.ws_col = 80;
+       }
+       if (height) {
+               *height = (int) win.ws_row;
+       }
+       if (width) {
+               *width = (int) win.ws_col;
+       }
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
+
diff --git a/busybox/libbb/get_ug_id.c b/busybox/libbb/get_ug_id.c
new file mode 100644 (file)
index 0000000..b0b9b2e
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+
+extern unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *))
+{
+       unsigned long r;
+       char *p;
+
+       r = strtoul(s, &p, 10);
+       if (*p || (s == p)) {
+               r = my_getxxnam(s);
+       }
+
+       return r;
+}
diff --git a/busybox/libbb/getopt_ulflags.c b/busybox/libbb/getopt_ulflags.c
new file mode 100644 (file)
index 0000000..39a7d1d
--- /dev/null
@@ -0,0 +1,171 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * universal getopt_ulflags implementation for busybox
+ *
+ * Copyright (C) 2003  Vladimir Oleynik  <dzo@simtreas.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+/*
+You can set bb_opt_complementaly as string with one or more
+complementaly or incongruously options.
+If sequential founded option haved from this string
+then your incongruously pairs unsets and complementaly make add sets.
+Format:
+one char - option for check,
+chars - complementaly option for add sets.
+- chars - option triggered for unsets.
+~ chars - option incongruously.
+*       - option list, called add_to_list(*ptr_from_usaged, optarg)
+:       - separator.
+Example: du applet can have options "-s" and "-d size"
+If getopt found -s then -d option flag unset or if found -d then -s unset.
+For this result you must set bb_opt_complementaly = "s-d:d-s".
+Result have last option flag only from called arguments.
+Warning! You can check returned flag, pointer to "d:" argument seted
+to own optarg always.
+Example two: cut applet must only one type of list may be specified,
+and -b, -c and -f incongruously option, overwited option is error also.
+You must set bb_opt_complementaly = "b~cf:c~bf:f~bc".
+If called have more one specified, return value have error flag -
+high bite set (0x80000000UL).
+Example three: grep applet can have one or more "-e pattern" arguments.
+You should use bb_getopt_ulflags() as
+llist_t *paterns;
+bb_opt_complementaly = "e*";
+bb_getopt_ulflags (argc, argv, "e:", &paterns);
+*/
+
+const char *bb_opt_complementaly;
+
+typedef struct
+{
+       unsigned char opt;
+       char list_flg;
+       unsigned long switch_on;
+       unsigned long switch_off;
+       unsigned long incongruously;
+       void **optarg;              /* char **optarg or llist_t **optarg */
+} t_complementaly;
+
+/* You can set bb_applet_long_options for parse called long options */
+
+static const struct option bb_default_long_options[] = {
+     /*   { "help", 0, NULL, '?' }, */
+       { 0, 0, 0, 0 }
+};
+
+const struct option *bb_applet_long_options = bb_default_long_options;
+
+
+unsigned long
+bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
+{
+  unsigned long flags = 0;
+  t_complementaly complementaly[sizeof(flags) * 8 + 1];
+  int c;
+  const unsigned char *s;
+  t_complementaly *on_off;
+  va_list p;
+
+  va_start (p, applet_opts);
+
+  /* skip GNU extension */
+  s = applet_opts;
+  if(*s == '+' || *s == '-')
+       s++;
+
+  c = 0;
+  on_off = complementaly;
+  for (; *s; s++) {
+       if(c >= (sizeof(flags)*8))
+               break;
+       on_off->opt = *s;
+       on_off->switch_on = (1 << c);
+       on_off->list_flg = 0;
+       on_off->switch_off = 0;
+       on_off->incongruously = 0;
+       on_off->optarg = NULL;
+       if (s[1] == ':') {
+         on_off->optarg = va_arg (p, void **);
+         do
+               s++;
+         while (s[1] == ':');
+       }
+       on_off++;
+       c++;
+  }
+  on_off->opt = 0;
+  c = 0;
+  for (s = bb_opt_complementaly; s && *s; s++) {
+    t_complementaly *pair;
+
+    if (*s == ':') {
+         c = 0;
+         continue;
+    }
+    if (c)
+         continue;
+    for (on_off = complementaly; on_off->opt; on_off++)
+         if (on_off->opt == *s)
+           break;
+    pair = on_off;
+    for(s++; *s && *s != ':'; s++) {
+      if (*s == '-' || *s == '~') {
+         c = *s;
+      } else if(*s == '*') {
+       pair->list_flg++;
+      } else {
+         unsigned long *pair_switch = &(pair->switch_on);
+
+         if(c)
+           pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously);
+         for (on_off = complementaly; on_off->opt; on_off++)
+           if (on_off->opt == *s) {
+                 *pair_switch |= on_off->switch_on;
+                 break;
+           }
+      }
+    }
+    s--;
+  }
+
+  while ((c = getopt_long (argc, argv, applet_opts,
+                           bb_applet_long_options, NULL)) > 0) {
+       for (on_off = complementaly; on_off->opt != c; on_off++) {
+           if(!on_off->opt)
+                       bb_show_usage ();
+       }
+       if(flags & on_off->incongruously)
+           flags |= 0x80000000UL;
+       flags &= ~on_off->switch_off;
+       flags |= on_off->switch_on;
+       if(on_off->list_flg) {
+          *(llist_t **)(on_off->optarg) =
+               llist_add_to(*(llist_t **)(on_off->optarg), optarg);
+       } else if (on_off->optarg) {
+           *(char **)(on_off->optarg) = optarg;
+       }
+  }
+  return flags;
+}
diff --git a/busybox/libbb/hash_fd.c b/busybox/libbb/hash_fd.c
new file mode 100644 (file)
index 0000000..e37ac54
--- /dev/null
@@ -0,0 +1,859 @@
+/*
+ *  Based on shasum from http://www.netsw.org/crypto/hash/
+ *  Majorly hacked up to use Dr Brian Gladman's sha1 code
+ *
+ *  Copyright (C) 2003 Glenn L. McGrath
+ *  Copyright (C) 2003 Erik Andersen
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <byteswap.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "busybox.h"
+
+
+#ifdef CONFIG_SHA1SUM
+/*
+ ---------------------------------------------------------------------------
+ Begin Dr. Gladman's sha1 code
+ ---------------------------------------------------------------------------
+*/
+
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+   1. distributions of this source code include the above copyright
+      notice, this list of conditions and the following disclaimer;
+
+   2. distributions in binary form include the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other associated materials;
+
+   3. the copyright holder's name is not used to endorse products
+      built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 10/11/2002
+
+ This is a byte oriented version of SHA1 that operates on arrays of bytes
+ stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
+*/
+
+# define SHA1_BLOCK_SIZE  64
+# define SHA1_DIGEST_SIZE 20
+# define SHA1_HASH_SIZE   SHA1_DIGEST_SIZE
+# define SHA2_GOOD        0
+# define SHA2_BAD         1
+
+# define rotl32(x,n) (((x) << n) | ((x) >> (32 - n)))
+
+# if __BYTE_ORDER == __BIG_ENDIAN
+#  define swap_b32(x) (x)
+# elif defined(bswap_32)
+#  define swap_b32(x) bswap_32(x)
+# else
+#  define swap_b32(x) ((rotl32((x), 8) & 0x00ff00ff) | (rotl32((x), 24) & 0xff00ff00))
+# endif /* __BYTE_ORDER */
+
+# define SHA1_MASK   (SHA1_BLOCK_SIZE - 1)
+
+/* reverse byte order in 32-bit words   */
+#define ch(x,y,z)       ((z) ^ ((x) & ((y) ^ (z))))
+#define parity(x,y,z)   ((x) ^ (y) ^ (z))
+#define maj(x,y,z)      (((x) & (y)) | ((z) & ((x) | (y))))
+
+/* A normal version as set out in the FIPS. This version uses   */
+/* partial loop unrolling and is optimised for the Pentium 4    */
+# define rnd(f,k)    \
+    t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \
+    e = d; d = c; c = rotl32(b, 30); b = t
+
+/* type to hold the SHA1 context  */
+struct sha1_ctx_t {
+       uint32_t count[2];
+       uint32_t hash[5];
+       uint32_t wbuf[16];
+};
+
+static void sha1_compile(struct sha1_ctx_t *ctx)
+{
+       uint32_t w[80], i, a, b, c, d, e, t;
+
+       /* note that words are compiled from the buffer into 32-bit */
+       /* words in big-endian order so an order reversal is needed */
+       /* here on little endian machines                           */
+       for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i)
+               w[i] = swap_b32(ctx->wbuf[i]);
+
+       for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i)
+               w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
+
+       a = ctx->hash[0];
+       b = ctx->hash[1];
+       c = ctx->hash[2];
+       d = ctx->hash[3];
+       e = ctx->hash[4];
+
+       for (i = 0; i < 20; ++i) {
+               rnd(ch, 0x5a827999);
+       }
+
+       for (i = 20; i < 40; ++i) {
+               rnd(parity, 0x6ed9eba1);
+       }
+
+       for (i = 40; i < 60; ++i) {
+               rnd(maj, 0x8f1bbcdc);
+       }
+
+       for (i = 60; i < 80; ++i) {
+               rnd(parity, 0xca62c1d6);
+       }
+
+       ctx->hash[0] += a;
+       ctx->hash[1] += b;
+       ctx->hash[2] += c;
+       ctx->hash[3] += d;
+       ctx->hash[4] += e;
+}
+
+static void sha1_begin(struct sha1_ctx_t *ctx)
+{
+       ctx->count[0] = ctx->count[1] = 0;
+       ctx->hash[0] = 0x67452301;
+       ctx->hash[1] = 0xefcdab89;
+       ctx->hash[2] = 0x98badcfe;
+       ctx->hash[3] = 0x10325476;
+       ctx->hash[4] = 0xc3d2e1f0;
+}
+
+/* SHA1 hash data in an array of bytes into hash buffer and call the        */
+/* hash_compile function as required.                                       */
+static void sha1_hash(const void *data, size_t len, void *ctx_v)
+{
+       struct sha1_ctx_t *ctx = (struct sha1_ctx_t *) ctx_v;
+       uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK);
+       uint32_t freeb = SHA1_BLOCK_SIZE - pos;
+       const unsigned char *sp = data;
+
+       if ((ctx->count[0] += len) < len)
+               ++(ctx->count[1]);
+
+       while (len >= freeb) {  /* tranfer whole blocks while possible  */
+               memcpy(((unsigned char *) ctx->wbuf) + pos, sp, freeb);
+               sp += freeb;
+               len -= freeb;
+               freeb = SHA1_BLOCK_SIZE;
+               pos = 0;
+               sha1_compile(ctx);
+       }
+
+       memcpy(((unsigned char *) ctx->wbuf) + pos, sp, len);
+}
+
+/* SHA1 Final padding and digest calculation  */
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+static uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };
+static uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 };
+# else
+static uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 };
+static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 };
+# endif /* __BYTE_ORDER */
+
+void sha1_end(unsigned char hval[], struct sha1_ctx_t *ctx)
+{
+       uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK);
+
+       /* mask out the rest of any partial 32-bit word and then set    */
+       /* the next byte to 0x80. On big-endian machines any bytes in   */
+       /* the buffer will be at the top end of 32 bit words, on little */
+       /* endian machines they will be at the bottom. Hence the AND    */
+       /* and OR masks above are reversed for little endian systems    */
+       ctx->wbuf[cnt >> 2] =
+               (ctx->wbuf[cnt >> 2] & mask[cnt & 3]) | bits[cnt & 3];
+
+       /* we need 9 or more empty positions, one for the padding byte  */
+       /* (above) and eight for the length count.  If there is not     */
+       /* enough space pad and empty the buffer                        */
+       if (cnt > SHA1_BLOCK_SIZE - 9) {
+               if (cnt < 60)
+                       ctx->wbuf[15] = 0;
+               sha1_compile(ctx);
+               cnt = 0;
+       } else                          /* compute a word index for the empty buffer positions  */
+               cnt = (cnt >> 2) + 1;
+
+       while (cnt < 14)        /* and zero pad all but last two positions      */
+               ctx->wbuf[cnt++] = 0;
+
+       /* assemble the eight byte counter in the buffer in big-endian  */
+       /* format                                                       */
+
+       ctx->wbuf[14] = swap_b32((ctx->count[1] << 3) | (ctx->count[0] >> 29));
+       ctx->wbuf[15] = swap_b32(ctx->count[0] << 3);
+
+       sha1_compile(ctx);
+
+       /* extract the hash value as bytes in case the hash buffer is   */
+       /* misaligned for 32-bit words                                  */
+
+       for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
+               hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3));
+}
+
+/*
+ ---------------------------------------------------------------------------
+ End of Dr. Gladman's sha1 code
+ ---------------------------------------------------------------------------
+*/
+#endif /* CONFIG_SHA1 */
+
+
+
+
+
+#ifdef CONFIG_MD5SUM
+/*
+ * md5sum.c - Compute MD5 checksum of files or strings according to the
+ *            definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ *
+ * June 29, 2001        Manuel Novoa III
+ *
+ * Added MD5SUM_SIZE_VS_SPEED configuration option.
+ *
+ * Current valid values, with data from my system for comparison, are:
+ *   (using uClibc and running on linux-2.4.4.tar.bz2)
+ *                     user times (sec)  text size (386)
+ *     0 (fastest)         1.1                6144
+ *     1                   1.4                5392
+ *     2                   3.0                5088
+ *     3 (smallest)        5.1                4912
+ */
+
+# define MD5SUM_SIZE_VS_SPEED 2
+
+/* Handle endian-ness */
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  define SWAP(n) (n)
+# elif defined(bswap_32)
+#  define SWAP(n) bswap_32(n)
+# else
+#  define SWAP(n) ((n << 24) | ((n&65280)<<8) | ((n&16711680)>>8) | (n>>24))
+# endif
+
+# if MD5SUM_SIZE_VS_SPEED == 0
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */  };
+# endif        /* MD5SUM_SIZE_VS_SPEED == 0 */
+
+/* Structure to save state of computation between the single steps.  */
+struct md5_ctx_t {
+       uint32_t A;
+       uint32_t B;
+       uint32_t C;
+       uint32_t D;
+       uint32_t total[2];
+       uint32_t buflen;
+       char buffer[128];
+};
+
+/* Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+static void md5_begin(struct md5_ctx_t *ctx)
+{
+       ctx->A = 0x67452301;
+       ctx->B = 0xefcdab89;
+       ctx->C = 0x98badcfe;
+       ctx->D = 0x10325476;
+
+       ctx->total[0] = ctx->total[1] = 0;
+       ctx->buflen = 0;
+}
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ * and defined in the RFC 1321.  The first function is a little bit optimized
+ * (as found in Colin Plumbs public domain implementation).
+ * #define FF(b, c, d) ((b & c) | (~b & d))
+ */
+# define FF(b, c, d) (d ^ (b & (c ^ d)))
+# define FG(b, c, d) FF (d, b, c)
+# define FH(b, c, d) (b ^ c ^ d)
+# define FI(b, c, d) (c ^ (b | ~d))
+
+/* Starting with the result of former calls of this function (or the
+ * initialization function update the context for the next LEN bytes
+ * starting at BUFFER.
+ * It is necessary that LEN is a multiple of 64!!!
+ */
+static void md5_hash_block(const void *buffer, size_t len, struct md5_ctx_t *ctx)
+{
+       uint32_t correct_words[16];
+       const uint32_t *words = buffer;
+       size_t nwords = len / sizeof(uint32_t);
+       const uint32_t *endp = words + nwords;
+
+# if MD5SUM_SIZE_VS_SPEED > 0
+       static const uint32_t C_array[] = {
+               /* round 1 */
+               0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+               0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+               0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+               0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+               /* round 2 */
+               0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+               0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+               0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+               0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+               /* round 3 */
+               0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+               0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+               0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+               0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+               /* round 4 */
+               0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+               0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+               0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+               0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+       };
+
+       static const char P_array[] = {
+#  if MD5SUM_SIZE_VS_SPEED > 1
+               0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,   /* 1 */
+#  endif       /* MD5SUM_SIZE_VS_SPEED > 1 */
+               1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,   /* 2 */
+               5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,   /* 3 */
+               0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9    /* 4 */
+       };
+
+#  if MD5SUM_SIZE_VS_SPEED > 1
+       static const char S_array[] = {
+               7, 12, 17, 22,
+               5, 9, 14, 20,
+               4, 11, 16, 23,
+               6, 10, 15, 21
+       };
+#  endif       /* MD5SUM_SIZE_VS_SPEED > 1 */
+# endif
+
+       uint32_t A = ctx->A;
+       uint32_t B = ctx->B;
+       uint32_t C = ctx->C;
+       uint32_t D = ctx->D;
+
+       /* First increment the byte count.  RFC 1321 specifies the possible
+          length of the file up to 2^64 bits.  Here we only compute the
+          number of bytes.  Do a double word increment.  */
+       ctx->total[0] += len;
+       if (ctx->total[0] < len)
+               ++ctx->total[1];
+
+       /* Process all bytes in the buffer with 64 bytes in each round of
+          the loop.  */
+       while (words < endp) {
+               uint32_t *cwp = correct_words;
+               uint32_t A_save = A;
+               uint32_t B_save = B;
+               uint32_t C_save = C;
+               uint32_t D_save = D;
+
+# if MD5SUM_SIZE_VS_SPEED > 1
+#  define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+               const uint32_t *pc;
+               const char *pp;
+               const char *ps;
+               int i;
+               uint32_t temp;
+
+               for (i = 0; i < 16; i++) {
+                       cwp[i] = SWAP(words[i]);
+               }
+               words += 16;
+
+#  if MD5SUM_SIZE_VS_SPEED > 2
+               pc = C_array;
+               pp = P_array;
+               ps = S_array - 4;
+
+               for (i = 0; i < 64; i++) {
+                       if ((i & 0x0f) == 0)
+                               ps += 4;
+                       temp = A;
+                       switch (i >> 4) {
+                       case 0:
+                               temp += FF(B, C, D);
+                               break;
+                       case 1:
+                               temp += FG(B, C, D);
+                               break;
+                       case 2:
+                               temp += FH(B, C, D);
+                               break;
+                       case 3:
+                               temp += FI(B, C, D);
+                       }
+                       temp += cwp[(int) (*pp++)] + *pc++;
+                       CYCLIC(temp, ps[i & 3]);
+                       temp += B;
+                       A = D;
+                       D = C;
+                       C = B;
+                       B = temp;
+               }
+#  else
+               pc = C_array;
+               pp = P_array;
+               ps = S_array;
+
+               for (i = 0; i < 16; i++) {
+                       temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+                       CYCLIC(temp, ps[i & 3]);
+                       temp += B;
+                       A = D;
+                       D = C;
+                       C = B;
+                       B = temp;
+               }
+
+               ps += 4;
+               for (i = 0; i < 16; i++) {
+                       temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+                       CYCLIC(temp, ps[i & 3]);
+                       temp += B;
+                       A = D;
+                       D = C;
+                       C = B;
+                       B = temp;
+               }
+               ps += 4;
+               for (i = 0; i < 16; i++) {
+                       temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+                       CYCLIC(temp, ps[i & 3]);
+                       temp += B;
+                       A = D;
+                       D = C;
+                       C = B;
+                       B = temp;
+               }
+               ps += 4;
+               for (i = 0; i < 16; i++) {
+                       temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+                       CYCLIC(temp, ps[i & 3]);
+                       temp += B;
+                       A = D;
+                       D = C;
+                       C = B;
+                       B = temp;
+               }
+
+#  endif       /* MD5SUM_SIZE_VS_SPEED > 2 */
+# else
+               /* First round: using the given function, the context and a constant
+                  the next context is computed.  Because the algorithms processing
+                  unit is a 32-bit word and it is determined to work on words in
+                  little endian byte order we perhaps have to change the byte order
+                  before the computation.  To reduce the work for the next steps
+                  we store the swapped words in the array CORRECT_WORDS.  */
+
+#  define OP(a, b, c, d, s, T) \
+      do       \
+        {      \
+         a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T;     \
+         ++words;      \
+         CYCLIC (a, s);        \
+         a += b;       \
+        }      \
+      while (0)
+
+               /* It is unfortunate that C does not provide an operator for
+                  cyclic rotation.  Hope the C compiler is smart enough.  */
+               /* gcc 2.95.4 seems to be --aaronl */
+#  define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+               /* Before we start, one word to the strange constants.
+                  They are defined in RFC 1321 as
+
+                  T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+                */
+
+#  if MD5SUM_SIZE_VS_SPEED == 1
+               const uint32_t *pc;
+               const char *pp;
+               int i;
+#  endif       /* MD5SUM_SIZE_VS_SPEED */
+
+               /* Round 1.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+               pc = C_array;
+               for (i = 0; i < 4; i++) {
+                       OP(A, B, C, D, 7, *pc++);
+                       OP(D, A, B, C, 12, *pc++);
+                       OP(C, D, A, B, 17, *pc++);
+                       OP(B, C, D, A, 22, *pc++);
+               }
+#  else
+               OP(A, B, C, D, 7, 0xd76aa478);
+               OP(D, A, B, C, 12, 0xe8c7b756);
+               OP(C, D, A, B, 17, 0x242070db);
+               OP(B, C, D, A, 22, 0xc1bdceee);
+               OP(A, B, C, D, 7, 0xf57c0faf);
+               OP(D, A, B, C, 12, 0x4787c62a);
+               OP(C, D, A, B, 17, 0xa8304613);
+               OP(B, C, D, A, 22, 0xfd469501);
+               OP(A, B, C, D, 7, 0x698098d8);
+               OP(D, A, B, C, 12, 0x8b44f7af);
+               OP(C, D, A, B, 17, 0xffff5bb1);
+               OP(B, C, D, A, 22, 0x895cd7be);
+               OP(A, B, C, D, 7, 0x6b901122);
+               OP(D, A, B, C, 12, 0xfd987193);
+               OP(C, D, A, B, 17, 0xa679438e);
+               OP(B, C, D, A, 22, 0x49b40821);
+#  endif       /* MD5SUM_SIZE_VS_SPEED == 1 */
+
+               /* For the second to fourth round we have the possibly swapped words
+                  in CORRECT_WORDS.  Redefine the macro to take an additional first
+                  argument specifying the function to use.  */
+#  undef OP
+#  define OP(f, a, b, c, d, k, s, T)   \
+      do       \
+       {       \
+         a += f (b, c, d) + correct_words[k] + T;      \
+         CYCLIC (a, s);        \
+         a += b;       \
+       }       \
+      while (0)
+
+               /* Round 2.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+               pp = P_array;
+               for (i = 0; i < 4; i++) {
+                       OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
+                       OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
+                       OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
+                       OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
+               }
+#  else
+               OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+               OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+               OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+               OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+               OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+               OP(FG, D, A, B, C, 10, 9, 0x02441453);
+               OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+               OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+               OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+               OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+               OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+               OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+               OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+               OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+               OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+               OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+#  endif       /* MD5SUM_SIZE_VS_SPEED == 1 */
+
+               /* Round 3.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+               for (i = 0; i < 4; i++) {
+                       OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
+                       OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
+                       OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
+                       OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
+               }
+#  else
+               OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+               OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+               OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+               OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+               OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+               OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+               OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+               OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+               OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+               OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+               OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+               OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+               OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+               OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+               OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+               OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+#  endif       /* MD5SUM_SIZE_VS_SPEED == 1 */
+
+               /* Round 4.  */
+#  if MD5SUM_SIZE_VS_SPEED == 1
+               for (i = 0; i < 4; i++) {
+                       OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
+                       OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
+                       OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
+                       OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
+               }
+#  else
+               OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+               OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+               OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+               OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+               OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+               OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+               OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+               OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+               OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+               OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+               OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+               OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+               OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+               OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+               OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+               OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+#  endif       /* MD5SUM_SIZE_VS_SPEED == 1 */
+# endif        /* MD5SUM_SIZE_VS_SPEED > 1 */
+
+               /* Add the starting values of the context.  */
+               A += A_save;
+               B += B_save;
+               C += C_save;
+               D += D_save;
+       }
+
+       /* Put checksum in context given as argument.  */
+       ctx->A = A;
+       ctx->B = B;
+       ctx->C = C;
+       ctx->D = D;
+}
+
+/* Starting with the result of former calls of this function (or the
+ * initialization function update the context for the next LEN bytes
+ * starting at BUFFER.
+ * It is NOT required that LEN is a multiple of 64.
+ */
+
+static void md5_hash_bytes(const void *buffer, size_t len, struct md5_ctx_t *ctx)
+{
+       /* When we already have some bits in our internal buffer concatenate
+          both inputs first.  */
+       if (ctx->buflen != 0) {
+               size_t left_over = ctx->buflen;
+               size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+               memcpy(&ctx->buffer[left_over], buffer, add);
+               ctx->buflen += add;
+
+               if (left_over + add > 64) {
+                       md5_hash_block(ctx->buffer, (left_over + add) & ~63, ctx);
+                       /* The regions in the following copy operation cannot overlap.  */
+                       memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+                                  (left_over + add) & 63);
+                       ctx->buflen = (left_over + add) & 63;
+               }
+
+               buffer = (const char *) buffer + add;
+               len -= add;
+       }
+
+       /* Process available complete blocks.  */
+       if (len > 64) {
+               md5_hash_block(buffer, len & ~63, ctx);
+               buffer = (const char *) buffer + (len & ~63);
+               len &= 63;
+       }
+
+       /* Move remaining bytes in internal buffer.  */
+       if (len > 0) {
+               memcpy(ctx->buffer, buffer, len);
+               ctx->buflen = len;
+       }
+}
+
+static void md5_hash(const void *buffer, size_t length, void *md5_ctx)
+{
+       if (length % 64 == 0) {
+               md5_hash_block(buffer, length, md5_ctx);
+       } else {
+               md5_hash_bytes(buffer, length, md5_ctx);
+       }
+}
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ * in first 16 bytes following RESBUF.  The result is always in little
+ * endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ *
+ * IMPORTANT: On some systems it is required that RESBUF is correctly
+ * aligned for a 32 bits value.
+ */
+static void *md5_end(void *resbuf, struct md5_ctx_t *ctx)
+{
+       /* Take yet unprocessed bytes into account.  */
+       uint32_t bytes = ctx->buflen;
+       size_t pad;
+
+       /* Now count remaining bytes.  */
+       ctx->total[0] += bytes;
+       if (ctx->total[0] < bytes)
+               ++ctx->total[1];
+
+       pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+# if MD5SUM_SIZE_VS_SPEED > 0
+       memset(&ctx->buffer[bytes], 0, pad);
+       ctx->buffer[bytes] = 0x80;
+# else
+       memcpy(&ctx->buffer[bytes], fillbuf, pad);
+# endif        /* MD5SUM_SIZE_VS_SPEED > 0 */
+
+       /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+       *(uint32_t *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3);
+       *(uint32_t *) & ctx->buffer[bytes + pad + 4] =
+               SWAP(((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
+
+       /* Process last bytes.  */
+       md5_hash_block(ctx->buffer, bytes + pad + 8, ctx);
+
+       /* Put result from CTX in first 16 bytes following RESBUF.  The result is
+        * always in little endian byte order, so that a byte-wise output yields
+        * to the wanted ASCII representation of the message digest.
+        *
+        * IMPORTANT: On some systems it is required that RESBUF is correctly
+        * aligned for a 32 bits value.
+        */
+       ((uint32_t *) resbuf)[0] = SWAP(ctx->A);
+       ((uint32_t *) resbuf)[1] = SWAP(ctx->B);
+       ((uint32_t *) resbuf)[2] = SWAP(ctx->C);
+       ((uint32_t *) resbuf)[3] = SWAP(ctx->D);
+
+       return resbuf;
+}
+#endif /* CONFIG_MD5SUM */
+
+
+
+
+extern int hash_fd(int src_fd, const size_t size, const uint8_t hash_algo,
+                                  uint8_t * hashval)
+{
+       int result = EXIT_SUCCESS;
+//     size_t hashed_count = 0;
+       size_t blocksize = 0;
+       size_t remaining = size;
+       unsigned char *buffer = NULL;
+       void (*hash_fn_ptr)(const void *, size_t, void *) = NULL;
+       void *cx = NULL;
+
+#ifdef CONFIG_SHA1SUM
+       struct sha1_ctx_t sha1_cx;
+#endif
+#ifdef CONFIG_MD5SUM
+       struct md5_ctx_t md5_cx;
+#endif
+
+
+#ifdef CONFIG_SHA1SUM
+       if (hash_algo == HASH_SHA1) {
+               /* Ensure that BLOCKSIZE is a multiple of 64.  */
+               blocksize = 65536;
+               buffer = xmalloc(blocksize);
+               hash_fn_ptr = sha1_hash;
+               cx = &sha1_cx;
+       }
+#endif
+#ifdef CONFIG_MD5SUM
+       if (hash_algo == HASH_MD5) {
+               blocksize = 4096;
+               buffer = xmalloc(blocksize + 72);
+               hash_fn_ptr = md5_hash;
+               cx = &md5_cx;
+       }
+#endif
+
+       /* Initialize the computation context.  */
+#ifdef CONFIG_SHA1SUM
+       if (hash_algo == HASH_SHA1) {
+               sha1_begin(&sha1_cx);
+       }
+#endif
+#ifdef CONFIG_MD5SUM
+       if (hash_algo == HASH_MD5) {
+               md5_begin(&md5_cx);
+       }
+#endif
+       /* Iterate over full file contents.  */
+       while ((remaining == (size_t) -1) || (remaining > 0)) {
+               size_t read_try;
+               ssize_t read_got;
+
+               if (remaining > blocksize) {
+                       read_try = blocksize;
+               } else {
+                       read_try = remaining;
+               }
+               read_got = bb_full_read(src_fd, buffer, read_try);
+               if (read_got < 1) {
+                       /* count == 0 means short read
+                        * count == -1 means read error */
+                       result = read_got - 1;
+                       break;
+               }
+               if (remaining != (size_t) -1) {
+                       remaining -= read_got;
+               }
+
+               /* Process buffer */
+               hash_fn_ptr(buffer, read_got, cx);
+       }
+
+       /* Finalize and write the hash into our buffer.  */
+#ifdef CONFIG_SHA1SUM
+       if (hash_algo == HASH_SHA1) {
+               sha1_end(hashval, &sha1_cx);
+       }
+#endif
+#ifdef CONFIG_MD5SUM
+       if (hash_algo == HASH_MD5) {
+               md5_end(hashval, &md5_cx);
+       }
+#endif
+
+       free(buffer);
+       return result;
+}
diff --git a/busybox/libbb/herror_msg.c b/busybox/libbb/herror_msg.c
new file mode 100644 (file)
index 0000000..87ec15a
--- /dev/null
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+
+extern void bb_herror_msg(const char *s, ...)
+{
+       va_list p;
+
+       va_start(p, s);
+       bb_vherror_msg(s, p);
+       va_end(p);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/herror_msg_and_die.c b/busybox/libbb/herror_msg_and_die.c
new file mode 100644 (file)
index 0000000..5c765f1
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+
+extern void bb_herror_msg_and_die(const char *s, ...)
+{
+       va_list p;
+
+       va_start(p, s);
+       bb_vherror_msg(s, p);
+       va_end(p);
+       exit(bb_default_error_retval);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/human_readable.c b/busybox/libbb/human_readable.c
new file mode 100644 (file)
index 0000000..ad9025c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * June 30, 2001                 Manuel Novoa III
+ *
+ * All-integer version (hey, not everyone has floating point) of
+ * make_human_readable_str, modified from similar code I had written
+ * for busybox several months ago.
+ *
+ * Notes:
+ *   1) I'm using an unsigned long long to hold the product size * block_size,
+ *      as df (which calls this routine) could request a representation of a
+ *      partition size in bytes > max of unsigned long.  If long longs aren't
+ *      available, it would be possible to do what's needed using polynomial
+ *      representations (say, powers of 1024) and manipulating coefficients.
+ *      The base ten "bytes" output could be handled similarly.
+ *
+ *   2) This routine always outputs a decimal point and a tenths digit when
+ *      display_unit != 0.  Hence, it isn't uncommon for the returned string
+ *      to have a length of 5 or 6.
+ *
+ *      It might be nice to add a flag to indicate no decimal digits in
+ *      that case.  This could be either an additional parameter, or a
+ *      special value of display_unit.  Such a flag would also be nice for du.
+ *
+ *      Some code to omit the decimal point and tenths digit is sketched out
+ *      and "#if 0"'d below.
+ */
+
+#include <stdio.h>
+#include "libbb.h"
+
+const char *make_human_readable_str(unsigned long long size,
+       unsigned long block_size, unsigned long display_unit)
+{
+       /* The code will adjust for additional (appended) units. */
+       static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' };
+       static const char fmt[] = "%Lu";
+       static const char fmt_tenths[] = "%Lu.%d%c";
+
+       static char str[21];            /* Sufficient for 64 bit unsigned integers. */
+
+       unsigned long long val;
+       int frac;
+       const char *u;
+       const char *f;
+
+       u = zero_and_units;
+       f = fmt;
+       frac = 0;
+
+       val = size * block_size;
+       if (val == 0) {
+               return u;
+       }
+
+       if (display_unit) {
+               val += display_unit/2;  /* Deal with rounding. */
+               val /= display_unit;    /* Don't combine with the line above!!! */
+       } else {
+               ++u;
+               while ((val >= KILOBYTE)
+                          && (u < zero_and_units + sizeof(zero_and_units) - 1)) {
+                       f = fmt_tenths;
+                       ++u;
+                       frac = ((((int)(val % KILOBYTE)) * 10) + (KILOBYTE/2)) / KILOBYTE;
+                       val /= KILOBYTE;
+               }
+               if (frac >= 10) {               /* We need to round up here. */
+                       ++val;
+                       frac = 0;
+               }
+#if 0
+               /* Sample code to omit decimal point and tenths digit. */
+               if ( /* no_tenths */ 1 ) {
+                       if ( frac >= 5 ) {
+                               ++val;
+                       }
+                       f = "%Lu%*c" /* fmt_no_tenths */ ;
+                       frac = 1;
+               }
+#endif
+       }
+
+       /* If f==fmt then 'frac' and 'u' are ignored. */
+       snprintf(str, sizeof(str), f, val, frac, *u);
+
+       return str;
+}
diff --git a/busybox/libbb/inet_common.c b/busybox/libbb/inet_common.c
new file mode 100644 (file)
index 0000000..321322d
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ *                      Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III       Mar 12, 2001
+ *
+ * Version:     $Id: inet_common.c,v 1.8 2004/03/10 07:42:38 mjn3 Exp $
+ *
+ */
+
+#include "inet_common.h"
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libbb.h"
+
+#ifdef DEBUG
+# include <resolv.h>
+#endif
+
+
+const char bb_INET_default[] = "default";
+
+int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst)
+{
+       struct hostent *hp;
+       struct netent *np;
+
+       /* Grmpf. -FvK */
+       s_in->sin_family = AF_INET;
+       s_in->sin_port = 0;
+
+       /* Default is special, meaning 0.0.0.0. */
+       if (!strcmp(name, bb_INET_default)) {
+               s_in->sin_addr.s_addr = INADDR_ANY;
+               return (1);
+       }
+       /* Look to see if it's a dotted quad. */
+       if (inet_aton(name, &s_in->sin_addr)) {
+               return 0;
+       }
+       /* If we expect this to be a hostname, try hostname database first */
+#ifdef DEBUG
+       if (hostfirst) {
+               bb_error_msg("gethostbyname (%s)", name);
+       }
+#endif
+       if (hostfirst && (hp = gethostbyname(name)) != (struct hostent *) NULL) {
+               memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0],
+                          sizeof(struct in_addr));
+               return 0;
+       }
+       /* Try the NETWORKS database to see if this is a known network. */
+#ifdef DEBUG
+       bb_error_msg("getnetbyname (%s)", name);
+#endif
+       if ((np = getnetbyname(name)) != (struct netent *) NULL) {
+               s_in->sin_addr.s_addr = htonl(np->n_net);
+               return 1;
+       }
+       if (hostfirst) {
+               /* Don't try again */
+               errno = h_errno;
+               return -1;
+       }
+#ifdef DEBUG
+       res_init();
+       _res.options |= RES_DEBUG;
+#endif
+
+#ifdef DEBUG
+       bb_error_msg("gethostbyname (%s)", name);
+#endif
+       if ((hp = gethostbyname(name)) == (struct hostent *) NULL) {
+               errno = h_errno;
+               return -1;
+       }
+       memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0],
+                  sizeof(struct in_addr));
+
+       return 0;
+}
+
+/* cache */
+struct addr {
+       struct sockaddr_in addr;
+       char *name;
+       int host;
+       struct addr *next;
+};
+
+static struct addr *INET_nn = NULL;    /* addr-to-name cache           */
+
+/* numeric: & 0x8000: default instead of *,
+ *          & 0x4000: host instead of net,
+ *          & 0x0fff: don't resolve
+ */
+int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
+                                 int numeric, unsigned int netmask)
+{
+       struct hostent *ent;
+       struct netent *np;
+       struct addr *pn;
+       unsigned long ad, host_ad;
+       int host = 0;
+
+       /* Grmpf. -FvK */
+       if (s_in->sin_family != AF_INET) {
+#ifdef DEBUG
+               bb_error_msg("rresolve: unsupport address family %d !",
+                                 s_in->sin_family);
+#endif
+               errno = EAFNOSUPPORT;
+               return (-1);
+       }
+       ad = (unsigned long) s_in->sin_addr.s_addr;
+#ifdef DEBUG
+       bb_error_msg("rresolve: %08lx, mask %08x, num %08x", ad, netmask, numeric);
+#endif
+       if (ad == INADDR_ANY) {
+               if ((numeric & 0x0FFF) == 0) {
+                       if (numeric & 0x8000)
+                               safe_strncpy(name, bb_INET_default, len);
+                       else
+                               safe_strncpy(name, "*", len);
+                       return (0);
+               }
+       }
+       if (numeric & 0x0FFF) {
+               safe_strncpy(name, inet_ntoa(s_in->sin_addr), len);
+               return (0);
+       }
+
+       if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
+               host = 1;
+#if 0
+       INET_nn = NULL;
+#endif
+       pn = INET_nn;
+       while (pn != NULL) {
+               if (pn->addr.sin_addr.s_addr == ad && pn->host == host) {
+                       safe_strncpy(name, pn->name, len);
+#ifdef DEBUG
+                       bb_error_msg("rresolve: found %s %08lx in cache",
+                                         (host ? "host" : "net"), ad);
+#endif
+                       return (0);
+               }
+               pn = pn->next;
+       }
+
+       host_ad = ntohl(ad);
+       np = NULL;
+       ent = NULL;
+       if (host) {
+#ifdef DEBUG
+               bb_error_msg("gethostbyaddr (%08lx)", ad);
+#endif
+               ent = gethostbyaddr((char *) &ad, 4, AF_INET);
+               if (ent != NULL) {
+                       safe_strncpy(name, ent->h_name, len);
+               }
+       } else {
+#ifdef DEBUG
+               bb_error_msg("getnetbyaddr (%08lx)", host_ad);
+#endif
+               np = getnetbyaddr(host_ad, AF_INET);
+               if (np != NULL) {
+                       safe_strncpy(name, np->n_name, len);
+               }
+       }
+       if ((ent == NULL) && (np == NULL)) {
+               safe_strncpy(name, inet_ntoa(s_in->sin_addr), len);
+       }
+       pn = (struct addr *) xmalloc(sizeof(struct addr));
+       pn->addr = *s_in;
+       pn->next = INET_nn;
+       pn->host = host;
+       pn->name = bb_xstrdup(name);
+       INET_nn = pn;
+
+       return (0);
+}
+
+#ifdef CONFIG_FEATURE_IPV6
+
+int INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
+{
+       struct addrinfo req, *ai;
+       int s;
+
+       memset(&req, '\0', sizeof req);
+       req.ai_family = AF_INET6;
+       if ((s = getaddrinfo(name, NULL, &req, &ai))) {
+               bb_error_msg("getaddrinfo: %s: %d", name, s);
+               return -1;
+       }
+       memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6));
+
+       freeaddrinfo(ai);
+
+       return (0);
+}
+
+#ifndef IN6_IS_ADDR_UNSPECIFIED
+# define IN6_IS_ADDR_UNSPECIFIED(a) \
+        (((__u32 *) (a))[0] == 0 && ((__u32 *) (a))[1] == 0 && \
+         ((__u32 *) (a))[2] == 0 && ((__u32 *) (a))[3] == 0)
+#endif
+
+
+int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6,
+                                  int numeric)
+{
+       int s;
+
+       /* Grmpf. -FvK */
+       if (sin6->sin6_family != AF_INET6) {
+#ifdef DEBUG
+               bb_error_msg(_("rresolve: unsupport address family %d !\n"),
+                                 sin6->sin6_family);
+#endif
+               errno = EAFNOSUPPORT;
+               return (-1);
+       }
+       if (numeric & 0x7FFF) {
+               inet_ntop(AF_INET6, &sin6->sin6_addr, name, len);
+               return (0);
+       }
+       if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+               if (numeric & 0x8000) {
+                       strcpy(name, "default");
+               } else {
+                       strcpy(name, "*");
+               }
+               return (0);
+       }
+
+       s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), name, len, NULL, 0, 0);
+       if (s) {
+               bb_error_msg("getnameinfo failed");
+               return -1;
+       }
+       return (0);
+}
+
+#endif                                                 /* CONFIG_FEATURE_IPV6 */
diff --git a/busybox/libbb/inode_hash.c b/busybox/libbb/inode_hash.c
new file mode 100644 (file)
index 0000000..fbcd813
--- /dev/null
@@ -0,0 +1,111 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libbb.h"
+
+#define HASH_SIZE      311             /* Should be prime */
+#define hash_inode(i)  ((i) % HASH_SIZE)
+
+typedef struct ino_dev_hash_bucket_struct {
+  struct ino_dev_hash_bucket_struct *next;
+  ino_t ino;
+  dev_t dev;
+  char name[1];
+} ino_dev_hashtable_bucket_t;
+
+static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
+
+/*
+ * Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in
+ * `ino_dev_hashtable', else return 0
+ *
+ * If NAME is a non-NULL pointer to a character pointer, and there is
+ * a match, then set *NAME to the value of the name slot in that
+ * bucket.
+ */
+int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name)
+{
+       ino_dev_hashtable_bucket_t *bucket;
+
+       bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
+       while (bucket != NULL) {
+         if ((bucket->ino == statbuf->st_ino) &&
+                 (bucket->dev == statbuf->st_dev))
+         {
+               if (name) *name = bucket->name;
+               return 1;
+         }
+         bucket = bucket->next;
+       }
+       return 0;
+}
+
+/* Add statbuf to statbuf hash table */
+void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
+{
+       int i;
+       size_t s;
+       ino_dev_hashtable_bucket_t *bucket;
+
+       i = hash_inode(statbuf->st_ino);
+       s = name ? strlen(name) : 0;
+       bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s);
+       bucket->ino = statbuf->st_ino;
+       bucket->dev = statbuf->st_dev;
+       if (name)
+               strcpy(bucket->name, name);
+       else
+               bucket->name[0] = '\0';
+       bucket->next = ino_dev_hashtable[i];
+       ino_dev_hashtable[i] = bucket;
+}
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+/* Clear statbuf hash table */
+void reset_ino_dev_hashtable(void)
+{
+       int i;
+       ino_dev_hashtable_bucket_t *bucket;
+
+       for (i = 0; i < HASH_SIZE; i++) {
+               while (ino_dev_hashtable[i] != NULL) {
+                       bucket = ino_dev_hashtable[i]->next;
+                       free(ino_dev_hashtable[i]);
+                       ino_dev_hashtable[i] = bucket;
+               }
+       }
+}
+#endif
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/interface.c b/busybox/libbb/interface.c
new file mode 100644 (file)
index 0000000..fe2d0b4
--- /dev/null
@@ -0,0 +1,2083 @@
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ *                     Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III       Mar 12, 2001
+ *
+ * Pruned unused code using KEEP_UNUSED define.
+ * Added print_bytes_scaled function to reduce code size.
+ * Added some (potentially) missing defines.
+ * Improved display support for -a and for a named interface.
+ *
+ * -----------------------------------------------------------
+ *
+ * ifconfig   This file contains an implementation of the command
+ *              that either displays or sets the characteristics of
+ *              one or more of the system's networking interfaces.
+ *
+ * Version:     $Id: interface.c,v 1.25 2004/08/26 21:45:21 andersen Exp $
+ *
+ * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *              and others.  Copyright 1993 MicroWalt Corporation
+ *
+ *              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.
+ *
+ * Patched to support 'add' and 'del' keywords for INET(4) addresses
+ * by Mrs. Brisby <mrs.brisby@nimh.org>
+ *
+ * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *                     - gettext instead of catgets for i18n
+ *          10/1998  - Andi Kleen. Use interface list primitives.
+ *         20001008 - Bernd Eckenfels, Patch from RH for setting mtu
+ *                     (default AF was wrong)
+ */
+
+/* #define KEEP_UNUSED */
+
+/*
+ *
+ * Protocol Families.
+ *
+ */
+#define HAVE_AFINET 1
+#undef HAVE_AFIPX
+#undef HAVE_AFATALK
+#undef HAVE_AFNETROM
+#undef HAVE_AFX25
+#undef HAVE_AFECONET
+#undef HAVE_AFASH
+
+/*
+ *
+ * Device Hardware types.
+ *
+ */
+#define HAVE_HWETHER   1
+#define HAVE_HWPPP     1
+#undef HAVE_HWSLIP
+
+
+#include "inet_common.h"
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include "libbb.h"
+
+#ifdef CONFIG_FEATURE_IPV6
+# define HAVE_AFINET6 1
+#else
+# undef HAVE_AFINET6
+#endif
+
+#define _(x) x
+#define _PATH_PROCNET_DEV               "/proc/net/dev"
+#define _PATH_PROCNET_IFINET6           "/proc/net/if_inet6"
+#define new(p) ((p) = xcalloc(1,sizeof(*(p))))
+#define KRELEASE(maj,min,patch) ((maj) * 65536 + (min)*256 + (patch))
+
+#ifdef HAVE_HWSLIP
+#include <net/if_slip.h>
+#endif
+
+#if HAVE_AFINET6
+
+#ifndef _LINUX_IN6_H
+/*
+ *    This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+       struct in6_addr ifr6_addr;
+       uint32_t ifr6_prefixlen;
+       unsigned int ifr6_ifindex;
+};
+
+#endif
+
+#endif                                                 /* HAVE_AFINET6 */
+
+#if HAVE_AFIPX
+#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
+#include <netipx/ipx.h>
+#else
+#include "ipx.h"
+#endif
+#endif
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+#define SIOCSIFTXQLEN      0x8943
+#define SIOCGIFTXQLEN      0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+#define ifr_qlen        ifr_ifru.ifru_mtu
+#endif
+
+#ifndef HAVE_TXQUEUELEN
+#define HAVE_TXQUEUELEN 1
+#endif
+
+#ifndef IFF_DYNAMIC
+#define IFF_DYNAMIC     0x8000 /* dialup device with changing addresses */
+#endif
+
+/* This structure defines protocol families and their handlers. */
+struct aftype {
+       const char *name;
+       const char *title;
+       int af;
+       int alen;
+       char *(*print) (unsigned char *);
+       char *(*sprint) (struct sockaddr *, int numeric);
+       int (*input) (int type, char *bufp, struct sockaddr *);
+       void (*herror) (char *text);
+       int (*rprint) (int options);
+       int (*rinput) (int typ, int ext, char **argv);
+
+       /* may modify src */
+       int (*getmask) (char *src, struct sockaddr * mask, char *name);
+
+       int fd;
+       char *flag_file;
+};
+
+#ifdef KEEP_UNUSED
+
+static int flag_unx;
+
+#ifdef HAVE_AFIPX
+static int flag_ipx;
+#endif
+#ifdef HAVE_AFX25
+static int flag_ax25;
+#endif
+#ifdef HAVE_AFATALK
+static int flag_ddp;
+#endif
+#ifdef HAVE_AFNETROM
+static int flag_netrom;
+#endif
+static int flag_inet;
+
+#ifdef HAVE_AFINET6
+static int flag_inet6;
+#endif
+#ifdef HAVE_AFECONET
+static int flag_econet;
+#endif
+#ifdef HAVE_AFX25
+static int flag_x25 = 0;
+#endif
+#ifdef HAVE_AFASH
+static int flag_ash;
+#endif
+
+
+static struct aftrans_t {
+       char *alias;
+       char *name;
+       int *flag;
+} aftrans[] = {
+
+#ifdef HAVE_AFX25
+       {
+       "ax25", "ax25", &flag_ax25},
+#endif
+       {
+       "ip", "inet", &flag_inet},
+#ifdef HAVE_AFINET6
+       {
+       "ip6", "inet6", &flag_inet6},
+#endif
+#ifdef HAVE_AFIPX
+       {
+       "ipx", "ipx", &flag_ipx},
+#endif
+#ifdef HAVE_AFATALK
+       {
+       "appletalk", "ddp", &flag_ddp},
+#endif
+#ifdef HAVE_AFNETROM
+       {
+       "netrom", "netrom", &flag_netrom},
+#endif
+       {
+       "inet", "inet", &flag_inet},
+#ifdef HAVE_AFINET6
+       {
+       "inet6", "inet6", &flag_inet6},
+#endif
+#ifdef HAVE_AFATALK
+       {
+       "ddp", "ddp", &flag_ddp},
+#endif
+       {
+       "unix", "unix", &flag_unx}, {
+       "tcpip", "inet", &flag_inet},
+#ifdef HAVE_AFECONET
+       {
+       "econet", "ec", &flag_econet},
+#endif
+#ifdef HAVE_AFX25
+       {
+       "x25", "x25", &flag_x25},
+#endif
+#ifdef HAVE_AFASH
+       {
+       "ash", "ash", &flag_ash},
+#endif
+       {
+       0, 0, 0}
+};
+
+static char afname[256] = "";
+#endif                                                 /* KEEP_UNUSED */
+
+#if HAVE_AFUNIX
+
+/* Display a UNIX domain address. */
+static char *UNIX_print(unsigned char *ptr)
+{
+       return (ptr);
+}
+
+
+/* Display a UNIX domain address. */
+static char *UNIX_sprint(struct sockaddr *sap, int numeric)
+{
+       static char buf[64];
+
+       if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+               return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf));
+       return (UNIX_print(sap->sa_data));
+}
+
+
+static struct aftype unix_aftype = {
+       "unix", "UNIX Domain", AF_UNIX, 0,
+       UNIX_print, UNIX_sprint, NULL, NULL,
+       NULL, NULL, NULL,
+       -1,
+       "/proc/net/unix"
+};
+#endif                                                 /* HAVE_AFUNIX */
+
+#if HAVE_AFINET
+
+#ifdef KEEP_UNUSED
+static void INET_reserror(char *text)
+{
+       herror(text);
+}
+
+/* Display an Internet socket address. */
+static char *INET_print(unsigned char *ptr)
+{
+       return (inet_ntoa((*(struct in_addr *) ptr)));
+}
+#endif                                                 /* KEEP_UNUSED */
+
+/* Display an Internet socket address. */
+static char *INET_sprint(struct sockaddr *sap, int numeric)
+{
+       static char buff[128];
+
+       if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+               return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
+
+       if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
+                                         numeric, 0xffffff00) != 0)
+               return (NULL);
+
+       return (buff);
+}
+
+#ifdef KEEP_UNUSED
+static char *INET_sprintmask(struct sockaddr *sap, int numeric,
+                                                        unsigned int netmask)
+{
+       static char buff[128];
+
+       if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+               return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
+       if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
+                                         numeric, netmask) != 0)
+               return (NULL);
+       return (buff);
+}
+
+static int INET_getsock(char *bufp, struct sockaddr *sap)
+{
+       char *sp = bufp, *bp;
+       unsigned int i;
+       unsigned val;
+       struct sockaddr_in *sin;
+
+       sin = (struct sockaddr_in *) sap;
+       sin->sin_family = AF_INET;
+       sin->sin_port = 0;
+
+       val = 0;
+       bp = (char *) &val;
+       for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) {
+               *sp = toupper(*sp);
+
+               if ((*sp >= 'A') && (*sp <= 'F'))
+                       bp[i] |= (int) (*sp - 'A') + 10;
+               else if ((*sp >= '0') && (*sp <= '9'))
+                       bp[i] |= (int) (*sp - '0');
+               else
+                       return (-1);
+
+               bp[i] <<= 4;
+               sp++;
+               *sp = toupper(*sp);
+
+               if ((*sp >= 'A') && (*sp <= 'F'))
+                       bp[i] |= (int) (*sp - 'A') + 10;
+               else if ((*sp >= '0') && (*sp <= '9'))
+                       bp[i] |= (int) (*sp - '0');
+               else
+                       return (-1);
+
+               sp++;
+       }
+       sin->sin_addr.s_addr = htonl(val);
+
+       return (sp - bufp);
+}
+
+static int INET_input(int type, char *bufp, struct sockaddr *sap)
+{
+       switch (type) {
+       case 1:
+               return (INET_getsock(bufp, sap));
+       case 256:
+               return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
+       default:
+               return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
+       }
+}
+
+static int INET_getnetmask(char *adr, struct sockaddr *m, char *name)
+{
+       struct sockaddr_in *mask = (struct sockaddr_in *) m;
+       char *slash, *end;
+       int prefix;
+
+       if ((slash = strchr(adr, '/')) == NULL)
+               return 0;
+
+       *slash++ = '\0';
+       prefix = strtoul(slash, &end, 0);
+       if (*end != '\0')
+               return -1;
+
+       if (name) {
+               sprintf(name, "/%d", prefix);
+       }
+       mask->sin_family = AF_INET;
+       mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix));
+       return 1;
+}
+#endif                                                 /* KEEP_UNUSED */
+
+static struct aftype inet_aftype = {
+       "inet", "DARPA Internet", AF_INET, sizeof(unsigned long),
+       NULL /* UNUSED INET_print */ , INET_sprint,
+       NULL /* UNUSED INET_input */ , NULL /* UNUSED INET_reserror */ ,
+       NULL /*INET_rprint */ , NULL /*INET_rinput */ ,
+       NULL /* UNUSED INET_getnetmask */ ,
+       -1,
+       NULL
+};
+
+#endif                                                 /* HAVE_AFINET */
+
+#if HAVE_AFINET6
+
+#ifdef KEEP_UNUSED
+static void INET6_reserror(char *text)
+{
+       herror(text);
+}
+
+/* Display an Internet socket address. */
+static char *INET6_print(unsigned char *ptr)
+{
+       static char name[80];
+
+       inet_ntop(AF_INET6, (struct in6_addr *) ptr, name, 80);
+       return name;
+}
+#endif                                                 /* KEEP_UNUSED */
+
+/* Display an Internet socket address. */
+/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
+static char *INET6_sprint(struct sockaddr *sap, int numeric)
+{
+       static char buff[128];
+
+       if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+               return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
+       if (INET6_rresolve
+               (buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric) != 0)
+               return safe_strncpy(buff, _("[UNKNOWN]"), sizeof(buff));
+       return (buff);
+}
+
+#ifdef KEEP_UNUSED
+static int INET6_getsock(char *bufp, struct sockaddr *sap)
+{
+       struct sockaddr_in6 *sin6;
+
+       sin6 = (struct sockaddr_in6 *) sap;
+       sin6->sin6_family = AF_INET6;
+       sin6->sin6_port = 0;
+
+       if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
+               return (-1);
+
+       return 16;                      /* ?;) */
+}
+
+static int INET6_input(int type, char *bufp, struct sockaddr *sap)
+{
+       switch (type) {
+       case 1:
+               return (INET6_getsock(bufp, sap));
+       default:
+               return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
+       }
+}
+#endif                                                 /* KEEP_UNUSED */
+
+static struct aftype inet6_aftype = {
+       "inet6", "IPv6", AF_INET6, sizeof(struct in6_addr),
+       NULL /* UNUSED INET6_print */ , INET6_sprint,
+       NULL /* UNUSED INET6_input */ , NULL /* UNUSED INET6_reserror */ ,
+       NULL /*INET6_rprint */ , NULL /*INET6_rinput */ ,
+       NULL /* UNUSED INET6_getnetmask */ ,
+       -1,
+       NULL
+};
+
+#endif                                                 /* HAVE_AFINET6 */
+
+/* Display an UNSPEC address. */
+static char *UNSPEC_print(unsigned char *ptr)
+{
+       static char buff[sizeof(struct sockaddr) * 3 + 1];
+       char *pos;
+       unsigned int i;
+
+       pos = buff;
+       for (i = 0; i < sizeof(struct sockaddr); i++) {
+               /* careful -- not every libc's sprintf returns # bytes written */
+               sprintf(pos, "%02X-", (*ptr++ & 0377));
+               pos += 3;
+       }
+       /* Erase trailing "-".  Works as long as sizeof(struct sockaddr) != 0 */
+       *--pos = '\0';
+       return (buff);
+}
+
+/* Display an UNSPEC socket address. */
+static char *UNSPEC_sprint(struct sockaddr *sap, int numeric)
+{
+       static char buf[64];
+
+       if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+               return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf));
+       return (UNSPEC_print(sap->sa_data));
+}
+
+static struct aftype unspec_aftype = {
+       "unspec", "UNSPEC", AF_UNSPEC, 0,
+       UNSPEC_print, UNSPEC_sprint, NULL, NULL,
+       NULL,
+};
+
+static struct aftype *aftypes[] = {
+#if HAVE_AFUNIX
+       &unix_aftype,
+#endif
+#if HAVE_AFINET
+       &inet_aftype,
+#endif
+#if HAVE_AFINET6
+       &inet6_aftype,
+#endif
+#if HAVE_AFAX25
+       &ax25_aftype,
+#endif
+#if HAVE_AFNETROM
+       &netrom_aftype,
+#endif
+#if HAVE_AFROSE
+       &rose_aftype,
+#endif
+#if HAVE_AFIPX
+       &ipx_aftype,
+#endif
+#if HAVE_AFATALK
+       &ddp_aftype,
+#endif
+#if HAVE_AFECONET
+       &ec_aftype,
+#endif
+#if HAVE_AFASH
+       &ash_aftype,
+#endif
+#if HAVE_AFX25
+       &x25_aftype,
+#endif
+       &unspec_aftype,
+       NULL
+};
+
+#ifdef KEEP_UNUSED
+static short sVafinit = 0;
+
+static void afinit()
+{
+       unspec_aftype.title = _("UNSPEC");
+#if HAVE_AFUNIX
+       unix_aftype.title = _("UNIX Domain");
+#endif
+#if HAVE_AFINET
+       inet_aftype.title = _("DARPA Internet");
+#endif
+#if HAVE_AFINET6
+       inet6_aftype.title = _("IPv6");
+#endif
+#if HAVE_AFAX25
+       ax25_aftype.title = _("AMPR AX.25");
+#endif
+#if HAVE_AFNETROM
+       netrom_aftype.title = _("AMPR NET/ROM");
+#endif
+#if HAVE_AFIPX
+       ipx_aftype.title = _("Novell IPX");
+#endif
+#if HAVE_AFATALK
+       ddp_aftype.title = _("Appletalk DDP");
+#endif
+#if HAVE_AFECONET
+       ec_aftype.title = _("Econet");
+#endif
+#if HAVE_AFX25
+       x25_aftype.title = _("CCITT X.25");
+#endif
+#if HAVE_AFROSE
+       rose_aftype.title = _("AMPR ROSE");
+#endif
+#if HAVE_AFASH
+       ash_aftype.title = _("Ash");
+#endif
+       sVafinit = 1;
+}
+
+static int aftrans_opt(const char *arg)
+{
+       struct aftrans_t *paft;
+       char *tmp1, *tmp2;
+       char buf[256];
+
+       safe_strncpy(buf, arg, sizeof(buf));
+
+       tmp1 = buf;
+
+       while (tmp1) {
+
+               tmp2 = strchr(tmp1, ',');
+
+               if (tmp2)
+                       *(tmp2++) = '\0';
+
+               paft = aftrans;
+               for (paft = aftrans; paft->alias; paft++) {
+                       if (strcmp(tmp1, paft->alias))
+                               continue;
+                       if (strlen(paft->name) + strlen(afname) + 1 >= sizeof(afname)) {
+                               bb_error_msg(_("Too many address family arguments."));
+                               return (0);
+                       }
+                       if (paft->flag)
+                               (*paft->flag)++;
+                       if (afname[0])
+                               strcat(afname, ",");
+                       strcat(afname, paft->name);
+                       break;
+               }
+               if (!paft->alias) {
+                       bb_error_msg(_("Unknown address family `%s'."), tmp1);
+                       return (1);
+               }
+               tmp1 = tmp2;
+       }
+
+       return (0);
+}
+
+/* set the default AF list from the program name or a constant value    */
+static void aftrans_def(char *tool, char *argv0, char *dflt)
+{
+       char *tmp;
+       char *buf;
+
+       strcpy(afname, dflt);
+
+       if (!(tmp = strrchr(argv0, '/')))
+               tmp = argv0;    /* no slash?! */
+       else
+               tmp++;
+
+       if (!(buf = strdup(tmp)))
+               return;
+
+       if (strlen(tool) >= strlen(tmp)) {
+               free(buf);
+               return;
+       }
+       tmp = buf + (strlen(tmp) - strlen(tool));
+
+       if (strcmp(tmp, tool) != 0) {
+               free(buf);
+               return;
+       }
+       *tmp = '\0';
+       if ((tmp = strchr(buf, '_')))
+               *tmp = '\0';
+
+       afname[0] = '\0';
+       if (aftrans_opt(buf))
+               strcpy(afname, buf);
+
+       free(buf);
+}
+
+/* Check our protocol family table for this family. */
+static struct aftype *get_aftype(const char *name)
+{
+       struct aftype **afp;
+
+#ifdef KEEP_UNUSED
+       if (!sVafinit)
+               afinit();
+#endif                                                 /* KEEP_UNUSED */
+
+       afp = aftypes;
+       while (*afp != NULL) {
+               if (!strcmp((*afp)->name, name))
+                       return (*afp);
+               afp++;
+       }
+       if (strchr(name, ','))
+               bb_error_msg(_("Please don't supply more than one address family."));
+       return (NULL);
+}
+#endif                                                 /* KEEP_UNUSED */
+
+/* Check our protocol family table for this family. */
+static struct aftype *get_afntype(int af)
+{
+       struct aftype **afp;
+
+#ifdef KEEP_UNUSED
+       if (!sVafinit)
+               afinit();
+#endif                                                 /* KEEP_UNUSED */
+
+       afp = aftypes;
+       while (*afp != NULL) {
+               if ((*afp)->af == af)
+                       return (*afp);
+               afp++;
+       }
+       return (NULL);
+}
+
+/* Check our protocol family table for this family and return its socket */
+static int get_socket_for_af(int af)
+{
+       struct aftype **afp;
+
+#ifdef KEEP_UNUSED
+       if (!sVafinit)
+               afinit();
+#endif                                                 /* KEEP_UNUSED */
+
+       afp = aftypes;
+       while (*afp != NULL) {
+               if ((*afp)->af == af)
+                       return (*afp)->fd;
+               afp++;
+       }
+       return -1;
+}
+
+#ifdef KEEP_UNUSED
+/* type: 0=all, 1=getroute */
+static void print_aflist(int type)
+{
+       int count = 0;
+       char *txt;
+       struct aftype **afp;
+
+#ifdef KEEP_UNUSED
+       if (!sVafinit)
+               afinit();
+#endif                                                 /* KEEP_UNUSED */
+
+       afp = aftypes;
+       while (*afp != NULL) {
+               if ((type == 1 && ((*afp)->rprint == NULL)) || ((*afp)->af == 0)) {
+                       afp++;
+                       continue;
+               }
+               if ((count % 3) == 0)
+                       fprintf(stderr, count ? "\n    " : "    ");
+               txt = (*afp)->name;
+               if (!txt)
+                       txt = "..";
+               fprintf(stderr, "%s (%s) ", txt, _((*afp)->title));
+               count++;
+               afp++;
+       }
+       fprintf(stderr, "\n");
+}
+#endif                                                 /* KEEP_UNUSED */
+
+struct user_net_device_stats {
+       unsigned long long rx_packets;  /* total packets received       */
+       unsigned long long tx_packets;  /* total packets transmitted    */
+       unsigned long long rx_bytes;    /* total bytes received         */
+       unsigned long long tx_bytes;    /* total bytes transmitted      */
+       unsigned long rx_errors;        /* bad packets received         */
+       unsigned long tx_errors;        /* packet transmit problems     */
+       unsigned long rx_dropped;       /* no space in linux buffers    */
+       unsigned long tx_dropped;       /* no space available in linux  */
+       unsigned long rx_multicast;     /* multicast packets received   */
+       unsigned long rx_compressed;
+       unsigned long tx_compressed;
+       unsigned long collisions;
+
+       /* detailed rx_errors: */
+       unsigned long rx_length_errors;
+       unsigned long rx_over_errors;   /* receiver ring buff overflow  */
+       unsigned long rx_crc_errors;    /* recved pkt with crc error    */
+       unsigned long rx_frame_errors;  /* recv'd frame alignment error */
+       unsigned long rx_fifo_errors;   /* recv'r fifo overrun          */
+       unsigned long rx_missed_errors; /* receiver missed packet     */
+       /* detailed tx_errors */
+       unsigned long tx_aborted_errors;
+       unsigned long tx_carrier_errors;
+       unsigned long tx_fifo_errors;
+       unsigned long tx_heartbeat_errors;
+       unsigned long tx_window_errors;
+};
+
+struct interface {
+       struct interface *next, *prev;
+       char name[IFNAMSIZ];    /* interface name        */
+       short type;                     /* if type               */
+       short flags;            /* various flags         */
+       int metric;                     /* routing metric        */
+       int mtu;                        /* MTU value             */
+       int tx_queue_len;       /* transmit queue length */
+       struct ifmap map;       /* hardware setup        */
+       struct sockaddr addr;   /* IP address            */
+       struct sockaddr dstaddr;        /* P-P IP address        */
+       struct sockaddr broadaddr;      /* IP broadcast address  */
+       struct sockaddr netmask;        /* IP network mask       */
+       struct sockaddr ipxaddr_bb;     /* IPX network address   */
+       struct sockaddr ipxaddr_sn;     /* IPX network address   */
+       struct sockaddr ipxaddr_e3;     /* IPX network address   */
+       struct sockaddr ipxaddr_e2;     /* IPX network address   */
+       struct sockaddr ddpaddr;        /* Appletalk DDP address */
+       struct sockaddr ecaddr; /* Econet address        */
+       int has_ip;
+       int has_ipx_bb;
+       int has_ipx_sn;
+       int has_ipx_e3;
+       int has_ipx_e2;
+       int has_ax25;
+       int has_ddp;
+       int has_econet;
+       char hwaddr[32];        /* HW address            */
+       int statistics_valid;
+       struct user_net_device_stats stats;     /* statistics            */
+       int keepalive;          /* keepalive value for SLIP */
+       int outfill;            /* outfill value for SLIP */
+};
+
+
+int interface_opt_a = 0;       /* show all interfaces          */
+
+#ifdef KEEP_UNUSED
+static int opt_i = 0;  /* show the statistics          */
+static int opt_v = 0;  /* debugging output flag        */
+
+static int addr_family = 0;    /* currently selected AF        */
+#endif                                                 /* KEEP_UNUSED */
+
+static struct interface *int_list, *int_last;
+static int skfd = -1;  /* generic raw socket desc.     */
+
+
+static int sockets_open(int family)
+{
+       struct aftype **aft;
+       int sfd = -1;
+       static int force = -1;
+
+       if (force < 0) {
+               force = 0;
+               if (get_kernel_revision() < KRELEASE(2, 1, 0))
+                       force = 1;
+               if (access("/proc/net", R_OK))
+                       force = 1;
+       }
+       for (aft = aftypes; *aft; aft++) {
+               struct aftype *af = *aft;
+               int type = SOCK_DGRAM;
+
+               if (af->af == AF_UNSPEC)
+                       continue;
+               if (family && family != af->af)
+                       continue;
+               if (af->fd != -1) {
+                       sfd = af->fd;
+                       continue;
+               }
+               /* Check some /proc file first to not stress kmod */
+               if (!family && !force && af->flag_file) {
+                       if (access(af->flag_file, R_OK))
+                               continue;
+               }
+#if HAVE_AFNETROM
+               if (af->af == AF_NETROM)
+                       type = SOCK_SEQPACKET;
+#endif
+#if HAVE_AFX25
+               if (af->af == AF_X25)
+                       type = SOCK_SEQPACKET;
+#endif
+               af->fd = socket(af->af, type, 0);
+               if (af->fd >= 0)
+                       sfd = af->fd;
+       }
+       if (sfd < 0) {
+               bb_error_msg(_("No usable address families found."));
+       }
+       return sfd;
+}
+
+/* like strcmp(), but knows about numbers */
+static int nstrcmp(const char *a, const char *b)
+{
+       const char *a_ptr = a;
+       const char *b_ptr = b;
+
+       while (*a == *b) {
+               if (*a == '\0') {
+                       return 0;
+               }
+               if (!isdigit(*a) && isdigit(*(a+1))) {
+                       a_ptr = a+1;
+                       b_ptr = b+1;
+               }
+               a++;
+               b++;
+       }
+
+       if (isdigit(*a) && isdigit(*b)) {
+               return atoi(a_ptr) > atoi(b_ptr) ? 1 : -1;
+       }
+       return *a - *b;
+}
+
+static struct interface *add_interface(char *name)
+{
+       struct interface *ife, **nextp, *new;
+
+       for (ife = int_last; ife; ife = ife->prev) {
+               int n = nstrcmp(ife->name, name);
+
+               if (n == 0)
+                       return ife;
+               if (n < 0)
+                       break;
+       }
+       new(new);
+       safe_strncpy(new->name, name, IFNAMSIZ);
+       nextp = ife ? &ife->next : &int_list;
+       new->prev = ife;
+       new->next = *nextp;
+       if (new->next)
+               new->next->prev = new;
+       else
+               int_last = new;
+       *nextp = new;
+       return new;
+}
+
+
+static int if_readconf(void)
+{
+       int numreqs = 30;
+       struct ifconf ifc;
+       struct ifreq *ifr;
+       int n, err = -1;
+       int skfd2;
+
+       /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
+          (as of 2.1.128) */
+       skfd2 = get_socket_for_af(AF_INET);
+       if (skfd2 < 0) {
+               bb_perror_msg(("warning: no inet socket available"));
+               /* Try to soldier on with whatever socket we can get hold of.  */
+               skfd2 = sockets_open(0);
+               if (skfd2 < 0)
+                       return -1;
+       }
+
+       ifc.ifc_buf = NULL;
+       for (;;) {
+               ifc.ifc_len = sizeof(struct ifreq) * numreqs;
+               ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
+
+               if (ioctl(skfd2, SIOCGIFCONF, &ifc) < 0) {
+                       perror("SIOCGIFCONF");
+                       goto out;
+               }
+               if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
+                       /* assume it overflowed and try again */
+                       numreqs += 10;
+                       continue;
+               }
+               break;
+       }
+
+       ifr = ifc.ifc_req;
+       for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
+               add_interface(ifr->ifr_name);
+               ifr++;
+       }
+       err = 0;
+
+  out:
+       free(ifc.ifc_buf);
+       return err;
+}
+
+char *get_name(char *name, char *p)
+{
+       /* Extract <name>[:<alias>] from nul-terminated p where p matches
+          <name>[:<alias>]: after leading whitespace.
+          If match is not made, set name empty and return unchanged p */
+       int namestart=0, nameend=0, aliasend;
+       while (isspace(p[namestart]))
+               namestart++;
+       nameend=namestart;
+       while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
+               nameend++;
+       if (p[nameend]==':') {
+               aliasend=nameend+1;
+               while (p[aliasend] && isdigit(p[aliasend]))
+                       aliasend++;
+               if (p[aliasend]==':') {
+                       nameend=aliasend;
+               }
+               if ((nameend-namestart)<IFNAMSIZ) {
+                       memcpy(name,&p[namestart],nameend-namestart);
+                       name[nameend-namestart]='\0';
+                       p=&p[nameend];
+               } else {
+                       /* Interface name too large */
+                       name[0]='\0';
+               }
+       } else {
+               /* first ':' not found - return empty */
+               name[0]='\0';
+       }
+       return p + 1;
+}
+
+/* If scanf supports size qualifiers for %n conversions, then we can
+ * use a modified fmt that simply stores the position in the fields
+ * having no associated fields in the proc string.  Of course, we need
+ * to zero them again when we're done.  But that is smaller than the
+ * old approach of multiple scanf occurrences with large numbers of
+ * args. */
+
+/* static const char *ss_fmt[] = { */
+/*     "%Ln%Lu%lu%lu%lu%lu%ln%ln%Ln%Lu%lu%lu%lu%lu%lu", */
+/*     "%Lu%Lu%lu%lu%lu%lu%ln%ln%Lu%Lu%lu%lu%lu%lu%lu", */
+/*     "%Lu%Lu%lu%lu%lu%lu%lu%lu%Lu%Lu%lu%lu%lu%lu%lu%lu" */
+/* }; */
+
+       /* Lie about the size of the int pointed to for %n. */
+#if INT_MAX == LONG_MAX
+static const char *ss_fmt[] = {
+       "%n%Lu%u%u%u%u%n%n%n%Lu%u%u%u%u%u",
+       "%Lu%Lu%u%u%u%u%n%n%Lu%Lu%u%u%u%u%u",
+       "%Lu%Lu%u%u%u%u%u%u%Lu%Lu%u%u%u%u%u%u"
+};
+#else
+static const char *ss_fmt[] = {
+       "%n%Lu%lu%lu%lu%lu%n%n%n%Lu%lu%lu%lu%lu%lu",
+       "%Lu%Lu%lu%lu%lu%lu%n%n%Lu%Lu%lu%lu%lu%lu%lu",
+       "%Lu%Lu%lu%lu%lu%lu%lu%lu%Lu%Lu%lu%lu%lu%lu%lu%lu"
+};
+
+#endif
+
+static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
+{
+       memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
+
+       sscanf(bp, ss_fmt[procnetdev_vsn],
+                  &ife->stats.rx_bytes, /* missing for 0 */
+                  &ife->stats.rx_packets,
+                  &ife->stats.rx_errors,
+                  &ife->stats.rx_dropped,
+                  &ife->stats.rx_fifo_errors,
+                  &ife->stats.rx_frame_errors,
+                  &ife->stats.rx_compressed, /* missing for <= 1 */
+                  &ife->stats.rx_multicast, /* missing for <= 1 */
+                  &ife->stats.tx_bytes, /* missing for 0 */
+                  &ife->stats.tx_packets,
+                  &ife->stats.tx_errors,
+                  &ife->stats.tx_dropped,
+                  &ife->stats.tx_fifo_errors,
+                  &ife->stats.collisions,
+                  &ife->stats.tx_carrier_errors,
+                  &ife->stats.tx_compressed /* missing for <= 1 */
+                  );
+
+       if (procnetdev_vsn <= 1) {
+               if (procnetdev_vsn == 0) {
+                       ife->stats.rx_bytes = 0;
+                       ife->stats.tx_bytes = 0;
+               }
+               ife->stats.rx_multicast = 0;
+               ife->stats.rx_compressed = 0;
+               ife->stats.tx_compressed = 0;
+       }
+}
+
+static inline int procnetdev_version(char *buf)
+{
+       if (strstr(buf, "compressed"))
+               return 2;
+       if (strstr(buf, "bytes"))
+               return 1;
+       return 0;
+}
+
+static int if_readlist_proc(char *target)
+{
+       static int proc_read;
+       FILE *fh;
+       char buf[512];
+       struct interface *ife;
+       int err, procnetdev_vsn;
+
+       if (proc_read)
+               return 0;
+       if (!target)
+               proc_read = 1;
+
+       fh = fopen(_PATH_PROCNET_DEV, "r");
+       if (!fh) {
+               bb_perror_msg(_("Warning: cannot open %s. Limited output."), _PATH_PROCNET_DEV);
+               return if_readconf();
+       }
+       fgets(buf, sizeof buf, fh);     /* eat line */
+       fgets(buf, sizeof buf, fh);
+
+       procnetdev_vsn = procnetdev_version(buf);
+
+       err = 0;
+       while (fgets(buf, sizeof buf, fh)) {
+               char *s, name[128];
+
+               s = get_name(name, buf);
+               ife = add_interface(name);
+               get_dev_fields(s, ife, procnetdev_vsn);
+               ife->statistics_valid = 1;
+               if (target && !strcmp(target, name))
+                       break;
+       }
+       if (ferror(fh)) {
+               perror(_PATH_PROCNET_DEV);
+               err = -1;
+               proc_read = 0;
+       }
+       fclose(fh);
+       return err;
+}
+
+static int if_readlist(void)
+{
+       int err = if_readlist_proc(NULL);
+
+       if (!err)
+               err = if_readconf();
+       return err;
+}
+
+static int for_all_interfaces(int (*doit) (struct interface *, void *),
+                                                         void *cookie)
+{
+       struct interface *ife;
+
+       if (!int_list && (if_readlist() < 0))
+               return -1;
+       for (ife = int_list; ife; ife = ife->next) {
+               int err = doit(ife, cookie);
+
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+/* Support for fetching an IPX address */
+
+#if HAVE_AFIPX
+static int ipx_getaddr(int sock, int ft, struct ifreq *ifr)
+{
+       ((struct sockaddr_ipx *) &ifr->ifr_addr)->sipx_type = ft;
+       return ioctl(sock, SIOCGIFADDR, ifr);
+}
+#endif
+
+
+/* Fetch the interface configuration from the kernel. */
+static int if_fetch(struct interface *ife)
+{
+       struct ifreq ifr;
+       int fd;
+       char *ifname = ife->name;
+
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
+               return (-1);
+       ife->flags = ifr.ifr_flags;
+
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
+               memset(ife->hwaddr, 0, 32);
+       else
+               memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
+
+       ife->type = ifr.ifr_hwaddr.sa_family;
+
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0)
+               ife->metric = 0;
+       else
+               ife->metric = ifr.ifr_metric;
+
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0)
+               ife->mtu = 0;
+       else
+               ife->mtu = ifr.ifr_mtu;
+
+#ifdef HAVE_HWSLIP
+       if (ife->type == ARPHRD_SLIP || ife->type == ARPHRD_CSLIP ||
+               ife->type == ARPHRD_SLIP6 || ife->type == ARPHRD_CSLIP6 ||
+               ife->type == ARPHRD_ADAPT) {
+#ifdef SIOCGOUTFILL
+               strcpy(ifr.ifr_name, ifname);
+               if (ioctl(skfd, SIOCGOUTFILL, &ifr) < 0)
+                       ife->outfill = 0;
+               else
+                       ife->outfill = (unsigned int) ifr.ifr_data;
+#endif
+#ifdef SIOCGKEEPALIVE
+               strcpy(ifr.ifr_name, ifname);
+               if (ioctl(skfd, SIOCGKEEPALIVE, &ifr) < 0)
+                       ife->keepalive = 0;
+               else
+                       ife->keepalive = (unsigned int) ifr.ifr_data;
+#endif
+       }
+#endif
+
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0)
+               memset(&ife->map, 0, sizeof(struct ifmap));
+       else
+               memcpy(&ife->map, &ifr.ifr_map, sizeof(struct ifmap));
+
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0)
+               memset(&ife->map, 0, sizeof(struct ifmap));
+       else
+               ife->map = ifr.ifr_map;
+
+#ifdef HAVE_TXQUEUELEN
+       strcpy(ifr.ifr_name, ifname);
+       if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0)
+               ife->tx_queue_len = -1; /* unknown value */
+       else
+               ife->tx_queue_len = ifr.ifr_qlen;
+#else
+       ife->tx_queue_len = -1; /* unknown value */
+#endif
+
+#if HAVE_AFINET
+       /* IPv4 address? */
+       fd = get_socket_for_af(AF_INET);
+       if (fd >= 0) {
+               strcpy(ifr.ifr_name, ifname);
+               ifr.ifr_addr.sa_family = AF_INET;
+               if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
+                       ife->has_ip = 1;
+                       ife->addr = ifr.ifr_addr;
+                       strcpy(ifr.ifr_name, ifname);
+                       if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0)
+                               memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
+                       else
+                               ife->dstaddr = ifr.ifr_dstaddr;
+
+                       strcpy(ifr.ifr_name, ifname);
+                       if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
+                               memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
+                       else
+                               ife->broadaddr = ifr.ifr_broadaddr;
+
+                       strcpy(ifr.ifr_name, ifname);
+                       if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)
+                               memset(&ife->netmask, 0, sizeof(struct sockaddr));
+                       else
+                               ife->netmask = ifr.ifr_netmask;
+               } else
+                       memset(&ife->addr, 0, sizeof(struct sockaddr));
+       }
+#endif
+
+#if HAVE_AFATALK
+       /* DDP address maybe ? */
+       fd = get_socket_for_af(AF_APPLETALK);
+       if (fd >= 0) {
+               strcpy(ifr.ifr_name, ifname);
+               if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
+                       ife->ddpaddr = ifr.ifr_addr;
+                       ife->has_ddp = 1;
+               }
+       }
+#endif
+
+#if HAVE_AFIPX
+       /* Look for IPX addresses with all framing types */
+       fd = get_socket_for_af(AF_IPX);
+       if (fd >= 0) {
+               strcpy(ifr.ifr_name, ifname);
+               if (!ipx_getaddr(fd, IPX_FRAME_ETHERII, &ifr)) {
+                       ife->has_ipx_bb = 1;
+                       ife->ipxaddr_bb = ifr.ifr_addr;
+               }
+               strcpy(ifr.ifr_name, ifname);
+               if (!ipx_getaddr(fd, IPX_FRAME_SNAP, &ifr)) {
+                       ife->has_ipx_sn = 1;
+                       ife->ipxaddr_sn = ifr.ifr_addr;
+               }
+               strcpy(ifr.ifr_name, ifname);
+               if (!ipx_getaddr(fd, IPX_FRAME_8023, &ifr)) {
+                       ife->has_ipx_e3 = 1;
+                       ife->ipxaddr_e3 = ifr.ifr_addr;
+               }
+               strcpy(ifr.ifr_name, ifname);
+               if (!ipx_getaddr(fd, IPX_FRAME_8022, &ifr)) {
+                       ife->has_ipx_e2 = 1;
+                       ife->ipxaddr_e2 = ifr.ifr_addr;
+               }
+       }
+#endif
+
+#if HAVE_AFECONET
+       /* Econet address maybe? */
+       fd = get_socket_for_af(AF_ECONET);
+       if (fd >= 0) {
+               strcpy(ifr.ifr_name, ifname);
+               if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
+                       ife->ecaddr = ifr.ifr_addr;
+                       ife->has_econet = 1;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+
+static int do_if_fetch(struct interface *ife)
+{
+       if (if_fetch(ife) < 0) {
+               char *errmsg;
+
+               if (errno == ENODEV) {
+                       /* Give better error message for this case. */
+                       errmsg = _("Device not found");
+               } else {
+                       errmsg = strerror(errno);
+               }
+               bb_error_msg(_("%s: error fetching interface information: %s\n"),
+                               ife->name, errmsg);
+               return -1;
+       }
+       return 0;
+}
+
+/* This structure defines hardware protocols and their handlers. */
+struct hwtype {
+       const char *name;
+       const char *title;
+       int type;
+       int alen;
+       char *(*print) (unsigned char *);
+       int (*input) (char *, struct sockaddr *);
+       int (*activate) (int fd);
+       int suppress_null_addr;
+};
+
+static struct hwtype unspec_hwtype = {
+       "unspec", "UNSPEC", -1, 0,
+       UNSPEC_print, NULL, NULL
+};
+
+static struct hwtype loop_hwtype = {
+       "loop", "Local Loopback", ARPHRD_LOOPBACK, 0,
+       NULL, NULL, NULL
+};
+
+#if HAVE_HWETHER
+#include <net/if_arp.h>
+
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+/* Display an Ethernet address in readable format. */
+static char *pr_ether(unsigned char *ptr)
+{
+       static char buff[64];
+
+       snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
+                        (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
+                        (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
+               );
+       return (buff);
+}
+
+#ifdef KEEP_UNUSED
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(char *bufp, struct sockaddr *sap)
+{
+       unsigned char *ptr;
+       char c, *orig;
+       int i;
+       unsigned val;
+
+       sap->sa_family = ether_hwtype.type;
+       ptr = sap->sa_data;
+
+       i = 0;
+       orig = bufp;
+       while ((*bufp != '\0') && (i < ETH_ALEN)) {
+               val = 0;
+               c = *bufp++;
+               if (isdigit(c))
+                       val = c - '0';
+               else if (c >= 'a' && c <= 'f')
+                       val = c - 'a' + 10;
+               else if (c >= 'A' && c <= 'F')
+                       val = c - 'A' + 10;
+               else {
+#ifdef DEBUG
+                       bb_error_msg(_("in_ether(%s): invalid ether address!\n"), orig);
+#endif
+                       errno = EINVAL;
+                       return (-1);
+               }
+               val <<= 4;
+               c = *bufp;
+               if (isdigit(c))
+                       val |= c - '0';
+               else if (c >= 'a' && c <= 'f')
+                       val |= c - 'a' + 10;
+               else if (c >= 'A' && c <= 'F')
+                       val |= c - 'A' + 10;
+               else if (c == ':' || c == 0)
+                       val >>= 4;
+               else {
+#ifdef DEBUG
+                       bb_error_msg(_("in_ether(%s): invalid ether address!"), orig);
+#endif
+                       errno = EINVAL;
+                       return (-1);
+               }
+               if (c != 0)
+                       bufp++;
+               *ptr++ = (unsigned char) (val & 0377);
+               i++;
+
+               /* We might get a semicolon here - not required. */
+               if (*bufp == ':') {
+#ifdef DEBUG
+                       if (i == ETH_ALEN) {
+                               bb_error_msg(_("in_ether(%s): trailing : ignored!"), orig);
+                       }
+#endif
+                       bufp++;
+               }
+       }
+
+#ifdef DEBUG
+       /* That's it.  Any trailing junk? */
+       if ((i == ETH_ALEN) && (*bufp != '\0')) {
+               bb_error_msg(_("in_ether(%s): trailing junk!"), orig);
+               errno = EINVAL;
+               return (-1);
+       }
+       bb_error_msg("in_ether(%s): %s", orig, pr_ether(sap->sa_data));
+#endif
+
+       return (0);
+}
+#endif                                                 /* KEEP_UNUSED */
+
+
+static struct hwtype ether_hwtype = {
+       "ether", "Ethernet", ARPHRD_ETHER, ETH_ALEN,
+       pr_ether, NULL /* UNUSED in_ether */ , NULL
+};
+
+
+#endif                                                 /* HAVE_HWETHER */
+
+
+#if HAVE_HWPPP
+
+#include <net/if_arp.h>
+
+#ifdef KEEP_UNUSED
+/* Start the PPP encapsulation on the file descriptor. */
+static int do_ppp(int fd)
+{
+       bb_error_msg(_("You cannot start PPP with this program."));
+       return -1;
+}
+#endif                                                 /* KEEP_UNUSED */
+
+static struct hwtype ppp_hwtype = {
+       "ppp", "Point-Point Protocol", ARPHRD_PPP, 0,
+       NULL, NULL, NULL /* UNUSED do_ppp */ , 0
+};
+
+
+#endif                                                 /* HAVE_PPP */
+
+static struct hwtype *hwtypes[] = {
+
+       &loop_hwtype,
+
+#if HAVE_HWSLIP
+       &slip_hwtype,
+       &cslip_hwtype,
+       &slip6_hwtype,
+       &cslip6_hwtype,
+       &adaptive_hwtype,
+#endif
+#if HAVE_HWSTRIP
+       &strip_hwtype,
+#endif
+#if HAVE_HWASH
+       &ash_hwtype,
+#endif
+#if HAVE_HWETHER
+       &ether_hwtype,
+#endif
+#if HAVE_HWTR
+       &tr_hwtype,
+#ifdef ARPHRD_IEEE802_TR
+       &tr_hwtype1,
+#endif
+#endif
+#if HAVE_HWAX25
+       &ax25_hwtype,
+#endif
+#if HAVE_HWNETROM
+       &netrom_hwtype,
+#endif
+#if HAVE_HWROSE
+       &rose_hwtype,
+#endif
+#if HAVE_HWTUNNEL
+       &tunnel_hwtype,
+#endif
+#if HAVE_HWPPP
+       &ppp_hwtype,
+#endif
+#if HAVE_HWHDLCLAPB
+       &hdlc_hwtype,
+       &lapb_hwtype,
+#endif
+#if HAVE_HWARC
+       &arcnet_hwtype,
+#endif
+#if HAVE_HWFR
+       &dlci_hwtype,
+       &frad_hwtype,
+#endif
+#if HAVE_HWSIT
+       &sit_hwtype,
+#endif
+#if HAVE_HWFDDI
+       &fddi_hwtype,
+#endif
+#if HAVE_HWHIPPI
+       &hippi_hwtype,
+#endif
+#if HAVE_HWIRDA
+       &irda_hwtype,
+#endif
+#if HAVE_HWEC
+       &ec_hwtype,
+#endif
+#if HAVE_HWX25
+       &x25_hwtype,
+#endif
+       &unspec_hwtype,
+       NULL
+};
+
+#ifdef KEEP_UNUSED
+static short sVhwinit = 0;
+
+static void hwinit()
+{
+       loop_hwtype.title = _("Local Loopback");
+       unspec_hwtype.title = _("UNSPEC");
+#if HAVE_HWSLIP
+       slip_hwtype.title = _("Serial Line IP");
+       cslip_hwtype.title = _("VJ Serial Line IP");
+       slip6_hwtype.title = _("6-bit Serial Line IP");
+       cslip6_hwtype.title = _("VJ 6-bit Serial Line IP");
+       adaptive_hwtype.title = _("Adaptive Serial Line IP");
+#endif
+#if HAVE_HWETHER
+       ether_hwtype.title = _("Ethernet");
+#endif
+#if HAVE_HWASH
+       ash_hwtype.title = _("Ash");
+#endif
+#if HAVE_HWFDDI
+       fddi_hwtype.title = _("Fiber Distributed Data Interface");
+#endif
+#if HAVE_HWHIPPI
+       hippi_hwtype.title = _("HIPPI");
+#endif
+#if HAVE_HWAX25
+       ax25_hwtype.title = _("AMPR AX.25");
+#endif
+#if HAVE_HWROSE
+       rose_hwtype.title = _("AMPR ROSE");
+#endif
+#if HAVE_HWNETROM
+       netrom_hwtype.title = _("AMPR NET/ROM");
+#endif
+#if HAVE_HWX25
+       x25_hwtype.title = _("generic X.25");
+#endif
+#if HAVE_HWTUNNEL
+       tunnel_hwtype.title = _("IPIP Tunnel");
+#endif
+#if HAVE_HWPPP
+       ppp_hwtype.title = _("Point-to-Point Protocol");
+#endif
+#if HAVE_HWHDLCLAPB
+       hdlc_hwtype.title = _("(Cisco)-HDLC");
+       lapb_hwtype.title = _("LAPB");
+#endif
+#if HAVE_HWARC
+       arcnet_hwtype.title = _("ARCnet");
+#endif
+#if HAVE_HWFR
+       dlci_hwtype.title = _("Frame Relay DLCI");
+       frad_hwtype.title = _("Frame Relay Access Device");
+#endif
+#if HAVE_HWSIT
+       sit_hwtype.title = _("IPv6-in-IPv4");
+#endif
+#if HAVE_HWIRDA
+       irda_hwtype.title = _("IrLAP");
+#endif
+#if HAVE_HWTR
+       tr_hwtype.title = _("16/4 Mbps Token Ring");
+#ifdef ARPHRD_IEEE802_TR
+       tr_hwtype1.title = _("16/4 Mbps Token Ring (New)");
+#endif
+#endif
+#if HAVE_HWEC
+       ec_hwtype.title = _("Econet");
+#endif
+       sVhwinit = 1;
+}
+#endif                                                 /* KEEP_UNUSED */
+
+#ifdef IFF_PORTSEL
+#if 0
+static const char * const if_port_text[][4] = {
+       /* Keep in step with <linux/netdevice.h> */
+       {"unknown", NULL, NULL, NULL},
+       {"10base2", "bnc", "coax", NULL},
+       {"10baseT", "utp", "tpe", NULL},
+       {"AUI", "thick", "db15", NULL},
+       {"100baseT", NULL, NULL, NULL},
+       {"100baseTX", NULL, NULL, NULL},
+       {"100baseFX", NULL, NULL, NULL},
+       {NULL, NULL, NULL, NULL},
+};
+#else
+static const char * const if_port_text[] = {
+       /* Keep in step with <linux/netdevice.h> */
+       "unknown",
+       "10base2",
+       "10baseT",
+       "AUI",
+       "100baseT",
+       "100baseTX",
+       "100baseFX",
+       NULL
+};
+#endif
+#endif
+
+/* Check our hardware type table for this type. */
+static struct hwtype *get_hwntype(int type)
+{
+       struct hwtype **hwp;
+
+#ifdef KEEP_UNUSED
+       if (!sVhwinit)
+               hwinit();
+#endif                                                 /* KEEP_UNUSED */
+
+       hwp = hwtypes;
+       while (*hwp != NULL) {
+               if ((*hwp)->type == type)
+                       return (*hwp);
+               hwp++;
+       }
+       return (NULL);
+}
+
+/* return 1 if address is all zeros */
+static int hw_null_address(struct hwtype *hw, void *ap)
+{
+       unsigned int i;
+       unsigned char *address = (unsigned char *) ap;
+
+       for (i = 0; i < hw->alen; i++)
+               if (address[i])
+                       return 0;
+       return 1;
+}
+
+static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
+
+static void print_bytes_scaled(unsigned long long ull, const char *end)
+{
+       unsigned long long int_part;
+       const char *ext;
+       unsigned int frac_part;
+       int i;
+
+       frac_part = 0;
+       ext = TRext;
+       int_part = ull;
+       i = 4;
+       do {
+#if 0
+               /* This does correct rounding and is a little larger.  But it
+                * uses KiB as the smallest displayed unit. */
+               if ((int_part < (1024*1024 - 51)) || !--i) {
+                       i = 0;
+                       int_part += 51;         /* 1024*.05 = 51.2 */
+                       frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
+               }
+               int_part /= 1024;
+               ext += 3;       /* KiB, MiB, GiB, TiB */
+#else
+               if (int_part >= 1024) {
+                       frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
+                       int_part /= 1024;
+                       ext += 3;       /* KiB, MiB, GiB, TiB */
+               }
+               --i;
+#endif
+       } while (i);
+
+       printf("X bytes:%Lu (%Lu.%u %sB)%s", ull, int_part, frac_part, ext, end);
+}
+
+static const char * const ife_print_flags_strs[] = {
+       "UP ",
+       "BROADCAST ",
+       "DEBUG ",
+       "LOOPBACK ",
+       "POINTOPOINT ",
+       "NOTRAILERS ",
+       "RUNNING ",
+       "NOARP ",
+       "PROMISC ",
+       "ALLMULTI ",
+       "SLAVE ",
+       "MASTER ",
+       "MULTICAST ",
+#ifdef HAVE_DYNAMIC
+       "DYNAMIC "
+#endif
+};
+
+static const unsigned short ife_print_flags_mask[] = {
+       IFF_UP,
+       IFF_BROADCAST,
+       IFF_DEBUG,
+       IFF_LOOPBACK,
+       IFF_POINTOPOINT,
+       IFF_NOTRAILERS,
+       IFF_RUNNING,
+       IFF_NOARP,
+       IFF_PROMISC,
+       IFF_ALLMULTI,
+       IFF_SLAVE,
+       IFF_MASTER,
+       IFF_MULTICAST,
+#ifdef HAVE_DYNAMIC
+       IFF_DYNAMIC
+#endif
+       0
+};
+
+static void ife_print(struct interface *ptr)
+{
+       struct aftype *ap;
+       struct hwtype *hw;
+       int hf;
+       int can_compress = 0;
+
+#if HAVE_AFIPX
+       static struct aftype *ipxtype = NULL;
+#endif
+#if HAVE_AFECONET
+       static struct aftype *ectype = NULL;
+#endif
+#if HAVE_AFATALK
+       static struct aftype *ddptype = NULL;
+#endif
+#if HAVE_AFINET6
+       FILE *f;
+       char addr6[40], devname[20];
+       struct sockaddr_in6 sap;
+       int plen, scope, dad_status, if_idx;
+       char addr6p[8][5];
+#endif
+
+       ap = get_afntype(ptr->addr.sa_family);
+       if (ap == NULL)
+               ap = get_afntype(0);
+
+       hf = ptr->type;
+
+       if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
+               can_compress = 1;
+
+       hw = get_hwntype(hf);
+       if (hw == NULL)
+               hw = get_hwntype(-1);
+
+       printf(_("%-9.9s Link encap:%s  "), ptr->name, _(hw->title));
+       /* For some hardware types (eg Ash, ATM) we don't print the
+          hardware address if it's null.  */
+       if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
+                                                               hw->suppress_null_addr)))
+               printf(_("HWaddr %s  "), hw->print(ptr->hwaddr));
+#ifdef IFF_PORTSEL
+       if (ptr->flags & IFF_PORTSEL) {
+               printf(_("Media:%s"), if_port_text[ptr->map.port] /* [0] */);
+               if (ptr->flags & IFF_AUTOMEDIA)
+                       printf(_("(auto)"));
+       }
+#endif
+       printf("\n");
+
+#if HAVE_AFINET
+       if (ptr->has_ip) {
+               printf(_("          %s addr:%s "), ap->name,
+                          ap->sprint(&ptr->addr, 1));
+               if (ptr->flags & IFF_POINTOPOINT) {
+                       printf(_(" P-t-P:%s "), ap->sprint(&ptr->dstaddr, 1));
+               }
+               if (ptr->flags & IFF_BROADCAST) {
+                       printf(_(" Bcast:%s "), ap->sprint(&ptr->broadaddr, 1));
+               }
+               printf(_(" Mask:%s\n"), ap->sprint(&ptr->netmask, 1));
+       }
+#endif
+
+#if HAVE_AFINET6
+
+#define IPV6_ADDR_ANY           0x0000U
+
+#define IPV6_ADDR_UNICAST       0x0001U
+#define IPV6_ADDR_MULTICAST     0x0002U
+#define IPV6_ADDR_ANYCAST       0x0004U
+
+#define IPV6_ADDR_LOOPBACK      0x0010U
+#define IPV6_ADDR_LINKLOCAL     0x0020U
+#define IPV6_ADDR_SITELOCAL     0x0040U
+
+#define IPV6_ADDR_COMPATv4      0x0080U
+
+#define IPV6_ADDR_SCOPE_MASK    0x00f0U
+
+#define IPV6_ADDR_MAPPED        0x1000U
+#define IPV6_ADDR_RESERVED      0x2000U        /* reserved address space */
+
+       if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
+               while (fscanf
+                          (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
+                               addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
+                               addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
+                               &dad_status, devname) != EOF) {
+                       if (!strcmp(devname, ptr->name)) {
+                               sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+                                               addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+                                               addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+                               inet_pton(AF_INET6, addr6,
+                                                 (struct sockaddr *) &sap.sin6_addr);
+                               sap.sin6_family = AF_INET6;
+                               printf(_("          inet6 addr: %s/%d"),
+                                          inet6_aftype.sprint((struct sockaddr *) &sap, 1),
+                                          plen);
+                               printf(_(" Scope:"));
+                               switch (scope & IPV6_ADDR_SCOPE_MASK) {
+                               case 0:
+                                       printf(_("Global"));
+                                       break;
+                               case IPV6_ADDR_LINKLOCAL:
+                                       printf(_("Link"));
+                                       break;
+                               case IPV6_ADDR_SITELOCAL:
+                                       printf(_("Site"));
+                                       break;
+                               case IPV6_ADDR_COMPATv4:
+                                       printf(_("Compat"));
+                                       break;
+                               case IPV6_ADDR_LOOPBACK:
+                                       printf(_("Host"));
+                                       break;
+                               default:
+                                       printf(_("Unknown"));
+                               }
+                               printf("\n");
+                       }
+               }
+               fclose(f);
+       }
+#endif
+
+#if HAVE_AFIPX
+       if (ipxtype == NULL)
+               ipxtype = get_afntype(AF_IPX);
+
+       if (ipxtype != NULL) {
+               if (ptr->has_ipx_bb)
+                       printf(_("          IPX/Ethernet II addr:%s\n"),
+                                  ipxtype->sprint(&ptr->ipxaddr_bb, 1));
+               if (ptr->has_ipx_sn)
+                       printf(_("          IPX/Ethernet SNAP addr:%s\n"),
+                                  ipxtype->sprint(&ptr->ipxaddr_sn, 1));
+               if (ptr->has_ipx_e2)
+                       printf(_("          IPX/Ethernet 802.2 addr:%s\n"),
+                                  ipxtype->sprint(&ptr->ipxaddr_e2, 1));
+               if (ptr->has_ipx_e3)
+                       printf(_("          IPX/Ethernet 802.3 addr:%s\n"),
+                                  ipxtype->sprint(&ptr->ipxaddr_e3, 1));
+       }
+#endif
+
+#if HAVE_AFATALK
+       if (ddptype == NULL)
+               ddptype = get_afntype(AF_APPLETALK);
+       if (ddptype != NULL) {
+               if (ptr->has_ddp)
+                       printf(_("          EtherTalk Phase 2 addr:%s\n"),
+                                  ddptype->sprint(&ptr->ddpaddr, 1));
+       }
+#endif
+
+#if HAVE_AFECONET
+       if (ectype == NULL)
+               ectype = get_afntype(AF_ECONET);
+       if (ectype != NULL) {
+               if (ptr->has_econet)
+                       printf(_("          econet addr:%s\n"),
+                                  ectype->sprint(&ptr->ecaddr, 1));
+       }
+#endif
+
+       printf("          ");
+       /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
+
+       if (ptr->flags == 0) {
+               printf(_("[NO FLAGS] "));
+       } else {
+               int i = 0;
+               do {
+                       if (ptr->flags & ife_print_flags_mask[i]) {
+                               printf(_(ife_print_flags_strs[i]));
+                       }
+               } while (ife_print_flags_mask[++i]);
+       }
+
+       /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
+       printf(_(" MTU:%d  Metric:%d"), ptr->mtu, ptr->metric ? ptr->metric : 1);
+#ifdef SIOCSKEEPALIVE
+       if (ptr->outfill || ptr->keepalive)
+               printf(_("  Outfill:%d  Keepalive:%d"), ptr->outfill, ptr->keepalive);
+#endif
+       printf("\n");
+
+       /* If needed, display the interface statistics. */
+
+       if (ptr->statistics_valid) {
+               /* XXX: statistics are currently only printed for the primary address,
+                *      not for the aliases, although strictly speaking they're shared
+                *      by all addresses.
+                */
+               printf("          ");
+
+               printf(_("RX packets:%Lu errors:%lu dropped:%lu overruns:%lu frame:%lu\n"),
+                          ptr->stats.rx_packets, ptr->stats.rx_errors,
+                          ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
+                          ptr->stats.rx_frame_errors);
+               if (can_compress)
+                       printf(_("             compressed:%lu\n"),
+                                  ptr->stats.rx_compressed);
+               printf("          ");
+               printf(_("TX packets:%Lu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n"),
+                          ptr->stats.tx_packets, ptr->stats.tx_errors,
+                          ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
+                          ptr->stats.tx_carrier_errors);
+               printf(_("          collisions:%lu "), ptr->stats.collisions);
+               if (can_compress)
+                       printf(_("compressed:%lu "), ptr->stats.tx_compressed);
+               if (ptr->tx_queue_len != -1)
+                       printf(_("txqueuelen:%d "), ptr->tx_queue_len);
+               printf("\n          R");
+               print_bytes_scaled(ptr->stats.rx_bytes, "  T");
+               print_bytes_scaled(ptr->stats.tx_bytes, "\n");
+
+       }
+
+       if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
+                ptr->map.base_addr)) {
+               printf("          ");
+               if (ptr->map.irq)
+                       printf(_("Interrupt:%d "), ptr->map.irq);
+               if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
+                                                                                          I/O maps */
+                       printf(_("Base address:0x%lx "),
+                                  (unsigned long) ptr->map.base_addr);
+               if (ptr->map.mem_start) {
+                       printf(_("Memory:%lx-%lx "), ptr->map.mem_start,
+                                  ptr->map.mem_end);
+               }
+               if (ptr->map.dma)
+                       printf(_("DMA chan:%x "), ptr->map.dma);
+               printf("\n");
+       }
+       printf("\n");
+}
+
+
+static int do_if_print(struct interface *ife, void *cookie)
+{
+       int *opt_a = (int *) cookie;
+       int res;
+
+       res = do_if_fetch(ife);
+       if (res >= 0) {
+               if ((ife->flags & IFF_UP) || *opt_a)
+                       ife_print(ife);
+       }
+       return res;
+}
+
+static struct interface *lookup_interface(char *name)
+{
+       struct interface *ife = NULL;
+
+       if (if_readlist_proc(name) < 0)
+               return NULL;
+       ife = add_interface(name);
+       return ife;
+}
+
+/* for ipv4 add/del modes */
+static int if_print(char *ifname)
+{
+       int res;
+
+       if (!ifname) {
+               res = for_all_interfaces(do_if_print, &interface_opt_a);
+       } else {
+               struct interface *ife;
+
+               ife = lookup_interface(ifname);
+               res = do_if_fetch(ife);
+               if (res >= 0)
+                       ife_print(ife);
+       }
+       return res;
+}
+
+int display_interfaces(char *ifname)
+{
+       int status;
+
+       /* Create a channel to the NET kernel. */
+       if ((skfd = sockets_open(0)) < 0) {
+               bb_perror_msg_and_die("socket");
+       }
+
+       /* Do we have to show the current setup? */
+       status = if_print(ifname);
+       close(skfd);
+       exit(status < 0);
+}
diff --git a/busybox/libbb/isdirectory.c b/busybox/libbb/isdirectory.c
new file mode 100644 (file)
index 0000000..7f8f7e4
--- /dev/null
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/stat.h>
+#include "libbb.h"
+
+/*
+ * Return TRUE if a fileName is a directory.
+ * Nonexistent files return FALSE.
+ */
+int is_directory(const char *fileName, const int followLinks, struct stat *statBuf)
+{
+       int status;
+       struct stat astatBuf;
+
+       if (statBuf == NULL) {
+           /* set from auto stack buffer */
+           statBuf = &astatBuf;
+       }
+
+       if (followLinks)
+               status = stat(fileName, statBuf);
+       else
+               status = lstat(fileName, statBuf);
+
+       if (status < 0 || !(S_ISDIR(statBuf->st_mode))) {
+           status = FALSE;
+       }
+       else status = TRUE;
+
+       return status;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/kernel_version.c b/busybox/libbb/kernel_version.c
new file mode 100644 (file)
index 0000000..e01aafa
--- /dev/null
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/utsname.h>               /* for uname(2) */
+
+#include "libbb.h"
+
+/* Returns kernel version encoded as major*65536 + minor*256 + patch,
+ * so, for example,  to check if the kernel is greater than 2.2.11:
+ *     if (get_kernel_revision() <= 2*65536+2*256+11) { <stuff> }
+ */
+extern int get_kernel_revision(void)
+{
+       struct utsname name;
+       char *s;
+       int i, r;
+
+       if (uname(&name) == -1) {
+               bb_perror_msg("cannot get system information");
+               return (0);
+       }
+
+       s = name.release;
+       r = 0;
+       for (i=0 ; i<3 ; i++) {
+               r = r * 256 + atoi(strtok(s, "."));
+               s = NULL;
+       }
+       return r;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/last_char_is.c b/busybox/libbb/last_char_is.c
new file mode 100644 (file)
index 0000000..9bd7099
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * busybox library eXtended function
+ *
+ * Copyright (C) 2001 Larry Doolittle, <ldoolitt@recycle.lbl.gov>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <string.h>
+#include "libbb.h"
+
+/* Find out if the last character of a string matches the one given Don't
+ * underrun the buffer if the string length is 0.  Also avoids a possible
+ * space-hogging inline of strlen() per usage.
+ */
+char * last_char_is(const char *s, int c)
+{
+       char *sret = (char *)s;
+       if (sret) {
+               sret = strrchr(sret, c);
+               if(sret != NULL && *(sret+1) != 0)
+                       sret = NULL;
+       }
+       return sret;
+}
diff --git a/busybox/libbb/llist_add_to.c b/busybox/libbb/llist_add_to.c
new file mode 100644 (file)
index 0000000..61e53f0
--- /dev/null
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+extern llist_t *llist_add_to(llist_t *old_head, char *new_item)
+{
+       llist_t *new_head;
+
+       new_head = xmalloc(sizeof(llist_t));
+       new_head->data = new_item;
+       new_head->link = old_head;
+
+       return(new_head);
+}
diff --git a/busybox/libbb/login.c b/busybox/libbb/login.c
new file mode 100644 (file)
index 0000000..3f67a81
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * issue.c: issue printing code
+ *
+ * Copyright (C) 2003 Bastian Blank <waldi@tuxbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Optimize and correcting OCRNL by Vladimir Oleynik <dzo@simtreas.ru>
+ */
+
+#include <sys/param.h>  /* MAXHOSTNAMELEN */
+#include <stdio.h>
+#include <unistd.h>
+#include "libbb.h"
+
+#include <sys/utsname.h>
+#include <time.h>
+
+#define LOGIN " login: "
+
+static const char fmtstr_d[] = "%A, %d %B %Y";
+static const char fmtstr_t[] = "%H:%M:%S";
+
+void print_login_issue(const char *issue_file, const char *tty)
+{
+       FILE *fd;
+       int c;
+       char buf[256];
+       const char *outbuf;
+       time_t t;
+       struct utsname uts;
+
+       time(&t);
+       uname(&uts);
+
+       puts("\r");     /* start a new line */
+
+       if ((fd = fopen(issue_file, "r"))) {
+               while ((c = fgetc(fd)) != EOF) {
+                       outbuf = buf;
+                       buf[0] = c;
+                       if(c == '\n') {
+                               buf[1] = '\r';
+                               buf[2] = 0;
+                       } else {
+                               buf[1] = 0;
+                       }
+                       if (c == '\\' || c == '%') {
+                               c = fgetc(fd);
+                               switch (c) {
+                                       case 's':
+                                               outbuf = uts.sysname;
+                                               break;
+
+                                       case 'n':
+                                               outbuf = uts.nodename;
+                                               break;
+
+                                       case 'r':
+                                               outbuf = uts.release;
+                                               break;
+
+                                       case 'v':
+                                               outbuf = uts.version;
+                                               break;
+
+                                       case 'm':
+                                               outbuf = uts.machine;
+                                               break;
+
+                                       case 'D':
+                                       case 'o':
+                                               getdomainname(buf, sizeof(buf));
+                                               buf[sizeof(buf) - 1] = '\0';
+                                               break;
+
+                                       case 'd':
+                                               strftime(buf, sizeof(buf), fmtstr_d, localtime(&t));
+                                               break;
+
+                                       case 't':
+                                               strftime(buf, sizeof(buf), fmtstr_t, localtime(&t));
+                                               break;
+
+                                       case 'h':
+                                               gethostname(buf, sizeof(buf) - 1);
+                                               break;
+
+                                       case 'l':
+                                               outbuf = tty;
+                                               break;
+
+                                       default:
+                                               buf[0] = c;
+                               }
+               }
+                       fputs(outbuf, stdout);
+               }
+
+               fclose(fd);
+
+               fflush(stdout);
+       }
+}
+
+void print_login_prompt(void)
+{
+       char buf[MAXHOSTNAMELEN+1];
+
+       gethostname(buf, MAXHOSTNAMELEN);
+       fputs(buf, stdout);
+
+       fputs(LOGIN, stdout);
+       fflush(stdout);
+}
+
diff --git a/busybox/libbb/loop.c b/busybox/libbb/loop.c
new file mode 100644 (file)
index 0000000..e2287b5
--- /dev/null
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "libbb.h"
+
+/* Grumble...  The 2.6.x kernel breaks asm/posix_types.h
+ * so we get to try and cope as best we can... */
+#include <linux/version.h>
+#include <asm/posix_types.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#define __bb_kernel_dev_t   __kernel_old_dev_t
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define __bb_kernel_dev_t   __kernel_dev_t
+#else
+#define __bb_kernel_dev_t   unsigned short
+#endif
+
+/* Stuff stolen from linux/loop.h */
+#define LO_NAME_SIZE        64
+#define LO_KEY_SIZE         32
+#define LOOP_SET_FD         0x4C00
+#define LOOP_CLR_FD         0x4C01
+#define LOOP_SET_STATUS     0x4C02
+#define LOOP_GET_STATUS     0x4C03
+struct loop_info {
+       int                lo_number;
+       __bb_kernel_dev_t  lo_device;
+       unsigned long      lo_inode;
+       __bb_kernel_dev_t  lo_rdevice;
+       int                lo_offset;
+       int                lo_encrypt_type;
+       int                lo_encrypt_key_size;
+       int                lo_flags;
+       char               lo_name[LO_NAME_SIZE];
+       unsigned char      lo_encrypt_key[LO_KEY_SIZE];
+       unsigned long      lo_init[2];
+       char               reserved[4];
+};
+
+extern int del_loop(const char *device)
+{
+       int fd;
+
+       if ((fd = open(device, O_RDONLY)) < 0) {
+               bb_perror_msg("%s", device);
+               return (FALSE);
+       }
+       if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
+               close(fd);
+               bb_perror_msg("ioctl: LOOP_CLR_FD");
+               return (FALSE);
+       }
+       close(fd);
+       return (TRUE);
+}
+
+extern int set_loop(const char *device, const char *file, int offset,
+                                       int *loopro)
+{
+       struct loop_info loopinfo;
+       int fd, ffd, mode;
+
+       mode = *loopro ? O_RDONLY : O_RDWR;
+       if ((ffd = open(file, mode)) < 0 && !*loopro
+               && (errno != EROFS || (ffd = open(file, mode = O_RDONLY)) < 0)) {
+               bb_perror_msg("%s", file);
+               return 1;
+       }
+       if ((fd = open(device, mode)) < 0) {
+               close(ffd);
+               bb_perror_msg("%s", device);
+               return 1;
+       }
+       *loopro = (mode == O_RDONLY);
+
+       memset(&loopinfo, 0, sizeof(loopinfo));
+       safe_strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
+
+       loopinfo.lo_offset = offset;
+
+       loopinfo.lo_encrypt_key_size = 0;
+       if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
+               bb_perror_msg("ioctl: LOOP_SET_FD");
+               close(fd);
+               close(ffd);
+               return 1;
+       }
+       if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
+               (void) ioctl(fd, LOOP_CLR_FD, 0);
+               bb_perror_msg("ioctl: LOOP_SET_STATUS");
+               close(fd);
+               close(ffd);
+               return 1;
+       }
+       close(fd);
+       close(ffd);
+       return 0;
+}
+
+extern char *find_unused_loop_device(void)
+{
+       char dev[20];
+       int i, fd;
+       struct stat statbuf;
+       struct loop_info loopinfo;
+
+       for (i = 0; i <= 7; i++) {
+               sprintf(dev, LOOP_FORMAT, i);
+               if (stat(dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
+                       if ((fd = open(dev, O_RDONLY)) >= 0) {
+                               if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) != 0) {
+                                       if (errno == ENXIO) {   /* probably free */
+                                               close(fd);
+                                               return strdup(dev);
+                                       }
+                               }
+                               close(fd);
+                       }
+               }
+       }
+       return NULL;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/make_directory.c b/busybox/libbb/make_directory.c
new file mode 100644 (file)
index 0000000..d96acf0
--- /dev/null
@@ -0,0 +1,117 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_mode implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Mar 5, 2003    Manuel Novoa III
+ *
+ * This is the main work function for the 'mkdir' applet.  As such, it
+ * strives to be SUSv3 compliant in it's behaviour when recursively
+ * making missing parent dirs, and in it's mode setting of the final
+ * directory 'path'.
+ *
+ * To recursively build all missing intermediate directories, make
+ * sure that (flags & FILEUTILS_RECUR) is non-zero.  Newly created
+ * intermediate directories will have at least u+wx perms.
+ *
+ * To set specific permissions on 'path', pass the appropriate 'mode'
+ * val.  Otherwise, pass -1 to get default permissions.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+int bb_make_directory (char *path, long mode, int flags)
+{
+       mode_t mask;
+       const char *fail_msg;
+       char *s = path;
+       char c;
+       struct stat st;
+
+       mask = umask(0);
+       if (mode == -1) {
+               umask(mask);
+               mode = (S_IXUSR | S_IXGRP | S_IXOTH |
+                               S_IWUSR | S_IWGRP | S_IWOTH |
+                               S_IRUSR | S_IRGRP | S_IROTH) & ~mask;
+       } else {
+               umask(mask & ~0300);
+       }
+
+       do {
+               c = 0;
+
+               if (flags & FILEUTILS_RECUR) {  /* Get the parent. */
+                       /* Bypass leading non-'/'s and then subsequent '/'s. */
+                       while (*s) {
+                               if (*s == '/') {
+                                       do {
+                                               ++s;
+                                       } while (*s == '/');
+                                       c = *s;         /* Save the current char */
+                                       *s = 0;         /* and replace it with nul. */
+                                       break;
+                               }
+                               ++s;
+                       }
+               }
+
+               if (mkdir(path, 0777) < 0) {
+                       /* If we failed for any other reason than the directory
+                        * already exists, output a diagnostic and return -1.*/
+                       if (errno != EEXIST
+                               || !(flags & FILEUTILS_RECUR)
+                               || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
+                               fail_msg = "create";
+                               umask(mask);
+                               break;
+                       }
+                       /* Since the directory exists, don't attempt to change
+                        * permissions if it was the full target.  Note that
+                        * this is not an error conditon. */
+                       if (!c) {
+                               umask(mask);
+                               return 0;
+                       }
+               }
+
+               if (!c) {
+                       /* Done.  If necessary, updated perms on the newly
+                        * created directory.  Failure to update here _is_
+                        * an error.*/
+                       umask(mask);
+                       if ((mode != -1) && (chmod(path, mode) < 0)){
+                               fail_msg = "set permissions of";
+                               break;
+                       }
+                       return 0;
+               }
+
+               /* Remove any inserted nul from the path (recursive mode). */
+               *s = c;
+
+       } while (1);
+
+       bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
+       return -1;
+}
diff --git a/busybox/libbb/messages.c b/busybox/libbb/messages.c
new file mode 100644 (file)
index 0000000..671c452
--- /dev/null
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "busybox.h"
+#include "libbb.h"
+
+#ifdef L_full_version
+       const char * const bb_msg_full_version = BB_BANNER " multi-call binary";
+#endif
+#ifdef L_memory_exhausted
+       const char * const bb_msg_memory_exhausted = "memory exhausted";
+#endif
+#ifdef L_invalid_date
+       const char * const bb_msg_invalid_date = "invalid date `%s'";
+#endif
+#ifdef L_io_error
+       const char * const bb_msg_io_error = "%s: input/output error -- %m";
+#endif
+#ifdef L_write_error
+       const char * const bb_msg_write_error = "Write Error";
+#endif
+#ifdef L_name_longer_than_foo
+       const char * const bb_msg_name_longer_than_foo = "Names longer than %d chars not supported.";
+#endif
+#ifdef L_unknown
+       const char * const bb_msg_unknown = "(unknown)";
+#endif
+#ifdef L_can_not_create_raw_socket
+       const char * const bb_msg_can_not_create_raw_socket = "can`t create raw socket";
+#endif
+#ifdef L_perm_denied_are_you_root
+       const char * const bb_msg_perm_denied_are_you_root = "permission denied. (are you root?)";
+#endif
+#ifdef L_msg_standard_input
+       const char * const bb_msg_standard_input = "standard input";
+#endif
+#ifdef L_msg_standard_output
+       const char * const bb_msg_standard_output = "standard output";
+#endif
+
+#ifdef L_passwd_file
+#define PASSWD_FILE        "/etc/passwd"
+const char * const bb_path_passwd_file = PASSWD_FILE;
+#endif
+
+#ifdef L_shadow_file
+#define SHADOW_FILE        "/etc/shadow"
+const char * const bb_path_shadow_file = SHADOW_FILE;
+#endif
+
+#ifdef L_group_file
+#define GROUP_FILE         "/etc/group"
+const char * const bb_path_group_file = GROUP_FILE;
+#endif
+
+#ifdef L_gshadow_file
+#define GSHADOW_FILE       "/etc/gshadow"
+const char * const bb_path_gshadow_file = GSHADOW_FILE;
+#endif
+
+#ifdef L_nologin_file
+#define NOLOGIN_FILE       "/etc/nologin"
+const char * const bb_path_nologin_file = NOLOGIN_FILE;
+#endif
+
+#ifdef L_securetty_file
+#define SECURETTY_FILE     "/etc/securetty"
+const char * const bb_path_securetty_file = SECURETTY_FILE;
+#endif
+
+#ifdef L_motd_file
+#define MOTD_FILE          "/etc/motd"
+const char * const bb_path_motd_file = MOTD_FILE;
+#endif
+
+#ifdef L_shell_file
+const char * const bb_default_login_shell = LIBBB_DEFAULT_LOGIN_SHELL;
+#endif
+
diff --git a/busybox/libbb/mode_string.c b/busybox/libbb/mode_string.c
new file mode 100644 (file)
index 0000000..83142ba
--- /dev/null
@@ -0,0 +1,139 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mode_string implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Aug 13, 2003
+ * Fix a bug reported by junkio@cox.net involving the mode_chars index.
+ */
+
+
+#include <assert.h>
+#include <sys/stat.h>
+
+#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \
+ || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \
+ || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \
+ || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 )
+#error permission bitflag value assumption(s) violated!
+#endif
+
+#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
+ || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \
+ || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
+ || ( S_IFIFO != 0010000 )
+#warning mode type bitflag value assumption(s) violated! falling back to larger version
+
+#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777
+#undef mode_t
+#define mode_t unsigned short
+#endif
+
+static const mode_t mode_flags[] = {
+       S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID,
+       S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID,
+       S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX
+};
+
+/* The static const char arrays below are duplicated for the two cases
+ * because moving them ahead of the mode_flags declaration cause a text
+ * size increase with the gcc version I'm using. */
+
+/* The previous version used "0pcCd?bB-?l?s???".  However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux.  So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/*                                  0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char *bb_mode_string(int mode)
+{
+       static char buf[12];
+       char *p = buf;
+
+       int i, j, k;
+
+       *p = type_chars[ (mode >> 12) & 0xf ];
+       i = 0;
+       do {
+               j = k = 0;
+               do {
+                       *++p = '-';
+                       if (mode & mode_flags[i+j]) {
+                               *p = mode_chars[j];
+                               k = j;
+                       }
+               } while (++j < 3);
+               if (mode & mode_flags[i+j]) {
+                       *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)];
+               }
+               i += 4;
+       } while (i < 12);
+
+       /* Note: We don't bother with nul termination because bss initialization
+        * should have taken care of that for us.  If the user scribbled in buf
+        * memory, they deserve whatever happens.  But we'll at least assert. */
+       assert(buf[10] == 0);
+
+       return buf;
+}
+
+#else
+
+/* The previous version used "0pcCd?bB-?l?s???".  However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux.  So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/*                                  0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char *bb_mode_string(int mode)
+{
+       static char buf[12];
+       char *p = buf;
+
+       int i, j, k, m;
+
+       *p = type_chars[ (mode >> 12) & 0xf ];
+       i = 0;
+       m = 0400;
+       do {
+               j = k = 0;
+               do {
+                       *++p = '-';
+                       if (mode & m) {
+                               *p = mode_chars[j];
+                               k = j;
+                       }
+                       m >>= 1;
+               } while (++j < 3);
+               ++i;
+               if (mode & (010000 >> i)) {
+                       *p = mode_chars[3 + (k & 2) + (i == 3)];
+               }
+       } while (i < 3);
+
+       /* Note: We don't bother with nul termination because bss initialization
+        * should have taken care of that for us.  If the user scribbled in buf
+        * memory, they deserve whatever happens.  But we'll at least assert. */
+       assert(buf[10] == 0);
+
+       return buf;
+}
+
+#endif
diff --git a/busybox/libbb/module_syscalls.c b/busybox/libbb/module_syscalls.c
new file mode 100644 (file)
index 0000000..a2ff528
--- /dev/null
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * some system calls possibly missing from libc
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include "libbb.h"
+
+/* uClibc always supplies (possibly ENOSYS) versions of these functions. */
+#ifndef __UCLIBC__
+
+/* These syscalls are not included in very old glibc versions */
+int delete_module(const char *name)
+{
+#ifndef __NR_delete_module
+#warning This kernel does not support the delete_module syscall
+#warning -> The delete_module system call is being stubbed out...
+    errno=ENOSYS;
+    return -1;
+#else
+    return(syscall(__NR_delete_module, name));
+#endif
+}
+
+int get_kernel_syms(__ptr_t ks)
+{
+#ifndef __NR_get_kernel_syms
+#warning This kernel does not support the get_kernel_syms syscall
+#warning -> The get_kernel_syms system call is being stubbed out...
+    errno=ENOSYS;
+    return -1;
+#else
+    return(syscall(__NR_get_kernel_syms, ks));
+#endif
+}
+
+/* This may have 5 arguments (for old 2.0 kernels) or 2 arguments
+ * (for 2.2 and 2.4 kernels).  Use the greatest common denominator,
+ * and let the kernel cope with whatever it gets.  Its good at that. */
+int init_module(void *first, void *second, void *third, void *fourth, void *fifth)
+{
+#ifndef __NR_init_module
+#warning This kernel does not support the init_module syscall
+#warning -> The init_module system call is being stubbed out...
+    errno=ENOSYS;
+    return -1;
+#else
+    return(syscall(__NR_init_module, first, second, third, fourth, fifth));
+#endif
+}
+
+int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret)
+{
+#ifndef __NR_query_module
+#warning This kernel does not support the query_module syscall
+#warning -> The query_module system call is being stubbed out...
+    bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
+           "BusyBox with a kernel supporting the query_module system call.\n");
+    errno=ENOSYS;
+    return -1;
+#else
+    return(syscall(__NR_query_module, name, which, buf, bufsize, ret));
+#endif
+}
+
+/* Jump through hoops to fixup error return codes */
+unsigned long create_module(const char *name, size_t size)
+{
+#ifndef __NR_create_module
+#warning This kernel does not support the create_module syscall
+#warning -> The create_module system call is being stubbed out...
+    errno=ENOSYS;
+    return -1;
+#else
+    long ret = syscall(__NR_create_module, name, size);
+
+    if (ret == -1 && errno > 125) {
+       ret = -errno;
+       errno = 0;
+    }
+    return ret;
+#endif
+}
+
+
+#endif /* __UCLIBC__ */
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
+
diff --git a/busybox/libbb/mtab.c b/busybox/libbb/mtab.c
new file mode 100644 (file)
index 0000000..b1f74c4
--- /dev/null
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <mntent.h>
+#include "libbb.h"
+
+#define MTAB_MAX_ENTRIES 40
+static const int MS_RDONLY = 1;        /* Mount read-only.  */
+
+void erase_mtab(const char *name)
+{
+       struct mntent entries[MTAB_MAX_ENTRIES];
+       int count = 0;
+       FILE *mountTable = setmntent(bb_path_mtab_file, "r");
+       struct mntent *m;
+
+       /* Check if reading the mtab file failed */
+       if (mountTable == 0
+                       /* Bummer.  fall back on trying the /proc filesystem */
+                       && (mountTable = setmntent("/proc/mounts", "r")) == 0) {
+               bb_perror_msg(bb_path_mtab_file);
+               return;
+       }
+
+       while (((m = getmntent(mountTable)) != 0) && (count < MTAB_MAX_ENTRIES))
+       {
+               entries[count].mnt_fsname = strdup(m->mnt_fsname);
+               entries[count].mnt_dir = strdup(m->mnt_dir);
+               entries[count].mnt_type = strdup(m->mnt_type);
+               entries[count].mnt_opts = strdup(m->mnt_opts);
+               entries[count].mnt_freq = m->mnt_freq;
+               entries[count].mnt_passno = m->mnt_passno;
+               count++;
+       }
+       endmntent(mountTable);
+       if ((mountTable = setmntent(bb_path_mtab_file, "w"))) {
+               int i;
+
+               for (i = 0; i < count; i++) {
+                       int result = (strcmp(entries[i].mnt_fsname, name) == 0
+                                                 || strcmp(entries[i].mnt_dir, name) == 0);
+
+                       if (result)
+                               continue;
+                       else
+                               addmntent(mountTable, &entries[i]);
+               }
+               endmntent(mountTable);
+       } else if (errno != EROFS)
+               bb_perror_msg(bb_path_mtab_file);
+}
+
+void write_mtab(char *blockDevice, char *directory,
+                               char *filesystemType, long flags, char *string_flags)
+{
+       FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
+       struct mntent m;
+
+       if (mountTable == 0) {
+               bb_perror_msg(bb_path_mtab_file);
+               return;
+       }
+       if (mountTable) {
+               int length = strlen(directory);
+
+               if (length > 1 && directory[length - 1] == '/')
+                       directory[length - 1] = '\0';
+
+               if (filesystemType == 0) {
+                       struct mntent *p = find_mount_point(blockDevice, "/proc/mounts");
+
+                       if (p && p->mnt_type)
+                               filesystemType = p->mnt_type;
+               }
+               m.mnt_fsname = blockDevice;
+               m.mnt_dir = directory;
+               m.mnt_type = filesystemType ? filesystemType : "default";
+
+               if (*string_flags) {
+                       m.mnt_opts = string_flags;
+               } else {
+                       if ((flags | MS_RDONLY) == flags)
+                               m.mnt_opts = "ro";
+                       else
+                               m.mnt_opts = "rw";
+               }
+
+               m.mnt_freq = 0;
+               m.mnt_passno = 0;
+               addmntent(mountTable, &m);
+               endmntent(mountTable);
+       }
+}
diff --git a/busybox/libbb/mtab_file.c b/busybox/libbb/mtab_file.c
new file mode 100644 (file)
index 0000000..42504e8
--- /dev/null
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include "libbb.h"
+
+
+/* Busybox mount uses either /proc/mounts or /etc/mtab to
+ * get the list of currently mounted filesystems */
+#if defined CONFIG_FEATURE_MTAB_SUPPORT
+const char bb_path_mtab_file[] = CONFIG_FEATURE_MTAB_FILENAME;
+#else
+const char bb_path_mtab_file[] = "/proc/mounts";
+#endif
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/my_getgrgid.c b/busybox/libbb/my_getgrgid.c
new file mode 100644 (file)
index 0000000..9ac14a3
--- /dev/null
@@ -0,0 +1,57 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+  /* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more
+  * flexible :
+  *
+  * if bufsize is > 0 char *group cannot be set to NULL.
+  *                   On success groupname is written on static allocated buffer group
+  *                   (and a pointer to it is returned).
+  *                   On failure gid as string is written to static allocated buffer 
+  *                   group and NULL is returned.
+  * if bufsize is = 0 char *group can be set to NULL.
+  *                   On success groupname is returned. 
+  *                   On failure NULL is returned.
+  * if bufsize is < 0 char *group can be set to NULL.
+  *                   On success groupname is returned.
+  *                   On failure an error message is printed and the program exits.   
+  */
+  
+#include "libbb.h"
+#include "grp_.h"
+
+/* gets a groupname given a gid */
+char * my_getgrgid(char *group, long gid, int bufsize)
+{
+       struct group *mygroup = getgrgid(gid);
+
+       return  my_getug(group, (mygroup) ? mygroup->gr_name : (char *)mygroup, gid, bufsize, 'g');
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/my_getgrnam.c b/busybox/libbb/my_getgrnam.c
new file mode 100644 (file)
index 0000000..22a617c
--- /dev/null
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "libbb.h"
+#include "pwd_.h"
+#include "grp_.h"
+
+
+/* returns a gid given a group name */
+long my_getgrnam(const char *name)
+{
+       struct group *mygroup;
+
+       mygroup  = getgrnam(name);
+       if (mygroup==NULL)
+               bb_error_msg_and_die("unknown group name: %s", name);
+
+       return (mygroup->gr_gid);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/my_getpwnam.c b/busybox/libbb/my_getpwnam.c
new file mode 100644 (file)
index 0000000..a9fd0cd
--- /dev/null
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "libbb.h"
+#include "pwd_.h"
+#include "grp_.h"
+
+
+/* returns a uid given a username */
+long my_getpwnam(const char *name)
+{
+       struct passwd *myuser;
+
+       myuser  = getpwnam(name);
+       if (myuser==NULL)
+               bb_error_msg_and_die("unknown user name: %s", name);
+
+       return myuser->pw_uid;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/my_getpwuid.c b/busybox/libbb/my_getpwuid.c
new file mode 100644 (file)
index 0000000..7da360a
--- /dev/null
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more
+  * flexible :
+  *
+  * if bufsize is > 0 char *name can not be set to NULL.
+  *                   On success username is written on the static allocated buffer name 
+  *                   (and a pointer to it is returned).
+  *                   On failure uid as string is written to the static allocated buffer name
+  *                   and NULL is returned.
+  * if bufsize is = 0 char *name can be set to NULL.
+  *                   On success username is returned. 
+  *                   On failure NULL is returned.
+  * if bufsize is < 0 char *name can be set to NULL
+  *                   On success username is returned.
+  *                   On failure an error message is printed and the program exits.   
+  */
+  
+#include "libbb.h"
+#include "pwd_.h"
+
+/* gets a username given a uid */
+char * my_getpwuid(char *name, long uid, int bufsize)
+{
+       struct passwd *myuser = getpwuid(uid);
+
+       return  my_getug(name, (myuser) ? myuser->pw_name : (char *)myuser , uid, bufsize, 'u');
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/my_getug.c b/busybox/libbb/my_getug.c
new file mode 100644 (file)
index 0000000..9b7292d
--- /dev/null
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2004 by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /* 
+  *
+  * if bufsize is > 0 char *buffer can not be set to NULL.
+  *                   If idname is not NULL it is written on the static allocated buffer 
+  *                   (and a pointer to it is returned).
+  *                   if idname is NULL, id as string is written to the static allocated buffer
+  *                   and NULL is returned.
+  * if bufsize is = 0 char *buffer can be set to NULL.
+  *                   If idname exists a pointer to it is returned,
+  *                   else NULL is returned.
+  * if bufsize is < 0 char *buffer can be set to NULL.
+  *                   If idname exists a pointer to it is returned,
+  *                   else an error message is printed and the program exits.   
+  */
+  
+#include <stdio.h>
+#include <assert.h>
+#include "libbb.h"
+
+
+/* internal function for my_getpwuid and my_getgrgid */
+char * my_getug(char *buffer, char *idname, long id, int bufsize, char prefix)
+{
+       if(bufsize > 0 ) {
+               assert(buffer!=NULL);
+               if(idname) {
+                       return safe_strncpy(buffer, idname, bufsize);
+               }
+               snprintf(buffer, bufsize, "%ld", id);
+       } else if(bufsize < 0 && !idname) {
+               bb_error_msg_and_die("unknown %cid %ld", prefix, id); 
+       }
+       return idname;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/obscure.c b/busybox/libbb/obscure.c
new file mode 100644 (file)
index 0000000..aa15e40
--- /dev/null
@@ -0,0 +1,251 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This version of obscure.c contains modifications to support "cracklib"
+ * by Alec Muffet (alec.muffett@uk.sun.com).  You must obtain the Cracklib
+ * library source code for this function to operate.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "libbb.h"
+
+/*
+ * can't be a palindrome - like `R A D A R' or `M A D A M'
+ */
+
+static int palindrome(const char *newval)
+{
+       int i, j;
+
+       i = strlen(newval);
+
+       for (j = 0; j < i; j++)
+               if (newval[i - j - 1] != newval[j])
+                       return 0;
+
+       return 1;
+}
+
+/*
+ * more than half of the characters are different ones.
+ */
+
+static int similiar(const char *old, const char *newval)
+{
+       int i, j;
+
+       for (i = j = 0; newval[i] && old[i]; i++)
+               if (strchr(newval, old[i]))
+                       j++;
+
+       if (i >= j * 2)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * a nice mix of characters.
+ */
+
+static int simple(const char *newval)
+{
+       int digits = 0;
+       int uppers = 0;
+       int lowers = 0;
+       int others = 0;
+       int c;
+       int size;
+       int i;
+
+       for (i = 0; (c = *newval++) != 0; i++) {
+               if (isdigit(c))
+                       digits = c;
+               else if (isupper(c))
+                       uppers = c;
+               else if (islower(c))
+                       lowers = c;
+               else
+                       others = c;
+       }
+
+       /*
+        * The scam is this - a password of only one character type
+        * must be 8 letters long.  Two types, 7, and so on.
+        */
+
+       size = 9;
+       if (digits)
+               size--;
+       if (uppers)
+               size--;
+       if (lowers)
+               size--;
+       if (others)
+               size--;
+
+       if (size <= i)
+               return 0;
+
+       return 1;
+}
+
+static char *str_lower(char *string)
+{
+       char *cp;
+
+       for (cp = string; *cp; cp++)
+               *cp = tolower(*cp);
+       return string;
+}
+
+static const char *
+password_check(const char *old, const char *newval, const struct passwd *pwdp)
+{
+       const char *msg;
+       char *newmono, *wrapped;
+       int lenwrap;
+
+       if (strcmp(newval, old) == 0)
+               return "no change";
+       if (simple(newval))
+               return "too simple";
+
+       msg = NULL;
+       newmono = str_lower(bb_xstrdup(newval));
+       lenwrap = strlen(old);
+       wrapped = (char *) xmalloc(lenwrap * 2 + 1);
+       str_lower(strcpy(wrapped, old));
+
+       if (palindrome(newmono))
+               msg = "a palindrome";
+
+       else if (strcmp(wrapped, newmono) == 0)
+               msg = "case changes only";
+
+       else if (similiar(wrapped, newmono))
+               msg = "too similiar";
+
+       else {
+               safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1);
+               if (strstr(wrapped, newmono))
+                       msg = "rotated";
+       }
+
+       bzero(newmono, strlen(newmono));
+       bzero(wrapped, lenwrap * 2);
+       free(newmono);
+       free(wrapped);
+
+       return msg;
+}
+
+static const char *
+obscure_msg(const char *old, const char *newval, const struct passwd *pwdp)
+{
+       int maxlen, oldlen, newlen;
+       char *new1, *old1;
+       const char *msg;
+
+       oldlen = strlen(old);
+       newlen = strlen(newval);
+
+#if 0                                                  /* why not check the password when set for the first time?  --marekm */
+       if (old[0] == '\0')
+               /* return (1); */
+               return NULL;
+#endif
+
+       if (newlen < 5)
+               return "too short";
+
+       /*
+        * Remaining checks are optional.
+        */
+       /* Not for us -- Sean
+        *if (!getdef_bool("OBSCURE_CHECKS_ENAB"))
+        *      return NULL;
+        */
+       msg = password_check(old, newval, pwdp);
+       if (msg)
+               return msg;
+
+       /* The traditional crypt() truncates passwords to 8 chars.  It is
+          possible to circumvent the above checks by choosing an easy
+          8-char password and adding some random characters to it...
+          Example: "password$%^&*123".  So check it again, this time
+          truncated to the maximum length.  Idea from npasswd.  --marekm */
+
+       maxlen = 8;
+       if (oldlen <= maxlen && newlen <= maxlen)
+               return NULL;
+
+       new1 = (char *) bb_xstrdup(newval);
+       old1 = (char *) bb_xstrdup(old);
+       if (newlen > maxlen)
+               new1[maxlen] = '\0';
+       if (oldlen > maxlen)
+               old1[maxlen] = '\0';
+
+       msg = password_check(old1, new1, pwdp);
+
+       bzero(new1, newlen);
+       bzero(old1, oldlen);
+       free(new1);
+       free(old1);
+
+       return msg;
+}
+
+/*
+ * Obscure - see if password is obscure enough.
+ *
+ *     The programmer is encouraged to add as much complexity to this
+ *     routine as desired.  Included are some of my favorite ways to
+ *     check passwords.
+ */
+
+extern int obscure(const char *old, const char *newval, const struct passwd *pwdp)
+{
+       const char *msg = obscure_msg(old, newval, pwdp);
+
+       /*  if (msg) { */
+       if (msg != NULL) {
+               printf("Bad password: %s.\n", msg);
+               /* return 0; */
+               return 1;
+       }
+       /* return 1; */
+       return 0;
+}
diff --git a/busybox/libbb/parse_mode.c b/busybox/libbb/parse_mode.c
new file mode 100644 (file)
index 0000000..185957b
--- /dev/null
@@ -0,0 +1,177 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_mode implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+#define FILEMODEBITS    (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+
+extern int bb_parse_mode(const char *s, mode_t *current_mode)
+{
+       static const mode_t who_mask[] = {
+               S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
+               S_ISUID | S_IRWXU,              /* u */
+               S_ISGID | S_IRWXG,              /* g */
+               S_IRWXO                                 /* o */
+       };
+
+       static const mode_t perm_mask[] = {
+               S_IRUSR | S_IRGRP | S_IROTH, /* r */
+               S_IWUSR | S_IWGRP | S_IWOTH, /* w */
+               S_IXUSR | S_IXGRP | S_IXOTH, /* x */
+               S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
+               S_ISUID | S_ISGID,              /* s */
+               S_ISVTX                                 /* t */
+       };
+
+       static const char who_chars[] = "augo";
+       static const char perm_chars[] = "rwxXst";
+
+       const char *p;
+
+       mode_t wholist;
+       mode_t permlist;
+       mode_t mask;
+       mode_t new_mode;
+       char op;
+
+       assert(s);
+
+       if (((unsigned int)(*s - '0')) < 8) {
+               unsigned long tmp;
+               char *e;
+
+               tmp = strtol(s, &e, 8);
+               if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
+                       return 0;
+               }
+               *current_mode = tmp;
+               return 1;
+       }
+
+       mask = umask(0);
+       umask(mask);
+
+       new_mode = *current_mode;
+
+       /* Note: We allow empty clauses, and hence empty modes.
+        * We treat an empty mode as no change to perms. */
+
+       while (*s) {    /* Process clauses. */
+
+               if (*s == ',') {        /* We allow empty clauses. */
+                       ++s;
+                       continue;
+               }
+
+               /* Get a wholist. */
+               wholist = 0;
+
+       WHO_LIST:
+               p = who_chars;
+               do {
+                       if (*p == *s) {
+                               wholist |= who_mask[(int)(p-who_chars)];
+                               if (!*++s) {
+                                       return 0;
+                               }
+                               goto WHO_LIST;
+                       }
+               } while (*++p);
+
+               do {    /* Process action list. */
+                       if ((*s != '+') && (*s != '-')) {
+                               if (*s != '=') {
+                                       return 0;
+                               }
+                               /* Since op is '=', clear all bits corresponding to the
+                                * wholist, of all file bits if wholist is empty. */
+                               permlist = ~FILEMODEBITS;
+                               if (wholist) {
+                                       permlist = ~wholist;
+                               }
+                               new_mode &= permlist;
+                       }
+                       op = *s++;
+
+                       /* Check for permcopy. */
+                       p = who_chars + 1;      /* Skip 'a' entry. */
+                       do {
+                               if (*p == *s) {
+                                       int i = 0;
+                                       permlist = who_mask[(int)(p-who_chars)]
+                                                        & (S_IRWXU | S_IRWXG | S_IRWXO)
+                                                        & new_mode;
+                                       do {
+                                               if (permlist & perm_mask[i]) {
+                                                       permlist |= perm_mask[i];
+                                               }
+                                       } while (++i < 3);
+                                       ++s;
+                                       goto GOT_ACTION;
+                               }
+                       } while (*++p);
+
+                       /* It was not a permcopy, so get a permlist. */
+                       permlist = 0;
+
+               PERM_LIST:
+                       p = perm_chars;
+                       do {
+                               if (*p == *s) {
+                                       if ((*p != 'X')
+                                               || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
+                                       ) {
+                                               permlist |= perm_mask[(int)(p-perm_chars)];
+                                       }
+                                       if (!*++s) {
+                                               break;
+                                       }
+                                       goto PERM_LIST;
+                               }
+                       } while (*++p);
+
+               GOT_ACTION:
+                       if (permlist) { /* The permlist was nonempty. */
+                               mode_t tmp = ~mask;
+                               if (wholist) {
+                                       tmp = wholist;
+                               }
+                               permlist &= tmp;
+
+                               if (op == '-') {
+                                       new_mode &= ~permlist;
+                               } else {
+                                       new_mode |= permlist;
+                               }
+                       }
+               } while (*s && (*s != ','));
+       }
+
+       *current_mode = new_mode;
+
+       return 1;
+}
diff --git a/busybox/libbb/parse_number.c b/busybox/libbb/parse_number.c
new file mode 100644 (file)
index 0000000..5262239
--- /dev/null
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_xparse_number implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+#include "libbb.h"
+
+extern
+unsigned long bb_xparse_number(const char *numstr,
+                                                          const struct suffix_mult *suffixes)
+{
+       unsigned long int r;
+       char *e;
+       int old_errno;
+
+       /* Since this is a lib function, we're not allowed to reset errno to 0.
+        * Doing so could break an app that is deferring checking of errno.
+        * So, save the old value so that we can restore it if successful. */
+       old_errno = errno;
+       errno = 0;
+       r = strtoul(numstr, &e, 10);
+
+       if ((numstr != e) && !errno) {
+               errno = old_errno;      /* Ok.  So restore errno. */
+               if (!*e) {
+                       return r;
+               }
+               if (suffixes) {
+                       assert(suffixes->suffix);       /* No nul suffixes. */
+                       do {
+                               if (strcmp(suffixes->suffix, e) == 0) {
+                                       if (ULONG_MAX / suffixes->mult < r) {   /* Overflow! */
+                                               break;
+                                       }
+                                       return r * suffixes->mult;
+                               }
+                               ++suffixes;
+                       } while (suffixes->suffix);
+               }
+       }
+       bb_error_msg_and_die("invalid number `%s'", numstr);
+}
diff --git a/busybox/libbb/perror_msg.c b/busybox/libbb/perror_msg.c
new file mode 100644 (file)
index 0000000..8ba0531
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+extern void bb_perror_msg(const char *s, ...)
+{
+       va_list p;
+
+       va_start(p, s);
+       bb_vperror_msg(s, p);
+       va_end(p);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/perror_msg_and_die.c b/busybox/libbb/perror_msg_and_die.c
new file mode 100644 (file)
index 0000000..15bf042
--- /dev/null
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+extern void bb_perror_msg_and_die(const char *s, ...)
+{
+       va_list p;
+
+       va_start(p, s);
+       bb_vperror_msg(s, p);
+       va_end(p);
+       exit(bb_default_error_retval);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/perror_nomsg.c b/busybox/libbb/perror_nomsg.c
new file mode 100644 (file)
index 0000000..464cb86
--- /dev/null
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_perror_nomsg implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stddef.h>
+#include <libbb.h>
+
+extern void bb_perror_nomsg(void)
+{
+       /* Ignore the gcc warning about a null format string. */
+       bb_perror_msg(NULL);
+}
diff --git a/busybox/libbb/perror_nomsg_and_die.c b/busybox/libbb/perror_nomsg_and_die.c
new file mode 100644 (file)
index 0000000..bab2284
--- /dev/null
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_perror_nomsg_and_die implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stddef.h>
+#include "libbb.h"
+
+extern void bb_perror_nomsg_and_die(void)
+{
+       /* Ignore the gcc warning about a null format string. */
+       bb_perror_msg_and_die(NULL);
+}
diff --git a/busybox/libbb/print_file.c b/busybox/libbb/print_file.c
new file mode 100644 (file)
index 0000000..963db14
--- /dev/null
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libbb.h"
+
+extern void bb_xprint_and_close_file(FILE *file)
+{
+       bb_xfflush_stdout();
+       /* Note: Do not use STDOUT_FILENO here, as this is a lib routine
+        *       and the calling code may have reassigned stdout. */
+       if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1) {
+               /* bb_copyfd outputs any needed messages, so just die. */
+               exit(bb_default_error_retval);
+       }
+       /* Note: Since we're reading, don't bother checking the return value
+        *       of fclose().  The only possible failure is EINTR which
+        *       should already have been taken care of. */
+       fclose(file);
+}
+
+/* Returns:
+ *    0      if successful
+ *   -1      if 'filename' does not exist or is a directory
+ *  exits with default error code if an error occurs
+ */
+
+extern int bb_xprint_file_by_name(const char *filename)
+{
+       FILE *f;
+
+#if 0
+       /* This check shouldn't be necessary for linux, but is left
+       * here disabled just in case. */
+       struct stat statBuf;
+
+       if(is_directory(filename, TRUE, &statBuf)) {
+               bb_error_msg("%s: Is directory", filename);
+       } else
+#endif
+       if ((f = bb_wfopen(filename, "r")) != NULL) {
+               bb_xprint_and_close_file(f);
+               return 0;
+       }
+
+       return -1;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/printf.c b/busybox/libbb/printf.c
new file mode 100644 (file)
index 0000000..1156da9
--- /dev/null
@@ -0,0 +1,181 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * *printf implementations for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Mar 12, 2003     Manuel Novoa III
+ *
+ * While fwrite(), fputc(), fputs(), etc. all set the stream error flag
+ * on failure, the *printf functions are unique in that they can fail
+ * for reasons not related to the actual output itself.  Among the possible
+ * reasons for failure which don't set the streams error indicator,
+ * SUSv3 lists EILSEQ, EINVAL, and ENOMEM.
+ *
+ * In some cases, it would be desirable to have a group of *printf()
+ * functions available that _always_ set the stream error indicator on
+ * failure.  That would allow us to defer error checking until applet
+ * exit.  Unfortunately, there is no standard way of setting a streams
+ * error indicator... even though we can clear it with clearerr().
+ *
+ * Therefore, we have to resort to implementation dependent code.  Feel
+ * free to send patches for stdio implementations where the following
+ * fails.
+ *
+ * NOTE: None of this is thread safe.  As busybox is a non-threaded app,
+ *       that isn't currently an issue.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "libbb.h"
+
+#if defined(__UCLIBC__)
+
+# if defined(__FLAG_ERROR)
+/* Using my newer stdio implementation.  Unlocked macros are:
+ * #define __CLEARERR(stream) \
+         ((stream)->modeflags &= ~(__FLAG_EOF|__FLAG_ERROR), (void)0)
+ * #define __FEOF(stream)              ((stream)->modeflags & __FLAG_EOF)
+ * #define __FERROR(stream)    ((stream)->modeflags & __FLAG_ERROR)
+ */
+#  if defined(__MASK_READING)
+#   define SET_FERROR_UNLOCKED(S)    ((S)->__modeflags |= __FLAG_ERROR)
+#  else
+#   define SET_FERROR_UNLOCKED(S)    ((S)->modeflags |= __FLAG_ERROR)
+#  endif
+
+# elif defined(__MODE_ERR)
+/* Using either the original stdio implementation (from dev86) or
+ * my original stdio rewrite.  Macros were:
+ * #define ferror(fp)  (((fp)->mode&__MODE_ERR) != 0)
+ * #define feof(fp)    (((fp)->mode&__MODE_EOF) != 0)
+ * #define clearerr(fp)        ((fp)->mode &= ~(__MODE_EOF|__MODE_ERR),0)
+ */
+#define SET_FERROR_UNLOCKED(S)    ((S)->mode |= __MODE_ERR)
+
+# else
+#error unknown uClibc stdio implemenation!
+# endif
+
+#elif defined(__GLIBC__)
+
+# if defined(_STDIO_USES_IOSTREAM)
+/* Apparently using the newer libio implementation, with associated defines:
+ * #define _IO_feof_unlocked(__fp) (((__fp)->_flags & _IO_EOF_SEEN) != 0)
+ * #define _IO_ferror_unlocked(__fp) (((__fp)->_flags & _IO_ERR_SEEN) != 0)
+ */
+#define SET_FERROR_UNLOCKED(S)    ((S)->_flags |= _IO_ERR_SEEN)
+
+# else
+/* Assume the older version of glibc which used a bitfield entry
+ * as a stream error flag.  The associated defines were:
+ * #define __clearerr(stream)      ((stream)->__error = (stream)->__eof = 0)
+ * #define feof_unlocked(stream)         ((stream)->__eof != 0)
+ * #define ferror_unlocked(stream)       ((stream)->__error != 0)
+ */
+#define SET_FERROR_UNLOCKED(S)    ((S)->__error = 1)
+
+# endif
+
+#elif defined(__NEWLIB_H__)
+/* I honestly don't know if there are different versions of stdio in
+ * newlibs history.  Anyway, here's what's current.
+ * #define __sfeof(p)      (((p)->_flags & __SEOF) != 0)
+ * #define __sferror(p)    (((p)->_flags & __SERR) != 0)
+ * #define __sclearerr(p)  ((void)((p)->_flags &= ~(__SERR|__SEOF)))
+ */
+#define SET_FERROR_UNLOCKED(S)    ((S)->_flags |= __SERR)
+
+#elif defined(__dietlibc__)
+/*
+ *     WARNING!!!  dietlibc is quite buggy.  WARNING!!!
+ *
+ * Some example bugs as of March 12, 2003...
+ *   1) fputc() doesn't set the error indicator on failure.
+ *   2) freopen() doesn't maintain the same stream object, contrary to
+ *      standards.  This makes it useless in its primary role of
+ *      reassociating stdin/stdout/stderr.
+ *   3) printf() often fails to correctly format output when conversions
+ *      involve padding.  It is also practically useless for floating
+ *      point output.
+ *
+ * But, if you're determined to use it anyway, (as of the current version)
+ * you can extract the information you need from dietstdio.h.  See the
+ * other library implementations for examples.
+ */
+#error dietlibc is currently not supported.  Please see the commented source.
+
+#else /* some other lib */
+/* Please see the comments for the above supported libraries for examples
+ * of what is required to support your stdio implementation.
+ */
+#error Your stdio library is currently not supported.  Please see the commented source.
+#endif
+
+#ifdef L_bb_vfprintf
+extern int bb_vfprintf(FILE * __restrict stream,
+                                          const char * __restrict format,
+                                          va_list arg)
+{
+       int rv;
+
+       if ((rv = vfprintf(stream, format, arg)) < 0) {
+               SET_FERROR_UNLOCKED(stream);
+       }
+
+       return rv;
+}
+#endif
+
+#ifdef L_bb_vprintf
+extern int bb_vprintf(const char * __restrict format, va_list arg)
+{
+       return bb_vfprintf(stdout, format, arg);
+}
+#endif
+
+#ifdef L_bb_fprintf
+extern int bb_fprintf(FILE * __restrict stream,
+                                         const char * __restrict format, ...)
+{
+       va_list arg;
+       int rv;
+
+       va_start(arg, format);
+       rv = bb_vfprintf(stream, format, arg);
+       va_end(arg);
+
+       return rv;
+}
+#endif
+
+#ifdef L_bb_printf
+extern int bb_printf(const char * __restrict format, ...)
+{
+       va_list arg;
+       int rv;
+
+       va_start(arg, format);
+       rv = bb_vfprintf(stdout, format, arg);
+       va_end(arg);
+
+       return rv;
+}
+#endif
diff --git a/busybox/libbb/process_escape_sequence.c b/busybox/libbb/process_escape_sequence.c
new file mode 100644 (file)
index 0000000..28b1e36
--- /dev/null
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) Manuel Novoa III <mjn3@codepoet.org>
+ * and Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+#include "libbb.h"
+
+#define WANT_HEX_ESCAPES 1
+
+/* Usual "this only works for ascii compatible encodings" disclaimer. */
+#undef _tolower
+#define _tolower(X) ((X)|((char) 0x20))
+
+char bb_process_escape_sequence(const char **ptr)
+{
+       static const char charmap[] = {
+               'a',  'b',  'f',  'n',  'r',  't',  'v',  '\\', 0,
+               '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' };
+
+       const char *p;
+       const char *q;
+       unsigned int num_digits;
+       unsigned int r;
+       unsigned int n;
+       unsigned int d;
+       unsigned int base;
+
+       num_digits = n = 0;
+       base = 8;
+       q = *ptr;
+
+#ifdef WANT_HEX_ESCAPES
+       if (*q == 'x') {
+               ++q;
+               base = 16;
+               ++num_digits;
+       }
+#endif
+
+       do {
+               d = (unsigned int)(*q - '0');
+#ifdef WANT_HEX_ESCAPES
+               if (d >= 10) {
+                       d = ((unsigned int)(_tolower(*q) - 'a')) + 10;
+               }
+#endif
+
+               if (d >= base) {
+#ifdef WANT_HEX_ESCAPES
+                       if ((base == 16) && (!--num_digits)) {
+/*                             return '\\'; */
+                               --q;
+                       }
+#endif
+                       break;
+               }
+
+               r = n * base + d;
+               if (r > UCHAR_MAX) {
+                       break;
+               }
+
+               n = r;
+               ++q;
+       } while (++num_digits < 3);
+
+       if (num_digits == 0) {  /* mnemonic escape sequence? */
+               p = charmap;
+               do {
+                       if (*p == *q) {
+                               q++;
+                               break;
+                       }
+               } while (*++p);
+               n = *(p+(sizeof(charmap)/2));
+       }
+
+       *ptr = q;
+
+       return (char) n;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/procps.c b/busybox/libbb/procps.c
new file mode 100644 (file)
index 0000000..e405fb7
--- /dev/null
@@ -0,0 +1,158 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
+ * GNU Library General Public License Version 2, or any later version
+ *
+ */
+
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <asm/page.h>
+
+#include "libbb.h"
+
+extern procps_status_t * procps_scan(int save_user_arg0
+#ifdef CONFIG_SELINUX
+       , int use_selinux , security_id_t *sid
+#endif
+       )
+{
+       static DIR *dir;
+       struct dirent *entry;
+       static procps_status_t ret_status;
+       char *name;
+       int n;
+       char status[32];
+       char buf[1024];
+       FILE *fp;
+       procps_status_t curstatus;
+       int pid;
+       long tasknice;
+       struct stat sb;
+
+       if (!dir) {
+               dir = opendir("/proc");
+               if(!dir)
+                       bb_error_msg_and_die("Can't open /proc");
+       }
+       for(;;) {
+               if((entry = readdir(dir)) == NULL) {
+                       closedir(dir);
+                       dir = 0;
+                       return 0;
+               }
+               name = entry->d_name;
+               if (!(*name >= '0' && *name <= '9'))
+                       continue;
+
+               memset(&curstatus, 0, sizeof(procps_status_t));
+               pid = atoi(name);
+               curstatus.pid = pid;
+
+               sprintf(status, "/proc/%d", pid);
+               if(stat(status, &sb))
+                       continue;
+               my_getpwuid(curstatus.user, sb.st_uid, sizeof(curstatus.user));
+
+               sprintf(status, "/proc/%d/stat", pid);
+               if((fp = fopen(status, "r")) == NULL)
+                       continue;
+#ifdef CONFIG_SELINUX
+               if(use_selinux)
+               {
+                       if(fstat_secure(fileno(fp), &sb, sid))
+                               continue;
+               }
+               else
+#endif
+               name = fgets(buf, sizeof(buf), fp);
+               fclose(fp);
+               if(name == NULL)
+                       continue;
+               name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+               if(name == 0 || name[1] != ' ')
+                       continue;
+               *name = 0;
+               sscanf(buf, "%*s (%15c", curstatus.short_cmd);
+               n = sscanf(name+2,
+               "%c %d "
+               "%*s %*s %*s %*s "     /* pgrp, session, tty, tpgid */
+               "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+               "%lu %lu "
+#else
+               "%*s %*s "
+#endif
+               "%*s %*s %*s "         /* cutime, cstime, priority */
+               "%ld "
+               "%*s %*s %*s "         /* timeout, it_real_value, start_time */
+               "%*s "                 /* vsize */
+               "%ld",
+               curstatus.state, &curstatus.ppid,
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+               &curstatus.utime, &curstatus.stime,
+#endif
+               &tasknice,
+               &curstatus.rss);
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+               if(n != 6)
+#else
+               if(n != 4)
+#endif
+                       continue;
+
+               if (curstatus.rss == 0 && curstatus.state[0] != 'Z')
+                       curstatus.state[1] = 'W';
+               else
+                       curstatus.state[1] = ' ';
+               if (tasknice < 0)
+                       curstatus.state[2] = '<';
+               else if (tasknice > 0)
+                       curstatus.state[2] = 'N';
+               else
+                       curstatus.state[2] = ' ';
+
+#ifdef PAGE_SHIFT
+               curstatus.rss <<= (PAGE_SHIFT - 10);     /* 2**10 = 1kb */
+#else
+               curstatus.rss *= (getpagesize() >> 10);     /* 2**10 = 1kb */
+#endif
+
+               if(save_user_arg0) {
+                       sprintf(status, "/proc/%d/cmdline", pid);
+                       if((fp = fopen(status, "r")) == NULL)
+                               continue;
+                       if((n=fread(buf, 1, sizeof(buf)-1, fp)) > 0) {
+                               if(buf[n-1]=='\n')
+                                       buf[--n] = 0;
+                               name = buf;
+                               while(n) {
+                                       if(((unsigned char)*name) < ' ')
+                                               *name = ' ';
+                                       name++;
+                                       n--;
+                               }
+                               *name = 0;
+                               if(buf[0])
+                                       curstatus.cmd = strdup(buf);
+                               /* if NULL it work true also */
+                       }
+                       fclose(fp);
+               }
+               return memcpy(&ret_status, &curstatus, sizeof(procps_status_t));
+       }
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/pw_encrypt.c b/busybox/libbb/pw_encrypt.c
new file mode 100644 (file)
index 0000000..727149d
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routine.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <string.h>
+#include <crypt.h>
+#include "libbb.h"
+
+
+extern char *pw_encrypt(const char *clear, const char *salt)
+{
+       static char cipher[128];
+       char *cp;
+
+#ifdef CONFIG_FEATURE_SHA1_PASSWORDS
+       if (strncmp(salt, "$2$", 3) == 0) {
+               return sha1_crypt(clear);
+       }
+#endif
+       cp = (char *) crypt(clear, salt);
+       /* if crypt (a nonstandard crypt) returns a string too large,
+          truncate it so we don't overrun buffers and hope there is
+          enough security in what's left */
+       safe_strncpy(cipher, cp, sizeof(cipher));
+       return cipher;
+}
+
diff --git a/busybox/libbb/pwd2spwd.c b/busybox/libbb/pwd2spwd.c
new file mode 100644 (file)
index 0000000..3dd625b
--- /dev/null
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <time.h>
+#include <sys/types.h>
+#include "libbb.h"
+#include "shadow_.h"
+
+/*
+ * pwd_to_spwd - create entries for new spwd structure
+ *
+ *     pwd_to_spwd() creates a new (struct spwd) containing the
+ *     information in the pointed-to (struct passwd).
+ */
+#define DAY (24L*3600L)
+#define WEEK (7*DAY)
+#define SCALE DAY
+struct spwd *pwd_to_spwd(const struct passwd *pw)
+{
+       static struct spwd sp;
+
+       /*
+        * Nice, easy parts first.  The name and passwd map directly
+        * from the old password structure to the new one.
+        */
+       sp.sp_namp = pw->pw_name;
+       sp.sp_pwdp = pw->pw_passwd;
+
+       /*
+        * Defaults used if there is no pw_age information.
+        */
+       sp.sp_min = 0;
+       sp.sp_max = (10000L * DAY) / SCALE;
+       sp.sp_lstchg = time((time_t *) 0) / SCALE;
+
+       /*
+        * These fields have no corresponding information in the password
+        * file.  They are set to uninitialized values.
+        */
+       sp.sp_warn = -1;
+       sp.sp_expire = -1;
+       sp.sp_inact = -1;
+       sp.sp_flag = -1;
+
+       return &sp;
+}
+
diff --git a/busybox/libbb/qmodule.c b/busybox/libbb/qmodule.c
new file mode 100644 (file)
index 0000000..fd14d10
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+   Copyright (C) 2002 Tim Riker <Tim@Rikers.org>
+   everyone seems to claim it someplace. ;-)
+*/
+
+#include <errno.h>
+
+#include "libbb.h"
+
+int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
+
+int my_query_module(const char *name, int which, void **buf,
+               size_t *bufsize, size_t *ret)
+{
+       int my_ret;
+
+       my_ret = query_module(name, which, *buf, *bufsize, ret);
+
+       if (my_ret == -1 && errno == ENOSPC) {
+               *buf = xrealloc(*buf, *ret);
+               *bufsize = *ret;
+
+               my_ret = query_module(name, which, *buf, *bufsize, ret);
+       }
+
+       return my_ret;
+}
+
+
diff --git a/busybox/libbb/read_package_field.c b/busybox/libbb/read_package_field.c
new file mode 100644 (file)
index 0000000..4292689
--- /dev/null
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "libbb.h"
+
+/*
+ * Gets the next package field from package_buffer, seperated into the field name
+ * and field value, it returns the int offset to the first character of the next field
+ */
+int read_package_field(const char *package_buffer, char **field_name, char **field_value)
+{
+       int offset_name_start = 0;
+       int offset_name_end = 0;
+       int offset_value_start = 0;
+       int offset_value_end = 0;
+       int offset = 0;
+       int next_offset;
+       int name_length;
+       int value_length;
+       int exit_flag = FALSE;
+
+       if (package_buffer == NULL) {
+               *field_name = NULL;
+               *field_value = NULL;
+               return(-1);
+       }
+       while (1) {
+               next_offset = offset + 1;
+               switch (package_buffer[offset]) {
+                       case('\0'):
+                               exit_flag = TRUE;
+                               break;
+                       case(':'):
+                               if (offset_name_end == 0) {
+                                       offset_name_end = offset;
+                                       offset_value_start = next_offset;
+                               }
+                               /* TODO: Name might still have trailing spaces if ':' isnt
+                                * immediately after name */
+                               break;
+                       case('\n'):
+                               /* TODO: The char next_offset may be out of bounds */
+                               if (package_buffer[next_offset] != ' ') {
+                                       exit_flag = TRUE;
+                                       break;
+                               }
+                       case('\t'):
+                       case(' '):
+                               /* increment the value start point if its a just filler */
+                               if (offset_name_start == offset) {
+                                       offset_name_start++;
+                               }
+                               if (offset_value_start == offset) {
+                                       offset_value_start++;
+                               }
+                               break;
+               }
+               if (exit_flag) {
+                       /* Check that the names are valid */
+                       offset_value_end = offset;
+                       name_length = offset_name_end - offset_name_start;
+                       value_length = offset_value_end - offset_value_start;
+                       if (name_length == 0) {
+                               break;
+                       }
+                       if ((name_length > 0) && (value_length > 0)) {
+                               break;
+                       }
+
+                       /* If not valid, start fresh with next field */
+                       exit_flag = FALSE;
+                       offset_name_start = offset + 1;
+                       offset_name_end = 0;
+                       offset_value_start = offset + 1;
+                       offset_value_end = offset + 1;
+                       offset++;
+               }
+               offset++;
+       }
+       if (name_length == 0) {
+               *field_name = NULL;
+       } else {
+               *field_name = bb_xstrndup(&package_buffer[offset_name_start], name_length);
+       }
+       if (value_length > 0) {
+               *field_value = bb_xstrndup(&package_buffer[offset_value_start], value_length);
+       } else {
+               *field_value = NULL;
+       }
+       return(next_offset);
+}
+
diff --git a/busybox/libbb/recursive_action.c b/busybox/libbb/recursive_action.c
new file mode 100644 (file)
index 0000000..d276298
--- /dev/null
@@ -0,0 +1,141 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdlib.h>    /* free() */
+#include "libbb.h"
+
+#undef DEBUG_RECURS_ACTION
+
+
+/*
+ * Walk down all the directories under the specified
+ * location, and do something (something specified
+ * by the fileAction and dirAction function pointers).
+ *
+ * Unfortunately, while nftw(3) could replace this and reduce
+ * code size a bit, nftw() wasn't supported before GNU libc 2.1,
+ * and so isn't sufficiently portable to take over since glibc2.1
+ * is so stinking huge.
+ */
+int recursive_action(const char *fileName,
+                                       int recurse, int followLinks, int depthFirst,
+                                       int (*fileAction) (const char *fileName,
+                                                                          struct stat * statbuf,
+                                                                          void* userData),
+                                       int (*dirAction) (const char *fileName,
+                                                                         struct stat * statbuf,
+                                                                         void* userData),
+                                       void* userData)
+{
+       int status;
+       struct stat statbuf;
+       struct dirent *next;
+
+       if (followLinks)
+               status = stat(fileName, &statbuf);
+       else
+               status = lstat(fileName, &statbuf);
+
+       if (status < 0) {
+#ifdef DEBUG_RECURS_ACTION
+               bb_error_msg("status=%d followLinks=%d TRUE=%d",
+                               status, followLinks, TRUE);
+#endif
+               bb_perror_msg("%s", fileName);
+               return FALSE;
+       }
+
+       if (! followLinks && (S_ISLNK(statbuf.st_mode))) {
+               if (fileAction == NULL)
+                       return TRUE;
+               else
+                       return fileAction(fileName, &statbuf, userData);
+       }
+
+       if (! recurse) {
+               if (S_ISDIR(statbuf.st_mode)) {
+                       if (dirAction != NULL)
+                               return (dirAction(fileName, &statbuf, userData));
+                       else
+                               return TRUE;
+               }
+       }
+
+       if (S_ISDIR(statbuf.st_mode)) {
+               DIR *dir;
+
+               if (dirAction != NULL && ! depthFirst) {
+                       status = dirAction(fileName, &statbuf, userData);
+                       if (! status) {
+                               bb_perror_msg("%s", fileName);
+                               return FALSE;
+                       } else if (status == SKIP)
+                               return TRUE;
+               }
+               dir = opendir(fileName);
+               if (!dir) {
+                       bb_perror_msg("%s", fileName);
+                       return FALSE;
+               }
+               status = TRUE;
+               while ((next = readdir(dir)) != NULL) {
+                       char *nextFile;
+
+                       nextFile = concat_subpath_file(fileName, next->d_name);
+                       if(nextFile == NULL)
+                               continue;
+                       if (! recursive_action(nextFile, TRUE, followLinks, depthFirst,
+                                               fileAction, dirAction, userData)) {
+                               status = FALSE;
+                       }
+                       free(nextFile);
+               }
+               closedir(dir);
+               if (dirAction != NULL && depthFirst) {
+                       if (! dirAction(fileName, &statbuf, userData)) {
+                               bb_perror_msg("%s", fileName);
+                               return FALSE;
+                       }
+               }
+               if (! status)
+                       return FALSE;
+       } else {
+               if (fileAction == NULL)
+                       return TRUE;
+               else
+                       return fileAction(fileName, &statbuf, userData);
+       }
+       return TRUE;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/remove_file.c b/busybox/libbb/remove_file.c
new file mode 100644 (file)
index 0000000..8b45c58
--- /dev/null
@@ -0,0 +1,124 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini remove_file implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "libbb.h"
+
+extern int remove_file(const char *path, int flags)
+{
+       struct stat path_stat;
+       int path_exists = 1;
+
+       if (lstat(path, &path_stat) < 0) {
+               if (errno != ENOENT) {
+                       bb_perror_msg("unable to stat `%s'", path);
+                       return -1;
+               }
+
+               path_exists = 0;
+       }
+
+       if (!path_exists) {
+               if (!(flags & FILEUTILS_FORCE)) {
+                       bb_perror_msg("cannot remove `%s'", path);
+                       return -1;
+               }
+               return 0;
+       }
+
+       if (S_ISDIR(path_stat.st_mode)) {
+               DIR *dp;
+               struct dirent *d;
+               int status = 0;
+
+               if (!(flags & FILEUTILS_RECUR)) {
+                       bb_error_msg("%s: is a directory", path);
+                       return -1;
+               }
+
+               if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 &&
+                                       isatty(0)) ||
+                               (flags & FILEUTILS_INTERACTIVE)) {
+                       fprintf(stderr, "%s: descend into directory `%s'? ", bb_applet_name,
+                                       path);
+                       if (!bb_ask_confirmation())
+                               return 0;
+               }
+
+               if ((dp = opendir(path)) == NULL) {
+                       bb_perror_msg("unable to open `%s'", path);
+                       return -1;
+               }
+
+               while ((d = readdir(dp)) != NULL) {
+                       char *new_path;
+
+                       new_path = concat_subpath_file(path, d->d_name);
+                       if(new_path == NULL)
+                               continue;
+                       if (remove_file(new_path, flags) < 0)
+                               status = -1;
+                       free(new_path);
+               }
+
+               if (closedir(dp) < 0) {
+                       bb_perror_msg("unable to close `%s'", path);
+                       return -1;
+               }
+
+               if (flags & FILEUTILS_INTERACTIVE) {
+                       fprintf(stderr, "%s: remove directory `%s'? ", bb_applet_name, path);
+                       if (!bb_ask_confirmation())
+                               return status;
+               }
+
+               if (rmdir(path) < 0) {
+                       bb_perror_msg("unable to remove `%s'", path);
+                       return -1;
+               }
+
+               return status;
+       } else {
+               if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 &&
+                                       !S_ISLNK(path_stat.st_mode) &&
+                                       isatty(0)) ||
+                               (flags & FILEUTILS_INTERACTIVE)) {
+                       fprintf(stderr, "%s: remove `%s'? ", bb_applet_name, path);
+                       if (!bb_ask_confirmation())
+                               return 0;
+               }
+
+               if (unlink(path) < 0) {
+                       bb_perror_msg("unable to remove `%s'", path);
+                       return -1;
+               }
+
+               return 0;
+       }
+}
diff --git a/busybox/libbb/restricted_shell.c b/busybox/libbb/restricted_shell.c
new file mode 100644 (file)
index 0000000..74a6414
--- /dev/null
@@ -0,0 +1,57 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "libbb.h"
+
+
+
+/* Return 1 if SHELL is a restricted shell (one not returned by
+   getusershell), else 0, meaning it is a standard shell.  */
+
+int restricted_shell ( const char *shell )
+{
+       char *line;
+
+       setusershell ( );
+       while (( line = getusershell ( ))) {
+               if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 ))
+                       break;
+       }
+       endusershell ( );
+       return line ? 0 : 1;
+}
+
diff --git a/busybox/libbb/run_parts.c b/busybox/libbb/run_parts.c
new file mode 100644 (file)
index 0000000..4c8841f
--- /dev/null
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * run command from specified directory
+ *
+ *
+ * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
+ * rewrite to vfork usage by
+ * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ */
+
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "libbb.h"
+
+/* valid_name */
+/* True or false? Is this a valid filename (upper/lower alpha, digits,
+ * underscores, and hyphens only?)
+ */
+static int valid_name(const struct dirent *d)
+{
+       char *c = d->d_name;
+
+       while (*c) {
+               if (!isalnum(*c) && (*c != '_') && (*c != '-')) {
+                       return 0;
+               }
+               ++c;
+       }
+       return 1;
+}
+
+/* test mode = 1 is the same as official run_parts
+ * test_mode = 2 means to fail silently on missing directories
+ */
+
+extern int run_parts(char **args, const unsigned char test_mode, char **env)
+{
+       struct dirent **namelist = 0;
+       struct stat st;
+       char *filename;
+       char *arg0 = args[0];
+       int entries;
+       int i;
+       int exitstatus = 0;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &i;
+       (void) &exitstatus;
+#endif
+       /* scandir() isn't POSIX, but it makes things easy. */
+       entries = scandir(arg0, &namelist, valid_name, alphasort);
+
+       if (entries == -1) {
+               if (test_mode & 2) {
+                       return(2);
+               }
+               bb_perror_msg_and_die("failed to open directory %s", arg0);
+       }
+
+       for (i = 0; i < entries; i++) {
+
+               filename = concat_path_file(arg0, namelist[i]->d_name);
+
+               if (stat(filename, &st) < 0) {
+                       bb_perror_msg_and_die("failed to stat component %s", filename);
+               }
+               if (S_ISREG(st.st_mode) && !access(filename, X_OK)) {
+                       if (test_mode) {
+                               puts(filename);
+                       } else {
+                               /* exec_errno is common vfork variable */
+                               volatile int exec_errno = 0;
+                               int result;
+                               int pid;
+
+                               if ((pid = vfork()) < 0) {
+                                       bb_perror_msg_and_die("failed to fork");
+                               } else if (!pid) {
+                                       args[0] = filename;
+                                       execve(filename, args, env);
+                                       exec_errno = errno;
+                                       _exit(1);
+                               }
+
+                               waitpid(pid, &result, 0);
+                               if(exec_errno) {
+                                       errno = exec_errno;
+                                       bb_perror_msg("failed to exec %s", filename);
+                                       exitstatus = 1;
+                               }
+                               if (WIFEXITED(result) && WEXITSTATUS(result)) {
+                                       bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result));
+                                       exitstatus = 1;
+                               } else if (WIFSIGNALED(result)) {
+                                       bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result));
+                                       exitstatus = 1;
+                               }
+                       }
+               }
+               else if (!S_ISDIR(st.st_mode)) {
+                       bb_error_msg("component %s is not an executable plain file", filename);
+                       exitstatus = 1;
+               }
+
+               free(namelist[i]);
+               free(filename);
+       }
+       free(namelist);
+
+       return(exitstatus);
+}
diff --git a/busybox/libbb/run_shell.c b/busybox/libbb/run_shell.c
new file mode 100644 (file)
index 0000000..993b4e7
--- /dev/null
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "libbb.h"
+#ifdef CONFIG_SELINUX
+#include <proc_secure.h>
+#endif
+
+/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+   If COMMAND is nonzero, pass it to the shell with the -c option.
+   If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
+   arguments.  */
+
+void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args
+#ifdef CONFIG_SELINUX
+       , security_id_t sid
+#endif
+)
+{
+       const char **args;
+       int argno = 1;
+       int additional_args_cnt = 0;
+
+       for ( args = additional_args; args && *args; args++ )
+               additional_args_cnt++;
+
+               args = (const char **) xmalloc (sizeof (char *) * ( 4  + additional_args_cnt ));
+
+       args [0] = bb_get_last_path_component ( bb_xstrdup ( shell ));
+
+       if ( loginshell ) {
+               char *args0;
+               bb_xasprintf ( &args0, "-%s", args [0] );
+               args [0] = args0;
+       }
+
+       if ( command ) {
+               args [argno++] = "-c";
+               args [argno++] = command;
+       }
+       if ( additional_args ) {
+               for ( ; *additional_args; ++additional_args )
+                       args [argno++] = *additional_args;
+       }
+       args [argno] = 0;
+#ifdef CONFIG_SELINUX
+       if(sid)
+               execve_secure(shell, (char **) args, environ, sid);
+       else
+#endif
+       execv ( shell, (char **) args );
+       bb_perror_msg_and_die ( "cannot run %s", shell );
+}
diff --git a/busybox/libbb/safe_read.c b/busybox/libbb/safe_read.c
new file mode 100644 (file)
index 0000000..92e1d8a
--- /dev/null
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include "libbb.h"
+
+
+
+ssize_t safe_read(int fd, void *buf, size_t count)
+{
+       ssize_t n;
+
+       do {
+               n = read(fd, buf, count);
+       } while (n < 0 && errno == EINTR);
+
+       return n;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/safe_strncpy.c b/busybox/libbb/safe_strncpy.c
new file mode 100644 (file)
index 0000000..2016e6b
--- /dev/null
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include "libbb.h"
+
+
+
+/* Like strncpy but make sure the resulting string is always 0 terminated. */
+extern char * safe_strncpy(char *dst, const char *src, size_t size)
+{
+       dst[size-1] = '\0';
+       return strncpy(dst, src, size-1);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/safe_strtol.c b/busybox/libbb/safe_strtol.c
new file mode 100644 (file)
index 0000000..fcbdba8
--- /dev/null
@@ -0,0 +1,92 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include "libbb.h"
+
+#ifdef L_safe_strtoi
+extern
+int safe_strtoi(char *arg, int* value)
+{
+       int error;
+       long lvalue = *value;
+       error = safe_strtol(arg, &lvalue);
+       *value = (int) lvalue;
+       return error;
+}
+#endif
+
+#ifdef L_safe_strtod
+extern
+int safe_strtod(char *arg, double* value)
+{
+       char *endptr;
+       int errno_save = errno;
+
+       assert(arg!=NULL);
+       errno = 0;
+       *value = strtod(arg, &endptr);
+       if (errno != 0 || *endptr!='\0' || endptr==arg) {
+               return 1;
+       }
+       errno = errno_save;
+       return 0;
+}
+#endif
+
+#ifdef L_safe_strtol
+extern
+int safe_strtol(char *arg, long* value)
+{
+       char *endptr;
+       int errno_save = errno;
+
+       assert(arg!=NULL);
+       errno = 0;
+       *value = strtol(arg, &endptr, 0);
+       if (errno != 0 || *endptr!='\0' || endptr==arg) {
+               return 1;
+       }
+       errno = errno_save;
+       return 0;
+}
+#endif
+
+#ifdef L_safe_strtoul
+extern
+int safe_strtoul(char *arg, unsigned long* value)
+{
+       char *endptr;
+       int errno_save = errno;
+
+       assert(arg!=NULL);
+       errno = 0;
+       *value = strtoul(arg, &endptr, 0);
+       if (errno != 0 || *endptr!='\0' || endptr==arg) {
+               return 1;
+       }
+       errno = errno_save;
+       return 0;
+}
+#endif
+
diff --git a/busybox/libbb/safe_write.c b/busybox/libbb/safe_write.c
new file mode 100644 (file)
index 0000000..201ea1c
--- /dev/null
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include "libbb.h"
+
+
+
+ssize_t safe_write(int fd, const void *buf, size_t count)
+{
+       ssize_t n;
+
+       do {
+               n = write(fd, buf, count);
+       } while (n < 0 && errno == EINTR);
+
+       return n;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/setup_environment.c b/busybox/libbb/setup_environment.c
new file mode 100644 (file)
index 0000000..aeb285a
--- /dev/null
@@ -0,0 +1,93 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "libbb.h"
+
+
+
+#define DEFAULT_LOGIN_PATH      "/bin:/usr/bin"
+#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin"
+
+static void xsetenv ( const char *key, const char *value )
+{
+           if ( setenv ( key, value, 1 ))
+                               bb_error_msg_and_die (bb_msg_memory_exhausted);
+}
+
+void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw )
+{
+       if ( loginshell ) {
+               const char *term;
+
+               /* Change the current working directory to be the home directory
+                * of the user.  It is a fatal error for this process to be unable
+                * to change to that directory.  There is no "default" home
+                * directory.
+                * Some systems default to HOME=/
+                */             
+               if ( chdir ( pw-> pw_dir )) {
+                       if ( chdir ( "/" )) {
+                               syslog ( LOG_WARNING, "unable to cd to %s' for user %s'\n", pw-> pw_dir, pw-> pw_name );
+                               bb_error_msg_and_die ( "cannot cd to home directory or /" );
+                       }
+                       fputs ( "warning: cannot change to home directory\n", stderr );
+               }
+
+               /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
+                  Unset all other environment variables.  */
+               term = getenv ("TERM");
+               clearenv ( );
+               if ( term )
+                       xsetenv ( "TERM", term );
+               xsetenv ( "HOME",    pw-> pw_dir );
+               xsetenv ( "SHELL",   shell );
+               xsetenv ( "USER",    pw-> pw_name );
+               xsetenv ( "LOGNAME", pw-> pw_name );
+               xsetenv ( "PATH",    ( pw-> pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH ));
+       }
+       else if ( changeenv ) {
+               /* Set HOME, SHELL, and if not becoming a super-user,
+                  USER and LOGNAME.  */
+               xsetenv ( "HOME",  pw-> pw_dir );
+               xsetenv ( "SHELL", shell );
+               if  ( pw-> pw_uid ) {
+                       xsetenv ( "USER",    pw-> pw_name );
+                       xsetenv ( "LOGNAME", pw-> pw_name );
+               }
+       }
+}
+
diff --git a/busybox/libbb/simplify_path.c b/busybox/libbb/simplify_path.c
new file mode 100644 (file)
index 0000000..743133c
--- /dev/null
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_simplify_path implementation for busybox
+ *
+ * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include "libbb.h"
+
+char *bb_simplify_path(const char *path)
+{
+       char *s, *start, *p;
+
+       if (path[0] == '/')
+               start = bb_xstrdup(path);
+       else {
+               s = xgetcwd(NULL);
+               start = concat_path_file(s, path);
+               free(s);
+       }
+       p = s = start;
+
+       do {
+               if (*p == '/') {
+                       if (*s == '/') {        /* skip duplicate (or initial) slash */
+                               continue;
+                       } else if (*s == '.') {
+                               if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */
+                                       continue;
+                               } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) {
+                                       ++s;
+                                       if (p > start) {
+                                               while (*--p != '/');    /* omit previous dir */
+                                       }
+                                       continue;
+                               }
+                       }
+               }
+               *++p = *s;
+       } while (*++s);
+
+       if ((p == start) || (*p != '/')) {      /* not a trailing slash */
+               ++p;                                    /* so keep last character */
+       }
+       *p = 0;
+
+       return start;
+}
diff --git a/busybox/libbb/skip_whitespace.c b/busybox/libbb/skip_whitespace.c
new file mode 100644 (file)
index 0000000..bf049a2
--- /dev/null
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * skip_whitespace implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <ctype.h>
+#include "libbb.h"
+
+extern const char *bb_skip_whitespace(const char *s)
+{
+       while (isspace(*s)) {
+               ++s;
+       }
+
+       return s;
+}
diff --git a/busybox/libbb/speed_table.c b/busybox/libbb/speed_table.c
new file mode 100644 (file)
index 0000000..b04429e
--- /dev/null
@@ -0,0 +1,130 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * compact speed_t <-> speed functions for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <termios.h>
+#include "libbb.h"
+
+struct speed_map {
+       unsigned short speed;
+       unsigned short value;
+};
+
+static const struct speed_map speeds[] = {
+       {B0, 0},
+       {B50, 50},
+       {B75, 75},
+       {B110, 110},
+       {B134, 134},
+       {B150, 150},
+       {B200, 200},
+       {B300, 300},
+       {B600, 600},
+       {B1200, 1200},
+       {B1800, 1800},
+       {B2400, 2400},
+       {B4800, 4800},
+       {B9600, 9600},
+#ifdef B19200
+       {B19200, 19200},
+#elif defined(EXTA)
+       {EXTA, 19200},
+#endif
+#ifdef B38400
+       {B38400, 38400/256 + 0x8000U},
+#elif defined(EXTB)
+       {EXTB, 38400/256 + 0x8000U},
+#endif
+#ifdef B57600
+       {B57600, 57600/256 + 0x8000U},
+#endif
+#ifdef B115200
+       {B115200, 115200/256 + 0x8000U},
+#endif
+#ifdef B230400
+       {B230400, 230400/256 + 0x8000U},
+#endif
+#ifdef B460800
+       {B460800, 460800/256 + 0x8000U},
+#endif
+};
+
+static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map));
+
+unsigned long bb_baud_to_value(speed_t speed)
+{
+       int i = 0;
+
+       do {
+               if (speed == speeds[i].speed) {
+                       if (speeds[i].value & 0x8000U) {
+                               return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256;
+                       }
+                       return speeds[i].value;
+               }
+       } while (++i < NUM_SPEEDS);
+
+       return 0;
+}
+
+speed_t bb_value_to_baud(unsigned long value)
+{
+       int i = 0;
+
+       do {
+               if (value == bb_baud_to_value(speeds[i].speed)) {
+                       return speeds[i].speed;
+               }
+       } while (++i < NUM_SPEEDS);
+
+       return (speed_t) - 1;
+}
+
+#if 0
+/* testing code */
+#include <stdio.h>
+
+int main(void)
+{
+       unsigned long v;
+       speed_t s;
+
+       for (v = 0 ; v < 500000 ; v++) {
+               s = bb_value_to_baud(v);
+               if (s == (speed_t) -1) {
+                       continue;
+               }
+               printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
+       }
+
+       printf("-------------------------------\n");
+
+       for (s = 0 ; s < 010017+1 ; s++) {
+               v = bb_baud_to_value(s);
+               if (!v) {
+                       continue;
+               }
+               printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
+       }
+
+       return 0;
+}
+#endif
diff --git a/busybox/libbb/syscalls.c b/busybox/libbb/syscalls.c
new file mode 100644 (file)
index 0000000..9e89dbd
--- /dev/null
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * some system calls possibly missing from libc
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+/* Kernel headers before 2.1.mumble need this on the Alpha to get
+   _syscall* defined.  */
+#define __LIBRARY__
+#include <sys/syscall.h>
+#include "libbb.h"
+
+int sysfs( int option, unsigned int fs_index, char * buf)
+{
+       return(syscall(__NR_sysfs, option, fs_index, buf));
+}
+
+int pivot_root(const char * new_root,const char * put_old)
+{
+#ifndef __NR_pivot_root
+#warning This kernel does not support the pivot_root syscall
+#warning -> The pivot_root system call is being stubbed out...
+    /* BusyBox was compiled against a kernel that did not support
+     *  the pivot_root system call.  To make this application work,
+     *  you will need to recompile with a kernel supporting the
+     *  pivot_root system call.
+     */
+    bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
+           "BusyBox with a kernel supporting the pivot_root system call.\n");
+    errno=ENOSYS;
+    return -1;
+#else
+    return(syscall(__NR_pivot_root, new_root, put_old));
+#endif
+}
+
+
+
+/* These syscalls are not included in ancient glibc versions */
+#if ((__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1))
+
+int bdflush(int func, int data)
+{
+    return(syscall(__NR_bdflush, func, data));
+}
+
+#ifndef __alpha__
+# define __NR_klogctl __NR_syslog
+int klogctl(int type, char *b, int len)
+{
+    return(syscall(__NR_klogctl, type, b, len));
+}
+#endif
+
+
+int umount2(const char * special_file, int flags)
+{
+#ifndef __NR_pivot_root
+#warning This kernel does not support the umount2 syscall
+#warning -> The umount2 system call is being stubbed out...
+    /* BusyBox was compiled against a kernel that did not support
+     *  the umount2 system call.  To make this application work,
+     *  you will need to recompile with a kernel supporting the
+     *  umount2 system call.
+     */
+    bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
+           "BusyBox with a kernel supporting the umount2 system call.\n");
+    errno=ENOSYS;
+    return -1;
+#else
+    return(syscall(__NR_umount2, special_file, flags));
+#endif
+}
+
+
+#endif
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/syslog_msg_with_name.c b/busybox/libbb/syslog_msg_with_name.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/busybox/libbb/trim.c b/busybox/libbb/trim.c
new file mode 100644 (file)
index 0000000..38aa282
--- /dev/null
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "libbb.h"
+
+
+void trim(char *s)
+{
+       int len = strlen(s);
+
+       /* trim trailing whitespace */
+       while ( len > 0 && isspace(s[len-1]))
+               s[--len]='\0';
+
+       /* trim leading whitespace */
+       memmove(s, &s[strspn(s, " \n\r\t\v")], len);
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/u_signal_names.c b/busybox/libbb/u_signal_names.c
new file mode 100644 (file)
index 0000000..be444a9
--- /dev/null
@@ -0,0 +1,189 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+struct signal_name {
+       const char *name;
+       int number;
+};
+
+static const struct signal_name signames[] = {
+       /* POSIX signals */
+       { "EXIT",       0 },            /* 0 */
+       { "HUP",        SIGHUP },       /* 1 */
+       { "INT",        SIGINT },       /* 2 */
+       { "QUIT",       SIGQUIT },      /* 3 */
+       { "ILL",        SIGILL },       /* 4 */
+       { "ABRT",       SIGABRT },      /* 6 */
+       { "FPE",        SIGFPE },       /* 8 */
+       { "KILL",       SIGKILL },      /* 9 */
+       { "SEGV",       SIGSEGV },      /* 11 */
+       { "PIPE",       SIGPIPE },      /* 13 */
+       { "ALRM",       SIGALRM },      /* 14 */
+       { "TERM",       SIGTERM },      /* 15 */
+       { "USR1",       SIGUSR1 },      /* 10 (arm,i386,m68k,ppc), 30 (alpha,sparc*), 16 (mips) */
+       { "USR2",       SIGUSR2 },      /* 12 (arm,i386,m68k,ppc), 31 (alpha,sparc*), 17 (mips) */
+       { "CHLD",       SIGCHLD },      /* 17 (arm,i386,m68k,ppc), 20 (alpha,sparc*), 18 (mips) */
+       { "CONT",       SIGCONT },      /* 18 (arm,i386,m68k,ppc), 19 (alpha,sparc*), 25 (mips) */
+       { "STOP",       SIGSTOP },      /* 19 (arm,i386,m68k,ppc), 17 (alpha,sparc*), 23 (mips) */
+       { "TSTP",       SIGTSTP },      /* 20 (arm,i386,m68k,ppc), 18 (alpha,sparc*), 24 (mips) */
+       { "TTIN",       SIGTTIN },      /* 21 (arm,i386,m68k,ppc,alpha,sparc*), 26 (mips) */
+       { "TTOU",       SIGTTOU },      /* 22 (arm,i386,m68k,ppc,alpha,sparc*), 27 (mips) */
+       /* Miscellaneous other signals */
+#ifdef SIGTRAP
+       { "TRAP",       SIGTRAP },      /* 5 */
+#endif
+#ifdef SIGIOT
+       { "IOT",        SIGIOT },       /* 6, same as SIGABRT */
+#endif
+#ifdef SIGEMT
+       { "EMT",        SIGEMT },       /* 7 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGBUS
+       { "BUS",        SIGBUS },       /* 7 (arm,i386,m68k,ppc), 10 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGSYS
+       { "SYS",        SIGSYS },       /* 12 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGSTKFLT
+       { "STKFLT",     SIGSTKFLT },    /* 16 (arm,i386,m68k,ppc) */
+#endif
+#ifdef SIGURG
+       { "URG",        SIGURG },       /* 23 (arm,i386,m68k,ppc), 16 (alpha,sparc*), 21 (mips) */
+#endif
+#ifdef SIGIO
+       { "IO",         SIGIO },        /* 29 (arm,i386,m68k,ppc), 23 (alpha,sparc*), 22 (mips) */
+#endif
+#ifdef SIGPOLL
+       { "POLL",       SIGPOLL },      /* same as SIGIO */
+#endif
+#ifdef SIGCLD
+       { "CLD",        SIGCLD },       /* same as SIGCHLD (mips) */
+#endif
+#ifdef SIGXCPU
+       { "XCPU",       SIGXCPU },      /* 24 (arm,i386,m68k,ppc,alpha,sparc*), 30 (mips) */
+#endif
+#ifdef SIGXFSZ
+       { "XFSZ",       SIGXFSZ },      /* 25 (arm,i386,m68k,ppc,alpha,sparc*), 31 (mips) */
+#endif
+#ifdef SIGVTALRM
+       { "VTALRM",     SIGVTALRM },    /* 26 (arm,i386,m68k,ppc,alpha,sparc*), 28 (mips) */
+#endif
+#ifdef SIGPROF
+       { "PROF",       SIGPROF },      /* 27 (arm,i386,m68k,ppc,alpha,sparc*), 29 (mips) */
+#endif
+#ifdef SIGPWR
+       { "PWR",        SIGPWR },       /* 30 (arm,i386,m68k,ppc), 29 (alpha,sparc*), 19 (mips) */
+#endif
+#ifdef SIGINFO
+       { "INFO",       SIGINFO },      /* 29 (alpha) */
+#endif
+#ifdef SIGLOST
+       { "LOST",       SIGLOST },      /* 29 (arm,i386,m68k,ppc,sparc*) */
+#endif
+#ifdef SIGWINCH
+       { "WINCH",      SIGWINCH },     /* 28 (arm,i386,m68k,ppc,alpha,sparc*), 20 (mips) */
+#endif
+#ifdef SIGUNUSED
+       { "UNUSED",     SIGUNUSED },    /* 31 (arm,i386,m68k,ppc) */
+#endif
+       {0, 0}
+};
+
+/*
+       if str_sig == NULL returned signal name [*signo],
+       if str_sig != NULL - set *signo from signal_name,
+               findings with digit number or with or without SIG-prefix name
+
+       if startnum=0 flag for support finding zero signal,
+               but str_sig="0" always found, (hmm - standart or realize?)
+       if startnum<0 returned reverse signal_number  <-> signal_name
+       if found error - returned NULL
+
+*/
+
+const char *
+u_signal_names(const char *str_sig, int *signo, int startnum)
+{
+       static char retstr[16];
+       const struct signal_name *s = signames;
+       static const char prefix[] = "SIG";
+       const char *sptr;
+
+       if(startnum)
+               s++;
+       if(str_sig==NULL) {
+               while (s->name != 0) {
+                       if(s->number == *signo)
+                               break;
+                       s++;
+               }
+       } else {
+               if (isdigit(((unsigned char)*str_sig))) {
+                       char *endp;
+                       long int sn = strtol(str_sig, &endp, 10);
+                       /* test correct and overflow */
+                       if(*endp == 0 && sn >= 0 && sn < NSIG) {
+                               *signo = (int)sn;
+                               /* test for unnamed */
+                               sptr = u_signal_names(0, signo, 0);
+                               if(sptr==NULL)
+                                       return NULL;
+                               if(sn!=0)
+                                       sptr += 3;
+                               return sptr;
+                       }
+               } else {
+                       sptr = str_sig;
+                       while (s->name != 0) {
+                               if (strcasecmp(s->name, sptr) == 0) {
+                                       *signo = s->number;
+                                       if(startnum<0) {
+                                               sprintf(retstr, "%d", *signo);
+                                               return retstr;
+                                       }
+                                       break;
+                               }
+                               if(s!=signames && sptr == str_sig &&
+                                               strncasecmp(sptr, prefix, 3) == 0) {
+                                       sptr += 3;      /* strlen(prefix) */
+                                       continue;
+                               }
+                               sptr = str_sig;
+                               s++;
+                       }
+               }
+       }
+       if(s->name==0)
+               return NULL;
+       if(s!=signames)
+               strcpy(retstr, prefix);
+        else
+               retstr[0] = 0;
+       return strcat(retstr, s->name);
+}
diff --git a/busybox/libbb/vdprintf.c b/busybox/libbb/vdprintf.c
new file mode 100644 (file)
index 0000000..53fdbd3
--- /dev/null
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include "libbb.h"
+
+
+
+#if (__GLIBC__ < 2)
+extern int vdprintf(int d, const char *format, va_list ap)
+{
+       char buf[BUF_SIZE];
+       int len;
+
+       len = vsprintf(buf, format, ap);
+       return write(d, buf, len);
+}
+#endif
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/verror_msg.c b/busybox/libbb/verror_msg.c
new file mode 100644 (file)
index 0000000..07b37e4
--- /dev/null
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+extern void bb_verror_msg(const char *s, va_list p)
+{
+       fflush(stdout);
+       fprintf(stderr, "%s: ", bb_applet_name);
+       vfprintf(stderr, s, p);
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/vfork_daemon_rexec.c b/busybox/libbb/vfork_daemon_rexec.c
new file mode 100644 (file)
index 0000000..80022b3
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Rexec program for system have fork() as vfork() with foreground option
+ *
+ * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu>
+ *
+ * daemon() portion taken from uClibc:
+ *
+ * Copyright (c) 1991, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Modified for uClibc by Erik Andersen <andersee@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <paths.h>
+#include "libbb.h"
+
+
+#if defined(__uClinux__)
+void vfork_daemon_rexec(int nochdir, int noclose,
+               int argc, char **argv, char *foreground_opt)
+{
+       int fd;
+       char **vfork_args;
+       int a = 0;
+
+       setsid();
+
+       if (!nochdir)
+               chdir("/");
+
+       if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+               dup2(fd, STDIN_FILENO);
+               dup2(fd, STDOUT_FILENO);
+               dup2(fd, STDERR_FILENO);
+               if (fd > 2)
+                       close(fd);
+       }
+
+       vfork_args = xcalloc(sizeof(char *), argc + 3);
+       vfork_args[a++] = "/bin/busybox";
+       while(*argv) {
+           vfork_args[a++] = *argv;
+           argv++;
+       }
+       vfork_args[a] = foreground_opt;
+       switch (vfork()) {
+       case 0: /* child */
+               /* Make certain we are not a session leader, or else we
+                * might reacquire a controlling terminal */
+               if (vfork())
+                       _exit(0);
+               execv(vfork_args[0], vfork_args);
+               bb_perror_msg_and_die("execv %s", vfork_args[0]);
+       case -1: /* error */
+               bb_perror_msg_and_die("vfork");
+       default: /* parent */
+               exit(0);
+       }
+}
+#endif /* uClinux */
diff --git a/busybox/libbb/vherror_msg.c b/busybox/libbb/vherror_msg.c
new file mode 100644 (file)
index 0000000..1560eb5
--- /dev/null
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#include "libbb.h"
+
+
+extern void bb_vherror_msg(const char *s, va_list p)
+{
+       if(s == 0)
+               s = "";
+       bb_verror_msg(s, p);
+       if (*s)
+               fputs(": ", stderr);
+       herror("");
+}
diff --git a/busybox/libbb/vperror_msg.c b/busybox/libbb/vperror_msg.c
new file mode 100644 (file)
index 0000000..5c44696
--- /dev/null
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+extern void bb_vperror_msg(const char *s, va_list p)
+{
+       int err=errno;
+       if(s == 0) s = "";
+       bb_verror_msg(s, p);
+       if (*s) s = ": ";
+       fprintf(stderr, "%s%s\n", s, strerror(err));
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/warn_ignoring_args.c b/busybox/libbb/warn_ignoring_args.c
new file mode 100644 (file)
index 0000000..a1fa528
--- /dev/null
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * warn_ignoring_args implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <libbb.h>
+
+extern void bb_warn_ignoring_args(int n)
+{
+       if (n) {
+               bb_perror_msg("ignoring all arguments");
+       }
+}
diff --git a/busybox/libbb/wfopen.c b/busybox/libbb/wfopen.c
new file mode 100644 (file)
index 0000000..ab77cb1
--- /dev/null
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "libbb.h"
+
+FILE *bb_wfopen(const char *path, const char *mode)
+{
+       FILE *fp;
+       if ((fp = fopen(path, mode)) == NULL) {
+               bb_perror_msg("%s", path);
+               errno = 0;
+       }
+       return fp;
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/wfopen_input.c b/busybox/libbb/wfopen_input.c
new file mode 100644 (file)
index 0000000..bff6606
--- /dev/null
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wfopen_input implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* A number of applets need to open a file for reading, where the filename
+ * is a command line arg.  Since often that arg is '-' (meaning stdin),
+ * we avoid testing everywhere by consolidating things in this routine.
+ *
+ * Note: We also consider "" to main stdin (for 'cmp' at least).
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <libbb.h>
+
+FILE *bb_wfopen_input(const char *filename)
+{
+       FILE *fp = stdin;
+
+       if ((filename != bb_msg_standard_input)
+               && filename[0] && ((filename[0] != '-') || filename[1])
+       ) {
+#if 0
+               /* This check shouldn't be necessary for linux, but is left
+                * here disabled just in case. */
+               struct stat stat_buf;
+               if (is_directory(filename, 1, &stat_buf)) {
+                       bb_error_msg("%s: Is a directory", filename);
+                       return NULL;
+               }
+#endif
+               fp = bb_wfopen(filename, "r");
+       }
+
+       return fp;
+}
diff --git a/busybox/libbb/xconnect.c b/busybox/libbb/xconnect.c
new file mode 100644 (file)
index 0000000..09a1daa
--- /dev/null
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Connect to host at port using address resolution from getaddrinfo
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "libbb.h"
+
+/* Return network byte ordered port number for a service.
+ * If "port" is a number use it as the port.
+ * If "port" is a name it is looked up in /etc/services, if it isnt found return
+ * default_port
+ */
+unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port)
+{
+       unsigned short port_nr = htons(default_port);
+       if (port) {
+               char *endptr;
+               int old_errno;
+               long port_long;
+
+               /* Since this is a lib function, we're not allowed to reset errno to 0.
+                * Doing so could break an app that is deferring checking of errno. */
+               old_errno = errno;
+               errno = 0;
+               port_long = strtol(port, &endptr, 10);
+               if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) {
+                       struct servent *tserv = getservbyname(port, protocol);
+                       if (tserv) {
+                               port_nr = tserv->s_port;
+                       }
+               } else {
+                       port_nr = htons(port_long);
+               }
+               errno = old_errno;
+       }
+       return port_nr;
+}
+
+void bb_lookup_host(struct sockaddr_in *s_in, const char *host)
+{
+       struct hostent *he;
+
+       memset(s_in, 0, sizeof(struct sockaddr_in));
+       s_in->sin_family = AF_INET;
+       he = xgethostbyname(host);
+       memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length);
+}
+
+int xconnect(struct sockaddr_in *s_addr)
+{
+       int s = socket(AF_INET, SOCK_STREAM, 0);
+       if (connect(s, (struct sockaddr_in *)s_addr, sizeof(struct sockaddr_in)) < 0)
+       {
+               bb_perror_msg_and_die("Unable to connect to remote host (%s)",
+                               inet_ntoa(s_addr->sin_addr));
+       }
+       return s;
+}
diff --git a/busybox/libbb/xfuncs.c b/busybox/libbb/xfuncs.c
new file mode 100644 (file)
index 0000000..01b2f87
--- /dev/null
@@ -0,0 +1,197 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "libbb.h"
+
+
+#ifndef DMALLOC
+#ifdef L_xmalloc
+extern void *xmalloc(size_t size)
+{
+       void *ptr = malloc(size);
+       if (ptr == NULL && size != 0)
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
+       return ptr;
+}
+#endif
+
+#ifdef L_xrealloc
+extern void *xrealloc(void *ptr, size_t size)
+{
+       ptr = realloc(ptr, size);
+       if (ptr == NULL && size != 0)
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
+       return ptr;
+}
+#endif
+
+#ifdef L_xcalloc
+extern void *xcalloc(size_t nmemb, size_t size)
+{
+       void *ptr = calloc(nmemb, size);
+       if (ptr == NULL && nmemb != 0 && size != 0)
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
+       return ptr;
+}
+#endif
+#endif /* DMALLOC */
+
+#ifdef L_xstrdup
+extern char * bb_xstrdup (const char *s) {
+       char *t;
+
+       if (s == NULL)
+               return NULL;
+
+       t = strdup (s);
+
+       if (t == NULL)
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
+
+       return t;
+}
+#endif
+
+#ifdef L_xstrndup
+extern char * bb_xstrndup (const char *s, int n) {
+       char *t;
+
+       if (s == NULL)
+               bb_error_msg_and_die("bb_xstrndup bug");
+
+       t = xmalloc(++n);
+
+       return safe_strncpy(t,s,n);
+}
+#endif
+
+#ifdef L_xfopen
+FILE *bb_xfopen(const char *path, const char *mode)
+{
+       FILE *fp;
+       if ((fp = fopen(path, mode)) == NULL)
+               bb_perror_msg_and_die("%s", path);
+       return fp;
+}
+#endif
+
+#ifdef L_xopen
+extern int bb_xopen(const char *pathname, int flags)
+{
+       int ret;
+
+       ret = open(pathname, flags, 0777);
+       if (ret == -1) {
+               bb_perror_msg_and_die("%s", pathname);
+       }
+       return ret;
+}
+#endif
+
+#ifdef L_xread
+extern ssize_t bb_xread(int fd, void *buf, size_t count)
+{
+       ssize_t size;
+
+       size = read(fd, buf, count);
+       if (size == -1) {
+               bb_perror_msg_and_die("Read error");
+       }
+       return(size);
+}
+#endif
+
+#ifdef L_xread_all
+extern void bb_xread_all(int fd, void *buf, size_t count)
+{
+       ssize_t size;
+
+       while (count) {
+               if ((size = bb_xread(fd, buf, count)) == 0) {   /* EOF */
+                       bb_error_msg_and_die("Short read");
+               }
+               count -= size;
+               buf = ((char *) buf) + size;
+       }
+       return;
+}
+#endif
+
+#ifdef L_xread_char
+extern unsigned char bb_xread_char(int fd)
+{
+       char tmp;
+
+       bb_xread_all(fd, &tmp, 1);
+
+       return(tmp);
+}
+#endif
+
+#ifdef L_xferror
+extern void bb_xferror(FILE *fp, const char *fn)
+{
+       if (ferror(fp)) {
+               bb_error_msg_and_die("%s", fn);
+       }
+}
+#endif
+
+#ifdef L_xferror_stdout
+extern void bb_xferror_stdout(void)
+{
+       bb_xferror(stdout, bb_msg_standard_output);
+}
+#endif
+
+#ifdef L_xfflush_stdout
+extern void bb_xfflush_stdout(void)
+{
+       if (fflush(stdout)) {
+               bb_perror_msg_and_die(bb_msg_standard_output);
+       }
+}
+#endif
+
+#ifdef L_strlen
+/* Stupid gcc always includes its own builtin strlen()... */
+#undef strlen
+size_t bb_strlen(const char *string)
+{
+           return(strlen(string));
+}
+#endif
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libbb/xgetcwd.c b/busybox/libbb/xgetcwd.c
new file mode 100644 (file)
index 0000000..1fcdba1
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * xgetcwd.c -- return current directory with unlimited length
+ * Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+ * Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+ *
+ * Special function for busybox written by Vladimir Oleynik <dzo@simtreas.ru>
+*/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/param.h>
+#include "libbb.h"
+
+/* Amount to increase buffer size by in each try. */
+#define PATH_INCR 32
+
+/* Return the current directory, newly allocated, arbitrarily long.
+   Return NULL and set errno on error.
+   If argument is not NULL (previous usage allocate memory), call free()
+*/
+
+char *
+xgetcwd (char *cwd)
+{
+  char *ret;
+  unsigned path_max;
+
+  path_max = (unsigned) PATH_MAX;
+  path_max += 2;                /* The getcwd docs say to do this. */
+
+  if(cwd==0)
+       cwd = xmalloc (path_max);
+
+  while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) {
+      path_max += PATH_INCR;
+      cwd = xrealloc (cwd, path_max);
+  }
+
+  if (ret == NULL) {
+      free (cwd);
+      bb_perror_msg("getcwd()");
+      return NULL;
+  }
+
+  return cwd;
+}
diff --git a/busybox/libbb/xgethostbyname.c b/busybox/libbb/xgethostbyname.c
new file mode 100644 (file)
index 0000000..6b2dff7
--- /dev/null
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini xgethostbyname implementation.
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <netdb.h>
+#include "libbb.h"
+
+
+struct hostent *xgethostbyname(const char *name)
+{
+       struct hostent *retval;
+
+       if ((retval = gethostbyname(name)) == NULL)
+               bb_herror_msg_and_die("%s", name);
+
+       return retval;
+}
diff --git a/busybox/libbb/xgethostbyname2.c b/busybox/libbb/xgethostbyname2.c
new file mode 100644 (file)
index 0000000..3a16ae4
--- /dev/null
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini xgethostbyname2 implementation.
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <netdb.h>
+#include "libbb.h"
+
+
+#ifdef CONFIG_FEATURE_IPV6
+struct hostent *xgethostbyname2(const char *name, int af)
+{
+       struct hostent *retval;
+
+       if ((retval = gethostbyname2(name, af)) == NULL)
+               bb_herror_msg_and_die("%s", name);
+
+       return retval;
+}
+#endif
diff --git a/busybox/libbb/xgetlarg.c b/busybox/libbb/xgetlarg.c
new file mode 100644 (file)
index 0000000..56fb60e
--- /dev/null
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003-2004 Erik Andersen <andersen@codepoet.org>
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "busybox.h"
+
+extern long bb_xgetlarg(const char *arg, int base, long lower, long upper)
+{
+       long result;
+       char *endptr;
+       int errno_save = errno;
+
+       assert(arg!=NULL);
+
+       /* Don't allow leading whitespace. */
+       if ((isspace)(*arg)) {  /* Use an actual funciton call for minimal size. */
+               bb_show_usage();
+       }
+
+       errno = 0;
+       result = strtol(arg, &endptr, base);
+       if (errno != 0 || *endptr!='\0' || endptr==arg || result < lower || result > upper)
+               bb_show_usage();
+       errno = errno_save;
+       return result;
+}
diff --git a/busybox/libbb/xgetularg.c b/busybox/libbb/xgetularg.c
new file mode 100644 (file)
index 0000000..e900854
--- /dev/null
@@ -0,0 +1,160 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * xgetularg* implementations for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include "libbb.h"
+
+#ifdef L_xgetularg_bnd_sfx
+extern
+unsigned long bb_xgetularg_bnd_sfx(const char *arg, int base,
+                                                                  unsigned long lower,
+                                                                  unsigned long upper,
+                                                                  const struct suffix_mult *suffixes)
+{
+       unsigned long r;
+       int old_errno;
+       char *e;
+
+       assert(arg);
+
+       /* Disallow '-' and any leading whitespace.  Speed isn't critical here
+        * since we're parsing commandline args.  So make sure we get the
+        * actual isspace function rather than a larger macro implementaion. */
+       if ((*arg == '-') || (isspace)(*arg)) {
+               bb_show_usage();
+       }
+
+       /* Since this is a lib function, we're not allowed to reset errno to 0.
+        * Doing so could break an app that is deferring checking of errno.
+        * So, save the old value so that we can restore it if successful. */
+       old_errno = errno;
+       errno = 0;
+       r = strtoul(arg, &e, base);
+       /* Do the initial validity check.  Note: The standards do not
+        * guarantee that errno is set if no digits were found.  So we
+        * must test for this explicitly. */
+       if (errno || (arg == e)) {      /* error or no digits */
+               bb_show_usage();
+       }
+       errno = old_errno;      /* Ok.  So restore errno. */
+
+       /* Do optional suffix parsing.  Allow 'empty' suffix tables.
+        * Note that we also all nul suffixes with associated multipliers,
+        * to allow for scaling of the arg by some default multiplier. */
+
+       if (suffixes) {
+               while (suffixes->suffix) {
+                       if (strcmp(suffixes->suffix, e) == 0) {
+                               if (ULONG_MAX / suffixes->mult < r) {   /* Overflow! */
+                                       bb_show_usage();
+                               }
+                               ++e;
+                               r *= suffixes->mult;
+                               break;
+                       }
+                       ++suffixes;
+               }
+       }
+
+       /* Finally, check for illegal trailing chars and range limits. */
+       /* Note: although we allow leading space (via stroul), trailing space
+        * is an error.  It would be easy enough to allow though if desired. */
+       if (*e || (r < lower) || (r > upper)) {
+               bb_show_usage();
+       }
+
+       return r;
+}
+#endif
+
+#ifdef L_xgetlarg_bnd_sfx
+extern
+long bb_xgetlarg_bnd_sfx(const char *arg, int base,
+                                                long lower,
+                                                long upper,
+                                                const struct suffix_mult *suffixes)
+{
+       unsigned long u = LONG_MAX;
+       long r;
+       const char *p = arg;
+
+       if ((*p == '-') && (p[1] != '+')) {
+               ++p;
+#if LONG_MAX == (-(LONG_MIN + 1))
+               ++u;    /* two's complement */
+#endif
+       }
+
+       r = bb_xgetularg_bnd_sfx(p, base, 0, u, suffixes);
+
+       if (*arg == '-') {
+               r = -r;
+       }
+
+       if ((r < lower) || (r > upper)) {
+               bb_show_usage();
+       }
+
+       return r;
+}
+#endif
+
+#ifdef L_getlarg10_sfx
+extern
+long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes)
+{
+       return bb_xgetlarg_bnd_sfx(arg, 10, LONG_MIN, LONG_MAX, suffixes);
+}
+#endif
+
+#ifdef L_xgetularg_bnd
+extern
+unsigned long bb_xgetularg_bnd(const char *arg, int base,
+                                                          unsigned long lower,
+                                                          unsigned long upper)
+{
+       return bb_xgetularg_bnd_sfx(arg, base, lower, upper, NULL);
+}
+#endif
+
+#ifdef L_xgetularg10_bnd
+extern
+unsigned long bb_xgetularg10_bnd(const char *arg,
+                                                                unsigned long lower,
+                                                                unsigned long upper)
+{
+       return bb_xgetularg_bnd(arg, 10, lower, upper);
+}
+#endif
+
+#ifdef L_xgetularg10
+extern
+unsigned long bb_xgetularg10(const char *arg)
+{
+       return bb_xgetularg10_bnd(arg, 0, ULONG_MAX);
+}
+#endif
diff --git a/busybox/libbb/xreadlink.c b/busybox/libbb/xreadlink.c
new file mode 100644 (file)
index 0000000..49823fa
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  xreadlink.c - safe implementation of readlink.
+ *  Returns a NULL on failure...
+ */
+
+#include <stdio.h>
+
+/*
+ * NOTE: This function returns a malloced char* that you will have to free
+ * yourself. You have been warned.
+ */
+
+#include <unistd.h>
+#include "libbb.h"
+
+extern char *xreadlink(const char *path)
+{   
+       static const int GROWBY = 80; /* how large we will grow strings by */
+
+       char *buf = NULL;
+       int bufsize = 0, readsize = 0;
+
+       do {
+               buf = xrealloc(buf, bufsize += GROWBY);
+               readsize = readlink(path, buf, bufsize); /* 1st try */
+               if (readsize == -1) {
+                       bb_perror_msg("%s", path);
+                       free(buf);
+                       return NULL;
+               }
+       }
+       while (bufsize < readsize + 1);
+
+       buf[readsize] = '\0';
+
+       return buf;
+}
diff --git a/busybox/libbb/xregcomp.c b/busybox/libbb/xregcomp.c
new file mode 100644 (file)
index 0000000..fa6c0fa
--- /dev/null
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <stdio.h>
+#include "libbb.h"
+#include <regex.h>
+
+
+
+void xregcomp(regex_t *preg, const char *regex, int cflags)
+{
+       int ret;
+       if ((ret = regcomp(preg, regex, cflags)) != 0) {
+               int errmsgsz = regerror(ret, preg, NULL, 0);
+               char *errmsg = xmalloc(errmsgsz);
+               regerror(ret, preg, errmsg, errmsgsz);
+               bb_error_msg_and_die("xregcomp: %s", errmsg);
+       }
+}
+
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/libpwdgrp/Makefile b/busybox/libpwdgrp/Makefile
new file mode 100644 (file)
index 0000000..c833550
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/libpwgrp
+LIBPWDGRP_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/libpwdgrp/Makefile.in b/busybox/libpwdgrp/Makefile.in
new file mode 100644 (file)
index 0000000..9bdfc10
--- /dev/null
@@ -0,0 +1,53 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LIBPWDGRP_AR:=libpwdgrp.a
+ifndef $(LIBPWDGRP_DIR)
+LIBPWDGRP_DIR:=$(top_builddir)/libpwdgrp/
+endif
+srcdir=$(top_srcdir)/libpwdgrp
+
+
+LIBPWDGRP_MSRC0:=$(srcdir)/pwd_grp.c
+LIBPWDGRP_MOBJ0-$(CONFIG_USE_BB_PWD_GRP):= fgetpwent_r.o fgetgrent_r.o \
+       fgetpwent.o fgetgrent.o getpwnam_r.o getgrnam_r.o getpwuid_r.o \
+       getgrgid_r.o getpwuid.o getgrgid.o getpwnam.o getgrnam.o getpw.o \
+       getpwent_r.o getgrent_r.o getpwent.o getgrent.o \
+       initgroups.o putpwent.o putgrent.o
+LIBPWDGRP_MOBJS0=$(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP_MOBJ0-y))
+
+LIBPWDGRP_MSRC1:=$(srcdir)/pwd_grp.c
+LIBPWDGRP_MOBJ1-$(CONFIG_USE_BB_PWD_GRP):= __parsepwent.o __parsegrent.o \
+       __pgsreader.o fgetspent_r.o fgetspent.o sgetspent_r.o getspnam_r.o \
+       getspnam.o getspent_r.o getspent.o sgetspent.o \
+       putspent.o __parsespent.o # getspuid_r.o getspuid.o
+LIBPWDGRP_MOBJS1=$(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP_MOBJ1-y))
+
+libraries-y+=$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR)
+
+$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR): $(LIBPWDGRP_MOBJS0) $(LIBPWDGRP_MOBJS1)
+       $(AR) -ro $@ $(LIBPWDGRP_MOBJS0) $(LIBPWDGRP_MOBJS1)
+
+$(LIBPWDGRP_MOBJS0): $(LIBPWDGRP_MSRC0)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
+$(LIBPWDGRP_MOBJS1): $(LIBPWDGRP_MSRC1)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
+
+
diff --git a/busybox/libpwdgrp/pwd_grp.c b/busybox/libpwdgrp/pwd_grp.c
new file mode 100644 (file)
index 0000000..9412fae
--- /dev/null
@@ -0,0 +1,1116 @@
+/*  Copyright (C) 2003     Manuel Novoa III
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*  Nov 6, 2003  Initial version.
+ *
+ *  NOTE: This implementation is quite strict about requiring all
+ *    field seperators.  It also does not allow leading whitespace
+ *    except when processing the numeric fields.  glibc is more
+ *    lenient.  See the various glibc difference comments below.
+ *
+ *  TODO:
+ *    Move to dynamic allocation of (currently staticly allocated)
+ *      buffers; especially for the group-related functions since
+ *      large group member lists will cause error returns.
+ *
+ */
+
+#include <features.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include "busybox.h"
+#include "pwd_.h"
+#include "grp_.h"
+#include "shadow_.h"
+
+#ifndef _PATH_SHADOW
+#define        _PATH_SHADOW    "/etc/shadow"
+#endif
+#ifndef _PATH_PASSWD
+#define        _PATH_PASSWD    "/etc/passwd"
+#endif
+#ifndef _PATH_GROUP
+#define        _PATH_GROUP     "/etc/group"
+#endif
+
+/**********************************************************************/
+/* Sizes for staticly allocated buffers. */
+
+/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
+ * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
+#define PWD_BUFFER_SIZE 256
+#define GRP_BUFFER_SIZE 256
+
+/**********************************************************************/
+/* Prototypes for internal functions. */
+
+extern int __parsepwent(void *pw, char *line);
+extern int __parsegrent(void *gr, char *line);
+extern int __parsespent(void *sp, char *line);
+
+extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
+                                          char *__restrict line_buff, size_t buflen, FILE *f);
+
+/**********************************************************************/
+/* For the various fget??ent_r funcs, return
+ *
+ *  0: success
+ *  ENOENT: end-of-file encountered
+ *  ERANGE: buflen too small
+ *  other error values possible. See __pgsreader.
+ *
+ * Also, *result == resultbuf on success and NULL on failure.
+ *
+ * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
+ *   We do not, as it really isn't an error if we reach the end-of-file.
+ *   Doing so is analogous to having fgetc() set errno on EOF.
+ */
+/**********************************************************************/
+#ifdef L_fgetpwent_r
+
+int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
+                               char *__restrict buffer, size_t buflen,
+                               struct passwd **__restrict result)
+{
+       int rv;
+
+       *result = NULL;
+
+       if (!(rv = __pgsreader(__parsepwent, resultbuf, buffer, buflen, stream))) {
+               *result = resultbuf;
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_fgetgrent_r
+
+int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
+                               char *__restrict buffer, size_t buflen,
+                               struct group **__restrict result)
+{
+       int rv;
+
+       *result = NULL;
+
+       if (!(rv = __pgsreader(__parsegrent, resultbuf, buffer, buflen, stream))) {
+               *result = resultbuf;
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_fgetspent_r
+
+int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
+                               char *__restrict buffer, size_t buflen,
+                               struct spwd **__restrict result)
+{
+       int rv;
+
+       *result = NULL;
+
+       if (!(rv = __pgsreader(__parsespent, resultbuf, buffer, buflen, stream))) {
+               *result = resultbuf;
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+/* For the various fget??ent funcs, return NULL on failure and a
+ * pointer to the appropriate struct (staticly allocated) on success.
+ */
+/**********************************************************************/
+#ifdef L_fgetpwent
+
+struct passwd *fgetpwent(FILE *stream)
+{
+       static char buffer[PWD_BUFFER_SIZE];
+       static struct passwd resultbuf;
+       struct passwd *result;
+
+       fgetpwent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_fgetgrent
+
+struct group *fgetgrent(FILE *stream)
+{
+       static char buffer[GRP_BUFFER_SIZE];
+       static struct group resultbuf;
+       struct group *result;
+
+       fgetgrent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_fgetspent
+
+extern int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
+                               char *__restrict buffer, size_t buflen,
+                               struct spwd **__restrict result);
+struct spwd *fgetspent(FILE *stream)
+{
+       static char buffer[PWD_BUFFER_SIZE];
+       static struct spwd resultbuf;
+       struct spwd *result;
+
+       fgetspent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_sgetspent_r
+
+int sgetspent_r(const char *string, struct spwd *result_buf,
+                               char *buffer, size_t buflen, struct spwd **result)
+{
+       int rv = ERANGE;
+
+       *result = NULL;
+
+       if (buflen < PWD_BUFFER_SIZE) {
+       DO_ERANGE:
+               errno=rv;
+               goto DONE;
+       }
+
+       if (string != buffer) {
+               if (strlen(string) >= buflen) {
+                       goto DO_ERANGE;
+               }
+               strcpy(buffer, string);
+       }
+
+       if (!(rv = __parsespent(result_buf, buffer))) {
+               *result = result_buf;
+       }
+
+ DONE:
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+
+#ifdef GETXXKEY_R_FUNC
+#error GETXXKEY_R_FUNC is already defined!
+#endif
+
+#ifdef L_getpwnam_r
+#define GETXXKEY_R_FUNC                        getpwnam_r
+#define GETXXKEY_R_PARSER      __parsepwent
+#define GETXXKEY_R_ENTTYPE             struct passwd
+#define GETXXKEY_R_TEST(ENT)   (!strcmp((ENT)->pw_name, key))
+#define DO_GETXXKEY_R_KEYTYPE  const char *__restrict
+#define DO_GETXXKEY_R_PATHNAME  _PATH_PASSWD
+#endif
+
+#ifdef L_getgrnam_r
+#define GETXXKEY_R_FUNC                        getgrnam_r
+#define GETXXKEY_R_PARSER      __parsegrent
+#define GETXXKEY_R_ENTTYPE             struct group
+#define GETXXKEY_R_TEST(ENT)   (!strcmp((ENT)->gr_name, key))
+#define DO_GETXXKEY_R_KEYTYPE  const char *__restrict
+#define DO_GETXXKEY_R_PATHNAME  _PATH_GROUP
+#endif
+
+#ifdef L_getspnam_r
+#define GETXXKEY_R_FUNC                        getspnam_r
+#define GETXXKEY_R_PARSER      __parsespent
+#define GETXXKEY_R_ENTTYPE             struct spwd
+#define GETXXKEY_R_TEST(ENT)   (!strcmp((ENT)->sp_namp, key))
+#define DO_GETXXKEY_R_KEYTYPE  const char *__restrict
+#define DO_GETXXKEY_R_PATHNAME  _PATH_SHADOW
+#endif
+
+#ifdef L_getpwuid_r
+#define GETXXKEY_R_FUNC                        getpwuid_r
+#define GETXXKEY_R_PARSER      __parsepwent
+#define GETXXKEY_R_ENTTYPE             struct passwd
+#define GETXXKEY_R_TEST(ENT)   ((ENT)->pw_uid == key)
+#define DO_GETXXKEY_R_KEYTYPE  uid_t
+#define DO_GETXXKEY_R_PATHNAME  _PATH_PASSWD
+#endif
+
+#ifdef L_getgrgid_r
+#define GETXXKEY_R_FUNC                        getgrgid_r
+#define GETXXKEY_R_PARSER      __parsegrent
+#define GETXXKEY_R_ENTTYPE             struct group
+#define GETXXKEY_R_TEST(ENT)   ((ENT)->gr_gid == key)
+#define DO_GETXXKEY_R_KEYTYPE  gid_t
+#define DO_GETXXKEY_R_PATHNAME  _PATH_GROUP
+#endif
+
+/**********************************************************************/
+#ifdef GETXXKEY_R_FUNC
+
+int GETXXKEY_R_FUNC(DO_GETXXKEY_R_KEYTYPE key,
+                                       GETXXKEY_R_ENTTYPE *__restrict resultbuf,
+                                       char *__restrict buffer, size_t buflen,
+                                       GETXXKEY_R_ENTTYPE **__restrict result)
+{
+       FILE *stream;
+       int rv;
+
+       *result = NULL;
+
+       if (!(stream = fopen(DO_GETXXKEY_R_PATHNAME, "r"))) {
+               rv = errno;
+       } else {
+               do {
+                       if (!(rv = __pgsreader(GETXXKEY_R_PARSER, resultbuf,
+                                                                  buffer, buflen, stream))
+                               ) {
+                               if (GETXXKEY_R_TEST(resultbuf)) { /* Found key? */
+                                       *result = resultbuf;
+                                       break;
+                               }
+                       } else {
+                               if (rv == ENOENT) {     /* end-of-file encountered. */
+                                       rv = 0;
+                               }
+                               break;
+                       }
+               } while (1);
+               fclose(stream);
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getpwuid
+
+struct passwd *getpwuid(uid_t uid)
+{
+       static char buffer[PWD_BUFFER_SIZE];
+       static struct passwd resultbuf;
+       struct passwd *result;
+
+       getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getgrgid
+
+struct group *getgrgid(gid_t gid)
+{
+       static char buffer[GRP_BUFFER_SIZE];
+       static struct group resultbuf;
+       struct group *result;
+
+       getgrgid_r(gid, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getspuid_r
+
+/* This function is non-standard and is currently not built.  It seems
+ * to have been created as a reentrant version of the non-standard
+ * functions getspuid.  Why getspuid was added, I do not know. */
+
+int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
+                      char *__restrict buffer, size_t buflen,
+                      struct spwd **__restrict result)
+{
+       int rv;
+       struct passwd *pp;
+       struct passwd password;
+       char pwd_buff[PWD_BUFFER_SIZE];
+
+       *result = NULL;
+       if (!(rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp))) {
+               rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getspuid
+
+/* This function is non-standard and is currently not built.
+ * Why it was added, I do not know. */
+
+struct spwd *getspuid(uid_t uid)
+{
+       static char buffer[PWD_BUFFER_SIZE];
+       static struct spwd resultbuf;
+       struct spwd *result;
+
+       getspuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getpwnam
+
+struct passwd *getpwnam(const char *name)
+{
+       static char buffer[PWD_BUFFER_SIZE];
+       static struct passwd resultbuf;
+       struct passwd *result;
+
+       getpwnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getgrnam
+
+struct group *getgrnam(const char *name)
+{
+       static char buffer[GRP_BUFFER_SIZE];
+       static struct group resultbuf;
+       struct group *result;
+
+       getgrnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getspnam
+
+struct spwd *getspnam(const char *name)
+{
+       static char buffer[PWD_BUFFER_SIZE];
+       static struct spwd resultbuf;
+       struct spwd *result;
+
+       getspnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getpw
+
+int getpw(uid_t uid, char *buf)
+{
+       struct passwd resultbuf;
+       struct passwd *result;
+       char buffer[PWD_BUFFER_SIZE];
+
+       if (!buf) {
+               errno=EINVAL;
+       } else if (!getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result)) {
+               if (sprintf(buf, "%s:%s:%lu:%lu:%s:%s:%s\n",
+                                       resultbuf.pw_name, resultbuf.pw_passwd,
+                                       (unsigned long)(resultbuf.pw_uid),
+                                       (unsigned long)(resultbuf.pw_gid),
+                                       resultbuf.pw_gecos, resultbuf.pw_dir,
+                                       resultbuf.pw_shell) >= 0
+                       ) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getpwent_r
+
+static FILE *pwf /*= NULL*/;
+void setpwent(void)
+{
+       if (pwf) {
+               rewind(pwf);
+       }
+}
+
+void endpwent(void)
+{
+       if (pwf) {
+               fclose(pwf);
+               pwf = NULL;
+       }
+}
+
+
+int getpwent_r(struct passwd *__restrict resultbuf, 
+                          char *__restrict buffer, size_t buflen,
+                          struct passwd **__restrict result)
+{
+       int rv;
+
+       *result = NULL;                         /* In case of error... */
+
+       if (!pwf) {
+               if (!(pwf = fopen(_PATH_PASSWD, "r"))) {
+                       rv = errno;
+                       goto ERR;
+               }
+       }
+
+       if (!(rv = __pgsreader(__parsepwent, resultbuf,
+                                                  buffer, buflen, pwf))) {
+               *result = resultbuf;
+       }
+
+ ERR:
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getgrent_r
+
+static FILE *grf /*= NULL*/;
+void setgrent(void)
+{
+       if (grf) {
+               rewind(grf);
+       }
+}
+
+void endgrent(void)
+{
+       if (grf) {
+               fclose(grf);
+               grf = NULL;
+       }
+}
+
+int getgrent_r(struct group *__restrict resultbuf,
+                          char *__restrict buffer, size_t buflen,
+                          struct group **__restrict result)
+{
+       int rv;
+
+       *result = NULL;                         /* In case of error... */
+
+       if (!grf) {
+               if (!(grf = fopen(_PATH_GROUP, "r"))) {
+                       rv = errno;
+                       goto ERR;
+               }
+       }
+
+       if (!(rv = __pgsreader(__parsegrent, resultbuf,
+                                                  buffer, buflen, grf))) {
+               *result = resultbuf;
+       }
+
+ ERR:
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getspent_r
+
+static FILE *spf /*= NULL*/;
+void setspent(void)
+{
+       if (spf) {
+               rewind(spf);
+       }
+}
+
+void endspent(void)
+{
+       if (spf) {
+               fclose(spf);
+               spf = NULL;
+       }
+}
+
+int getspent_r(struct spwd *resultbuf, char *buffer, 
+                          size_t buflen, struct spwd **result)
+{
+       int rv;
+
+       *result = NULL;                         /* In case of error... */
+
+       if (!spf) {
+               if (!(spf = fopen(_PATH_SHADOW, "r"))) {
+                       rv = errno;
+                       goto ERR;
+               }
+       }
+
+       if (!(rv = __pgsreader(__parsespent, resultbuf,
+                                                  buffer, buflen, spf))) {
+               *result = resultbuf;
+       }
+
+ ERR:
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getpwent
+
+struct passwd *getpwent(void)
+{
+       static char line_buff[PWD_BUFFER_SIZE];
+       static struct passwd pwd;
+       struct passwd *result;
+
+       getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getgrent
+
+struct group *getgrent(void)
+{
+       static char line_buff[GRP_BUFFER_SIZE];
+       static struct group gr;
+       struct group *result;
+
+       getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getspent
+
+struct spwd *getspent(void)
+{
+       static char line_buff[PWD_BUFFER_SIZE];
+       static struct spwd spwd;
+       struct spwd *result;
+
+       getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_sgetspent
+
+struct spwd *sgetspent(const char *string)
+{
+       static char line_buff[PWD_BUFFER_SIZE];
+       static struct spwd spwd;
+       struct spwd *result;
+
+       sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
+       return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_initgroups
+
+int initgroups(const char *user, gid_t gid)
+{
+       FILE *grf;
+       gid_t *group_list;
+       int num_groups, rv;
+       char **m;
+       struct group group;
+       char buff[PWD_BUFFER_SIZE];
+
+       rv = -1;
+
+       /* We alloc space for 8 gids at a time. */
+       if (((group_list = (gid_t *) malloc(8*sizeof(gid_t *))) != NULL)
+               && ((grf = fopen(_PATH_GROUP, "r")) != NULL)
+               ) {
+
+               *group_list = gid;
+               num_groups = 1;
+
+               while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grf)) {
+                       assert(group.gr_mem); /* Must have at least a NULL terminator. */
+                       if (group.gr_gid != gid) {
+                               for (m=group.gr_mem ; *m ; m++) {
+                                       if (!strcmp(*m, user)) {
+                                               if (!(num_groups & 7)) {
+                                                       gid_t *tmp = (gid_t *)
+                                                               realloc(group_list,
+                                                                               (num_groups+8) * sizeof(gid_t *));
+                                                       if (!tmp) {
+                                                               rv = -1;
+                                                               goto DO_CLOSE;
+                                                       }
+                                                       group_list = tmp;
+                                               }
+                                               group_list[num_groups++] = group.gr_gid;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               rv = setgroups(num_groups, group_list);
+       DO_CLOSE:
+               fclose(grf);
+       }
+
+       /* group_list will be NULL if initial malloc failed, which may trigger
+        * warnings from various malloc debuggers. */
+       free(group_list);
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_putpwent
+
+int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
+{
+       int rv = -1;
+
+       if (!p || !f) {
+               errno=EINVAL;
+       } else {
+               /* No extra thread locking is needed above what fprintf does. */
+               if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
+                                       p->pw_name, p->pw_passwd,
+                                       (unsigned long)(p->pw_uid),
+                                       (unsigned long)(p->pw_gid),
+                                       p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
+                       ) {
+                       rv = 0;
+               }
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_putgrent
+
+int putgrent(const struct group *__restrict p, FILE *__restrict f)
+{
+       static const char format[] = ",%s";
+       char **m;
+       const char *fmt;
+       int rv = -1;
+
+       if (!p || !f) {                         /* Sigh... glibc checks. */
+               errno=EINVAL;
+       } else {
+               if (fprintf(f, "%s:%s:%lu:",
+                                       p->gr_name, p->gr_passwd,
+                                       (unsigned long)(p->gr_gid)) >= 0
+                       ) {
+
+                       fmt = format + 1;
+
+                       assert(p->gr_mem);
+                       m = p->gr_mem;
+
+                       do {
+                               if (!*m) {
+                                       if (fputc_unlocked('\n', f) >= 0) {
+                                               rv = 0;
+                                       }
+                                       break;
+                               }
+                               if (fprintf(f, fmt, *m) < 0) {
+                                       break;
+                               }
+                               ++m;
+                               fmt = format;
+                       } while (1);
+
+               }
+
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_putspent
+
+static const unsigned char sp_off[] = {
+       offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
+       offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
+       offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
+       offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
+       offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
+       offsetof(struct spwd, sp_expire),       /* 7 - not a char ptr */
+};
+
+int putspent(const struct spwd *p, FILE *stream)
+{
+       static const char ld_format[] = "%ld:";
+       const char *f;
+       long int x;
+       int i;
+       int rv = -1;
+
+       /* Unlike putpwent and putgrent, glibc does not check the args. */
+       if (fprintf(stream, "%s:%s:", p->sp_namp,
+                               (p->sp_pwdp ? p->sp_pwdp : "")) < 0
+               ) {
+               goto DO_UNLOCK;
+       }
+
+       for (i=0 ; i < sizeof(sp_off) ; i++) {
+               f = ld_format;
+               if ((x = *(const long int *)(((const char *) p) + sp_off[i])) == -1) {
+                       f += 3;
+               }
+               if (fprintf(stream, f, x) < 0) {
+                       goto DO_UNLOCK;
+               }
+       }
+
+       if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
+               goto DO_UNLOCK;
+       }
+
+       if (fputc_unlocked('\n', stream) > 0) {
+               rv = 0;
+       }
+
+DO_UNLOCK:
+       return rv;
+}
+
+#endif
+/**********************************************************************/
+/* Internal uClibc functions.                                         */
+/**********************************************************************/
+#ifdef L___parsepwent
+
+static const unsigned char pw_off[] = {
+       offsetof(struct passwd, pw_name),       /* 0 */
+       offsetof(struct passwd, pw_passwd),     /* 1 */
+       offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
+       offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
+       offsetof(struct passwd, pw_gecos),      /* 4 */
+       offsetof(struct passwd, pw_dir),        /* 5 */
+       offsetof(struct passwd, pw_shell)       /* 6 */
+};
+
+int __parsepwent(void *data, char *line)
+{
+       char *endptr;
+       char *p;
+       int i;
+
+       i = 0;
+       do {
+               p = ((char *) ((struct passwd *) data)) + pw_off[i];
+
+               if ((i & 6) ^ 2) {      /* i!=2 and i!=3 */
+                       *((char **) p) = line;
+                       if (i==6) {
+                               return 0;
+                       }
+                       /* NOTE: glibc difference - glibc allows omission of
+                        * ':' seperators after the gid field if all remaining
+                        * entries are empty.  We require all separators. */
+                       if (!(line = strchr(line, ':'))) {
+                               break;
+                       }
+               } else {
+                       unsigned long t = strtoul(line, &endptr, 10);
+                       /* Make sure we had at least one digit, and that the
+                        * failing char is the next field seperator ':'.  See
+                        * glibc difference note above. */
+                       /* TODO: Also check for leading whitespace? */
+                       if ((endptr == line) || (*endptr != ':')) {
+                               break;
+                       }
+                       line = endptr;
+                       if (i & 1) {            /* i == 3 -- gid */
+                               *((gid_t *) p) = t;
+                       } else {                        /* i == 2 -- uid */
+                               *((uid_t *) p) = t;
+                       }
+               }
+
+               *line++ = 0;
+               ++i;
+       } while (1);
+
+       return -1;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L___parsegrent
+
+static const unsigned char gr_off[] = {
+       offsetof(struct group, gr_name),        /* 0 */
+       offsetof(struct group, gr_passwd),      /* 1 */
+       offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
+};
+
+int __parsegrent(void *data, char *line)
+{
+       char *endptr;
+       char *p;
+       int i;
+       char **members;
+       char *end_of_buf;
+
+       end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
+       i = 0;
+       do {
+               p = ((char *) ((struct group *) data)) + gr_off[i];
+
+               if (i < 2) {
+                       *((char **) p) = line;
+                       if (!(line = strchr(line, ':'))) {
+                               break;
+                       }
+                       *line++ = 0;
+                       ++i;
+               } else {
+                       *((gid_t *) p) = strtoul(line, &endptr, 10);
+
+                       /* NOTE: glibc difference - glibc allows omission of the
+                        * trailing colon when there is no member list.  We treat
+                        * this as an error. */
+
+                       /* Make sure we had at least one digit, and that the
+                        * failing char is the next field seperator ':'.  See
+                        * glibc difference note above. */
+                       if ((endptr == line) || (*endptr != ':')) {
+                               break;
+                       }
+
+                       i = 1;                          /* Count terminating NULL ptr. */
+                       p = endptr;
+
+                       if (p[1]) { /* We have a member list to process. */
+                               /* Overwrite the last ':' with a ',' before counting.
+                                * This allows us to test for initial ',' and adds
+                                * one ',' so that the ',' count equals the member
+                                * count. */
+                               *p = ',';
+                               do {
+                                       /* NOTE: glibc difference - glibc allows and trims leading
+                                        * (but not trailing) space.  We treat this as an error. */
+                                       /* NOTE: glibc difference - glibc allows consecutive and
+                                        * trailing commas, and ignores "empty string" users.  We
+                                        * treat this as an error. */
+                                       if (*p == ',') {
+                                               ++i;
+                                               *p = 0; /* nul-terminate each member string. */
+                                               if (!*++p || (*p == ',') || isspace(*p)) {
+                                                       goto ERR;
+                                               }
+                                       }
+                               } while (*++p);
+                       }
+
+                       /* Now align (p+1), rounding up. */
+                       /* Assumes sizeof(char **) is a power of 2. */
+                       members = (char **)( (((intptr_t) p) + sizeof(char **))
+                                                                & ~((intptr_t)(sizeof(char **) - 1)) );
+
+                       if (((char *)(members + i)) > end_of_buf) {     /* No space. */
+                               break;
+                       }
+
+                       ((struct group *) data)->gr_mem = members;
+
+                       if (--i) {
+                               p = endptr;     /* Pointing to char prior to first member. */
+                               do {
+                                       *members++ = ++p;
+                                       if (!--i) break;
+                                       while (*++p) {}
+                               } while (1);
+                       }                               
+                       *members = NULL;
+
+                       return 0;
+               }
+       } while (1);
+
+ ERR:
+       return -1;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L___parsespent
+
+static const unsigned char sp_off[] = {
+       offsetof(struct spwd, sp_namp),         /* 0 */
+       offsetof(struct spwd, sp_pwdp),         /* 1 */
+       offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
+       offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
+       offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
+       offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
+       offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
+       offsetof(struct spwd, sp_expire),       /* 7 - not a char ptr */
+       offsetof(struct spwd, sp_flag)          /* 8 - not a char ptr */
+};
+
+int __parsespent(void *data, char * line)
+{
+       char *endptr;
+       char *p;
+       int i;
+
+       i = 0;
+       do {
+               p = ((char *) ((struct spwd *) data)) + sp_off[i];
+               if (i < 2) {
+                       *((char **) p) = line;
+                       if (!(line = strchr(line, ':'))) {
+                               break;
+                       }
+               } else {
+#if 0
+                       if (i==5) {                     /* Support for old format. */
+                               while (isspace(*line)) ++line; /* glibc eats space here. */
+                               if (!*line) {
+                                       ((struct spwd *) data)->sp_warn = -1;
+                                       ((struct spwd *) data)->sp_inact = -1;
+                                       ((struct spwd *) data)->sp_expire = -1;
+                                       ((struct spwd *) data)->sp_flag = ~0UL;
+                                       return 0;
+                               }
+                       }
+#endif
+
+                       *((long *) p) = (long) strtoul(line, &endptr, 10);
+
+                       if (endptr == line) {
+                               *((long *) p) = ((i != 8) ? -1L : ((long)(~0UL)));
+                       }
+
+                       line = endptr;
+
+                       if (i == 8) {
+                               if (!*endptr) {
+                                       return 0;
+                               }
+                               break;
+                       }
+
+                       if (*endptr != ':') {
+                               break;
+                       }
+
+               }
+
+               *line++ = 0;
+               ++i;
+       } while (1);
+
+       return EINVAL;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L___pgsreader
+
+/* Reads until if EOF, or until if finds a line which fits in the buffer
+ * and for which the parser function succeeds.
+ *
+ * Returns 0 on success and ENOENT for end-of-file (glibc concession).
+ */
+
+int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
+                               char *__restrict line_buff, size_t buflen, FILE *f)
+{
+       int line_len;
+       int skip;
+       int rv = ERANGE;
+
+       if (buflen < PWD_BUFFER_SIZE) {
+               errno=rv;
+       } else {
+               skip = 0;
+               do {
+                       if (!fgets_unlocked(line_buff, buflen, f)) {
+                               if (feof_unlocked(f)) {
+                                       rv = ENOENT;
+                               }
+                               break;
+                       }
+
+                       line_len = strlen(line_buff) - 1; /* strlen() must be > 0. */
+                       if (line_buff[line_len] == '\n') {
+                               line_buff[line_len] = 0;
+                       } else if (line_len + 2 == buflen) { /* line too long */
+                               ++skip;
+                               continue;
+                       }
+
+                       if (skip) {
+                               --skip;
+                               continue;
+                       }
+
+                       /* NOTE: glibc difference - glibc strips leading whitespace from
+                        * records.  We do not allow leading whitespace. */
+
+                       /* Skip empty lines, comment lines, and lines with leading
+                        * whitespace. */
+                       if (*line_buff && (*line_buff != '#') && !isspace(*line_buff)) {
+                               if (__parserfunc == __parsegrent) {     /* Do evil group hack. */
+                                       /* The group entry parsing function needs to know where
+                                        * the end of the buffer is so that it can construct the
+                                        * group member ptr table. */
+                                       ((struct group *) data)->gr_name = line_buff + buflen;
+                               }
+
+                               if (!__parserfunc(data, line_buff)) {
+                                       rv = 0;
+                                       break;
+                               }
+                       }
+               } while (1);
+
+       }
+
+       return rv;
+}
+
+#endif
+/**********************************************************************/
diff --git a/busybox/loginutils/Config.in b/busybox/loginutils/Config.in
new file mode 100644 (file)
index 0000000..5619aa9
--- /dev/null
@@ -0,0 +1,161 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Login/Password Management Utilities"
+
+config CONFIG_USE_BB_PWD_GRP
+       bool "Use internal password and group functions rather than system functions"
+       default n
+       help
+           If you leave this disabled, busybox will use the system's password
+           and group functions.  And if you are using the GNU C library
+           (glibc), you will then need to install the /etc/nsswitch.conf
+           configuration file and the required /lib/libnss_* libraries in
+           order for the password and group functions to work.  This generally
+           makes your embedded system quite a bit larger.
+
+           Enabling this option will cause busybox to directly access the
+           system's /etc/password, /etc/group files (and your system will be
+           smaller, and I will get fewer emails asking about how glibc NSS
+           works).  When this option is enabled, you will not be able to use
+           PAM to access remote LDAP password servers and whatnot.  And if you
+           want hostname resolution to work with glibc, you still need the
+           /lib/libnss_* libraries.
+
+           If you enable this option, it will add about 1.5k to busybox.
+
+
+config CONFIG_ADDGROUP
+       bool "addgroup"
+       default n
+       help
+         Utility for creating a new group account.
+
+config CONFIG_DELGROUP
+       bool "delgroup"
+       default n
+       help
+         Utility for deleting a group account.
+
+config CONFIG_ADDUSER
+       bool "adduser"
+       default n
+       help
+         Utility for creating a new user account.
+
+config CONFIG_DELUSER
+       bool "deluser"
+       default n
+       help
+         Utility for deleting a user account.
+
+config CONFIG_GETTY
+       bool "getty"
+       default n
+       help
+         getty lets you log in on a tty, it is normally invoked by init.
+
+config CONFIG_FEATURE_U_W_TMP
+       bool "  Support utmp and wtmp files"
+       depends on CONFIG_GETTY || CONFIG_LOGIN || CONFIG_SU || CONFIG_WHO || CONFIG_LAST
+       default n
+       help
+         The files /var/run/utmp and /var/run/wtmp can be used to track when
+         user's have logged into and logged out of the system, allowing programs
+         such as 'who' and 'last' to list who is currently logged in.
+
+config CONFIG_LOGIN
+       bool "login"
+       default n
+       select CONFIG_FEATURE_SUID
+       help
+         login is used when signing onto a system.
+
+         Note that Busybox binary must be setuid root for this applet to
+         work properly.
+
+config CONFIG_FEATURE_SECURETTY
+       bool "  Support for /etc/securetty"
+       default y
+       depends on CONFIG_LOGIN
+       help
+         The file  /etc/securetty  is used by (some versions of) login(1).
+         The file contains the device names of tty lines (one per line,
+         without leading /dev/) on which root is allowed to login.
+
+config CONFIG_PASSWD
+       bool "passwd"
+       default n
+       select CONFIG_FEATURE_SUID
+       help
+         passwd changes passwords for user and group accounts.  A normal user
+         may only change the password for his/her own account, the super user
+         may change the password for any account.  The administrator of a group
+         may change the password for the group.
+
+         Note that Busybox binary must be setuid root for this applet to
+         work properly.
+
+config CONFIG_SU
+       bool "su"
+       default n
+       select CONFIG_FEATURE_SUID
+       help
+         su is used to become another user during a login session.
+         Invoked without a username, su defaults to becoming the super user.
+
+         Note that Busybox binary must be setuid root for this applet to
+         work properly.
+
+config CONFIG_SULOGIN
+       bool "sulogin"
+       default n
+       help
+         sulogin is invoked when the system goes into single user
+         mode (this is done through an entry in inittab).
+
+config CONFIG_VLOCK
+       bool "vlock"
+       default n
+       select CONFIG_FEATURE_SUID
+       help
+         Build the "vlock" applet which allows you to lock (virtual) terminals.
+
+         Note that Busybox binary must be setuid root for this applet to
+         work properly.
+
+comment "Common options for adduser, deluser, login, su"
+       depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
+
+config CONFIG_FEATURE_SHADOWPASSWDS
+       bool "Support for shadow passwords"
+       default n
+       depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
+       help
+         Build support for shadow password in /etc/shadow.  This file is only
+         readable by root and thus the encrypted passwords are no longer
+         publicly readable.
+
+config CONFIG_USE_BB_SHADOW
+       bool "  Use busybox shadow password functions"
+       default n
+       depends on CONFIG_USE_BB_PWD_GRP && CONFIG_FEATURE_SHADOWPASSWDS
+       help
+           If you leave this disabled, busybox will use the system's shadow
+           password handling functions.  And if you are using the GNU C library
+           (glibc), you will then need to install the /etc/nsswitch.conf
+           configuration file and the required /lib/libnss_* libraries in
+           order for the shadow password functions to work.  This generally
+           makes your embedded system quite a bit larger.
+
+           Enabling this option will cause busybox to directly access the
+           system's /etc/shadow file when handling shadow passwords.  This
+           makes your system smaller and I will get fewer emails asking about
+           how glibc NSS works).  When this option is enabled, you will not be
+           able to use PAM to access shadow passwords from remote LDAP
+           password servers and whatnot.
+
+endmenu
+
diff --git a/busybox/loginutils/Makefile b/busybox/loginutils/Makefile
new file mode 100644 (file)
index 0000000..98226ae
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/loginutils
+LOGINUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/loginutils/Makefile.in b/busybox/loginutils/Makefile.in
new file mode 100644 (file)
index 0000000..96a61e6
--- /dev/null
@@ -0,0 +1,57 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LOGINUTILS_AR:=loginutils.a
+ifndef LOGINUTILS_DIR
+LOGINUTILS_DIR:=$(top_builddir)/loginutils/
+endif
+srcdir=$(top_srcdir)/loginutils
+
+LOGINUTILS-y:=
+LOGINUTILS-$(CONFIG_ADDGROUP)  += addgroup.o
+LOGINUTILS-$(CONFIG_ADDUSER)   += adduser.o
+LOGINUTILS-$(CONFIG_GETTY)     += getty.o
+LOGINUTILS-$(CONFIG_LOGIN)     += login.o
+LOGINUTILS-$(CONFIG_PASSWD)    += passwd.o
+LOGINUTILS-$(CONFIG_SU)                += su.o
+LOGINUTILS-$(CONFIG_SULOGIN)   += sulogin.o
+LOGINUTILS-$(CONFIG_VLOCK)     += vlock.o
+LOGINUTILS-$(CONFIG_DELUSER)   += deluser.o
+LOGINUTILS-$(CONFIG_DELGROUP)  += delgroup.o
+
+libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR)
+
+needcrypt-y:=
+needcrypt-$(CONFIG_LOGIN)      := y
+needcrypt-$(CONFIG_PASSWD)     := y
+needcrypt-$(CONFIG_SU)         := y
+needcrypt-$(CONFIG_SULOGIN)    := y
+needcrypt-$(CONFIG_VLOCK)      := y
+
+
+ifeq ($(needcrypt-y),y)
+  LIBRARIES += -lcrypt
+endif
+
+$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
+       $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
+
+$(LOGINUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/loginutils/addgroup.c b/busybox/loginutils/addgroup.c
new file mode 100644 (file)
index 0000000..804d696
--- /dev/null
@@ -0,0 +1,171 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * addgroup - add users to /etc/passwd and /etc/shadow
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "busybox.h"
+#include "pwd_.h"
+#include "grp_.h"
+
+
+/* structs __________________________ */
+
+/* data _____________________________ */
+
+/* defaults : should this be in an external file? */
+static const char default_passwd[] = "x";
+
+
+/* make sure gr_name isn't taken, make sure gid is kosher
+ * return 1 on failure */
+static int group_study(const char *filename, struct group *g)
+{
+       FILE *etc_group;
+       gid_t desired;
+
+       struct group *grp;
+       const int max = 65000;
+
+       etc_group = bb_xfopen(filename, "r");
+
+       /* make sure gr_name isn't taken, make sure gid is kosher */
+       desired = g->gr_gid;
+       while ((grp = fgetgrent(etc_group))) {
+               if ((strcmp(grp->gr_name, g->gr_name)) == 0) {
+                       bb_error_msg_and_die("%s: group already in use\n", g->gr_name);
+               }
+               if ((desired) && grp->gr_gid == desired) {
+                       bb_error_msg_and_die("%d: gid has already been allocated\n",
+                                                         desired);
+               }
+               if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) {
+                       g->gr_gid = grp->gr_gid;
+               }
+       }
+       fclose(etc_group);
+
+       /* gid */
+       if (desired) {
+               g->gr_gid = desired;
+       } else {
+               g->gr_gid++;
+       }
+       /* return 1; */
+       return 0;
+}
+
+/* append a new user to the passwd file */
+static int addgroup(const char *filename, char *group, gid_t gid, const char *user)
+{
+       FILE *etc_group;
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       FILE *etc_gshadow;
+#endif
+
+       struct group gr;
+
+       /* group:passwd:gid:userlist */
+       static const char entryfmt[] = "%s:%s:%d:%s\n";
+
+       /* make sure gid and group haven't already been allocated */
+       gr.gr_gid = gid;
+       gr.gr_name = group;
+       if (group_study(filename, &gr))
+               return 1;
+
+       /* add entry to group */
+       etc_group = bb_xfopen(filename, "a");
+
+       fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, user);
+       fclose(etc_group);
+
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       /* add entry to gshadow if necessary */
+       if (access(bb_path_gshadow_file, F_OK|W_OK) == 0) {
+               etc_gshadow = bb_xfopen(bb_path_gshadow_file, "a");
+               fprintf(etc_gshadow, "%s:!::\n", group);
+               fclose(etc_gshadow);
+       }
+#endif
+
+       /* return 1; */
+       return 0;
+}
+
+#ifndef CONFIG_ADDUSER
+static inline void if_i_am_not_root(void)
+{
+       if (geteuid()) {
+               bb_error_msg_and_die( "Only root may add a user or group to the system.");
+       }
+}
+#else
+extern void if_i_am_not_root(void);
+#endif
+
+/*
+ * addgroup will take a login_name as its first parameter.
+ *
+ * gid
+ *
+ * can be customized via command-line parameters.
+ * ________________________________________________________________________ */
+int addgroup_main(int argc, char **argv)
+{
+       char *group;
+       char *user;
+       gid_t gid = 0;
+
+       /* get remaining args */
+       if(bb_getopt_ulflags(argc, argv, "g:", &group)) {
+               gid = bb_xgetlarg(group, 10, 0, LONG_MAX);
+       }
+
+       if (optind < argc) {
+               group = argv[optind];
+               optind++;
+       } else {
+               bb_show_usage();
+       }
+
+       if (optind < argc) {
+               user = argv[optind];
+       } else {
+               user = "";
+       }
+       
+       if_i_am_not_root();
+
+       /* werk */
+       return addgroup(bb_path_group_file, group, gid, user);
+}
diff --git a/busybox/loginutils/adduser.c b/busybox/loginutils/adduser.c
new file mode 100644 (file)
index 0000000..7fa05a0
--- /dev/null
@@ -0,0 +1,314 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * adduser - add users to /etc/passwd and /etc/shadow
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "busybox.h"
+
+
+
+/* structs __________________________ */
+
+typedef struct {
+       uid_t u;
+       gid_t g;
+} Id;
+
+/* data _____________________________ */
+
+/* defaults : should this be in an external file? */
+static const char default_passwd[] = "x";
+static const char default_gecos[] = "Linux User,,,";
+static const char default_home_prefix[] = "/home";
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+/* shadow in use? */
+static int shadow_enabled = 0;
+#endif
+
+/* remix */
+/* EDR recoded such that the uid may be passed in *p */
+static int passwd_study(const char *filename, struct passwd *p)
+{
+       struct passwd *pw;
+       FILE *passwd;
+
+       const int min = 500;
+       const int max = 65000;
+
+       passwd = bb_wfopen(filename, "r");
+       if (!passwd)
+               return 4;
+
+       /* EDR if uid is out of bounds, set to min */
+       if ((p->pw_uid > max) || (p->pw_uid < min))
+               p->pw_uid = min;
+
+       /* stuff to do:
+        * make sure login isn't taken;
+        * find free uid and gid;
+        */
+       while ((pw = fgetpwent(passwd))) {
+               if (strcmp(pw->pw_name, p->pw_name) == 0) {
+                       /* return 0; */
+                       return 1;
+               }
+               if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
+                       && (pw->pw_uid >= min)) {
+                       p->pw_uid = pw->pw_uid + 1;
+               }
+       }
+
+       if (p->pw_gid == 0) {
+               /* EDR check for an already existing gid */
+               while (getgrgid(p->pw_uid) != NULL)
+                       p->pw_uid++;
+
+               /* EDR also check for an existing group definition */
+               if (getgrnam(p->pw_name) != NULL)
+                       return 3;
+
+               /* EDR create new gid always = uid */
+               p->pw_gid = p->pw_uid;
+       }
+
+       /* EDR bounds check */
+       if ((p->pw_uid > max) || (p->pw_uid < min))
+               return 2;
+
+       /* return 1; */
+       return 0;
+}
+
+static void addgroup_wrapper(const char *login, gid_t gid)
+{
+       char *cmd;
+
+       bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
+       system(cmd);
+       free(cmd);
+}
+
+static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
+
+static void passwd_wrapper(const char *login)
+{
+       static const char prog[] = "passwd";
+       execlp(prog, prog, login, NULL);
+       bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
+}
+
+/* putpwent(3) remix */
+static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
+{
+       FILE *passwd;
+       int r;
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       FILE *shadow;
+       struct spwd *sp;
+#endif
+       int new_group = 1;
+
+       /* if using a pre-existing group, don't create one */
+       if (p->pw_gid != 0)
+               new_group = 0;
+
+       /* make sure everything is kosher and setup uid && gid */
+       passwd = bb_wfopen(filename, "a");
+       if (passwd == NULL) {
+               return 1;
+       }
+       fseek(passwd, 0, SEEK_END);
+
+       /* if (passwd_study(filename, p) == 0) { */
+       r = passwd_study(filename, p);
+       if (r) {
+               if (r == 1)
+                       bb_error_msg("%s: login already in use", p->pw_name);
+               else if (r == 2)
+                       bb_error_msg("illegal uid or no uids left");
+               else if (r == 3)
+                       bb_error_msg("group name %s already in use", p->pw_name);
+               else
+                       bb_error_msg("generic error.");
+               return 1;
+       }
+
+       /* add to passwd */
+       if (putpwent(p, passwd) == -1) {
+               return 1;
+       }
+       fclose(passwd);
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       /* add to shadow if necessary */
+       if (shadow_enabled) {
+               shadow = bb_wfopen(bb_path_shadow_file, "a");
+               if (shadow == NULL) {
+                       return 1;
+               }
+               fseek(shadow, 0, SEEK_END);
+               sp = pwd_to_spwd(p);
+               sp->sp_max = 99999;             /* debianish */
+               sp->sp_warn = 7;
+               fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
+                               sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
+                               sp->sp_warn);
+               fclose(shadow);
+       }
+#endif
+
+       if (new_group) {
+               /* add to group */
+               /* addgroup should be responsible for dealing w/ gshadow */
+               addgroup_wrapper(p->pw_name, p->pw_gid);
+       }
+
+       /* Clear the umask for this process so it doesn't
+        * * screw up the permissions on the mkdir and chown. */
+       umask(0);
+
+       if (makehome) {
+               /* mkdir */
+               if (mkdir(p->pw_dir, 0755)) {
+                       bb_perror_msg("%s", p->pw_dir);
+               }
+               /* Set the owner and group so it is owned by the new user. */
+               if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
+                       bb_perror_msg("%s", p->pw_dir);
+               }
+               /* Now fix up the permissions to 2755. Can't do it before now
+                * since chown will clear the setgid bit */
+               if (chmod(p->pw_dir, 02755)) {
+                       bb_perror_msg("%s", p->pw_dir);
+               }
+       }
+
+       if (setpass) {
+               /* interactively set passwd */
+               passwd_wrapper(p->pw_name);
+       }
+
+       return 0;
+}
+
+
+/* return current uid (root is always uid == 0, right?) */
+#ifndef CONFIG_ADDGROUP
+static inline void if_i_am_not_root(void)
+#else
+void if_i_am_not_root(void)
+#endif
+{
+       if (geteuid()) {
+               bb_error_msg_and_die( "Only root may add a user or group to the system.");
+       }
+}
+
+#define SETPASS                                (1 << 4)
+#define MAKEHOME                       (1 << 6)
+
+/*
+ * adduser will take a login_name as its first parameter.
+ *
+ * home
+ * shell
+ * gecos
+ *
+ * can be customized via command-line parameters.
+ * ________________________________________________________________________ */
+int adduser_main(int argc, char **argv)
+{
+       struct passwd pw;
+       const char *login;
+       const char *gecos = default_gecos;
+       const char *home = NULL;
+       const char *shell = DEFAULT_SHELL;
+       const char *usegroup = NULL;
+       int flags;
+       int setpass = 1;
+       int makehome = 1;
+
+       /* init */
+       if (argc < 2) {
+               bb_show_usage();
+       }
+       /* get args */
+       flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
+
+       if (flags & SETPASS) {
+               setpass = 0;
+       }
+       if (flags & MAKEHOME) {
+               makehome = 0;
+       }
+
+       /* got root? */
+       if_i_am_not_root();
+
+       /* get login */
+       if (optind >= argc) {
+               bb_error_msg_and_die( "no user specified");
+       }
+       login = argv[optind];
+
+       /* create string for $HOME if not specified already */
+       if (!home) {
+               home = concat_path_file(default_home_prefix, login);
+       }
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       /* is /etc/shadow in use? */
+       shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
+#endif
+
+       /* create a passwd struct */
+       pw.pw_name = (char *)login;
+       pw.pw_passwd = (char *)default_passwd;
+       pw.pw_uid = 0;
+       pw.pw_gid = 0;
+       pw.pw_gecos = (char *)gecos;
+       pw.pw_dir = (char *)home;
+       pw.pw_shell = (char *)shell;
+
+       if (usegroup) {
+               /* Add user to a group that already exists */
+               pw.pw_gid = my_getgrnam(usegroup);
+               /* exits on error */    
+       }
+
+       /* grand finale */
+       return adduser(bb_path_passwd_file, &pw, makehome, setpass);
+}
diff --git a/busybox/loginutils/delgroup.c b/busybox/loginutils/delgroup.c
new file mode 100644 (file)
index 0000000..91edf29
--- /dev/null
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * deluser (remove lusers from the system ;) for TinyLogin
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "busybox.h"
+
+
+#if ! defined CONFIG_DELUSER
+#include "delline.c"
+#else
+extern int del_line_matching(const char *login, const char *filename);
+#endif
+
+int delgroup_main(int argc, char **argv)
+{
+       /* int successful; */
+       int failure;
+
+       if (argc != 2) {
+               bb_show_usage();
+       } else {
+
+               failure = del_line_matching(argv[1], bb_path_group_file);
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+               if (access(bb_path_gshadow_file, W_OK) == 0) {
+                       /* EDR the |= works if the error is not 0, so he had it wrong */
+                       failure |= del_line_matching(argv[1], bb_path_gshadow_file);
+               }
+#endif
+               if (failure) {
+                       bb_error_msg_and_die("%s: Group could not be removed\n", argv[1]);
+               }
+
+       }
+       return (EXIT_SUCCESS);
+}
+
+/* $Id: delgroup.c,v 1.2 2003/07/14 21:50:51 andersen Exp $ */
diff --git a/busybox/loginutils/delline.c b/busybox/loginutils/delline.c
new file mode 100644 (file)
index 0000000..8d534c8
--- /dev/null
@@ -0,0 +1,113 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * deluser (remove lusers from the system ;) for TinyLogin
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "busybox.h"
+
+
+
+/* where to start and stop deletion */
+typedef struct {
+       size_t start;
+       size_t stop;
+} Bounds;
+
+/* An interesting side-effect of boundary()'s
+ * implementation is that the first user (typically root)
+ * cannot be removed.  Let's call it a feature. */
+static inline Bounds boundary(const char *buffer, const char *login)
+{
+       char needle[256];
+       char *start;
+       char *stop;
+       Bounds b;
+
+       snprintf(needle, 256, "\n%s:", login);
+       needle[255] = 0;
+       start = strstr(buffer, needle);
+       if (!start) {
+               b.start = 0;
+               b.stop = 0;
+               return b;
+       }
+       start++;
+
+       stop = index(start, '\n');      /* index is a BSD-ism */
+       b.start = start - buffer;
+       b.stop = stop - buffer;
+       return b;
+}
+
+/* grep -v ^login (except it only deletes the first match) */
+/* ...in fact, I think I'm going to simplify this later */
+int del_line_matching(const char *login, const char *filename)
+{
+       char *buffer;
+       FILE *passwd;
+       size_t len;
+       Bounds b;
+       struct stat statbuf;
+
+       /* load into buffer */
+       passwd = fopen(filename, "r");
+       if (!passwd) {
+               return 1;
+       }
+       stat(filename, &statbuf);
+       len = statbuf.st_size;
+       buffer = (char *) malloc(len * sizeof(char));
+
+       if (!buffer) {
+               fclose(passwd);
+               return 1;
+       }
+       fread(buffer, len, sizeof(char), passwd);
+
+       fclose(passwd);
+
+       /* find the user to remove */
+       b = boundary(buffer, login);
+       if (b.stop == 0) {
+               free(buffer);
+               return 1;
+       }
+
+       /* write the file w/o the user */
+       passwd = fopen(filename, "w");
+       if (!passwd) {
+               return 1;
+       }
+       fwrite(buffer, (b.start - 1), sizeof(char), passwd);
+       fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd);
+
+       fclose(passwd);
+
+       return 0;
+}
+
+
+/* $Id: delline.c,v 1.2 2003/07/14 21:50:51 andersen Exp $ */
diff --git a/busybox/loginutils/deluser.c b/busybox/loginutils/deluser.c
new file mode 100644 (file)
index 0000000..1cd2b01
--- /dev/null
@@ -0,0 +1,68 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * deluser (remove lusers from the system ;) for TinyLogin
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "busybox.h"
+
+
+#include "delline.c"
+
+static const char deluser_format[]="%s: User could not be removed from %s";
+
+int deluser_main(int argc, char **argv)
+{
+       /* int successful; */
+       int failure;
+
+       if (argc != 2) {
+               bb_show_usage();
+       } else {
+
+               failure = del_line_matching(argv[1], bb_path_passwd_file);
+               if (failure) {
+                       bb_error_msg_and_die(deluser_format, argv[1], bb_path_passwd_file);
+               }
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+               failure = del_line_matching(argv[1], bb_path_shadow_file);
+               if (failure) {
+                       bb_error_msg_and_die(deluser_format, argv[1], bb_path_shadow_file);
+               }
+               failure = del_line_matching(argv[1], bb_path_gshadow_file);
+               if (failure) {
+                       bb_error_msg_and_die(deluser_format, argv[1], bb_path_gshadow_file);
+               }
+#endif
+               failure = del_line_matching(argv[1], bb_path_group_file);
+               if (failure) {
+                       bb_error_msg_and_die(deluser_format, argv[1], bb_path_group_file);
+               }
+
+       }
+       return (EXIT_SUCCESS);
+}
+
+/* $Id: deluser.c,v 1.4 2003/07/14 20:20:45 andersen Exp $ */
diff --git a/busybox/loginutils/getty.c b/busybox/loginutils/getty.c
new file mode 100644 (file)
index 0000000..923432b
--- /dev/null
@@ -0,0 +1,1020 @@
+/* vi: set sw=4 ts=4: */
+/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
+   Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
+   This program is freely distributable. The entire man-page used to
+   be here. Now read the real man-page agetty.8 instead.
+
+   -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
+
+   1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+   - added Native Language Support
+
+   1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
+   - enable hardware flow control before displaying /etc/issue
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <getopt.h>
+#include <termios.h>
+#include "busybox.h"
+
+#define _PATH_LOGIN     "/bin/login"
+
+ /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
+#ifdef CONFIG_SYSLOGD
+#include <sys/param.h>
+#define USE_SYSLOG
+#include <syslog.h>
+#endif
+
+
+ /*
+  * Some heuristics to find out what environment we are in: if it is not
+  * System V, assume it is SunOS 4.
+  */
+
+#ifdef LOGIN_PROCESS                   /* defined in System V utmp.h */
+#define        SYSV_STYLE                              /* select System V style getty */
+#ifdef CONFIG_FEATURE_U_W_TMP
+extern void updwtmp(const char *filename, const struct utmp *ut);
+#endif
+#endif  /* LOGIN_PROCESS */
+
+ /*
+  * Things you may want to modify.
+  *
+  * If ISSUE is not defined, agetty will never display the contents of the
+  * /etc/issue file. You will not want to spit out large "issue" files at the
+  * wrong baud rate. Relevant for System V only.
+  *
+  * You may disagree with the default line-editing etc. characters defined
+  * below. Note, however, that DEL cannot be used for interrupt generation
+  * and for line editing at the same time.
+  */
+
+#ifdef SYSV_STYLE
+#define        ISSUE "/etc/issue"              /* displayed before the login prompt */
+#include <sys/utsname.h>
+#include <time.h>
+#endif
+
+/* Some shorthands for control characters. */
+
+#define CTL(x)         (x ^ 0100)      /* Assumes ASCII dialect */
+#define        CR              CTL('M')                /* carriage return */
+#define        NL              CTL('J')                /* line feed */
+#define        BS              CTL('H')                /* back space */
+#define        DEL             CTL('?')                /* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change this. */
+
+#define DEF_ERASE      DEL                     /* default erase character */
+#define DEF_INTR       CTL('C')        /* default interrupt character */
+#define DEF_QUIT       CTL('\\')       /* default quit char */
+#define DEF_KILL       CTL('U')        /* default kill char */
+#define DEF_EOF                CTL('D')        /* default EOF char */
+#define DEF_EOL                0
+#define DEF_SWITCH     0                       /* default switch char */
+
+ /*
+  * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
+  * because the termio -> termios translation does not clear the termios
+  * CIBAUD bits. Therefore, the tty driver would sometimes report that input
+  * baud rate != output baud rate. I did not notice that problem with SunOS
+  * 4.1. We will use termios where available, and termio otherwise.
+  */
+
+/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
+   properly, but all is well if we use termios?! */
+
+#ifdef TCGETS
+#undef TCGETA
+#undef TCSETA
+#undef TCSETAW
+#define        termio  termios
+#define        TCGETA  TCGETS
+#define        TCSETA  TCSETS
+#define        TCSETAW TCSETSW
+#endif
+
+ /*
+  * This program tries to not use the standard-i/o library.  This keeps the
+  * executable small on systems that do not have shared libraries (System V
+  * Release <3).
+  */
+#ifndef BUFSIZ
+#define        BUFSIZ          1024
+#endif
+
+ /*
+  * When multiple baud rates are specified on the command line, the first one
+  * we will try is the first one specified.
+  */
+
+#define        FIRST_SPEED     0
+
+/* Storage for command-line options. */
+
+#define        MAX_SPEED       10                      /* max. nr. of baud rates */
+
+struct options {
+       int flags;                                      /* toggle switches, see below */
+       int timeout;                            /* time-out period */
+       char *login;                            /* login program */
+       char *tty;                                      /* name of tty */
+       char *initstring;                       /* modem init string */
+       char *issue;                            /* alternative issue file */
+       int numspeed;                           /* number of baud rates to try */
+       int speeds[MAX_SPEED];          /* baud rates to be tried */
+};
+
+#define        F_PARSE         (1<<0)          /* process modem status messages */
+#define        F_ISSUE         (1<<1)          /* display /etc/issue */
+#define        F_RTSCTS        (1<<2)          /* enable RTS/CTS flow control */
+#define F_LOCAL                (1<<3)          /* force local */
+#define F_INITSTRING    (1<<4) /* initstring is set */
+#define F_WAITCRLF     (1<<5)          /* wait for CR or LF */
+#define F_CUSTISSUE    (1<<6)          /* give alternative issue file */
+#define F_NOPROMPT     (1<<7)          /* don't ask for login name! */
+
+/* Storage for things detected while the login name was read. */
+
+struct chardata {
+       int erase;                                      /* erase character */
+       int kill;                                       /* kill character */
+       int eol;                                        /* end-of-line character */
+       int parity;                                     /* what parity did we see */
+       int capslock;                           /* upper case without lower case */
+};
+
+/* Initial values for the above. */
+
+struct chardata init_chardata = {
+       DEF_ERASE,                                      /* default erase character */
+       DEF_KILL,                                       /* default kill character */
+       13,                                                     /* default eol char */
+       0,                                                      /* space parity */
+       0,                                                      /* no capslock */
+};
+
+#if 0
+struct Speedtab {
+       long speed;
+       int code;
+};
+
+static struct Speedtab 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, EXTB},
+#endif
+#ifdef B57600
+       {57600, B57600},
+#endif
+#ifdef B115200
+       {115200, B115200},
+#endif
+#ifdef B230400
+       {230400, B230400},
+#endif
+       {0, 0},
+};
+#endif
+
+static void parse_args(int argc, char **argv, struct options *op);
+static void parse_speeds(struct options *op, char *arg);
+static void open_tty(char *tty, struct termio *tp, int local);
+static void termio_init(struct termio *tp, int speed, struct options *op);
+static void auto_baud(struct termio *tp);
+static void do_prompt(struct options *op, struct termio *tp);
+static void next_speed(struct termio *tp, struct options *op);
+static char *get_logname(struct options *op, struct chardata *cp,
+
+                                 struct termio *tp);
+static void termio_final(struct options *op, struct termio *tp,
+
+                                 struct chardata *cp);
+static int caps_lock(const char *s);
+static int bcode(char *s);
+static void error(const char *fmt, ...) __attribute__ ((noreturn));
+
+#ifdef CONFIG_FEATURE_U_W_TMP
+static void update_utmp(char *line);
+#endif
+
+/* The following is used for understandable diagnostics. */
+
+/* Fake hostname for ut_host specified on command line. */
+static char *fakehost = NULL;
+
+/* ... */
+#ifdef DEBUGGING
+#define debug(s) fprintf(dbf,s); fflush(dbf)
+#define DEBUGTERM "/dev/ttyp0"
+FILE *dbf;
+#else
+#define debug(s)                               /* nothing */
+#endif
+
+int getty_main(int argc, char **argv)
+{
+       char *logname = NULL;           /* login name, given to /bin/login */
+       struct chardata chardata;       /* set by get_logname() */
+       struct termio termio;           /* terminal mode bits */
+       static struct options options = {
+               F_ISSUE,                                /* show /etc/issue (SYSV_STYLE) */
+               0,                                              /* no timeout */
+               _PATH_LOGIN,                    /* default login program */
+               "tty1",                                 /* default tty line */
+               "",                                             /* modem init string */
+               ISSUE,                                  /* default issue file */
+               0,                                              /* no baud rates known yet */
+       };
+
+#ifdef DEBUGGING
+       dbf = bb_xfopen(DEBUGTERM, "w");
+
+       {
+               int i;
+
+               for (i = 1; i < argc; i++) {
+                       debug(argv[i]);
+                       debug("\n");
+               }
+       }
+#endif
+
+       /* Parse command-line arguments. */
+
+       parse_args(argc, argv, &options);
+
+#ifdef __linux__
+       setsid();
+#endif
+
+       /* Update the utmp file. */
+
+
+#ifdef SYSV_STYLE
+#ifdef CONFIG_FEATURE_U_W_TMP
+       update_utmp(options.tty);
+#endif
+#endif
+
+       debug("calling open_tty\n");
+       /* Open the tty as standard { input, output, error }. */
+       open_tty(options.tty, &termio, options.flags & F_LOCAL);
+
+#ifdef __linux__
+       {
+               int iv;
+
+               iv = getpid();
+               ioctl(0, TIOCSPGRP, &iv);
+       }
+#endif
+       /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
+       debug("calling termio_init\n");
+       termio_init(&termio, options.speeds[FIRST_SPEED], &options);
+
+       /* write the modem init string and DON'T flush the buffers */
+       if (options.flags & F_INITSTRING) {
+               debug("writing init string\n");
+               write(1, options.initstring, strlen(options.initstring));
+       }
+
+       if (!(options.flags & F_LOCAL)) {
+               /* go to blocking write mode unless -L is specified */
+               fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
+       }
+
+       /* Optionally detect the baud rate from the modem status message. */
+       debug("before autobaud\n");
+       if (options.flags & F_PARSE)
+               auto_baud(&termio);
+
+       /* Set the optional timer. */
+       if (options.timeout)
+               (void) alarm((unsigned) options.timeout);
+
+       /* optionally wait for CR or LF before writing /etc/issue */
+       if (options.flags & F_WAITCRLF) {
+               char ch;
+
+               debug("waiting for cr-lf\n");
+               while (read(0, &ch, 1) == 1) {
+                       ch &= 0x7f;                     /* strip "parity bit" */
+#ifdef DEBUGGING
+                       fprintf(dbf, "read %c\n", ch);
+#endif
+                       if (ch == '\n' || ch == '\r')
+                               break;
+               }
+       }
+
+       chardata = init_chardata;
+       if (!(options.flags & F_NOPROMPT)) {
+               /* Read the login name. */
+               debug("reading login name\n");
+               /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */
+               while ((logname = get_logname(&options, &chardata, &termio)) ==
+                          NULL) next_speed(&termio, &options);
+       }
+
+       /* Disable timer. */
+
+       if (options.timeout)
+               (void) alarm(0);
+
+       /* Finalize the termio settings. */
+
+       termio_final(&options, &termio, &chardata);
+
+       /* Now the newline character should be properly written. */
+
+       (void) write(1, "\n", 1);
+
+       /* Let the login program take care of password validation. */
+
+       (void) execl(options.login, options.login, "--", logname, (char *) 0);
+       error("%s: can't exec %s: %m", options.tty, options.login);
+}
+
+/* parse-args - parse command-line arguments */
+
+static void parse_args(int argc, char **argv, struct options *op)
+{
+       extern char *optarg;            /* getopt */
+       extern int optind;                      /* getopt */
+       int c;
+
+       while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) {
+               switch (c) {
+               case 'I':
+                       if (!(op->initstring = strdup(optarg)))
+                               error(bb_msg_memory_exhausted);
+
+                       {
+                               const char *p;
+                               char *q;
+
+                               /* copy optarg into op->initstring decoding \ddd
+                                  octal codes into chars */
+                               q = op->initstring;
+                               p = optarg;
+                               while (*p) {
+                                       if (*p == '\\') {
+                                               p++;
+                                               *q++ = bb_process_escape_sequence(&p);
+                                       } else {
+                                               *q++ = *p++;
+                                       }
+                               }
+                               *q = '\0';
+                       }
+                       op->flags |= F_INITSTRING;
+                       break;
+
+               case 'L':                               /* force local */
+                       op->flags |= F_LOCAL;
+                       break;
+               case 'H':                               /* fake login host */
+                       fakehost = optarg;
+                       break;
+               case 'f':                               /* custom issue file */
+                       op->flags |= F_CUSTISSUE;
+                       op->issue = optarg;
+                       break;
+               case 'h':                               /* enable h/w flow control */
+                       op->flags |= F_RTSCTS;
+                       break;
+               case 'i':                               /* do not show /etc/issue */
+                       op->flags &= ~F_ISSUE;
+                       break;
+               case 'l':
+                       op->login = optarg;     /* non-default login program */
+                       break;
+               case 'm':                               /* parse modem status message */
+                       op->flags |= F_PARSE;
+                       break;
+               case 'n':
+                       op->flags |= F_NOPROMPT;
+                       break;
+               case 't':                               /* time out */
+                       if ((op->timeout = atoi(optarg)) <= 0)
+                               error("bad timeout value: %s", optarg);
+                       break;
+               case 'w':
+                       op->flags |= F_WAITCRLF;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+       debug("after getopt loop\n");
+       if (argc < optind + 2)          /* check parameter count */
+               bb_show_usage();
+
+       /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+       if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
+               /* a number first, assume it's a speed (BSD style) */
+               parse_speeds(op, argv[optind++]);       /* baud rate(s) */
+               op->tty = argv[optind]; /* tty name */
+       } else {
+               op->tty = argv[optind++];       /* tty name */
+               parse_speeds(op, argv[optind]); /* baud rate(s) */
+       }
+
+       optind++;
+       if (argc > optind && argv[optind])
+               setenv("TERM", argv[optind], 1);
+
+       debug("exiting parseargs\n");
+}
+
+/* parse_speeds - parse alternate baud rates */
+
+static void parse_speeds(struct options *op, char *arg)
+{
+       char *cp;
+
+       debug("entered parse_speeds\n");
+       for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
+               if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
+                       error("bad speed: %s", cp);
+               if (op->numspeed > MAX_SPEED)
+                       error("too many alternate speeds");
+       }
+       debug("exiting parsespeeds\n");
+}
+
+#ifdef SYSV_STYLE
+#ifdef CONFIG_FEATURE_U_W_TMP
+
+/* update_utmp - update our utmp entry */
+static void update_utmp(char *line)
+{
+       struct utmp ut;
+       struct utmp *utp;
+       time_t t;
+       int mypid = getpid();
+#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
+       struct flock lock;
+#endif
+
+       /*
+        * The utmp file holds miscellaneous information about things started by
+        * /sbin/init and other system-related events. Our purpose is to update
+        * the utmp entry for the current process, in particular the process type
+        * and the tty line we are listening to. Return successfully only if the
+        * utmp file can be opened for update, and if we are able to find our
+        * entry in the utmp file.
+        */
+       if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
+               close(creat(_PATH_UTMP, 0664));
+       }
+       utmpname(_PATH_UTMP);
+       setutent();
+       while ((utp = getutent())
+                  && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid))  /* nothing */
+               ;
+
+       if (utp) {
+               memcpy(&ut, utp, sizeof(ut));
+       } else {
+               /* some inits don't initialize utmp... */
+               memset(&ut, 0, sizeof(ut));
+               strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+       }
+       /*endutent(); */
+
+       strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
+       strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+       if (fakehost)
+               strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
+       time(&t);
+       ut.ut_time = t;
+       ut.ut_type = LOGIN_PROCESS;
+       ut.ut_pid = mypid;
+
+       pututline(&ut);
+       endutent();
+
+       {
+               if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
+                       close(creat(_PATH_WTMP, 0664));
+               }
+               updwtmp(_PATH_WTMP, &ut);
+       }
+}
+
+#endif /* CONFIG_FEATURE_U_W_TMP */
+#endif /* SYSV_STYLE */
+
+/* open_tty - set up tty as standard { input, output, error } */
+static void open_tty(char *tty, struct termio *tp, int local)
+{
+       /* Get rid of the present standard { output, error} if any. */
+
+       (void) close(1);
+       (void) close(2);
+       errno = 0;                                      /* ignore above errors */
+
+       /* Set up new standard input, unless we are given an already opened port. */
+
+       if (strcmp(tty, "-")) {
+               struct stat st;
+
+               /* Sanity checks... */
+
+               if (chdir("/dev"))
+                       error("/dev: chdir() failed: %m");
+               if (stat(tty, &st) < 0)
+                       error("/dev/%s: %m", tty);
+               if ((st.st_mode & S_IFMT) != S_IFCHR)
+                       error("/dev/%s: not a character device", tty);
+
+               /* Open the tty as standard input. */
+
+               (void) close(0);
+               errno = 0;                              /* ignore close(2) errors */
+
+               debug("open(2)\n");
+               if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0)
+                       error("/dev/%s: cannot open as standard input: %m", tty);
+
+       } else {
+
+               /*
+                * Standard input should already be connected to an open port. Make
+                * sure it is open for read/write.
+                */
+
+               if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
+                       error("%s: not open for read/write", tty);
+       }
+
+       /* Set up standard output and standard error file descriptors. */
+       debug("duping\n");
+       if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
+               error("%s: dup problem: %m", tty);      /* we have a problem */
+
+       /*
+        * The following ioctl will fail if stdin is not a tty, but also when
+        * there is noise on the modem control lines. In the latter case, the
+        * common course of action is (1) fix your cables (2) give the modem more
+        * time to properly reset after hanging up. SunOS users can achieve (2)
+        * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
+        * 5 seconds seems to be a good value.
+        */
+
+       if (ioctl(0, TCGETA, tp) < 0)
+               error("%s: ioctl: %m", tty);
+
+       /*
+        * It seems to be a terminal. Set proper protections and ownership. Mode
+        * 0622 is suitable for SYSV <4 because /bin/login does not change
+        * protections. SunOS 4 login will change the protections to 0620 (write
+        * access for group tty) after the login has succeeded.
+        */
+
+#ifdef DEBIAN
+       {
+               /* tty to root.dialout 660 */
+               struct group *gr;
+               int id;
+
+               id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0;
+               chown(tty, 0, id);
+               chmod(tty, 0660);
+
+               /* vcs,vcsa to root.sys 600 */
+               if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
+                       char *vcs, *vcsa;
+
+                       if (!(vcs = strdup(tty)))
+                               error("Can't malloc for vcs");
+                       if (!(vcsa = malloc(strlen(tty) + 2)))
+                               error("Can't malloc for vcsa");
+                       strcpy(vcs, "vcs");
+                       strcpy(vcs + 3, tty + 3);
+                       strcpy(vcsa, "vcsa");
+                       strcpy(vcsa + 4, tty + 3);
+
+                       id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
+                       chown(vcs, 0, id);
+                       chmod(vcs, 0600);
+                       chown(vcsa, 0, id);
+                       chmod(vcs, 0600);
+
+                       free(vcs);
+                       free(vcsa);
+               }
+       }
+#else
+       (void) chown(tty, 0, 0);        /* root, sys */
+       (void) chmod(tty, 0622);        /* crw--w--w- */
+       errno = 0;                                      /* ignore above errors */
+#endif
+}
+
+/* termio_init - initialize termio settings */
+
+static void termio_init(struct termio *tp, int speed, struct options *op)
+{
+
+       /*
+        * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
+        * Special characters are set after we have read the login name; all
+        * reads will be done in raw mode anyway. Errors will be dealt with
+        * lateron.
+        */
+#ifdef __linux__
+       /* flush input and output queues, important for modems! */
+       (void) ioctl(0, TCFLSH, TCIOFLUSH);
+#endif
+
+       tp->c_cflag = CS8 | HUPCL | CREAD | speed;
+       if (op->flags & F_LOCAL) {
+               tp->c_cflag |= CLOCAL;
+       }
+
+       tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
+       tp->c_cc[VMIN] = 1;
+       tp->c_cc[VTIME] = 0;
+
+       /* Optionally enable hardware flow control */
+
+#ifdef CRTSCTS
+       if (op->flags & F_RTSCTS)
+               tp->c_cflag |= CRTSCTS;
+#endif
+
+       (void) ioctl(0, TCSETA, tp);
+
+       /* go to blocking input even in local mode */
+       fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
+
+       debug("term_io 2\n");
+}
+
+/* auto_baud - extract baud rate from modem status message */
+static void auto_baud(struct termio *tp)
+{
+       int speed;
+       int vmin;
+       unsigned iflag;
+       char buf[BUFSIZ];
+       char *bp;
+       int nread;
+
+       /*
+        * This works only if the modem produces its status code AFTER raising
+        * the DCD line, and if the computer is fast enough to set the proper
+        * baud rate before the message has gone by. We expect a message of the
+        * following format:
+        *
+        * <junk><number><junk>
+        *
+        * The number is interpreted as the baud rate of the incoming call. If the
+        * modem does not tell us the baud rate within one second, we will keep
+        * using the current baud rate. It is advisable to enable BREAK
+        * processing (comma-separated list of baud rates) if the processing of
+        * modem status messages is enabled.
+        */
+
+       /*
+        * Use 7-bit characters, don't block if input queue is empty. Errors will
+        * be dealt with lateron.
+        */
+
+       iflag = tp->c_iflag;
+       tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */
+       vmin = tp->c_cc[VMIN];
+       tp->c_cc[VMIN] = 0;                     /* don't block if queue empty */
+       (void) ioctl(0, TCSETA, tp);
+
+       /*
+        * Wait for a while, then read everything the modem has said so far and
+        * try to extract the speed of the dial-in call.
+        */
+
+       (void) sleep(1);
+       if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
+               buf[nread] = '\0';
+               for (bp = buf; bp < buf + nread; bp++) {
+                       if (isascii(*bp) && isdigit(*bp)) {
+                               if ((speed = bcode(bp))) {
+                                       tp->c_cflag &= ~CBAUD;
+                                       tp->c_cflag |= speed;
+                               }
+                               break;
+                       }
+               }
+       }
+       /* Restore terminal settings. Errors will be dealt with lateron. */
+
+       tp->c_iflag = iflag;
+       tp->c_cc[VMIN] = vmin;
+       (void) ioctl(0, TCSETA, tp);
+}
+
+/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
+static void do_prompt(struct options *op, struct termio *tp)
+{
+#ifdef ISSUE                                   /* optional: show /etc/issue */
+       print_login_issue(op->issue, op->tty);
+#endif
+       print_login_prompt();
+}
+
+/* next_speed - select next baud rate */
+static void next_speed(struct termio *tp, struct options *op)
+{
+       static int baud_index = FIRST_SPEED;    /* current speed index */
+
+       baud_index = (baud_index + 1) % op->numspeed;
+       tp->c_cflag &= ~CBAUD;
+       tp->c_cflag |= op->speeds[baud_index];
+       (void) ioctl(0, TCSETA, tp);
+}
+
+/* get_logname - get user name, establish parity, speed, erase, kill, eol */
+/* return NULL on failure, logname on success */
+static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp)
+{
+       static char logname[BUFSIZ];
+       char *bp;
+       char c;                                         /* input character, full eight bits */
+       char ascval;                            /* low 7 bits of input character */
+       int bits;                                       /* # of "1" bits per character */
+       int mask;                                       /* mask with 1 bit up */
+       static char *erase[] = {        /* backspace-space-backspace */
+               "\010\040\010",                 /* space parity */
+               "\010\040\010",                 /* odd parity */
+               "\210\240\210",                 /* even parity */
+               "\210\240\210",                 /* no parity */
+       };
+
+       /* Initialize kill, erase, parity etc. (also after switching speeds). */
+
+       *cp = init_chardata;
+
+       /* Flush pending input (esp. after parsing or switching the baud rate). */
+
+       (void) sleep(1);
+       (void) ioctl(0, TCFLSH, TCIFLUSH);
+
+       /* Prompt for and read a login name. */
+
+       for (*logname = 0; *logname == 0; /* void */ ) {
+
+               /* Write issue file and prompt, with "parity" bit == 0. */
+
+               do_prompt(op, tp);
+
+               /* Read name, watch for break, parity, erase, kill, end-of-line. */
+
+               for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
+
+                       /* Do not report trivial EINTR/EIO errors. */
+
+                       if (read(0, &c, 1) < 1) {
+                               if (errno == EINTR || errno == EIO)
+                                       exit(0);
+                               error("%s: read: %m", op->tty);
+                       }
+                       /* Do BREAK handling elsewhere. */
+
+                       if ((c == 0) && op->numspeed > 1)
+                               /* return (0); */
+                               return NULL;
+
+                       /* Do parity bit handling. */
+
+                       if (c != (ascval = (c & 0177))) {       /* "parity" bit on ? */
+                               for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
+                                       if (mask & ascval)
+                                               bits++; /* count "1" bits */
+                               cp->parity |= ((bits & 1) ? 1 : 2);
+                       }
+                       /* Do erase, kill and end-of-line processing. */
+
+                       switch (ascval) {
+                       case CR:
+                       case NL:
+                               *bp = 0;                /* terminate logname */
+                               cp->eol = ascval;       /* set end-of-line char */
+                               break;
+                       case BS:
+                       case DEL:
+                       case '#':
+                               cp->erase = ascval;     /* set erase character */
+                               if (bp > logname) {
+                                       (void) write(1, erase[cp->parity], 3);
+                                       bp--;
+                               }
+                               break;
+                       case CTL('U'):
+                       case '@':
+                               cp->kill = ascval;      /* set kill character */
+                               while (bp > logname) {
+                                       (void) write(1, erase[cp->parity], 3);
+                                       bp--;
+                               }
+                               break;
+                       case CTL('D'):
+                               exit(0);
+                       default:
+                               if (!isascii(ascval) || !isprint(ascval)) {
+                                       /* ignore garbage characters */ ;
+                               } else if (bp - logname >= sizeof(logname) - 1) {
+                                       error("%s: input overrun", op->tty);
+                               } else {
+                                       (void) write(1, &c, 1); /* echo the character */
+                                       *bp++ = ascval; /* and store it */
+                               }
+                               break;
+                       }
+               }
+       }
+       /* Handle names with upper case and no lower case. */
+
+       if ((cp->capslock = caps_lock(logname))) {
+               for (bp = logname; *bp; bp++)
+                       if (isupper(*bp))
+                               *bp = tolower(*bp);     /* map name to lower case */
+       }
+       return (logname);
+}
+
+/* termio_final - set the final tty mode bits */
+static void termio_final(struct options *op, struct termio *tp, struct chardata *cp)
+{
+       /* General terminal-independent stuff. */
+
+       tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */
+       tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
+       /* no longer| ECHOCTL | ECHOPRT */
+       tp->c_oflag |= OPOST;
+       /* tp->c_cflag = 0; */
+       tp->c_cc[VINTR] = DEF_INTR;     /* default interrupt */
+       tp->c_cc[VQUIT] = DEF_QUIT;     /* default quit */
+       tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
+       tp->c_cc[VEOL] = DEF_EOL;
+       tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
+
+       /* Account for special characters seen in input. */
+
+       if (cp->eol == CR) {
+               tp->c_iflag |= ICRNL;   /* map CR in input to NL */
+               tp->c_oflag |= ONLCR;   /* map NL in output to CR-NL */
+       }
+       tp->c_cc[VERASE] = cp->erase;   /* set erase character */
+       tp->c_cc[VKILL] = cp->kill;     /* set kill character */
+
+       /* Account for the presence or absence of parity bits in input. */
+
+       switch (cp->parity) {
+       case 0:                                 /* space (always 0) parity */
+               break;
+       case 1:                                 /* odd parity */
+               tp->c_cflag |= PARODD;
+               /* FALLTHROUGH */
+       case 2:                                 /* even parity */
+               tp->c_cflag |= PARENB;
+               tp->c_iflag |= INPCK | ISTRIP;
+               /* FALLTHROUGH */
+       case (1 | 2):                           /* no parity bit */
+               tp->c_cflag &= ~CSIZE;
+               tp->c_cflag |= CS7;
+               break;
+       }
+       /* Account for upper case without lower case. */
+
+       if (cp->capslock) {
+               tp->c_iflag |= IUCLC;
+               tp->c_lflag |= XCASE;
+               tp->c_oflag |= OLCUC;
+       }
+       /* Optionally enable hardware flow control */
+
+#ifdef CRTSCTS
+       if (op->flags & F_RTSCTS)
+               tp->c_cflag |= CRTSCTS;
+#endif
+
+       /* Finally, make the new settings effective */
+
+       if (ioctl(0, TCSETA, tp) < 0)
+               error("%s: ioctl: TCSETA: %m", op->tty);
+}
+
+/* caps_lock - string contains upper case without lower case */
+/* returns 1 if true, 0 if false */
+static int caps_lock(const char *s)
+{
+       int capslock;
+
+       for (capslock = 0; *s; s++) {
+               if (islower(*s))
+                       return (0);
+               if (capslock == 0)
+                       capslock = isupper(*s);
+       }
+       return (capslock);
+}
+
+/* bcode - convert speed string to speed code; return 0 on failure */
+static int bcode(char *s)
+{
+       int r;
+       unsigned long value;
+       if (safe_strtoul(s, &value)) {
+               return -1;
+       }
+       if ((r = bb_value_to_baud(value)) > 0) {
+               return r;
+       }
+       return 0;
+}
+
+/* error - report errors to console or syslog; only understands %s and %m */
+
+#define        str2cpy(b,s1,s2)        strcat(strcpy(b,s1),s2)
+
+/*
+ * output error messages
+ */
+static void error(const char *fmt, ...)
+{
+       va_list va_alist;
+       char buf[256], *bp;
+
+#ifndef        USE_SYSLOG
+       int fd;
+#endif
+
+#ifdef USE_SYSLOG
+       buf[0] = '\0';
+       bp = buf;
+#else
+       strncpy(buf, bb_applet_name, 256);
+       strncat(buf, ": ", 256);
+       buf[255] = 0;
+       bp = buf + strlen(buf);
+#endif
+
+       va_start(va_alist, fmt);
+       vsnprintf(bp, 256 - strlen(buf), fmt, va_alist);
+       buf[255] = 0;
+       va_end(va_alist);
+
+#ifdef USE_SYSLOG
+       openlog(bb_applet_name, 0, LOG_AUTH);
+       syslog(LOG_ERR, "%s", buf);
+       closelog();
+#else
+       strncat(bp, "\r\n", 256 - strlen(buf));
+       buf[255] = 0;
+       if ((fd = open("/dev/console", 1)) >= 0) {
+               write(fd, buf, strlen(buf));
+               close(fd);
+       }
+#endif
+       (void) sleep((unsigned) 10);    /* be kind to init(8) */
+       exit(1);
+}
diff --git a/busybox/loginutils/login.c b/busybox/loginutils/login.c
new file mode 100644 (file)
index 0000000..f3630f1
--- /dev/null
@@ -0,0 +1,486 @@
+/* vi: set sw=4 ts=4: */
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "busybox.h"
+#ifdef CONFIG_SELINUX
+#include <flask_util.h>
+#include <get_sid_list.h>
+#include <proc_secure.h>
+#include <fs_secure.h>
+#endif
+
+#ifdef CONFIG_FEATURE_U_W_TMP
+// import from utmp.c
+static void checkutmp(int picky);
+static void setutmp(const char *name, const char *line);
+/* Stuff global to this file */
+struct utmp utent;
+#endif
+
+// login defines
+#define TIMEOUT       60
+#define EMPTY_USERNAME_COUNT    10
+#define USERNAME_SIZE 32
+
+
+static int check_nologin ( int amroot );
+
+#if defined CONFIG_FEATURE_SECURETTY
+static int check_tty ( const char *tty );
+
+#else
+static inline int check_tty ( const char *tty )  { return 1; }
+
+#endif
+
+static int is_my_tty ( const char *tty );
+static int login_prompt ( char *buf_name );
+static void motd ( void );
+
+
+static void alarm_handler ( int sig )
+{
+       fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
+       exit ( EXIT_SUCCESS );
+}
+
+
+extern int login_main(int argc, char **argv)
+{
+       char tty[BUFSIZ];
+       char full_tty[200];
+       char fromhost[512];
+       char username[USERNAME_SIZE];
+       const char *tmp;
+       int amroot;
+       int flag;
+       int failed;
+       int count=0;
+       struct passwd *pw, pw_copy;
+#ifdef CONFIG_WHEEL_GROUP
+       struct group *grp;
+#endif
+       int opt_preserve = 0;
+       int opt_fflag = 0;
+       char *opt_host = 0;
+       int alarmstarted = 0;
+#ifdef CONFIG_SELINUX
+       int flask_enabled = is_flask_enabled();
+       security_id_t sid = 0, old_tty_sid, new_tty_sid;
+#endif
+
+       username[0]=0;
+       amroot = ( getuid ( ) == 0 );
+       signal ( SIGALRM, alarm_handler );
+       alarm ( TIMEOUT );
+       alarmstarted = 1;
+
+       while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
+               switch ( flag ) {
+               case 'p':
+                       opt_preserve = 1;
+                       break;
+               case 'f':
+                       /*
+                        * username must be a separate token
+                        * (-f root, *NOT* -froot). --marekm
+                        */
+                       if ( optarg != argv[optind-1] )
+                               bb_show_usage( );
+
+                       if ( !amroot )          /* Auth bypass only if real UID is zero */
+                               bb_error_msg_and_die ( "-f permission denied" );
+
+                       safe_strncpy(username, optarg, USERNAME_SIZE);
+                       opt_fflag = 1;
+                       break;
+               case 'h':
+                       opt_host = optarg;
+                       break;
+               default:
+                       bb_show_usage( );
+               }
+       }
+
+       if (optind < argc)             // user from command line (getty)
+               safe_strncpy(username, argv[optind], USERNAME_SIZE);
+
+       if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
+               return EXIT_FAILURE;            /* Must be a terminal */
+
+#ifdef CONFIG_FEATURE_U_W_TMP
+       checkutmp ( !amroot );
+#endif
+
+       tmp = ttyname ( 0 );
+       if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
+               safe_strncpy ( tty, tmp + 5, sizeof( tty ));
+       else if ( tmp && *tmp == '/' )
+               safe_strncpy ( tty, tmp, sizeof( tty ));
+       else
+               safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
+
+#ifdef CONFIG_FEATURE_U_W_TMP
+       if ( amroot )
+               memset ( utent.ut_host, 0, sizeof utent.ut_host );
+#endif
+
+       if ( opt_host ) {
+#ifdef CONFIG_FEATURE_U_W_TMP
+               safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
+#endif
+               snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
+       }
+       else
+               snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
+
+       setpgrp();
+
+       openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
+
+       while ( 1 ) {
+               failed = 0;
+
+               if ( !username[0] )
+                       if(!login_prompt ( username ))
+                               return EXIT_FAILURE;
+
+               if ( !alarmstarted && ( TIMEOUT > 0 )) {
+                       alarm ( TIMEOUT );
+                       alarmstarted = 1;
+               }
+
+               if (!( pw = getpwnam ( username ))) {
+                       pw_copy.pw_name   = "UNKNOWN";
+                       pw_copy.pw_passwd = "!";
+                       opt_fflag = 0;
+                       failed = 1;
+               } else
+                       pw_copy = *pw;
+
+               pw = &pw_copy;
+
+               if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
+                       failed = 1;
+
+               if ( opt_fflag ) {
+                       opt_fflag = 0;
+                       goto auth_ok;
+               }
+
+               if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
+                       failed = 1;
+
+               /* Don't check the password if password entry is empty (!) */
+               if ( !pw-> pw_passwd[0] )
+                       goto auth_ok;
+
+               /* authorization takes place here */
+               if ( correct_password ( pw ))
+                       goto auth_ok;
+
+               failed = 1;
+
+auth_ok:
+               if ( !failed)
+                       break;
+
+               { // delay next try
+                       time_t start, now;
+
+                       time ( &start );
+                       now = start;
+                       while ( difftime ( now, start ) < FAIL_DELAY) {
+                               sleep ( FAIL_DELAY );
+                               time ( &now );
+                       }
+               }
+
+               puts("Login incorrect");
+               username[0] = 0;
+               if ( ++count == 3 ) {
+                       syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
+                       return EXIT_FAILURE;
+       }
+       }
+
+       alarm ( 0 );
+       if ( check_nologin ( pw-> pw_uid == 0 ))
+               return EXIT_FAILURE;
+
+#ifdef CONFIG_FEATURE_U_W_TMP
+       setutmp ( username, tty );
+#endif
+#ifdef CONFIG_SELINUX
+       if (flask_enabled)
+       {
+               struct stat st;
+
+               if (get_default_sid(username, 0, &sid))
+               {
+                       fprintf(stderr, "Unable to get SID for %s\n", username);
+                       exit(1);
+               }
+               if (stat_secure(tty, &st, &old_tty_sid))
+               {
+                       fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
+                       return EXIT_FAILURE;
+               }
+               if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
+               {
+                       fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
+                       return EXIT_FAILURE;
+               }
+               if(chsid(tty, new_tty_sid) != 0)
+               {
+                       fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
+                       return EXIT_FAILURE;
+               }
+       }
+       else
+               sid = 0;
+#endif
+
+       if ( *tty != '/' )
+               snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
+       else
+               safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
+
+       if ( !is_my_tty ( full_tty ))
+               syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
+
+       /* Try these, but don't complain if they fail
+        * (for example when the root fs is read only) */
+       chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
+       chmod ( full_tty, 0600 );
+
+       change_identity ( pw );
+       tmp = pw-> pw_shell;
+       if(!tmp || !*tmp)
+               tmp = DEFAULT_SHELL;
+       setup_environment ( tmp, 1, !opt_preserve, pw );
+
+       motd ( );
+       signal ( SIGALRM, SIG_DFL );    /* default alarm signal */
+
+       if ( pw-> pw_uid == 0 )
+               syslog ( LOG_INFO, "root login %s\n", fromhost );
+       run_shell ( tmp, 1, 0, 0
+#ifdef CONFIG_SELINUX
+       , sid
+#endif
+        );     /* exec the shell finally. */
+
+       return EXIT_FAILURE;
+}
+
+
+
+static int login_prompt ( char *buf_name )
+{
+       char buf [1024];
+       char *sp, *ep;
+       int i;
+
+       for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
+               print_login_prompt();
+
+               if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
+                       return 0;
+
+               if ( !strchr ( buf, '\n' ))
+                       return 0;
+
+               for ( sp = buf; isspace ( *sp ); sp++ ) { }
+               for ( ep = sp; isgraph ( *ep ); ep++ ) { }
+
+               *ep = 0;
+               safe_strncpy(buf_name, sp, USERNAME_SIZE);
+               if(buf_name[0])
+                       return 1;
+       }
+       return 0;
+}
+
+
+static int check_nologin ( int amroot )
+{
+       if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
+               FILE *fp;
+               int c;
+
+               if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
+                       while (( c = getc ( fp )) != EOF )
+                               putchar (( c == '\n' ) ? '\r' : c );
+
+                       fflush ( stdout );
+                       fclose ( fp );
+               } else {
+                       puts ( "\r\nSystem closed for routine maintenance.\r" );
+               }
+               if ( !amroot )
+                       return 1;
+
+               puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
+       }
+       return 0;
+}
+
+#ifdef CONFIG_FEATURE_SECURETTY
+
+static int check_tty ( const char *tty )
+{
+       FILE *fp;
+       int i;
+       char buf[BUFSIZ];
+
+       if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
+               while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
+                       for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
+                               if ( !isspace ( buf[i] ))
+                                       break;
+                       }
+                       buf[++i] = '\0';
+                       if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
+                               continue;
+
+                       if ( strcmp ( buf, tty ) == 0 ) {
+                               fclose ( fp );
+                               return 1;
+                       }
+               }
+               fclose(fp);
+               return 0;
+       }
+       /* A missing securetty file is not an error. */
+       return 1;
+}
+
+#endif
+
+/* returns 1 if true */
+static int is_my_tty ( const char *tty )
+{
+       struct stat by_name, by_fd;
+
+       if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
+               return 0;
+
+       if ( by_name. st_rdev != by_fd. st_rdev )
+               return 0;
+       else
+               return 1;
+}
+
+
+static void motd ( )
+{
+       FILE *fp;
+       register int c;
+
+       if (( fp = fopen ( bb_path_motd_file, "r" ))) {
+               while (( c = getc ( fp )) != EOF )
+                       putchar ( c );
+               fclose ( fp );
+       }
+}
+
+
+#ifdef CONFIG_FEATURE_U_W_TMP
+// vv  Taken from tinylogin utmp.c  vv
+
+#define        NO_UTENT \
+       "No utmp entry.  You must exec \"login\" from the lowest level \"sh\""
+#define        NO_TTY \
+       "Unable to determine your tty name."
+
+/*
+ * checkutmp - see if utmp file is correct for this process
+ *
+ *     System V is very picky about the contents of the utmp file
+ *     and requires that a slot for the current process exist.
+ *     The utmp file is scanned for an entry with the same process
+ *     ID.  If no entry exists the process exits with a message.
+ *
+ *     The "picky" flag is for network and other logins that may
+ *     use special flags.  It allows the pid checks to be overridden.
+ *     This means that getty should never invoke login with any
+ *     command line flags.
+ */
+
+static void checkutmp(int picky)
+{
+       char *line;
+       struct utmp *ut;
+       pid_t pid = getpid();
+
+       setutent();
+
+       /* First, try to find a valid utmp entry for this process.  */
+       while ((ut = getutent()))
+               if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
+                       (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
+                       break;
+
+       /* If there is one, just use it, otherwise create a new one.  */
+       if (ut) {
+               utent = *ut;
+       } else {
+               if (picky) {
+                       puts(NO_UTENT);
+                       exit(1);
+               }
+               line = ttyname(0);
+               if (!line) {
+                       puts(NO_TTY);
+                       exit(1);
+               }
+               if (strncmp(line, "/dev/", 5) == 0)
+                       line += 5;
+               memset((void *) &utent, 0, sizeof utent);
+               utent.ut_type = LOGIN_PROCESS;
+               utent.ut_pid = pid;
+               strncpy(utent.ut_line, line, sizeof utent.ut_line);
+               /* XXX - assumes /dev/tty?? */
+               strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
+               strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
+               time(&utent.ut_time);
+       }
+}
+
+/*
+ * setutmp - put a USER_PROCESS entry in the utmp file
+ *
+ *     setutmp changes the type of the current utmp entry to
+ *     USER_PROCESS.  the wtmp file will be updated as well.
+ */
+
+static void setutmp(const char *name, const char *line)
+{
+       utent.ut_type = USER_PROCESS;
+       strncpy(utent.ut_user, name, sizeof utent.ut_user);
+       time(&utent.ut_time);
+       /* other fields already filled in by checkutmp above */
+       setutent();
+       pututline(&utent);
+       endutent();
+       if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
+               close(creat(_PATH_WTMP, 0664));
+       }
+       updwtmp(_PATH_WTMP, &utent);
+}
+#endif /* CONFIG_FEATURE_U_W_TMP */
diff --git a/busybox/loginutils/passwd.c b/busybox/loginutils/passwd.c
new file mode 100644 (file)
index 0000000..9c4b4dd
--- /dev/null
@@ -0,0 +1,400 @@
+/* vi: set sw=4 ts=4: */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utime.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/resource.h>
+#include <errno.h>
+
+#include "busybox.h"
+
+static char crypt_passwd[128];
+
+static int create_backup(const char *backup, FILE * fp);
+static int new_password(const struct passwd *pw, int amroot, int algo);
+static void set_filesize_limit(int blocks);
+
+
+int get_algo(char *a)
+{
+       int x = 1;                                      /* standard: MD5 */
+
+       if (strcasecmp(a, "des") == 0)
+               x = 0;
+       return x;
+}
+
+
+extern int update_passwd(const struct passwd *pw, char *crypt_pw)
+{
+       char filename[1024];
+       char buf[1025];
+       char buffer[80];
+       char username[32];
+       char *pw_rest;
+       int mask;
+       int continued;
+       FILE *fp;
+       FILE *out_fp;
+       struct stat sb;
+       struct flock lock;
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       if (access(bb_path_shadow_file, F_OK) == 0) {
+               snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
+       } else
+#endif
+       {
+               snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
+       }
+
+       if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
+               /* return 0; */
+               return 1;
+       }
+
+       /* Lock the password file before updating */
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 0;
+       if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
+               fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+               return 1;
+       }
+       lock.l_type = F_UNLCK;
+
+       snprintf(buf, sizeof buf, "%s-", filename);
+       if (create_backup(buf, fp)) {
+               fcntl(fileno(fp), F_SETLK, &lock);
+               fclose(fp);
+               return 1;
+       }
+       snprintf(buf, sizeof buf, "%s+", filename);
+       mask = umask(0777);
+       out_fp = fopen(buf, "w");
+       umask(mask);
+       if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
+               || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
+               fcntl(fileno(fp), F_SETLK, &lock);
+               fclose(fp);
+               fclose(out_fp);
+               return 1;
+       }
+
+       continued = 0;
+       snprintf(username, sizeof username, "%s:", pw->pw_name);
+       rewind(fp);
+       while (!feof(fp)) {
+               fgets(buffer, sizeof buffer, fp);
+               if (!continued) {               // Check to see if we're updating this line.
+                       if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match.
+                               pw_rest = strchr(buffer, ':');
+                               *pw_rest++ = '\0';
+                               pw_rest = strchr(pw_rest, ':');
+                               fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
+                       } else {
+                               fputs(buffer, out_fp);
+                       }
+               } else {
+                       fputs(buffer, out_fp);
+               }
+               if (buffer[strlen(buffer) - 1] == '\n') {
+                       continued = 0;
+               } else {
+                       continued = 1;
+               }
+               bzero(buffer, sizeof buffer);
+       }
+
+       if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
+               unlink(buf);
+               fcntl(fileno(fp), F_SETLK, &lock);
+               fclose(fp);
+               return 1;
+       }
+       if (rename(buf, filename) < 0) {
+               fcntl(fileno(fp), F_SETLK, &lock);
+               fclose(fp);
+               return 1;
+       } else {
+               fcntl(fileno(fp), F_SETLK, &lock);
+               fclose(fp);
+               return 0;
+       }
+}
+
+
+extern int passwd_main(int argc, char **argv)
+{
+       int amroot;
+       char *cp;
+       char *np;
+       char *name;
+       char *myname;
+       int flag;
+       int algo = 1;                           /* -a - password algorithm */
+       int lflg = 0;                           /* -l - lock account */
+       int uflg = 0;                           /* -u - unlock account */
+       int dflg = 0;                           /* -d - delete password */
+       const struct passwd *pw;
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       const struct spwd *sp;
+#endif                                                 /* CONFIG_FEATURE_SHADOWPASSWDS */
+       amroot = (getuid() == 0);
+       openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
+       while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
+               switch (flag) {
+               case 'a':
+                       algo = get_algo(optarg);
+                       break;
+               case 'd':
+                       dflg++;
+                       break;
+               case 'l':
+                       lflg++;
+                       break;
+               case 'u':
+                       uflg++;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+       myname = (char *) bb_xstrdup(my_getpwuid(NULL, getuid(), -1));
+       /* exits on error */
+       if (optind < argc) {
+               name = argv[optind];
+       } else {
+               name = myname;
+       }
+       if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
+               bb_show_usage();
+       }
+       pw = getpwnam(name);
+       if (!pw) {
+               bb_error_msg_and_die("Unknown user %s\n", name);
+       }
+       if (!amroot && pw->pw_uid != getuid()) {
+               syslog(LOG_WARNING, "can't change pwd for `%s'", name);
+               bb_error_msg_and_die("Permission denied.\n");
+       }
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       sp = getspnam(name);
+       if (!sp) {
+               sp = (struct spwd *) pwd_to_spwd(pw);
+       }
+       cp = sp->sp_pwdp;
+       np = sp->sp_namp;
+#else
+       cp = pw->pw_passwd;
+       np = name;
+#endif                                                 /* CONFIG_FEATURE_SHADOWPASSWDS */
+
+       safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
+       if (!(dflg || lflg || uflg)) {
+               if (!amroot) {
+                       if (cp[0] == '!') {
+                               syslog(LOG_WARNING, "password locked for `%s'", np);
+                               bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
+                       }
+               }
+               printf("Changing password for %s\n", name);
+               if (new_password(pw, amroot, algo)) {
+                       bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
+               }
+       } else if (lflg) {
+               if (crypt_passwd[0] != '!') {
+                       memmove(&crypt_passwd[1], crypt_passwd,
+                                       sizeof crypt_passwd - 1);
+                       crypt_passwd[sizeof crypt_passwd - 1] = '\0';
+                       crypt_passwd[0] = '!';
+               }
+       } else if (uflg) {
+               if (crypt_passwd[0] == '!') {
+                       memmove(crypt_passwd, &crypt_passwd[1],
+                                       sizeof crypt_passwd - 1);
+               }
+       } else if (dflg) {
+               crypt_passwd[0] = '\0';
+       }
+       set_filesize_limit(30000);
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_IGN);
+       umask(077);
+       if (setuid(0)) {
+               syslog(LOG_ERR, "can't setuid(0)");
+               bb_error_msg_and_die( "Cannot change ID to root.\n");
+       }
+       if (!update_passwd(pw, crypt_passwd)) {
+               syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
+                          myname);
+               printf("Password changed.\n");
+       } else {
+               syslog(LOG_WARNING, "an error occurred updating the password file");
+               bb_error_msg_and_die("An error occurred updating the password file.\n");
+       }
+       return (0);
+}
+
+
+
+static int create_backup(const char *backup, FILE * fp)
+{
+       struct stat sb;
+       struct utimbuf ub;
+       FILE *bkfp;
+       int c, mask;
+
+       if (fstat(fileno(fp), &sb))
+               /* return -1; */
+               return 1;
+
+       mask = umask(077);
+       bkfp = fopen(backup, "w");
+       umask(mask);
+       if (!bkfp)
+               /* return -1; */
+               return 1;
+
+       /* TODO: faster copy, not one-char-at-a-time.  --marekm */
+       rewind(fp);
+       while ((c = getc(fp)) != EOF) {
+               if (putc(c, bkfp) == EOF)
+                       break;
+       }
+       if (c != EOF || fflush(bkfp)) {
+               fclose(bkfp);
+               /* return -1; */
+               return 1;
+       }
+       if (fclose(bkfp))
+               /* return -1; */
+               return 1;
+
+       ub.actime = sb.st_atime;
+       ub.modtime = sb.st_mtime;
+       utime(backup, &ub);
+       return 0;
+}
+
+static int i64c(int i)
+{
+       if (i <= 0)
+               return ('.');
+       if (i == 1)
+               return ('/');
+       if (i >= 2 && i < 12)
+               return ('0' - 2 + i);
+       if (i >= 12 && i < 38)
+               return ('A' - 12 + i);
+       if (i >= 38 && i < 63)
+               return ('a' - 38 + i);
+       return ('z');
+}
+
+static char *crypt_make_salt(void)
+{
+       time_t now;
+       static unsigned long x;
+       static char result[3];
+
+       time(&now);
+       x += now + getpid() + clock();
+       result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
+       result[1] = i64c(((x >> 12) ^ x) & 077);
+       result[2] = '\0';
+       return result;
+}
+
+
+static int new_password(const struct passwd *pw, int amroot, int algo)
+{
+       char *clear;
+       char *cipher;
+       char *cp;
+       char orig[200];
+       char pass[200];
+       time_t start, now;
+
+       if (!amroot && crypt_passwd[0]) {
+               if (!(clear = bb_askpass(0, "Old password:"))) {
+                       /* return -1; */
+                       return 1;
+               }
+               cipher = pw_encrypt(clear, crypt_passwd);
+               if (strcmp(cipher, crypt_passwd) != 0) {
+                       syslog(LOG_WARNING, "incorrect password for `%s'",
+                                  pw->pw_name);
+                       time(&start);
+                       now = start;
+                       while (difftime(now, start) < FAIL_DELAY) {
+                               sleep(FAIL_DELAY);
+                               time(&now);
+                       }
+                       fprintf(stderr, "Incorrect password.\n");
+                       /* return -1; */
+                       return 1;
+               }
+               safe_strncpy(orig, clear, sizeof(orig));
+               bzero(clear, strlen(clear));
+               bzero(cipher, strlen(cipher));
+       } else {
+               orig[0] = '\0';
+       }
+       if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n"
+                                         "Please use a combination of upper and lower case letters and numbers.\n"
+                                         "Enter new password: ")))
+       {
+               bzero(orig, sizeof orig);
+               /* return -1; */
+               return 1;
+       }
+       safe_strncpy(pass, cp, sizeof(pass));
+       bzero(cp, strlen(cp));
+       /* if (!obscure(orig, pass, pw)) { */
+       if (obscure(orig, pass, pw)) {
+               if (amroot) {
+                       printf("\nWarning: weak password (continuing).\n");
+               } else {
+                       /* return -1; */
+                       return 1;
+               }
+       }
+       if (!(cp = bb_askpass(0, "Re-enter new password: "))) {
+               bzero(orig, sizeof orig);
+               /* return -1; */
+               return 1;
+       }
+       if (strcmp(cp, pass)) {
+               fprintf(stderr, "Passwords do not match.\n");
+               /* return -1; */
+               return 1;
+       }
+       bzero(cp, strlen(cp));
+       bzero(orig, sizeof(orig));
+
+       if (algo == 1) {
+               cp = pw_encrypt(pass, "$1$");
+       } else
+               cp = pw_encrypt(pass, crypt_make_salt());
+       bzero(pass, sizeof pass);
+       safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
+       return 0;
+}
+
+static void set_filesize_limit(int blocks)
+{
+       struct rlimit rlimit_fsize;
+
+       rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
+       setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
+}
diff --git a/busybox/loginutils/su.c b/busybox/loginutils/su.c
new file mode 100644 (file)
index 0000000..ec0c16c
--- /dev/null
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "busybox.h"
+
+
+
+/* The shell to run if none is given in the user's passwd entry.  */
+#define DEFAULT_USER  "root"
+
+//#define SYSLOG_SUCCESS
+#define SYSLOG_FAILURE
+
+
+#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
+/* Log the fact that someone has run su */
+
+# if defined( SYSLOG_SUCCESS ) && defined( SYSLOG_FAILURE )
+static void log_su (const char *successful, const char *old_user, const char *tty)
+{
+       syslog ( LOG_NOTICE, "%s%s on %s", successful, old_user, tty);
+}
+#  define log_su_successful(cu, u, tty) if(!cu) log_su("", u, tty)
+#  define log_su_failure(cu, u, tty)    if(!cu) log_su("FAILED SU ", u, tty)
+# else
+       /* partial logging */
+#  if !defined( SYSLOG_SUCESS )
+#   define log_su_successful(cu, u, tty)
+#   define log_su_failure(cu, u, t) if(!cu) \
+                       syslog(LOG_NOTICE, "FAILED SU %s on %s", u, t)
+#  else
+#   define log_su_successful(cu, u, t) if(!cu) \
+                       syslog(LOG_NOTICE, "%s on %s", u, t)
+#   define log_su_failure(cu, u, tty)
+#  endif
+# endif
+#else
+       /* logging not used */
+# define log_su_successful(cu, u, tty)
+# define log_su_failure(cu, u, tty)
+#endif
+
+
+int su_main ( int argc, char **argv )
+{
+       unsigned long flags;
+       int opt_preserve;
+       int opt_loginshell;
+       char *opt_shell = 0;
+       char *opt_command = 0;
+       char *opt_username = DEFAULT_USER;
+       char **opt_args = 0;
+       struct passwd *pw;
+       uid_t cur_uid = getuid();
+
+#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
+       const char *tty;
+       const char *old_user;
+#endif
+
+       flags = bb_getopt_ulflags(argc, argv, "mplc:s:",
+                                                 &opt_command, &opt_shell);
+       opt_preserve = flags & 3;
+       opt_loginshell = (flags & 4 ? 1 : 0);
+
+       if (optind < argc  && argv[optind][0] == '-' && argv[optind][1] == 0) {
+               opt_loginshell = 1;
+               ++optind;
+    }
+
+       /* get user if specified */
+       if ( optind < argc )
+               opt_username = argv [optind++];
+
+       if ( optind < argc )
+               opt_args = argv + optind;
+
+#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
+#ifdef CONFIG_FEATURE_U_W_TMP
+       /* The utmp entry (via getlogin) is probably the best way to identify
+          the user, especially if someone su's from a su-shell.  */
+       old_user = getlogin ( );
+       if ( !old_user )
+#endif
+               {
+               /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid.  */
+               pw = getpwuid ( cur_uid );
+               old_user = ( pw ? pw->pw_name : "" );
+       }
+       tty = ttyname ( 2 );
+       if(!tty)
+               tty = "none";
+
+       openlog ( bb_applet_name, 0, LOG_AUTH );
+#endif
+
+       pw = getpwnam ( opt_username );
+       if ( !pw )
+               bb_error_msg_and_die ( "user %s does not exist", opt_username );
+
+       /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
+          is a username that is retrieved via NIS (YP), but that doesn't have
+          a default shell listed.  */
+       if ( !pw-> pw_shell || !pw->pw_shell [0] )
+               pw-> pw_shell = (char *) DEFAULT_SHELL;
+
+       if ((( cur_uid == 0 ) || correct_password ( pw ))) {
+               log_su_successful(pw->pw_uid, old_user, tty );
+       } else {
+               log_su_failure (pw->pw_uid, old_user, tty );
+               bb_error_msg_and_die ( "incorrect password" );
+       }
+
+#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
+       closelog();
+#endif
+
+       if ( !opt_shell && opt_preserve )
+               opt_shell = getenv ( "SHELL" );
+
+       if ( opt_shell && cur_uid && restricted_shell ( pw-> pw_shell )) {
+               /* The user being su'd to has a nonstandard shell, and so is
+                  probably a uucp account or has restricted access.  Don't
+                  compromise the account by allowing access with a standard
+                  shell.  */
+               fputs ( "using restricted shell\n", stderr );
+               opt_shell = 0;
+       }
+
+       if ( !opt_shell )
+               opt_shell = pw->pw_shell;
+
+       change_identity ( pw );
+       setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
+       run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args
+#ifdef CONFIG_SELINUX
+       , 0
+#endif
+       );
+
+       return EXIT_FAILURE;
+}
diff --git a/busybox/loginutils/sulogin.c b/busybox/loginutils/sulogin.c
new file mode 100644 (file)
index 0000000..f21b095
--- /dev/null
@@ -0,0 +1,158 @@
+/* vi: set sw=4 ts=4: */
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "busybox.h"
+
+
+// sulogin defines
+#define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \
+       "(or type Control-D for normal startup):"
+
+static const char *forbid[] = {
+       "ENV",
+       "BASH_ENV",
+       "HOME",
+       "IFS",
+       "PATH",
+       "SHELL",
+       "LD_LIBRARY_PATH",
+       "LD_PRELOAD",
+       "LD_TRACE_LOADED_OBJECTS",
+       "LD_BIND_NOW",
+       "LD_AOUT_LIBRARY_PATH",
+       "LD_AOUT_PRELOAD",
+       "LD_NOWARN",
+       "LD_KEEPDIR",
+       (char *) 0
+};
+
+
+
+static void catchalarm(int junk)
+{
+       exit(EXIT_FAILURE);
+}
+
+
+extern int sulogin_main(int argc, char **argv)
+{
+       char *cp;
+       char *device = (char *) 0;
+       const char *name = "root";
+       int timeout = 0;
+       static char pass[BUFSIZ];
+       struct passwd pwent;
+       struct passwd *pwd;
+       time_t start, now;
+       const char **p;
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       struct spwd *spwd = NULL;
+#endif                                                 /* CONFIG_FEATURE_SHADOWPASSWDS */
+
+       openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
+       if (argc > 1) {
+               if (strncmp(argv[1], "-t", 2) == 0) {
+                       if (strcmp(argv[1], "-t") == 0) {
+                               if (argc > 2) {
+                                       timeout = atoi(argv[2]);
+                                       if (argc > 3) {
+                                               device = argv[3];
+                                       }
+                               }
+                       } else {
+                               if (argc > 2) {
+                                       device = argv[2];
+                               }
+                       }
+               } else {
+                       device = argv[1];
+               }
+               if (device) {
+                       close(0);
+                       close(1);
+                       close(2);
+                       if (open(device, O_RDWR) >= 0) {
+                               dup(0);
+                               dup(0);
+                       } else {
+                               syslog(LOG_WARNING, "cannot open %s\n", device);
+                               exit(EXIT_FAILURE);
+                       }
+               }
+       }
+       if (access(bb_path_passwd_file, 0) == -1) {
+               syslog(LOG_WARNING, "No password file\n");
+               bb_error_msg_and_die("No password file\n");
+       }
+       if (!isatty(0) || !isatty(1) || !isatty(2)) {
+               exit(EXIT_FAILURE);
+       }
+
+
+       /* Clear out anything dangerous from the environment */
+       for (p = forbid; *p; p++)
+               unsetenv(*p);
+
+
+       signal(SIGALRM, catchalarm);
+       if (!(pwd = getpwnam(name))) {
+               syslog(LOG_WARNING, "No password entry for `root'\n");
+               bb_error_msg_and_die("No password entry for `root'\n");
+       }
+       pwent = *pwd;
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       spwd = NULL;
+       if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0)
+                               || (strcmp(pwd->pw_passwd, "*") == 0))) {
+               endspent();
+               spwd = getspnam(name);
+               if (spwd) {
+                       pwent.pw_passwd = spwd->sp_pwdp;
+               }
+       }
+#endif                                                 /* CONFIG_FEATURE_SHADOWPASSWDS */
+       while (1) {
+               cp = bb_askpass(timeout, SULOGIN_PROMPT);
+               if (!cp || !*cp) {
+                       puts("\n");
+                       fflush(stdout);
+                       syslog(LOG_INFO, "Normal startup\n");
+                       exit(EXIT_SUCCESS);
+               } else {
+                       safe_strncpy(pass, cp, sizeof(pass));
+                       bzero(cp, strlen(cp));
+               }
+               if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) {
+                       break;
+               }
+               time(&start);
+               now = start;
+               while (difftime(now, start) < FAIL_DELAY) {
+                       sleep(FAIL_DELAY);
+                       time(&now);
+               }
+               puts("Login incorrect");
+               fflush(stdout);
+               syslog(LOG_WARNING, "Incorrect root password\n");
+       }
+       bzero(pass, strlen(pass));
+       signal(SIGALRM, SIG_DFL);
+       puts("Entering System Maintenance Mode\n");
+       fflush(stdout);
+       syslog(LOG_INFO, "System Maintenance Mode\n");
+       run_shell(pwent.pw_shell, 1, 0, 0);
+       return (0);
+}
diff --git a/busybox/loginutils/vlock.c b/busybox/loginutils/vlock.c
new file mode 100644 (file)
index 0000000..def484a
--- /dev/null
@@ -0,0 +1,231 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vlock implementation for busybox
+ *
+ * Copyright (C) 2000 by spoon <spoon@ix.netcom.com>
+ * Written by spoon <spon@ix.netcom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
+ * original vlock.  I snagged a bunch of his code to write this
+ * minimalistic vlock.
+ */
+/* Fixed by Erik Andersen to do passwords the tinylogin way...
+ * It now works with md5, sha1, etc passwords. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/vt.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+
+#include "busybox.h"
+
+static struct passwd *pw;
+static struct vt_mode ovtm;
+static struct termios oterm;
+static int vfd;
+static int o_lock_all = 0;
+
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+static struct spwd *spw;
+
+/* getspuid - get a shadow entry by uid */
+struct spwd *getspuid(uid_t uid)
+{
+       struct spwd *sp;
+       struct passwd *mypw;
+
+       if ((mypw = getpwuid(getuid())) == NULL) {
+               return (NULL);
+       }
+       setspent();
+       while ((sp = getspent()) != NULL) {
+               if (strcmp(mypw->pw_name, sp->sp_namp) == 0)
+                       break;
+       }
+       endspent();
+       return (sp);
+}
+#endif
+
+static void release_vt(int signo)
+{
+       if (!o_lock_all)
+               ioctl(vfd, VT_RELDISP, 1);
+       else
+               ioctl(vfd, VT_RELDISP, 0);
+}
+
+static void acquire_vt(int signo)
+{
+       ioctl(vfd, VT_RELDISP, VT_ACKACQ);
+}
+
+static void restore_terminal(void)
+{
+       ioctl(vfd, VT_SETMODE, &ovtm);
+       tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
+}
+
+extern int vlock_main(int argc, char **argv)
+{
+       sigset_t sig;
+       struct sigaction sa;
+       struct vt_mode vtm;
+       int times = 0;
+       struct termios term;
+
+       if (argc > 2) {
+               bb_show_usage();
+       }
+
+       if (argc == 2) {
+               if (strncmp(argv[1], "-a", 2)) {
+                       bb_show_usage();
+               } else {
+                       o_lock_all = 1;
+               }
+       }
+
+       if ((pw = getpwuid(getuid())) == NULL) {
+               bb_error_msg_and_die("no password for uid %d\n", getuid());
+       }
+#ifdef CONFIG_FEATURE_SHADOWPASSWDS
+       if ((strcmp(pw->pw_passwd, "x") == 0)
+               || (strcmp(pw->pw_passwd, "*") == 0)) {
+
+               if ((spw = getspuid(getuid())) == NULL) {
+                       bb_error_msg_and_die("could not read shadow password for uid %d: %s\n",
+                                          getuid(), strerror(errno));
+               }
+               if (spw->sp_pwdp) {
+                       pw->pw_passwd = spw->sp_pwdp;
+               }
+       }
+#endif                                                 /* CONFIG_FEATURE_SHADOWPASSWDS */
+       if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') {
+               bb_error_msg_and_die("Account disabled for uid %d\n", getuid());
+       }
+
+       /* we no longer need root privs */
+       setuid(getuid());
+       setgid(getgid());
+
+       if ((vfd = open("/dev/tty", O_RDWR)) < 0) {
+               bb_error_msg_and_die("/dev/tty");
+       };
+
+       if (ioctl(vfd, VT_GETMODE, &vtm) < 0) {
+               bb_error_msg_and_die("/dev/tty");
+       };
+
+       /* mask a bunch of signals */
+       sigprocmask(SIG_SETMASK, NULL, &sig);
+       sigdelset(&sig, SIGUSR1);
+       sigdelset(&sig, SIGUSR2);
+       sigaddset(&sig, SIGTSTP);
+       sigaddset(&sig, SIGTTIN);
+       sigaddset(&sig, SIGTTOU);
+       sigaddset(&sig, SIGHUP);
+       sigaddset(&sig, SIGCHLD);
+       sigaddset(&sig, SIGQUIT);
+       sigaddset(&sig, SIGINT);
+
+       sigemptyset(&(sa.sa_mask));
+       sa.sa_flags = SA_RESTART;
+       sa.sa_handler = release_vt;
+       sigaction(SIGUSR1, &sa, NULL);
+       sa.sa_handler = acquire_vt;
+       sigaction(SIGUSR2, &sa, NULL);
+
+       /* need to handle some signals so that we don't get killed by them */
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGHUP, &sa, NULL);
+       sigaction(SIGQUIT, &sa, NULL);
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTSTP, &sa, NULL);
+
+       ovtm = vtm;
+       vtm.mode = VT_PROCESS;
+       vtm.relsig = SIGUSR1;
+       vtm.acqsig = SIGUSR2;
+       ioctl(vfd, VT_SETMODE, &vtm);
+
+       tcgetattr(STDIN_FILENO, &oterm);
+       term = oterm;
+       term.c_iflag &= ~BRKINT;
+       term.c_iflag |= IGNBRK;
+       term.c_lflag &= ~ISIG;
+       term.c_lflag &= ~(ECHO | ECHOCTL);
+       tcsetattr(STDIN_FILENO, TCSANOW, &term);
+
+       do {
+               char *pass, *crypt_pass;
+               char prompt[100];
+
+               if (o_lock_all) {
+                       printf("All Virtual Consoles locked.\n");
+               } else {
+                       printf("This Virtual Console locked.\n");
+               }
+               fflush(stdout);
+
+               snprintf(prompt, 100, "%s's password: ", pw->pw_name);
+
+               if ((pass = bb_askpass(0, prompt)) == NULL) {
+                       restore_terminal();
+                       bb_perror_msg_and_die("password");
+               }
+
+               crypt_pass = pw_encrypt(pass, pw->pw_passwd);
+               if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) {
+                       memset(pass, 0, strlen(pass));
+                       memset(crypt_pass, 0, strlen(crypt_pass));
+                       restore_terminal();
+                       return 0;
+               }
+               memset(pass, 0, strlen(pass));
+               memset(crypt_pass, 0, strlen(crypt_pass));
+
+               if (isatty(STDIN_FILENO) == 0) {
+                       restore_terminal();
+                       bb_perror_msg_and_die("isatty");
+               }
+
+               sleep(++times);
+               printf("Password incorrect.\n");
+               if (times >= 3) {
+                       sleep(15);
+                       times = 2;
+               }
+       } while (1);
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/miscutils/Config.in b/busybox/miscutils/Config.in
new file mode 100644 (file)
index 0000000..77e13e8
--- /dev/null
@@ -0,0 +1,201 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Miscellaneous Utilities"
+
+config CONFIG_ADJTIMEX
+       bool "adjtimex"
+       default n
+       help
+         Adjtimex reads and optionally sets adjustment parameters for
+         the Linux clock adjustment algorithm.
+
+config CONFIG_CROND
+       bool "crond"
+       default n
+       select CONFIG_FEATURE_SUID
+       help
+         Crond is a background daemon that parses individual crontab
+         files and executes commands on behalf of the users in question.
+         This is a port of dcron from slackware.  It uses files of the
+         format /var/spool/cron/crontabs/<username> files, for example:
+             $ cat /var/spool/cron/crontabs/root
+             # Run daily cron jobs at 4:40 every day:
+             40 4 * * * /etc/cron/daily > /dev/null 2>&1
+         Note that Busybox binary must be setuid root for this applet to
+         work properly.
+
+config CONFIG_FEATURE_CROND_CALL_SENDMAIL
+       bool "  Using /usr/sbin/sendmail?"
+       default n
+       depends on CONFIG_CROND
+       help
+         Support calling /usr/sbin/sendmail for send cmd outputs.
+
+config CONFIG_CRONTAB
+       bool "crontab"
+       default n
+       help
+         Crontab manipulates the crontab for a particular user.  Only
+         the superuser may specify a different user and/or crontab directory.
+
+config CONFIG_DC
+       bool "dc"
+       default n
+       help
+         Dc is a reverse-polish desk calculator which supports unlimited
+         precision arithmetic.
+
+config CONFIG_DEVFSD
+       bool "devfsd"
+       default n
+       help
+         Provides compatibility with old device names on a devfs systems.
+         You should set it to true if you have devfs enabled.
+         The following keywords in devsfd.conf are supported:
+         "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
+         "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
+         "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
+
+          But only if they are written UPPERCASE!!!!!!!!
+
+config CONFIG_DEVFSD_MODLOAD
+       bool "Adds support for MODLOAD keyword in devsfd.conf"
+       default n
+       depends on CONFIG_DEVFSD
+       help
+         This actually doesn't work with busybox  modutils but needs the real modutils.
+
+config CONFIG_DEVFSD_FG_NP
+       bool "Enables the -fg and -np options"
+       default n
+       depends on CONFIG_DEVFSD
+       help
+               -fg     Run the daemon in the foreground.
+               -np     Exit  after  parsing  the configuration file. Do not poll for events.
+
+config CONFIG_DEVFSD_VERBOSE
+       bool "Increases logging (and size)"
+       default n
+       depends on CONFIG_DEVFSD
+       help
+         Increases logging to stderr or syslog.
+
+config CONFIG_LAST
+       bool "last"
+       default n
+       select CONFIG_FEATURE_U_W_TMP
+       help
+         'last' displays a list of the last users that logged into the system.
+
+config CONFIG_HDPARM
+       bool "hdparm"
+       default n
+       help
+         Get/Set hard drive parameters.  Primarily intended for ATA
+         drives.  Adds about 13k (or around 30k if you enable the
+         CONFIG_FEATURE_HDPARM_GET_IDENTITY option)....
+
+config CONFIG_FEATURE_HDPARM_GET_IDENTITY
+       bool "  Support obtaining detailed information directly from drives"
+       default y
+       depends on CONFIG_HDPARM
+       help
+         Enables the -I and -Istdin options to obtain detailed information
+         directly from drives about their capabilities and supported ATA
+         feature set.  Enabling this option will add about 16k...
+
+config CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
+       bool "  Register an IDE interface (DANGEROUS)"
+       default n
+       depends on CONFIG_HDPARM
+       help
+         Enables the 'hdparm -R' option to register an IDE interface.
+         This is dangerous stuff, so you should probably say N.
+
+config CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+       bool "  Un-register an IDE interface (DANGEROUS)"
+       default n
+       depends on CONFIG_HDPARM
+       help
+         Enables the 'hdparm -U' option to un-register an IDE interface.
+         This is dangerous stuff, so you should probably say N.
+
+config CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
+       bool "  perform device reset (DANGEROUS)"
+       default n
+       depends on CONFIG_HDPARM
+       help
+         Enables the 'hdparm -w' option to perform a device reset.
+         This is dangerous stuff, so you should probably say N.
+
+config CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+       bool "  tristate device for hotswap (DANGEROUS)"
+       default n
+       depends on CONFIG_HDPARM
+       help
+         Enables the 'hdparm -x' option to tristate device for hotswap,
+         and the '-b' option to get/set bus state.  This is dangerous
+         stuff, so you should probably say N.
+
+config CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
+       bool "  get/set using_dma flag (DANGEROUS)"
+       default n
+       depends on CONFIG_HDPARM
+       help
+         Enables the 'hdparm -d' option to get/set using_dma flag.
+         This is dangerous stuff, so you should probably say N.
+
+config CONFIG_MAKEDEVS
+       bool "makedevs"
+       default n
+       help
+         'makedevs' is a utility used and created by the Linux Router Project.
+         It creates a large number of device special files (/dev devices)
+         rather quickly, and can be considerably faster then running mknod a
+         zillion times.
+
+config CONFIG_MT
+       bool "mt"
+       default n
+       help
+         mt is used to control tape devices.  You can use the mt utility
+         to advance or rewind a tape past a specified number of archive
+         files on the tape.
+
+config CONFIG_RX
+        bool "rx"
+       default n
+       help
+         Receive files using the Xmodem protocol.
+
+config CONFIG_STRINGS
+       bool "strings"
+       default n
+       help
+         strings prints the printable character sequences for each file
+         specified.
+
+config CONFIG_TIME
+       bool "time"
+       default n
+       help
+         The time command runs the specified program with the given arguments.
+         When the command finishes, time writes a message to standard output
+         giving timing statistics about this program run.
+
+config CONFIG_WATCHDOG
+       bool "watchdog"
+       default n
+       help
+         The watchdog utility is used with hardware or software watchdog
+         device drivers.  It opens the specified watchdog device special file
+         and periodically writes a magic character to the device.  If the
+         watchdog applet ever fails to write the magic character within a
+         certain amount of time, the watchdog device assumes the system has
+         hung, and will cause the hardware to reboot.
+
+endmenu
+
diff --git a/busybox/miscutils/Makefile b/busybox/miscutils/Makefile
new file mode 100644 (file)
index 0000000..ac427dc
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/miscutils
+MISCUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/miscutils/Makefile.in b/busybox/miscutils/Makefile.in
new file mode 100644 (file)
index 0000000..ddddf72
--- /dev/null
@@ -0,0 +1,55 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+MISCUTILS_AR:=miscutils.a
+ifndef $(MISCUTILS_DIR)
+MISCUTILS_DIR:=$(top_builddir)/miscutils/
+endif
+srcdir=$(top_srcdir)/miscutils
+
+MISCUTILS-y:=
+MISCUTILS-$(CONFIG_ADJTIMEX)           += adjtimex.o
+MISCUTILS-$(CONFIG_CROND)               += crond.o
+MISCUTILS-$(CONFIG_CRONTAB)             += crontab.o
+MISCUTILS-$(CONFIG_DC)                 += dc.o
+MISCUTILS-$(CONFIG_DEVFSD)             += devfsd.o
+MISCUTILS-$(CONFIG_HDPARM)             += hdparm.o
+MISCUTILS-$(CONFIG_LAST)               += last.o
+MISCUTILS-$(CONFIG_MAKEDEVS)           += makedevs.o
+MISCUTILS-$(CONFIG_MT)                 += mt.o
+MISCUTILS-$(CONFIG_RX)                 += rx.o
+MISCUTILS-$(CONFIG_STRINGS)            += strings.o
+MISCUTILS-$(CONFIG_TIME)               += time.o
+MISCUTILS-$(CONFIG_WATCHDOG)           += watchdog.o
+
+libraries-y+=$(MISCUTILS_DIR)$(MISCUTILS_AR)
+
+needlibm-y:=
+needlibm-$(CONFIG_DC) := y
+
+ifeq ($(needlibm-y),y)
+  LIBRARIES += -lm
+endif
+
+$(MISCUTILS_DIR)$(MISCUTILS_AR): $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y))
+       $(AR) -ro $@ $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y))
+
+$(MISCUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/miscutils/adjtimex.c b/busybox/miscutils/adjtimex.c
new file mode 100644 (file)
index 0000000..110c026
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables.
+ *
+ * Originally written: October 1997
+ * Last hack: March 2001
+ * Copyright 1997, 2000, 2001 Larry Doolittle <LRDoolittle@lbl.gov>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License (Version 2,
+ *  June 1991) as published by the Free Software Foundation.  At the
+ *  time of writing, that license was published by the FSF with the URL
+ *  http://www.gnu.org/copyleft/gpl.html, and is incorporated herein by
+ *  reference.
+ *
+ *  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.
+ *
+ * This adjtimex(1) is very similar in intent to adjtimex(8) by Steven
+ * Dick <ssd@nevets.oau.org> and Jim Van Zandt <jrv@vanzandt.mv.com>
+ * (see http://metalab.unc.edu/pub/Linux/system/admin/time/adjtimex*).
+ * That version predates this one, and is _much_ bigger and more
+ * featureful.  My independently written version was very similar to
+ * Steven's from the start, because they both follow the kernel timex
+ * structure.  I further tweaked this version to be equivalent to Steven's
+ * where possible, but I don't like getopt_long, so the actual usage
+ * syntax is incompatible.
+ *
+ * Amazingly enough, my Red Hat 5.2 sys/timex (and sub-includes)
+ * don't actually give a prototype for adjtimex(2), so building
+ * this code (with -Wall) gives a warning.  Later versions of
+ * glibc fix this issue.
+ *
+ * This program is too simple for a Makefile, just build with:
+ *  gcc -Wall -O adjtimex.c -o adjtimex
+ *
+ * busyboxed 20 March 2001, Larry Doolittle <ldoolitt@recycle.lbl.gov>
+ * It will autosense if it is built in a busybox environment, based
+ * on the BB_VER preprocessor macro.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/timex.h>
+#include "busybox.h"
+
+static struct {int bit; char *name;} statlist[] = {
+       { STA_PLL,       "PLL"       },
+       { STA_PPSFREQ,   "PPSFREQ"   },
+       { STA_PPSTIME,   "PPSTIME"   },
+       { STA_FLL,       "FFL"       },
+       { STA_INS,       "INS"       },
+       { STA_DEL,       "DEL"       },
+       { STA_UNSYNC,    "UNSYNC"    },
+       { STA_FREQHOLD,  "FREQHOLD"  },
+       { STA_PPSSIGNAL, "PPSSIGNAL" },
+       { STA_PPSJITTER, "PPSJITTER" },
+       { STA_PPSWANDER, "PPSWANDER" },
+       { STA_PPSERROR,  "PPSERROR"  },
+       { STA_CLOCKERR,  "CLOCKERR"  },
+       { 0, NULL } };
+
+static char *ret_code_descript[] = {
+       "clock synchronized",
+       "insert leap second",
+       "delete leap second",
+       "leap second in progress",
+       "leap second has occurred",
+       "clock not synchronized" };
+
+#ifdef BB_VER
+#define main adjtimex_main
+#else
+void usage(char *prog)
+{
+       fprintf(stderr,
+               "Usage: %s [ -q ] [ -o offset ] [ -f frequency ] [ -p timeconstant ] [ -t tick ]\n",
+               prog);
+}
+#define bb_show_usage() usage(argv[0])
+#endif
+
+int main(int argc, char ** argv)
+{
+       struct timex txc;
+       int quiet=0;
+       int c, i, ret, sep;
+       char *descript;
+       txc.modes=0;
+       for (;;) {
+               c = getopt( argc, argv, "qo:f:p:t:");
+               if (c == EOF) break;
+               switch (c) {
+                       case 'q':
+                               quiet=1;
+                               break;
+                       case 'o':
+                               txc.offset = atoi(optarg);
+                               txc.modes |= ADJ_OFFSET_SINGLESHOT;
+                               break;
+                       case 'f':
+                               txc.freq = atoi(optarg);
+                               txc.modes |= ADJ_FREQUENCY;
+                               break;
+                       case 'p':
+                               txc.constant = atoi(optarg);
+                               txc.modes |= ADJ_TIMECONST;
+                               break;
+                       case 't':
+                               txc.tick = atoi(optarg);
+                               txc.modes |= ADJ_TICK;
+                               break;
+                       default:
+                               bb_show_usage();
+                               exit(1);
+               }
+       }
+       if (argc != optind) { /* no valid non-option parameters */
+               bb_show_usage();
+               exit(1);
+       }
+
+       ret = adjtimex(&txc);
+
+       if (ret < 0) perror("adjtimex");
+
+       if (!quiet && ret>=0) {
+               printf(
+                       "    mode:         %d\n"
+                       "-o  offset:       %ld\n"
+                       "-f  frequency:    %ld\n"
+                       "    maxerror:     %ld\n"
+                       "    esterror:     %ld\n"
+                       "    status:       %d ( ",
+               txc.modes, txc.offset, txc.freq, txc.maxerror,
+               txc.esterror, txc.status);
+
+               /* representative output of next code fragment:
+                  "PLL | PPSTIME" */
+               sep=0;
+               for (i=0; statlist[i].name; i++) {
+                       if (txc.status & statlist[i].bit) {
+                               if (sep) fputs(" | ",stdout);
+                               fputs(statlist[i].name,stdout);
+                               sep=1;
+                       }
+               }
+
+               descript = "error";
+               if (ret >= 0 && ret <= 5) descript = ret_code_descript[ret];
+               printf(" )\n"
+                       "-p  timeconstant: %ld\n"
+                       "    precision:    %ld\n"
+                       "    tolerance:    %ld\n"
+                       "-t  tick:         %ld\n"
+                       "    time.tv_sec:  %ld\n"
+                       "    time.tv_usec: %ld\n"
+                       "    return value: %d (%s)\n",
+               txc.constant,
+               txc.precision, txc.tolerance, txc.tick,
+               (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript);
+       }
+       return (ret<0);
+}
diff --git a/busybox/miscutils/crond.c b/busybox/miscutils/crond.c
new file mode 100644 (file)
index 0000000..085cf6e
--- /dev/null
@@ -0,0 +1,1055 @@
+/*
+ * crond -d[#] -c <crondir> -f -b
+ *
+ * run as root, but NOT setuid root
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * May be distributed under the GNU General Public License
+ *
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
+ */
+
+#define VERSION "2.3.2"
+
+#undef FEATURE_DEBUG_OPT
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+#include "busybox.h"
+
+#define arysize(ary)    (sizeof(ary)/sizeof((ary)[0]))
+
+#ifndef CRONTABS
+#define CRONTABS        "/var/spool/cron/crontabs"
+#endif
+#ifndef TMPDIR
+#define TMPDIR          "/var/spool/cron"
+#endif
+#ifndef SENDMAIL
+#define SENDMAIL        "/usr/sbin/sendmail"
+#endif
+#ifndef SENDMAIL_ARGS
+#define SENDMAIL_ARGS   "-ti", "oem"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE      "cron.update"
+#endif
+#ifndef MAXLINES
+#define MAXLINES        256    /* max lines in non-root crontabs */
+#endif
+
+typedef struct CronFile {
+       struct CronFile *cf_Next;
+       struct CronLine *cf_LineBase;
+       char *cf_User;          /* username                     */
+       int cf_Ready;           /* bool: one or more jobs ready */
+       int cf_Running;         /* bool: one or more jobs running */
+       int cf_Deleted;         /* marked for deletion, ignore  */
+} CronFile;
+
+typedef struct CronLine {
+       struct CronLine *cl_Next;
+       char *cl_Shell;         /* shell command                        */
+       pid_t cl_Pid;           /* running pid, 0, or armed (-1)        */
+       int cl_MailFlag;        /* running pid is for mail              */
+       int cl_MailPos;         /* 'empty file' size                    */
+       char cl_Mins[60];       /* 0-59                                 */
+       char cl_Hrs[24];        /* 0-23                                 */
+       char cl_Days[32];       /* 1-31                                 */
+       char cl_Mons[12];       /* 0-11                                 */
+       char cl_Dow[7];         /* 0-6, beginning sunday                */
+} CronLine;
+
+#define RUN_RANOUT      1
+#define RUN_RUNNING     2
+#define RUN_FAILED      3
+
+#define DaemonUid 0
+
+#ifdef FEATURE_DEBUG_OPT
+static short DebugOpt;
+#endif
+
+static short LogLevel = 8;
+static const char *LogFile;
+static const char *CDir = CRONTABS;
+
+static void startlogger(void);
+
+static void CheckUpdates(void);
+static void SynchronizeDir(void);
+static int TestJobs(time_t t1, time_t t2);
+static void RunJobs(void);
+static int CheckJobs(void);
+
+static void RunJob(const char *user, CronLine * line);
+
+#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
+static void EndJob(const char *user, CronLine * line);
+#else
+#define EndJob(user, line)  line->cl_Pid = 0
+#endif
+
+static void DeleteFile(const char *userName);
+
+static CronFile *FileBase;
+
+
+static void crondlog(const char *ctl, ...)
+{
+       va_list va;
+       const char *fmt;
+       int level = (int) (ctl[0] & 0xf);
+       int type = level == 20 ?
+               LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
+
+
+       va_start(va, ctl);
+       fmt = ctl + 1;
+       if (level >= LogLevel) {
+
+#ifdef FEATURE_DEBUG_OPT
+               if (DebugOpt) {
+                       vfprintf(stderr, fmt, va);
+               } else
+#endif
+               if (LogFile == 0) {
+                       vsyslog(type, fmt, va);
+               } else {
+                       int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 600);
+                       if (logfd >= 0) {
+                               vdprintf(logfd, fmt, va);
+                               close(logfd);
+#ifdef FEATURE_DEBUG_OPT
+                       } else {
+                               bb_perror_msg("Can't open log file");
+#endif
+                       }
+               }
+       }
+       va_end(va);
+       if (ctl[0] & 0200) {
+               exit(20);
+       }
+}
+
+int crond_main(int ac, char **av)
+{
+       unsigned long opt;
+       char *lopt, *Lopt, *copt;
+
+#ifdef FEATURE_DEBUG_OPT
+       char *dopt;
+
+       bb_opt_complementaly = "f-b:b-f:S-L:L-S:d-l";
+#else
+       bb_opt_complementaly = "f-b:b-f:S-L:L-S";
+#endif
+
+       opterr = 0;                     /* disable getopt 'errors' message. */
+       opt = bb_getopt_ulflags(ac, av, "l:L:fbSc:"
+#ifdef FEATURE_DEBUG_OPT
+                                                       "d:"
+#endif
+                                                       , &lopt, &Lopt, &copt
+#ifdef FEATURE_DEBUG_OPT
+                                                       , &dopt
+#endif
+               );
+       if (opt & 1) {
+               LogLevel = atoi(lopt);
+       }
+       if (opt & 2) {
+               if (*Lopt != 0) {
+                       LogFile = Lopt;
+               }
+       }
+       if (opt & 32) {
+               if (*copt != 0) {
+                       CDir = copt;
+               }
+       }
+#ifdef FEATURE_DEBUG_OPT
+       if (opt & 64) {
+               DebugOpt = atoi(dopt);
+               LogLevel = 0;
+       }
+#endif
+
+       /*
+        * change directory
+        */
+
+       if (chdir(CDir) != 0) {
+               bb_perror_msg_and_die("%s", CDir);
+       }
+       signal(SIGHUP, SIG_IGN);        /* hmm.. but, if kill -HUP original
+                                                                * version - his died. ;(
+                                                                */
+       /*
+        * close stdin and stdout, stderr.
+        * close unused descriptors -  don't need.
+        * optional detach from controlling terminal
+        */
+
+       if (!(opt & 4)) {
+#if defined(__uClinux__)
+               /* reexec for vfork() do continue parent */
+               vfork_daemon_rexec(1, 0, ac, av, "-f");
+#else                                                  /* uClinux */
+               if (daemon(1, 0) < 0) {
+                       bb_perror_msg_and_die("daemon");
+               }
+#endif                                                 /* uClinux */
+       }
+
+       (void) startlogger();   /* need if syslog mode selected */
+
+       /*
+        * main loop - synchronize to 1 second after the minute, minimum sleep
+        *             of 1 second.
+        */
+
+       crondlog("\011%s " VERSION " dillon, started, log level %d\n",
+                        bb_applet_name, LogLevel);
+
+       SynchronizeDir();
+
+       {
+               time_t t1 = time(NULL);
+               time_t t2;
+               long dt;
+               short rescan = 60;
+               short sleep_time = 60;
+
+               for (;;) {
+                       sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));
+
+                       t2 = time(NULL);
+                       dt = t2 - t1;
+
+                       /*
+                        * The file 'cron.update' is checked to determine new cron
+                        * jobs.  The directory is rescanned once an hour to deal
+                        * with any screwups.
+                        *
+                        * check for disparity.  Disparities over an hour either way
+                        * result in resynchronization.  A reverse-indexed disparity
+                        * less then an hour causes us to effectively sleep until we
+                        * match the original time (i.e. no re-execution of jobs that
+                        * have just been run).  A forward-indexed disparity less then
+                        * an hour causes intermediate jobs to be run, but only once
+                        * in the worst case.
+                        *
+                        * when running jobs, the inequality used is greater but not
+                        * equal to t1, and less then or equal to t2.
+                        */
+
+                       if (--rescan == 0) {
+                               rescan = 60;
+                               SynchronizeDir();
+                       }
+                       CheckUpdates();
+#ifdef FEATURE_DEBUG_OPT
+                       if (DebugOpt)
+                               crondlog("\005Wakeup dt=%d\n", dt);
+#endif
+                       if (dt < -60 * 60 || dt > 60 * 60) {
+                               t1 = t2;
+                               crondlog("\111time disparity of %d minutes detected\n", dt / 60);
+                       } else if (dt > 0) {
+                               TestJobs(t1, t2);
+                               RunJobs();
+                               sleep(5);
+                               if (CheckJobs() > 0) {
+                                       sleep_time = 10;
+                               } else {
+                                       sleep_time = 60;
+                               }
+                               t1 = t2;
+                       }
+               }
+       }
+       /* not reached */
+}
+
+#if defined(FEATURE_DEBUG_OPT) || defined(CONFIG_FEATURE_CROND_CALL_SENDMAIL)
+/*
+    write to temp file..
+*/
+static void fdprintf(int fd, const char *ctl, ...)
+{
+       va_list va;
+
+       va_start(va, ctl);
+       vdprintf(fd, ctl, va);
+       va_end(va);
+}
+#endif
+
+
+static int ChangeUser(const char *user)
+{
+       struct passwd *pas;
+       const char *err_msg;
+
+       /*
+        * Obtain password entry and change privileges
+        */
+       pas = getpwnam(user);
+       if (pas == 0) {
+               crondlog("\011failed to get uid for %s", user);
+               return (-1);
+       }
+       setenv("USER", pas->pw_name, 1);
+       setenv("HOME", pas->pw_dir, 1);
+       setenv("SHELL", DEFAULT_SHELL, 1);
+
+       /*
+        * Change running state to the user in question
+        */
+       err_msg = change_identity_e2str(pas);
+       if (err_msg) {
+               crondlog("\011%s for user %s", err_msg, user);
+               return (-1);
+       }
+       if (chdir(pas->pw_dir) < 0) {
+               crondlog("\011chdir failed: %s: %m", pas->pw_dir);
+               if (chdir(TMPDIR) < 0) {
+                       crondlog("\011chdir failed: %s: %m", TMPDIR);
+                       return (-1);
+               }
+       }
+       return (pas->pw_uid);
+}
+
+static void startlogger(void)
+{
+       if (LogFile == 0) {
+               openlog(bb_applet_name, LOG_CONS | LOG_PID, LOG_CRON);
+       }
+#ifdef FEATURE_DEBUG_OPT
+       else {                          /* test logfile */
+               int logfd;
+
+               if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 600)) >= 0) {
+                       close(logfd);
+               } else {
+                       bb_perror_msg("Failed to open log file '%s' reason", LogFile);
+               }
+       }
+#endif
+}
+
+
+static const char *const DowAry[] = {
+       "sun",
+       "mon",
+       "tue",
+       "wed",
+       "thu",
+       "fri",
+       "sat",
+
+       "Sun",
+       "Mon",
+       "Tue",
+       "Wed",
+       "Thu",
+       "Fri",
+       "Sat",
+       NULL
+};
+
+static const char *const MonAry[] = {
+       "jan",
+       "feb",
+       "mar",
+       "apr",
+       "may",
+       "jun",
+       "jul",
+       "aug",
+       "sep",
+       "oct",
+       "nov",
+       "dec",
+
+       "Jan",
+       "Feb",
+       "Mar",
+       "Apr",
+       "May",
+       "Jun",
+       "Jul",
+       "Aug",
+       "Sep",
+       "Oct",
+       "Nov",
+       "Dec",
+       NULL
+};
+
+static char *ParseField(char *user, char *ary, int modvalue, int off,
+                                               const char *const *names, char *ptr)
+{
+       char *base = ptr;
+       int n1 = -1;
+       int n2 = -1;
+
+       if (base == NULL) {
+               return (NULL);
+       }
+
+       while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
+               int skip = 0;
+
+               /* Handle numeric digit or symbol or '*' */
+
+               if (*ptr == '*') {
+                       n1 = 0;         /* everything will be filled */
+                       n2 = modvalue - 1;
+                       skip = 1;
+                       ++ptr;
+               } else if (*ptr >= '0' && *ptr <= '9') {
+                       if (n1 < 0) {
+                               n1 = strtol(ptr, &ptr, 10) + off;
+                       } else {
+                               n2 = strtol(ptr, &ptr, 10) + off;
+                       }
+                       skip = 1;
+               } else if (names) {
+                       int i;
+
+                       for (i = 0; names[i]; ++i) {
+                               if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
+                                       break;
+                               }
+                       }
+                       if (names[i]) {
+                               ptr += strlen(names[i]);
+                               if (n1 < 0) {
+                                       n1 = i;
+                               } else {
+                                       n2 = i;
+                               }
+                               skip = 1;
+                       }
+               }
+
+               /* handle optional range '-' */
+
+               if (skip == 0) {
+                       crondlog("\111failed user %s parsing %s\n", user, base);
+                       return (NULL);
+               }
+               if (*ptr == '-' && n2 < 0) {
+                       ++ptr;
+                       continue;
+               }
+
+               /*
+                * collapse single-value ranges, handle skipmark, and fill
+                * in the character array appropriately.
+                */
+
+               if (n2 < 0) {
+                       n2 = n1;
+               }
+               if (*ptr == '/') {
+                       skip = strtol(ptr + 1, &ptr, 10);
+               }
+               /*
+                * fill array, using a failsafe is the easiest way to prevent
+                * an endless loop
+                */
+
+               {
+                       int s0 = 1;
+                       int failsafe = 1024;
+
+                       --n1;
+                       do {
+                               n1 = (n1 + 1) % modvalue;
+
+                               if (--s0 == 0) {
+                                       ary[n1 % modvalue] = 1;
+                                       s0 = skip;
+                               }
+                       }
+                       while (n1 != n2 && --failsafe);
+
+                       if (failsafe == 0) {
+                               crondlog("\111failed user %s parsing %s\n", user, base);
+                               return (NULL);
+                       }
+               }
+               if (*ptr != ',') {
+                       break;
+               }
+               ++ptr;
+               n1 = -1;
+               n2 = -1;
+       }
+
+       if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
+               crondlog("\111failed user %s parsing %s\n", user, base);
+               return (NULL);
+       }
+
+       while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {
+               ++ptr;
+       }
+#ifdef FEATURE_DEBUG_OPT
+       if (DebugOpt) {
+               int i;
+
+               for (i = 0; i < modvalue; ++i) {
+                       crondlog("\005%d", ary[i]);
+               }
+               crondlog("\005\n");
+       }
+#endif
+
+       return (ptr);
+}
+
+static void FixDayDow(CronLine * line)
+{
+       short i;
+       short weekUsed = 0;
+       short daysUsed = 0;
+
+       for (i = 0; i < arysize(line->cl_Dow); ++i) {
+               if (line->cl_Dow[i] == 0) {
+                       weekUsed = 1;
+                       break;
+               }
+       }
+       for (i = 0; i < arysize(line->cl_Days); ++i) {
+               if (line->cl_Days[i] == 0) {
+                       daysUsed = 1;
+                       break;
+               }
+       }
+       if (weekUsed && !daysUsed) {
+               memset(line->cl_Days, 0, sizeof(line->cl_Days));
+       }
+       if (daysUsed && !weekUsed) {
+               memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
+       }
+}
+
+
+
+static void SynchronizeFile(const char *fileName)
+{
+       int maxEntries = MAXLINES;
+       int maxLines;
+       char buf[1024];
+
+       if (strcmp(fileName, "root") == 0) {
+               maxEntries = 65535;
+       }
+       maxLines = maxEntries * 10;
+
+       if (fileName) {
+               FILE *fi;
+
+               DeleteFile(fileName);
+
+               fi = fopen(fileName, "r");
+               if (fi != NULL) {
+                       struct stat sbuf;
+
+                       if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
+                               CronFile *file = calloc(1, sizeof(CronFile));
+                               CronLine **pline;
+
+                               file->cf_User = strdup(fileName);
+                               pline = &file->cf_LineBase;
+
+                               while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
+                                       CronLine line;
+                                       char *ptr;
+
+                                       if (buf[0]) {
+                                               buf[strlen(buf) - 1] = 0;
+                                       }
+                                       if (buf[0] == 0 || buf[0] == '#' || buf[0] == ' ' || buf[0] == '\t') {
+                                               continue;
+                                       }
+                                       if (--maxEntries == 0) {
+                                               break;
+                                       }
+                                       memset(&line, 0, sizeof(line));
+
+#ifdef FEATURE_DEBUG_OPT
+                                       if (DebugOpt) {
+                                               crondlog("\111User %s Entry %s\n", fileName, buf);
+                                       }
+#endif
+
+                                       /* parse date ranges */
+                                       ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
+                                       ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
+                                       ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
+                                       ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
+                                       ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
+
+                                       /* check failure */
+                                       if (ptr == NULL) {
+                                               continue;
+                                       }
+
+                                       /*
+                                        * fix days and dow - if one is not * and the other
+                                        * is *, the other is set to 0, and vise-versa
+                                        */
+
+                                       FixDayDow(&line);
+
+                                       *pline = calloc(1, sizeof(CronLine));
+                                       **pline = line;
+
+                                       /* copy command */
+                                       (*pline)->cl_Shell = strdup(ptr);
+
+#ifdef FEATURE_DEBUG_OPT
+                                       if (DebugOpt) {
+                                               crondlog("\111    Command %s\n", ptr);
+                                       }
+#endif
+
+                                       pline = &((*pline)->cl_Next);
+                               }
+                               *pline = NULL;
+
+                               file->cf_Next = FileBase;
+                               FileBase = file;
+
+                               if (maxLines == 0 || maxEntries == 0) {
+                                       crondlog("\111Maximum number of lines reached for user %s\n", fileName);
+                               }
+                       }
+                       fclose(fi);
+               }
+       }
+}
+
+static void CheckUpdates(void)
+{
+       FILE *fi;
+       char buf[256];
+
+       fi = fopen(CRONUPDATE, "r");
+       if (fi != NULL) {
+               remove(CRONUPDATE);
+               while (fgets(buf, sizeof(buf), fi) != NULL) {
+                       SynchronizeFile(strtok(buf, " \t\r\n"));
+               }
+               fclose(fi);
+       }
+}
+
+static void SynchronizeDir(void)
+{
+       /* Attempt to delete the database. */
+
+       for (;;) {
+               CronFile *file;
+
+               for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);
+               if (file == NULL) {
+                       break;
+               }
+               DeleteFile(file->cf_User);
+       }
+
+       /*
+        * Remove cron update file
+        *
+        * Re-chdir, in case directory was renamed & deleted, or otherwise
+        * screwed up.
+        *
+        * scan directory and add associated users
+        */
+
+       remove(CRONUPDATE);
+       if (chdir(CDir) < 0) {
+               crondlog("\311unable to find %s\n", CDir);
+       }
+       {
+               DIR *dir = opendir(".");
+               struct dirent *den;
+
+               if (dir) {
+                       while ((den = readdir(dir))) {
+                               if (strchr(den->d_name, '.') != NULL) {
+                                       continue;
+                               }
+                               if (getpwnam(den->d_name)) {
+                                       SynchronizeFile(den->d_name);
+                               } else {
+                                       crondlog("\007ignoring %s\n", den->d_name);
+                               }
+                       }
+                       closedir(dir);
+               } else {
+                       crondlog("\311Unable to open current dir!\n");
+               }
+       }
+}
+
+
+/*
+ *  DeleteFile() - delete user database
+ *
+ *  Note: multiple entries for same user may exist if we were unable to
+ *  completely delete a database due to running processes.
+ */
+
+static void DeleteFile(const char *userName)
+{
+       CronFile **pfile = &FileBase;
+       CronFile *file;
+
+       while ((file = *pfile) != NULL) {
+               if (strcmp(userName, file->cf_User) == 0) {
+                       CronLine **pline = &file->cf_LineBase;
+                       CronLine *line;
+
+                       file->cf_Running = 0;
+                       file->cf_Deleted = 1;
+
+                       while ((line = *pline) != NULL) {
+                               if (line->cl_Pid > 0) {
+                                       file->cf_Running = 1;
+                                       pline = &line->cl_Next;
+                               } else {
+                                       *pline = line->cl_Next;
+                                       free(line->cl_Shell);
+                                       free(line);
+                               }
+                       }
+                       if (file->cf_Running == 0) {
+                               *pfile = file->cf_Next;
+                               free(file->cf_User);
+                               free(file);
+                       } else {
+                               pfile = &file->cf_Next;
+                       }
+               } else {
+                       pfile = &file->cf_Next;
+               }
+       }
+}
+
+/*
+ * TestJobs()
+ *
+ * determine which jobs need to be run.  Under normal conditions, the
+ * period is about a minute (one scan).  Worst case it will be one
+ * hour (60 scans).
+ */
+
+static int TestJobs(time_t t1, time_t t2)
+{
+       short nJobs = 0;
+       time_t t;
+
+       /* Find jobs > t1 and <= t2 */
+
+       for (t = t1 - t1 % 60; t <= t2; t += 60) {
+               if (t > t1) {
+                       struct tm *tp = localtime(&t);
+                       CronFile *file;
+                       CronLine *line;
+
+                       for (file = FileBase; file; file = file->cf_Next) {
+#ifdef FEATURE_DEBUG_OPT
+                               if (DebugOpt)
+                                       crondlog("\005FILE %s:\n", file->cf_User);
+#endif
+                               if (file->cf_Deleted)
+                                       continue;
+                               for (line = file->cf_LineBase; line; line = line->cl_Next) {
+#ifdef FEATURE_DEBUG_OPT
+                                       if (DebugOpt)
+                                               crondlog("\005    LINE %s\n", line->cl_Shell);
+#endif
+                                       if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&
+                                               (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
+                                               && line->cl_Mons[tp->tm_mon]) {
+#ifdef FEATURE_DEBUG_OPT
+                                               if (DebugOpt) {
+                                                       crondlog("\005    JobToDo: %d %s\n",
+                                                               line->cl_Pid, line->cl_Shell);
+                                               }
+#endif
+                                               if (line->cl_Pid > 0) {
+                                                       crondlog("\010    process already running: %s %s\n",
+                                                               file->cf_User, line->cl_Shell);
+                                               } else if (line->cl_Pid == 0) {
+                                                       line->cl_Pid = -1;
+                                                       file->cf_Ready = 1;
+                                                       ++nJobs;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       return (nJobs);
+}
+
+static void RunJobs(void)
+{
+       CronFile *file;
+       CronLine *line;
+
+       for (file = FileBase; file; file = file->cf_Next) {
+               if (file->cf_Ready) {
+                       file->cf_Ready = 0;
+
+                       for (line = file->cf_LineBase; line; line = line->cl_Next) {
+                               if (line->cl_Pid < 0) {
+
+                                       RunJob(file->cf_User, line);
+
+                                       crondlog("\010USER %s pid %3d cmd %s\n",
+                                               file->cf_User, line->cl_Pid, line->cl_Shell);
+                                       if (line->cl_Pid < 0) {
+                                               file->cf_Ready = 1;
+                                       }
+                                       else if (line->cl_Pid > 0) {
+                                               file->cf_Running = 1;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/*
+ * CheckJobs() - check for job completion
+ *
+ * Check for job completion, return number of jobs still running after
+ * all done.
+ */
+
+static int CheckJobs(void)
+{
+       CronFile *file;
+       CronLine *line;
+       int nStillRunning = 0;
+
+       for (file = FileBase; file; file = file->cf_Next) {
+               if (file->cf_Running) {
+                       file->cf_Running = 0;
+
+                       for (line = file->cf_LineBase; line; line = line->cl_Next) {
+                               if (line->cl_Pid > 0) {
+                                       int status;
+                                       int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
+
+                                       if (r < 0 || r == line->cl_Pid) {
+                                               EndJob(file->cf_User, line);
+                                               if (line->cl_Pid) {
+                                                       file->cf_Running = 1;
+                                               }
+                                       } else if (r == 0) {
+                                               file->cf_Running = 1;
+                                       }
+                               }
+                       }
+               }
+               nStillRunning += file->cf_Running;
+       }
+       return (nStillRunning);
+}
+
+
+#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
+static void
+ForkJob(const char *user, CronLine * line, int mailFd,
+               const char *prog, const char *cmd, const char *arg, const char *mailf)
+{
+       /* Fork as the user in question and run program */
+       pid_t pid = fork();
+
+       line->cl_Pid = pid;
+       if (pid == 0) {
+               /* CHILD */
+
+               /* Change running state to the user in question */
+
+               if (ChangeUser(user) < 0) {
+                       exit(0);
+               }
+#ifdef FEATURE_DEBUG_OPT
+               if (DebugOpt) {
+                       crondlog("\005Child Running %s\n", prog);
+               }
+#endif
+
+               if (mailFd >= 0) {
+                       dup2(mailFd, mailf != NULL);
+                       dup2((mailf ? mailFd : 1), 2);
+                       close(mailFd);
+               }
+               execl(prog, prog, cmd, arg, NULL);
+               crondlog("\024unable to exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);
+               if (mailf) {
+                       fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
+               }
+               exit(0);
+       } else if (pid < 0) {
+               /* FORK FAILED */
+               crondlog("\024couldn't fork, user %s\n", user);
+               line->cl_Pid = 0;
+               if (mailf) {
+                       remove(mailf);
+               }
+       } else if (mailf) {
+               /* PARENT, FORK SUCCESS
+                * rename mail-file based on pid of process
+                */
+               char mailFile2[128];
+
+               snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);
+               rename(mailf, mailFile2);
+       }
+       /*
+        * Close the mail file descriptor.. we can't just leave it open in
+        * a structure, closing it later, because we might run out of descriptors
+        */
+
+       if (mailFd >= 0) {
+               close(mailFd);
+       }
+}
+
+static void RunJob(const char *user, CronLine * line)
+{
+       char mailFile[128];
+       int mailFd;
+
+       line->cl_Pid = 0;
+       line->cl_MailFlag = 0;
+
+       /* open mail file - owner root so nobody can screw with it. */
+
+       snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
+       mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
+
+       if (mailFd >= 0) {
+               line->cl_MailFlag = 1;
+               fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
+                       line->cl_Shell);
+               line->cl_MailPos = lseek(mailFd, 0, 1);
+       } else {
+               crondlog("\024unable to create mail file user %s file %s, output to /dev/null\n", user, mailFile);
+       }
+
+       ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
+}
+
+/*
+ * EndJob - called when job terminates and when mail terminates
+ */
+
+static void EndJob(const char *user, CronLine * line)
+{
+       int mailFd;
+       char mailFile[128];
+       struct stat sbuf;
+
+       /* No job */
+
+       if (line->cl_Pid <= 0) {
+               line->cl_Pid = 0;
+               return;
+       }
+
+       /*
+        * End of job and no mail file
+        * End of sendmail job
+        */
+
+       snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
+       line->cl_Pid = 0;
+
+       if (line->cl_MailFlag != 1) {
+               return;
+       }
+       line->cl_MailFlag = 0;
+
+       /*
+        * End of primary job - check for mail file.  If size has increased and
+        * the file is still valid, we sendmail it.
+        */
+
+       mailFd = open(mailFile, O_RDONLY);
+       remove(mailFile);
+       if (mailFd < 0) {
+               return;
+       }
+
+       if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid ||     sbuf.st_nlink != 0 ||
+               sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) {
+               close(mailFd);
+               return;
+       }
+       ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
+}
+#else
+/* crond without sendmail */
+
+static void RunJob(const char *user, CronLine * line)
+{
+       /* Fork as the user in question and run program */
+       pid_t pid = fork();
+
+       if (pid == 0) {
+               /* CHILD */
+
+               /* Change running state to the user in question */
+
+               if (ChangeUser(user) < 0) {
+                       exit(0);
+               }
+#ifdef FEATURE_DEBUG_OPT
+               if (DebugOpt) {
+                       crondlog("\005Child Running %s\n", DEFAULT_SHELL);
+               }
+#endif
+
+               execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
+               crondlog("\024unable to exec, user %s cmd %s -c %s\n", user,
+                                DEFAULT_SHELL, line->cl_Shell);
+               exit(0);
+       } else if (pid < 0) {
+               /* FORK FAILED */
+               crondlog("\024couldn't fork, user %s\n", user);
+               pid = 0;
+       }
+       line->cl_Pid = pid;
+}
+#endif                                                 /* CONFIG_FEATURE_CROND_CALL_SENDMAIL */
diff --git a/busybox/miscutils/crontab.c b/busybox/miscutils/crontab.c
new file mode 100644 (file)
index 0000000..89e1377
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * CRONTAB
+ *
+ * usually setuid root, -c option only works if getuid() == geteuid()
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * May be distributed under the GNU General Public License
+ *
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+#ifndef CRONTABS
+#define CRONTABS        "/var/spool/cron/crontabs"
+#endif
+#ifndef TMPDIR
+#define TMPDIR          "/var/spool/cron"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE      "cron.update"
+#endif
+#ifndef PATH_VI
+#define PATH_VI         "/bin/vi"   /* location of vi       */
+#endif
+
+#include "busybox.h"
+
+static const char  *CDir = CRONTABS;
+
+static void EditFile(const char *user, const char *file);
+static int GetReplaceStream(const char *user, const char *file);
+static int  ChangeUser(const char *user, short dochdir);
+
+int
+crontab_main(int ac, char **av)
+{
+    enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
+    const struct passwd *pas;
+    const char *repFile = NULL;
+    int repFd = 0;
+    int i;
+    char caller[256];           /* user that ran program */
+    int   UserId;
+
+    UserId = getuid();
+    if ((pas = getpwuid(UserId)) == NULL)
+       bb_perror_msg_and_die("getpwuid");
+
+    strncpy(caller, pas->pw_name, sizeof(caller));
+
+    i = 1;
+    if (ac > 1) {
+       if (av[1][0] == '-' && av[1][1] == 0) {
+           option = REPLACE;
+           ++i;
+       } else if (av[1][0] != '-') {
+           option = REPLACE;
+           ++i;
+           repFile = av[1];
+       }
+    }
+
+    for (; i < ac; ++i) {
+       char *ptr = av[i];
+
+       if (*ptr != '-')
+           break;
+       ptr += 2;
+
+       switch(ptr[-1]) {
+       case 'l':
+           if (ptr[-1] == 'l')
+               option = LIST;
+           /* fall through */
+       case 'e':
+           if (ptr[-1] == 'e')
+               option = EDIT;
+           /* fall through */
+       case 'd':
+           if (ptr[-1] == 'd')
+               option = DELETE;
+           /* fall through */
+       case 'u':
+           if (i + 1 < ac && av[i+1][0] != '-') {
+               ++i;
+               if (getuid() == geteuid()) {
+                   pas = getpwnam(av[i]);
+                   if (pas) {
+                       UserId = pas->pw_uid;
+                   } else {
+                       bb_error_msg_and_die("user %s unknown", av[i]);
+                   }
+               } else {
+                   bb_error_msg_and_die("only the superuser may specify a user");
+               }
+           }
+           break;
+       case 'c':
+           if (getuid() == geteuid()) {
+               CDir = (*ptr) ? ptr : av[++i];
+           } else {
+               bb_error_msg_and_die("-c option: superuser only");
+           }
+           break;
+       default:
+           i = ac;
+           break;
+       }
+    }
+    if (i != ac || option == NONE)
+       bb_show_usage();
+
+    /*
+     * Get password entry
+     */
+
+    if ((pas = getpwuid(UserId)) == NULL)
+       bb_perror_msg_and_die("getpwuid");
+
+    /*
+     * If there is a replacement file, obtain a secure descriptor to it.
+     */
+
+    if (repFile) {
+       repFd = GetReplaceStream(caller, repFile);
+       if (repFd < 0)
+           bb_error_msg_and_die("unable to read replacement file");
+    }
+
+    /*
+     * Change directory to our crontab directory
+     */
+
+    if (chdir(CDir) < 0)
+       bb_perror_msg_and_die("cannot change dir to %s", CDir);
+
+    /*
+     * Handle options as appropriate
+     */
+
+    switch(option) {
+    case LIST:
+       {
+           FILE *fi;
+           char buf[1024];
+
+           if ((fi = fopen(pas->pw_name, "r"))) {
+               while (fgets(buf, sizeof(buf), fi) != NULL)
+                   fputs(buf, stdout);
+               fclose(fi);
+           } else {
+               bb_error_msg("no crontab for %s", pas->pw_name);
+           }
+       }
+       break;
+    case EDIT:
+       {
+           FILE *fi;
+           int fd;
+           int n;
+           char tmp[128];
+           char buf[1024];
+
+           snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
+           if ((fd = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) >= 0) {
+               chown(tmp, getuid(), getgid());
+               if ((fi = fopen(pas->pw_name, "r"))) {
+                   while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
+                       write(fd, buf, n);
+               }
+               EditFile(caller, tmp);
+               remove(tmp);
+               lseek(fd, 0L, 0);
+               repFd = fd;
+           } else {
+               bb_error_msg_and_die("unable to create %s", tmp);
+           }
+
+       }
+       option = REPLACE;
+       /* fall through */
+    case REPLACE:
+       {
+           char buf[1024];
+           char path[1024];
+           int fd;
+           int n;
+
+           snprintf(path, sizeof(path), "%s.new", pas->pw_name);
+           if ((fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600)) >= 0) {
+               while ((n = read(repFd, buf, sizeof(buf))) > 0) {
+                   write(fd, buf, n);
+               }
+               close(fd);
+               rename(path, pas->pw_name);
+           } else {
+               bb_error_msg("unable to create %s/%s", CDir, path);
+           }
+           close(repFd);
+       }
+       break;
+    case DELETE:
+       remove(pas->pw_name);
+       break;
+    case NONE:
+    default:
+       break;
+    }
+
+    /*
+     *  Bump notification file.  Handle window where crond picks file up
+     *  before we can write our entry out.
+     */
+
+    if (option == REPLACE || option == DELETE) {
+       FILE *fo;
+       struct stat st;
+
+       while ((fo = fopen(CRONUPDATE, "a"))) {
+           fprintf(fo, "%s\n", pas->pw_name);
+           fflush(fo);
+           if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
+               fclose(fo);
+               break;
+           }
+           fclose(fo);
+           /* loop */
+       }
+       if (fo == NULL) {
+           bb_error_msg("unable to append to %s/%s", CDir, CRONUPDATE);
+       }
+    }
+    return 0;
+}
+
+static int
+GetReplaceStream(const char *user, const char *file)
+{
+    int filedes[2];
+    int pid;
+    int fd;
+    int n;
+    char buf[1024];
+
+    if (pipe(filedes) < 0) {
+       perror("pipe");
+       return(-1);
+    }
+    if ((pid = fork()) < 0) {
+       perror("fork");
+       return(-1);
+    }
+    if (pid > 0) {
+       /*
+        * PARENT
+        */
+
+       close(filedes[1]);
+       if (read(filedes[0], buf, 1) != 1) {
+           close(filedes[0]);
+           filedes[0] = -1;
+       }
+       return(filedes[0]);
+    }
+
+    /*
+     * CHILD
+     */
+
+    close(filedes[0]);
+
+    if (ChangeUser(user, 0) < 0)
+       exit(0);
+
+    fd = open(file, O_RDONLY);
+    if (fd < 0) {
+       bb_error_msg("unable to open %s", file);
+       exit(0);
+    }
+    buf[0] = 0;
+    write(filedes[1], buf, 1);
+    while ((n = read(fd, buf, sizeof(buf))) > 0) {
+       write(filedes[1], buf, n);
+    }
+    exit(0);
+}
+
+static void
+EditFile(const char *user, const char *file)
+{
+    int pid;
+
+    if ((pid = fork()) == 0) {
+       /*
+        * CHILD - change user and run editor
+        */
+       char *ptr;
+       char visual[1024];
+
+       if (ChangeUser(user, 1) < 0)
+           exit(0);
+       if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) > 256)
+           ptr = PATH_VI;
+
+       snprintf(visual, sizeof(visual), "%s %s", ptr, file);
+       execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", visual, NULL);
+       perror("exec");
+       exit(0);
+    }
+    if (pid < 0) {
+       /*
+        * PARENT - failure
+        */
+       bb_perror_msg_and_die("fork");
+    }
+    wait4(pid, NULL, 0, NULL);
+}
+
+static int
+ChangeUser(const char *user, short dochdir)
+{
+    struct passwd *pas;
+
+    /*
+     * Obtain password entry and change privileges
+     */
+
+    if ((pas = getpwnam(user)) == 0) {
+       bb_perror_msg_and_die("failed to get uid for %s", user);
+       return(-1);
+    }
+    setenv("USER", pas->pw_name, 1);
+    setenv("HOME", pas->pw_dir, 1);
+    setenv("SHELL", DEFAULT_SHELL, 1);
+
+    /*
+     * Change running state to the user in question
+     */
+    change_identity(pas);
+
+    if (dochdir) {
+       if (chdir(pas->pw_dir) < 0) {
+           bb_perror_msg_and_die("chdir failed: %s %s", user, pas->pw_dir);
+           if (chdir(TMPDIR) < 0) {
+               bb_perror_msg_and_die("chdir failed: %s %s", user, TMPDIR);
+               return(-1);
+           }
+       }
+    }
+    return(pas->pw_uid);
+}
diff --git a/busybox/miscutils/dc.c b/busybox/miscutils/dc.c
new file mode 100644 (file)
index 0000000..112f6df
--- /dev/null
@@ -0,0 +1,228 @@
+/* vi: set sw=4 ts=4: */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include "busybox.h"
+
+/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
+
+static double stack[100];
+static unsigned int pointer;
+static unsigned char base;
+
+static void push(double a)
+{
+       if (pointer >= (sizeof(stack) / sizeof(*stack)))
+               bb_error_msg_and_die("stack overflow");
+       stack[pointer++] = a;
+}
+
+static double pop(void)
+{
+       if (pointer == 0)
+               bb_error_msg_and_die("stack underflow");
+       return stack[--pointer];
+}
+
+static void add(void)
+{
+       push(pop() + pop());
+}
+
+static void sub(void)
+{
+       double subtrahend = pop();
+
+       push(pop() - subtrahend);
+}
+
+static void mul(void)
+{
+       push(pop() * pop());
+}
+
+static void power(void)
+{
+       double topower = pop();
+
+       push(pow(pop(), topower));
+}
+
+static void divide(void)
+{
+       double divisor = pop();
+
+       push(pop() / divisor);
+}
+
+static void mod(void)
+{
+       unsigned int d = pop();
+
+       push((unsigned int) pop() % d);
+}
+
+static void and(void)
+{
+       push((unsigned int) pop() & (unsigned int) pop());
+}
+
+static void or(void)
+{
+       push((unsigned int) pop() | (unsigned int) pop());
+}
+
+static void eor(void)
+{
+       push((unsigned int) pop() ^ (unsigned int) pop());
+}
+
+static void not(void)
+{
+       push(~(unsigned int) pop());
+}
+
+static void set_output_base(void)
+{
+       base=(unsigned char)pop();
+       if ((base != 10) && (base != 16)) {
+               fprintf(stderr, "Error: base = %d is not supported.\n", base);
+               base=10;
+       }
+}
+
+static void print_base(double print)
+{
+       if (base == 16)
+               printf("%x\n", (unsigned int)print);
+       else
+       printf("%g\n", print);
+}
+
+static void print_stack_no_pop(void)
+{
+       unsigned int i=pointer;
+       while (i)
+               print_base(stack[--i]);
+}
+
+static void print_no_pop(void)
+{
+       print_base(stack[pointer-1]);
+}
+
+struct op {
+       const char *name;
+       void (*function) (void);
+};
+
+static const struct op operators[] = {
+       {"+",   add},
+       {"add", add},
+       {"-",   sub},
+       {"sub", sub},
+       {"*",   mul},
+       {"mul", mul},
+       {"/",   divide},
+       {"div", divide},
+       {"**",  power},
+       {"exp", power},
+       {"pow", power},
+       {"%",   mod},
+       {"mod", mod},
+       {"and", and},
+       {"or",  or},
+       {"not", not},
+       {"eor", eor},
+       {"xor", eor},
+       {"p", print_no_pop},
+       {"f", print_stack_no_pop},
+       {"o", set_output_base},
+       {0,     0}
+};
+
+static void stack_machine(const char *argument)
+{
+       char *endPointer = 0;
+       double d;
+       const struct op *o = operators;
+
+       if (argument == 0)
+               return;
+
+       d = strtod(argument, &endPointer);
+
+       if (endPointer != argument) {
+               push(d);
+               return;
+       }
+
+       while (o->name != 0) {
+               if (strcmp(o->name, argument) == 0) {
+                       (*(o->function)) ();
+                       return;
+               }
+               o++;
+       }
+       bb_error_msg_and_die("%s: syntax error.", argument);
+}
+
+/* return pointer to next token in buffer and set *buffer to one char
+ * past the end of the above mentioned token
+ */
+static char *get_token(char **buffer)
+{
+       char *start   = NULL;
+       char *current = *buffer;
+
+       while (isspace(*current)) { current++; }
+       if (*current != 0) {
+               start = current;
+               while (!isspace(*current) && *current != 0) { current++; }
+               *buffer = current;
+       }
+       return start;
+}
+
+/* In Perl one might say, scalar m|\s*(\S+)\s*|g */
+static int number_of_tokens(char *buffer)
+{
+       int   i = 0;
+       char *b = buffer;
+       while (get_token(&b)) { i++; }
+       return i;
+}
+
+int dc_main(int argc, char **argv)
+{
+       /* take stuff from stdin if no args are given */
+       if (argc <= 1) {
+               int i, len;
+               char *line   = NULL;
+               char *cursor = NULL;
+               char *token  = NULL;
+               while ((line = bb_get_chomped_line_from_file(stdin))) {
+                       cursor = line;
+                       len = number_of_tokens(line);
+                       for (i = 0; i < len; i++) {
+                               token = get_token(&cursor);
+                               *cursor++ = 0;
+                               stack_machine(token);
+                       }
+                       free(line);
+               }
+       } else {
+               if (*argv[1]=='-')
+                       bb_show_usage();
+               while (argc >= 2) {
+                       stack_machine(argv[1]);
+                       argv++;
+                       argc--;
+               }
+       }
+       stack_machine(0);
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/miscutils/devfsd.c b/busybox/miscutils/devfsd.c
new file mode 100644 (file)
index 0000000..5e183e6
--- /dev/null
@@ -0,0 +1,2183 @@
+/*
+       devfsd implementation for busybox
+
+       Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
+
+       Busybox version is based on some previous work and ideas
+       Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+
+       devfsd.c
+
+       Main file for  devfsd  (devfs daemon for Linux).
+
+    Copyright (C) 1998-2002  Richard Gooch
+
+       devfsd.h
+
+    Header file for  devfsd  (devfs daemon for Linux).
+
+    Copyright (C) 1998-2000  Richard Gooch
+
+       compat_name.c
+
+    Compatibility name file for  devfsd  (build compatibility names).
+
+    Copyright (C) 1998-2002  Richard Gooch
+
+       expression.c
+
+    This code provides Borne Shell-like expression expansion.
+
+    Copyright (C) 1997-1999  Richard Gooch
+
+       This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+    The postal address is:
+      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+*/
+
+#include "libbb.h"
+#include "busybox.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <signal.h>
+#include <regex.h>
+#include <errno.h>
+#include <sys/sysmacros.h>
+
+
+/* Various defines taken from linux/major.h */
+#define IDE0_MAJOR     3
+#define IDE1_MAJOR     22
+#define IDE2_MAJOR     33
+#define IDE3_MAJOR     34
+#define IDE4_MAJOR     56
+#define IDE5_MAJOR     57
+#define IDE6_MAJOR     88
+#define IDE7_MAJOR     89
+#define IDE8_MAJOR     90
+#define IDE9_MAJOR     91
+
+
+/* Various defines taken from linux/devfs_fs.h */
+#define DEVFSD_PROTOCOL_REVISION_KERNEL  5
+#define        DEVFSD_IOCTL_BASE       'd'
+/*  These are the various ioctls  */
+#define DEVFSDIOC_GET_PROTO_REV         _IOR(DEVFSD_IOCTL_BASE, 0, int)
+#define DEVFSDIOC_SET_EVENT_MASK        _IOW(DEVFSD_IOCTL_BASE, 2, int)
+#define DEVFSDIOC_RELEASE_EVENT_QUEUE   _IOW(DEVFSD_IOCTL_BASE, 3, int)
+#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
+#define DEVFSD_NOTIFY_REGISTERED    0
+#define DEVFSD_NOTIFY_UNREGISTERED  1
+#define DEVFSD_NOTIFY_ASYNC_OPEN    2
+#define DEVFSD_NOTIFY_CLOSE         3
+#define DEVFSD_NOTIFY_LOOKUP        4
+#define DEVFSD_NOTIFY_CHANGE        5
+#define DEVFSD_NOTIFY_CREATE        6
+#define DEVFSD_NOTIFY_DELETE        7
+#define DEVFS_PATHLEN               1024
+/*  Never change this otherwise the binary interface will change   */
+
+struct devfsd_notify_struct
+{   /*  Use native C types to ensure same types in kernel and user space     */
+    unsigned int type;           /*  DEVFSD_NOTIFY_* value                   */
+    unsigned int mode;           /*  Mode of the inode or device entry       */
+    unsigned int major;          /*  Major number of device entry            */
+    unsigned int minor;          /*  Minor number of device entry            */
+    unsigned int uid;            /*  Uid of process, inode or device entry   */
+    unsigned int gid;            /*  Gid of process, inode or device entry   */
+    unsigned int overrun_count;  /*  Number of lost events                   */
+    unsigned int namelen;        /*  Number of characters not including '\0' */
+    /*  The device name MUST come last                                       */
+    char devname[DEVFS_PATHLEN]; /*  This will be '\0' terminated            */
+};
+
+#define BUFFER_SIZE 16384
+#define DEVFSD_VERSION "1.3.25"
+#define CONFIG_FILE  "/etc/devfsd.conf"
+#define MODPROBE               "/sbin/modprobe"
+#define MODPROBE_SWITCH_1 "-k"
+#define MODPROBE_SWITCH_2 "-C"
+#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
+#define MAX_ARGS     (6 + 1)
+#define MAX_SUBEXPR  10
+#define STRING_LENGTH 255
+
+/* for get_uid_gid() */
+#define UID                    0
+#define GID                    1
+
+/*     for msg_logger(), do_ioctl(),
+       fork_and_execute() */
+# define DIE                   1
+# define NO_DIE                        0
+
+/* for dir_operation() */
+#define RESTORE                0
+#define SERVICE                1
+#define READ_CONFIG 2
+
+/*  Update only after changing code to reflect new protocol  */
+#define DEVFSD_PROTOCOL_REVISION_DAEMON  5
+
+/*  Compile-time check  */
+#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
+#error protocol version mismatch. Update your kernel headers
+#endif
+
+#define AC_PERMISSIONS                         0
+#define AC_MODLOAD                                     1
+#define AC_EXECUTE                                     2
+#define AC_MFUNCTION                           3       /* not supported by busybox */
+#define AC_CFUNCTION                           4       /* not supported by busybox */
+#define AC_COPY                                                5
+#define AC_IGNORE                                      6
+#define AC_MKOLDCOMPAT                         7
+#define AC_MKNEWCOMPAT                         8
+#define AC_RMOLDCOMPAT                         9
+#define AC_RMNEWCOMPAT                         10
+#define AC_RESTORE                                     11
+
+
+struct permissions_type
+{
+    mode_t mode;
+    uid_t uid;
+    gid_t gid;
+};
+
+struct execute_type
+{
+    char *argv[MAX_ARGS + 1];  /*  argv[0] must always be the programme  */
+};
+
+struct copy_type
+{
+    const char *source;
+    const char *destination;
+};
+
+struct action_type
+{
+    unsigned int what;
+    unsigned int when;
+};
+
+struct config_entry_struct
+{
+    struct action_type action;
+    regex_t preg;
+    union
+    {
+       struct permissions_type permissions;
+       struct execute_type execute;
+       struct copy_type copy;
+    }
+    u;
+    struct config_entry_struct *next;
+};
+
+struct get_variable_info
+{
+    const struct devfsd_notify_struct *info;
+    const char *devname;
+    char devpath[STRING_LENGTH];
+};
+
+static void dir_operation(int , const char * , int,  unsigned long* );
+static void service(struct stat statbuf, char *path);
+static int st_expr_expand(char *, unsigned, const char *, const char *(*) (const char *, void *), void *);
+static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
+static int mksymlink (const char *oldpath, const char *newpath);
+static void read_config_file (char *path, int optional, unsigned long *event_mask);
+static void process_config_line (const char *, unsigned long *);
+static int  do_servicing (int, unsigned long);
+static void service_name (const struct devfsd_notify_struct *);
+static void action_permissions (const struct devfsd_notify_struct *, const struct config_entry_struct *);
+static void action_execute (const struct devfsd_notify_struct *, const struct config_entry_struct *,
+                                                       const regmatch_t *, unsigned);
+#ifdef CONFIG_DEVFSD_MODLOAD
+static void action_modload (const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
+#endif
+static void action_copy (const struct devfsd_notify_struct *, const struct config_entry_struct *,
+                                                const regmatch_t *, unsigned);
+static void action_compat (const struct devfsd_notify_struct *, unsigned);
+static void free_config (void);
+static void restore(char *spath, struct stat source_stat, int rootlen);
+static int copy_inode (const char *, const struct stat *, mode_t, const char *, const struct stat *);
+static mode_t get_mode (const char *);
+static void signal_handler (int);
+static const char *get_variable (const char *, void *);
+static int make_dir_tree (const char *);
+static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
+                                                        const char *, const regmatch_t *, unsigned );
+static void expand_regexp (char *, size_t, const char *, const char *, const regmatch_t *, unsigned );
+static const char *expand_variable(    char *, unsigned, unsigned *, const char *,
+                                                                       const char *(*) (const char *, void *), void * );
+static const char *get_variable_v2(const char *, const char *(*) (const char *, void *), void *);
+static char get_old_ide_name (unsigned , unsigned);
+static char *write_old_sd_name (char *, unsigned, unsigned, char *);
+
+/* busybox functions */
+#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
+static void msg_logger(int die, int pri, const char * fmt, ... );
+#endif
+static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag);
+static void fork_and_execute(int die, char *arg0, char **arg );
+static int get_uid_gid ( int, const char *);
+static void safe_memcpy( char * dest, const char * src, int len);
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr);
+static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr);
+
+/* Structs and vars */
+static struct config_entry_struct *first_config = NULL;
+static struct config_entry_struct *last_config = NULL;
+static const char *mount_point = NULL;
+static volatile int caught_signal = FALSE;
+static volatile int caught_sighup = FALSE;
+static struct initial_symlink_struct
+{
+    char *dest;
+    char *name;
+} initial_symlinks[] =
+{
+    {"/proc/self/fd", "fd"},
+    {"fd/0", "stdin"},
+    {"fd/1", "stdout"},
+    {"fd/2", "stderr"},
+    {NULL, NULL},
+};
+
+static struct event_type
+{
+    unsigned int type;        /*  The DEVFSD_NOTIFY_* value                  */
+    const char *config_name;  /*  The name used in the config file           */
+} event_types[] =
+{
+    {DEVFSD_NOTIFY_REGISTERED,   "REGISTER"},
+    {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
+    {DEVFSD_NOTIFY_ASYNC_OPEN,   "ASYNC_OPEN"},
+    {DEVFSD_NOTIFY_CLOSE,        "CLOSE"},
+    {DEVFSD_NOTIFY_LOOKUP,       "LOOKUP"},
+    {DEVFSD_NOTIFY_CHANGE,       "CHANGE"},
+    {DEVFSD_NOTIFY_CREATE,       "CREATE"},
+    {DEVFSD_NOTIFY_DELETE,       "DELETE"},
+    {0xffffffff,                 NULL}
+};
+
+/* busybox functions and messages */
+
+extern void xregcomp(regex_t * preg, const char *regex, int cflags);
+
+const char * const bb_msg_proto_rev                    = "protocol revision";
+#ifdef CONFIG_DEVFSD_VERBOSE
+const char * const bb_msg_bad_config                   = "bad %s config file: %s\n";
+const char * const bb_msg_small_buffer         = "buffer too small\n";
+const char * const bb_msg_variable_not_found = "variable: %s not found\n";
+#endif
+
+#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
+static void msg_logger(int die, int pri, const char * fmt, ... )
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (access ("/dev/log", F_OK) == 0)
+       {
+               openlog(bb_applet_name, 0, LOG_DAEMON);
+               vsyslog( pri , fmt , ap);
+               closelog();
+       }
+#ifndef CONFIG_DEBUG
+       else
+#endif
+               bb_verror_msg(fmt, ap);
+       va_end(ap);
+       if(die==DIE)
+               exit(EXIT_FAILURE);
+}
+#endif
+
+static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag)
+{
+#ifdef CONFIG_DEVFSD_VERBOSE
+       if (ioctl (fd, request, event_mask_flag) == -1)
+               msg_logger(die, LOG_ERR, "ioctl(): %m\n");
+#else
+       if (ioctl (fd, request, event_mask_flag) == -1)
+               exit(EXIT_FAILURE);
+#endif
+}
+
+static void fork_and_execute(int die, char *arg0, char **arg )
+{
+       switch ( fork () )
+       {
+       case 0:
+               /*  Child  */
+               break;
+       case -1:
+               /*  Parent: Error  : die or return */
+#ifdef CONFIG_DEVFSD_VERBOSE
+               msg_logger(die, LOG_ERR,(char *) bb_msg_memory_exhausted);
+#else
+               if(die == DIE)
+                       exit(EXIT_FAILURE);
+#endif
+               return;
+       default:
+               /*  Parent : ok : return or exit  */
+               if(arg0 != NULL)
+               {
+                       wait (NULL);
+                       return;
+               }
+               exit (EXIT_SUCCESS);
+       }
+        /* Child : if arg0 != NULL do execvp */
+       if(arg0 != NULL )
+       {
+               execvp (arg0, arg);
+#ifdef CONFIG_DEVFSD_VERBOSE
+               msg_logger(DIE, LOG_ERR, "execvp(): %s: %m\n", arg0);
+#else
+               exit(EXIT_FAILURE);
+#endif
+       }
+}
+
+static void safe_memcpy( char *dest, const char *src, int len)
+{
+       memcpy (dest , src , len );
+       dest[len] = '\0';
+}
+
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr)
+{
+       if( d[n - 4]=='d' && d[n - 3]=='i' && d[n - 2]=='s' && d[n - 1]=='c')
+               return (2 + addendum);
+       else if( d[n - 2]=='c' && d[n - 1]=='d')
+               return (3 + addendum);
+       else if(ptr[0]=='p' && ptr[1]=='a' && ptr[2]=='r' && ptr[3]=='t')
+               return (4 + addendum);
+       else if( ptr[n - 2]=='m' && ptr[n - 1]=='t')
+               return (5 + addendum);
+       else
+               return 0;
+}
+
+static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr)
+{
+       if(d[0]=='s' && d[1]=='c' && d[2]=='s' && d[3]=='i' && d[4]=='/')
+       {
+               if( d[n - 7]=='g' && d[n - 6]=='e' && d[n - 5]=='n' &&
+                       d[n - 4]=='e' && d[n - 3]=='r' && d[n - 2]=='i' &&
+                       d[n - 1]=='c' )
+                       return 1;
+               return scan_dev_name_common(d, n, 0, ptr);
+       }
+       else if(d[0]=='i' && d[1]=='d' && d[2]=='e' && d[3]=='/' &&
+                       d[4]=='h' && d[5]=='o' && d[6]=='s' && d[7]=='t')
+       {
+               return scan_dev_name_common(d, n, 4, ptr);
+       }
+       else if(d[0]=='s' && d[1]=='b' && d[2]=='p' && d[3]=='/')
+       {
+               return 10;
+       }
+       else if(d[0]=='v' && d[1]=='c' && d[2]=='c' && d[3]=='/')
+       {
+               return 11;
+       }
+       else if(d[0]=='p' && d[1]=='t' && d[2]=='y' && d[3]=='/')
+       {
+               return 12;
+       }
+       return 0;
+}
+
+/*  Public functions follow  */
+
+int devfsd_main (int argc, char **argv)
+{
+       int print_version = FALSE;
+#ifdef CONFIG_DEVFSD_FG_NP
+       int do_daemon = TRUE;
+       int no_polling = FALSE;
+#endif
+       int do_scan;
+       int fd, proto_rev, count;
+       unsigned long event_mask = 0;
+       struct sigaction new_action;
+       struct initial_symlink_struct *curr;
+
+       if (argc < 2)
+               bb_show_usage();
+
+       for (count = 2; count < argc; ++count)
+       {
+               if(argv[count][0] == '-')
+               {
+                       if(argv[count][1]=='v' && !argv[count][2]) /* -v */
+                                       print_version = TRUE;
+#ifdef CONFIG_DEVFSD_FG_NP
+                       else if(argv[count][1]=='f' && argv[count][2]=='g' && !argv[count][3]) /* -fg */
+                                       do_daemon = FALSE;
+                       else if(argv[count][1]=='n' && argv[count][2]=='p' && !argv[count][3]) /* -np */
+                                       no_polling = TRUE;
+#endif
+                       else
+                               bb_show_usage();
+               }
+       }
+
+       /* strip last / from mount point, so we don't need to check for it later */
+       while( argv[1][1]!='\0' && argv[1][strlen(argv[1])-1] == '/' )
+               argv[1][strlen(argv[1]) -1] = '\0';
+
+       mount_point = argv[1];
+
+       if (chdir (mount_point) != 0)
+#ifdef CONFIG_DEVFSD_VERBOSE
+               bb_error_msg_and_die( " %s: %m", mount_point);
+#else
+               exit(EXIT_FAILURE);
+#endif
+
+       fd = bb_xopen (".devfsd", O_RDONLY);
+
+       if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0)
+#ifdef CONFIG_DEVFSD_VERBOSE
+               bb_error_msg( "FD_CLOEXEC");
+#else
+               exit(EXIT_FAILURE);
+#endif
+
+       do_ioctl(DIE, fd, DEVFSDIOC_GET_PROTO_REV,(int )&proto_rev);
+
+       /*setup initial entries */
+    for (curr = initial_symlinks; curr->dest != NULL; ++curr)
+               symlink (curr->dest, curr->name);
+
+       /* NB: The check for CONFIG_FILE is done in read_config_file() */
+
+       if ( print_version  || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) )
+       {
+               bb_printf( "%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
+                                        bb_applet_name,DEVFSD_VERSION,bb_msg_proto_rev,
+                                        DEVFSD_PROTOCOL_REVISION_DAEMON,bb_msg_proto_rev, proto_rev);
+               if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
+                       bb_error_msg_and_die( "%s mismatch!",bb_msg_proto_rev);
+               exit(EXIT_SUCCESS); /* -v */
+       }
+       /*  Tell kernel we are special (i.e. we get to see hidden entries)  */
+       do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, 0);
+
+       sigemptyset (&new_action.sa_mask);
+       new_action.sa_flags = 0;
+
+       /*  Set up SIGHUP and SIGUSR1 handlers  */
+       new_action.sa_handler = signal_handler;
+       if (sigaction (SIGHUP, &new_action, NULL) != 0 || sigaction (SIGUSR1, &new_action, NULL) != 0 )
+#ifdef CONFIG_DEVFSD_VERBOSE
+               bb_error_msg_and_die( "sigaction()");
+#else
+               exit(EXIT_FAILURE);
+#endif
+
+       bb_printf("%s v%s  started for %s\n",bb_applet_name, DEVFSD_VERSION, mount_point);
+
+       /*  Set umask so that mknod(2), open(2) and mkdir(2) have complete control      over permissions  */
+       umask (0);
+       read_config_file (CONFIG_FILE, FALSE, &event_mask);
+       /*  Do the scan before forking, so that boot scripts see the finished product  */
+       dir_operation(SERVICE,mount_point,0,NULL);
+#ifdef CONFIG_DEVFSD_FG_NP
+       if (no_polling)
+               exit (0);
+       if (do_daemon)
+       {
+#endif
+               /*  Release so that the child can grab it  */
+               do_ioctl(DIE, fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
+               fork_and_execute(DIE, NULL, NULL);
+               setsid ();        /*  Prevent hangups and become pgrp leader         */
+#ifdef CONFIG_DEVFSD_FG_NP
+       }
+       else
+               setpgid (0, 0);  /*  Become process group leader                    */
+#endif
+
+       while (TRUE)
+       {
+               do_scan = do_servicing (fd, event_mask);
+
+               free_config ();
+               read_config_file (CONFIG_FILE, FALSE, &event_mask);
+               if (do_scan)
+                       dir_operation(SERVICE,mount_point,0,NULL);
+       }
+}   /*  End Function main  */
+
+
+/*  Private functions follow  */
+
+static void read_config_file (char *path, int optional, unsigned long *event_mask)
+/*  [SUMMARY] Read a configuration database.
+    <path> The path to read the database from. If this is a directory, all
+    entries in that directory will be read (except hidden entries).
+    <optional> If TRUE, the routine will silently ignore a missing config file.
+    <event_mask> The event mask is written here. This is not initialised.
+    [RETURNS] Nothing.
+*/
+{
+       struct stat statbuf;
+       FILE *fp;
+       char buf[STRING_LENGTH];
+       char *line=NULL;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "read_config_file(): %s\n", path);
+#endif
+       if (stat (path, &statbuf) != 0 || statbuf.st_size == 0 )
+               goto read_config_file_err;
+
+       if ( S_ISDIR (statbuf.st_mode) )
+       {
+               /* strip last / from dirname so we don't need to check for it later */
+               while( path  && path[1]!='\0' && path[strlen(path)-1] == '/')
+                       path[strlen(path) -1] = '\0';
+
+               dir_operation(READ_CONFIG, path, 0, event_mask);
+               return;
+       }
+
+       if ( ( fp = fopen (path, "r") ) != NULL )
+       {
+               while (fgets (buf, STRING_LENGTH, fp) != NULL)
+               {
+                       /*  GETS(3)       Linux Programmer's Manual
+                       fgets() reads in at most one less than size characters from stream  and
+                       stores  them  into  the buffer pointed to by s.  Reading stops after an
+                       EOF or a newline.  If a newline is read, it is stored into the  buffer.
+                       A '\0' is stored after the last character in the buffer.
+                       */
+                       /*buf[strlen (buf) - 1] = '\0';*/
+                       /*  Skip whitespace  */
+                       for (line = buf; isspace (*line); ++line)
+                               /*VOID*/;
+                       if (line[0] == '\0' || line[0] == '#' )
+                               continue;
+                       process_config_line (line, event_mask);
+               }
+               fclose (fp);
+               errno=0;
+       }
+read_config_file_err:
+#ifdef CONFIG_DEVFSD_VERBOSE
+       msg_logger(((optional ==  0 ) && (errno == ENOENT))? DIE : NO_DIE, LOG_ERR, "read config file: %s: %m\n", path);
+#else
+       if(optional ==  0  && errno == ENOENT)
+               exit(EXIT_FAILURE);
+#endif
+       return;
+}   /*  End Function read_config_file   */
+
+static void process_config_line (const char *line, unsigned long *event_mask)
+/*  [SUMMARY] Process a line from a configuration file.
+    <line> The configuration line.
+    <event_mask> The event mask is written here. This is not initialised.
+    [RETURNS] Nothing.
+*/
+{
+       int  num_args, count;
+       struct config_entry_struct *new;
+       char p[MAX_ARGS][STRING_LENGTH];
+       char when[STRING_LENGTH], what[STRING_LENGTH];
+       char name[STRING_LENGTH];
+       char * msg="";
+       char *ptr;
+
+       /* !!!! Only Uppercase Keywords in devsfd.conf */
+       const char *options[] = {       "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
+                                                               "PERMISSIONS", "MODLOAD", "EXECUTE", "COPY", "IGNORE",
+                                                               "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT", 0 };
+
+       short int i;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "process_config_line()\n");
+#endif
+
+       for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0';
+       num_args = sscanf (line, "%s %s %s %s %s %s %s %s %s %s",
+                       when, name, what,
+                       p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
+
+       i = compare_string_array(options, when );
+
+       /*"CLEAR_CONFIG"*/
+       if( i == 0)
+       {
+               free_config ();
+               *event_mask = 0;
+               return;
+       }
+
+       if ( num_args < 2)
+               goto process_config_line_err;
+
+       /* "INCLUDE" & "OPTIONAL_INCLUDE" */
+       if( i == 1 || i == 2 )
+       {
+               st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL );
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_INFO, "%sinclude: %s\n",(toupper (when[0]) == 'I') ? "": "optional_", name);
+#endif
+               read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask);
+               return;
+       }
+       /* "RESTORE" */
+       if( i == 3)
+       {
+               dir_operation(RESTORE,name, strlen (name),NULL);
+               return;
+       }
+       if (num_args < 3)
+               goto process_config_line_err;
+
+       new = xmalloc (sizeof *new);
+       memset (new, 0, sizeof *new);
+
+       for (count = 0; event_types[count].config_name != NULL; ++count)
+       {
+               if (strcasecmp (when, event_types[count].config_name) != 0)
+                       continue;
+               new->action.when = event_types[count].type;
+               break;
+       }
+       if (event_types[count].config_name == NULL)
+       {
+               msg="WHEN in";
+               goto process_config_line_err;
+       }
+
+       i = compare_string_array(options, what );
+
+       switch(i)
+       {
+               case 4: /* "PERMISSIONS" */
+                       new->action.what = AC_PERMISSIONS;
+                       /*  Get user and group  */
+                       if ( ( ptr = strchr (p[0], '.') ) == NULL )
+                       {
+                               msg="UID.GID";
+                               goto process_config_line_err; /*"missing '.' in UID.GID */
+                       }
+
+                       *ptr++ = '\0';
+                       new->u.permissions.uid = get_uid_gid (UID, p[0]);
+                       new->u.permissions.gid = get_uid_gid (GID, ptr);
+                       /*  Get mode  */
+                       new->u.permissions.mode = get_mode (p[1]);
+                       break;
+#ifdef CONFIG_DEVFSD_MODLOAD
+               case 5: /*  MODLOAD */
+                       /*This  action will pass "/dev/$devname" (i.e. "/dev/" prefixed to
+                       the device name) to the module loading  facility.  In  addition,
+                       the /etc/modules.devfs configuration file is used.*/
+                        new->action.what = AC_MODLOAD;
+                        break;
+#endif
+               case 6: /* EXECUTE */
+                       new->action.what = AC_EXECUTE;
+                       num_args -= 3;
+
+                       for (count = 0; count < num_args; ++count)
+                               new->u.execute.argv[count] = bb_xstrdup (p[count]);
+
+                       new->u.execute.argv[num_args] = NULL;
+                       break;
+               case 7: /* COPY */
+                       new->action.what = AC_COPY;
+                       num_args -= 3;
+                       if (num_args != 2)
+                               goto process_config_line_err; /* missing path and function in line */
+
+                       new->u.copy.source = bb_xstrdup (p[0]);
+                       new->u.copy.destination = bb_xstrdup (p[1]);
+                       break;
+               case 8: /* IGNORE */
+               /* FALLTROUGH */
+               case 9: /* MKOLDCOMPAT */
+               /* FALLTROUGH */
+               case 10: /* MKNEWCOMPAT */
+               /* FALLTROUGH */
+               case 11:/* RMOLDCOMPAT */
+               /* FALLTROUGH */
+               case 12: /* RMNEWCOMPAT */
+               /*      AC_IGNORE                                       6
+                       AC_MKOLDCOMPAT                          7
+                       AC_MKNEWCOMPAT                          8
+                       AC_RMOLDCOMPAT                          9
+                       AC_RMNEWCOMPAT                          10*/
+                       new->action.what = i - 2;
+                       break;
+               default:
+                       msg ="WHAT in";
+                       goto process_config_line_err;
+               /*esac*/
+       } /* switch (i) */
+
+       xregcomp( &new->preg, name, REG_EXTENDED);
+
+       *event_mask |= 1 << new->action.when;
+       new->next = NULL;
+       if (first_config == NULL)
+               first_config = new;
+       else
+               last_config->next = new;
+       last_config = new;
+       return;
+process_config_line_err:
+#ifdef CONFIG_DEVFSD_VERBOSE
+       msg_logger( DIE, LOG_ERR, bb_msg_bad_config, msg , line);
+#else
+       exit(EXIT_FAILURE);
+#endif
+}  /*  End Function process_config_line   */
+
+static int do_servicing (int fd, unsigned long event_mask)
+/*  [SUMMARY] Service devfs changes until a signal is received.
+    <fd> The open control file.
+    <event_mask> The event mask.
+    [RETURNS] TRUE if SIGHUP was caught, else FALSE.
+*/
+{
+       ssize_t bytes;
+       struct devfsd_notify_struct info;
+       unsigned long tmp_event_mask;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "do_servicing()\n");
+#endif
+       /*  Tell devfs what events we care about  */
+       tmp_event_mask = event_mask;
+       do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, tmp_event_mask);
+       while (!caught_signal)
+       {
+               errno = 0;
+               bytes = read (fd, (char *) &info, sizeof info);
+               if (caught_signal)
+                       break;      /*  Must test for this first     */
+               if (errno == EINTR)
+                       continue;  /*  Yes, the order is important  */
+               if (bytes < 1)
+                       break;
+               service_name (&info);
+       }
+       if (caught_signal)
+       {
+               int c_sighup = caught_sighup;
+
+               caught_signal = FALSE;
+               caught_sighup = FALSE;
+               return (c_sighup);
+       }
+#ifdef CONFIG_DEVFSD_VERBOSE
+       msg_logger( NO_DIE, LOG_ERR, "read error on control file: %m\n");
+#endif
+       /* This is to shut up a compiler warning */
+       exit(EXIT_FAILURE);
+}   /*  End Function do_servicing  */
+
+static void service_name (const struct devfsd_notify_struct *info)
+/*  [SUMMARY] Service a single devfs change.
+    <info> The devfs change.
+    [RETURNS] Nothing.
+*/
+{
+       unsigned int n;
+       regmatch_t mbuf[MAX_SUBEXPR];
+       struct config_entry_struct *entry;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "service_name()\n");
+       if (info->overrun_count > 0)
+               msg_logger( NO_DIE, LOG_ERR, "lost %u events\n", info->overrun_count);
+#endif
+
+       /*  Discard lookups on "/dev/log" and "/dev/initctl"  */
+       if(   info->type == DEVFSD_NOTIFY_LOOKUP &&
+               ((info->devname[0]=='l' && info->devname[1]=='o' &&
+                 info->devname[2]=='g' && !info->devname[3]) ||
+               ( info->devname[0]=='i' && info->devname[1]=='n' &&
+                 info->devname[2]=='i' && info->devname[3]=='t' &&
+                 info->devname[4]=='c' && info->devname[5]=='t' &&
+                 info->devname[6]=='l' && !info->devname[7])))
+                       return;
+       for (entry = first_config; entry != NULL; entry = entry->next)
+       {
+               /*  First check if action matches the type, then check if name matches */
+               if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
+                       continue;
+               for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
+                       /* VOID */;
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what);
+#endif
+               switch (entry->action.what)
+               {
+                       case AC_PERMISSIONS:
+                               action_permissions (info, entry);
+                               break;
+#ifdef CONFIG_DEVFSD_MODLOAD
+                       case AC_MODLOAD:
+                               action_modload (info, entry);
+                               break;
+#endif
+                       case AC_EXECUTE:
+                               action_execute (info, entry, mbuf, n);
+                               break;
+                       case AC_COPY:
+                               action_copy (info, entry, mbuf, n);
+                               break;
+                       case AC_IGNORE:
+                               return;
+                               /*break;*/
+                       case AC_MKOLDCOMPAT:
+                       case AC_MKNEWCOMPAT:
+                       case AC_RMOLDCOMPAT:
+                       case AC_RMNEWCOMPAT:
+                               action_compat (info, entry->action.what);
+                               break;
+                       default:
+#ifdef CONFIG_DEVFSD_VERBOSE
+                               msg_logger( DIE, LOG_ERR, "Unknown action\n");
+#else
+                               exit(EXIT_FAILURE);
+#endif
+                               /*break;*/
+               }
+       }
+}   /*  End Function service_name  */
+
+static void action_permissions (const struct devfsd_notify_struct *info,
+                               const struct config_entry_struct *entry)
+/*  [SUMMARY] Update permissions for a device entry.
+    <info> The devfs change.
+    <entry> The config file entry.
+    [RETURNS] Nothing.
+*/
+{
+       struct stat statbuf;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
+#endif
+
+       if ( stat (info->devname, &statbuf) != 0        ||
+                chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
+                chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
+       {
+#ifdef CONFIG_DEVFSD_VERBOSE
+                       msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
+#endif
+               return;
+       }
+
+}   /*  End Function action_permissions  */
+
+#ifdef CONFIG_DEVFSD_MODLOAD
+static void action_modload (const struct devfsd_notify_struct *info,
+                           const struct config_entry_struct *entry)
+/*  [SUMMARY] Load a module.
+    <info> The devfs change.
+    <entry> The config file entry.
+    [RETURNS] Nothing.
+*/
+{
+       char *argv[6];
+       char device[STRING_LENGTH];
+
+       argv[0] = MODPROBE;
+       argv[1] = MODPROBE_SWITCH_1; /* "-k" */
+       argv[2] = MODPROBE_SWITCH_2; /* "-C" */
+       argv[3] = CONFIG_MODULES_DEVFS;
+       argv[4] = device;
+       argv[5] = NULL;
+
+       snprintf (device, sizeof (device), "/dev/%s", info->devname);
+       #ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "action_modload():%s %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3],argv[4]);
+       #endif
+       fork_and_execute(DIE, argv[0], argv);
+}  /*  End Function action_modload  */
+#endif
+
+static void action_execute (const struct devfsd_notify_struct *info,
+                           const struct config_entry_struct *entry,
+                           const regmatch_t *regexpr, unsigned int numexpr)
+/*  [SUMMARY] Execute a programme.
+    <info> The devfs change.
+    <entry> The config file entry.
+    <regexpr> The number of subexpression (start, end) offsets within the
+    device name.
+    <numexpr> The number of elements within <<regexpr>>.
+    [RETURNS] Nothing.
+*/
+{
+       unsigned int count;
+       struct get_variable_info gv_info;
+       char *argv[MAX_ARGS + 1];
+       char largv[MAX_ARGS + 1][STRING_LENGTH];
+
+#ifdef CONFIG_DEBUG
+       int i;
+       char buff[512];
+#endif
+
+       gv_info.info = info;
+       gv_info.devname = info->devname;
+       snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
+       for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
+       {
+               expand_expression (largv[count], STRING_LENGTH,
+                               entry->u.execute.argv[count],
+                               get_variable, &gv_info,
+                               gv_info.devname, regexpr, numexpr );
+               argv[count] = largv[count];
+       }
+       argv[count] = NULL;
+
+#ifdef CONFIG_DEBUG
+       buff[0]='\0';
+       for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
+       {
+               strcat(buff," ");
+               if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512)
+                       break;
+               strcat(buff,argv[i]);
+       }
+       strcat(buff,"\n");
+       msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
+#endif
+
+       fork_and_execute(NO_DIE, argv[0], argv);
+}   /*  End Function action_execute  */
+
+
+static void action_copy (const struct devfsd_notify_struct *info,
+                        const struct config_entry_struct *entry,
+                         const regmatch_t *regexpr, unsigned int numexpr)
+/*  [SUMMARY] Copy permissions.
+    <info> The devfs change.
+    <entry> The config file entry.
+    <regexpr> This list of subexpression (start, end) offsets within the
+    device name.
+    <numexpr> The number of elements in <<regexpr>>.
+    [RETURNS] Nothing.
+*/
+{
+       mode_t new_mode;
+       struct get_variable_info gv_info;
+       struct stat source_stat, dest_stat;
+       char source[STRING_LENGTH], destination[STRING_LENGTH];
+       dest_stat.st_mode = 0;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
+#endif
+
+       if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
+               return;
+       gv_info.info = info;
+       gv_info.devname = info->devname;
+
+       snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
+       expand_expression (source, STRING_LENGTH, entry->u.copy.source,
+                               get_variable, &gv_info, gv_info.devname,
+                               regexpr, numexpr);
+
+       expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
+                               get_variable, &gv_info, gv_info.devname,
+                               regexpr, numexpr);
+
+       if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
+                       return;
+       lstat (destination, &dest_stat);
+       new_mode = source_stat.st_mode & ~S_ISVTX;
+       if (info->type == DEVFSD_NOTIFY_CREATE)
+               new_mode |= S_ISVTX;
+       else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
+               new_mode |= S_ISVTX;
+#ifdef CONFIG_DEBUG
+       if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) && (errno != EEXIST))
+               msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination);
+#else
+       copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
+#endif
+       return;
+}   /*  End Function action_copy  */
+
+static void action_compat (const struct devfsd_notify_struct *info, unsigned int action)
+/*  [SUMMARY] Process a compatibility request.
+    <info> The devfs change.
+    <action> The action to take.
+    [RETURNS] Nothing.
+*/
+{
+       const char *compat_name = NULL;
+       const char *dest_name = info->devname;
+       char *ptr=NULL;
+       char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
+       int mode, host, bus, target, lun;
+       unsigned int i;
+       char rewind_;
+       /* 1 to 5  "scsi/" , 6 to 9 "ide/host" */
+       const char *fmt[] = {   NULL ,
+                                                       "sg/c%db%dt%du%d",                      /* scsi/generic */
+                                                       "sd/c%db%dt%du%d",                      /* scsi/disc */
+                                                       "sr/c%db%dt%du%d",                      /* scsi/cd */
+                                                       "sd/c%db%dt%du%dp%d",           /* scsi/part */
+                                                       "st/c%db%dt%du%dm%d%c",         /* scsi/mt */
+                                                       "ide/hd/c%db%dt%du%d",          /* ide/host/disc */
+                                                       "ide/cd/c%db%dt%du%d",          /* ide/host/cd */
+                                                       "ide/hd/c%db%dt%du%dp%d",       /* ide/host/part */
+                                                       "ide/mt/c%db%dt%du%d%s",        /* ide/host/mt */
+                                                       NULL };
+
+       /*  First construct compatibility name  */
+       switch (action)
+       {
+               case AC_MKOLDCOMPAT:
+               case AC_RMOLDCOMPAT:
+                       compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
+                       break;
+               case AC_MKNEWCOMPAT:
+               case AC_RMNEWCOMPAT:
+                       ptr = strrchr (info->devname, '/') + 1;
+                       i=scan_dev_name(info->devname, info->namelen, ptr);
+
+#ifdef CONFIG_DEBUG
+                       msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i);
+#endif
+
+                       /* nothing found */
+                       if(i==0 || i > 9)
+                               return;
+
+                       sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
+                       snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0));
+                       dest_name = dest_buf;
+                       compat_name = compat_buf;
+
+
+                       /* 1 == scsi/generic  2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
+                       if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 )
+                               sprintf ( compat_buf, fmt[i], host, bus, target, lun);
+
+                       /* 4 == scsi/part 8 == ide/host/part */
+                       if( i == 4 || i == 8)
+                               sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) );
+
+                       /* 5 == scsi/mt */
+                       if( i == 5)
+                       {
+                               rewind_ = info->devname[info->namelen - 1];
+                               if (rewind_ != 'n')
+                                       rewind_ = '\0';
+                               mode=0;
+                               if(ptr[2] ==  'l' /*108*/ || ptr[2] == 'm'/*109*/)
+                                       mode = ptr[2] - 107; /* 1 or 2 */
+                               if(ptr[2] ==  'a')
+                                       mode = 3;
+                               sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
+                       }
+
+                       /* 9 == ide/host/mt */
+                       if( i ==  9 )
+                               snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
+               /* esac */
+       } /* switch(action) */
+
+       if(compat_name == NULL )
+               return;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name);
+#endif
+
+       /*  Now decide what to do with it  */
+       switch (action)
+       {
+               case AC_MKOLDCOMPAT:
+               case AC_MKNEWCOMPAT:
+                       mksymlink (dest_name, compat_name);
+                       break;
+               case AC_RMOLDCOMPAT:
+               case AC_RMNEWCOMPAT:
+#ifdef CONFIG_DEBUG
+                       if (unlink (compat_name) != 0)
+                               msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
+#else
+                       unlink (compat_name);
+#endif
+                       break;
+               /*esac*/
+       } /* switch(action) */
+}   /*  End Function action_compat  */
+
+static void restore(char *spath, struct stat source_stat, int rootlen)
+{
+       char dpath[STRING_LENGTH];
+       struct stat dest_stat;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "restore()\n");
+#endif
+
+       dest_stat.st_mode = 0;
+       snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
+       lstat (dpath, &dest_stat);
+
+       if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
+               copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
+
+       if ( S_ISDIR (source_stat.st_mode) )
+               dir_operation(RESTORE, spath, rootlen,NULL);
+}
+
+
+static int copy_inode (const char *destpath, const struct stat *dest_stat,
+                       mode_t new_mode,
+                       const char *sourcepath, const struct stat *source_stat)
+/*  [SUMMARY] Copy an inode.
+    <destpath> The destination path. An existing inode may be deleted.
+    <dest_stat> The destination stat(2) information.
+    <new_mode> The desired new mode for the destination.
+    <sourcepath> The source path.
+    <source_stat> The source stat(2) information.
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+       int source_len, dest_len;
+       char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
+       int fd, val;
+       struct sockaddr_un un_addr;
+       char symlink_val[STRING_LENGTH];
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
+#endif
+
+       if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
+       {
+               /*  Same type  */
+               if ( S_ISLNK (source_stat->st_mode) )
+               {
+                       if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
+                               ( dest_len   = readlink (destpath  , dest_link  , STRING_LENGTH - 1) ) < 0 )
+                               return (FALSE);
+                       source_link[source_len] = '\0';
+                       dest_link[dest_len]     = '\0';
+                       if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
+                       {
+                               unlink (destpath);
+                               symlink (source_link, destpath);
+                       }
+                       return (TRUE);
+               }   /*  Else not a symlink  */
+               chmod (destpath, new_mode & ~S_IFMT);
+               chown (destpath, source_stat->st_uid, source_stat->st_gid);
+               return (TRUE);
+       }
+       /*  Different types: unlink and create  */
+       unlink (destpath);
+       switch (source_stat->st_mode & S_IFMT)
+       {
+               case S_IFSOCK:
+                       if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
+                               break;
+                       un_addr.sun_family = AF_UNIX;
+                       snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
+                       val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
+                       close (fd);
+                       if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
+                               break;
+                       goto do_chown;
+               case S_IFLNK:
+                       if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
+                               break;
+                       symlink_val[val] = '\0';
+                       if (symlink (symlink_val, destpath) == 0)
+                               return (TRUE);
+                       break;
+               case S_IFREG:
+                       if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
+                               break;
+                       close (fd);
+                       if (chmod (destpath, new_mode & ~S_IFMT) != 0)
+                               break;
+                       goto do_chown;
+               case S_IFBLK:
+               case S_IFCHR:
+               case S_IFIFO:
+                       if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
+                               break;
+                       goto do_chown;
+               case S_IFDIR:
+                       if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
+                               break;
+do_chown:
+                       if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
+                               return (TRUE);
+               /*break;*/
+       }
+       return (FALSE);
+}   /*  End Function copy_inode  */
+
+static void free_config ()
+/*  [SUMMARY] Free the configuration information.
+    [RETURNS] Nothing.
+*/
+{
+       struct config_entry_struct *c_entry;
+       void *next;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
+#endif
+
+       for (c_entry = first_config; c_entry != NULL; c_entry = next)
+       {
+               unsigned int count;
+
+               next = c_entry->next;
+               regfree (&c_entry->preg);
+               if (c_entry->action.what == AC_EXECUTE)
+               {
+                       for (count = 0; count < MAX_ARGS; ++count)
+                       {
+                               if (c_entry->u.execute.argv[count] == NULL)
+                                       break;
+                               free (c_entry->u.execute.argv[count]);
+                       }
+               }
+               free (c_entry);
+       }
+       first_config = NULL;
+       last_config = NULL;
+}   /*  End Function free_config  */
+
+static int get_uid_gid (int flag, const char *string)
+/*  [SUMMARY] Convert a string to a UID or GID value.
+       <flag> "UID" or "GID".
+       <string> The string.
+    [RETURNS] The UID or GID value.
+*/
+{
+       struct passwd *pw_ent;
+       struct group *grp_ent;
+#ifdef CONFIG_DEVFSD_VERBOSE
+       char * msg="user";
+#endif
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
+
+
+       if(flag != UID && flag != GID )
+               msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
+#endif
+
+       if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
+               return atoi (string);
+
+       if ( flag == UID && ( pw_ent  = getpwnam (string) ) != NULL )
+               return (pw_ent->pw_uid);
+
+       if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
+               return (grp_ent->gr_gid);
+#ifdef CONFIG_DEVFSD_VERBOSE
+       else
+               msg="group";
+
+       msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32);
+#endif
+       return (0);
+}/*  End Function get_uid_gid  */
+
+static mode_t get_mode (const char *string)
+/*  [SUMMARY] Convert a string to a mode value.
+    <string> The string.
+    [RETURNS] The mode value.
+*/
+{
+       mode_t mode;
+       int i;
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
+#endif
+
+       if ( isdigit (string[0]) )
+               return strtoul (string, NULL, 8);
+       if (strlen (string) != 9)
+#ifdef CONFIG_DEVFSD_VERBOSE
+               msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string);
+#else
+               exit(EXIT_FAILURE);
+#endif
+       mode = 0;
+       i= S_IRUSR;
+       while(i>0)
+       {
+               if(string[0]=='r'||string[0]=='w'||string[0]=='x')
+                       mode+=i;
+               i=i/2;
+               string++;
+       }
+       return (mode);
+}   /*  End Function get_mode  */
+
+static void signal_handler (int sig)
+{
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
+#endif
+
+       caught_signal = TRUE;
+       if (sig == SIGHUP)
+               caught_sighup = TRUE;
+#ifdef CONFIG_DEVFSD_VERBOSE
+       msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
+#endif
+}   /*  End Function signal_handler  */
+
+static const char *get_variable (const char *variable, void *info)
+{
+       struct get_variable_info *gv_info = info;
+       static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
+       const char *field_names[] = { "hostname", "mntpt", "devpath", "devname",
+                                                                  "uid", "gid", "mode", hostname, mount_point,
+                                                                  gv_info->devpath, gv_info->devname, 0 };
+       short int i;
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
+#endif
+
+       if (gethostname (hostname, STRING_LENGTH - 1) != 0)
+#ifdef CONFIG_DEVFSD_VERBOSE
+               msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
+#else
+               exit(EXIT_FAILURE);
+#endif
+               /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
+               hostname[STRING_LENGTH - 1] = '\0';
+
+       /* compare_string_array returns i>=0  */
+       i=compare_string_array(field_names, variable);
+
+       if ( i > 6 && (i > 1 && gv_info == NULL))
+                       return (NULL);
+       if( i >= 0 || i <= 3)
+       {
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]);
+#endif
+               return(field_names[i+7]);
+       }
+
+       if(i == 4 )
+               sprintf (sbuf, "%u", gv_info->info->uid);
+       else if(i == 5)
+               sprintf (sbuf, "%u", gv_info->info->gid);
+       else if(i == 6)
+               sprintf (sbuf, "%o", gv_info->info->mode);
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf);
+#endif
+       return (sbuf);
+}   /*  End Function get_variable  */
+
+static void service(struct stat statbuf, char *path)
+{
+       struct devfsd_notify_struct info;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "service()\n");
+#endif
+
+       memset (&info, 0, sizeof info);
+       info.type = DEVFSD_NOTIFY_REGISTERED;
+       info.mode = statbuf.st_mode;
+       info.major = major (statbuf.st_rdev);
+       info.minor = minor (statbuf.st_rdev);
+       info.uid = statbuf.st_uid;
+       info.gid = statbuf.st_gid;
+       snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
+       info.namelen = strlen (info.devname);
+       service_name (&info);
+       if ( S_ISDIR (statbuf.st_mode) )
+               dir_operation(SERVICE,path,0,NULL);
+}
+
+static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
+/*  [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
+       <flag> To choose which function to perform
+       <dp> The directory pointer. This is closed upon completion.
+    <dir_name> The name of the directory.
+       <rootlen> string length parameter.
+    [RETURNS] Nothing.
+*/
+{
+       struct stat statbuf;
+       DIR *dp;
+       struct dirent *de;
+       char path[STRING_LENGTH];
+
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
+#endif
+
+       if((dp = opendir( dir_name))==NULL)
+       {
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
+#endif
+               return;
+       }
+
+       while ( (de = readdir (dp) ) != NULL )
+       {
+
+               if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
+                       continue;
+               snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path);
+#endif
+
+               if (lstat (path, &statbuf) != 0)
+               {
+#ifdef CONFIG_DEBUG
+                       msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
+#endif
+                       continue;
+               }
+               switch(type)
+               {
+                       case SERVICE:
+                               service(statbuf,path);
+                               break;
+                       case RESTORE:
+                               restore(path, statbuf, var);
+                               break;
+                       case READ_CONFIG:
+                               read_config_file (path, var, event_mask);
+                               break;
+               }
+       }
+       closedir (dp);
+}   /*  End Function do_scan_and_service  */
+
+static int mksymlink (const char *oldpath, const char *newpath)
+/*  [SUMMARY] Create a symlink, creating intervening directories as required.
+    <oldpath> The string contained in the symlink.
+    <newpath> The name of the new symlink.
+    [RETURNS] 0 on success, else -1.
+*/
+{
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
+#endif
+
+
+       if ( !make_dir_tree (newpath) )
+               return (-1);
+
+       if (symlink (oldpath, newpath) != 0)
+    {
+               if (errno != EEXIST)
+               {
+#ifdef CONFIG_DEBUG
+                       msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath);
+#endif
+                       return (-1);
+               }
+       }
+    return (0);
+}   /*  End Function mksymlink  */
+
+
+static int make_dir_tree (const char *path)
+/*  [SUMMARY] Creating intervening directories for a path as required.
+    <path> The full pathname (including the leaf node).
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
+#endif
+       if (bb_make_directory( dirname((char *)path), -1, FILEUTILS_RECUR )==-1)
+       {
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
+#endif
+               return (FALSE);
+       }
+       return(TRUE);
+} /*  End Function make_dir_tree  */
+
+static int expand_expression(char *output, unsigned int outsize,
+                             const char *input,
+                             const char *(*get_variable_func)(const char *variable, void *info),
+                              void *info,
+                              const char *devname,
+                              const regmatch_t *ex, unsigned int numexp)
+/*  [SUMMARY] Expand environment variables and regular subexpressions in string.
+    <output> The output expanded expression is written here.
+    <length> The size of the output buffer.
+    <input> The input expression. This may equal <<output>>.
+    <get_variable> A function which will be used to get variable values. If
+    this returns NULL, the environment is searched instead. If this is NULL,
+    only the environment is searched.
+    <info> An arbitrary pointer passed to <<get_variable>>.
+    <devname> Device name; specifically, this is the string that contains all
+    of the regular subexpressions.
+    <ex> Array of start / end offsets into info->devname for each subexpression
+    <numexp> Number of regular subexpressions found in <<devname>>.
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+       char temp[STRING_LENGTH];
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
+#endif
+
+       if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
+               return (FALSE);
+       expand_regexp (output, outsize, temp, devname, ex, numexp);
+       return (TRUE);
+}   /*  End Function expand_expression  */
+
+static void expand_regexp (char *output, size_t outsize, const char *input,
+                          const char *devname,
+                          const regmatch_t *ex, unsigned int numex )
+/*  [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
+    <output> The output expanded expression is written here.
+    <outsize> The size of the output buffer.
+    <input> The input expression. This may NOT equal <<output>>, because
+    supporting that would require yet another string-copy. However, it's not
+    hard to write a simple wrapper function to add this functionality for those
+    few cases that need it.
+    <devname> Device name; specifically, this is the string that contains all
+    of the regular subexpressions.
+    <ex> An array of start and end offsets into <<devname>>, one for each
+    subexpression
+    <numex> Number of subexpressions in the offset-array <<ex>>.
+    [RETURNS] Nothing.
+*/
+{
+       const char last_exp = '0' - 1 + numex;
+       int c = -1;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
+#endif
+
+       /*  Guarantee NULL termination by writing an explicit '\0' character into
+       the very last byte  */
+       if (outsize)
+               output[--outsize] = '\0';
+       /*  Copy the input string into the output buffer, replacing '\\' with '\'
+       and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
+       codes are deleted  */
+       while ( (c != '\0') && (outsize != 0) )
+       {
+               c = *input;
+               ++input;
+               if (c == '\\')
+               {
+                       c = *input;
+                       ++input;
+                       if (c != '\\')
+                       {
+                               if ((c >= '0') && (c <= last_exp))
+                               {
+                                       const regmatch_t *subexp = ex + (c - '0');
+                                       unsigned int sublen = subexp->rm_eo - subexp->rm_so;
+
+                                       /*  Range checking  */
+                                       if (sublen > outsize)
+                                               sublen = outsize;
+                                       strncpy (output, devname + subexp->rm_so, sublen);
+                                       output += sublen;
+                                       outsize -= sublen;
+                               }
+                               continue;
+                       }
+               }
+               *output = c;
+               ++output;
+               --outsize;
+       } /* while */
+}   /*  End Function expand_regexp  */
+
+
+/* from compat_name.c */
+
+struct translate_struct
+{
+       char *match;    /*  The string to match to (up to length)                */
+       char *format;   /*  Format of output, "%s" takes data past match string,
+                       NULL is effectively "%s" (just more efficient)       */
+};
+
+static struct translate_struct translate_table[] =
+{
+       {"sound/",     NULL},
+       {"printers/",  "lp%s"},
+       {"v4l/",       NULL},
+       {"parports/",  "parport%s"},
+       {"fb/",        "fb%s"},
+       {"netlink/",   NULL},
+       {"loop/",      "loop%s"},
+       {"floppy/",    "fd%s"},
+       {"rd/",        "ram%s"},
+       {"md/",        "md%s"},         /*  Meta-devices                         */
+       {"vc/",        "tty%s"},
+       {"misc/",      NULL},
+       {"isdn/",      NULL},
+       {"pg/",        "pg%s"},         /*  Parallel port generic ATAPI interface*/
+       {"i2c/",       "i2c-%s"},
+       {"staliomem/", "staliomem%s"},  /*  Stallion serial driver control       */
+       {"tts/E",      "ttyE%s"},       /*  Stallion serial driver               */
+       {"cua/E",      "cue%s"},        /*  Stallion serial driver callout       */
+       {"tts/R",      "ttyR%s"},       /*  Rocketport serial driver             */
+       {"cua/R",      "cur%s"},        /*  Rocketport serial driver callout     */
+       {"ip2/",       "ip2%s"},        /*  Computone serial driver control      */
+       {"tts/F",      "ttyF%s"},       /*  Computone serial driver              */
+       {"cua/F",      "cuf%s"},        /*  Computone serial driver callout      */
+       {"tts/C",      "ttyC%s"},       /*  Cyclades serial driver               */
+       {"cua/C",      "cub%s"},        /*  Cyclades serial driver callout       */
+       {"tts/",       "ttyS%s"},       /*  Generic serial: must be after others */
+       {"cua/",       "cua%s"},        /*  Generic serial: must be after others */
+       {"input/js",   "js%s"},         /*  Joystick driver                      */
+       {NULL,         NULL}
+};
+
+const char *get_old_name (const char *devname, unsigned int namelen,
+                         char *buffer, unsigned int major, unsigned int minor)
+/*  [SUMMARY] Translate a kernel-supplied name into an old name.
+    <devname> The device name provided by the kernel.
+    <namelen> The length of the name.
+    <buffer> A buffer that may be used. This should be at least 128 bytes long.
+    <major> The major number for the device.
+    <minor> The minor number for the device.
+    [RETURNS] A pointer to the old name if known, else NULL.
+*/
+{
+       const char *compat_name = NULL;
+       char *ptr;
+       struct translate_struct *trans;
+       unsigned int i;
+       char mode;
+       int indexx;
+       const char *pty1;
+       const char *pty2;
+       size_t len;
+       /* 1 to 5  "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
+       const char *fmt[] = {   NULL ,
+                                                       "sg%u",                 /* scsi/generic */
+                                                       NULL,                   /* scsi/disc */
+                                                       "sr%u",                 /* scsi/cd */
+                                                       NULL,                   /* scsi/part */
+                                                       "nst%u%c",              /* scsi/mt */
+                                                       "hd%c"  ,               /* ide/host/disc */
+                                                       "hd%c"  ,               /* ide/host/cd */
+                                                       "hd%c%s",               /* ide/host/part */
+                                                       "%sht%d",               /* ide/host/mt */
+                                                       "sbpcd%u",              /* sbp/ */
+                                                       "vcs%s",                /* vcc/ */
+                                                       "%cty%c%c",             /* pty/ */
+                                                       NULL };
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
+#endif
+
+       for (trans = translate_table; trans->match != NULL; ++trans)
+       {
+                len = strlen (trans->match);
+
+               if (strncmp (devname, trans->match, len) == 0)
+               {
+                       if (trans->format == NULL)
+                               return (devname + len);
+                       sprintf (buffer, trans->format, devname + len);
+                       return (buffer);
+               }
+       }
+
+       ptr = (strrchr (devname, '/') + 1);
+       i = scan_dev_name(devname, namelen, ptr);
+
+       if( i > 0 && i < 13)
+               compat_name = buffer;
+       else
+               return NULL;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i);
+#endif
+
+       /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
+       if( i == 1 || i == 3 || i == 10 )
+               sprintf (buffer, fmt[i], minor);
+
+       /* 2 ==scsi/disc, 4 == scsi/part */
+       if( i == 2 || i == 4)
+               compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4)));
+
+       /* 5 == scsi/mt */
+       if( i == 5)
+       {
+               mode = ptr[2];
+               if (mode == 'n')
+                       mode = '\0';
+               sprintf (buffer, fmt[i], minor & 0x1f, mode);
+               if (devname[namelen - 1] != 'n')
+                       ++compat_name;
+       }
+       /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
+       if( i == 6 || i == 7 || i == 8 )
+               sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4);    /* last arg should be ignored for i == 6 or i== 7 */
+
+       /* 9 ==  ide/host/mt */
+       if( i == 9 )
+               sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
+
+       /*  11 == vcc/ */
+       if( i == 11 )
+       {
+               sprintf (buffer, fmt[i], devname + 4);
+               if (buffer[3] == '0')
+                       buffer[3] = '\0';
+       }
+       /* 12 ==  pty/ */
+       if( i == 12 )
+       {
+               pty1 = "pqrstuvwxyzabcde";
+               pty2 = "0123456789abcdef";
+               indexx = atoi (devname + 5);
+               sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
+       }
+#ifdef CONFIG_DEBUG
+       if(compat_name!=NULL)
+               msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name  %s\n", compat_name);
+#endif
+       return (compat_name);
+}   /*  End Function get_old_name  */
+
+static char get_old_ide_name (unsigned int major, unsigned int minor)
+/*  [SUMMARY] Get the old IDE name for a device.
+    <major> The major number for the device.
+    <minor> The minor number for the device.
+    [RETURNS] The drive letter.
+*/
+{
+       char letter='y';        /* 121 */
+       char c='a';             /*  97 */
+       int i=IDE0_MAJOR;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
+#endif
+
+       /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
+       do {
+               if(     i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
+                       i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
+                       i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
+                       i==IDE9_MAJOR )
+               {
+                       if(i==major)
+                       {
+                               letter=c;
+                               break;
+                       }
+                       c+=2;
+               }
+               i++;
+       } while(i<=IDE9_MAJOR);
+
+       if (minor > 63)
+               ++letter;
+       return (letter);
+}   /*  End Function get_old_ide_name  */
+
+static char *write_old_sd_name (char *buffer,
+                               unsigned int major, unsigned int minor,
+                               char *part)
+/*  [SUMMARY] Write the old SCSI disc name to a buffer.
+    <buffer> The buffer to write to.
+    <major> The major number for the device.
+    <minor> The minor number for the device.
+    <part> The partition string. Must be "" for a whole-disc entry.
+    [RETURNS] A pointer to the buffer on success, else NULL.
+*/
+{
+       unsigned int disc_index;
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
+#endif
+
+       if (major == 8)
+       {
+               sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
+               return (buffer);
+       }
+       if ( (major > 64) && (major < 72) )
+       {
+               disc_index = ( (major - 64) << 4 ) + (minor >> 4);
+               if (disc_index < 26)
+                       sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
+               else
+                       sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
+               return (buffer);
+       }
+       return (NULL);
+}   /*  End Function write_old_sd_name  */
+
+
+/*  expression.c */
+
+/*EXPERIMENTAL_FUNCTION*/
+
+int st_expr_expand (char *output, unsigned int length, const char *input,
+                    const char *(*get_variable_func) (const char *variable,
+                                                 void *info),
+                    void *info)
+/*  [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
+    <output> The output expanded expression is written here.
+    <length> The size of the output buffer.
+    <input> The input expression. This may equal <<output>>.
+    <get_variable> A function which will be used to get variable values. If
+    this returns NULL, the environment is searched instead. If this is NULL,
+    only the environment is searched.
+    <info> An arbitrary pointer passed to <<get_variable>>.
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+       char ch;
+       unsigned int len;
+       unsigned int out_pos = 0;
+       const char *env;
+       const char *ptr;
+       struct passwd *pwent;
+       char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
+#endif
+
+       if (length > BUFFER_SIZE)
+               length = BUFFER_SIZE;
+       for (; TRUE; ++input)
+       {
+               switch (ch = *input)
+               {
+                       case '$':
+                               /*  Variable expansion  */
+                               input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
+                               if (input == NULL)
+                                       return (FALSE);
+                               break;
+                       case '~':
+                               /*  Home directory expansion  */
+                               ch = input[1];
+                               if ( isspace (ch) || (ch == '/') || (ch == '\0') )
+                               {
+                                       /* User's own home directory: leave separator for next time */
+                                       if ( ( env = getenv ("HOME") ) == NULL )
+                                       {
+#ifdef CONFIG_DEVFSD_VERBOSE
+                                               msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
+#endif
+                                               return (FALSE);
+                                       }
+                                       len = strlen (env);
+                                       if (len + out_pos >= length)
+                                               goto st_expr_expand_out;
+                                       memcpy (buffer + out_pos, env, len + 1);
+                                       out_pos += len;
+                                       continue;
+                               }
+                               /*  Someone else's home directory  */
+                               for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
+                                       /* VOID */ ;
+                               len = ptr - input;
+                               if (len >= sizeof tmp)
+                                       goto st_expr_expand_out;
+                               safe_memcpy (tmp, input, len);
+                               input = ptr - 1;
+                               if ( ( pwent = getpwnam (tmp) ) == NULL )
+                               {
+#ifdef CONFIG_DEVFSD_VERBOSE
+                                       msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp);
+#endif
+                                       return (FALSE);
+                               }
+                               len = strlen (pwent->pw_dir);
+                               if (len + out_pos >= length)
+                                       goto st_expr_expand_out;
+                               memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
+                               out_pos += len;
+                               break;
+                       case '\0':
+                       /* Falltrough */
+                       default:
+                               if (out_pos >= length)
+                                       goto st_expr_expand_out;
+                               buffer[out_pos++] = ch;
+                               if (ch == '\0')
+                               {
+                                       memcpy (output, buffer, out_pos);
+                                       return (TRUE);
+                               }
+                               break;
+                       /* esac */
+               }
+       }
+       return (FALSE);
+st_expr_expand_out:
+#ifdef CONFIG_DEVFSD_VERBOSE
+       msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
+#endif
+       return (FALSE);
+}   /*  End Function st_expr_expand  */
+
+
+/*  Private functions follow  */
+
+static const char *expand_variable (char *buffer, unsigned int length,
+                                   unsigned int *out_pos, const char *input,
+                                   const char *(*func) (const char *variable,
+                                                        void *info),
+                                   void *info)
+/*  [SUMMARY] Expand a variable.
+    <buffer> The buffer to write to.
+    <length> The length of the output buffer.
+    <out_pos> The current output position. This is updated.
+    <input> A pointer to the input character pointer.
+    <func> A function which will be used to get variable values. If this
+    returns NULL, the environment is searched instead. If this is NULL, only
+    the environment is searched.
+    <info> An arbitrary pointer passed to <<func>>.
+    <errfp> Diagnostic messages are written here.
+    [RETURNS] A pointer to the end of this subexpression on success, else NULL.
+*/
+{
+       char ch;
+       int len;
+       unsigned int open_braces;
+       const char *env, *ptr;
+       char tmp[STRING_LENGTH];
+
+#ifdef CONFIG_DEBUG
+       msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
+#endif
+
+       ch = input[0];
+       if (ch == '$')
+       {
+               /*  Special case for "$$": PID  */
+               sprintf ( tmp, "%d", (int) getpid () );
+               len = strlen (tmp);
+               if (len + *out_pos >= length)
+                       goto expand_variable_out;
+
+               memcpy (buffer + *out_pos, tmp, len + 1);
+               out_pos += len;
+               return (input);
+       }
+       /*  Ordinary variable expansion, possibly in braces  */
+       if (ch != '{')
+       {
+               /*  Simple variable expansion  */
+               for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
+                       /* VOID */ ;
+               len = ptr - input;
+               if (len >= sizeof tmp)
+                       goto expand_variable_out;
+
+               safe_memcpy (tmp, input, len);
+               input = ptr - 1;
+               if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
+               {
+#ifdef CONFIG_DEVFSD_VERBOSE
+                       msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
+#endif
+                       return (NULL);
+               }
+               len = strlen (env);
+               if (len + *out_pos >= length)
+                       goto expand_variable_out;
+
+               memcpy (buffer + *out_pos, env, len + 1);
+               *out_pos += len;
+               return (input);
+       }
+       /*  Variable in braces: check for ':' tricks  */
+       ch = *++input;
+       for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
+               /* VOID */;
+       if (ch == '}')
+       {
+               /*  Must be simple variable expansion with "${var}"  */
+               len = ptr - input;
+               if (len >= sizeof tmp)
+                       goto expand_variable_out;
+
+               safe_memcpy (tmp, input, len);
+               ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
+               if (ptr == NULL)
+                       return (NULL);
+               return (input + len);
+       }
+       if (ch != ':' || ptr[1] != '-' )
+       {
+#ifdef CONFIG_DEVFSD_VERBOSE
+               msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
+#endif
+               return (NULL);
+       }
+       /*  It's that handy "${var:-word}" expression. Check if var is defined  */
+       len = ptr - input;
+       if (len >= sizeof tmp)
+               goto expand_variable_out;
+
+       safe_memcpy (tmp, input, len);
+       /*  Move input pointer to ':'  */
+       input = ptr;
+       /*  First skip to closing brace, taking note of nested expressions  */
+       ptr += 2;
+       ch = ptr[0];
+       for (open_braces = 1; open_braces > 0; ch = *++ptr)
+       {
+               switch (ch)
+               {
+                       case '{':
+                               ++open_braces;
+                               break;
+                       case '}':
+                               --open_braces;
+                               break;
+                       case '\0':
+#ifdef CONFIG_DEVFSD_VERBOSE
+                               msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
+#endif
+                               return (NULL);
+                       default:
+                               break;
+               }
+       }
+       --ptr;
+       /*  At this point ptr should point to closing brace of "${var:-word}"  */
+       if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
+       {
+               /*  Found environment variable, so skip the input to the closing brace
+                       and return the variable  */
+               input = ptr;
+               len = strlen (env);
+               if (len + *out_pos >= length)
+                       goto expand_variable_out;
+
+               memcpy (buffer + *out_pos, env, len + 1);
+               *out_pos += len;
+               return (input);
+       }
+       /*  Environment variable was not found, so process word. Advance input
+       pointer to start of word in "${var:-word}"  */
+       input += 2;
+       len = ptr - input;
+       if (len >= sizeof tmp)
+               goto expand_variable_out;
+
+       safe_memcpy (tmp, input, len);
+       input = ptr;
+       if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
+               return (NULL);
+       len = strlen (tmp);
+       if (len + *out_pos >= length)
+               goto expand_variable_out;
+
+       memcpy (buffer + *out_pos, tmp, len + 1);
+       *out_pos += len;
+       return (input);
+expand_variable_out:
+#ifdef CONFIG_DEVFSD_VERBOSE
+       msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
+#endif
+       return (NULL);
+}   /*  End Function expand_variable  */
+
+
+static const char *get_variable_v2 (const char *variable,
+                                 const char *(*func) (const char *variable, void *info),
+                                void *info)
+/*  [SUMMARY] Get a variable from the environment or .
+    <variable> The variable name.
+    <func> A function which will be used to get the variable. If this returns
+    NULL, the environment is searched instead. If this is NULL, only the
+    environment is searched.
+    [RETURNS] The value of the variable on success, else NULL.
+*/
+{
+       const char *value;
+
+#ifdef CONFIG_DEBUG
+               msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
+#endif
+
+       if (func != NULL)
+       {
+               value = (*func) (variable, info);
+               if (value != NULL)
+                       return (value);
+       }
+       return getenv (variable);
+}   /*  End Function get_variable  */
+
+/* END OF CODE */
diff --git a/busybox/miscutils/hdparm.c b/busybox/miscutils/hdparm.c
new file mode 100644 (file)
index 0000000..0d2c328
--- /dev/null
@@ -0,0 +1,2872 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * hdparm implementation for busybox
+ *
+ *
+ * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+ *
+ * Hacked by Tito <farmatito@tiscali.it> for size optimization.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ *
+ * This program is based on the source code of hdparm: see below...
+ * hdparm.c - Command line interface to get/set hard disk parameters
+ *          - by Mark Lord (C) 1994-2002 -- freely distributable
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <endian.h>
+#include <sys/ioctl.h>
+#include <sys/shm.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/mount.h>
+#include "busybox.h"
+#include <linux/types.h>
+#include <linux/hdreg.h>
+#include <linux/major.h>
+#include <asm/byteorder.h>
+
+
+#if (__BYTE_ORDER == __BIG_ENDIAN) && !defined(__USE_XOPEN)
+#define __USE_XOPEN
+#endif
+
+/* device types */
+/* ------------ */
+#define NO_DEV                  0xffff
+#define ATA_DEV                 0x0000
+#define ATAPI_DEV               0x0001
+
+/* word definitions */
+/* ---------------- */
+#define GEN_CONFIG             0   /* general configuration */
+#define LCYLS                  1   /* number of logical cylinders */
+#define CONFIG                 2   /* specific configuration */
+#define LHEADS                 3   /* number of logical heads */
+#define TRACK_BYTES            4   /* number of bytes/track (ATA-1) */
+#define SECT_BYTES             5   /* number of bytes/sector (ATA-1) */
+#define LSECTS                 6   /* number of logical sectors/track */
+#define START_SERIAL            10  /* ASCII serial number */
+#define LENGTH_SERIAL           10  /* 10 words (20 bytes or characters) */
+#define BUF_TYPE               20  /* buffer type (ATA-1) */
+#define BUFFER__SIZE           21  /* buffer size (ATA-1) */
+#define RW_LONG                        22  /* extra bytes in R/W LONG cmd ( < ATA-4)*/
+#define START_FW_REV            23  /* ASCII firmware revision */
+#define LENGTH_FW_REV           4  /*  4 words (8 bytes or characters) */
+#define START_MODEL                    27  /* ASCII model number */
+#define LENGTH_MODEL           20  /* 20 words (40 bytes or characters) */
+#define SECTOR_XFER_MAX                47  /* r/w multiple: max sectors xfered */
+#define DWORD_IO               48  /* can do double-word IO (ATA-1 only) */
+#define CAPAB_0                        49  /* capabilities */
+#define CAPAB_1                        50
+#define PIO_MODE               51  /* max PIO mode supported (obsolete)*/
+#define DMA_MODE               52  /* max Singleword DMA mode supported (obs)*/
+#define WHATS_VALID            53  /* what fields are valid */
+#define LCYLS_CUR              54  /* current logical cylinders */
+#define LHEADS_CUR             55  /* current logical heads */
+#define LSECTS_CUR             56  /* current logical sectors/track */
+#define CAPACITY_LSB           57  /* current capacity in sectors */
+#define CAPACITY_MSB           58
+#define SECTOR_XFER_CUR                59  /* r/w multiple: current sectors xfered */
+#define LBA_SECTS_LSB          60  /* LBA: total number of user */
+#define LBA_SECTS_MSB          61  /*      addressable sectors */
+#define SINGLE_DMA             62  /* singleword DMA modes */
+#define MULTI_DMA              63  /* multiword DMA modes */
+#define ADV_PIO_MODES          64  /* advanced PIO modes supported */
+                                   /* multiword DMA xfer cycle time: */
+#define DMA_TIME_MIN           65  /*   - minimum */
+#define DMA_TIME_NORM          66  /*   - manufacturer's recommended   */
+                                   /* minimum PIO xfer cycle time: */
+#define PIO_NO_FLOW            67  /*   - without flow control */
+#define PIO_FLOW               68  /*   - with IORDY flow control */
+#define PKT_REL                        71  /* typical #ns from PKT cmd to bus rel */
+#define SVC_NBSY               72  /* typical #ns from SERVICE cmd to !BSY */
+#define CDR_MAJOR              73  /* CD ROM: major version number */
+#define CDR_MINOR              74  /* CD ROM: minor version number */
+#define QUEUE_DEPTH            75  /* queue depth */
+#define MAJOR                  80  /* major version number */
+#define MINOR                  81  /* minor version number */
+#define CMDS_SUPP_0            82  /* command/feature set(s) supported */
+#define CMDS_SUPP_1            83
+#define CMDS_SUPP_2            84
+#define CMDS_EN_0              85  /* command/feature set(s) enabled */
+#define CMDS_EN_1              86
+#define CMDS_EN_2              87
+#define ULTRA_DMA              88  /* ultra DMA modes */
+                                   /* time to complete security erase */
+#define ERASE_TIME             89  /*   - ordinary */
+#define ENH_ERASE_TIME         90  /*   - enhanced */
+#define ADV_PWR                        91  /* current advanced power management level
+                                      in low byte, 0x40 in high byte. */
+#define PSWD_CODE              92  /* master password revision code    */
+#define HWRST_RSLT             93  /* hardware reset result */
+#define ACOUSTIC               94  /* acoustic mgmt values ( >= ATA-6) */
+#define LBA_LSB                        100 /* LBA: maximum.  Currently only 48 */
+#define LBA_MID                        101 /*      bits are used, but addr 103 */
+#define LBA_48_MSB             102 /*      has been reserved for LBA in */
+#define LBA_64_MSB             103 /*      the future. */
+#define RM_STAT                127 /* removable media status notification feature set support */
+#define SECU_STATUS            128 /* security status */
+#define CFA_PWR_MODE           160 /* CFA power mode 1 */
+#define START_MEDIA             176 /* media serial number */
+#define LENGTH_MEDIA            20  /* 20 words (40 bytes or characters)*/
+#define START_MANUF             196 /* media manufacturer I.D. */
+#define LENGTH_MANUF            10  /* 10 words (20 bytes or characters) */
+#define INTEGRITY              255 /* integrity word */
+
+/* bit definitions within the words */
+/* -------------------------------- */
+
+/* many words are considered valid if bit 15 is 0 and bit 14 is 1 */
+#define VALID                  0xc000
+#define VALID_VAL              0x4000
+/* many words are considered invalid if they are either all-0 or all-1 */
+#define NOVAL_0                        0x0000
+#define NOVAL_1                        0xffff
+
+/* word 0: gen_config */
+#define NOT_ATA                        0x8000
+#define NOT_ATAPI              0x4000  /* (check only if bit 15 == 1) */
+#define MEDIA_REMOVABLE                0x0080
+#define DRIVE_NOT_REMOVABLE    0x0040  /* bit obsoleted in ATA 6 */
+#define INCOMPLETE             0x0004
+#define CFA_SUPPORT_VAL                0x848a  /* 848a=CFA feature set support */
+#define DRQ_RESPONSE_TIME      0x0060
+#define DRQ_3MS_VAL            0x0000
+#define DRQ_INTR_VAL           0x0020
+#define DRQ_50US_VAL           0x0040
+#define PKT_SIZE_SUPPORTED     0x0003
+#define PKT_SIZE_12_VAL                0x0000
+#define PKT_SIZE_16_VAL                0x0001
+#define EQPT_TYPE              0x1f00
+#define SHIFT_EQPT             8
+
+#define CDROM 0x0005
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static const char *pkt_str[] = {
+       "Direct-access device",                 /* word 0, bits 12-8 = 00 */
+       "Sequential-access device",             /* word 0, bits 12-8 = 01 */
+       "Printer",                              /* word 0, bits 12-8 = 02 */
+       "Processor",                            /* word 0, bits 12-8 = 03 */
+       "Write-once device",                    /* word 0, bits 12-8 = 04 */
+       "CD-ROM",                               /* word 0, bits 12-8 = 05 */
+       "Scanner",                              /* word 0, bits 12-8 = 06 */
+       "Optical memory",                       /* word 0, bits 12-8 = 07 */
+       "Medium changer",                       /* word 0, bits 12-8 = 08 */
+       "Communications device",                /* word 0, bits 12-8 = 09 */
+       "ACS-IT8 device",                       /* word 0, bits 12-8 = 0a */
+       "ACS-IT8 device",                       /* word 0, bits 12-8 = 0b */
+       "Array controller",                     /* word 0, bits 12-8 = 0c */
+       "Enclosure services",                   /* word 0, bits 12-8 = 0d */
+       "Reduced block command device",         /* word 0, bits 12-8 = 0e */
+       "Optical card reader/writer",           /* word 0, bits 12-8 = 0f */
+       "",                                     /* word 0, bits 12-8 = 10 */
+       "",                                     /* word 0, bits 12-8 = 11 */
+       "",                                     /* word 0, bits 12-8 = 12 */
+       "",                                     /* word 0, bits 12-8 = 13 */
+       "",                                     /* word 0, bits 12-8 = 14 */
+       "",                                     /* word 0, bits 12-8 = 15 */
+       "",                                     /* word 0, bits 12-8 = 16 */
+       "",                                     /* word 0, bits 12-8 = 17 */
+       "",                                     /* word 0, bits 12-8 = 18 */
+       "",                                     /* word 0, bits 12-8 = 19 */
+       "",                                     /* word 0, bits 12-8 = 1a */
+       "",                                     /* word 0, bits 12-8 = 1b */
+       "",                                     /* word 0, bits 12-8 = 1c */
+       "",                                     /* word 0, bits 12-8 = 1d */
+       "",                                     /* word 0, bits 12-8 = 1e */
+       "Unknown",                              /* word 0, bits 12-8 = 1f */
+};
+static const char *ata1_cfg_str[] = {                  /* word 0 in ATA-1 mode */
+       "reserved",                             /* bit 0 */
+       "hard sectored",                        /* bit 1 */
+       "soft sectored",                        /* bit 2 */
+       "not MFM encoded ",                     /* bit 3 */
+       "head switch time > 15us",              /* bit 4 */
+       "spindle motor control option",         /* bit 5 */
+       "fixed drive",                          /* bit 6 */
+       "removable drive",                      /* bit 7 */
+       "disk xfer rate <= 5Mbs",               /* bit 8 */
+       "disk xfer rate > 5Mbs, <= 10Mbs",      /* bit 9 */
+       "disk xfer rate > 5Mbs",                /* bit 10 */
+       "rotational speed tol.",                /* bit 11 */
+       "data strobe offset option",            /* bit 12 */
+       "track offset option",                  /* bit 13 */
+       "format speed tolerance gap reqd",      /* bit 14 */
+       "ATAPI"                                 /* bit 14 */
+};
+#endif
+
+/* word 1: number of logical cylinders */
+#define LCYLS_MAX              0x3fff /* maximum allowable value */
+
+/* word 2: specific configuration
+ * (a) require SET FEATURES to spin-up
+ * (b) require spin-up to fully reply to IDENTIFY DEVICE
+ */
+#define STBY_NID_VAL           0x37c8  /*     (a) and     (b) */
+#define STBY_ID_VAL            0x738c  /*     (a) and not (b) */
+#define PWRD_NID_VAL           0x8c73  /* not (a) and     (b) */
+#define PWRD_ID_VAL            0xc837  /* not (a) and not (b) */
+
+/* words 47 & 59: sector_xfer_max & sector_xfer_cur */
+#define SECTOR_XFER            0x00ff  /* sectors xfered on r/w multiple cmds*/
+#define MULTIPLE_SETTING_VALID  0x0100  /* 1=multiple sector setting is valid */
+
+/* word 49: capabilities 0 */
+#define STD_STBY               0x2000  /* 1=standard values supported (ATA);
+                                          0=vendor specific values */
+#define IORDY_SUP              0x0800  /* 1=support; 0=may be supported */
+#define IORDY_OFF              0x0400  /* 1=may be disabled */
+#define LBA_SUP                        0x0200  /* 1=Logical Block Address support */
+#define DMA_SUP                        0x0100  /* 1=Direct Memory Access support */
+#define DMA_IL_SUP             0x8000  /* 1=interleaved DMA support (ATAPI) */
+#define CMD_Q_SUP              0x4000  /* 1=command queuing support (ATAPI) */
+#define OVLP_SUP               0x2000  /* 1=overlap operation support (ATAPI) */
+#define SWRST_REQ              0x1000  /* 1=ATA SW reset required (ATAPI, obsolete */
+
+/* word 50: capabilities 1 */
+#define MIN_STANDBY_TIMER      0x0001  /* 1=device specific standby timer value minimum */
+
+/* words 51 & 52: PIO & DMA cycle times */
+#define MODE                   0xff00  /* the mode is in the MSBs */
+
+/* word 53: whats_valid */
+#define OK_W88                 0x0004  /* the ultra_dma info is valid */
+#define OK_W64_70              0x0002  /* see above for word descriptions */
+#define OK_W54_58              0x0001  /* current cyl, head, sector, cap. info valid */
+
+/*word 63,88: dma_mode, ultra_dma_mode*/
+#define MODE_MAX               7       /* bit definitions force udma <=7 (when
+                                        * udma >=8 comes out it'll have to be
+                                        * defined in a new dma_mode word!) */
+
+/* word 64: PIO transfer modes */
+#define PIO_SUP                        0x00ff  /* only bits 0 & 1 are used so far,  */
+#define PIO_MODE_MAX           8       /* but all 8 bits are defined        */
+
+/* word 75: queue_depth */
+#define DEPTH_BITS             0x001f  /* bits used for queue depth */
+
+/* words 80-81: version numbers */
+/* NOVAL_0 or  NOVAL_1 means device does not report version */
+
+/* word 81: minor version number */
+#define MINOR_MAX              0x1C
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static const char *minor_str[] = {                             /* word 81 value: */
+       "device does not report version",               /* 0x0000       */
+       "ATA-1 X3T9.2 781D prior to revision 4",        /* 0x0001       */
+       "ATA-1 published, ANSI X3.221-1994",            /* 0x0002       */
+       "ATA-1 X3T9.2 781D revision 4",                 /* 0x0003       */
+       "ATA-2 published, ANSI X3.279-1996",            /* 0x0004       */
+       "ATA-2 X3T10 948D prior to revision 2k",        /* 0x0005       */
+       "ATA-3 X3T10 2008D revision 1",                 /* 0x0006       */
+       "ATA-2 X3T10 948D revision 2k",                 /* 0x0007       */
+       "ATA-3 X3T10 2008D revision 0",                 /* 0x0008       */
+       "ATA-2 X3T10 948D revision 3",                  /* 0x0009       */
+       "ATA-3 published, ANSI X3.298-199x",            /* 0x000a       */
+       "ATA-3 X3T10 2008D revision 6",                 /* 0x000b       */
+       "ATA-3 X3T13 2008D revision 7 and 7a",          /* 0x000c       */
+       "ATA/ATAPI-4 X3T13 1153D revision 6",           /* 0x000d       */
+       "ATA/ATAPI-4 T13 1153D revision 13",            /* 0x000e       */
+       "ATA/ATAPI-4 X3T13 1153D revision 7",           /* 0x000f       */
+       "ATA/ATAPI-4 T13 1153D revision 18",            /* 0x0010       */
+       "ATA/ATAPI-4 T13 1153D revision 15",            /* 0x0011       */
+       "ATA/ATAPI-4 published, ANSI NCITS 317-1998",   /* 0x0012       */
+       "ATA/ATAPI-5 T13 1321D revision 3",
+       "ATA/ATAPI-4 T13 1153D revision 14",            /* 0x0014       */
+       "ATA/ATAPI-5 T13 1321D revision 1",             /* 0x0015       */
+       "ATA/ATAPI-5 published, ANSI NCITS 340-2000",   /* 0x0016       */
+       "ATA/ATAPI-4 T13 1153D revision 17",            /* 0x0017       */
+       "ATA/ATAPI-6 T13 1410D revision 0",             /* 0x0018       */
+       "ATA/ATAPI-6 T13 1410D revision 3a",            /* 0x0019       */
+       "Reserved",                                     /* 0x001a       */
+       "ATA/ATAPI-6 T13 1410D revision 2",             /* 0x001b       */
+       "ATA/ATAPI-6 T13 1410D revision 1",             /* 0x001c       */
+       "reserved"                                      /* 0x001d       */
+       "reserved"                                      /* 0x001e       */
+       "reserved"                                      /* 0x001f-0xfffe*/
+};
+#endif
+static const char actual_ver[] = {
+                       /* word 81 value: */
+       0,              /* 0x0000       WARNING:        */
+       1,              /* 0x0001       WARNING:        */
+       1,              /* 0x0002       WARNING:        */
+       1,              /* 0x0003       WARNING:        */
+       2,              /* 0x0004       WARNING:   This array           */
+       2,              /* 0x0005       WARNING:   corresponds          */
+       3,              /* 0x0006       WARNING:   *exactly*            */
+       2,              /* 0x0007       WARNING:   to the ATA/          */
+       3,              /* 0x0008       WARNING:   ATAPI version        */
+       2,              /* 0x0009       WARNING:   listed in            */
+       3,              /* 0x000a       WARNING:   the                  */
+       3,              /* 0x000b       WARNING:   minor_str            */
+       3,              /* 0x000c       WARNING:   array                */
+       4,              /* 0x000d       WARNING:   above.               */
+       4,              /* 0x000e       WARNING:                        */
+       4,              /* 0x000f       WARNING:   if you change        */
+       4,              /* 0x0010       WARNING:   that one,            */
+       4,              /* 0x0011       WARNING:   change this one      */
+       4,              /* 0x0012       WARNING:   too!!!               */
+       5,              /* 0x0013       WARNING:        */
+       4,              /* 0x0014       WARNING:        */
+       5,              /* 0x0015       WARNING:        */
+       5,              /* 0x0016       WARNING:        */
+       4,              /* 0x0017       WARNING:        */
+       6,              /* 0x0018       WARNING:        */
+       6,              /* 0x0019       WARNING:        */
+       0,              /* 0x001a       WARNING:        */
+       6,              /* 0x001b       WARNING:        */
+       6,              /* 0x001c       WARNING:        */
+       0               /* 0x001d-0xfffe                */
+};
+
+/* words 82-84: cmds/feats supported */
+#define CMDS_W82               0x77ff  /* word 82: defined command locations*/
+#define CMDS_W83               0x3fff  /* word 83: defined command locations*/
+#define CMDS_W84               0x002f  /* word 83: defined command locations*/
+#define SUPPORT_48_BIT         0x0400
+#define NUM_CMD_FEAT_STR       48
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static const char *cmd_feat_str[] = {
+       "",                                     /* word 82 bit 15: obsolete  */
+       "NOP cmd",                              /* word 82 bit 14 */
+       "READ BUFFER cmd",                      /* word 82 bit 13 */
+       "WRITE BUFFER cmd",                     /* word 82 bit 12 */
+       "",                                     /* word 82 bit 11: obsolete  */
+       "Host Protected Area feature set",      /* word 82 bit 10 */
+       "DEVICE RESET cmd",                     /* word 82 bit  9 */
+       "SERVICE interrupt",                    /* word 82 bit  8 */
+       "Release interrupt",                    /* word 82 bit  7 */
+       "Look-ahead",                           /* word 82 bit  6 */
+       "Write cache",                          /* word 82 bit  5 */
+       "PACKET command feature set",           /* word 82 bit  4 */
+       "Power Management feature set",         /* word 82 bit  3 */
+       "Removable Media feature set",          /* word 82 bit  2 */
+       "Security Mode feature set",            /* word 82 bit  1 */
+       "SMART feature set",                    /* word 82 bit  0 */
+                                               /* --------------*/
+       "",                                     /* word 83 bit 15: !valid bit */
+       "",                                     /* word 83 bit 14:  valid bit */
+       "FLUSH CACHE EXT command",              /* word 83 bit 13 */
+       "Mandatory FLUSH CACHE command ",       /* word 83 bit 12 */
+       "Device Configuration Overlay feature set ",
+       "48-bit Address feature set ",          /* word 83 bit 10 */
+       "",
+       "SET MAX security extension",           /* word 83 bit  8 */
+       "Address Offset Reserved Area Boot",    /* word 83 bit  7 */
+       "SET FEATURES subcommand required to spinup after power up",
+       "Power-Up In Standby feature set",      /* word 83 bit  5 */
+       "Removable Media Status Notification feature set",
+       "Adv. Power Management feature set",/* word 83 bit  3 */
+       "CFA feature set",                      /* word 83 bit  2 */
+       "READ/WRITE DMA QUEUED",                /* word 83 bit  1 */
+       "DOWNLOAD MICROCODE cmd",               /* word 83 bit  0 */
+                                               /* --------------*/
+       "",                                     /* word 84 bit 15: !valid bit */
+       "",                                     /* word 84 bit 14:  valid bit */
+       "",                                     /* word 84 bit 13:  reserved */
+       "",                                     /* word 84 bit 12:  reserved */
+       "",                                     /* word 84 bit 11:  reserved */
+       "",                                     /* word 84 bit 10:  reserved */
+       "",                                     /* word 84 bit  9:  reserved */
+       "",                                     /* word 84 bit  8:  reserved */
+       "",                                     /* word 84 bit  7:  reserved */
+       "",                                     /* word 84 bit  6:  reserved */
+       "General Purpose Logging feature set",  /* word 84 bit  5 */
+       "",                                     /* word 84 bit  4:  reserved */
+       "Media Card Pass Through Command feature set ",
+       "Media serial number ",                 /* word 84 bit  2 */
+       "SMART self-test ",                     /* word 84 bit  1 */
+       "SMART error logging "                  /* word 84 bit  0 */
+};
+#endif
+
+
+/* words 85-87: cmds/feats enabled */
+/* use cmd_feat_str[] to display what commands and features have
+ * been enabled with words 85-87
+ */
+
+/* words 89, 90, SECU ERASE TIME */
+#define ERASE_BITS             0x00ff
+
+/* word 92: master password revision */
+/* NOVAL_0 or  NOVAL_1 means no support for master password revision */
+
+/* word 93: hw reset result */
+#define CBLID                  0x2000  /* CBLID status */
+#define RST0                   0x0001  /* 1=reset to device #0 */
+#define DEV_DET                        0x0006  /* how device num determined */
+#define JUMPER_VAL             0x0002  /* device num determined by jumper */
+#define CSEL_VAL               0x0004  /* device num determined by CSEL_VAL */
+
+/* word 127: removable media status notification feature set support */
+#define RM_STAT_BITS           0x0003
+#define RM_STAT_SUP            0x0001
+
+/* word 128: security */
+#define SECU_ENABLED   0x0002
+#define SECU_LEVEL             0x0010
+#define NUM_SECU_STR   6
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static const char *secu_str[] = {
+       "supported",                    /* word 128, bit 0 */
+       "enabled",                      /* word 128, bit 1 */
+       "locked",                       /* word 128, bit 2 */
+       "frozen",                       /* word 128, bit 3 */
+       "expired: security count",      /* word 128, bit 4 */
+       "supported: enhanced erase"     /* word 128, bit 5 */
+};
+#endif
+
+/* word 160: CFA power mode */
+#define VALID_W160             0x8000  /* 1=word valid */
+#define PWR_MODE_REQ           0x2000  /* 1=CFA power mode req'd by some cmds*/
+#define PWR_MODE_OFF           0x1000  /* 1=CFA power moded disabled */
+#define MAX_AMPS               0x0fff  /* value = max current in ma */
+
+/* word 255: integrity */
+#define SIG                    0x00ff  /* signature location */
+#define SIG_VAL                        0x00A5  /* signature value */
+
+#define VERSION "v5.4"
+
+#define TIMING_MB              64
+#define TIMING_BUF_MB          1
+#define TIMING_BUF_BYTES       (TIMING_BUF_MB * 1024 * 1024)
+#define TIMING_BUF_COUNT       (timing_MB / TIMING_BUF_MB)
+#define BUFCACHE_FACTOR                2
+
+#undef DO_FLUSHCACHE           /* under construction: force cache flush on -W0 */
+
+/* Busybox messages and functions */
+
+const char * const bb_msg_shared_mem   ="could not %s sharedmem buf";
+const char * const bb_msg_op_not_supp  =" operation not supported on %s disks";
+
+static void bb_ioctl(int fd, int request, void *argp, const char *string)
+{
+       if (ioctl (fd, request, argp) != 0)
+               bb_perror_msg(" %s", string);
+}
+
+static void if_printf(unsigned long i, char *fmt, ... )
+{
+               va_list ap;
+               va_start(ap, fmt);
+               if (i)
+                       vprintf(fmt, ap);
+               va_end(ap);
+}
+
+static void on_off (unsigned int value);
+
+static void if_printf_on_off(unsigned long get_arg,const char *fmt, unsigned long arg)
+{
+       if (get_arg)
+       {
+               printf(fmt, arg);
+               on_off(arg);
+       }
+}
+
+static void bb_ioctl_on_off(int fd, int request, void *argp, const char *string,
+                                                        const char * fmt)
+{
+       if (ioctl (fd, request, &argp) != 0)
+               bb_perror_msg(" %s", string);
+       else
+       {
+               printf(fmt, (unsigned long) argp);
+               on_off((unsigned long) argp);
+       }
+}
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static void if_else_printf(unsigned long i, char *fmt1, char *fmt2, ... )
+{
+               va_list ap;
+               va_start(ap, fmt2);
+               if (i)
+                       vprintf(fmt1, ap);
+               else
+                       vprintf(fmt2, ap);
+               va_end(ap);
+}
+
+static void print_ascii(uint16_t *p, uint8_t length);
+
+static void xprint_ascii(uint16_t *val ,int i, char * string, int n)
+{
+       if(val[i])
+       {
+               printf("\t%-20s",string);
+               print_ascii(&val[i], n);
+       }
+}
+
+static void if_strcat(unsigned long test, char *modes, char *string)
+{
+       if (test)
+               strcat(modes,string);
+}
+#endif
+
+static void sync_and_sleep(int i)
+{
+       sync();
+       sleep(i);
+}
+
+static uint16_t check_if_min_and_set_val(uint16_t a, uint16_t b)
+{
+       if( a < b)
+               a = b;
+       return a;
+}
+
+static uint16_t check_if_maj_and_set_val(uint16_t a, uint16_t b)
+{
+       if( a > b)
+               a = b;
+       return a;
+}
+
+static unsigned long int set_flag(char *p, char max)
+{
+       if (*p >= '0' && *p <=  max )
+               return 1;
+       return 0;
+}
+
+/* end of  busybox specific stuff */
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode)
+{
+       uint16_t ii;
+       uint8_t err_dma = 0;
+
+       for(ii = 0; ii <= MODE_MAX; ii++)
+       {
+               if(mode_sel & 0x0001)
+               {
+                       printf("*%cdma%u ",cc,ii);
+                       if(*have_mode)
+                               err_dma = 1;
+                       *have_mode = 1;
+               }
+               else if(mode_sup & 0x0001)
+                       printf("%cdma%u ",cc,ii);
+
+               mode_sup >>=1;
+               mode_sel >>=1;
+       }
+       return err_dma;
+}
+
+static void print_ascii(uint16_t *p, uint8_t length) {
+       uint8_t ii;
+       char cl;
+
+       /* find first non-space & print it */
+       for(ii = 0; ii< length; ii++)
+       {
+               if(((char) 0x00ff&((*p)>>8)) != ' ')
+                       break;
+               if((cl = (char) 0x00ff&(*p)) != ' ')
+               {
+                       if_printf((cl != '\0'),"%c",cl);
+                       p++;
+                       ii++;
+                       break;
+               }
+               p++;
+       }
+       /* print the rest */
+       for(; ii< length; ii++)
+       {
+               if(!(*p))
+                       break; /* some older devices have NULLs */
+               printf("%c%c",(char)0x00ff&((*p)>>8),(char)(*p)&0x00ff);
+               p++;
+       }
+       printf("\n");
+}
+
+/* identify() is the only extern function used across two source files.  The
+   others, though, were declared in hdparm.c with global scope; since other
+   functions in that file have static (file) scope, I assume the difference is
+   intentional. */
+static void identify (uint16_t *id_supplied, const char *devname)
+{
+
+       char *id_file = NULL;
+       FILE *fl;
+       uint16_t val[256], ii, jj, kk;
+       uint16_t like_std = 1, std = 0, min_std = 0xffff;
+       uint16_t dev = NO_DEV, eqpt = NO_DEV;
+       uint8_t  have_mode = 0, err_dma = 0;
+       uint8_t  chksum = 0;
+       uint32_t ll, mm, nn, oo;
+       __u64 bbbig; /* (:) */
+
+       if (id_supplied)
+       {
+#if __BYTE_ORDER == __BIG_ENDIAN
+               swab(id_supplied, val, sizeof(val));
+#else
+               memcpy(val, id_supplied, sizeof(val));
+#endif
+       }
+       else
+       {
+               id_file = xcalloc(1, strlen(devname)+22);
+               sprintf(id_file, "/proc/ide/%s/identify", devname);
+               /* open the file, read in all the info and close it */
+               if (id_file == NULL)
+                       fl = stdin;
+               else
+                       fl = bb_xfopen(id_file, "r");
+
+               /* calculate checksum over all bytes */
+               for(ii = GEN_CONFIG; ii<=INTEGRITY; ii++)
+               {
+                       unsigned int scratch;
+                       if(1 != fscanf(fl,"%04x",&scratch))
+                               break;
+                       val[ii] = (uint16_t)scratch;
+                       chksum += val[ii] + (val[ii] >> 8);
+               }
+               fclose(fl);
+               /*bb_fclose_nonstdin(fl);*/
+               if(ii < (INTEGRITY+1))
+                       bb_error_msg_and_die("Input file wrong format or length");
+       }
+       chksum &= 0xff;
+
+       /* check if we recognise the device type */
+       printf("\n");
+       if(!(val[GEN_CONFIG] & NOT_ATA))
+       {
+               dev = ATA_DEV;
+               printf("ATA device, with ");
+       }
+       else if(val[GEN_CONFIG]==CFA_SUPPORT_VAL)
+       {
+               dev = ATA_DEV;
+               like_std = 4;
+               printf("CompactFlash ATA device, with ");
+       }
+       else if(!(val[GEN_CONFIG] & NOT_ATAPI))
+       {
+               dev = ATAPI_DEV;
+               eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT;
+               printf("ATAPI %s, with ", pkt_str[eqpt]);
+               like_std = 3;
+       }
+       else
+               /*"Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n"*/
+               bb_error_msg_and_die("Unknown device type");
+
+       if_printf(!(val[GEN_CONFIG] & MEDIA_REMOVABLE),"non-");
+       printf("removable media\n");
+
+       /* Info from the specific configuration word says whether or not the
+        * ID command completed correctly.  It is only defined, however in
+        * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior
+        * standards.  Since the values allowed for this word are extremely
+        * specific, it should be safe to check it now, even though we don't
+        * know yet what standard this device is using.
+        */
+       if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL) ||
+          (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL) )
+       {
+               like_std = 5;
+               if_printf(((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)),
+                                       "powers-up in standby; SET FEATURES subcmd spins-up.\n");
+               if_printf((((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE)),
+                                       "\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n");
+       }
+
+       /* output the model and serial numbers and the fw revision */
+       xprint_ascii(val, START_MODEL,  "Model Number:",        LENGTH_MODEL);
+       xprint_ascii(val, START_SERIAL, "Serial Number:",       LENGTH_SERIAL);
+       xprint_ascii(val, START_FW_REV, "Firmware Revision:",   LENGTH_FW_REV);
+       xprint_ascii(val, START_MEDIA,  "Media Serial Num:",    LENGTH_MEDIA);
+       xprint_ascii(val, START_MANUF,  "Media Manufacturer:",  LENGTH_MANUF);
+
+       /* major & minor standards version number (Note: these words were not
+        * defined until ATA-3 & the CDROM std uses different words.) */
+       printf("Standards:");
+       if(eqpt != CDROM)
+       {
+               if(val[MINOR] && (val[MINOR] <= MINOR_MAX))
+               {
+                       like_std=check_if_min_and_set_val(like_std, 3);
+                       std = actual_ver[val[MINOR]];
+                       if_printf(std,"\n\tUsed: %s ",minor_str[val[MINOR]]);
+
+               }
+               /* looks like when they up-issue the std, they obsolete one;
+                * thus, only the newest 4 issues need be supported. (That's
+                * what "kk" and "min_std" are all about.) */
+               if(val[MAJOR] && (val[MAJOR] !=NOVAL_1))
+               {
+                       printf("\n\tSupported: ");
+                       jj = val[MAJOR] << 1;
+                       kk = like_std >4 ? like_std-4: 0;
+                       for(ii = 14; (ii >0)&&(ii>kk); ii--)
+                       {
+                               if(jj & 0x8000)
+                               {
+                                       printf("%u ", ii);
+                                       if(like_std < ii)
+                                       {
+                                               like_std = ii;
+                                               kk = like_std >4 ? like_std-4: 0;
+                                       }
+                                       min_std=check_if_maj_and_set_val(min_std, ii);
+                               }
+                               jj <<= 1;
+                       }
+                       like_std=check_if_min_and_set_val(like_std, 3);
+               }
+               /* Figure out what standard the device is using if it hasn't told
+                * us.  If we know the std, check if the device is using any of
+                * the words from the next level up.  It happens.
+                */
+               like_std=check_if_min_and_set_val(like_std, std);
+
+               if(((std == 5) || (!std && (like_std < 6))) &&
+                       ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+                       ((      val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) ||
+                       (((     val[CMDS_SUPP_2] & VALID) == VALID_VAL) &&
+                       (       val[CMDS_SUPP_2] & CMDS_W84) ) ) )
+               {
+                       like_std = 6;
+               }
+               else if(((std == 4) || (!std && (like_std < 5))) &&
+                       ((((val[INTEGRITY]      & SIG) == SIG_VAL) && !chksum) ||
+                       ((      val[HWRST_RSLT] & VALID) == VALID_VAL) ||
+                       (((     val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+                       ((      val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) )
+               {
+                       like_std = 5;
+               }
+               else if(((std == 3) || (!std && (like_std < 4))) &&
+                               ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+                               (((     val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) ||
+                               ((      val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) ||
+                               ((      val[CAPAB_1] & VALID) == VALID_VAL) ||
+                               ((      val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) ||
+                               ((      val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) ) )
+               {
+                       like_std = 4;
+               }
+               else if(((std == 2) || (!std && (like_std < 3))) &&
+                          ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) )
+               {
+                       like_std = 3;
+               }
+               else if(((std == 1) || (!std && (like_std < 2))) &&
+                               ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) ||
+                               (val[WHATS_VALID] & OK_W64_70)) )
+               {
+                       like_std = 2;
+               }
+               if(!std)
+                       printf("\n\tLikely used: %u\n",like_std);
+               else if(like_std > std)
+                       printf("& some of %u\n",like_std);
+               else
+                       printf("\n");
+       }
+       else
+       {
+               /* TBD: do CDROM stuff more thoroughly.  For now... */
+               kk = 0;
+               if(val[CDR_MINOR] == 9)
+               {
+                       kk = 1;
+                       printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5");
+               }
+               if(val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1))
+               {
+                       kk = 1;
+                       printf("\n\tSupported: CD-ROM ATAPI");
+                       jj = val[CDR_MAJOR] >> 1;
+                       for(ii = 1; ii <15; ii++)
+                       {
+                               if_printf((jj & 0x0001),"-%u ", ii);
+                               jj >>= 1;
+                       }
+               }
+               if_else_printf((!kk),"\n\tLikely used CD-ROM ATAPI-1\n","\n");
+               /* the cdrom stuff is more like ATA-2 than anything else, so: */
+               like_std = 2;
+       }
+
+       if(min_std == 0xffff)
+               min_std = like_std > 4 ? like_std - 3 : 1;
+
+       printf("Configuration:\n");
+       /* more info from the general configuration word */
+       if((eqpt != CDROM) && (like_std == 1))
+       {
+               jj = val[GEN_CONFIG] >> 1;
+               for(ii = 1; ii < 15; ii++)
+               {
+                       if_printf((jj & 0x0001),"\t%s\n",ata1_cfg_str[ii]);
+                       jj >>=1;
+               }
+       }
+       if(dev == ATAPI_DEV)
+       {
+               printf("\tDRQ response: "); /* Data Request (DRQ) */
+               switch(val[GEN_CONFIG] & DRQ_RESPONSE_TIME)
+               {
+                       case DRQ_3MS_VAL :
+                               printf("3ms.\n");
+                               break;
+                       case DRQ_INTR_VAL :
+                               printf("<=10ms with INTRQ\n");
+                               break;
+                       case DRQ_50US_VAL :
+                               printf("50us.\n");
+                               break;
+                       default :
+                               printf("unknown.\n");
+                               break;
+               }
+               printf("\tPacket size: ");
+               switch(val[GEN_CONFIG] & PKT_SIZE_SUPPORTED)
+               {
+                       case PKT_SIZE_12_VAL :
+                               printf("12 bytes\n");
+                               break;
+                       case PKT_SIZE_16_VAL :
+                               printf("16 bytes\n");
+                               break;
+                       default :
+                               printf("Unknown\n");
+                               break;
+               }
+       }
+       else
+       {
+               /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */
+               ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB];
+               mm = 0; bbbig = 0;
+               if ( (ll > 0x00FBFC10) && (!val[LCYLS]))
+                       printf("\tCHS addressing not supported\n");
+               else
+               {
+                       jj = val[WHATS_VALID] & OK_W54_58;
+                       printf("\tLogical\t\tmax\tcurrent\n\tcylinders\t%u\t%u\n\theads\t\t%u\t%u\n\tsectors/track\t%u\t%u\n\t--\n",
+                                       val[LCYLS],jj?val[LCYLS_CUR]:0, val[LHEADS],jj?val[LHEADS_CUR]:0, val[LSECTS],jj?val[LSECTS_CUR]:0);
+
+                       if_printf(((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES])),
+                                               "\tbytes/track: %u\tbytes/sector: %u\n",val[TRACK_BYTES], val[SECT_BYTES]);
+
+                       if(jj)
+                       {
+                               mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB];
+                               if(like_std < 3)
+                               {
+                                        /* check Endian of capacity bytes */
+                                       nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR];
+                                       oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB];
+                                       if(abs(mm - nn) > abs(oo - nn))
+                                               mm = oo;
+                               }
+                               printf("\tCHS current addressable sectors:%11u\n",mm);
+                       }
+               }
+               /* LBA addressing */
+               printf("\tLBA    user addressable sectors:%11u\n",ll);
+               if( ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+                    (val[CMDS_SUPP_1] & SUPPORT_48_BIT) )
+               {
+                       bbbig = (__u64)val[LBA_64_MSB]  << 48 |
+                               (__u64)val[LBA_48_MSB]  << 32 |
+                               (__u64)val[LBA_MID]     << 16 |
+                                       val[LBA_LSB] ;
+                       printf("\tLBA48  user addressable sectors:%11llu\n",bbbig);
+               }
+
+               if (!bbbig)
+                       bbbig = (__u64)(ll>mm ? ll : mm); /* # 512 byte blocks */
+               printf("\tdevice size with M = 1024*1024: %11llu MBytes\n",bbbig>>11);
+               bbbig = (bbbig<<9)/1000000;
+               printf("\tdevice size with M = 1000*1000: %11llu MBytes ",bbbig);
+
+               if_else_printf((bbbig > 1000),"(%llu GB)\n","\n",bbbig/1000);
+
+       }
+
+       /* hw support of commands (capabilities) */
+       printf("Capabilities:\n\t");
+
+       if(dev == ATAPI_DEV)
+       {
+               if(eqpt != CDROM)
+                       if_printf((val[CAPAB_0] & CMD_Q_SUP),"Cmd queuing, ");
+
+               if_printf((val[CAPAB_0] & OVLP_SUP),"Cmd overlap, ");
+       }
+       if_printf((val[CAPAB_0] & LBA_SUP),"LBA, ");
+
+       if(like_std != 1)
+       {
+               printf("IORDY");
+               if_printf((!(val[CAPAB_0] & IORDY_SUP)),"(may be)");
+               if_else_printf((val[CAPAB_0] & IORDY_OFF),"(can","(cannot");
+               printf(" be disabled)\n");
+       }
+       else
+               printf("no IORDY\n");
+
+       if((like_std == 1) && val[BUF_TYPE])
+       {
+               kk = val[BUF_TYPE];
+               printf("\tBuffer type: %04x: ",kk);
+               if_else_printf((kk < 2),"single port, single-sector","dual port, multi-sector");
+               if_printf((kk > 2)," with read caching ability");
+               printf("\n");
+       }
+       jj = 0;
+       if((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1)))
+       {
+               printf("\tBuffer size: %.1fkB",(float)val[BUFFER__SIZE]/2);
+               jj = 1;
+       }
+       if((min_std < 4) && (val[RW_LONG]))
+       {
+               printf("\tbytes avail on r/w long: %u",val[RW_LONG]);
+               jj = 1;
+       }
+       if((eqpt != CDROM) && (like_std > 3))
+       {
+               printf("\tQueue depth: %u",(val[QUEUE_DEPTH] & DEPTH_BITS)+1);
+               jj = 1;
+       }
+       if_printf(jj,"\n");
+
+       if(dev == ATA_DEV)
+       {
+               if(like_std == 1)
+                       printf("\tCan%s perform double-word IO\n",(!val[DWORD_IO]) ?"not":"");
+               else
+               {
+                       printf("\tStandby timer values: spec'd by ");
+                       if_else_printf((val[CAPAB_0] & STD_STBY),"Standard","Vendor");
+                       if((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL))
+                               printf(", %s device specific minimum\n",(val[CAPAB_1] & MIN_STANDBY_TIMER)?"with":"no");
+                       else
+                         printf("\n");
+               }
+               printf("\tR/W multiple sector transfer: ");
+               if((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER))
+                       printf("not supported\n");
+               else
+               {
+                       printf("Max = %u\tCurrent = ",val[SECTOR_XFER_MAX] & SECTOR_XFER);
+                       if_else_printf((val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID),
+                                                       "%u\n","?\n",val[SECTOR_XFER_CUR] & SECTOR_XFER);
+               }
+               if((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008))
+               {
+                       /* We print out elsewhere whether the APM feature is enabled or
+                          not.  If it's not enabled, let's not repeat the info; just print
+                          nothing here. */
+                       printf("\tAdvancedPM level: ");
+                       if ( (val[ADV_PWR] & 0xFF00) == 0x4000 )
+                       {
+                               uint8_t apm_level = val[ADV_PWR] & 0x00FF;
+                               printf("%u (0x%x)\n", apm_level, apm_level);
+                       }
+                       else
+                               printf("unknown setting (0x%04x)\n", val[ADV_PWR]);
+               }
+               if(like_std > 5)
+               {
+                       if_printf(val[ACOUSTIC],"\tRecommended acoustic management value: %u, current value: %u\n",
+                                                                       (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff);
+               }
+       }
+       else
+       {
+                /* ATAPI */
+               if(eqpt != CDROM)
+                       if_printf((val[CAPAB_0] & SWRST_REQ),"\tATA sw reset required\n");
+
+               if(val[PKT_REL] || val[SVC_NBSY])
+               {
+                       printf("\tOverlap support:");
+                       if_printf(val[PKT_REL]," %uus to release bus.",val[PKT_REL]);
+                       if_printf(val[SVC_NBSY]," %uus to clear BSY after SERVICE cmd.",val[SVC_NBSY]);
+                       printf("\n");
+               }
+       }
+
+       /* DMA stuff. Check that only one DMA mode is selected. */
+       printf("\tDMA: ");
+       if(!(val[CAPAB_0] & DMA_SUP))
+               printf("not supported\n");
+       else
+       {
+               if_printf((val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA]),
+                                       " sdma%u\n",(val[DMA_MODE] & MODE) >> 8);
+               if(val[SINGLE_DMA])
+               {
+                       jj = val[SINGLE_DMA];
+                       kk = val[SINGLE_DMA] >> 8;
+                       err_dma += mode_loop(jj,kk,'s',&have_mode);
+               }
+               if(val[MULTI_DMA])
+               {
+                       jj = val[MULTI_DMA];
+                       kk = val[MULTI_DMA] >> 8;
+                       err_dma += mode_loop(jj,kk,'m',&have_mode);
+               }
+               if((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA])
+               {
+                       jj = val[ULTRA_DMA];
+                       kk = val[ULTRA_DMA] >> 8;
+                       err_dma += mode_loop(jj,kk,'u',&have_mode);
+               }
+               if_printf((err_dma || !have_mode),"(?)");
+               printf("\n");
+
+               if_printf(((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP)),
+                                       "\t     Interleaved DMA support\n");
+
+               if((val[WHATS_VALID] & OK_W64_70) &&
+                  (val[DMA_TIME_MIN] || val[DMA_TIME_NORM]))
+               {
+                       printf("\t     Cycle time:");
+                       if_printf(val[DMA_TIME_MIN]," min=%uns",val[DMA_TIME_MIN]);
+                       if_printf(val[DMA_TIME_NORM]," recommended=%uns",val[DMA_TIME_NORM]);
+                       printf("\n");
+               }
+       }
+
+       /* Programmed IO stuff */
+       printf("\tPIO: ");
+        /* If a drive supports mode n (e.g. 3), it also supports all modes less
+        * than n (e.g. 3, 2, 1 and 0).  Print all the modes. */
+       if((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP))
+       {
+               jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007;
+               for(ii = 0; ii <= PIO_MODE_MAX ; ii++)
+               {
+                       if_printf((jj & 0x0001),"pio%d ",ii);
+                       jj >>=1;
+               }
+               printf("\n");
+       }
+       else if(((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE) )
+       {
+               for(ii = 0; ii <= val[PIO_MODE]>>8; ii++)
+                       printf("pio%d ",ii);
+               printf("\n");
+       }
+       else
+               printf("unknown\n");
+
+       if(val[WHATS_VALID] & OK_W64_70)
+       {
+               if(val[PIO_NO_FLOW] || val[PIO_FLOW])
+               {
+                       printf("\t     Cycle time:");
+                       if_printf(val[PIO_NO_FLOW]," no flow control=%uns", val[PIO_NO_FLOW]);
+                       if_printf(val[PIO_FLOW],"  IORDY flow control=%uns", val[PIO_FLOW]);
+                       printf("\n");
+               }
+       }
+
+       if((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+       {
+               printf("Commands/features:\n\tEnabled\tSupported:\n");
+               jj = val[CMDS_SUPP_0];
+               kk = val[CMDS_EN_0];
+               for(ii = 0; ii < NUM_CMD_FEAT_STR; ii++)
+               {
+                       if((jj & 0x8000) && (*cmd_feat_str[ii] != '\0'))
+                       {
+                               if_else_printf((kk & 0x8000),"\t   *","\t");
+                               printf("\t%s\n",cmd_feat_str[ii]);
+                       }
+                       jj <<=1; kk<<=1;
+                       if(ii%16 == 15)
+                       {
+                               jj = val[CMDS_SUPP_0+1+(ii/16)];
+                               kk = val[CMDS_EN_0+1+(ii/16)];
+                       }
+                       if(ii == 31)
+                       {
+                               if((val[CMDS_SUPP_2] & VALID) != VALID_VAL)
+                                       ii +=16;
+                       }
+               }
+       }
+       if_printf(((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP),
+                               "\tRemovable Media Status Notification feature set supported\n");
+
+
+       /* security */
+       if((eqpt != CDROM) && (like_std > 3) &&
+          (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME]))
+       {
+               printf("Security: \n");
+               if_printf((val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1)),
+                                       "\tMaster password revision code = %u\n",val[PSWD_CODE]);
+               jj = val[SECU_STATUS];
+               if(jj)
+               {
+                       for(ii = 0; ii < NUM_SECU_STR; ii++)
+                       {
+                               if_else_printf((!(jj & 0x0001)),"\tnot\t%s\n", "\t\t%s\n", secu_str[ii]);
+                               jj >>=1;
+                       }
+                       if(val[SECU_STATUS] & SECU_ENABLED)
+                       {
+                               printf("\tSecurity level ");
+                               if_else_printf((val[SECU_STATUS] & SECU_LEVEL),"maximum\n","high\n");
+                       }
+               }
+               jj =  val[ERASE_TIME]     & ERASE_BITS;
+               kk =  val[ENH_ERASE_TIME] & ERASE_BITS;
+               if(jj || kk)
+               {
+                       printf("\t");
+                       if_printf(jj,"%umin for SECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1);
+                       if_printf(kk,"%umin for ENHANCED SECURITY ERASE UNIT.", kk==ERASE_BITS ? 508 : kk<<1);
+                       printf("\n");
+               }
+       }
+
+       /* reset result */
+       if((val[HWRST_RSLT] & VALID) == VALID_VAL)
+       {
+               printf("HW reset results:\n");
+               if_else_printf((val[HWRST_RSLT] & CBLID),"\tCBLID- above Vih\n","\tCBLID- below Vih\n");
+
+               if(val[HWRST_RSLT] & RST0)
+               {
+                       printf("\tDevice num = 0");
+                       jj = val[HWRST_RSLT];
+               }
+               else
+               {
+                       printf("\tDevice num = 1");
+                       jj = val[HWRST_RSLT] >> 8;
+               }
+
+               if((jj & DEV_DET) == JUMPER_VAL)
+                       printf(" determined by the jumper");
+               else if((jj & DEV_DET) == CSEL_VAL)
+                       printf(" determined by CSEL");
+               printf("\n");
+       }
+
+       /* more stuff from std 5 */
+       if((like_std > 4) && (eqpt != CDROM))
+       {
+               if(val[CFA_PWR_MODE] & VALID_W160)
+               {
+                       printf("CFA power mode 1:\n\t");
+                       if_else_printf((val[CFA_PWR_MODE] & PWR_MODE_OFF),"disabled","enabled");
+
+                       if_printf((val[CFA_PWR_MODE] & PWR_MODE_REQ)," and required by some commands");
+                       printf("\n");
+
+                       if_printf((val[CFA_PWR_MODE] & MAX_AMPS),"\tMaximum current = %uma\n",val[CFA_PWR_MODE] & MAX_AMPS);
+               }
+               if((val[INTEGRITY] & SIG) == SIG_VAL)
+               {
+                       printf("Checksum: ");
+                       if_printf(chksum,"in");
+                       printf("correct\n");
+               }
+       }
+
+       exit(0);
+}
+#endif
+
+static int verbose = 0, get_identity = 0, get_geom = 0, noisy = 1, quiet = 0;
+static int flagcount = 0, do_flush = 0, is_scsi_hd = 0, is_xt_hd = 0;
+static int do_ctimings, do_timings = 0;
+
+static unsigned long set_readahead= 0, get_readahead= 0, Xreadahead= 0;
+static unsigned long set_readonly = 0, get_readonly = 0, readonly = 0;
+static unsigned long set_unmask   = 0, get_unmask   = 0, unmask   = 0;
+static unsigned long set_mult     = 0, get_mult     = 0, mult     = 0;
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
+static unsigned long set_dma      = 0, get_dma      = 0, dma      = 0;
+#endif
+static unsigned long set_dma_q   = 0, get_dma_q    = 0, dma_q    = 0;
+static unsigned long set_nowerr   = 0, get_nowerr   = 0, nowerr   = 0;
+static unsigned long set_keep     = 0, get_keep     = 0, keep     = 0;
+static unsigned long set_io32bit  = 0, get_io32bit  = 0, io32bit  = 0;
+static unsigned long set_piomode  = 0, noisy_piomode= 0;
+static int piomode = 0;
+#ifdef HDIO_DRIVE_CMD
+static unsigned long set_dkeep    = 0, get_dkeep    = 0, dkeep    = 0;
+static unsigned long set_standby  = 0, get_standby  = 0, standby_requested= 0;
+static unsigned long set_xfermode = 0, get_xfermode = 0;
+static int xfermode_requested= 0;
+static unsigned long set_lookahead= 0, get_lookahead= 0, lookahead= 0;
+static unsigned long set_prefetch = 0, get_prefetch = 0, prefetch = 0;
+static unsigned long set_defects  = 0, get_defects  = 0, defects  = 0;
+static unsigned long set_wcache   = 0, get_wcache   = 0, wcache   = 0;
+static unsigned long set_doorlock = 0, get_doorlock = 0, doorlock = 0;
+static unsigned long set_seagate  = 0, get_seagate  = 0;
+static unsigned long set_standbynow = 0, get_standbynow = 0;
+static unsigned long set_sleepnow   = 0, get_sleepnow   = 0;
+static unsigned long get_powermode  = 0;
+static unsigned long set_apmmode = 0, get_apmmode= 0, apmmode = 0;
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static int get_IDentity = 0;
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+static int     unregister_hwif = 0;
+static int     hwif = 0;
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
+static int     scan_hwif = 0;
+static int     hwif_data = 0;
+static int     hwif_ctrl = 0;
+static int     hwif_irq = 0;
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static int     set_busstate = 0, get_busstate = 0, busstate = 0;
+#endif
+static int     reread_partn = 0;
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
+static int     perform_reset = 0;
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static int     perform_tristate = 0,   tristate = 0;
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+
+// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then
+// then the HDIO_GET_IDENTITY only returned 142 bytes.
+// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes,
+// and HDIO_GET_IDENTITY returns 512 bytes.  But the latest
+// 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY
+// (which they should, but they should just return -EINVAL).
+//
+// So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes.
+// On a really old system, it will not, and we will be confused.
+// Too bad, really.
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static const char *cfg_str[] =
+{      "",          " HardSect",   " SoftSect",  " NotMFM",
+       " HdSw>15uSec", " SpinMotCtl", " Fixed",     " Removeable",
+       " DTR<=5Mbs",   " DTR>5Mbs",   " DTR>10Mbs", " RotSpdTol>.5%",
+       " dStbOff",     " TrkOff",     " FmtGapReq", " nonMagnetic"
+};
+
+static const char *BuffType[]  = {"unknown", "1Sect", "DualPort", "DualPortCache"};
+
+static void dump_identity (const struct hd_driveid *id)
+{
+       int i;
+       char pmodes[64] = {0,}, dmodes[128]={0,}, umodes[128]={0,};
+       const unsigned short int *id_regs= (const void*) id;
+       unsigned long capacity;
+
+       printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={",
+                               id->model, id->fw_rev, id->serial_no);
+       for (i=0; i<=15; i++)
+               if_printf((id->config & (1<<i)),"%s", cfg_str[i]);
+
+       printf(" }\n RawCHS=%u/%u/%u, TrkSize=%u, SectSize=%u, ECCbytes=%u\n",
+                               id->cyls, id->heads, id->sectors, id->track_bytes,
+                               id->sector_bytes, id->ecc_bytes);
+
+       if (id->buf_type > 3)
+               printf("%s%u", " BuffType=", id->buf_type);
+       else
+               printf("%s%s", " BuffType=", BuffType[id->buf_type]);
+
+       printf(", BuffSize=%ukB, MaxMultSect=%u", id->buf_size/2, id->max_multsect);
+       if (id->max_multsect)
+       {
+               printf(", MultSect=");
+               if (!(id->multsect_valid&1))
+                       printf("?%u?", id->multsect);
+               else if (id->multsect)
+                       printf("%u", id->multsect);
+               else
+                       printf("off");
+       }
+       printf("\n");
+       if (id->tPIO <= 5)
+       {
+               strcat(pmodes, "pio0 ");
+               if_strcat((id->tPIO >= 1), pmodes, "pio1 ");
+               if_strcat((id->tPIO >= 2), pmodes, "pio2 ");
+
+       }
+       if_printf((!(id->field_valid&1))," (maybe):");
+#if __BYTE_ORDER == __BIG_ENDIAN
+       capacity = (id->cur_capacity0 << 16) | id->cur_capacity1;
+#else
+       capacity = (id->cur_capacity1 << 16) | id->cur_capacity0;
+#endif
+       printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s",id->cur_cyls, id->cur_heads,
+                                                                                                       id->cur_sectors, capacity ,
+                                                                                       ((id->capability&2)==0)?"no":"yes");
+
+       if_printf((id->capability&2),", LBAsects=%u", id->lba_capacity);
+
+       if (id->capability&1)
+       {
+               if (id->dma_1word | id->dma_mword)
+               {
+                       if_strcat((id->dma_1word & 0x100),      dmodes, "*");
+                       if_strcat((id->dma_1word & 1),          dmodes, "sdma0 ");
+                       if_strcat((id->dma_1word & 0x200),      dmodes, "*");
+                       if_strcat((id->dma_1word & 2),          dmodes, "sdma1 ");
+                       if_strcat((id->dma_1word & 0x400),      dmodes, "*");
+                       if_strcat((id->dma_1word & 4),          dmodes, "sdma2 ");
+                       if_strcat((id->dma_1word & 0xf800),     dmodes, "*");
+                       if_strcat((id->dma_1word & 0xf8),       dmodes, "sdma? ");
+                       if_strcat((id->dma_mword & 0x100),      dmodes, "*");
+                       if_strcat((id->dma_mword & 1),          dmodes, "mdma0 ");
+                       if_strcat((id->dma_mword & 0x200),      dmodes, "*");
+                       if_strcat((id->dma_mword & 2),          dmodes, "mdma1 ");
+                       if_strcat((id->dma_mword & 0x400),      dmodes, "*");
+                       if_strcat((id->dma_mword & 4),          dmodes, "mdma2 ");
+                       if_strcat((id->dma_mword & 0xf800),     dmodes, "*");
+                       if_strcat((id->dma_mword & 0xf8),       dmodes, "mdma? ");
+               }
+       }
+       printf("\n IORDY=");
+       if (id->capability&8)
+               printf((id->capability&4) ? "on/off" : "yes");
+       else
+               printf("no");
+
+       if ((id->capability&8) || (id->field_valid&2))
+       {
+               if (id->field_valid&2)
+               {
+                       printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy);
+                       if_strcat((id->eide_pio_modes & 1), pmodes, "pio3 ");
+                       if_strcat((id->eide_pio_modes & 2), pmodes, "pio4 ");
+                       if_strcat((id->eide_pio_modes &~3), pmodes, "pio? ");
+               }
+               if (id->field_valid&4)
+               {
+                       if_strcat((id->dma_ultra & 0x100),umodes,"*");
+                       if_strcat((id->dma_ultra & 0x001),umodes,"udma0 ");
+                       if_strcat((id->dma_ultra & 0x200),umodes,"*");
+                       if_strcat((id->dma_ultra & 0x002),umodes,"udma1 ");
+                       if_strcat((id->dma_ultra & 0x400),umodes,"*");
+                       if_strcat((id->dma_ultra & 0x004),umodes,"udma2 ");
+#ifdef __NEW_HD_DRIVE_ID
+                       if (id->hw_config & 0x2000)
+                       {
+#else /* !__NEW_HD_DRIVE_ID */
+                       if (id->word93 & 0x2000)
+                       {
+#endif /* __NEW_HD_DRIVE_ID */
+                               if_strcat((id->dma_ultra & 0x0800),umodes,"*");
+                               if_strcat((id->dma_ultra & 0x0008),umodes,"udma3 ");
+                               if_strcat((id->dma_ultra & 0x1000),umodes,"*");
+                               if_strcat((id->dma_ultra & 0x0010),umodes,"udma4 ");
+                               if_strcat((id->dma_ultra & 0x2000),umodes,"*");
+                               if_strcat((id->dma_ultra & 0x0020),umodes,"udma5 ");
+                               if_strcat((id->dma_ultra & 0x4000),umodes,"*");
+                               if_strcat((id->dma_ultra & 0x0040),umodes,"udma6 ");
+                               if_strcat((id->dma_ultra & 0x8000),umodes,"*");
+                               if_strcat((id->dma_ultra & 0x0080),umodes,"udma7 ");
+                       }
+               }
+       }
+       if_printf(((id->capability&1) && (id->field_valid&2)),
+                               ", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time);
+       printf("\n PIO modes:  %s", pmodes);
+       if_printf((*dmodes),"\n DMA modes:  %s", dmodes);
+       if_printf((*umodes),"\n UDMA modes: %s", umodes);
+
+       printf("\n AdvancedPM=%s",((id_regs[83]&8)==0)?"no":"yes");
+       if (id_regs[83] & 8)
+       {
+               if (!(id_regs[86]&8))
+                       printf(": disabled (255)");
+               else if ((id_regs[91]&0xFF00)!=0x4000)
+                       printf(": unknown setting");
+               else
+                       printf(": mode=0x%02X (%u)",id_regs[91]&0xFF,id_regs[91]&0xFF);
+       }
+       if_printf( (id_regs[82]&0x20)," WriteCache=%s",(id_regs[85]&0x20) ? "enabled" : "disabled");
+#ifdef __NEW_HD_DRIVE_ID
+       if ((id->minor_rev_num && id->minor_rev_num <= 31) || (id->major_rev_num && id->minor_rev_num <= 31))
+       {
+               printf("\n Drive conforms to: ");
+               if_else_printf((id->minor_rev_num <= 31),"%s: ","unknown: ", minor_str[id->minor_rev_num]);
+               if (id->major_rev_num < 31)
+               {
+                       for (i=0; i <= 15; i++)
+                               if_printf((id->major_rev_num & (1<<i))," %u", i);
+               }
+       }
+#endif /* __NEW_HD_DRIVE_ID */
+       printf("\n\n * signifies the current active mode\n\n");
+}
+#endif
+
+static void flush_buffer_cache (int fd)
+{
+       fsync (fd);                             /* flush buffers */
+       bb_ioctl(fd, BLKFLSBUF, NULL,"BLKFLSBUF" ) ;/* do it again, big time */
+#ifdef HDIO_DRIVE_CMD
+       sleep(1);
+       if (ioctl(fd, HDIO_DRIVE_CMD, NULL) && errno != EINVAL) /* await completion */
+                       bb_perror_msg("HDIO_DRIVE_CMD");
+#endif
+}
+
+static int seek_to_zero (int fd)
+{
+       if (lseek(fd, (off_t) 0, SEEK_SET))
+               return 1;
+       return 0;
+}
+
+static int read_big_block (int fd, char *buf)
+{
+
+       const char *string;
+       int i, rc;
+       if ((rc = read(fd, buf, TIMING_BUF_BYTES)) != TIMING_BUF_BYTES)
+       {
+               switch(rc)
+               {
+                       case -1:
+                               string = "read()";
+                               break;
+                       case  0:
+                               string = "read() hit EOF - device too small";
+                               break;
+                       default:
+                               string = "read(%u) returned %u bytes";
+               }
+               bb_error_msg(string, TIMING_BUF_BYTES, rc);
+               return 1;
+       }
+
+       /* access all sectors of buf to ensure the read fully completed */
+       for (i = 0; i < TIMING_BUF_BYTES; i += 512)
+               buf[i] &= 1;
+       return 0;
+}
+
+static double correction = 0.0;
+
+static void do_time (int flag, int fd)
+/*
+       flag = 0 time_cache
+       flag = 1 time_device
+*/
+{
+       int i;
+       char *buf;
+       double elapsed;
+       struct itimerval e1, e2;
+       int shmid;
+       int timing_MB = TIMING_MB;
+
+       if ((shmid = shmget(IPC_PRIVATE, TIMING_BUF_BYTES, 0600)) == -1)
+       {
+               bb_error_msg (bb_msg_shared_mem,"allocate"); /*"could not allocate sharedmem buf"*/
+               return;
+       }
+       if (shmctl(shmid, SHM_LOCK, NULL) == -1)
+       {
+               bb_error_msg (bb_msg_shared_mem,"lock"); /*"could not lock sharedmem buf"*/
+               (void) shmctl(shmid, IPC_RMID, NULL);
+               return;
+       }
+       if ((buf = shmat(shmid, (char *) 0, 0)) == (char *) -1)
+       {
+               bb_error_msg (bb_msg_shared_mem,"attach"); /*"could not attach sharedmem buf"*/
+               (void) shmctl(shmid, IPC_RMID, NULL);
+               return;
+       }
+       if (shmctl(shmid, IPC_RMID, NULL) == -1)
+               bb_error_msg ("shmctl(,IPC_RMID,)");
+
+       /* Clear out the device request queues & give them time to complete */
+       sync_and_sleep(3);
+
+       if(flag  == 0) /* Time cache */
+       {
+               /* Calculate a correction factor for the basic
+               * overhead of doing a read() from the buffer cache.
+               * To do this, we read the data once to "cache it" and
+               * to force full preallocation of our timing buffer,
+               * and then we re-read it 10 times while timing it.
+               *
+               * getitimer() is used rather than gettimeofday() because
+               * it is much more consistent (on my machine, at least).
+               */
+               setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
+               if (seek_to_zero (fd))
+                       return;
+               if (read_big_block (fd, buf))
+                       return;
+               printf(" Timing buffer-cache reads:   ");
+               fflush(stdout);
+
+               /* Clear out the device request queues & give them time to complete */
+               sync_and_sleep(1);
+
+               /* Time re-reading from the buffer-cache */
+               getitimer(ITIMER_REAL, &e1);
+               for (i = (BUFCACHE_FACTOR * TIMING_BUF_COUNT) ; i > 0; --i)
+               {
+                       if (seek_to_zero (fd))
+                               goto quit;
+                       if (read_big_block (fd, buf))
+                               goto quit;
+               }
+               getitimer(ITIMER_REAL, &e2);
+               correction = (e1.it_value.tv_sec - e2.it_value.tv_sec) + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
+
+               /* Now remove the lseek() from the correction factor */
+               getitimer(ITIMER_REAL, &e1);
+               for (i = (BUFCACHE_FACTOR * TIMING_BUF_COUNT) ; i > 0; --i)
+               {
+                       if (seek_to_zero (fd))
+                               goto quit;
+               }
+               getitimer(ITIMER_REAL, &e2);
+               correction -= (e1.it_value.tv_sec - e2.it_value.tv_sec)
+               + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
+
+               if ((BUFCACHE_FACTOR * timing_MB) >= correction)  /* more than 1MB/s */
+                       printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",
+                               (BUFCACHE_FACTOR * timing_MB),  correction,
+                               (BUFCACHE_FACTOR * timing_MB) / correction);
+               else
+                       printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",
+                               (BUFCACHE_FACTOR * timing_MB),  correction,
+                               (BUFCACHE_FACTOR * timing_MB) / correction * 1024);
+               correction /= BUFCACHE_FACTOR;
+
+               flush_buffer_cache(fd);
+               sleep(1);
+       }
+       else /* Time device */
+       {
+               printf(" Timing buffered disk reads:  ");
+               fflush(stdout);
+
+               /*
+               * getitimer() is used rather than gettimeofday() because
+               * it is much more consistent (on my machine, at least).
+               */
+               setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
+
+               /* Now do the timings for real */
+               getitimer(ITIMER_REAL, &e1);
+               for (i = TIMING_BUF_COUNT; i > 0; --i)
+               {
+                       if (read_big_block (fd, buf))
+                               goto quit;
+               }
+               getitimer(ITIMER_REAL, &e2);
+
+               elapsed =       (e1.it_value.tv_sec - e2.it_value.tv_sec) + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
+
+               if (timing_MB >= elapsed)  /* more than 1MB/s */
+                       printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",timing_MB, elapsed, timing_MB / elapsed);
+               else
+                       printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",timing_MB, elapsed, timing_MB / elapsed * 1024);
+
+               /*"Hmm.. suspicious results: probably not enough free memory for a proper test.");*/
+               if (elapsed <= (correction * 2))
+                                       bb_error_msg(bb_msg_memory_exhausted);
+
+#if 0  /* the "estimate" is just plain wrong for many systems.. */
+               else if (correction != 0.0) {
+                       printf(" Estimating raw driver speed: ");
+                       elapsed -= correction;
+                       if (timing_MB >= elapsed)  /* more than 1MB/s */
+                               printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",
+                                       timing_MB, elapsed, timing_MB / elapsed);
+                       else
+                               printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",
+                                       timing_MB, elapsed, timing_MB / elapsed * 1024);
+               }
+#endif
+       }
+quit:
+       if (-1 == shmdt(buf))
+               bb_error_msg (bb_msg_shared_mem,"detach"); /*"could not detach sharedmem buf"*/
+}
+
+
+static void no_scsi (void)
+{
+       /*" operation not supported on SCSI disks"*/
+       if (is_scsi_hd)
+               bb_error_msg_and_die(bb_msg_op_not_supp,"SCSI");
+}
+
+static void no_xt (void)
+{
+       if (is_xt_hd)
+               bb_error_msg_and_die(bb_msg_op_not_supp,"XT");
+}
+
+static void on_off (unsigned int value)
+{
+       printf(value ? " (on)\n" : " (off)\n");
+}
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static void bus_state_value (unsigned int value)
+{
+       const char *string;
+
+       switch (value)
+       {
+               case BUSSTATE_ON:
+                       string = " (on)\n";
+                       break;
+               case BUSSTATE_OFF:
+                       string = " (off)\n";
+                       break;
+               case BUSSTATE_TRISTATE:
+                       string = " (tristate)\n";
+                       break;
+               default:
+                       string = " (unknown: %d)\n";
+                       break;
+       }
+       printf(string, value);
+}
+#endif
+
+#ifdef HDIO_DRIVE_CMD
+static void interpret_standby (unsigned int standby)
+{
+       printf(" (");
+       switch(standby)
+       {
+               case 0:
+                       printf("off");
+                       break;
+               case 252:
+                       printf("21 minutes");
+                       break;
+               case 253:
+                       printf("vendor-specific");
+                       break;
+               case 254:
+                       printf("?reserved");
+                       break;
+               case 255:
+                       printf("21 minutes + 15 seconds");
+                       break;
+               default:
+                       if (standby <= 240)
+                       {
+                               unsigned int secs = standby * 5;
+                               unsigned int mins = secs / 60;
+                               secs %= 60;
+                               if_printf(mins,"%u minutes", mins);
+                               if_printf((mins && secs)," + ");
+                               if_printf(secs,"%u seconds", secs);
+                       }
+                       else if (standby <= 251)
+                       {
+                               unsigned int mins = (standby - 240) * 30;
+                               unsigned int hrs  = mins / 60;
+                               mins %= 60;
+                               if_printf(hrs,"%u hours", hrs);
+                               if_printf((hrs && mins)," + ");
+                               if_printf(mins,"%u minutes", mins);
+                       }
+                       else
+                               printf("illegal value");
+                       break;
+       }
+       printf(")\n");
+}
+
+struct xfermode_entry {
+    int val;
+    const char *name;
+};
+
+static const struct xfermode_entry xfermode_table[] = {
+    { 8,    "pio0" },
+    { 9,    "pio1" },
+    { 10,   "pio2" },
+    { 11,   "pio3" },
+    { 12,   "pio4" },
+    { 13,   "pio5" },
+    { 14,   "pio6" },
+    { 15,   "pio7" },
+    { 16,   "sdma0" },
+    { 17,   "sdma1" },
+    { 18,   "sdma2" },
+    { 19,   "sdma3" },
+    { 20,   "sdma4" },
+    { 21,   "sdma5" },
+    { 22,   "sdma6" },
+    { 23,   "sdma7" },
+    { 32,   "mdma0" },
+    { 33,   "mdma1" },
+    { 34,   "mdma2" },
+    { 35,   "mdma3" },
+    { 36,   "mdma4" },
+    { 37,   "mdma5" },
+    { 38,   "mdma6" },
+    { 39,   "mdma7" },
+    { 64,   "udma0" },
+    { 65,   "udma1" },
+    { 66,   "udma2" },
+    { 67,   "udma3" },
+    { 68,   "udma4" },
+    { 69,   "udma5" },
+    { 70,   "udma6" },
+    { 71,   "udma7" },
+    { 0, NULL }
+};
+
+static int translate_xfermode(char * name)
+{
+       const struct xfermode_entry *tmp;
+       char *endptr;
+       int val = -1;
+
+
+       for (tmp = xfermode_table; tmp->name != NULL; ++tmp)
+       {
+               if (!strcmp(name, tmp->name))
+                       return tmp->val;
+
+       }
+
+       val = strtol(name, &endptr, 10);
+       if (*endptr == '\0')
+               return val;
+
+       return -1;
+}
+
+static void interpret_xfermode (unsigned int xfermode)
+{
+       printf(" (");
+       switch(xfermode) {
+               case 0:
+                       printf("default PIO mode");
+                       break;
+               case 1:
+                       printf("default PIO mode, disable IORDY");
+                       break;
+               case 8:
+               case 9:
+               case 10:
+               case 11:
+               case 12:
+               case 13:
+               case 14:
+               case 15:
+                       printf("PIO flow control mode%u", xfermode-8);
+                       break;
+               case 16:
+               case 17:
+               case 18:
+               case 19:
+               case 20:
+               case 21:
+               case 22:
+               case 23:
+                       printf("singleword DMA mode%u", xfermode-16);
+                       break;
+               case 32:
+               case 33:
+               case 34:
+               case 35:
+               case 36:
+               case 37:
+               case 38:
+               case 39:
+                       printf("multiword DMA mode%u", xfermode-32);
+                       break;
+               case 64:
+               case 65:
+               case 66:
+               case 67:
+               case 68:
+               case 69:
+               case 70:
+               case 71:
+                       printf("UltraDMA mode%u", xfermode-64);
+                       break;
+               default:
+                       printf("unknown, probably not valid");
+                       break;
+       }
+       printf(")\n");
+}
+#endif /* HDIO_DRIVE_CMD */
+
+#ifndef VXVM_MAJOR
+#define VXVM_MAJOR 199
+#endif
+
+#ifndef CCISS_MAJOR
+#define CCISS_MAJOR 104
+#endif
+
+static void process_dev (char *devname)
+{
+       int fd;
+       static long parm, multcount;
+       struct stat stat_buf;
+#ifndef HDIO_DRIVE_CMD
+       int force_operation = 0;
+#endif
+       if (stat(devname,&stat_buf))
+               bb_perror_msg_and_die(devname);
+
+       switch(major(stat_buf.st_rdev))
+       {
+#ifdef SCSI_DISK0_MAJOR
+               case (SCSI_DISK0_MAJOR):
+               case (SCSI_DISK1_MAJOR):
+               case (SCSI_DISK2_MAJOR):
+               case (SCSI_DISK3_MAJOR):
+               case (SCSI_DISK4_MAJOR):
+               case (SCSI_DISK5_MAJOR):
+               case (SCSI_DISK6_MAJOR):
+               case (SCSI_DISK7_MAJOR):
+#else
+               case (SCSI_DISK_MAJOR):
+#endif
+#ifdef MD_MAJOR
+               case (MD_MAJOR):
+#endif
+               case (VXVM_MAJOR):
+#ifdef LVM_BLK_MAJOR
+               case (LVM_BLK_MAJOR):
+#endif
+               case (CCISS_MAJOR):
+                       is_scsi_hd = 1;
+                       break;
+#ifdef XT_DISK_MAJOR
+               case (XT_DISK_MAJOR):
+                       is_xt_hd = 1;
+                       break;
+#endif
+               case IDE0_MAJOR:
+               case IDE1_MAJOR:
+#ifdef IDE2_MAJOR
+               case IDE2_MAJOR:
+#endif
+#ifdef IDE3_MAJOR
+               case IDE3_MAJOR:
+#endif
+#ifdef IDE4_MAJOR
+               case IDE4_MAJOR:
+#endif
+#ifdef IDE5_MAJOR
+               case IDE5_MAJOR:
+#endif
+#ifdef IDE6_MAJOR
+               case IDE6_MAJOR:
+#endif
+#ifdef IDE7_MAJOR
+               case IDE7_MAJOR:
+#endif
+#ifdef IDE8_MAJOR
+               case IDE8_MAJOR:
+#endif
+#ifdef IDE9_MAJOR
+               case IDE9_MAJOR:
+#endif
+                       break;  /* do nothing */
+               default:
+                       bb_error_msg_and_die("%s not supported",devname);
+       }
+
+       fd = bb_xopen (devname, O_RDONLY|O_NONBLOCK);
+       if_printf( (!quiet),"\n%s:\n", devname);
+
+       if (set_readahead)
+       {
+               if_printf(get_readahead," setting fs readahead to %ld\n", Xreadahead);
+               bb_ioctl(fd, BLKRASET,(int *)Xreadahead,"BLKRASET");
+       }
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+       if (unregister_hwif)
+       {
+               no_scsi();
+               printf(" attempting to unregister hwif#%u\n", hwif);
+               bb_ioctl(fd, HDIO_UNREGISTER_HWIF,(int *)hwif,"HDIO_UNREGISTER_HWIF");
+       }
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
+       if (scan_hwif)
+       {
+               int     args[3];
+               no_scsi();
+               printf(" attempting to scan hwif (0x%x, 0x%x, %u)\n", hwif_data, hwif_ctrl, hwif_irq);
+               args[0] = hwif_data;
+               args[1] = hwif_ctrl;
+               args[2] = hwif_irq;
+               bb_ioctl(fd, HDIO_SCAN_HWIF, args, "HDIO_SCAN_HWIF");
+       }
+#endif
+       if (set_piomode)
+       {
+               no_scsi();
+               no_xt();
+
+               if (noisy_piomode)
+               {
+                       printf(" attempting to ");
+                       if (piomode == 255)
+                               printf("auto-tune PIO mode\n");
+                       else if (piomode < 100)
+                               printf("set PIO mode to %d\n", piomode);
+                       else if (piomode < 200)
+                               printf("set MDMA mode to %d\n", (piomode-100));
+                       else
+                               printf("set UDMA mode to %d\n", (piomode-200));
+               }
+               bb_ioctl(fd, HDIO_SET_PIO_MODE, (int *)piomode, "HDIO_SET_PIO_MODE");
+       }
+       if (set_io32bit)
+       {
+               no_scsi();
+               no_xt();
+               if_printf(get_io32bit," setting 32-bit IO_support flag to %ld\n", io32bit);
+               bb_ioctl(fd, HDIO_SET_32BIT, (int *)io32bit, "HDIO_SET_32BIT");
+       }
+       if (set_mult)
+       {
+               no_scsi();
+               no_xt();
+               if_printf(get_mult, " setting multcount to %ld\n", mult);
+               if(ioctl(fd, HDIO_SET_MULTCOUNT, mult))
+                       bb_perror_msg("HDIO_SET_MULTCOUNT");
+#ifndef HDIO_DRIVE_CMD
+               else
+                       force_operation = 1;
+#endif
+       }
+       if (set_readonly)
+       {
+               if_printf_on_off(get_readonly," setting readonly to %ld", readonly);
+               bb_ioctl(fd, BLKROSET, &readonly, "BLKROSET");
+       }
+       if (set_unmask)
+       {
+               no_scsi();
+               no_xt();
+               if_printf_on_off(get_unmask," setting unmaskirq to %ld", unmask);
+               bb_ioctl(fd, HDIO_SET_UNMASKINTR, (int *)unmask, "HDIO_SET_UNMASKINTR");
+       }
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
+       if (set_dma)
+       {
+               no_scsi();
+               if_printf_on_off(get_dma," setting using_dma to %ld", dma);
+               bb_ioctl(fd, HDIO_SET_DMA, (int *)dma, "HDIO_SET_DMA");
+       }
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA */
+       if (set_dma_q)
+       {
+               no_scsi();
+               if_printf_on_off(get_dma_q," setting DMA queue_depth to %ld", dma_q);
+               bb_ioctl(fd, HDIO_SET_QDMA, (int *)dma_q, "HDIO_SET_QDMA");
+       }
+       if (set_nowerr)
+       {
+               no_scsi();
+               no_xt();
+               if_printf_on_off(get_nowerr," setting nowerr to %ld", nowerr);
+               bb_ioctl(fd, HDIO_SET_NOWERR, (int *)nowerr,"HDIO_SET_NOWERR");
+       }
+       if (set_keep)
+       {
+               no_scsi();
+               no_xt();
+               if_printf_on_off(get_keep," setting keep_settings to %ld", keep);
+               bb_ioctl(fd, HDIO_SET_KEEPSETTINGS, (int *)keep,"HDIO_SET_KEEPSETTINGS");
+       }
+#ifdef HDIO_DRIVE_CMD
+       if (set_doorlock)
+       {
+               unsigned char args[4] = {0,0,0,0};
+               no_scsi();
+               no_xt();
+
+               args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK;
+               if_printf_on_off(get_doorlock," setting drive doorlock to %ld", doorlock);
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(doorlock)");
+       }
+       if (set_dkeep)
+       {
+               /* lock/unlock the drive's "feature" settings */
+               unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
+               no_scsi();
+               no_xt();
+
+               if_printf_on_off(get_dkeep," setting drive keep features to %ld", dkeep);
+               args[2] = dkeep ? 0x66 : 0xcc;
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(keepsettings)");
+       }
+       if (set_defects)
+       {
+               unsigned char args[4] = {WIN_SETFEATURES,0,0x04,0};
+               no_scsi();
+               args[2] = defects ? 0x04 : 0x84;
+               if_printf(get_defects," setting drive defect-mgmt to %ld\n", defects);
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(defectmgmt)");
+       }
+       if (set_prefetch)
+       {
+               unsigned char args[4] = {WIN_SETFEATURES,0,0xab,0};
+               no_scsi();
+               no_xt();
+
+               args[1] = prefetch;
+               if_printf(get_prefetch," setting drive prefetch to %ld\n", prefetch);
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setprefetch)");
+       }
+       if (set_xfermode)
+       {
+               unsigned char args[4] = {WIN_SETFEATURES,0,3,0};
+               no_scsi();
+               no_xt();
+
+               args[1] = xfermode_requested;
+               if (get_xfermode)
+               {
+                       printf(" setting xfermode to %d", xfermode_requested);
+                       interpret_xfermode(xfermode_requested);
+               }
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(setxfermode)");
+       }
+       if (set_lookahead)
+       {
+               unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
+               no_scsi();
+               no_xt();
+
+               args[2] = lookahead ? 0xaa : 0x55;
+               if_printf_on_off(get_lookahead," setting drive read-lookahead to %ld", lookahead);
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setreadahead)");
+       }
+       if (set_apmmode)
+       {
+               unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
+               no_scsi();
+               apmmode=check_if_min_and_set_val(apmmode,1);
+               apmmode=check_if_maj_and_set_val(apmmode,255);
+               if_printf(get_apmmode," setting APM level to");
+               if (apmmode==255)
+               {
+                       /* disable Advanced Power Management */
+                       args[2] = 0x85; /* feature register */
+                       if_printf(get_apmmode," disabled\n");
+               }
+               else
+               {
+                       /* set Advanced Power Management mode */
+                       args[2] = 0x05; /* feature register */
+                       args[1] = apmmode; /* sector count register */
+                       if_printf(get_apmmode," 0x%02lX (%ld)\n",apmmode,apmmode);
+               }
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD");
+       }
+       if (set_wcache)
+       {
+#ifdef DO_FLUSHCACHE
+#ifndef WIN_FLUSHCACHE
+#define WIN_FLUSHCACHE 0xe7
+#endif
+               unsigned char flushcache[4] = {WIN_FLUSHCACHE,0,0,0};
+#endif /* DO_FLUSHCACHE */
+               unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
+               no_scsi();
+               no_xt();
+               args[2] = wcache ? 0x02 : 0x82;
+               if_printf_on_off(get_wcache," setting drive write-caching to %ld", wcache);
+#ifdef DO_FLUSHCACHE
+               if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache))
+                       bb_perror_msg ("HDIO_DRIVE_CMD(flushcache)");
+#endif /* DO_FLUSHCACHE */
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setcache)");
+#ifdef DO_FLUSHCACHE
+               if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache))
+                       bb_perror_msg ("HDIO_DRIVE_CMD(flushcache)");
+#endif /* DO_FLUSHCACHE */
+       }
+       if (set_standbynow)
+       {
+#ifndef WIN_STANDBYNOW1
+#define WIN_STANDBYNOW1 0xE0
+#endif
+#ifndef WIN_STANDBYNOW2
+#define WIN_STANDBYNOW2 0x94
+#endif
+               unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0};
+               unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0};
+               no_scsi();
+               if_printf(get_standbynow," issuing standby command\n");
+               if (ioctl(fd, HDIO_DRIVE_CMD, &args1)
+                && ioctl(fd, HDIO_DRIVE_CMD, &args2))
+                       bb_perror_msg("HDIO_DRIVE_CMD(standby)");
+       }
+       if (set_sleepnow)
+       {
+#ifndef WIN_SLEEPNOW1
+#define WIN_SLEEPNOW1 0xE6
+#endif
+#ifndef WIN_SLEEPNOW2
+#define WIN_SLEEPNOW2 0x99
+#endif
+               unsigned char args1[4] = {WIN_SLEEPNOW1,0,0,0};
+               unsigned char args2[4] = {WIN_SLEEPNOW2,0,0,0};
+               no_scsi();
+               if_printf(get_sleepnow," issuing sleep command\n");
+               if (ioctl(fd, HDIO_DRIVE_CMD, &args1)
+                && ioctl(fd, HDIO_DRIVE_CMD, &args2))
+                       bb_perror_msg("HDIO_DRIVE_CMD(sleep)");
+       }
+       if (set_seagate)
+       {
+               unsigned char args[4] = {0xfb,0,0,0};
+               no_scsi();
+               no_xt();
+               if_printf(get_seagate," disabling Seagate auto powersaving mode\n");
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(seagatepwrsave)");
+       }
+       if (set_standby)
+       {
+               unsigned char args[4] = {WIN_SETIDLE1,standby_requested,0,0};
+               no_scsi();
+               no_xt();
+               if (get_standby)
+               {
+                       printf(" setting standby to %lu", standby_requested);
+                       interpret_standby(standby_requested);
+               }
+               bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setidle1)");
+       }
+#else  /* HDIO_DRIVE_CMD */
+       if (force_operation)
+       {
+               char buf[512];
+               flush_buffer_cache(fd);
+               if (-1 == read(fd, buf, sizeof(buf)))
+                       bb_error_msg("access failed");
+       }
+#endif /* HDIO_DRIVE_CMD */
+
+       if (!flagcount)
+               verbose = 1;
+
+       if ((verbose && !is_scsi_hd && !is_xt_hd) || get_mult || get_identity)
+       {
+               no_scsi();
+               multcount = -1;
+               if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount))
+               {
+                       if ((verbose && !is_xt_hd) || get_mult)
+                               bb_perror_msg("HDIO_GET_MULTCOUNT");
+               }
+               else if (verbose | get_mult)
+               {
+                       printf(" multcount    = %2ld", multcount);
+                       on_off(multcount);
+               }
+       }
+       if ((verbose && !is_scsi_hd && !is_xt_hd) || get_io32bit)
+       {
+               no_scsi();
+               no_xt();
+               if(ioctl(fd, HDIO_GET_32BIT, &parm))
+                       bb_perror_msg("HDIO_GET_32BIT");
+               else
+               {
+                       printf(" IO_support   =%3ld (", parm);
+                       switch (parm)
+                       {
+                               case 0:
+                                       printf("default ");
+                               case 2:
+                                       printf("16-bit)\n");
+                                       break;
+                               case 1:
+                                       printf("32-bit)\n");
+                                       break;
+                               case 3:
+                                       printf("32-bit w/sync)\n");
+                                       break;
+                               case 8:
+                                       printf("Request-Queue-Bypass)\n");
+                                       break;
+                               default:
+                                       printf("\?\?\?)\n");
+                               /*esac*/
+                       }
+               }
+       }
+       if ((verbose && !is_scsi_hd && !is_xt_hd) || get_unmask)
+       {
+               no_scsi();
+               no_xt();
+               bb_ioctl_on_off(fd, HDIO_GET_UNMASKINTR,(unsigned long *)parm,
+                                               "HDIO_GET_UNMASKINTR"," unmaskirq    = %2ld");
+       }
+
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
+       if ((verbose && !is_scsi_hd) || get_dma) {
+               no_scsi();
+               if(ioctl(fd, HDIO_GET_DMA, &parm))
+                       bb_perror_msg("HDIO_GET_DMA");
+               else
+               {
+                       printf(" using_dma    = %2ld", parm);
+                       if (parm == 8)
+                               printf(" (DMA-Assisted-PIO)\n");
+                       else
+                               on_off(parm);
+               }
+       }
+#endif
+       if (get_dma_q)
+       {
+               no_scsi();
+               bb_ioctl_on_off (fd, HDIO_GET_QDMA,(unsigned long *)parm,
+                                                 "HDIO_GET_QDMA"," queue_depth  = %2ld");
+       }
+       if ((verbose && !is_scsi_hd && !is_xt_hd) || get_keep)
+       {
+               no_scsi();
+               no_xt();
+               bb_ioctl_on_off (fd, HDIO_GET_KEEPSETTINGS,(unsigned long *)parm,
+                                                       "HDIO_GET_KEEPSETTINGS"," keepsettings = %2ld");
+       }
+
+       if (get_nowerr)
+       {
+               no_scsi();
+               no_xt();
+               bb_ioctl_on_off  (fd, HDIO_GET_NOWERR,(unsigned long *)&parm,
+                                                       " HDIO_GET_NOWERR"," nowerr       = %2ld");
+       }
+       if (verbose || get_readonly)
+       {
+               bb_ioctl_on_off(fd, BLKROGET,(unsigned long *)parm,
+                                                 " BLKROGET"," readonly     = %2ld");
+       }
+       if ((verbose && !is_scsi_hd) || get_readahead)
+       {
+               bb_ioctl_on_off (fd, BLKRAGET, (unsigned long *) parm,
+                                                       " BLKRAGET"," readahead    = %2ld");
+       }
+       if (verbose || get_geom)
+       {
+               static const char msg[] = " geometry     = %u/%u/%u, sectors = %ld, start = %ld\n";
+               static struct hd_geometry g;
+#ifdef HDIO_GETGEO_BIG
+               static struct hd_big_geometry bg;
+#endif
+
+               if (ioctl(fd, BLKGETSIZE, &parm))
+                       bb_perror_msg("BLKGETSIZE");
+#ifdef HDIO_GETGEO_BIG
+               else if (!ioctl(fd, HDIO_GETGEO_BIG, &bg))
+                       printf(msg, bg.cylinders, bg.heads, bg.sectors, parm, bg.start);
+#endif
+               else if (ioctl(fd, HDIO_GETGEO, &g))
+                       bb_perror_msg("HDIO_GETGEO");
+               else
+                       printf(msg, g.cylinders, g.heads, g.sectors, parm, g.start);
+       }
+#ifdef HDIO_DRIVE_CMD
+       if (get_powermode)
+       {
+#ifndef WIN_CHECKPOWERMODE1
+#define WIN_CHECKPOWERMODE1 0xE5
+#endif
+#ifndef WIN_CHECKPOWERMODE2
+#define WIN_CHECKPOWERMODE2 0x98
+#endif
+               unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
+               const char *state;
+               no_scsi();
+               if (ioctl(fd, HDIO_DRIVE_CMD, &args)
+                        && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
+                       && ioctl(fd, HDIO_DRIVE_CMD, &args))
+               {
+                       if (errno != EIO || args[0] != 0 || args[1] != 0)
+                               state = "unknown";
+                       else
+                               state = "sleeping";
+               }
+               else
+                       state = (args[2] == 255) ? "active/idle" : "standby";
+
+               printf(" drive state is:  %s\n", state);
+       }
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
+       if (perform_reset)
+       {
+               no_scsi();
+               no_xt();
+               bb_ioctl(fd, HDIO_DRIVE_RESET, NULL, "HDIO_DRIVE_RESET");
+       }
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+       if (perform_tristate)
+       {
+               unsigned char args[4] = {0,tristate,0,0};
+               no_scsi();
+               no_xt();
+               bb_ioctl(fd, HDIO_TRISTATE_HWIF, &args, "HDIO_TRISTATE_HWIF");
+       }
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+       if (get_identity)
+       {
+               static struct hd_driveid id;
+
+               no_scsi();
+               no_xt();
+
+               if (!ioctl(fd, HDIO_GET_IDENTITY, &id))
+               {
+                       if (multcount != -1)
+                       {
+                               id.multsect = multcount;
+                               id.multsect_valid |= 1;
+                       }
+                       else
+                               id.multsect_valid &= ~1;
+                       dump_identity(&id);
+               }
+               else if (errno == -ENOMSG)
+                       printf(" no identification info available\n");
+               else
+                       bb_perror_msg("HDIO_GET_IDENTITY");
+       }
+
+       if (get_IDentity)
+       {
+               unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,};
+               unsigned i;
+
+               no_scsi();
+               no_xt();
+
+               if (ioctl(fd, HDIO_DRIVE_CMD, &args))
+               {
+                       args[0] = WIN_PIDENTIFY;
+                       if (ioctl(fd, HDIO_DRIVE_CMD, &args))
+                       {
+                               bb_perror_msg("HDIO_DRIVE_CMD(identify)");
+                               goto identify_abort;
+                       }
+               }
+               for(i=0; i<(sizeof args)/2; i+=2)
+                   __le16_to_cpus((uint16_t *)(&args[i]));
+
+               identify((void *)&args[4], NULL);
+identify_abort:
+       /* VOID */;
+       }
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+       if (set_busstate)
+       {
+               no_scsi();
+               if (get_busstate)
+               {
+                       printf(" setting bus state to %d", busstate);
+                       bus_state_value(busstate);
+               }
+               bb_ioctl(fd, HDIO_SET_BUSSTATE, (int *)busstate, "HDIO_SET_BUSSTATE");
+       }
+#endif
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+       if (get_busstate)
+       {
+               no_scsi();
+               if (ioctl(fd, HDIO_GET_BUSSTATE, &parm))
+                       bb_perror_msg("HDIO_GET_BUSSTATE");
+               else
+               {
+                       printf(" busstate     = %2ld", parm);
+                       bus_state_value(parm);
+               }
+       }
+#endif
+       if (reread_partn)
+               bb_ioctl(fd, BLKRRPART, NULL, "BLKRRPART");
+
+
+       if (do_ctimings)
+               do_time(0,fd);          /*time cache  */
+       if (do_timings)
+               do_time(1,fd);          /*time device */
+       if (do_flush)
+               flush_buffer_cache (fd);
+       close (fd);
+}
+
+static char * GET_NUMBER(char *p, unsigned long *flag, unsigned long *num)
+{
+       *num = 0;
+       while (isdigit(*p)) {
+               *flag = 1;
+               *num = (*num * 10) + (*p++ - '0');
+       }
+       return p;
+}
+
+static char * GET_STRING(char *p, unsigned long *flag, int *num)
+{
+       char *tmpstr;
+       char name[32];
+       tmpstr = name;
+       tmpstr[0] = '\0';
+       while (isalnum(*p) && (tmpstr - name) < 31) {
+               tmpstr[0] = *p++;
+               tmpstr[1] = '\0';
+               ++tmpstr;
+       }
+       *num = translate_xfermode(name);
+       if (*num == -1)
+               *flag = 0;
+       else
+               *flag = 1;
+       return p;
+}
+
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+static int fromhex (unsigned char c)
+{
+       if (c >= 'a' && c <= 'f')
+               return 10 + (c - 'a');
+       if (c >= '0' && c <= '9')
+               return (c - '0');
+       bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c);
+}
+
+static int identify_from_stdin (void)
+{
+       unsigned short sbuf[800];
+       unsigned char  buf[1600], *b = (unsigned char *)buf;
+       int i, count = read(0, buf, 1280);
+
+       if (count != 1280)
+               bb_error_msg_and_die("read(1280 bytes) failed (rc=%d)", count);
+       for (i = 0; count >= 4; ++i)
+       {
+               sbuf[i] = (fromhex(b[0]) << 12) | (fromhex(b[1]) << 8) | (fromhex(b[2]) << 4) | fromhex(b[3]);
+               __le16_to_cpus((uint16_t *)(&sbuf[i]));
+               b += 5;
+               count -= 5;
+       }
+       identify(sbuf, NULL);
+       return 0;
+}
+#endif
+
+static void missing_arg(int arg, char c, char* add)
+{
+       if (!arg)
+               bb_error_msg("-%c: missing value %s", c, (add!=NULL)? add :"");
+}
+
+/* our main() routine: */
+int hdparm_main(int argc, char **argv)
+{
+       char c, *p;
+
+       ++argv;
+       if (!--argc)
+               bb_show_usage();
+
+       while (argc--)
+       {
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+               if (!strcmp("-Istdin", *argv))
+               {
+                       exit(identify_from_stdin());
+               }
+#endif
+               p = *argv++;
+               if (*p == '-')
+               {
+                       if (!*++p)
+                               bb_show_usage();
+                       while ((c = *p++))
+                       {
+                               ++flagcount;
+                               switch (c)
+                               {
+                                       case 'V':
+                                               /*bb_error_msg_and_die("%s", VERSION);*/
+                                               /* We have to return 0 here and not 1 */
+                                               printf("%s %s\n",bb_applet_name, VERSION);
+                                               exit(EXIT_SUCCESS);
+                                       case 'v':
+                                               verbose = 1;
+                                               break;
+#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+                                       case 'I':
+                                               get_IDentity = 1;
+                                               break;
+                                       case 'i':
+                                               get_identity = 1;
+                                               break;
+#endif
+                                       case 'g':
+                                               get_geom = 1;
+                                               break;
+                                       case 'f':
+                                               do_flush = 1;
+                                               break;
+                                       case 'q':
+                                               quiet = 1;
+                                               noisy = 0;
+                                               break;
+                                       case 'u':
+                                               get_unmask = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_unmask = set_flag(p,'1'))==1)
+                                                       unmask  = *p++ - '0';
+                                               break;
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
+                                       case 'd':
+                                               get_dma = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_dma = set_flag(p,'9'))==1)
+                                                       dma = *p++ - '0';
+                                               break;
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA */
+                                       case 'n':
+                                               get_nowerr = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_nowerr = set_flag(p,'1'))==1)
+                                                       nowerr  = *p++ - '0';
+                                               break;
+                                       case 'p':
+                                               noisy_piomode = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_STRING(p,&set_piomode,&piomode);
+                                               break;
+                                       case 'r':
+                                               get_readonly = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_readonly = set_flag(p,'1'))==1)
+                                                       readonly  = *p++ - '0';
+                                               break;
+                                       case 'm':
+                                               get_mult = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_mult,&mult);
+                                               break;
+                                       case 'c':
+                                               get_io32bit = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_io32bit,&io32bit);
+                                               break;
+#ifdef HDIO_DRIVE_CMD
+                                       case 'S':
+                                               get_standby = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_standby,&standby_requested);
+                                               missing_arg(set_standby, c, NULL);
+                                               break;
+
+                                       case 'D':
+                                               get_defects = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_defects,&defects);
+                                               missing_arg(set_defects, c, NULL);
+                                               break;
+                                       case 'P':
+                                               get_prefetch = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_prefetch,&prefetch);
+                                               missing_arg(set_prefetch, c, NULL);
+                                               break;
+
+                                       case 'X':
+                                               get_xfermode = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_STRING(p,&set_xfermode,&xfermode_requested);
+                                               missing_arg(set_xfermode, c, NULL);
+                                               break;
+
+                                       case 'K':
+                                               get_dkeep = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_dkeep = set_flag(p,'1'))==1)
+                                                       dkeep  = *p++ - '0';
+                                               else
+                                                       goto missing_arg_error;
+                                               break;
+
+                                       case 'A':
+                                               get_lookahead = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_lookahead = set_flag(p,'1'))==1)
+                                                       lookahead  = *p++ - '0';
+                                               else
+                                                       goto missing_arg_error;
+                                               break;
+
+                                       case 'L':
+                                               get_doorlock = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_doorlock = set_flag(p,'1'))==1)
+                                                       doorlock  = *p++ - '0';
+                                               else
+                                                       goto missing_arg_error;
+                                               break;
+
+                                       case 'W':
+                                               get_wcache = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_wcache = set_flag(p,'1'))==1)
+                                                       wcache  = *p++ - '0';
+                                               else
+missing_arg_error:
+                                                       missing_arg(1, c, "(0/1)");
+                                               break;
+
+                                       case 'C':
+                                               get_powermode = noisy;
+                                               noisy = 1;
+                                               break;
+
+                                       case 'y':
+                                               get_standbynow = noisy;
+                                               noisy = 1;
+                                               set_standbynow = 1;
+                                               break;
+
+                                       case 'Y':
+                                               get_sleepnow = noisy;
+                                               noisy = 1;
+                                               set_sleepnow = 1;
+                                               break;
+
+                                       case 'z':
+                                               reread_partn = 1;
+                                               break;
+
+                                       case 'Z':
+                                               get_seagate = noisy;
+                                               noisy = 1;
+                                               set_seagate = 1;
+                                               break;
+#endif /* HDIO_DRIVE_CMD */
+                                       case 'k':
+                                               get_keep = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_keep = set_flag(p,'1'))==1)
+                                                       keep  = *p++ - '0';
+                                               break;
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+                                       case 'U':
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if(! p)
+                                                       goto expected_hwif_error; /* "expected hwif_nr" */
+
+                                               sscanf(p++, "%i", &hwif);
+
+                                               unregister_hwif = 1;
+                                               break;
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF */
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
+                                       case 'R':
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if(! p)
+                                                       goto expected_hwif_error; /* "expected hwif_data" */
+
+                                               sscanf(p++, "%i", &hwif_data);
+
+                                               if (argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               else
+                                                       goto expected_hwif_error; /* "expected hwif_ctrl" */
+
+                                               sscanf(p, "%i", &hwif_ctrl);
+
+                                               if (argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               else
+expected_hwif_error:
+                                                       bb_error_msg_and_die("expected hwif value"); /* "expected hwif_irq" */
+
+                                               sscanf(p, "%i", &hwif_irq);
+
+                                               *p = '\0';
+
+                                               scan_hwif = 1;
+                                               break;
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF */
+                                       case 'Q':
+#ifdef HDIO_GET_QDMA
+                                               get_dma_q = noisy;
+                                               noisy = 1;
+#ifdef HDIO_SET_QDMA
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_dma_q,&dma_q);
+#ifdef HDIO_GET_QDMA
+                                               dma_q = -dma_q;
+#endif
+#endif
+#endif
+                                               break;
+
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
+                                       case 'w':
+                                               perform_reset = 1;
+                                               break;
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+                                       case 'x':
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((perform_tristate = set_flag(p,'1'))==1)
+                                                       tristate  = *p++ - '0';
+                                               else
+                                                       missing_arg(1, c, "(0/1)");
+                                               break;
+
+#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+                                       case 'a':
+                                               get_readahead = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_readahead,&Xreadahead);
+                                               break;
+                                       case 'B':
+                                               get_apmmode = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isalnum(**argv))
+                                                       p = *argv++, --argc;
+                                               p=GET_NUMBER(p,&set_apmmode,&apmmode);
+                                               missing_arg(set_apmmode, c, "(1-255)");
+                                               break;
+                                       case 't':
+                                               do_timings = 1;
+                                               do_flush = 1;
+                                               break;
+                                       case 'T':
+                                               do_ctimings = 1;
+                                               do_flush = 1;
+                                               break;
+#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+                                       case 'b':
+                                               get_busstate = noisy;
+                                               noisy = 1;
+                                               if (!*p && argc && isdigit(**argv))
+                                                       p = *argv++, --argc;
+                                               if((set_busstate = set_flag(p,'2'))==1)
+                                                       busstate  = *p++ - '0';
+                                               break;
+#endif
+                                       case 'h':
+                                       default:
+                                               bb_show_usage();
+                               }
+                       }
+                       if (!argc)
+                               bb_show_usage();
+               } else {
+                       process_dev (p);
+               }
+       }
+       return 0 ;
+}
diff --git a/busybox/miscutils/last.c b/busybox/miscutils/last.c
new file mode 100644 (file)
index 0000000..86613bf
--- /dev/null
@@ -0,0 +1,107 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * last implementation for busybox
+ *
+ * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include "busybox.h"
+
+#ifndef SHUTDOWN_TIME
+#  define SHUTDOWN_TIME 254
+#endif
+
+/* Grr... utmp char[] members  do not have to be nul-terminated.
+ * Do what we can while still keeping this reasonably small.
+ * Note: We are assuming the ut_id[] size is fixed at 4. */
+
+#if (UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)
+#error struct utmp member char[] size(s) have changed!
+#endif
+
+extern int last_main(int argc, char **argv)
+{
+       struct utmp ut;
+       int n, file = STDIN_FILENO;
+
+       if (argc > 1) {
+               bb_show_usage();
+       }
+       file = bb_xopen(_PATH_WTMP, O_RDONLY);
+
+       printf("%-10s %-14s %-18s %-12.12s %s\n", "USER", "TTY", "HOST", "LOGIN", "TIME");
+       while ((n = safe_read(file, (void*)&ut, sizeof(struct utmp))) != 0) {
+
+               if (n != sizeof(struct utmp)) {
+                       bb_perror_msg_and_die("short read");
+               }
+
+               if (strncmp(ut.ut_line, "~", 1) == 0) {
+                       if (strncmp(ut.ut_user, "shutdown", 8) == 0)
+                               ut.ut_type = SHUTDOWN_TIME;
+                       else if (strncmp(ut.ut_user, "reboot", 6) == 0)
+                               ut.ut_type = BOOT_TIME;
+                       else if (strncmp(ut.ut_user, "runlevel", 7) == 0)
+                               ut.ut_type = RUN_LVL;
+               } else {
+                       if (!ut.ut_name[0] || strcmp(ut.ut_name, "LOGIN") == 0 ||
+                                       ut.ut_name[0] == 0)
+                       {
+                               /* Don't bother.  This means we can't find how long
+                                * someone was logged in for.  Oh well. */
+                               continue;
+                       }
+                       if (ut.ut_type != DEAD_PROCESS &&
+                                       ut.ut_name[0] && ut.ut_line[0])
+                       {
+                               ut.ut_type = USER_PROCESS;
+                       }
+                       if (strcmp(ut.ut_name, "date") == 0) {
+                               if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
+                               if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
+                       }
+               }
+
+               if (ut.ut_type!=USER_PROCESS) {
+                       switch (ut.ut_type) {
+                               case OLD_TIME:
+                               case NEW_TIME:
+                               case RUN_LVL:
+                               case SHUTDOWN_TIME:
+                                       continue;
+                               case BOOT_TIME:
+                                       strcpy(ut.ut_line, "system boot");
+                                       break;
+                       }
+               }
+
+               printf("%-10s %-14s %-18s %-12.12s\n", ut.ut_user, ut.ut_line, ut.ut_host,
+                               ctime(&(ut.ut_tv.tv_sec)) + 4);
+       }
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox/miscutils/makedevs.c b/busybox/miscutils/makedevs.c
new file mode 100644 (file)
index 0000000..54a2e00
--- /dev/null
@@ -0,0 +1,93 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
+ *
+ * makedevs
+ * Make ranges of device files quickly.
+ * known bugs: can't deal with alpha ranges
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>     /* major() and minor() */
+#include "busybox.h"
+
+int makedevs_main(int argc, char **argv)
+{
+       mode_t mode;
+       char *basedev, *type, *nodname, buf[255];
+       int Smajor, Sminor, S, E;
+
+       if (argc < 7 || *argv[1]=='-')
+               bb_show_usage();
+
+       basedev = argv[1];
+       type = argv[2];
+       Smajor = atoi(argv[3]);
+       Sminor = atoi(argv[4]);
+       S = atoi(argv[5]);
+       E = atoi(argv[6]);
+       nodname = argc == 8 ? basedev : buf;
+
+       mode = 0660;
+
+       switch (type[0]) {
+       case 'c':
+               mode |= S_IFCHR;
+               break;
+       case 'b':
+               mode |= S_IFBLK;
+               break;
+       case 'f':
+               mode |= S_IFIFO;
+               break;
+       default:
+               bb_show_usage();
+       }
+
+       while (S <= E) {
+               int sz;
+
+               sz = snprintf(buf, sizeof(buf), "%s%d", basedev, S);
+               if(sz<0 || sz>=sizeof(buf))  /* libc different */
+                       bb_error_msg_and_die("%s too large", basedev);
+
+       /* if mode != S_IFCHR and != S_IFBLK third param in mknod() ignored */
+
+               if (mknod(nodname, mode, makedev(Smajor, Sminor)))
+                       bb_error_msg("Failed to create: %s", nodname);
+
+               if (nodname == basedev) /* ex. /dev/hda - to /dev/hda1 ... */
+                       nodname = buf;
+               S++;
+               Sminor++;
+       }
+
+       return 0;
+}
+
+/*
+And this is what this program replaces. The shell is too slow!
+
+makedev () {
+local basedev=$1; local S=$2; local E=$3
+local major=$4; local Sminor=$5; local type=$6
+local sbase=$7
+
+       if [ ! "$sbase" = "" ]; then
+               mknod "$basedev" $type $major $Sminor
+               S=`expr $S + 1`
+               Sminor=`expr $Sminor + 1`
+       fi
+
+       while [ $S -le $E ]; do
+               mknod "$basedev$S" $type $major $Sminor
+               S=`expr $S + 1`
+               Sminor=`expr $Sminor + 1`
+       done
+}
+*/
diff --git a/busybox/miscutils/mt.c b/busybox/miscutils/mt.c
new file mode 100644 (file)
index 0000000..b0cdacc
--- /dev/null
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mtio.h>
+#include <sys/fcntl.h>
+#include "busybox.h"
+
+struct mt_opcodes {
+       char *name;
+       short value;
+};
+
+/* missing: eod/seod, stoptions, stwrthreshold, densities */
+static const struct mt_opcodes opcodes[] = {
+       {"bsf", MTBSF},
+       {"bsfm", MTBSFM},
+       {"bsr", MTBSR},
+       {"bss", MTBSS},
+       {"datacompression", MTCOMPRESSION},
+       {"eom", MTEOM},
+       {"erase", MTERASE},
+       {"fsf", MTFSF},
+       {"fsfm", MTFSFM},
+       {"fsr", MTFSR},
+       {"fss", MTFSS},
+       {"load", MTLOAD},
+       {"lock", MTLOCK},
+       {"mkpart", MTMKPART},
+       {"nop", MTNOP},
+       {"offline", MTOFFL},
+       {"rewoffline", MTOFFL},
+       {"ras1", MTRAS1},
+       {"ras2", MTRAS2},
+       {"ras3", MTRAS3},
+       {"reset", MTRESET},
+       {"retension", MTRETEN},
+       {"rewind", MTREW},
+       {"seek", MTSEEK},
+       {"setblk", MTSETBLK},
+       {"setdensity", MTSETDENSITY},
+       {"drvbuffer", MTSETDRVBUFFER},
+       {"setpart", MTSETPART},
+       {"tell", MTTELL},
+       {"wset", MTWSM},
+       {"unload", MTUNLOAD},
+       {"unlock", MTUNLOCK},
+       {"eof", MTWEOF},
+       {"weof", MTWEOF},
+       {0, 0}
+};
+
+extern int mt_main(int argc, char **argv)
+{
+       const char *file = "/dev/tape";
+       const struct mt_opcodes *code = opcodes;
+       struct mtop op;
+       struct mtpos position;
+       int fd, mode;
+
+       if (argc < 2) {
+               bb_show_usage();
+       }
+
+       if (strcmp(argv[1], "-f") == 0) {
+               if (argc < 4) {
+                       bb_show_usage();
+               }
+               file = argv[2];
+               argv += 2;
+               argc -= 2;
+       }
+
+       while (code->name != 0) {
+               if (strcmp(code->name, argv[1]) == 0)
+                       break;
+               code++;
+       }
+
+       if (code->name == 0) {
+               bb_error_msg("unrecognized opcode %s.", argv[1]);
+               return EXIT_FAILURE;
+       }
+
+       op.mt_op = code->value;
+       if (argc >= 3)
+               op.mt_count = atoi(argv[2]);
+       else
+               op.mt_count = 1;                /* One, not zero, right? */
+
+       switch (code->value) {
+               case MTWEOF:
+               case MTERASE:
+               case MTWSM:
+               case MTSETDRVBUFFER:
+                       mode = O_WRONLY;
+                       break;
+
+               default:
+                       mode = O_RDONLY;
+                       break;
+       }
+
+       if ((fd = open(file, mode, 0)) < 0)
+               bb_perror_msg_and_die("%s", file);
+
+       switch (code->value) {
+               case MTTELL:
+                       if (ioctl(fd, MTIOCPOS, &position) < 0)
+                               bb_perror_msg_and_die("%s", file);
+                       printf ("At block %d.\n", (int) position.mt_blkno);
+                       break;
+
+               default:
+                       if (ioctl(fd, MTIOCTOP, &op) != 0)
+                               bb_perror_msg_and_die("%s", file);
+                       break;
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/miscutils/rx.c b/busybox/miscutils/rx.c
new file mode 100644 (file)
index 0000000..8edc887
--- /dev/null
@@ -0,0 +1,344 @@
+/*-------------------------------------------------------------------------
+ * Filename:      xmodem.c
+ * Version:       $Id: rx.c,v 1.2 2004/03/15 08:28:46 andersen Exp $
+ * Copyright:     Copyright (C) 2001, Hewlett-Packard Company
+ * Author:        Christopher Hoover <ch@hpl.hp.com>
+ * Description:   xmodem functionality for uploading of kernels
+ *                and the like
+ * Created at:    Thu Dec 20 01:58:08 PST 2001
+ *-----------------------------------------------------------------------*/
+/*
+ * xmodem.c: xmodem functionality for uploading of kernels and
+ *            the like
+ *
+ * Copyright (C) 2001 Hewlett-Packard Laboratories
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * This was originally written for blob and then adapted for busybox.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include "busybox.h"
+
+
+#define SOH 0x01
+#define STX 0x02
+#define EOT 0x04
+#define ACK 0x06
+#define NAK 0x15
+#define CAN 0x18
+#define BS  0x08
+
+/*
+
+Cf:
+
+  http://www.textfiles.com/apple/xmodem
+  http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
+  http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
+  http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
+
+*/
+
+#define TIMEOUT 1
+#define TIMEOUT_LONG 10
+#define MAXERRORS 10
+
+static inline void write_byte(int fd, char cc) {
+       write(fd, &cc, 1);
+}
+
+static inline void write_flush(int fd) {
+       tcdrain(fd);
+}
+
+static inline void read_flush(int fd) {
+       tcflush(fd, TCIFLUSH);
+}
+
+static int read_byte(int fd, unsigned int timeout) {
+       char buf[1];
+       int n;
+
+       alarm(timeout);
+
+       n = read(fd, &buf, 1);
+
+       alarm(0);
+
+       if (n == 1)
+               return buf[0] & 0xff;
+       else
+               return -1;
+}
+
+static int receive(char *error_buf, size_t error_buf_size,
+                                  int ttyfd, int filefd)
+{
+       char blockBuf[1024];
+       unsigned int errors = 0;
+       unsigned int wantBlockNo = 1;
+       unsigned int length = 0;
+       int docrc = 1;
+       char nak = 'C';
+       unsigned int timeout = TIMEOUT_LONG;
+
+#define note_error(fmt,args...) \
+       snprintf(error_buf, error_buf_size, fmt,##args)
+
+       read_flush(ttyfd);
+
+       /* Ask for CRC; if we get errors, we will go with checksum */
+       write_byte(ttyfd, nak);
+       write_flush(ttyfd);
+
+       for (;;) {
+               int blockBegin;
+               int blockNo, blockNoOnesCompl;
+               int blockLength;
+               int cksum = 0;
+               int crcHi = 0;
+               int crcLo = 0;
+
+               blockBegin = read_byte(ttyfd, timeout);
+               if (blockBegin < 0)
+                       goto timeout;
+
+               timeout = TIMEOUT;
+               nak = NAK;
+
+               switch (blockBegin) {
+               case SOH:
+               case STX:
+                       break;
+
+               case EOT:
+                       write_byte(ttyfd, ACK);
+                       write_flush(ttyfd);
+                       goto done;
+
+               default:
+                       goto error;
+               }
+
+               /* block no */
+               blockNo = read_byte(ttyfd, TIMEOUT);
+               if (blockNo < 0)
+                       goto timeout;
+
+               /* block no one's compliment */
+               blockNoOnesCompl = read_byte(ttyfd, TIMEOUT);
+               if (blockNoOnesCompl < 0)
+                       goto timeout;
+
+               if (blockNo != (255 - blockNoOnesCompl)) {
+                       note_error("bad block ones compl");
+                       goto error;
+               }
+
+               blockLength = (blockBegin == SOH) ? 128 : 1024;
+
+               {
+                       int i;
+
+                       for (i = 0; i < blockLength; i++) {
+                               int cc = read_byte(ttyfd, TIMEOUT);
+                               if (cc < 0)
+                                       goto timeout;
+                               blockBuf[i] = cc;
+                       }
+               }
+
+               if (docrc) {
+                       crcHi = read_byte(ttyfd, TIMEOUT);
+                       if (crcHi < 0)
+                               goto timeout;
+
+                       crcLo = read_byte(ttyfd, TIMEOUT);
+                       if (crcLo < 0)
+                               goto timeout;
+               } else {
+                       cksum = read_byte(ttyfd, TIMEOUT);
+                       if (cksum < 0)
+                               goto timeout;
+               }
+
+               if (blockNo == ((wantBlockNo - 1) & 0xff)) {
+                       /* a repeat of the last block is ok, just ignore it. */
+                       /* this also ignores the initial block 0 which is */
+                       /* meta data. */
+                       goto next;
+               } else if (blockNo != (wantBlockNo & 0xff)) {
+                       note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
+                       goto error;
+               }
+
+               if (docrc) {
+                       int crc = 0;
+                       int i, j;
+                       int expectedCrcHi;
+                       int expectedCrcLo;
+
+                       for (i = 0; i < blockLength; i++) {
+                               crc = crc ^ (int) blockBuf[i] << 8;
+                               for (j = 0; j < 8; j++)
+                                       if (crc & 0x8000)
+                                               crc = crc << 1 ^ 0x1021;
+                                       else
+                                               crc = crc << 1;
+                       }
+
+                       expectedCrcHi = (crc >> 8) & 0xff;
+                       expectedCrcLo = crc & 0xff;
+
+                       if ((crcHi != expectedCrcHi) ||
+                           (crcLo != expectedCrcLo)) {
+                               note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo);
+                               goto error;
+                       }
+               } else {
+                       unsigned char expectedCksum = 0;
+                       int i;
+
+                       for (i = 0; i < blockLength; i++)
+                               expectedCksum += blockBuf[i];
+
+                       if (cksum != expectedCksum) {
+                               note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum);
+                               goto error;
+                       }
+               }
+
+               wantBlockNo++;
+               length += blockLength;
+
+               if (bb_full_write(filefd, blockBuf, blockLength) < 0) {
+                       note_error("write to file failed: %m");
+                       goto fatal;
+               }
+
+       next:
+               errors = 0;
+               write_byte(ttyfd, ACK);
+               write_flush(ttyfd);
+               continue;
+
+       error:
+       timeout:
+               errors++;
+               if (errors == MAXERRORS) {
+                       /* Abort */
+                       int i;
+
+                       // if using crc, try again w/o crc
+                       if (nak == 'C') {
+                               nak = NAK;
+                               errors = 0;
+                               docrc = 0;
+                               goto timeout;
+                       }
+
+                       note_error("too many errors; giving up");
+
+               fatal:
+                       for (i = 0; i < 5; i ++)
+                               write_byte(ttyfd, CAN);
+                       for (i = 0; i < 5; i ++)
+                               write_byte(ttyfd, BS);
+                       write_flush(ttyfd);
+                       return -1;
+               }
+
+               read_flush(ttyfd);
+               write_byte(ttyfd, nak);
+               write_flush(ttyfd);
+       }
+
+ done:
+       return length;
+
+#undef note_error
+}
+
+static void sigalrm_handler(int signum)
+{
+}
+
+int rx_main(int argc, char **argv)
+{
+       char *fn;
+       int ttyfd, filefd;
+       struct termios tty, orig_tty;
+       struct sigaction act;
+       int n;
+       char error_buf[256];
+
+       if (argc != 2)
+                       bb_show_usage();
+
+       fn = argv[1];
+       ttyfd = open("/dev/tty", O_RDWR);
+       if (ttyfd < 0)
+                       bb_error_msg_and_die("%s: open on /dev/tty failed: %m\n", argv[0]);
+
+       filefd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0666);
+       if (filefd < 0)
+                       bb_error_msg_and_die("%s: open on %s failed: %m\n", argv[0], fn);
+
+       if (tcgetattr(ttyfd, &tty) < 0)
+                       bb_error_msg_and_die("%s: tcgetattr failed: %m\n", argv[0]);
+
+       orig_tty = tty;
+
+       cfmakeraw(&tty);
+       tcsetattr(ttyfd, TCSAFLUSH, &tty);
+
+       memset(&act, 0, sizeof(act));
+       act.sa_handler = sigalrm_handler;
+       sigaction(SIGALRM, &act, 0);
+
+       n = receive(error_buf, sizeof(error_buf), ttyfd, filefd);
+
+       close(filefd);
+
+       tcsetattr(ttyfd, TCSAFLUSH, &orig_tty);
+
+       if (n < 0)
+                       bb_error_msg_and_die("\n%s: receive failed:\n  %s\n",
+                                                          argv[0], error_buf);
+
+       bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/miscutils/strings.c b/busybox/miscutils/strings.c
new file mode 100644 (file)
index 0000000..92e9f0d
--- /dev/null
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * strings implementation for busybox
+ *
+ * Copyright (c) 1980, 1987
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
+ * Badly hacked by Tito Ragusa <farmatito@tiscali.it>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "busybox.h"
+
+#define ISSTR(ch)      (isprint(ch) || ch == '\t')
+
+int strings_main(int argc, char **argv)
+{
+       int n=4, c, i, opt=0, status=EXIT_SUCCESS;
+       long t=0, count;
+       FILE *file = stdin;
+       char *string=NULL;
+       const char *fmt="%s: ";
+
+       while ((i = getopt(argc, argv, "afon:")) > 0)
+               switch(i)
+               {
+                       case 'a':
+                               break;
+                       case 'f':
+                               opt+=1;
+                               break;
+                       case 'o':
+                               opt+=2;
+                               break;
+                       case 'n':
+                               n = bb_xgetlarg(optarg, 10, 1, INT_MAX);
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       i=0;
+
+       string=xmalloc(n+1);
+       string[n]='\0';
+       n-=1;
+
+       if(argc==0)
+       {
+               fmt="{%s}: ";
+               *argv=(char *)bb_msg_standard_input;
+               goto pipe;
+       }
+
+       for( ;*argv!=NULL && argc>0;argv++)
+       {
+               if((file=bb_wfopen(*argv,"r")))
+               {
+pipe:
+
+                       count=0;
+                       do{
+                               c=fgetc(file);
+                               if(ISSTR(c))
+                               {
+                                       if(i==0)
+                                               t=count;
+                                       if(i<=n)
+                                               string[i]=c;
+                                       if(i==n)
+                                       {
+                                               if(opt == 1 || opt == 3 )
+                                                       printf(fmt,*argv);
+                                               if(opt >= 2 )
+                                                       printf("%7lo ", t);
+                                               printf("%s", string);
+                                       }
+                                       if(i>n)
+                                               putchar(c);
+                                       i++;
+                               }
+                               else
+                               {
+                                       if(i>n)
+                                               putchar('\n');
+                                       i=0;
+                               }
+                               count++;
+                       }while(c!=EOF);
+
+                       bb_fclose_nonstdin(file);
+               }
+               else
+                       status=EXIT_FAILURE;
+       }
+       /*free(string);*/
+       exit(status);
+}
+
+/*
+ * Copyright (c) 1980, 1987
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/miscutils/time.c b/busybox/miscutils/time.c
new file mode 100644 (file)
index 0000000..ca896a1
--- /dev/null
@@ -0,0 +1,502 @@
+/* `time' utility to display resource usage of processes.
+   Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+/* Originally written by David Keppel <pardo@cs.washington.edu>.
+   Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
+   Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
+   */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>         /* For pid_t. */
+#include <sys/wait.h>
+#include <sys/param.h>         /* For getpagesize, maybe.  */
+
+#define TV_MSEC tv_usec / 1000
+#include <sys/resource.h>
+#include "busybox.h"
+
+/* Information on the resources used by a child process.  */
+typedef struct
+{
+  int waitstatus;
+  struct rusage ru;
+  struct timeval start, elapsed; /* Wallclock time of process.  */
+} resource_t;
+
+/* msec = milliseconds = 1/1,000 (1*10e-3) second.
+   usec = microseconds = 1/1,000,000 (1*10e-6) second.  */
+
+#ifndef TICKS_PER_SEC
+#define TICKS_PER_SEC 100
+#endif
+
+/* The number of milliseconds in one `tick' used by the `rusage' structure.  */
+#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
+
+/* Return the number of clock ticks that occur in M milliseconds.  */
+#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
+
+#define UL unsigned long
+
+static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
+
+/* The output format for the -p option .*/
+static const char *const posix_format = "real %e\nuser %U\nsys %S";
+
+
+/* Format string for printing all statistics verbosely.
+   Keep this output to 24 lines so users on terminals can see it all.*/
+static const char *const long_format =
+  "\tCommand being timed: \"%C\"\n"
+  "\tUser time (seconds): %U\n"
+  "\tSystem time (seconds): %S\n"
+  "\tPercent of CPU this job got: %P\n"
+  "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
+  "\tAverage shared text size (kbytes): %X\n"
+  "\tAverage unshared data size (kbytes): %D\n"
+  "\tAverage stack size (kbytes): %p\n"
+  "\tAverage total size (kbytes): %K\n"
+  "\tMaximum resident set size (kbytes): %M\n"
+  "\tAverage resident set size (kbytes): %t\n"
+  "\tMajor (requiring I/O) page faults: %F\n"
+  "\tMinor (reclaiming a frame) page faults: %R\n"
+  "\tVoluntary context switches: %w\n"
+  "\tInvoluntary context switches: %c\n"
+  "\tSwaps: %W\n"
+  "\tFile system inputs: %I\n"
+  "\tFile system outputs: %O\n"
+  "\tSocket messages sent: %s\n"
+  "\tSocket messages received: %r\n"
+  "\tSignals delivered: %k\n"
+  "\tPage size (bytes): %Z\n"
+  "\tExit status: %x";
+
+
+  /* Wait for and fill in data on child process PID.
+   Return 0 on error, 1 if ok.  */
+
+/* pid_t is short on BSDI, so don't try to promote it.  */
+static int resuse_end (pid_t pid, resource_t *resp)
+{
+    int status;
+
+    pid_t caught;
+
+    /* Ignore signals, but don't ignore the children.  When wait3
+       returns the child process, set the time the command finished. */
+    while ((caught = wait3 (&status, 0, &resp->ru)) != pid)
+    {
+       if (caught == -1)
+           return 0;
+    }
+
+    gettimeofday (&resp->elapsed, (struct timezone *) 0);
+    resp->elapsed.tv_sec -= resp->start.tv_sec;
+    if (resp->elapsed.tv_usec < resp->start.tv_usec)
+    {
+       /* Manually carry a one from the seconds field.  */
+       resp->elapsed.tv_usec += 1000000;
+       --resp->elapsed.tv_sec;
+    }
+    resp->elapsed.tv_usec -= resp->start.tv_usec;
+
+    resp->waitstatus = status;
+
+    return 1;
+}
+
+/* Print ARGV to FP, with each entry in ARGV separated by FILLER.  */
+static void fprintargv (FILE *fp, char *const *argv, const char *filler)
+{
+    char *const *av;
+
+    av = argv;
+    fputs (*av, fp);
+    while (*++av)
+    {
+       fputs (filler, fp);
+       fputs (*av, fp);
+    }
+    if (ferror (fp))
+       bb_error_msg_and_die("write error");
+}
+
+/* Return the number of kilobytes corresponding to a number of pages PAGES.
+   (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
+
+   Try to do arithmetic so that the risk of overflow errors is minimized.
+   This is funky since the pagesize could be less than 1K.
+   Note: Some machines express getrusage statistics in terms of K,
+   others in terms of pages.  */
+
+static unsigned long ptok (unsigned long pages)
+{
+    static unsigned long ps = 0;
+    unsigned long tmp;
+    static long size = LONG_MAX;
+
+    /* Initialization.  */
+    if (ps == 0)
+       ps = (long) getpagesize ();
+
+    /* Conversion.  */
+    if (pages > (LONG_MAX / ps))
+    {                          /* Could overflow.  */
+       tmp = pages / 1024;     /* Smaller first, */
+       size = tmp * ps;                /* then larger.  */
+    }
+    else
+    {                          /* Could underflow.  */
+       tmp = pages * ps;               /* Larger first, */
+       size = tmp / 1024;      /* then smaller.  */
+    }
+    return size;
+}
+
+/* summarize: Report on the system use of a command.
+
+   Copy the FMT argument to FP except that `%' sequences
+   have special meaning, and `\n' and `\t' are translated into
+   newline and tab, respectively, and `\\' is translated into `\'.
+
+   The character following a `%' can be:
+   (* means the tcsh time builtin also recognizes it)
+   % == a literal `%'
+   C == command name and arguments
+*  D == average unshared data size in K (ru_idrss+ru_isrss)
+*  E == elapsed real (wall clock) time in [hour:]min:sec
+*  F == major page faults (required physical I/O) (ru_majflt)
+*  I == file system inputs (ru_inblock)
+*  K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
+*  M == maximum resident set size in K (ru_maxrss)
+*  O == file system outputs (ru_oublock)
+*  P == percent of CPU this job got (total cpu time / elapsed time)
+*  R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
+*  S == system (kernel) time (seconds) (ru_stime)
+*  T == system time in [hour:]min:sec
+*  U == user time (seconds) (ru_utime)
+*  u == user time in [hour:]min:sec
+*  W == times swapped out (ru_nswap)
+*  X == average amount of shared text in K (ru_ixrss)
+   Z == page size
+*  c == involuntary context switches (ru_nivcsw)
+   e == elapsed real time in seconds
+*  k == signals delivered (ru_nsignals)
+   p == average unshared stack size in K (ru_isrss)
+*  r == socket messages received (ru_msgrcv)
+*  s == socket messages sent (ru_msgsnd)
+   t == average resident set size in K (ru_idrss)
+*  w == voluntary context switches (ru_nvcsw)
+   x == exit status of command
+
+   Various memory usages are found by converting from page-seconds
+   to kbytes by multiplying by the page size, dividing by 1024,
+   and dividing by elapsed real time.
+
+   FP is the stream to print to.
+   FMT is the format string, interpreted as described above.
+   COMMAND is the command and args that are being summarized.
+   RESP is resource information on the command.  */
+
+static void summarize (FILE *fp, const char *fmt, char **command, resource_t *resp)
+{
+    unsigned long r;           /* Elapsed real milliseconds.  */
+    unsigned long v;           /* Elapsed virtual (CPU) milliseconds.  */
+
+    if (WIFSTOPPED (resp->waitstatus))
+       fprintf (fp, "Command stopped by signal %d\n", WSTOPSIG (resp->waitstatus));
+    else if (WIFSIGNALED (resp->waitstatus))
+       fprintf (fp, "Command terminated by signal %d\n", WTERMSIG (resp->waitstatus));
+    else if (WIFEXITED (resp->waitstatus) && WEXITSTATUS (resp->waitstatus))
+       fprintf (fp, "Command exited with non-zero status %d\n", WEXITSTATUS (resp->waitstatus));
+
+    /* Convert all times to milliseconds.  Occasionally, one of these values
+       comes out as zero.  Dividing by zero causes problems, so we first
+       check the time value.  If it is zero, then we take `evasive action'
+       instead of calculating a value.  */
+
+    r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
+
+    v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
+       resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
+
+    while (*fmt)
+    {
+       switch (*fmt)
+       {
+           case '%':
+               switch (*++fmt)
+               {
+                   case '%':           /* Literal '%'.  */
+                       putc ('%', fp);
+                       break;
+                   case 'C':           /* The command that got timed.  */
+                       fprintargv (fp, command, " ");
+                       break;
+                   case 'D':           /* Average unshared data size.  */
+                       fprintf (fp, "%lu",
+                               MSEC_TO_TICKS (v) == 0 ? 0 :
+                               ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
+                               ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
+                       break;
+                   case 'E':           /* Elapsed real (wall clock) time.  */
+                       if (resp->elapsed.tv_sec >= 3600)       /* One hour -> h:m:s.  */
+                           fprintf (fp, "%ldh %ldm %02lds",
+                                   resp->elapsed.tv_sec / 3600,
+                                   (resp->elapsed.tv_sec % 3600) / 60,
+                                   resp->elapsed.tv_sec % 60);
+                       else
+                           fprintf (fp, "%ldm %ld.%02lds",     /* -> m:s.  */
+                                   resp->elapsed.tv_sec / 60,
+                                   resp->elapsed.tv_sec % 60,
+                                   resp->elapsed.tv_usec / 10000);
+                       break;
+                   case 'F':           /* Major page faults.  */
+                       fprintf (fp, "%ld", resp->ru.ru_majflt);
+                       break;
+                   case 'I':           /* Inputs.  */
+                       fprintf (fp, "%ld", resp->ru.ru_inblock);
+                       break;
+                   case 'K':           /* Average mem usage == data+stack+text.  */
+                       fprintf (fp, "%lu",
+                               MSEC_TO_TICKS (v) == 0 ? 0 :
+                               ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
+                               ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) +
+                               ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
+                       break;
+                   case 'M':           /* Maximum resident set size.  */
+                       fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss));
+                       break;
+                   case 'O':           /* Outputs.  */
+                       fprintf (fp, "%ld", resp->ru.ru_oublock);
+                       break;
+                   case 'P':           /* Percent of CPU this job got.  */
+                       /* % cpu is (total cpu time)/(elapsed time).  */
+                       if (r > 0)
+                           fprintf (fp, "%lu%%", (v * 100 / r));
+                       else
+                           fprintf (fp, "?%%");
+                       break;
+                   case 'R':           /* Minor page faults (reclaims).  */
+                       fprintf (fp, "%ld", resp->ru.ru_minflt);
+                       break;
+                   case 'S':           /* System time.  */
+                       fprintf (fp, "%ld.%02ld",
+                               resp->ru.ru_stime.tv_sec,
+                               resp->ru.ru_stime.TV_MSEC / 10);
+                       break;
+                   case 'T':           /* System time.  */
+                       if (resp->ru.ru_stime.tv_sec >= 3600)   /* One hour -> h:m:s.  */
+                           fprintf (fp, "%ldh %ldm %02lds",
+                                   resp->ru.ru_stime.tv_sec / 3600,
+                                   (resp->ru.ru_stime.tv_sec % 3600) / 60,
+                                   resp->ru.ru_stime.tv_sec % 60);
+                       else
+                           fprintf (fp, "%ldm %ld.%02lds",     /* -> m:s.  */
+                                   resp->ru.ru_stime.tv_sec / 60,
+                                   resp->ru.ru_stime.tv_sec % 60,
+                                   resp->ru.ru_stime.tv_usec / 10000);
+                       break;
+                   case 'U':           /* User time.  */
+                       fprintf (fp, "%ld.%02ld",
+                               resp->ru.ru_utime.tv_sec,
+                               resp->ru.ru_utime.TV_MSEC / 10);
+                       break;
+                   case 'u':           /* User time.  */
+                       if (resp->ru.ru_utime.tv_sec >= 3600)   /* One hour -> h:m:s.  */
+                           fprintf (fp, "%ldh %ldm %02lds",
+                                   resp->ru.ru_utime.tv_sec / 3600,
+                                   (resp->ru.ru_utime.tv_sec % 3600) / 60,
+                                   resp->ru.ru_utime.tv_sec % 60);
+                       else
+                           fprintf (fp, "%ldm %ld.%02lds",     /* -> m:s.  */
+                                   resp->ru.ru_utime.tv_sec / 60,
+                                   resp->ru.ru_utime.tv_sec % 60,
+                                   resp->ru.ru_utime.tv_usec / 10000);
+                       break;
+                   case 'W':           /* Times swapped out.  */
+                       fprintf (fp, "%ld", resp->ru.ru_nswap);
+                       break;
+                   case 'X':           /* Average shared text size.  */
+                       fprintf (fp, "%lu",
+                               MSEC_TO_TICKS (v) == 0 ? 0 :
+                               ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
+                       break;
+                   case 'Z':           /* Page size.  */
+                       fprintf (fp, "%d", getpagesize ());
+                       break;
+                   case 'c':           /* Involuntary context switches.  */
+                       fprintf (fp, "%ld", resp->ru.ru_nivcsw);
+                       break;
+                   case 'e':           /* Elapsed real time in seconds.  */
+                       fprintf (fp, "%ld.%02ld",
+                               resp->elapsed.tv_sec,
+                               resp->elapsed.tv_usec / 10000);
+                       break;
+                   case 'k':           /* Signals delivered.  */
+                       fprintf (fp, "%ld", resp->ru.ru_nsignals);
+                       break;
+                   case 'p':           /* Average stack segment.  */
+                       fprintf (fp, "%lu",
+                               MSEC_TO_TICKS (v) == 0 ? 0 :
+                               ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
+                       break;
+                   case 'r':           /* Incoming socket messages received.  */
+                       fprintf (fp, "%ld", resp->ru.ru_msgrcv);
+                       break;
+                   case 's':           /* Outgoing socket messages sent.  */
+                       fprintf (fp, "%ld", resp->ru.ru_msgsnd);
+                       break;
+                   case 't':           /* Average resident set size.  */
+                       fprintf (fp, "%lu",
+                               MSEC_TO_TICKS (v) == 0 ? 0 :
+                               ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v));
+                       break;
+                   case 'w':           /* Voluntary context switches.  */
+                       fprintf (fp, "%ld", resp->ru.ru_nvcsw);
+                       break;
+                   case 'x':           /* Exit status.  */
+                       fprintf (fp, "%d", WEXITSTATUS (resp->waitstatus));
+                       break;
+                   case '\0':
+                       putc ('?', fp);
+                       return;
+                   default:
+                       putc ('?', fp);
+                       putc (*fmt, fp);
+               }
+               ++fmt;
+               break;
+
+           case '\\':          /* Format escape.  */
+               switch (*++fmt)
+               {
+                   case 't':
+                       putc ('\t', fp);
+                       break;
+                   case 'n':
+                       putc ('\n', fp);
+                       break;
+                   case '\\':
+                       putc ('\\', fp);
+                       break;
+                   default:
+                       putc ('?', fp);
+                       putc ('\\', fp);
+                       putc (*fmt, fp);
+               }
+               ++fmt;
+               break;
+
+           default:
+               putc (*fmt++, fp);
+       }
+
+       if (ferror (fp))
+           bb_error_msg_and_die("write error");
+    }
+    putc ('\n', fp);
+
+    if (ferror (fp))
+       bb_error_msg_and_die("write error");
+}
+
+/* Run command CMD and return statistics on it.
+   Put the statistics in *RESP.  */
+static void run_command (char *const *cmd, resource_t *resp)
+{
+    pid_t pid;                 /* Pid of child.  */
+    __sighandler_t interrupt_signal, quit_signal;
+
+    gettimeofday (&resp->start, (struct timezone *) 0);
+    pid = fork ();             /* Run CMD as child process.  */
+    if (pid < 0)
+       bb_error_msg_and_die("cannot fork");
+    else if (pid == 0)
+    {                          /* If child.  */
+       /* Don't cast execvp arguments; that causes errors on some systems,
+          versus merely warnings if the cast is left off.  */
+       execvp (cmd[0], cmd);
+       bb_error_msg("cannot run %s", cmd[0]);
+       _exit (errno == ENOENT ? 127 : 126);
+    }
+
+    /* Have signals kill the child but not self (if possible).  */
+    interrupt_signal = signal (SIGINT, SIG_IGN);
+    quit_signal = signal (SIGQUIT, SIG_IGN);
+
+    if (resuse_end (pid, resp) == 0)
+       bb_error_msg("error waiting for child process");
+
+    /* Re-enable signals.  */
+    signal (SIGINT, interrupt_signal);
+    signal (SIGQUIT, quit_signal);
+}
+
+extern int time_main (int argc, char **argv)
+{
+    int gotone;
+    resource_t res;
+    const char *output_format = default_format;
+
+    argc--;
+    argv++;
+    /* Parse any options  -- don't use getopt() here so we don't
+     * consume the args of our client application... */
+    while (argc > 0 && **argv == '-') {
+       gotone = 0;
+       while (gotone==0 && *++(*argv)) {
+           switch (**argv) {
+               case 'v':
+                   output_format = long_format;
+                   break;
+               case 'p':
+                   output_format = posix_format;
+                   break;
+               default:
+                   bb_show_usage();
+           }
+           argc--;
+           argv++;
+           gotone = 1;
+       }
+    }
+
+    if (argv == NULL || *argv == NULL)
+       bb_show_usage();
+
+    run_command (argv, &res);
+    summarize (stderr, output_format, argv, &res);
+    fflush (stderr);
+
+    if (WIFSTOPPED (res.waitstatus))
+       exit (WSTOPSIG (res.waitstatus));
+    else if (WIFSIGNALED (res.waitstatus))
+       exit (WTERMSIG (res.waitstatus));
+    else if (WIFEXITED (res.waitstatus))
+       exit (WEXITSTATUS (res.waitstatus));
+    return 0;
+}
diff --git a/busybox/miscutils/watchdog.c b/busybox/miscutils/watchdog.c
new file mode 100644 (file)
index 0000000..276fade
--- /dev/null
@@ -0,0 +1,81 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini watchdog implementation for busybox
+ *
+ * Copyright (C) 2003  Paul Mundt <lethal@linux-sh.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "busybox.h"
+
+/* Userspace timer duration, in seconds */
+static unsigned int timer_duration = 30;
+
+/* Watchdog file descriptor */
+static int fd;
+
+static void watchdog_shutdown(int unused)
+{
+       write(fd, "V", 1);      /* Magic */
+       close(fd);
+       exit(0);
+}
+
+extern int watchdog_main(int argc, char **argv)
+{
+       int opt;
+
+       while ((opt = getopt(argc, argv, "t:")) > 0) {
+               switch (opt) {
+                       case 't':
+                               timer_duration = bb_xgetlarg(optarg, 10, 0, INT_MAX);
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       /* We're only interested in the watchdog device .. */
+       if (optind < argc - 1 || argc == 1)
+               bb_show_usage();
+
+       if (daemon(0, 1) < 0)
+               bb_perror_msg_and_die("Failed forking watchdog daemon");
+
+       signal(SIGHUP, watchdog_shutdown);
+       signal(SIGINT, watchdog_shutdown);
+
+       fd = bb_xopen(argv[argc - 1], O_WRONLY);
+
+       while (1) {
+               /*
+                * Make sure we clear the counter before sleeping, as the counter value
+                * is undefined at this point -- PFM
+                */
+               write(fd, "\0", 1);
+               sleep(timer_duration);
+       }
+
+       watchdog_shutdown(0);
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/modutils/Config.in b/busybox/modutils/Config.in
new file mode 100644 (file)
index 0000000..ada69d7
--- /dev/null
@@ -0,0 +1,113 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Module Utilities"
+
+config CONFIG_INSMOD
+       bool "insmod"
+       default n
+       help
+         insmod is used to load specified modules in the running kernel.
+
+config CONFIG_FEATURE_2_4_MODULES
+       bool "  Support version 2.2.x to 2.4.x Linux kernels"
+       default y
+       depends on CONFIG_INSMOD
+       help
+         Support module loading for 2.2.x and 2.4.x Linux kernels.
+
+config CONFIG_FEATURE_2_6_MODULES
+       bool "  Support version 2.6.x Linux kernels"
+       default n
+       depends on CONFIG_INSMOD
+       help
+         Support module loading for newer 2.6.x Linux kernels.
+
+config CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+       bool "  Module version checking"
+       default n
+       depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
+       help
+         Support checking of versions for modules.  This is used to
+         ensure that the kernel and module are made for each other.
+
+config CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+       bool "  Add module symbols to kernel symbol table"
+       default n
+       depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
+       help
+         By adding module symbols to the kernel symbol table, Oops messages
+         occuring within kernel modules can be properly debugged.  By enabling
+         this feature, module symbols will always be added to the kernel symbol
+         table for properly debugging support.  If you are not interested in
+         Oops messages from kernel modules, say N.
+
+config CONFIG_FEATURE_INSMOD_LOADINKMEM
+       bool "  In kernel memory optimization (uClinux only)"
+       default n
+       depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
+       help
+         This is a special uClinux only memory optimization that lets insmod
+         load the specified kernel module directly into kernel space, reducing
+         memory usage by preventing the need for two copies of the module
+         being loaded into memory.
+
+config CONFIG_FEATURE_INSMOD_LOAD_MAP
+       bool "  Enable load map (-m) option"
+       default n
+       depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
+       help
+         Enabling this, one would be able to get a load map
+         output on stdout. This makes kernel module debugging
+         easier.
+         If you don't plan to debug kernel modules, you
+         don't need this option.
+
+config CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL
+       bool "  Symbols in load map"
+       default y
+       depends on CONFIG_FEATURE_INSMOD_LOAD_MAP
+       help
+         Without this option, -m will only output section
+         load map.  With this option, -m will also output
+         symbols load map.
+
+config CONFIG_LSMOD
+       bool "lsmod"
+       default n
+       help
+         lsmod is used to display a list of loaded modules.
+
+config CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+       bool
+       default y
+       depends on CONFIG_FEATURE_2_4_MODULES && !CONFIG_FEATURE_2_6_MODULES
+
+config CONFIG_MODPROBE
+       bool "modprobe"
+       default n
+       help
+         Handle the loading of modules, and their dependancies on a high
+         level.
+
+config CONFIG_RMMOD
+       bool "rmmod"
+       default n
+       help
+         rmmod is used to unload specified modules from the kernel.
+
+config CONFIG_FEATURE_CHECK_TAINTED_MODULE
+       bool "Support tainted module checking with new kernels"
+       default y
+       depends on CONFIG_INSMOD || CONFIG_LSMOD
+       help
+         Support checking for tainted modules.  These are usually binary
+         only modules that will make the linux-kernel list ignore your
+         support request.
+         This option is required to support GPLONLY modules.
+
+
+endmenu
+
diff --git a/busybox/modutils/Makefile b/busybox/modutils/Makefile
new file mode 100644 (file)
index 0000000..d2b50b4
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/modutils
+MODUTILS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/modutils/Makefile.in b/busybox/modutils/Makefile.in
new file mode 100644 (file)
index 0000000..9bd11d4
--- /dev/null
@@ -0,0 +1,39 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+MODUTILS_AR:=modutils.a
+ifndef $(MODUTILS_DIR)
+MODUTILS_DIR:=$(top_builddir)/modutils/
+endif
+srcdir=$(top_srcdir)/modutils
+
+MODUTILS-y:=
+MODUTILS-$(CONFIG_INSMOD)              += insmod.o
+MODUTILS-$(CONFIG_LSMOD)               += lsmod.o
+MODUTILS-$(CONFIG_MODPROBE)            += modprobe.o
+MODUTILS-$(CONFIG_RMMOD)               += rmmod.o
+
+libraries-y+=$(MODUTILS_DIR)$(MODUTILS_AR)
+
+$(MODUTILS_DIR)$(MODUTILS_AR): $(patsubst %,$(MODUTILS_DIR)%, $(MODUTILS-y))
+       $(AR) -ro $@ $(patsubst %,$(MODUTILS_DIR)%, $(MODUTILS-y))
+
+$(MODUTILS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/modutils/insmod.c b/busybox/modutils/insmod.c
new file mode 100644 (file)
index 0000000..d88dd1b
--- /dev/null
@@ -0,0 +1,4039 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini insmod implementation for busybox
+ *
+ * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64,
+ * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * and Ron Alder <alder@lineo.com>
+ *
+ * Rodney Radford <rradford@mindspring.com> 17-Aug-2004.
+ *   Added x86_64 support.
+ *
+ * Miles Bader <miles@gnu.org> added NEC V850E support.
+ *
+ * Modified by Bryan Rittmeyer <bryan@ixiacom.com> to support SH4
+ * and (theoretically) SH3. I have only tested SH4 in little endian mode.
+ *
+ * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
+ * Nicolas Ferre <nicolas.ferre@alcove.fr> to support ARM7TDMI.  Only
+ * very minor changes required to also work with StrongArm and presumably
+ * all ARM based systems.
+ *
+ * Yoshinori Sato <ysato@users.sourceforge.jp> 19-May-2004.
+ *   added Renesas H8/300 support.
+ *
+ * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
+ *   Integrated support for sh64 (SH-5), from preliminary modutils
+ *   patches from Benedict Gaster <benedict.gaster@superh.com>.
+ *   Currently limited to support for 32bit ABI.
+ *
+ * Magnus Damm <damm@opensource.se> 22-May-2002.
+ *   The plt and got code are now using the same structs.
+ *   Added generic linked list code to fully support PowerPC.
+ *   Replaced the mess in arch_apply_relocation() with architecture blocks.
+ *   The arch_create_got() function got cleaned up with architecture blocks.
+ *   These blocks should be easy maintain and sync with obj_xxx.c in modutils.
+ *
+ * Magnus Damm <damm@opensource.se> added PowerPC support 20-Feb-2001.
+ *   PowerPC specific code stolen from modutils-2.3.16,
+ *   written by Paul Mackerras, Copyright 1996, 1997 Linux International.
+ *   I've only tested the code on mpc8xx platforms in big-endian mode.
+ *   Did some cleanup and added CONFIG_USE_xxx_ENTRIES...
+ *
+ * Quinn Jensen <jensenq@lineo.com> added MIPS support 23-Feb-2001.
+ *   based on modutils-2.4.2
+ *   MIPS specific support for Elf loading and relocation.
+ *   Copyright 1996, 1997 Linux International.
+ *   Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>
+ *
+ * Based almost entirely on the Linux modutils-2.3.11 implementation.
+ *   Copyright 1996, 1997 Linux International.
+ *   New implementation contributed by Richard Henderson <rth@tamu.edu>
+ *   Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+ *   Restructured (and partly rewritten) by:
+ *   Björn Ekwall <bj0rn@blox.se> February 1999
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+#include "busybox.h"
+
+#if !defined(CONFIG_FEATURE_2_4_MODULES) && \
+       !defined(CONFIG_FEATURE_2_6_MODULES)
+#define CONFIG_FEATURE_2_4_MODULES
+#endif
+
+#if !defined(CONFIG_FEATURE_2_4_MODULES)
+#define insmod_ng_main insmod_main
+#endif
+
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+extern int insmod_ng_main( int argc, char **argv);
+#endif
+
+
+#if defined(CONFIG_FEATURE_2_4_MODULES)
+
+
+#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM
+#define LOADBITS 0
+#else
+#define LOADBITS 1
+#endif
+
+
+/* ARM support */
+#if defined(__arm__)
+#define MATCH_MACHINE(x) (x == EM_ARM)
+#define SHT_RELM       SHT_REL
+#define Elf32_RelM     Elf32_Rel
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_PLT_ENTRIES
+#define CONFIG_PLT_ENTRY_SIZE 8
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 8
+#define CONFIG_USE_SINGLE
+#endif
+
+/* CRIS */
+#if defined(__cris__)
+#define MATCH_MACHINE(x) (x == EM_CRIS)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#ifndef EM_CRIS
+#define EM_CRIS 76
+#define R_CRIS_NONE 0
+#define R_CRIS_32   3
+#endif
+#endif
+
+/* H8/300 */
+#if defined(__H8300H__) || defined(__H8300S__)
+#define MATCH_MACHINE(x) (x == EM_H8_300)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_SINGLE
+#define SYMBOL_PREFIX  "_"
+#endif
+
+/* x86 */
+#if defined(__i386__)
+#ifndef EM_486
+#define MATCH_MACHINE(x) (x == EM_386)
+#else
+#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486)
+#endif
+#define SHT_RELM       SHT_REL
+#define Elf32_RelM     Elf32_Rel
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 4
+#define CONFIG_USE_SINGLE
+#endif
+
+/* IA64, aka Itanium */
+#if defined(__ia64__)
+#define MATCH_MACHINE(x) (x == EM_IA_64)
+#define SHT_RELM       SHT_RELA
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+/* m68k */
+#if defined(__mc68000__)
+#define MATCH_MACHINE(x) (x == EM_68K)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 4
+#define CONFIG_USE_SINGLE
+#endif
+
+/* MIPS */
+#if defined(__mips__)
+#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE)
+#define SHT_RELM       SHT_REL
+#define Elf32_RelM     Elf32_Rel
+#define ELFCLASSM      ELFCLASS32
+/* Account for ELF spec changes.  */
+#ifndef EM_MIPS_RS3_LE
+#ifdef EM_MIPS_RS4_BE
+#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE
+#else
+#define EM_MIPS_RS3_LE 10
+#endif
+#endif /* !EM_MIPS_RS3_LE */
+#define ARCHDATAM       "__dbe_table"
+#endif
+
+/* PowerPC */
+#if defined(__powerpc__)
+#define MATCH_MACHINE(x) (x == EM_PPC)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_PLT_ENTRIES
+#define CONFIG_PLT_ENTRY_SIZE 16
+#define CONFIG_USE_PLT_LIST
+#define CONFIG_LIST_ARCHTYPE ElfW(Addr)
+#define CONFIG_USE_LIST
+#define ARCHDATAM       "__ftr_fixup"
+#endif
+
+/* S390 */
+#if defined(__s390__)
+#define MATCH_MACHINE(x) (x == EM_S390)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_PLT_ENTRIES
+#define CONFIG_PLT_ENTRY_SIZE 8
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 8
+#define CONFIG_USE_SINGLE
+#endif
+
+/* SuperH */
+#if defined(__sh__)
+#define MATCH_MACHINE(x) (x == EM_SH)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 4
+#define CONFIG_USE_SINGLE
+/* the SH changes have only been tested in =little endian= mode */
+/* I'm not sure about big endian, so let's warn: */
+#if defined(__sh__) && defined(__BIG_ENDIAN__)
+#error insmod.c may require changes for use on big endian SH
+#endif
+/* it may or may not work on the SH1/SH2... Error on those also */
+#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__))
+#error insmod.c may require changes for SH1 or SH2 use
+#endif
+#endif
+
+/* Sparc */
+#if defined(__sparc__)
+#define MATCH_MACHINE(x) (x == EM_SPARC)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+
+/* v850e */
+#if defined (__v850e__)
+#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#define CONFIG_USE_PLT_ENTRIES
+#define CONFIG_PLT_ENTRY_SIZE 8
+#define CONFIG_USE_SINGLE
+#ifndef EM_CYGNUS_V850 /* grumble */
+#define EM_CYGNUS_V850         0x9080
+#endif
+#define SYMBOL_PREFIX  "_"
+#endif
+
+/* X86_64  */
+#if defined(__x86_64__)
+#define MATCH_MACHINE(x) (x == EM_X86_64)
+#define SHT_RELM       SHT_REL
+#define Elf64_RelM     Elf64_Rel
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+#ifndef SHT_RELM
+#error Sorry, but insmod.c does not yet support this architecture...
+#endif
+
+
+//----------------------------------------------------------------------------
+//--------modutils module.h, lines 45-242
+//----------------------------------------------------------------------------
+
+/* Definitions for the Linux module syscall interface.
+   Copyright 1996, 1997 Linux International.
+
+   Contributed by Richard Henderson <rth@tamu.edu>
+
+   This file is part of the Linux modutils.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2 of the License, or (at your
+   option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#ifndef MODUTILS_MODULE_H
+static const int MODUTILS_MODULE_H = 1;
+
+#ident "$Id: insmod.c,v 1.125 2004/09/02 23:03:25 andersen Exp $"
+
+/*======================================================================*/
+/* For sizeof() which are related to the module platform and not to the
+   environment isnmod is running in, use sizeof_xx instead of sizeof(xx).  */
+
+#define tgt_sizeof_char                sizeof(char)
+#define tgt_sizeof_short       sizeof(short)
+#define tgt_sizeof_int         sizeof(int)
+#define tgt_sizeof_long                sizeof(long)
+#define tgt_sizeof_char_p      sizeof(char *)
+#define tgt_sizeof_void_p      sizeof(void *)
+#define tgt_long               long
+
+#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64)
+#undef tgt_sizeof_long
+#undef tgt_sizeof_char_p
+#undef tgt_sizeof_void_p
+#undef tgt_long
+static const int tgt_sizeof_long = 8;
+static const int tgt_sizeof_char_p = 8;
+static const int tgt_sizeof_void_p = 8;
+#define tgt_long               long long
+#endif
+
+/*======================================================================*/
+/* The structures used in Linux 2.1.  */
+
+/* Note: new_module_symbol does not use tgt_long intentionally */
+struct new_module_symbol
+{
+       unsigned long value;
+       unsigned long name;
+};
+
+struct new_module_persist;
+
+struct new_module_ref
+{
+       unsigned tgt_long dep;          /* kernel addresses */
+       unsigned tgt_long ref;
+       unsigned tgt_long next_ref;
+};
+
+struct new_module
+{
+       unsigned tgt_long size_of_struct;       /* == sizeof(module) */
+       unsigned tgt_long next;
+       unsigned tgt_long name;
+       unsigned tgt_long size;
+
+       tgt_long usecount;
+       unsigned tgt_long flags;                /* AUTOCLEAN et al */
+
+       unsigned nsyms;
+       unsigned ndeps;
+
+       unsigned tgt_long syms;
+       unsigned tgt_long deps;
+       unsigned tgt_long refs;
+       unsigned tgt_long init;
+       unsigned tgt_long cleanup;
+       unsigned tgt_long ex_table_start;
+       unsigned tgt_long ex_table_end;
+#ifdef __alpha__
+       unsigned tgt_long gp;
+#endif
+       /* Everything after here is extension.  */
+       unsigned tgt_long persist_start;
+       unsigned tgt_long persist_end;
+       unsigned tgt_long can_unload;
+       unsigned tgt_long runsize;
+       const char *kallsyms_start;     /* All symbols for kernel debugging */
+       const char *kallsyms_end;
+       const char *archdata_start;     /* arch specific data for module */
+       const char *archdata_end;
+       const char *kernel_data;        /* Reserved for kernel internal use */
+};
+
+#ifdef ARCHDATAM
+#define ARCHDATA_SEC_NAME ARCHDATAM
+#else
+#define ARCHDATA_SEC_NAME "__archdata"
+#endif
+#define KALLSYMS_SEC_NAME "__kallsyms"
+
+
+struct new_module_info
+{
+       unsigned long addr;
+       unsigned long size;
+       unsigned long flags;
+       long usecount;
+};
+
+/* Bits of module.flags.  */
+static const int NEW_MOD_RUNNING = 1;
+static const int NEW_MOD_DELETED = 2;
+static const int NEW_MOD_AUTOCLEAN = 4;
+static const int NEW_MOD_VISITED = 8;
+static const int NEW_MOD_USED_ONCE = 16;
+
+int init_module(const char *name, const struct new_module *);
+int query_module(const char *name, int which, void *buf,
+               size_t bufsize, size_t *ret);
+
+/* Values for query_module's which.  */
+
+static const int QM_MODULES = 1;
+static const int QM_DEPS = 2;
+static const int QM_REFS = 3;
+static const int QM_SYMBOLS = 4;
+static const int QM_INFO = 5;
+
+/*======================================================================*/
+/* The system calls unchanged between 2.0 and 2.1.  */
+
+unsigned long create_module(const char *, size_t);
+int delete_module(const char *);
+
+
+#endif /* module.h */
+
+//----------------------------------------------------------------------------
+//--------end of modutils module.h
+//----------------------------------------------------------------------------
+
+
+
+//----------------------------------------------------------------------------
+//--------modutils obj.h, lines 253-462
+//----------------------------------------------------------------------------
+
+/* Elf object file loading and relocation routines.
+   Copyright 1996, 1997 Linux International.
+
+   Contributed by Richard Henderson <rth@tamu.edu>
+
+   This file is part of the Linux modutils.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2 of the License, or (at your
+   option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#ifndef MODUTILS_OBJ_H
+static const int MODUTILS_OBJ_H = 1;
+
+#ident "$Id: insmod.c,v 1.125 2004/09/02 23:03:25 andersen Exp $"
+
+/* The relocatable object is manipulated using elfin types.  */
+
+#include <stdio.h>
+#include <elf.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ELFDATAM       ELFDATA2LSB
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define ELFDATAM       ELFDATA2MSB
+#endif
+
+#ifndef ElfW
+# if ELFCLASSM == ELFCLASS32
+#  define ElfW(x)  Elf32_ ## x
+#  define ELFW(x)  ELF32_ ## x
+# else
+#  define ElfW(x)  Elf64_ ## x
+#  define ELFW(x)  ELF64_ ## x
+# endif
+#endif
+
+/* For some reason this is missing from some ancient C libraries....  */
+#ifndef ELF32_ST_INFO
+# define ELF32_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#ifndef ELF64_ST_INFO
+# define ELF64_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+#endif
+
+struct obj_string_patch;
+struct obj_symbol_patch;
+
+struct obj_section
+{
+       ElfW(Shdr) header;
+       const char *name;
+       char *contents;
+       struct obj_section *load_next;
+       int idx;
+};
+
+struct obj_symbol
+{
+       struct obj_symbol *next;        /* hash table link */
+       const char *name;
+       unsigned long value;
+       unsigned long size;
+       int secidx;                     /* the defining section index/module */
+       int info;
+       int ksymidx;                    /* for export to the kernel symtab */
+       int referenced;         /* actually used in the link */
+};
+
+/* Hardcode the hash table size.  We shouldn't be needing so many
+   symbols that we begin to degrade performance, and we get a big win
+   by giving the compiler a constant divisor.  */
+
+#define HASH_BUCKETS  521
+
+struct obj_file
+{
+       ElfW(Ehdr) header;
+       ElfW(Addr) baseaddr;
+       struct obj_section **sections;
+       struct obj_section *load_order;
+       struct obj_section **load_order_search_start;
+       struct obj_string_patch *string_patches;
+       struct obj_symbol_patch *symbol_patches;
+       int (*symbol_cmp)(const char *, const char *);
+       unsigned long (*symbol_hash)(const char *);
+       unsigned long local_symtab_size;
+       struct obj_symbol **local_symtab;
+       struct obj_symbol *symtab[HASH_BUCKETS];
+};
+
+enum obj_reloc
+{
+       obj_reloc_ok,
+       obj_reloc_overflow,
+       obj_reloc_dangerous,
+       obj_reloc_unhandled
+};
+
+struct obj_string_patch
+{
+       struct obj_string_patch *next;
+       int reloc_secidx;
+       ElfW(Addr) reloc_offset;
+       ElfW(Addr) string_offset;
+};
+
+struct obj_symbol_patch
+{
+       struct obj_symbol_patch *next;
+       int reloc_secidx;
+       ElfW(Addr) reloc_offset;
+       struct obj_symbol *sym;
+};
+
+
+/* Generic object manipulation routines.  */
+
+static unsigned long obj_elf_hash(const char *);
+
+static unsigned long obj_elf_hash_n(const char *, unsigned long len);
+
+static struct obj_symbol *obj_find_symbol (struct obj_file *f,
+                                        const char *name);
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file *f,
+                                 struct obj_symbol *sym);
+
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+static void obj_set_symbol_compare(struct obj_file *f,
+                           int (*cmp)(const char *, const char *),
+                           unsigned long (*hash)(const char *));
+#endif
+
+static struct obj_section *obj_find_section (struct obj_file *f,
+                                          const char *name);
+
+static void obj_insert_section_load_order (struct obj_file *f,
+                                   struct obj_section *sec);
+
+static struct obj_section *obj_create_alloced_section (struct obj_file *f,
+                                               const char *name,
+                                               unsigned long align,
+                                               unsigned long size);
+
+static struct obj_section *obj_create_alloced_section_first (struct obj_file *f,
+                                                     const char *name,
+                                                     unsigned long align,
+                                                     unsigned long size);
+
+static void *obj_extend_section (struct obj_section *sec, unsigned long more);
+
+static int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                    const char *string);
+
+static int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                    struct obj_symbol *sym);
+
+static int obj_check_undefineds(struct obj_file *f);
+
+static void obj_allocate_commons(struct obj_file *f);
+
+static unsigned long obj_load_size (struct obj_file *f);
+
+static int obj_relocate (struct obj_file *f, ElfW(Addr) base);
+
+static struct obj_file *obj_load(FILE *f, int loadprogbits);
+
+static int obj_create_image (struct obj_file *f, char *image);
+
+/* Architecture specific manipulation routines.  */
+
+static struct obj_file *arch_new_file (void);
+
+static struct obj_section *arch_new_section (void);
+
+static struct obj_symbol *arch_new_symbol (void);
+
+static enum obj_reloc arch_apply_relocation (struct obj_file *f,
+                                     struct obj_section *targsec,
+                                     struct obj_section *symsec,
+                                     struct obj_symbol *sym,
+                                     ElfW(RelM) *rel, ElfW(Addr) value);
+
+static void arch_create_got (struct obj_file *f);
+
+static int obj_gpl_license(struct obj_file *f, const char **license);
+
+#endif /* obj.h */
+//----------------------------------------------------------------------------
+//--------end of modutils obj.h
+//----------------------------------------------------------------------------
+
+
+/* SPFX is always a string, so it can be concatenated to string constants.  */
+#ifdef SYMBOL_PREFIX
+#define SPFX   SYMBOL_PREFIX
+#else
+#define SPFX   ""
+#endif
+
+
+#define _PATH_MODULES  "/lib/modules"
+static const int STRVERSIONLEN = 32;
+
+/*======================================================================*/
+
+static int flag_force_load = 0;
+static int flag_autoclean = 0;
+static int flag_verbose = 0;
+static int flag_quiet = 0;
+static int flag_export = 1;
+
+
+/*======================================================================*/
+
+#if defined(CONFIG_USE_LIST)
+
+struct arch_list_entry
+{
+       struct arch_list_entry *next;
+       CONFIG_LIST_ARCHTYPE addend;
+       int offset;
+       int inited : 1;
+};
+
+#endif
+
+#if defined(CONFIG_USE_SINGLE)
+
+struct arch_single_entry
+{
+       int offset;
+       int inited : 1;
+       int allocated : 1;
+};
+
+#endif
+
+#if defined(__mips__)
+struct mips_hi16
+{
+       struct mips_hi16 *next;
+       Elf32_Addr *addr;
+       Elf32_Addr value;
+};
+#endif
+
+struct arch_file {
+       struct obj_file root;
+#if defined(CONFIG_USE_PLT_ENTRIES)
+       struct obj_section *plt;
+#endif
+#if defined(CONFIG_USE_GOT_ENTRIES)
+       struct obj_section *got;
+#endif
+#if defined(__mips__)
+       struct mips_hi16 *mips_hi16_list;
+#endif
+};
+
+struct arch_symbol {
+       struct obj_symbol root;
+#if defined(CONFIG_USE_PLT_ENTRIES)
+#if defined(CONFIG_USE_PLT_LIST)
+       struct arch_list_entry *pltent;
+#else
+       struct arch_single_entry pltent;
+#endif
+#endif
+#if defined(CONFIG_USE_GOT_ENTRIES)
+       struct arch_single_entry gotent;
+#endif
+};
+
+
+struct external_module {
+       const char *name;
+       ElfW(Addr) addr;
+       int used;
+       size_t nsyms;
+       struct new_module_symbol *syms;
+};
+
+static struct new_module_symbol *ksyms;
+static size_t nksyms;
+
+static struct external_module *ext_modules;
+static int n_ext_modules;
+static int n_ext_modules_used;
+extern int delete_module(const char *);
+
+static char *m_filename;
+static char *m_fullName;
+
+
+
+/*======================================================================*/
+
+
+static int check_module_name_match(const char *filename, struct stat *statbuf,
+                                                  void *userdata)
+{
+       char *fullname = (char *) userdata;
+
+       if (fullname[0] == '\0')
+               return (FALSE);
+       else {
+               char *tmp, *tmp1 = bb_xstrdup(filename);
+               tmp = bb_get_last_path_component(tmp1);
+               if (strcmp(tmp, fullname) == 0) {
+                       free(tmp1);
+                       /* Stop searching if we find a match */
+                       m_filename = bb_xstrdup(filename);
+                       return (FALSE);
+               }
+               free(tmp1);
+       }
+       return (TRUE);
+}
+
+
+/*======================================================================*/
+
+static struct obj_file *arch_new_file(void)
+{
+       struct arch_file *f;
+       f = xmalloc(sizeof(*f));
+
+       memset(f, 0, sizeof(*f));
+
+       return &f->root;
+}
+
+static struct obj_section *arch_new_section(void)
+{
+       return xmalloc(sizeof(struct obj_section));
+}
+
+static struct obj_symbol *arch_new_symbol(void)
+{
+       struct arch_symbol *sym;
+       sym = xmalloc(sizeof(*sym));
+
+       memset(sym, 0, sizeof(*sym));
+
+       return &sym->root;
+}
+
+static enum obj_reloc
+arch_apply_relocation(struct obj_file *f,
+                                         struct obj_section *targsec,
+                                         struct obj_section *symsec,
+                                         struct obj_symbol *sym,
+                                     ElfW(RelM) *rel, ElfW(Addr) v)
+{
+       struct arch_file *ifile = (struct arch_file *) f;
+       enum obj_reloc ret = obj_reloc_ok;
+       ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset);
+       ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset;
+#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES)
+       struct arch_symbol *isym = (struct arch_symbol *) sym;
+#endif
+#if defined(CONFIG_USE_GOT_ENTRIES)
+       ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0;
+#endif
+#if defined(CONFIG_USE_PLT_ENTRIES)
+       ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0;
+       unsigned long *ip;
+#if defined(CONFIG_USE_PLT_LIST)
+       struct arch_list_entry *pe;
+#else
+       struct arch_single_entry *pe;
+#endif
+#endif
+
+       switch (ELF32_R_TYPE(rel->r_info)) {
+
+
+#if defined(__arm__)
+               case R_ARM_NONE:
+                       break;
+
+               case R_ARM_ABS32:
+                       *loc += v;
+                       break;
+
+               case R_ARM_GOT32:
+                       goto bb_use_got;
+
+               case R_ARM_GOTPC:
+                       /* relative reloc, always to _GLOBAL_OFFSET_TABLE_
+                        * (which is .got) similar to branch,
+                        * but is full 32 bits relative */
+
+                       assert(got);
+                       *loc += got - dot;
+                       break;
+
+               case R_ARM_PC24:
+               case R_ARM_PLT32:
+                       goto bb_use_plt;
+
+               case R_ARM_GOTOFF: /* address relative to the got */
+                       assert(got);
+                       *loc += v - got;
+                       break;
+
+#elif defined(__s390__)
+               case R_390_32:
+                       *(unsigned int *) loc += v;
+                       break;
+               case R_390_16:
+                       *(unsigned short *) loc += v;
+                       break;
+               case R_390_8:
+                       *(unsigned char *) loc += v;
+                       break;
+
+               case R_390_PC32:
+                       *(unsigned int *) loc += v - dot;
+                       break;
+               case R_390_PC16DBL:
+                       *(unsigned short *) loc += (v - dot) >> 1;
+                       break;
+               case R_390_PC16:
+                       *(unsigned short *) loc += v - dot;
+                       break;
+
+               case R_390_PLT32:
+               case R_390_PLT16DBL:
+                       /* find the plt entry and initialize it.  */
+                       assert(isym != NULL);
+                       pe = (struct arch_single_entry *) &isym->pltent;
+                       assert(pe->allocated);
+                       if (pe->inited == 0) {
+                               ip = (unsigned long *)(ifile->plt->contents + pe->offset);
+                               ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
+                               ip[1] = 0x100607f1;
+                               if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+                                       ip[2] = v - 2;
+                               else
+                                       ip[2] = v;
+                               pe->inited = 1;
+                       }
+
+                       /* Insert relative distance to target.  */
+                       v = plt + pe->offset - dot;
+                       if (ELF32_R_TYPE(rel->r_info) == R_390_PLT32)
+                               *(unsigned int *) loc = (unsigned int) v;
+                       else if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+                               *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
+                       break;
+
+               case R_390_GLOB_DAT:
+               case R_390_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_390_RELATIVE:
+                       *loc += f->baseaddr;
+                       break;
+
+               case R_390_GOTPC:
+                       assert(got != 0);
+                       *(unsigned long *) loc += got - dot;
+                       break;
+
+               case R_390_GOT12:
+               case R_390_GOT16:
+               case R_390_GOT32:
+                       assert(isym != NULL);
+                       assert(got != 0);
+                       if (!isym->gotent.inited)
+                       {
+                               isym->gotent.inited = 1;
+                               *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
+                       }
+                       if (ELF32_R_TYPE(rel->r_info) == R_390_GOT12)
+                               *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
+                       else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT16)
+                               *(unsigned short *) loc += isym->gotent.offset;
+                       else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT32)
+                               *(unsigned int *) loc += isym->gotent.offset;
+                       break;
+
+#ifndef R_390_GOTOFF32
+#define R_390_GOTOFF32 R_390_GOTOFF
+#endif
+               case R_390_GOTOFF32:
+                       assert(got != 0);
+                       *loc += v - got;
+                       break;
+
+#elif defined(__i386__)
+
+               case R_386_NONE:
+                       break;
+
+               case R_386_32:
+                       *loc += v;
+                       break;
+
+               case R_386_PLT32:
+               case R_386_PC32:
+                       *loc += v - dot;
+                       break;
+
+               case R_386_GLOB_DAT:
+               case R_386_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_386_RELATIVE:
+                       *loc += f->baseaddr;
+                       break;
+
+               case R_386_GOTPC:
+                       assert(got != 0);
+                       *loc += got - dot;
+                       break;
+
+               case R_386_GOT32:
+                       goto bb_use_got;
+
+               case R_386_GOTOFF:
+                       assert(got != 0);
+                       *loc += v - got;
+                       break;
+
+#elif defined(__mc68000__)
+
+               case R_68K_NONE:
+                       break;
+
+               case R_68K_32:
+                       *loc += v;
+                       break;
+
+               case R_68K_8:
+                       if (v > 0xff) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(char *)loc = v;
+                       break;
+
+               case R_68K_16:
+                       if (v > 0xffff) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(short *)loc = v;
+                       break;
+
+               case R_68K_PC8:
+                       v -= dot;
+                       if ((Elf32_Sword)v > 0x7f ||
+                                       (Elf32_Sword)v < -(Elf32_Sword)0x80) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(char *)loc = v;
+                       break;
+
+               case R_68K_PC16:
+                       v -= dot;
+                       if ((Elf32_Sword)v > 0x7fff ||
+                                       (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+                               ret = obj_reloc_overflow;
+                       }
+                       *(short *)loc = v;
+                       break;
+
+               case R_68K_PC32:
+                       *(int *)loc = v - dot;
+                       break;
+
+               case R_68K_GLOB_DAT:
+               case R_68K_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_68K_RELATIVE:
+                       *(int *)loc += f->baseaddr;
+                       break;
+
+               case R_68K_GOT32:
+                       goto bb_use_got;
+
+#ifdef R_68K_GOTOFF
+               case R_68K_GOTOFF:
+                       assert(got != 0);
+                       *loc += v - got;
+                       break;
+#endif
+
+#elif defined(__mips__)
+
+               case R_MIPS_NONE:
+                       break;
+
+               case R_MIPS_32:
+                       *loc += v;
+                       break;
+
+               case R_MIPS_26:
+                       if (v % 4)
+                               ret = obj_reloc_dangerous;
+                       if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000))
+                               ret = obj_reloc_overflow;
+                       *loc =
+                               (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) &
+                                                                               0x03ffffff);
+                       break;
+
+               case R_MIPS_HI16:
+                       {
+                               struct mips_hi16 *n;
+
+                               /* We cannot relocate this one now because we don't know the value
+                                  of the carry we need to add.  Save the information, and let LO16
+                                  do the actual relocation.  */
+                               n = (struct mips_hi16 *) xmalloc(sizeof *n);
+                               n->addr = loc;
+                               n->value = v;
+                               n->next = ifile->mips_hi16_list;
+                               ifile->mips_hi16_list = n;
+                               break;
+                       }
+
+               case R_MIPS_LO16:
+                       {
+                               unsigned long insnlo = *loc;
+                               Elf32_Addr val, vallo;
+
+                               /* Sign extend the addend we extract from the lo insn.  */
+                               vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
+
+                               if (ifile->mips_hi16_list != NULL) {
+                                       struct mips_hi16 *l;
+
+                                       l = ifile->mips_hi16_list;
+                                       while (l != NULL) {
+                                               struct mips_hi16 *next;
+                                               unsigned long insn;
+
+                                               /* The value for the HI16 had best be the same. */
+                                               assert(v == l->value);
+
+                                               /* Do the HI16 relocation.  Note that we actually don't
+                                                  need to know anything about the LO16 itself, except where
+                                                  to find the low 16 bits of the addend needed by the LO16.  */
+                                               insn = *l->addr;
+                                               val =
+                                                       ((insn & 0xffff) << 16) +
+                                                       vallo;
+                                               val += v;
+
+                                               /* Account for the sign extension that will happen in the
+                                                  low bits.  */
+                                               val =
+                                                       ((val >> 16) +
+                                                        ((val & 0x8000) !=
+                                                         0)) & 0xffff;
+
+                                               insn = (insn & ~0xffff) | val;
+                                               *l->addr = insn;
+
+                                               next = l->next;
+                                               free(l);
+                                               l = next;
+                                       }
+
+                                       ifile->mips_hi16_list = NULL;
+                               }
+
+                               /* Ok, we're done with the HI16 relocs.  Now deal with the LO16.  */
+                               val = v + vallo;
+                               insnlo = (insnlo & ~0xffff) | (val & 0xffff);
+                               *loc = insnlo;
+                               break;
+                       }
+
+#elif defined(__powerpc__)
+
+               case R_PPC_ADDR16_HA:
+                       *(unsigned short *)loc = (v + 0x8000) >> 16;
+                       break;
+
+               case R_PPC_ADDR16_HI:
+                       *(unsigned short *)loc = v >> 16;
+                       break;
+
+               case R_PPC_ADDR16_LO:
+                       *(unsigned short *)loc = v;
+                       break;
+
+               case R_PPC_REL24:
+                       goto bb_use_plt;
+
+               case R_PPC_REL32:
+                       *loc = v - dot;
+                       break;
+
+               case R_PPC_ADDR32:
+                       *loc = v;
+                       break;
+
+#elif defined(__sh__)
+
+               case R_SH_NONE:
+                       break;
+
+               case R_SH_DIR32:
+                       *loc += v;
+                       break;
+
+               case R_SH_REL32:
+                       *loc += v - dot;
+                       break;
+
+               case R_SH_PLT32:
+                       *loc = v - dot;
+                       break;
+
+               case R_SH_GLOB_DAT:
+               case R_SH_JMP_SLOT:
+                       *loc = v;
+                       break;
+
+               case R_SH_RELATIVE:
+                       *loc = f->baseaddr + rel->r_addend;
+                       break;
+
+               case R_SH_GOTPC:
+                       assert(got != 0);
+                       *loc = got - dot + rel->r_addend;
+                       break;
+
+               case R_SH_GOT32:
+                       goto bb_use_got;
+
+               case R_SH_GOTOFF:
+                       assert(got != 0);
+                       *loc = v - got;
+                       break;
+
+#if defined(__SH5__)
+               case R_SH_IMM_MEDLOW16:
+               case R_SH_IMM_LOW16:
+                       {
+                               Elf32_Addr word;
+
+                               if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
+                                       v >>= 16;
+
+                               /*
+                                *  movi and shori have the format:
+                                *
+                                *  |  op  | imm  | reg | reserved |
+                                *   31..26 25..10 9.. 4 3   ..   0
+                                *
+                                * so we simply mask and or in imm.
+                                */
+                               word = *loc & ~0x3fffc00;
+                               word |= (v & 0xffff) << 10;
+
+                               *loc = word;
+
+                               break;
+                       }
+
+               case R_SH_IMM_MEDLOW16_PCREL:
+               case R_SH_IMM_LOW16_PCREL:
+                       {
+                               Elf32_Addr word;
+
+                               word = *loc & ~0x3fffc00;
+
+                               v -= dot;
+
+                               if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
+                                       v >>= 16;
+
+                               word |= (v & 0xffff) << 10;
+
+                               *loc = word;
+
+                               break;
+                       }
+#endif /* __SH5__ */
+#endif /* __sh__ */
+
+               default:
+                       printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info));
+                       ret = obj_reloc_unhandled;
+                       break;
+
+#if defined (__v850e__)
+               case R_V850_NONE:
+                       break;
+
+               case R_V850_32:
+                       /* We write two shorts instead of a long because even
+                          32-bit insns only need half-word alignment, but
+                          32-bit data needs to be long-word aligned.  */
+                       v += ((unsigned short *)loc)[0];
+                       v += ((unsigned short *)loc)[1] << 16;
+                       ((unsigned short *)loc)[0] = v & 0xffff;
+                       ((unsigned short *)loc)[1] = (v >> 16) & 0xffff;
+                       break;
+
+               case R_V850_22_PCREL:
+                       goto bb_use_plt;
+#endif
+
+#if defined (__cris__)
+               case R_CRIS_NONE:
+                       break;
+
+               case R_CRIS_32:
+                       /* CRIS keeps the relocation value in the r_addend field and
+                        * should not use whats in *loc at all
+                        */
+                       *loc = v;
+                       break;
+#endif
+
+#if defined(__H8300H__) || defined(__H8300S__)
+               case R_H8_DIR24R8:
+                       loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1);
+                       *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v);
+                       break;
+               case R_H8_DIR24A8:
+                       *loc += v;
+                       break;
+               case R_H8_DIR32:
+               case R_H8_DIR32A16:
+                       *loc += v;
+                       break;
+               case R_H8_PCREL16:
+                       v -= dot + 2;
+                       if ((Elf32_Sword)v > 0x7fff || 
+                           (Elf32_Sword)v < -(Elf32_Sword)0x8000)
+                               ret = obj_reloc_overflow;
+                       else 
+                               *(unsigned short *)loc = v;
+                       break;
+               case R_H8_PCREL8:
+                       v -= dot + 1;
+                       if ((Elf32_Sword)v > 0x7f || 
+                           (Elf32_Sword)v < -(Elf32_Sword)0x80)
+                               ret = obj_reloc_overflow;
+                       else 
+                               *(unsigned char *)loc = v;
+                       break;
+#endif
+
+#if defined(CONFIG_USE_PLT_ENTRIES)
+
+bb_use_plt:
+
+                       /* find the plt entry and initialize it if necessary */
+                       assert(isym != NULL);
+
+#if defined(CONFIG_USE_PLT_LIST)
+                       for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;)
+                               pe = pe->next;
+                       assert(pe != NULL);
+#else
+                       pe = &isym->pltent;
+#endif
+
+                       if (! pe->inited) {
+                               ip = (unsigned long *) (ifile->plt->contents + pe->offset);
+
+                               /* generate some machine code */
+
+#if defined(__arm__)
+                               ip[0] = 0xe51ff004;                     /* ldr pc,[pc,#-4] */
+                               ip[1] = v;                              /* sym@ */
+#endif
+#if defined(__powerpc__)
+                               ip[0] = 0x3d600000 + ((v + 0x8000) >> 16);  /* lis r11,sym@ha */
+                               ip[1] = 0x396b0000 + (v & 0xffff);            /* addi r11,r11,sym@l */
+                               ip[2] = 0x7d6903a6;                           /* mtctr r11 */
+                               ip[3] = 0x4e800420;                           /* bctr */
+#endif
+#if defined (__v850e__)
+                               /* We have to trash a register, so we assume that any control
+                                  transfer more than 21-bits away must be a function call
+                                  (so we can use a call-clobbered register).  */
+                               ip[0] = 0x0621 + ((v & 0xffff) << 16);   /* mov sym, r1 ... */
+                               ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
+#endif
+                               pe->inited = 1;
+                       }
+
+                       /* relative distance to target */
+                       v -= dot;
+                       /* if the target is too far away.... */
+#if defined (__arm__) || defined (__powerpc__)
+                       if ((int)v < -0x02000000 || (int)v >= 0x02000000)
+#elif defined (__v850e__)
+                               if ((Elf32_Sword)v > 0x1fffff || (Elf32_Sword)v < (Elf32_Sword)-0x200000)
+#endif
+                                       /* go via the plt */
+                                       v = plt + pe->offset - dot;
+
+#if defined (__v850e__)
+                       if (v & 1)
+#else
+                               if (v & 3)
+#endif
+                                       ret = obj_reloc_dangerous;
+
+                       /* merge the offset into the instruction. */
+#if defined(__arm__)
+                       /* Convert to words. */
+                       v >>= 2;
+
+                       *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff);
+#endif
+#if defined(__powerpc__)
+                       *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
+#endif
+#if defined (__v850e__)
+                       /* We write two shorts instead of a long because even 32-bit insns
+                          only need half-word alignment, but the 32-bit data write needs
+                          to be long-word aligned.  */
+                       ((unsigned short *)loc)[0] =
+                               (*(unsigned short *)loc & 0xffc0) /* opcode + reg */
+                               | ((v >> 16) & 0x3f);             /* offs high part */
+                       ((unsigned short *)loc)[1] =
+                               (v & 0xffff);                    /* offs low part */
+#endif
+                       break;
+#endif /* CONFIG_USE_PLT_ENTRIES */
+
+#if defined(CONFIG_USE_GOT_ENTRIES)
+bb_use_got:
+
+                       assert(isym != NULL);
+                       /* needs an entry in the .got: set it, once */
+                       if (!isym->gotent.inited) {
+                               isym->gotent.inited = 1;
+                               *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v;
+                       }
+                       /* make the reloc with_respect_to_.got */
+#if defined(__sh__)
+                       *loc += isym->gotent.offset + rel->r_addend;
+#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__)
+                       *loc += isym->gotent.offset;
+#endif
+                       break;
+
+#endif /* CONFIG_USE_GOT_ENTRIES */
+       }
+
+       return ret;
+}
+
+
+#if defined(CONFIG_USE_LIST)
+
+static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list,
+                         int offset, int size)
+{
+       struct arch_list_entry *pe;
+
+       for (pe = *list; pe != NULL; pe = pe->next) {
+               if (pe->addend == rel->r_addend) {
+                       break;
+               }
+       }
+
+       if (pe == NULL) {
+               pe = xmalloc(sizeof(struct arch_list_entry));
+               pe->next = *list;
+               pe->addend = rel->r_addend;
+               pe->offset = offset;
+               pe->inited = 0;
+               *list = pe;
+               return size;
+       }
+       return 0;
+}
+
+#endif
+
+#if defined(CONFIG_USE_SINGLE)
+
+static int arch_single_init(ElfW(RelM) *rel, struct arch_single_entry *single,
+                            int offset, int size)
+{
+       if (single->allocated == 0) {
+               single->allocated = 1;
+               single->offset = offset;
+               single->inited = 0;
+               return size;
+       }
+       return 0;
+}
+
+#endif
+
+#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES)
+
+static struct obj_section *arch_xsect_init(struct obj_file *f, char *name,
+                                          int offset, int size)
+{
+       struct obj_section *myrelsec = obj_find_section(f, name);
+
+       if (offset == 0) {
+               offset += size;
+       }
+
+       if (myrelsec) {
+               obj_extend_section(myrelsec, offset);
+       } else {
+               myrelsec = obj_create_alloced_section(f, name,
+                               size, offset);
+               assert(myrelsec);
+       }
+
+       return myrelsec;
+}
+
+#endif
+
+static void arch_create_got(struct obj_file *f)
+{
+#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES)
+       struct arch_file *ifile = (struct arch_file *) f;
+       int i;
+#if defined(CONFIG_USE_GOT_ENTRIES)
+       int got_offset = 0, got_needed = 0, got_allocate;
+#endif
+#if defined(CONFIG_USE_PLT_ENTRIES)
+       int plt_offset = 0, plt_needed = 0, plt_allocate;
+#endif
+       struct obj_section *relsec, *symsec, *strsec;
+       ElfW(RelM) *rel, *relend;
+       ElfW(Sym) *symtab, *extsym;
+       const char *strtab, *name;
+       struct arch_symbol *intsym;
+
+       for (i = 0; i < f->header.e_shnum; ++i) {
+               relsec = f->sections[i];
+               if (relsec->header.sh_type != SHT_RELM)
+                       continue;
+
+               symsec = f->sections[relsec->header.sh_link];
+               strsec = f->sections[symsec->header.sh_link];
+
+               rel = (ElfW(RelM) *) relsec->contents;
+               relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+               symtab = (ElfW(Sym) *) symsec->contents;
+               strtab = (const char *) strsec->contents;
+
+               for (; rel < relend; ++rel) {
+                       extsym = &symtab[ELF32_R_SYM(rel->r_info)];
+
+#if defined(CONFIG_USE_GOT_ENTRIES)
+                       got_allocate = 0;
+#endif
+#if defined(CONFIG_USE_PLT_ENTRIES)
+                       plt_allocate = 0;
+#endif
+
+                       switch (ELF32_R_TYPE(rel->r_info)) {
+#if defined(__arm__)
+                               case R_ARM_PC24:
+                               case R_ARM_PLT32:
+                                       plt_allocate = 1;
+                                       break;
+
+                               case R_ARM_GOTOFF:
+                               case R_ARM_GOTPC:
+                                       got_needed = 1;
+                                       continue;
+
+                               case R_ARM_GOT32:
+                                       got_allocate = 1;
+                                       break;
+
+#elif defined(__i386__)
+                               case R_386_GOTPC:
+                               case R_386_GOTOFF:
+                                       got_needed = 1;
+                                       continue;
+
+                               case R_386_GOT32:
+                                       got_allocate = 1;
+                                       break;
+
+#elif defined(__powerpc__)
+                               case R_PPC_REL24:
+                                       plt_allocate = 1;
+                                       break;
+
+#elif defined(__mc68000__)
+                               case R_68K_GOT32:
+                                       got_allocate = 1;
+                                       break;
+
+#ifdef R_68K_GOTOFF
+                               case R_68K_GOTOFF:
+                                       got_needed = 1;
+                                       continue;
+#endif
+
+#elif defined(__sh__)
+                               case R_SH_GOT32:
+                                       got_allocate = 1;
+                                       break;
+
+                               case R_SH_GOTPC:
+                               case R_SH_GOTOFF:
+                                       got_needed = 1;
+                                       continue;
+
+#elif defined (__v850e__)
+                               case R_V850_22_PCREL:
+                                       plt_needed = 1;
+                                       break;
+
+#endif
+                               default:
+                                       continue;
+                       }
+
+                       if (extsym->st_name != 0) {
+                               name = strtab + extsym->st_name;
+                       } else {
+                               name = f->sections[extsym->st_shndx]->name;
+                       }
+                       intsym = (struct arch_symbol *) obj_find_symbol(f, name);
+#if defined(CONFIG_USE_GOT_ENTRIES)
+                       if (got_allocate) {
+                               got_offset += arch_single_init(
+                                               rel, &intsym->gotent,
+                                               got_offset, CONFIG_GOT_ENTRY_SIZE);
+
+                               got_needed = 1;
+                       }
+#endif
+#if defined(CONFIG_USE_PLT_ENTRIES)
+                       if (plt_allocate) {
+#if defined(CONFIG_USE_PLT_LIST)
+                               plt_offset += arch_list_add(
+                                               rel, &intsym->pltent,
+                                               plt_offset, CONFIG_PLT_ENTRY_SIZE);
+#else
+                               plt_offset += arch_single_init(
+                                               rel, &intsym->pltent,
+                                               plt_offset, CONFIG_PLT_ENTRY_SIZE);
+#endif
+                               plt_needed = 1;
+                       }
+#endif
+               }
+       }
+
+#if defined(CONFIG_USE_GOT_ENTRIES)
+       if (got_needed) {
+               ifile->got = arch_xsect_init(f, ".got", got_offset,
+                               CONFIG_GOT_ENTRY_SIZE);
+       }
+#endif
+
+#if defined(CONFIG_USE_PLT_ENTRIES)
+       if (plt_needed) {
+               ifile->plt = arch_xsect_init(f, ".plt", plt_offset,
+                               CONFIG_PLT_ENTRY_SIZE);
+       }
+#endif
+
+#endif /* defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) */
+}
+
+/*======================================================================*/
+
+/* Standard ELF hash function.  */
+static inline unsigned long obj_elf_hash_n(const char *name, unsigned long n)
+{
+       unsigned long h = 0;
+       unsigned long g;
+       unsigned char ch;
+
+       while (n > 0) {
+               ch = *name++;
+               h = (h << 4) + ch;
+               if ((g = (h & 0xf0000000)) != 0) {
+                       h ^= g >> 24;
+                       h &= ~g;
+               }
+               n--;
+       }
+       return h;
+}
+
+static unsigned long obj_elf_hash(const char *name)
+{
+       return obj_elf_hash_n(name, strlen(name));
+}
+
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+/* String comparison for non-co-versioned kernel and module.  */
+
+static int ncv_strcmp(const char *a, const char *b)
+{
+       size_t alen = strlen(a), blen = strlen(b);
+
+       if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R')
+               return strncmp(a, b, alen);
+       else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R')
+               return strncmp(a, b, blen);
+       else
+               return strcmp(a, b);
+}
+
+/* String hashing for non-co-versioned kernel and module.  Here
+   we are simply forced to drop the crc from the hash.  */
+
+static unsigned long ncv_symbol_hash(const char *str)
+{
+       size_t len = strlen(str);
+       if (len > 10 && str[len - 10] == '_' && str[len - 9] == 'R')
+               len -= 10;
+       return obj_elf_hash_n(str, len);
+}
+
+static void
+obj_set_symbol_compare(struct obj_file *f,
+                                          int (*cmp) (const char *, const char *),
+                                          unsigned long (*hash) (const char *))
+{
+       if (cmp)
+               f->symbol_cmp = cmp;
+       if (hash) {
+               struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
+               int i;
+
+               f->symbol_hash = hash;
+
+               memcpy(tmptab, f->symtab, sizeof(tmptab));
+               memset(f->symtab, 0, sizeof(f->symtab));
+
+               for (i = 0; i < HASH_BUCKETS; ++i)
+                       for (sym = tmptab[i]; sym; sym = next) {
+                               unsigned long h = hash(sym->name) % HASH_BUCKETS;
+                               next = sym->next;
+                               sym->next = f->symtab[h];
+                               f->symtab[h] = sym;
+                       }
+       }
+}
+
+#endif                                                 /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+
+static struct obj_symbol *
+obj_add_symbol(struct obj_file *f, const char *name,
+                                                                 unsigned long symidx, int info,
+                                                                 int secidx, ElfW(Addr) value,
+                                                                 unsigned long size)
+{
+       struct obj_symbol *sym;
+       unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+       int n_type = ELFW(ST_TYPE) (info);
+       int n_binding = ELFW(ST_BIND) (info);
+
+       for (sym = f->symtab[hash]; sym; sym = sym->next)
+               if (f->symbol_cmp(sym->name, name) == 0) {
+                       int o_secidx = sym->secidx;
+                       int o_info = sym->info;
+                       int o_type = ELFW(ST_TYPE) (o_info);
+                       int o_binding = ELFW(ST_BIND) (o_info);
+
+                       /* A redefinition!  Is it legal?  */
+
+                       if (secidx == SHN_UNDEF)
+                               return sym;
+                       else if (o_secidx == SHN_UNDEF)
+                               goto found;
+                       else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) {
+                               /* Cope with local and global symbols of the same name
+                                  in the same object file, as might have been created
+                                  by ld -r.  The only reason locals are now seen at this
+                                  level at all is so that we can do semi-sensible things
+                                  with parameters.  */
+
+                               struct obj_symbol *nsym, **p;
+
+                               nsym = arch_new_symbol();
+                               nsym->next = sym->next;
+                               nsym->ksymidx = -1;
+
+                               /* Excise the old (local) symbol from the hash chain.  */
+                               for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
+                                       continue;
+                               *p = sym = nsym;
+                               goto found;
+                       } else if (n_binding == STB_LOCAL) {
+                               /* Another symbol of the same name has already been defined.
+                                  Just add this to the local table.  */
+                               sym = arch_new_symbol();
+                               sym->next = NULL;
+                               sym->ksymidx = -1;
+                               f->local_symtab[symidx] = sym;
+                               goto found;
+                       } else if (n_binding == STB_WEAK)
+                               return sym;
+                       else if (o_binding == STB_WEAK)
+                               goto found;
+                       /* Don't unify COMMON symbols with object types the programmer
+                          doesn't expect.  */
+                       else if (secidx == SHN_COMMON
+                                       && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
+                               return sym;
+                       else if (o_secidx == SHN_COMMON
+                                       && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
+                               goto found;
+                       else {
+                               /* Don't report an error if the symbol is coming from
+                                  the kernel or some external module.  */
+                               if (secidx <= SHN_HIRESERVE)
+                                       bb_error_msg("%s multiply defined", name);
+                               return sym;
+                       }
+               }
+
+       /* Completely new symbol.  */
+       sym = arch_new_symbol();
+       sym->next = f->symtab[hash];
+       f->symtab[hash] = sym;
+       sym->ksymidx = -1;
+
+       if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) {
+               if (symidx >= f->local_symtab_size)
+                       bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
+                                       name, (long) symidx, (long) f->local_symtab_size);
+               else
+                       f->local_symtab[symidx] = sym;
+       }
+
+found:
+       sym->name = name;
+       sym->value = value;
+       sym->size = size;
+       sym->secidx = secidx;
+       sym->info = info;
+
+       return sym;
+}
+
+static struct obj_symbol *
+obj_find_symbol(struct obj_file *f, const char *name)
+{
+       struct obj_symbol *sym;
+       unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+
+       for (sym = f->symtab[hash]; sym; sym = sym->next)
+               if (f->symbol_cmp(sym->name, name) == 0)
+                       return sym;
+
+       return NULL;
+}
+
+static ElfW(Addr)
+       obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym)
+{
+       if (sym) {
+               if (sym->secidx >= SHN_LORESERVE)
+                       return sym->value;
+
+               return sym->value + f->sections[sym->secidx]->header.sh_addr;
+       } else {
+               /* As a special case, a NULL sym has value zero.  */
+               return 0;
+       }
+}
+
+static struct obj_section *obj_find_section(struct obj_file *f, const char *name)
+{
+       int i, n = f->header.e_shnum;
+
+       for (i = 0; i < n; ++i)
+               if (strcmp(f->sections[i]->name, name) == 0)
+                       return f->sections[i];
+
+       return NULL;
+}
+
+static int obj_load_order_prio(struct obj_section *a)
+{
+       unsigned long af, ac;
+
+       af = a->header.sh_flags;
+
+       ac = 0;
+       if (a->name[0] != '.' || strlen(a->name) != 10 ||
+                       strcmp(a->name + 5, ".init"))
+               ac |= 32;
+       if (af & SHF_ALLOC)
+               ac |= 16;
+       if (!(af & SHF_WRITE))
+               ac |= 8;
+       if (af & SHF_EXECINSTR)
+               ac |= 4;
+       if (a->header.sh_type != SHT_NOBITS)
+               ac |= 2;
+
+       return ac;
+}
+
+static void
+obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec)
+{
+       struct obj_section **p;
+       int prio = obj_load_order_prio(sec);
+       for (p = f->load_order_search_start; *p; p = &(*p)->load_next)
+               if (obj_load_order_prio(*p) < prio)
+                       break;
+       sec->load_next = *p;
+       *p = sec;
+}
+
+static struct obj_section *obj_create_alloced_section(struct obj_file *f,
+                                                                                          const char *name,
+                                                                                          unsigned long align,
+                                                                                          unsigned long size)
+{
+       int newidx = f->header.e_shnum++;
+       struct obj_section *sec;
+
+       f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
+       f->sections[newidx] = sec = arch_new_section();
+
+       memset(sec, 0, sizeof(*sec));
+       sec->header.sh_type = SHT_PROGBITS;
+       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+       sec->header.sh_size = size;
+       sec->header.sh_addralign = align;
+       sec->name = name;
+       sec->idx = newidx;
+       if (size)
+               sec->contents = xmalloc(size);
+
+       obj_insert_section_load_order(f, sec);
+
+       return sec;
+}
+
+static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
+                                                                                                        const char *name,
+                                                                                                        unsigned long align,
+                                                                                                        unsigned long size)
+{
+       int newidx = f->header.e_shnum++;
+       struct obj_section *sec;
+
+       f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
+       f->sections[newidx] = sec = arch_new_section();
+
+       memset(sec, 0, sizeof(*sec));
+       sec->header.sh_type = SHT_PROGBITS;
+       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+       sec->header.sh_size = size;
+       sec->header.sh_addralign = align;
+       sec->name = name;
+       sec->idx = newidx;
+       if (size)
+               sec->contents = xmalloc(size);
+
+       sec->load_next = f->load_order;
+       f->load_order = sec;
+       if (f->load_order_search_start == &f->load_order)
+               f->load_order_search_start = &sec->load_next;
+
+       return sec;
+}
+
+static void *obj_extend_section(struct obj_section *sec, unsigned long more)
+{
+       unsigned long oldsize = sec->header.sh_size;
+       if (more) {
+               sec->contents = xrealloc(sec->contents, sec->header.sh_size += more);
+       }
+       return sec->contents + oldsize;
+}
+
+
+/* Conditionally add the symbols from the given symbol set to the
+   new module.  */
+
+static int
+add_symbols_from( struct obj_file *f,
+                                int idx, struct new_module_symbol *syms, size_t nsyms)
+{
+       struct new_module_symbol *s;
+       size_t i;
+       int used = 0;
+#ifdef SYMBOL_PREFIX
+       char *name_buf = 0;
+       size_t name_alloced_size = 0;
+#endif
+#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+       int gpl;
+
+       gpl = obj_gpl_license(f, NULL) == 0;
+#endif
+       for (i = 0, s = syms; i < nsyms; ++i, ++s) {
+               /* Only add symbols that are already marked external.
+                  If we override locals we may cause problems for
+                  argument initialization.  We will also create a false
+                  dependency on the module.  */
+               struct obj_symbol *sym;
+               char *name;
+
+               /* GPL licensed modules can use symbols exported with
+                * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
+                * exported names.  Non-GPL modules never see any GPLONLY_
+                * symbols so they cannot fudge it by adding the prefix on
+                * their references.
+                */
+               if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
+#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+                       if (gpl)
+                               s->name += 8;
+                       else
+#endif
+                               continue;
+               }
+               name = (char *)s->name;
+
+#ifdef SYMBOL_PREFIX
+               /* Prepend SYMBOL_PREFIX to the symbol's name (the
+                  kernel exports `C names', but module object files
+                  reference `linker names').  */
+               size_t extra = sizeof SYMBOL_PREFIX;
+               size_t name_size = strlen (name) + extra;
+               if (name_size > name_alloced_size) {
+                       name_alloced_size = name_size * 2;
+                       name_buf = alloca (name_alloced_size);
+               }
+               strcpy (name_buf, SYMBOL_PREFIX);
+               strcpy (name_buf + extra - 1, name);
+               name = name_buf;
+#endif /* SYMBOL_PREFIX */
+
+               sym = obj_find_symbol(f, name);
+               if (sym && !(ELFW(ST_BIND) (sym->info) == STB_LOCAL)) {
+#ifdef SYMBOL_PREFIX
+                       /* Put NAME_BUF into more permanent storage.  */
+                       name = xmalloc (name_size);
+                       strcpy (name, name_buf);
+#endif
+                       sym = obj_add_symbol(f, name, -1,
+                                       ELFW(ST_INFO) (STB_GLOBAL,
+                                               STT_NOTYPE),
+                                       idx, s->value, 0);
+                       /* Did our symbol just get installed?  If so, mark the
+                          module as "used".  */
+                       if (sym->secidx == idx)
+                               used = 1;
+               }
+       }
+
+       return used;
+}
+
+static void add_kernel_symbols(struct obj_file *f)
+{
+       struct external_module *m;
+       int i, nused = 0;
+
+       /* Add module symbols first.  */
+
+       for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m)
+               if (m->nsyms
+                               && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms,
+                                       m->nsyms)) m->used = 1, ++nused;
+
+       n_ext_modules_used = nused;
+
+       /* And finally the symbols from the kernel proper.  */
+
+       if (nksyms)
+               add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
+}
+
+static char *get_modinfo_value(struct obj_file *f, const char *key)
+{
+       struct obj_section *sec;
+       char *p, *v, *n, *ep;
+       size_t klen = strlen(key);
+
+       sec = obj_find_section(f, ".modinfo");
+       if (sec == NULL)
+               return NULL;
+       p = sec->contents;
+       ep = p + sec->header.sh_size;
+       while (p < ep) {
+               v = strchr(p, '=');
+               n = strchr(p, '\0');
+               if (v) {
+                       if (p + klen == v && strncmp(p, key, klen) == 0)
+                               return v + 1;
+               } else {
+                       if (p + klen == n && strcmp(p, key) == 0)
+                               return n;
+               }
+               p = n + 1;
+       }
+
+       return NULL;
+}
+
+
+/*======================================================================*/
+/* Functions relating to module loading after 2.1.18.  */
+
+static int
+new_process_module_arguments(struct obj_file *f, int argc, char **argv)
+{
+       while (argc > 0) {
+               char *p, *q, *key, *sym_name;
+               struct obj_symbol *sym;
+               char *contents, *loc;
+               int min, max, n;
+
+               p = *argv;
+               if ((q = strchr(p, '=')) == NULL) {
+                       argc--;
+                       continue;
+               }
+
+               key = alloca(q - p + 6);
+               memcpy(key, "parm_", 5);
+               memcpy(key + 5, p, q - p);
+               key[q - p + 5] = 0;
+
+               p = get_modinfo_value(f, key);
+               key += 5;
+               if (p == NULL) {
+                       bb_error_msg("invalid parameter %s", key);
+                       return 0;
+               }
+
+#ifdef SYMBOL_PREFIX
+               sym_name = alloca (strlen (key) + sizeof SYMBOL_PREFIX);
+               strcpy (sym_name, SYMBOL_PREFIX);
+               strcat (sym_name, key);
+#else
+               sym_name = key;
+#endif
+               sym = obj_find_symbol(f, sym_name);
+
+               /* Also check that the parameter was not resolved from the kernel.  */
+               if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
+                       bb_error_msg("symbol for parameter %s not found", key);
+                       return 0;
+               }
+
+               if (isdigit(*p)) {
+                       min = strtoul(p, &p, 10);
+                       if (*p == '-')
+                               max = strtoul(p + 1, &p, 10);
+                       else
+                               max = min;
+               } else
+                       min = max = 1;
+
+               contents = f->sections[sym->secidx]->contents;
+               loc = contents + sym->value;
+               n = (*++q != '\0');
+
+               while (1) {
+                       if ((*p == 's') || (*p == 'c')) {
+                               char *str;
+
+                               /* Do C quoting if we begin with a ", else slurp the lot.  */
+                               if (*q == '"') {
+                                       char *r;
+
+                                       str = alloca(strlen(q));
+                                       for (r = str, q++; *q != '"'; ++q, ++r) {
+                                               if (*q == '\0') {
+                                                       bb_error_msg("improperly terminated string argument for %s",
+                                                                       key);
+                                                       return 0;
+                                               } else if (*q == '\\')
+                                                       switch (*++q) {
+                                                               case 'a':
+                                                                       *r = '\a';
+                                                                       break;
+                                                               case 'b':
+                                                                       *r = '\b';
+                                                                       break;
+                                                               case 'e':
+                                                                       *r = '\033';
+                                                                       break;
+                                                               case 'f':
+                                                                       *r = '\f';
+                                                                       break;
+                                                               case 'n':
+                                                                       *r = '\n';
+                                                                       break;
+                                                               case 'r':
+                                                                       *r = '\r';
+                                                                       break;
+                                                               case 't':
+                                                                       *r = '\t';
+                                                                       break;
+
+                                                               case '0':
+                                                               case '1':
+                                                               case '2':
+                                                               case '3':
+                                                               case '4':
+                                                               case '5':
+                                                               case '6':
+                                                               case '7':
+                                                                       {
+                                                                               int c = *q - '0';
+                                                                               if (q[1] >= '0' && q[1] <= '7') {
+                                                                                       c = (c * 8) + *++q - '0';
+                                                                                       if (q[1] >= '0' && q[1] <= '7')
+                                                                                               c = (c * 8) + *++q - '0';
+                                                                               }
+                                                                               *r = c;
+                                                                       }
+                                                                       break;
+
+                                                               default:
+                                                                       *r = *q;
+                                                                       break;
+                                                       } else
+                                                               *r = *q;
+                                       }
+                                       *r = '\0';
+                                       ++q;
+                               } else {
+                                       char *r;
+
+                                       /* In this case, the string is not quoted. We will break
+                                          it using the coma (like for ints). If the user wants to
+                                          include comas in a string, he just has to quote it */
+
+                                       /* Search the next coma */
+                                       r = strchr(q, ',');
+
+                                       /* Found ? */
+                                       if (r != (char *) NULL) {
+                                               /* Recopy the current field */
+                                               str = alloca(r - q + 1);
+                                               memcpy(str, q, r - q);
+
+                                               /* I don't know if it is useful, as the previous case
+                                                  doesn't nul terminate the string ??? */
+                                               str[r - q] = '\0';
+
+                                               /* Keep next fields */
+                                               q = r;
+                                       } else {
+                                               /* last string */
+                                               str = q;
+                                               q = "";
+                                       }
+                               }
+
+                               if (*p == 's') {
+                                       /* Normal string */
+                                       obj_string_patch(f, sym->secidx, loc - contents, str);
+                                       loc += tgt_sizeof_char_p;
+                               } else {
+                                       /* Array of chars (in fact, matrix !) */
+                                       unsigned long charssize;        /* size of each member */
+
+                                       /* Get the size of each member */
+                                       /* Probably we should do that outside the loop ? */
+                                       if (!isdigit(*(p + 1))) {
+                                               bb_error_msg("parameter type 'c' for %s must be followed by"
+                                                               " the maximum size", key);
+                                               return 0;
+                                       }
+                                       charssize = strtoul(p + 1, (char **) NULL, 10);
+
+                                       /* Check length */
+                                       if (strlen(str) >= charssize) {
+                                               bb_error_msg("string too long for %s (max %ld)", key,
+                                                               charssize - 1);
+                                               return 0;
+                                       }
+
+                                       /* Copy to location */
+                                       strcpy((char *) loc, str);
+                                       loc += charssize;
+                               }
+                       } else {
+                               long v = strtoul(q, &q, 0);
+                               switch (*p) {
+                                       case 'b':
+                                               *loc++ = v;
+                                               break;
+                                       case 'h':
+                                               *(short *) loc = v;
+                                               loc += tgt_sizeof_short;
+                                               break;
+                                       case 'i':
+                                               *(int *) loc = v;
+                                               loc += tgt_sizeof_int;
+                                               break;
+                                       case 'l':
+                                               *(long *) loc = v;
+                                               loc += tgt_sizeof_long;
+                                               break;
+
+                                       default:
+                                               bb_error_msg("unknown parameter type '%c' for %s", *p, key);
+                                               return 0;
+                               }
+                       }
+
+retry_end_of_value:
+                       switch (*q) {
+                               case '\0':
+                                       goto end_of_arg;
+
+                               case ' ':
+                               case '\t':
+                               case '\n':
+                               case '\r':
+                                       ++q;
+                                       goto retry_end_of_value;
+
+                               case ',':
+                                       if (++n > max) {
+                                               bb_error_msg("too many values for %s (max %d)", key, max);
+                                               return 0;
+                                       }
+                                       ++q;
+                                       break;
+
+                               default:
+                                       bb_error_msg("invalid argument syntax for %s", key);
+                                       return 0;
+                       }
+               }
+
+end_of_arg:
+               if (n < min) {
+                       bb_error_msg("too few values for %s (min %d)", key, min);
+                       return 0;
+               }
+
+               argc--, argv++;
+       }
+
+       return 1;
+}
+
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+static int new_is_module_checksummed(struct obj_file *f)
+{
+       const char *p = get_modinfo_value(f, "using_checksums");
+       if (p)
+               return atoi(p);
+       else
+               return 0;
+}
+
+/* Get the module's kernel version in the canonical integer form.  */
+
+static int
+new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+       char *p, *q;
+       int a, b, c;
+
+       p = get_modinfo_value(f, "kernel_version");
+       if (p == NULL)
+               return -1;
+       safe_strncpy(str, p, STRVERSIONLEN);
+
+       a = strtoul(p, &p, 10);
+       if (*p != '.')
+               return -1;
+       b = strtoul(p + 1, &p, 10);
+       if (*p != '.')
+               return -1;
+       c = strtoul(p + 1, &q, 10);
+       if (p + 1 == q)
+               return -1;
+
+       return a << 16 | b << 8 | c;
+}
+
+#endif   /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+
+
+/* Fetch the loaded modules, and all currently exported symbols.  */
+
+static int new_get_kernel_symbols(void)
+{
+       char *module_names, *mn;
+       struct external_module *modules, *m;
+       struct new_module_symbol *syms, *s;
+       size_t ret, bufsize, nmod, nsyms, i, j;
+
+       /* Collect the loaded modules.  */
+
+       module_names = xmalloc(bufsize = 256);
+retry_modules_load:
+       if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
+               if (errno == ENOSPC && bufsize < ret) {
+                       module_names = xrealloc(module_names, bufsize = ret);
+                       goto retry_modules_load;
+               }
+               bb_perror_msg("QM_MODULES");
+               return 0;
+       }
+
+       n_ext_modules = nmod = ret;
+
+       /* Collect the modules' symbols.  */
+
+       if (nmod){
+               ext_modules = modules = xmalloc(nmod * sizeof(*modules));
+               memset(modules, 0, nmod * sizeof(*modules));
+               for (i = 0, mn = module_names, m = modules;
+                               i < nmod; ++i, ++m, mn += strlen(mn) + 1) {
+                       struct new_module_info info;
+
+                       if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) {
+                               if (errno == ENOENT) {
+                                       /* The module was removed out from underneath us.  */
+                                       continue;
+                               }
+                               bb_perror_msg("query_module: QM_INFO: %s", mn);
+                               return 0;
+                       }
+
+                       syms = xmalloc(bufsize = 1024);
+retry_mod_sym_load:
+                       if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) {
+                               switch (errno) {
+                                       case ENOSPC:
+                                               syms = xrealloc(syms, bufsize = ret);
+                                               goto retry_mod_sym_load;
+                                       case ENOENT:
+                                               /* The module was removed out from underneath us.  */
+                                               continue;
+                                       default:
+                                               bb_perror_msg("query_module: QM_SYMBOLS: %s", mn);
+                                               return 0;
+                               }
+                       }
+                       nsyms = ret;
+
+                       m->name = mn;
+                       m->addr = info.addr;
+                       m->nsyms = nsyms;
+                       m->syms = syms;
+
+                       for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+                               s->name += (unsigned long) syms;
+                       }
+               }
+       }
+
+       /* Collect the kernel's symbols.  */
+
+       syms = xmalloc(bufsize = 16 * 1024);
+retry_kern_sym_load:
+       if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) {
+               if (errno == ENOSPC && bufsize < ret) {
+                       syms = xrealloc(syms, bufsize = ret);
+                       goto retry_kern_sym_load;
+               }
+               bb_perror_msg("kernel: QM_SYMBOLS");
+               return 0;
+       }
+       nksyms = nsyms = ret;
+       ksyms = syms;
+
+       for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+               s->name += (unsigned long) syms;
+       }
+       return 1;
+}
+
+
+/* Return the kernel symbol checksum version, or zero if not used.  */
+
+static int new_is_kernel_checksummed(void)
+{
+       struct new_module_symbol *s;
+       size_t i;
+
+       /* Using_Versions is not the first symbol, but it should be in there.  */
+
+       for (i = 0, s = ksyms; i < nksyms; ++i, ++s)
+               if (strcmp((char *) s->name, "Using_Versions") == 0)
+                       return s->value;
+
+       return 0;
+}
+
+
+static int new_create_this_module(struct obj_file *f, const char *m_name)
+{
+       struct obj_section *sec;
+
+       sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long,
+                       sizeof(struct new_module));
+       memset(sec->contents, 0, sizeof(struct new_module));
+
+       obj_add_symbol(f, SPFX "__this_module", -1,
+                       ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0,
+                       sizeof(struct new_module));
+
+       obj_string_patch(f, sec->idx, offsetof(struct new_module, name),
+                       m_name);
+
+       return 1;
+}
+
+#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add an entry to the __ksymtab section, creating it if necessary */
+static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
+{
+       struct obj_section *sec;
+       ElfW(Addr) ofs;
+
+       /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.
+        * If __ksymtab is defined but not marked alloc, x out the first character
+        * (no obj_delete routine) and create a new __ksymtab with the correct
+        * characteristics.
+        */
+       sec = obj_find_section(f, "__ksymtab");
+       if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {
+               *((char *)(sec->name)) = 'x';   /* override const */
+               sec = NULL;
+       }
+       if (!sec)
+               sec = obj_create_alloced_section(f, "__ksymtab",
+                               tgt_sizeof_void_p, 0);
+       if (!sec)
+               return;
+       sec->header.sh_flags |= SHF_ALLOC;
+       sec->header.sh_addralign = tgt_sizeof_void_p;   /* Empty section might
+                                                                                                          be byte-aligned */
+       ofs = sec->header.sh_size;
+       obj_symbol_patch(f, sec->idx, ofs, sym);
+       obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);
+       obj_extend_section(sec, 2 * tgt_sizeof_char_p);
+}
+#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+static int new_create_module_ksymtab(struct obj_file *f)
+{
+       struct obj_section *sec;
+       int i;
+
+       /* We must always add the module references.  */
+
+       if (n_ext_modules_used) {
+               struct new_module_ref *dep;
+               struct obj_symbol *tm;
+
+               sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
+                               (sizeof(struct new_module_ref)
+                                * n_ext_modules_used));
+               if (!sec)
+                       return 0;
+
+               tm = obj_find_symbol(f, SPFX "__this_module");
+               dep = (struct new_module_ref *) sec->contents;
+               for (i = 0; i < n_ext_modules; ++i)
+                       if (ext_modules[i].used) {
+                               dep->dep = ext_modules[i].addr;
+                               obj_symbol_patch(f, sec->idx,
+                                               (char *) &dep->ref - sec->contents, tm);
+                               dep->next_ref = 0;
+                               ++dep;
+                       }
+       }
+
+       if (flag_export && !obj_find_section(f, "__ksymtab")) {
+               size_t nsyms;
+               int *loaded;
+
+               sec =
+                       obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p,
+                                       0);
+
+               /* We don't want to export symbols residing in sections that
+                  aren't loaded.  There are a number of these created so that
+                  we make sure certain module options don't appear twice.  */
+
+               loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+               while (--i >= 0)
+                       loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+               for (nsyms = i = 0; i < HASH_BUCKETS; ++i) {
+                       struct obj_symbol *sym;
+                       for (sym = f->symtab[i]; sym; sym = sym->next)
+                               if (ELFW(ST_BIND) (sym->info) != STB_LOCAL
+                                               && sym->secidx <= SHN_HIRESERVE
+                                               && (sym->secidx >= SHN_LORESERVE
+                                                       || loaded[sym->secidx])) {
+                                       ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p;
+
+                                       obj_symbol_patch(f, sec->idx, ofs, sym);
+                                       obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p,
+                                                       sym->name);
+
+                                       nsyms++;
+                               }
+               }
+
+               obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p);
+       }
+
+       return 1;
+}
+
+
+static int
+new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
+{
+       struct new_module *module;
+       struct obj_section *sec;
+       void *image;
+       int ret;
+       tgt_long m_addr;
+
+       sec = obj_find_section(f, ".this");
+       if (!sec || !sec->contents) {
+               bb_perror_msg_and_die("corrupt module %s?",m_name);
+       }
+       module = (struct new_module *) sec->contents;
+       m_addr = sec->header.sh_addr;
+
+       module->size_of_struct = sizeof(*module);
+       module->size = m_size;
+       module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
+
+       sec = obj_find_section(f, "__ksymtab");
+       if (sec && sec->header.sh_size) {
+               module->syms = sec->header.sh_addr;
+               module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
+       }
+
+       if (n_ext_modules_used) {
+               sec = obj_find_section(f, ".kmodtab");
+               module->deps = sec->header.sh_addr;
+               module->ndeps = n_ext_modules_used;
+       }
+
+       module->init =
+               obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module"));
+       module->cleanup =
+               obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module"));
+
+       sec = obj_find_section(f, "__ex_table");
+       if (sec) {
+               module->ex_table_start = sec->header.sh_addr;
+               module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
+       }
+
+       sec = obj_find_section(f, ".text.init");
+       if (sec) {
+               module->runsize = sec->header.sh_addr - m_addr;
+       }
+       sec = obj_find_section(f, ".data.init");
+       if (sec) {
+               if (!module->runsize ||
+                               module->runsize > sec->header.sh_addr - m_addr)
+                       module->runsize = sec->header.sh_addr - m_addr;
+       }
+       sec = obj_find_section(f, ARCHDATA_SEC_NAME);
+       if (sec && sec->header.sh_size) {
+               module->archdata_start = (void*)sec->header.sh_addr;
+               module->archdata_end = module->archdata_start + sec->header.sh_size;
+       }
+       sec = obj_find_section(f, KALLSYMS_SEC_NAME);
+       if (sec && sec->header.sh_size) {
+               module->kallsyms_start = (void*)sec->header.sh_addr;
+               module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
+       }
+
+       /* Whew!  All of the initialization is complete.  Collect the final
+          module image and give it to the kernel.  */
+
+       image = xmalloc(m_size);
+       obj_create_image(f, image);
+
+       ret = init_module(m_name, (struct new_module *) image);
+       if (ret)
+               bb_perror_msg("init_module: %s", m_name);
+
+       free(image);
+
+       return ret == 0;
+}
+
+
+/*======================================================================*/
+
+static int
+obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                                const char *string)
+{
+       struct obj_string_patch *p;
+       struct obj_section *strsec;
+       size_t len = strlen(string) + 1;
+       char *loc;
+
+       p = xmalloc(sizeof(*p));
+       p->next = f->string_patches;
+       p->reloc_secidx = secidx;
+       p->reloc_offset = offset;
+       f->string_patches = p;
+
+       strsec = obj_find_section(f, ".kstrtab");
+       if (strsec == NULL) {
+               strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);
+               p->string_offset = 0;
+               loc = strsec->contents;
+       } else {
+               p->string_offset = strsec->header.sh_size;
+               loc = obj_extend_section(strsec, len);
+       }
+       memcpy(loc, string, len);
+
+       return 1;
+}
+
+static int
+obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+                                struct obj_symbol *sym)
+{
+       struct obj_symbol_patch *p;
+
+       p = xmalloc(sizeof(*p));
+       p->next = f->symbol_patches;
+       p->reloc_secidx = secidx;
+       p->reloc_offset = offset;
+       p->sym = sym;
+       f->symbol_patches = p;
+
+       return 1;
+}
+
+static int obj_check_undefineds(struct obj_file *f)
+{
+       unsigned long i;
+       int ret = 1;
+
+       for (i = 0; i < HASH_BUCKETS; ++i) {
+               struct obj_symbol *sym;
+               for (sym = f->symtab[i]; sym; sym = sym->next)
+                       if (sym->secidx == SHN_UNDEF) {
+                               if (ELFW(ST_BIND) (sym->info) == STB_WEAK) {
+                                       sym->secidx = SHN_ABS;
+                                       sym->value = 0;
+                               } else {
+                                       if (!flag_quiet) {
+                                               bb_error_msg("unresolved symbol %s", sym->name);
+                                       }
+                                       ret = 0;
+                               }
+                       }
+       }
+
+       return ret;
+}
+
+static void obj_allocate_commons(struct obj_file *f)
+{
+       struct common_entry {
+               struct common_entry *next;
+               struct obj_symbol *sym;
+       } *common_head = NULL;
+
+       unsigned long i;
+
+       for (i = 0; i < HASH_BUCKETS; ++i) {
+               struct obj_symbol *sym;
+               for (sym = f->symtab[i]; sym; sym = sym->next)
+                       if (sym->secidx == SHN_COMMON) {
+                               /* Collect all COMMON symbols and sort them by size so as to
+                                  minimize space wasted by alignment requirements.  */
+                               {
+                                       struct common_entry **p, *n;
+                                       for (p = &common_head; *p; p = &(*p)->next)
+                                               if (sym->size <= (*p)->sym->size)
+                                                       break;
+
+                                       n = alloca(sizeof(*n));
+                                       n->next = *p;
+                                       n->sym = sym;
+                                       *p = n;
+                               }
+                       }
+       }
+
+       for (i = 1; i < f->local_symtab_size; ++i) {
+               struct obj_symbol *sym = f->local_symtab[i];
+               if (sym && sym->secidx == SHN_COMMON) {
+                       struct common_entry **p, *n;
+                       for (p = &common_head; *p; p = &(*p)->next)
+                               if (sym == (*p)->sym)
+                                       break;
+                               else if (sym->size < (*p)->sym->size) {
+                                       n = alloca(sizeof(*n));
+                                       n->next = *p;
+                                       n->sym = sym;
+                                       *p = n;
+                                       break;
+                               }
+               }
+       }
+
+       if (common_head) {
+               /* Find the bss section.  */
+               for (i = 0; i < f->header.e_shnum; ++i)
+                       if (f->sections[i]->header.sh_type == SHT_NOBITS)
+                               break;
+
+               /* If for some reason there hadn't been one, create one.  */
+               if (i == f->header.e_shnum) {
+                       struct obj_section *sec;
+
+                       f->sections = xrealloc(f->sections, (i + 1) * sizeof(sec));
+                       f->sections[i] = sec = arch_new_section();
+                       f->header.e_shnum = i + 1;
+
+                       memset(sec, 0, sizeof(*sec));
+                       sec->header.sh_type = SHT_PROGBITS;
+                       sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+                       sec->name = ".bss";
+                       sec->idx = i;
+               }
+
+               /* Allocate the COMMONS.  */
+               {
+                       ElfW(Addr) bss_size = f->sections[i]->header.sh_size;
+                       ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;
+                       struct common_entry *c;
+
+                       for (c = common_head; c; c = c->next) {
+                               ElfW(Addr) align = c->sym->value;
+
+                               if (align > max_align)
+                                       max_align = align;
+                               if (bss_size & (align - 1))
+                                       bss_size = (bss_size | (align - 1)) + 1;
+
+                               c->sym->secidx = i;
+                               c->sym->value = bss_size;
+
+                               bss_size += c->sym->size;
+                       }
+
+                       f->sections[i]->header.sh_size = bss_size;
+                       f->sections[i]->header.sh_addralign = max_align;
+               }
+       }
+
+       /* For the sake of patch relocation and parameter initialization,
+          allocate zeroed data for NOBITS sections now.  Note that after
+          this we cannot assume NOBITS are really empty.  */
+       for (i = 0; i < f->header.e_shnum; ++i) {
+               struct obj_section *s = f->sections[i];
+               if (s->header.sh_type == SHT_NOBITS) {
+                       if (s->header.sh_size != 0)
+                               s->contents = memset(xmalloc(s->header.sh_size),
+                                               0, s->header.sh_size);
+                       else
+                               s->contents = NULL;
+
+                       s->header.sh_type = SHT_PROGBITS;
+               }
+       }
+}
+
+static unsigned long obj_load_size(struct obj_file *f)
+{
+       unsigned long dot = 0;
+       struct obj_section *sec;
+
+       /* Finalize the positions of the sections relative to one another.  */
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+               ElfW(Addr) align;
+
+               align = sec->header.sh_addralign;
+               if (align && (dot & (align - 1)))
+                       dot = (dot | (align - 1)) + 1;
+
+               sec->header.sh_addr = dot;
+               dot += sec->header.sh_size;
+       }
+
+       return dot;
+}
+
+static int obj_relocate(struct obj_file *f, ElfW(Addr) base)
+{
+       int i, n = f->header.e_shnum;
+       int ret = 1;
+
+       /* Finalize the addresses of the sections.  */
+
+       f->baseaddr = base;
+       for (i = 0; i < n; ++i)
+               f->sections[i]->header.sh_addr += base;
+
+       /* And iterate over all of the relocations.  */
+
+       for (i = 0; i < n; ++i) {
+               struct obj_section *relsec, *symsec, *targsec, *strsec;
+               ElfW(RelM) * rel, *relend;
+               ElfW(Sym) * symtab;
+               const char *strtab;
+
+               relsec = f->sections[i];
+               if (relsec->header.sh_type != SHT_RELM)
+                       continue;
+
+               symsec = f->sections[relsec->header.sh_link];
+               targsec = f->sections[relsec->header.sh_info];
+               strsec = f->sections[symsec->header.sh_link];
+
+               rel = (ElfW(RelM) *) relsec->contents;
+               relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+               symtab = (ElfW(Sym) *) symsec->contents;
+               strtab = (const char *) strsec->contents;
+
+               for (; rel < relend; ++rel) {
+                       ElfW(Addr) value = 0;
+                       struct obj_symbol *intsym = NULL;
+                       unsigned long symndx;
+                       ElfW(Sym) * extsym = 0;
+                       const char *errmsg;
+
+                       /* Attempt to find a value to use for this relocation.  */
+
+                       symndx = ELFW(R_SYM) (rel->r_info);
+                       if (symndx) {
+                               /* Note we've already checked for undefined symbols.  */
+
+                               extsym = &symtab[symndx];
+                               if (ELFW(ST_BIND) (extsym->st_info) == STB_LOCAL) {
+                                       /* Local symbols we look up in the local table to be sure
+                                          we get the one that is really intended.  */
+                                       intsym = f->local_symtab[symndx];
+                               } else {
+                                       /* Others we look up in the hash table.  */
+                                       const char *name;
+                                       if (extsym->st_name)
+                                               name = strtab + extsym->st_name;
+                                       else
+                                               name = f->sections[extsym->st_shndx]->name;
+                                       intsym = obj_find_symbol(f, name);
+                               }
+
+                               value = obj_symbol_final_value(f, intsym);
+                               intsym->referenced = 1;
+                       }
+#if SHT_RELM == SHT_RELA
+#if defined(__alpha__) && defined(AXP_BROKEN_GAS)
+                       /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9.  */
+                       if (!extsym || !extsym->st_name ||
+                                       ELFW(ST_BIND) (extsym->st_info) != STB_LOCAL)
+#endif
+                               value += rel->r_addend;
+#endif
+
+                       /* Do it! */
+                       switch (arch_apply_relocation
+                                       (f, targsec, symsec, intsym, rel, value)) {
+                               case obj_reloc_ok:
+                                       break;
+
+                               case obj_reloc_overflow:
+                                       errmsg = "Relocation overflow";
+                                       goto bad_reloc;
+                               case obj_reloc_dangerous:
+                                       errmsg = "Dangerous relocation";
+                                       goto bad_reloc;
+                               case obj_reloc_unhandled:
+                                       errmsg = "Unhandled relocation";
+bad_reloc:
+                                       if (extsym) {
+                                               bb_error_msg("%s of type %ld for %s", errmsg,
+                                                               (long) ELFW(R_TYPE) (rel->r_info),
+                                                               strtab + extsym->st_name);
+                                       } else {
+                                               bb_error_msg("%s of type %ld", errmsg,
+                                                               (long) ELFW(R_TYPE) (rel->r_info));
+                                       }
+                                       ret = 0;
+                                       break;
+                       }
+               }
+       }
+
+       /* Finally, take care of the patches.  */
+
+       if (f->string_patches) {
+               struct obj_string_patch *p;
+               struct obj_section *strsec;
+               ElfW(Addr) strsec_base;
+               strsec = obj_find_section(f, ".kstrtab");
+               strsec_base = strsec->header.sh_addr;
+
+               for (p = f->string_patches; p; p = p->next) {
+                       struct obj_section *targsec = f->sections[p->reloc_secidx];
+                       *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+                               = strsec_base + p->string_offset;
+               }
+       }
+
+       if (f->symbol_patches) {
+               struct obj_symbol_patch *p;
+
+               for (p = f->symbol_patches; p; p = p->next) {
+                       struct obj_section *targsec = f->sections[p->reloc_secidx];
+                       *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+                               = obj_symbol_final_value(f, p->sym);
+               }
+       }
+
+       return ret;
+}
+
+static int obj_create_image(struct obj_file *f, char *image)
+{
+       struct obj_section *sec;
+       ElfW(Addr) base = f->baseaddr;
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+               char *secimg;
+
+               if (sec->contents == 0 || sec->header.sh_size == 0)
+                       continue;
+
+               secimg = image + (sec->header.sh_addr - base);
+
+               /* Note that we allocated data for NOBITS sections earlier.  */
+               memcpy(secimg, sec->contents, sec->header.sh_size);
+       }
+
+       return 1;
+}
+
+/*======================================================================*/
+
+static struct obj_file *obj_load(FILE * fp, int loadprogbits)
+{
+       struct obj_file *f;
+       ElfW(Shdr) * section_headers;
+       int shnum, i;
+       char *shstrtab;
+
+       /* Read the file header.  */
+
+       f = arch_new_file();
+       memset(f, 0, sizeof(*f));
+       f->symbol_cmp = strcmp;
+       f->symbol_hash = obj_elf_hash;
+       f->load_order_search_start = &f->load_order;
+
+       fseek(fp, 0, SEEK_SET);
+       if (fread(&f->header, sizeof(f->header), 1, fp) != 1) {
+               bb_perror_msg("error reading ELF header");
+               return NULL;
+       }
+
+       if (f->header.e_ident[EI_MAG0] != ELFMAG0
+                       || f->header.e_ident[EI_MAG1] != ELFMAG1
+                       || f->header.e_ident[EI_MAG2] != ELFMAG2
+                       || f->header.e_ident[EI_MAG3] != ELFMAG3) {
+               bb_error_msg("not an ELF file");
+               return NULL;
+       }
+       if (f->header.e_ident[EI_CLASS] != ELFCLASSM
+                       || f->header.e_ident[EI_DATA] != ELFDATAM
+                       || f->header.e_ident[EI_VERSION] != EV_CURRENT
+                       || !MATCH_MACHINE(f->header.e_machine)) {
+               bb_error_msg("ELF file not for this architecture");
+               return NULL;
+       }
+       if (f->header.e_type != ET_REL) {
+               bb_error_msg("ELF file not a relocatable object");
+               return NULL;
+       }
+
+       /* Read the section headers.  */
+
+       if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
+               bb_error_msg("section header size mismatch: %lu != %lu",
+                               (unsigned long) f->header.e_shentsize,
+                               (unsigned long) sizeof(ElfW(Shdr)));
+               return NULL;
+       }
+
+       shnum = f->header.e_shnum;
+       f->sections = xmalloc(sizeof(struct obj_section *) * shnum);
+       memset(f->sections, 0, sizeof(struct obj_section *) * shnum);
+
+       section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
+       fseek(fp, f->header.e_shoff, SEEK_SET);
+       if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) {
+               bb_perror_msg("error reading ELF section headers");
+               return NULL;
+       }
+
+       /* Read the section data.  */
+
+       for (i = 0; i < shnum; ++i) {
+               struct obj_section *sec;
+
+               f->sections[i] = sec = arch_new_section();
+               memset(sec, 0, sizeof(*sec));
+
+               sec->header = section_headers[i];
+               sec->idx = i;
+
+               if(sec->header.sh_size) switch (sec->header.sh_type) {
+                       case SHT_NULL:
+                       case SHT_NOTE:
+                       case SHT_NOBITS:
+                               /* ignore */
+                               break;
+
+                       case SHT_PROGBITS:
+#if LOADBITS
+                               if (!loadprogbits) {
+                                       sec->contents = NULL;
+                                       break;
+                               }
+#endif
+                       case SHT_SYMTAB:
+                       case SHT_STRTAB:
+                       case SHT_RELM:
+                               if (sec->header.sh_size > 0) {
+                                       sec->contents = xmalloc(sec->header.sh_size);
+                                       fseek(fp, sec->header.sh_offset, SEEK_SET);
+                                       if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
+                                               bb_perror_msg("error reading ELF section data");
+                                               return NULL;
+                                       }
+                               } else {
+                                       sec->contents = NULL;
+                               }
+                               break;
+
+#if SHT_RELM == SHT_REL
+                       case SHT_RELA:
+                               bb_error_msg("RELA relocations not supported on this architecture");
+                               return NULL;
+#else
+                       case SHT_REL:
+                               bb_error_msg("REL relocations not supported on this architecture");
+                               return NULL;
+#endif
+
+                       default:
+                               if (sec->header.sh_type >= SHT_LOPROC) {
+                                       /* Assume processor specific section types are debug
+                                          info and can safely be ignored.  If this is ever not
+                                          the case (Hello MIPS?), don't put ifdefs here but
+                                          create an arch_load_proc_section().  */
+                                       break;
+                               }
+
+                               bb_error_msg("can't handle sections of type %ld",
+                                               (long) sec->header.sh_type);
+                               return NULL;
+               }
+       }
+
+       /* Do what sort of interpretation as needed by each section.  */
+
+       shstrtab = f->sections[f->header.e_shstrndx]->contents;
+
+       for (i = 0; i < shnum; ++i) {
+               struct obj_section *sec = f->sections[i];
+               sec->name = shstrtab + sec->header.sh_name;
+       }
+
+       for (i = 0; i < shnum; ++i) {
+               struct obj_section *sec = f->sections[i];
+
+               /* .modinfo should be contents only but gcc has no attribute for that.
+                * The kernel may have marked .modinfo as ALLOC, ignore this bit.
+                */
+               if (strcmp(sec->name, ".modinfo") == 0)
+                       sec->header.sh_flags &= ~SHF_ALLOC;
+
+               if (sec->header.sh_flags & SHF_ALLOC)
+                       obj_insert_section_load_order(f, sec);
+
+               switch (sec->header.sh_type) {
+                       case SHT_SYMTAB:
+                               {
+                                       unsigned long nsym, j;
+                                       char *strtab;
+                                       ElfW(Sym) * sym;
+
+                                       if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
+                                               bb_error_msg("symbol size mismatch: %lu != %lu",
+                                                               (unsigned long) sec->header.sh_entsize,
+                                                               (unsigned long) sizeof(ElfW(Sym)));
+                                               return NULL;
+                                       }
+
+                                       nsym = sec->header.sh_size / sizeof(ElfW(Sym));
+                                       strtab = f->sections[sec->header.sh_link]->contents;
+                                       sym = (ElfW(Sym) *) sec->contents;
+
+                                       /* Allocate space for a table of local symbols.  */
+                                       j = f->local_symtab_size = sec->header.sh_info;
+                                       f->local_symtab = xcalloc(j, sizeof(struct obj_symbol *));
+
+                                       /* Insert all symbols into the hash table.  */
+                                       for (j = 1, ++sym; j < nsym; ++j, ++sym) {
+                                               ElfW(Addr) val = sym->st_value;
+                                               const char *name;
+                                               if (sym->st_name)
+                                                       name = strtab + sym->st_name;
+                                               else if (sym->st_shndx < shnum)
+                                                       name = f->sections[sym->st_shndx]->name;
+                                               else
+                                                       continue;
+
+#if defined(__SH5__)
+                                               /*
+                                                * For sh64 it is possible that the target of a branch
+                                                * requires a mode switch (32 to 16 and back again).
+                                                *
+                                                * This is implied by the lsb being set in the target
+                                                * address for SHmedia mode and clear for SHcompact.
+                                                */
+                                               val |= sym->st_other & 4;
+#endif
+
+                                               obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
+                                                               val, sym->st_size);
+                                       }
+                               }
+                               break;
+
+                       case SHT_RELM:
+                               if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
+                                       bb_error_msg("relocation entry size mismatch: %lu != %lu",
+                                                       (unsigned long) sec->header.sh_entsize,
+                                                       (unsigned long) sizeof(ElfW(RelM)));
+                                       return NULL;
+                               }
+                               break;
+                               /* XXX  Relocation code from modutils-2.3.19 is not here.
+                                * Why?  That's about 20 lines of code from obj/obj_load.c,
+                                * which gets done in a second pass through the sections.
+                                * This BusyBox insmod does similar work in obj_relocate(). */
+               }
+       }
+
+       return f;
+}
+
+#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM
+/*
+ * load the unloaded sections directly into the memory allocated by
+ * kernel for the module
+ */
+
+static int obj_load_progbits(FILE * fp, struct obj_file* f, char* imagebase)
+{
+       ElfW(Addr) base = f->baseaddr;
+       struct obj_section* sec;
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+
+               /* section already loaded? */
+               if (sec->contents != NULL)
+                       continue;
+
+               if (sec->header.sh_size == 0)
+                       continue;
+
+               sec->contents = imagebase + (sec->header.sh_addr - base);
+               fseek(fp, sec->header.sh_offset, SEEK_SET);
+               if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
+                       bb_error_msg("error reading ELF section data: %s\n", strerror(errno));
+                       return 0;
+               }
+
+       }
+       return 1;
+}
+#endif
+
+static void hide_special_symbols(struct obj_file *f)
+{
+       static const char *const specials[] = {
+               SPFX "cleanup_module",
+               SPFX "init_module",
+               SPFX "kernel_version",
+               NULL
+       };
+
+       struct obj_symbol *sym;
+       const char *const *p;
+
+       for (p = specials; *p; ++p)
+               if ((sym = obj_find_symbol(f, *p)) != NULL)
+                       sym->info =
+                               ELFW(ST_INFO) (STB_LOCAL, ELFW(ST_TYPE) (sym->info));
+}
+
+
+#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license)
+{
+       struct obj_section *sec;
+       /* This list must match *exactly* the list of allowable licenses in
+        * linux/include/linux/module.h.  Checking for leading "GPL" will not
+        * work, somebody will use "GPL sucks, this is proprietary".
+        */
+       static const char *gpl_licenses[] = {
+               "GPL",
+               "GPL v2",
+               "GPL and additional rights",
+               "Dual BSD/GPL",
+               "Dual MPL/GPL",
+       };
+
+       if ((sec = obj_find_section(f, ".modinfo"))) {
+               const char *value, *ptr, *endptr;
+               ptr = sec->contents;
+               endptr = ptr + sec->header.sh_size;
+               while (ptr < endptr) {
+                       if ((value = strchr(ptr, '=')) && strncmp(ptr, "license", value-ptr) == 0) {
+                               int i;
+                               if (license)
+                                       *license = value+1;
+                               for (i = 0; i < sizeof(gpl_licenses)/sizeof(gpl_licenses[0]); ++i) {
+                                       if (strcmp(value+1, gpl_licenses[i]) == 0)
+                                               return(0);
+                               }
+                               return(2);
+                       }
+                       if (strchr(ptr, '\0'))
+                               ptr = strchr(ptr, '\0') + 1;
+                       else
+                               ptr = endptr;
+               }
+       }
+       return(1);
+}
+
+#define TAINT_FILENAME                  "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE        (1<<0)
+#define TAINT_FORCED_MODULE             (1<<1)
+#define TAINT_UNSAFE_SMP                (1<<2)
+#define TAINT_URL                                              "http://www.tux.org/lkml/#export-tainted"
+
+static void set_tainted(struct obj_file *f, int fd, char *m_name,
+               int kernel_has_tainted, int taint, const char *text1, const char *text2)
+{
+       char buf[80];
+       int oldval;
+       static int first = 1;
+       if (fd < 0 && !kernel_has_tainted)
+               return;         /* New modutils on old kernel */
+       printf("Warning: loading %s will taint the kernel: %s%s\n",
+                       m_name, text1, text2);
+       if (first) {
+               printf("  See %s for information about tainted modules\n", TAINT_URL);
+               first = 0;
+       }
+       if (fd >= 0) {
+               read(fd, buf, sizeof(buf)-1);
+               buf[sizeof(buf)-1] = '\0';
+               oldval = strtoul(buf, NULL, 10);
+               sprintf(buf, "%d\n", oldval | taint);
+               write(fd, buf, strlen(buf));
+       }
+}
+
+/* Check if loading this module will taint the kernel. */
+static void check_tainted_module(struct obj_file *f, char *m_name)
+{
+       static const char tainted_file[] = TAINT_FILENAME;
+       int fd, kernel_has_tainted;
+       const char *ptr;
+
+       kernel_has_tainted = 1;
+       if ((fd = open(tainted_file, O_RDWR)) < 0) {
+               if (errno == ENOENT)
+                       kernel_has_tainted = 0;
+               else if (errno == EACCES)
+                       kernel_has_tainted = 1;
+               else {
+                       perror(tainted_file);
+                       kernel_has_tainted = 0;
+               }
+       }
+
+       switch (obj_gpl_license(f, &ptr)) {
+               case 0:
+                       break;
+               case 1:
+                       set_tainted(f, fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
+                       break;
+               case 2:
+                       /* The module has a non-GPL license so we pretend that the
+                        * kernel always has a taint flag to get a warning even on
+                        * kernels without the proc flag.
+                        */
+                       set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
+                       break;
+               default:
+                       set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
+                       break;
+       }
+
+       if (flag_force_load)
+               set_tainted(f, fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
+
+       if (fd >= 0)
+               close(fd);
+}
+#else /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */
+#define check_tainted_module(x, y) do { } while(0);
+#endif /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */
+
+#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections.  this info is used by ksymoops to do better
+ * debugging.
+ */
+static int
+get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+       return new_get_module_version(f, str);
+#else  /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+       strncpy(str, "???", sizeof(str));
+       return -1;
+#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+}
+
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections.  this info is used by ksymoops to do better
+ * debugging.
+ */
+static void
+add_ksymoops_symbols(struct obj_file *f, const char *filename,
+                                const char *m_name)
+{
+       static const char symprefix[] = "__insmod_";
+       struct obj_section *sec;
+       struct obj_symbol *sym;
+       char *name, *absolute_filename;
+       char str[STRVERSIONLEN], real[PATH_MAX];
+       int i, l, lm_name, lfilename, use_ksymtab, version;
+       struct stat statbuf;
+
+       static const char *section_names[] = {
+               ".text",
+               ".rodata",
+               ".data",
+               ".bss"
+                       ".sbss"
+       };
+
+       if (realpath(filename, real)) {
+               absolute_filename = bb_xstrdup(real);
+       }
+       else {
+               int save_errno = errno;
+               bb_error_msg("cannot get realpath for %s", filename);
+               errno = save_errno;
+               perror("");
+               absolute_filename = bb_xstrdup(filename);
+       }
+
+       lm_name = strlen(m_name);
+       lfilename = strlen(absolute_filename);
+
+       /* add to ksymtab if it already exists or there is no ksymtab and other symbols
+        * are not to be exported.  otherwise leave ksymtab alone for now, the
+        * "export all symbols" compatibility code will export these symbols later.
+        */
+       use_ksymtab =  obj_find_section(f, "__ksymtab") || !flag_export;
+
+       if ((sec = obj_find_section(f, ".this"))) {
+               /* tag the module header with the object name, last modified
+                * timestamp and module version.  worst case for module version
+                * is 0xffffff, decimal 16777215.  putting all three fields in
+                * one symbol is less readable but saves kernel space.
+                */
+               l = sizeof(symprefix)+                  /* "__insmod_" */
+                       lm_name+                                /* module name */
+                       2+                                      /* "_O" */
+                       lfilename+                              /* object filename */
+                       2+                                      /* "_M" */
+                       2*sizeof(statbuf.st_mtime)+             /* mtime in hex */
+                       2+                                      /* "_V" */
+                       8+                                      /* version in dec */
+                       1;                                      /* nul */
+               name = xmalloc(l);
+               if (stat(absolute_filename, &statbuf) != 0)
+                       statbuf.st_mtime = 0;
+               version = get_module_version(f, str);   /* -1 if not found */
+               snprintf(name, l, "%s%s_O%s_M%0*lX_V%d",
+                               symprefix, m_name, absolute_filename,
+                               (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime,
+                               version);
+               sym = obj_add_symbol(f, name, -1,
+                               ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+                               sec->idx, sec->header.sh_addr, 0);
+               if (use_ksymtab)
+                       new_add_ksymtab(f, sym);
+       }
+       free(absolute_filename);
+#ifdef _NOT_SUPPORTED_
+       /* record where the persistent data is going, same address as previous symbol */
+
+       if (f->persist) {
+               l = sizeof(symprefix)+          /* "__insmod_" */
+                       lm_name+                /* module name */
+                       2+                      /* "_P" */
+                       strlen(f->persist)+     /* data store */
+                       1;                      /* nul */
+               name = xmalloc(l);
+               snprintf(name, l, "%s%s_P%s",
+                               symprefix, m_name, f->persist);
+               sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+                               sec->idx, sec->header.sh_addr, 0);
+               if (use_ksymtab)
+                       new_add_ksymtab(f, sym);
+       }
+#endif /* _NOT_SUPPORTED_ */
+       /* tag the desired sections if size is non-zero */
+
+       for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) {
+               if ((sec = obj_find_section(f, section_names[i])) &&
+                               sec->header.sh_size) {
+                       l = sizeof(symprefix)+          /* "__insmod_" */
+                               lm_name+                /* module name */
+                               2+                      /* "_S" */
+                               strlen(sec->name)+      /* section name */
+                               2+                      /* "_L" */
+                               8+                      /* length in dec */
+                               1;                      /* nul */
+                       name = xmalloc(l);
+                       snprintf(name, l, "%s%s_S%s_L%ld",
+                                       symprefix, m_name, sec->name,
+                                       (long)sec->header.sh_size);
+                       sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+                                       sec->idx, sec->header.sh_addr, 0);
+                       if (use_ksymtab)
+                               new_add_ksymtab(f, sym);
+               }
+       }
+}
+#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+static void print_load_map(struct obj_file *f)
+{
+       struct obj_symbol *sym;
+       struct obj_symbol **all, **p;
+       struct obj_section *sec;
+       int i, nsyms, *loaded;
+
+       /* Report on the section layout.  */
+
+       printf("Sections:       Size      %-*s  Align\n",
+                       (int) (2 * sizeof(void *)), "Address");
+
+       for (sec = f->load_order; sec; sec = sec->load_next) {
+               int a;
+               unsigned long tmp;
+
+               for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
+                       tmp >>= 1;
+               if (a == -1)
+                       a = 0;
+
+               printf("%-15s %08lx  %0*lx  2**%d\n",
+                               sec->name,
+                               (long)sec->header.sh_size,
+                               (int) (2 * sizeof(void *)),
+                               (long)sec->header.sh_addr,
+                               a);
+       }
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL
+       /* Quick reference which section indicies are loaded.  */
+
+       loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+       while (--i >= 0)
+               loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+       /* Collect the symbols we'll be listing.  */
+
+       for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
+               for (sym = f->symtab[i]; sym; sym = sym->next)
+                       if (sym->secidx <= SHN_HIRESERVE
+                                       && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+                               ++nsyms;
+
+       all = alloca(nsyms * sizeof(struct obj_symbol *));
+
+       for (i = 0, p = all; i < HASH_BUCKETS; ++i)
+               for (sym = f->symtab[i]; sym; sym = sym->next)
+                       if (sym->secidx <= SHN_HIRESERVE
+                                       && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+                               *p++ = sym;
+
+       /* And list them.  */
+       printf("\nSymbols:\n");
+       for (p = all; p < all + nsyms; ++p) {
+               char type = '?';
+               unsigned long value;
+
+               sym = *p;
+               if (sym->secidx == SHN_ABS) {
+                       type = 'A';
+                       value = sym->value;
+               } else if (sym->secidx == SHN_UNDEF) {
+                       type = 'U';
+                       value = 0;
+               } else {
+                       sec = f->sections[sym->secidx];
+
+                       if (sec->header.sh_type == SHT_NOBITS)
+                               type = 'B';
+                       else if (sec->header.sh_flags & SHF_ALLOC) {
+                               if (sec->header.sh_flags & SHF_EXECINSTR)
+                                       type = 'T';
+                               else if (sec->header.sh_flags & SHF_WRITE)
+                                       type = 'D';
+                               else
+                                       type = 'R';
+                       }
+                       value = sym->value + sec->header.sh_addr;
+               }
+
+               if (ELFW(ST_BIND) (sym->info) == STB_LOCAL)
+                       type = tolower(type);
+
+               printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
+                               type, sym->name);
+       }
+#endif
+}
+
+#endif
+
+extern int insmod_main( int argc, char **argv)
+{
+       int opt;
+       int len;
+       int k_crcs;
+       char *tmp, *tmp1;
+       unsigned long m_size;
+       ElfW(Addr) m_addr;
+       struct obj_file *f;
+       struct stat st;
+       char *m_name = 0;
+       int exit_status = EXIT_FAILURE;
+       int m_has_modinfo;
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+       struct utsname uts_info;
+       char m_strversion[STRVERSIONLEN];
+       int m_version, m_crcs;
+#endif
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       FILE *fp = 0;
+#else
+       FILE *fp;
+#endif
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+       int flag_print_load_map = 0;
+#endif
+       int k_version = 0;
+       struct utsname myuname;
+
+       /* Parse any options */
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+       while ((opt = getopt(argc, argv, "fkqsvxmLo:")) > 0)
+#else
+       while ((opt = getopt(argc, argv, "fkqsvxLo:")) > 0)
+#endif
+               {
+                       switch (opt) {
+                               case 'f':                       /* force loading */
+                                       flag_force_load = 1;
+                                       break;
+                               case 'k':                       /* module loaded by kerneld, auto-cleanable */
+                                       flag_autoclean = 1;
+                                       break;
+                               case 's':                       /* log to syslog */
+                                       /* log to syslog -- not supported              */
+                                       /* but kernel needs this for request_module(), */
+                                       /* as this calls: modprobe -k -s -- <module>   */
+                                       /* so silently ignore this flag                */
+                                       break;
+                               case 'v':                       /* verbose output */
+                                       flag_verbose = 1;
+                                       break;
+                               case 'q':                       /* silent */
+                                       flag_quiet = 1;
+                                       break;
+                               case 'x':                       /* do not export externs */
+                                       flag_export = 0;
+                                       break;
+                               case 'o':                       /* name the output module */
+                                       free(m_name);
+                                       m_name = bb_xstrdup(optarg);
+                                       break;
+                               case 'L':                       /* Stub warning */
+                                       /* This is needed for compatibility with modprobe.
+                                        * In theory, this does locking, but we don't do
+                                        * that.  So be careful and plan your life around not
+                                        * loading the same module 50 times concurrently. */
+                                       break;
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+                               case 'm':                       /* print module load map */
+                                       flag_print_load_map = 1;
+                                       break;
+#endif
+                               default:
+                                       bb_show_usage();
+                       }
+               }
+
+       if (argv[optind] == NULL) {
+               bb_show_usage();
+       }
+
+       /* Grab the module name */
+       tmp1 = bb_xstrdup(argv[optind]);
+       tmp = basename(tmp1);
+       len = strlen(tmp);
+
+       if (uname(&myuname) == 0) {
+               if (myuname.release[0] == '2') {
+                       k_version = myuname.release[2] - '0';
+               }
+       }
+
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+       if (k_version > 4 && len > 3 && tmp[len - 3] == '.' &&
+                       tmp[len - 2] == 'k' && tmp[len - 1] == 'o') {
+               len-=3;
+               tmp[len] = '\0';
+       }
+       else
+#endif
+               if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') {
+                       len-=2;
+                       tmp[len] = '\0';
+               }
+
+
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+       if (k_version > 4)
+               bb_xasprintf(&m_fullName, "%s.ko", tmp);
+       else
+#endif
+               bb_xasprintf(&m_fullName, "%s.o", tmp);
+
+       if (!m_name) {
+               m_name = tmp;
+       } else {
+               free(tmp1);
+               tmp1 = 0;       /* flag for free(m_name) before exit() */
+       }
+
+       /* Get a filedesc for the module.  Check we we have a complete path */
+       if (stat(argv[optind], &st) < 0 || !S_ISREG(st.st_mode) ||
+                       (fp = fopen(argv[optind], "r")) == NULL) {
+               /* Hmm.  Could not open it.  First search under /lib/modules/`uname -r`,
+                * but do not error out yet if we fail to find it... */
+               if (k_version) {        /* uname succeedd */
+                       char *module_dir;
+                       char *tmdn;
+                       char real_module_dir[FILENAME_MAX];
+
+                       tmdn = concat_path_file(_PATH_MODULES, myuname.release);
+                       /* Jump through hoops in case /lib/modules/`uname -r`
+                        * is a symlink.  We do not want recursive_action to
+                        * follow symlinks, but we do want to follow the
+                        * /lib/modules/`uname -r` dir, So resolve it ourselves
+                        * if it is a link... */
+                       if (realpath (tmdn, real_module_dir) == NULL)
+                               module_dir = tmdn;
+                       else
+                               module_dir = real_module_dir;
+                       recursive_action(module_dir, TRUE, FALSE, FALSE,
+                                       check_module_name_match, 0, m_fullName);
+                       free(tmdn);
+               }
+
+               /* Check if we have found anything yet */
+               if (m_filename == 0 || ((fp = fopen(m_filename, "r")) == NULL))
+               {
+                       char module_dir[FILENAME_MAX];
+
+                       free(m_filename);
+                       m_filename = 0;
+                       if (realpath (_PATH_MODULES, module_dir) == NULL)
+                               strcpy(module_dir, _PATH_MODULES);
+                       /* No module found under /lib/modules/`uname -r`, this
+                        * time cast the net a bit wider.  Search /lib/modules/ */
+                       if (! recursive_action(module_dir, TRUE, FALSE, FALSE,
+                                               check_module_name_match, 0, m_fullName))
+                       {
+                               if (m_filename == 0
+                                               || ((fp = fopen(m_filename, "r")) == NULL))
+                               {
+                                       bb_error_msg("%s: no module by that name found", m_fullName);
+                                       goto out;
+                               }
+                       } else
+                               bb_error_msg_and_die("%s: no module by that name found", m_fullName);
+               }
+       } else
+               m_filename = bb_xstrdup(argv[optind]);
+
+       if (!flag_quiet)
+               printf("Using %s\n", m_filename);
+
+#ifdef CONFIG_FEATURE_2_6_MODULES
+       if (k_version > 4)
+       {
+               optind--;
+               argv[optind + 1] = m_filename;
+               return insmod_ng_main(argc - optind, argv + optind);
+       }
+#endif
+
+       if ((f = obj_load(fp, LOADBITS)) == NULL)
+               bb_perror_msg_and_die("Could not load the module");
+
+       if (get_modinfo_value(f, "kernel_version") == NULL)
+               m_has_modinfo = 0;
+       else
+               m_has_modinfo = 1;
+
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+       /* Version correspondence?  */
+       if (!flag_quiet) {
+               if (uname(&uts_info) < 0)
+                       uts_info.release[0] = '\0';
+               if (m_has_modinfo) {
+                       m_version = new_get_module_version(f, m_strversion);
+                       if (m_version == -1) {
+                               bb_error_msg("couldn't find the kernel version the module was "
+                                               "compiled for");
+                               goto out;
+                       }
+               }
+
+               if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) {
+                       if (flag_force_load) {
+                               bb_error_msg("Warning: kernel-module version mismatch\n"
+                                               "\t%s was compiled for kernel version %s\n"
+                                               "\twhile this kernel is version %s",
+                                               m_filename, m_strversion, uts_info.release);
+                       } else {
+                               bb_error_msg("kernel-module version mismatch\n"
+                                               "\t%s was compiled for kernel version %s\n"
+                                               "\twhile this kernel is version %s.",
+                                               m_filename, m_strversion, uts_info.release);
+                               goto out;
+                       }
+               }
+       }
+       k_crcs = 0;
+#endif                                                 /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+
+       if (!query_module(NULL, 0, NULL, 0, NULL)) {
+               if (!new_get_kernel_symbols())
+                       goto out;
+               k_crcs = new_is_kernel_checksummed();
+       } else {
+               bb_error_msg("Not configured to support old kernels");
+               goto out;
+       }
+
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+       m_crcs = 0;
+       if (m_has_modinfo)
+               m_crcs = new_is_module_checksummed(f);
+
+       if (m_crcs != k_crcs)
+               obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
+#endif                                                 /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+
+       /* Let the module know about the kernel symbols.  */
+       add_kernel_symbols(f);
+
+       /* Allocate common symbols, symbol tables, and string tables.  */
+
+       if (!new_create_this_module(f, m_name))
+       {
+               goto out;
+       }
+
+       if (!obj_check_undefineds(f)) {
+               goto out;
+       }
+       obj_allocate_commons(f);
+       check_tainted_module(f, m_name);
+
+       /* done with the module name, on to the optional var=value arguments */
+       ++optind;
+
+       if (optind < argc) {
+               if (!new_process_module_arguments(f, argc - optind, argv + optind))
+               {
+                       goto out;
+               }
+       }
+
+       arch_create_got(f);
+       hide_special_symbols(f);
+
+#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+       add_ksymoops_symbols(f, m_filename, m_name);
+#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+       new_create_module_ksymtab(f);
+
+       /* Find current size of the module */
+       m_size = obj_load_size(f);
+
+
+       m_addr = create_module(m_name, m_size);
+       if (m_addr == -1) switch (errno) {
+               case EEXIST:
+                       bb_error_msg("A module named %s already exists", m_name);
+                       goto out;
+               case ENOMEM:
+                       bb_error_msg("Can't allocate kernel memory for module; needed %lu bytes",
+                                       m_size);
+                       goto out;
+               default:
+                       bb_perror_msg("create_module: %s", m_name);
+                       goto out;
+       }
+
+#if  !LOADBITS
+       /*
+        * the PROGBITS section was not loaded by the obj_load
+        * now we can load them directly into the kernel memory
+        */
+       if (!obj_load_progbits(fp, f, (char*)m_addr)) {
+               delete_module(m_name);
+               goto out;
+       }
+#endif
+
+       if (!obj_relocate(f, m_addr)) {
+               delete_module(m_name);
+               goto out;
+       }
+
+       if (!new_init_module(m_name, f, m_size))
+       {
+               delete_module(m_name);
+               goto out;
+       }
+
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+       if(flag_print_load_map)
+               print_load_map(f);
+#endif
+
+       exit_status = EXIT_SUCCESS;
+
+out:
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       if(fp)
+               fclose(fp);
+       if(tmp1) {
+               free(tmp1);
+       } else {
+               free(m_name);
+       }
+       free(m_filename);
+#endif
+       return(exit_status);
+}
+
+
+#endif
+
+
+#ifdef CONFIG_FEATURE_2_6_MODULES
+
+#include <sys/mman.h>
+#include <asm/unistd.h>
+#include <sys/syscall.h>
+
+/* We use error numbers in a loose translation... */
+static const char *moderror(int err)
+{
+       switch (err) {
+               case ENOEXEC:
+                       return "Invalid module format";
+               case ENOENT:
+                       return "Unknown symbol in module";
+               case ESRCH:
+                       return "Module has wrong symbol version";
+               case EINVAL:
+                       return "Invalid parameters";
+               default:
+                       return strerror(err);
+       }
+}
+
+extern int insmod_ng_main( int argc, char **argv)
+{
+       int i;
+       int fd;
+       long int ret;
+       struct stat st;
+       unsigned long len;
+       void *map;
+       char *filename, *options = bb_xstrdup("");
+
+       filename = argv[1];
+       if (!filename) {
+               bb_show_usage();
+               return -1;
+       }
+
+       /* Rest is options */
+       for (i = 2; i < argc; i++) {
+               options = xrealloc(options, strlen(options) + 2 + strlen(argv[i]) + 2);
+               /* Spaces handled by "" pairs, but no way of escaping quotes */
+               if (strchr(argv[i], ' ')) {
+                       strcat(options, "\"");
+                       strcat(options, argv[i]);
+                       strcat(options, "\"");
+               } else {
+                       strcat(options, argv[i]);
+               }
+               strcat(options, " ");
+       }
+
+       if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+               bb_perror_msg_and_die("cannot open module `%s'", filename);
+       }
+
+       fstat(fd, &st);
+       len = st.st_size;
+       map = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+       if (map == MAP_FAILED) {
+               bb_perror_msg_and_die("cannot mmap `%s'", filename);
+       }
+
+       ret = syscall(__NR_init_module, map, len, options);
+       if (ret != 0) {
+               bb_perror_msg_and_die("cannot insert `%s': %s (%li)",
+                               filename, moderror(errno), ret);
+       }
+
+       return 0;
+}
+
+#endif
diff --git a/busybox/modutils/lsmod.c b/busybox/modutils/lsmod.c
new file mode 100644 (file)
index 0000000..7bf314a
--- /dev/null
@@ -0,0 +1,174 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini lsmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
+ * Nicolas Ferre <nicolas.ferre@alcove.fr> to support pre 2.1 kernels
+ * (which lack the query_module() interface).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <assert.h>
+#include <getopt.h>
+#include <sys/utsname.h>
+#include <sys/file.h>
+#include "busybox.h"
+
+
+#ifndef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+static inline void check_tainted(void) { printf("\n"); }
+#else
+#define TAINT_FILENAME                  "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE        (1<<0)
+#define TAINT_FORCED_MODULE             (1<<1)
+#define TAINT_UNSAFE_SMP                (1<<2)
+
+static void check_tainted(void)
+{
+       int tainted;
+       FILE *f;
+
+       tainted = 0;
+       if ((f = fopen(TAINT_FILENAME, "r"))) {
+               fscanf(f, "%d", &tainted);
+               fclose(f);
+       }
+       if (f && tainted) {
+               printf("    Tainted: %c%c%c\n",
+                               tainted & TAINT_PROPRIETORY_MODULE      ? 'P' : 'G',
+                               tainted & TAINT_FORCED_MODULE           ? 'F' : ' ',
+                               tainted & TAINT_UNSAFE_SMP              ? 'S' : ' ');
+       }
+       else {
+               printf("    Not tainted\n");
+       }
+}
+#endif
+
+#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+
+struct module_info
+{
+       unsigned long addr;
+       unsigned long size;
+       unsigned long flags;
+       long usecount;
+};
+
+
+int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
+
+/* Values for query_module's which.  */
+static const int QM_MODULES = 1;
+static const int QM_DEPS = 2;
+static const int QM_REFS = 3;
+static const int QM_SYMBOLS = 4;
+static const int QM_INFO = 5;
+
+/* Bits of module.flags.  */
+static const int NEW_MOD_RUNNING = 1;
+static const int NEW_MOD_DELETED = 2;
+static const int NEW_MOD_AUTOCLEAN = 4;
+static const int NEW_MOD_VISITED = 8;
+static const int NEW_MOD_USED_ONCE = 16;
+static const int NEW_MOD_INITIALIZING = 64;
+
+extern int lsmod_main(int argc, char **argv)
+{
+       struct module_info info;
+       char *module_names, *mn, *deps, *dn;
+       size_t bufsize, depsize, nmod, count, i, j;
+
+       module_names = xmalloc(bufsize = 256);
+       if (my_query_module(NULL, QM_MODULES, (void **)&module_names, &bufsize,
+                               &nmod)) {
+               bb_perror_msg_and_die("QM_MODULES");
+       }
+
+       deps = xmalloc(depsize = 256);
+       printf("Module                  Size  Used by");
+       check_tainted();
+
+       for (i = 0, mn = module_names; i < nmod; mn += strlen(mn) + 1, i++) {
+               if (query_module(mn, QM_INFO, &info, sizeof(info), &count)) {
+                       if (errno == ENOENT) {
+                               /* The module was removed out from underneath us. */
+                               continue;
+                       }
+                       /* else choke */
+                       bb_perror_msg_and_die("module %s: QM_INFO", mn);
+               }
+               if (my_query_module(mn, QM_REFS, (void **)&deps, &depsize, &count)) {
+                       if (errno == ENOENT) {
+                               /* The module was removed out from underneath us. */
+                               continue;
+                       }
+                       bb_perror_msg_and_die("module %s: QM_REFS", mn);
+               }
+               printf("%-20s%8lu%4ld", mn, info.size, info.usecount);
+               if (info.flags & NEW_MOD_DELETED)
+                       printf(" (deleted)");
+               else if (info.flags & NEW_MOD_INITIALIZING)
+                       printf(" (initializing)");
+               else if (!(info.flags & NEW_MOD_RUNNING))
+                       printf(" (uninitialized)");
+               else {
+                       if (info.flags & NEW_MOD_AUTOCLEAN)
+                               printf(" (autoclean) ");
+                       if (!(info.flags & NEW_MOD_USED_ONCE))
+                               printf(" (unused)");
+               }
+               if (count) printf(" [");
+               for (j = 0, dn = deps; j < count; dn += strlen(dn) + 1, j++) {
+                       printf("%s%s", dn, (j==count-1)? "":" ");
+               }
+               if (count) printf("]");
+
+               printf("\n");
+       }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       free(module_names);
+#endif
+
+       return( 0);
+}
+
+#else /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
+
+extern int lsmod_main(int argc, char **argv)
+{
+       printf("Module                  Size  Used by");
+       check_tainted();
+
+       if (bb_xprint_file_by_name("/proc/modules") < 0) {
+               return 0;
+       }
+       return 1;
+}
+
+#endif /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
diff --git a/busybox/modutils/modprobe.c b/busybox/modutils/modprobe.c
new file mode 100644 (file)
index 0000000..83244fc
--- /dev/null
@@ -0,0 +1,654 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Modprobe written from scratch for BusyBox
+ *
+ * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
+ * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+#include <sys/utsname.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include "busybox.h"
+
+
+
+struct dep_t {
+       char *  m_name;
+       char *  m_path;
+       char *  m_options;
+
+       int     m_isalias  : 1;
+       int     m_reserved : 15;
+
+       int     m_depcnt   : 16;
+       char ** m_deparr;
+
+       struct dep_t * m_next;
+};
+
+struct mod_list_t {
+       char *  m_name;
+       char *  m_path;
+       char *  m_options;
+
+       struct mod_list_t * m_prev;
+       struct mod_list_t * m_next;
+};
+
+
+static struct dep_t *depend;
+static int autoclean, show_only, quiet, do_syslog, verbose;
+static int k_version;
+
+int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
+{
+       char *tag, *value;
+
+       while ( isspace ( *buffer ))
+               buffer++;
+       tag = value = buffer;
+       while ( !isspace ( *value ))
+               if (!*value) return 0;
+               else value++;
+       *value++ = 0;
+       while ( isspace ( *value ))
+               value++;
+       if (!*value) return 0;
+
+       *ptag = tag;
+       *pvalue = value;
+
+       return 1;
+}
+
+/* Jump through hoops to simulate how fgets() grabs just one line at a
+ * time... Don't use any stdio since modprobe gets called from a kernel
+ * thread and stdio junk can overflow the limited stack...
+ */
+static char *reads ( int fd, char *buffer, size_t len )
+{
+       int n = read ( fd, buffer, len );
+
+       if ( n > 0 ) {
+               char *p;
+
+               buffer [len-1] = 0;
+               p = strchr ( buffer, '\n' );
+
+               if ( p ) {
+                       off_t offset;
+
+                       offset = lseek ( fd, 0L, SEEK_CUR );               // Get the current file descriptor offset
+                       lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
+
+                       p[1] = 0;
+               }
+               return buffer;
+       }
+
+       else
+               return 0;
+}
+
+static struct dep_t *build_dep ( void )
+{
+       int fd;
+       struct utsname un;
+       struct dep_t *first = 0;
+       struct dep_t *current = 0;
+       char buffer[2048];
+       char *filename = buffer;
+       int continuation_line = 0;
+
+       k_version = 0;
+       if ( uname ( &un ))
+               return 0;
+
+       // check for buffer overflow in following code
+       if ( bb_strlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
+               return 0;
+       }
+       if (un.release[0] == '2') {
+               k_version = un.release[2] - '0';
+       }
+
+       strcpy ( filename, "/lib/modules/" );
+       strcat ( filename, un.release );
+       strcat ( filename, "/modules.dep" );
+
+       if (( fd = open ( filename, O_RDONLY )) < 0 ) {
+
+               /* Ok, that didn't work.  Fall back to looking in /lib/modules */
+               if (( fd = open ( "/lib/modules/modules.dep", O_RDONLY )) < 0 ) {
+                       return 0;
+               }
+       }
+
+       while ( reads ( fd, buffer, sizeof( buffer ))) {
+               int l = bb_strlen ( buffer );
+               char *p = 0;
+
+               while ( isspace ( buffer [l-1] )) {
+                       buffer [l-1] = 0;
+                       l--;
+               }
+
+               if ( l == 0 ) {
+                       continuation_line = 0;
+                       continue;
+               }
+
+               if ( !continuation_line ) {
+                       char *col = strchr ( buffer, ':' );
+                       char *dot = col;
+
+                       if ( col ) {
+                               char *mods;
+                               char *modpath;
+                               char *mod;
+
+                               *col = 0;
+                               mods = strrchr ( buffer, '/' );
+
+                               if ( !mods )
+                                       mods = buffer;
+                               else
+                                       mods++;
+
+                               modpath = strchr ( buffer, '/' );
+                               if ( !modpath )
+                                       modpath = buffer;
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+                               if ((k_version > 4) && ( *(col-3) == '.' ) &&
+                                               ( *(col-2) == 'k' ) && ( *(col-1) == 'o' ))
+                                       dot = col - 3;
+                               else
+#endif
+                                       if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
+                                               dot = col - 2;
+
+                               mod = bb_xstrndup ( mods, dot - mods );
+
+                               if ( !current ) {
+                                       first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
+                               }
+                               else {
+                                       current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
+                                       current = current-> m_next;
+                               }
+                               current-> m_name  = mod;
+                               current-> m_path  = bb_xstrdup(modpath);
+                               current-> m_options = 0;
+                               current-> m_isalias = 0;
+                               current-> m_depcnt  = 0;
+                               current-> m_deparr  = 0;
+                               current-> m_next    = 0;
+
+                               //printf ( "%s:\n", mod );
+                               p = col + 1;
+                       }
+                       else
+                               p = 0;
+               }
+               else
+                       p = buffer;
+
+               while ( p && *p && isblank(*p))
+                       p++;
+
+               if ( p && *p ) {
+                       char *end = &buffer [l-1];
+                       char *deps;
+                       char *dep;
+                       char *next;
+                       int ext = 0;
+
+                       while ( isblank ( *end ) || ( *end == '\\' ))
+                               end--;
+
+                       do
+                       {
+                               next = strchr (p, ' ' );
+                               if (next)
+                               {
+                                       *next = 0;
+                                       next--;
+                               }
+                               else
+                                       next = end;
+
+                               deps = strrchr ( p, '/' );
+
+                               if ( !deps || ( deps < p )) {
+                                       deps = p;
+
+                                       while ( isblank ( *deps ))
+                                               deps++;
+                               }
+                               else
+                                       deps++;
+
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+                               if ((k_version > 4) && ( *(next-2) == '.' ) && *(next-1) == 'k'  &&
+                                               ( *next == 'o' ))
+                                       ext = 3;
+                               else
+#endif
+                                       if (( *(next-1) == '.' ) && ( *next == 'o' ))
+                                               ext = 2;
+
+                               /* Cope with blank lines */
+                               if ((next-deps-ext+1) <= 0)
+                                       continue;
+                               dep = bb_xstrndup ( deps, next - deps - ext + 1 );
+
+                               current-> m_depcnt++;
+                               current-> m_deparr = (char **) xrealloc ( current-> m_deparr,
+                                               sizeof ( char *) * current-> m_depcnt );
+                               current-> m_deparr [current-> m_depcnt - 1] = dep;
+
+                               //printf ( "    %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
+                               p = next + 2;
+                       } while (next < end);
+               }
+
+               if ( buffer [l-1] == '\\' )
+                       continuation_line = 1;
+               else
+                       continuation_line = 0;
+       }
+       close ( fd );
+
+       // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
+
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+       if (( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 )
+#endif
+               if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
+                       if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
+                               return first;
+
+       continuation_line = 0;
+       while ( reads ( fd, buffer, sizeof( buffer ))) {
+               int l;
+               char *p;
+
+               p = strchr ( buffer, '#' );
+               if ( p )
+                       *p = 0;
+
+               l = bb_strlen ( buffer );
+
+               while ( l && isspace ( buffer [l-1] )) {
+                       buffer [l-1] = 0;
+                       l--;
+               }
+
+               if ( l == 0 ) {
+                       continuation_line = 0;
+                       continue;
+               }
+
+               if ( !continuation_line ) {
+                       if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
+                               char *alias, *mod;
+
+                               if ( parse_tag_value ( buffer + 6, &alias, &mod )) {
+                                       // fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
+
+                                       if ( !current ) {
+                                               first = current = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
+                                       }
+                                       else {
+                                               current-> m_next = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
+                                               current = current-> m_next;
+                                       }
+                                       current-> m_name  = bb_xstrdup ( alias );
+                                       current-> m_isalias = 1;
+
+                                       if (( strcmp ( mod, "off" ) == 0 ) || ( strcmp ( mod, "null" ) == 0 )) {
+                                               current-> m_depcnt = 0;
+                                               current-> m_deparr = 0;
+                                       }
+                                       else {
+                                               current-> m_depcnt  = 1;
+                                               current-> m_deparr  = xmalloc ( 1 * sizeof( char * ));
+                                               current-> m_deparr[0] = bb_xstrdup ( mod );
+                                       }
+                                       current-> m_next    = 0;
+                               }
+                       }
+                       else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) {
+                               char *mod, *opt;
+
+                               if ( parse_tag_value ( buffer + 8, &mod, &opt )) {
+                                       struct dep_t *dt;
+
+                                       for ( dt = first; dt; dt = dt-> m_next ) {
+                                               if ( strcmp ( dt-> m_name, mod ) == 0 )
+                                                       break;
+                                       }
+                                       if ( dt ) {
+                                               dt-> m_options = xrealloc ( dt-> m_options, bb_strlen( opt ) + 1 );
+                                               strcpy ( dt-> m_options, opt );
+
+                                               // fprintf ( stderr, "OPTION: '%s' -> '%s'\n", dt-> m_name, dt-> m_options );
+                                       }
+                               }
+                       }
+               }
+       }
+       close ( fd );
+
+       return first;
+}
+
+/* return 1 = loaded, 0 = not loaded, -1 = can't tell */
+static int already_loaded (const char *name)
+{
+       int fd;
+       char buffer[4096];
+
+       fd = open ("/proc/modules", O_RDONLY);
+       if (fd < 0)
+               return -1;
+
+       while ( reads ( fd, buffer, sizeof( buffer ))) {
+               char *p;
+
+               p = strchr (buffer, ' ');
+               if (p) {
+                       *p = 0;
+                       if (strcmp (name, buffer) == 0) {
+                               close (fd);
+                               return 1;
+                       }
+               }
+       }
+
+       close (fd);
+       return 0;
+}
+
+static int mod_process ( struct mod_list_t *list, int do_insert )
+{
+       char lcmd [4096];
+       int rc = 0;
+
+       while ( list ) {
+               *lcmd = '\0';
+               if ( do_insert ) {
+                       if (already_loaded (list->m_name) != 1)
+                               snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s",
+                                               do_syslog ? "-s" : "", autoclean ? "-k" : "",
+                                               quiet ? "-q" : "", list-> m_path, list-> m_options ?
+                                               list-> m_options : "" );
+               } else {
+                       /* modutils uses short name for removal */
+                       if (already_loaded (list->m_name) != 0)
+                               snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s",
+                                               do_syslog ? "-s" : "", list-> m_name );
+               }
+
+               if (*lcmd) {
+                       if (verbose) {
+                               printf("%s\n", lcmd);
+                       }
+                       if (!show_only) {
+                               int rc2 = system(lcmd);
+                               if (do_insert) {
+                                       rc = rc2; /* only last module matters */
+                               }
+                               else if (!rc2) {
+                                       rc = 0; /* success if remove any mod */
+                               }
+                       }
+               }
+               list = do_insert ? list-> m_prev : list-> m_next;
+       }
+       return (show_only) ? 0 : rc;
+}
+
+static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
+{
+       struct mod_list_t *find;
+       struct dep_t *dt;
+       char *opt = 0;
+       char *path = 0;
+
+       // check dependencies
+       for ( dt = depend; dt; dt = dt-> m_next ) {
+               if ( strcmp ( dt-> m_name, mod ) == 0) {
+                       mod = dt-> m_name;
+                       path = dt-> m_path;
+                       opt = dt-> m_options;
+                       break;
+               }
+       }
+
+       // resolve alias names
+       while ( dt && dt-> m_isalias ) {
+               if ( dt-> m_depcnt == 1 ) {
+                       struct dep_t *adt;
+
+                       for ( adt = depend; adt; adt = adt-> m_next ) {
+                               if ( strcmp ( adt-> m_name, dt-> m_deparr [0] ) == 0 )
+                                       break;
+                       }
+                       if ( adt ) {
+                               dt = adt;
+                               mod = dt-> m_name;
+                               path = dt-> m_path;
+                               if ( !opt )
+                                       opt = dt-> m_options;
+                       }
+                       else
+                               return;
+               }
+               else
+                       return;
+       }
+
+       if ( !path ) {
+               bb_error_msg ("module %s not found.", mod);
+               return;
+       }
+
+       // search for duplicates
+       for ( find = *head; find; find = find-> m_next ) {
+               if ( !strcmp ( mod, find-> m_name )) {
+                       // found -> dequeue it
+
+                       if ( find-> m_prev )
+                               find-> m_prev-> m_next = find-> m_next;
+                       else
+                               *head = find-> m_next;
+
+                       if ( find-> m_next )
+                               find-> m_next-> m_prev = find-> m_prev;
+                       else
+                               *tail = find-> m_prev;
+
+                       break; // there can be only one duplicate
+               }
+       }
+
+       if ( !find ) { // did not find a duplicate
+               find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));
+               find-> m_name = mod;
+               find-> m_path = path;
+               find-> m_options = opt;
+       }
+
+       // enqueue at tail
+       if ( *tail )
+               (*tail)-> m_next = find;
+       find-> m_prev   = *tail;
+       find-> m_next   = 0;
+
+       if ( !*head )
+               *head = find;
+       *tail = find;
+
+       if ( dt ) {
+               int i;
+
+               for ( i = 0; i < dt-> m_depcnt; i++ )
+                       check_dep ( dt-> m_deparr [i], head, tail );
+       }
+}
+
+
+
+static int mod_insert ( char *mod, int argc, char **argv )
+{
+       struct mod_list_t *tail = 0;
+       struct mod_list_t *head = 0;
+       int rc;
+
+       // get dep list for module mod
+       check_dep ( mod, &head, &tail );
+
+       if ( head && tail ) {
+#if defined(CONFIG_FEATURE_2_6_MODULES)
+               if ( argc ) {
+                       int i;
+                       int l = 0;
+
+                       // append module args
+                       for ( i = 0; i < argc; i++ )
+                               l += ( bb_strlen ( argv [i] ) + 1 );
+
+                       head-> m_options = xrealloc ( head-> m_options, l + 1 );
+                       head-> m_options [0] = 0;
+
+                       for ( i = 0; i < argc; i++ ) {
+                               strcat ( head-> m_options, argv [i] );
+                               strcat ( head-> m_options, " " );
+                       }
+               }
+#endif
+
+               // process tail ---> head
+               rc = mod_process ( tail, 1 );
+       }
+       else
+               rc = 1;
+
+       return rc;
+}
+
+static int mod_remove ( char *mod )
+{
+       int rc;
+       static struct mod_list_t rm_a_dummy = { "-a", 0, 0 };
+
+       struct mod_list_t *head = 0;
+       struct mod_list_t *tail = 0;
+
+       if ( mod )
+               check_dep ( mod, &head, &tail );
+       else  // autoclean
+               head = tail = &rm_a_dummy;
+
+       if ( head && tail )
+               rc = mod_process ( head, 0 );  // process head ---> tail
+       else
+               rc = 1;
+       return rc;
+
+}
+
+extern int modprobe_main(int argc, char** argv)
+{
+       int     opt;
+       int remove_opt = 0;
+
+       autoclean = show_only = quiet = do_syslog = verbose = 0;
+
+       while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
+               switch(opt) {
+                       case 'c': // no config used
+                       case 'l': // no pattern matching
+                               return EXIT_SUCCESS;
+                               break;
+                       case 'C': // no config used
+                       case 't': // no pattern matching
+                               bb_error_msg_and_die("-t and -C not supported");
+
+                       case 'a': // ignore
+                       case 'd': // ignore
+                               break;
+                       case 'k':
+                               autoclean++;
+                               break;
+                       case 'n':
+                               show_only++;
+                               break;
+                       case 'q':
+                               quiet++;
+                               break;
+                       case 'r':
+                               remove_opt++;
+                               break;
+                       case 's':
+                               do_syslog++;
+                               break;
+                       case 'v':
+                               verbose++;
+                               break;
+                       case 'V':
+                       default:
+                               bb_show_usage();
+                               break;
+               }
+       }
+
+       depend = build_dep ( );
+
+       if ( !depend )
+               bb_error_msg_and_die ( "could not parse modules.dep\n" );
+
+       if (remove_opt) {
+               int rc = EXIT_SUCCESS;
+               do {
+                       if (mod_remove ( optind < argc ?
+                                               bb_xstrdup (argv [optind]) : NULL )) {
+                               bb_error_msg ("failed to remove module %s",
+                                               argv [optind] );
+                               rc = EXIT_FAILURE;
+                       }
+               } while ( ++optind < argc );
+
+               return rc;
+       }
+
+       if (optind >= argc)
+               bb_error_msg_and_die ( "No module or pattern provided\n" );
+
+       if ( mod_insert ( bb_xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 ))
+               bb_error_msg_and_die ( "failed to load module %s", argv [optind] );
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/modutils/rmmod.c b/busybox/modutils/rmmod.c
new file mode 100644 (file)
index 0000000..f4e65d0
--- /dev/null
@@ -0,0 +1,124 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rmmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_2_6_MODULES
+static inline void filename2modname(char *modname, const char *filename)
+{
+       const char *afterslash;
+       unsigned int i;
+
+       afterslash = strrchr(filename, '/');
+       if (!afterslash)
+               afterslash = filename;
+       else
+               afterslash++;
+
+       /* Convert to underscores, stop at first . */
+       for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) {
+               if (afterslash[i] == '-')
+                       modname[i] = '_';
+               else
+                       modname[i] = afterslash[i];
+       }
+       modname[i] = '\0';
+}
+#endif
+
+extern int rmmod_main(int argc, char **argv)
+{
+       int n, ret = EXIT_SUCCESS;
+       size_t nmod = 0; /* number of modules */
+       size_t pnmod = -1; /* previous number of modules */
+       unsigned int flags = O_NONBLOCK|O_EXCL;
+#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+       void *buf; /* hold the module names which we ignore but must get */
+       size_t bufsize = 0;
+#endif
+
+       /* Parse command line. */
+       while ((n = getopt(argc, argv, "a")) != EOF) {
+               switch (n) {
+                       case 'w':       // --wait
+                               flags &= ~O_NONBLOCK;
+                               break;
+                       case 'f':       // --force
+                               flags |= O_TRUNC;
+                               break;
+                       case 'a':
+                               /* Unload _all_ unused modules via NULL delete_module() call */
+                               /* until the number of modules does not change */
+#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+                               buf = xmalloc(bufsize = 256);
+#endif
+                               while (nmod != pnmod) {
+                                       if (syscall(__NR_delete_module, NULL, flags) < 0) {
+                                               if (errno==EFAULT)
+                                                       return(ret);
+                                               bb_perror_msg_and_die("rmmod");
+                                       }
+                                       pnmod = nmod;
+#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+                                       /* 1 == QM_MODULES */
+                                       if (my_query_module(NULL, 1, &buf, &bufsize, &nmod)) {
+                                               bb_perror_msg_and_die("QM_MODULES");
+                                       }
+#endif
+                               }
+#if defined CONFIG_FEATURE_CLEAN_UP && CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+                               free(buf);
+#endif
+                               return EXIT_SUCCESS;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       if (optind == argc)
+               bb_show_usage();
+
+       {
+               for (n = optind; n < argc; n++) {
+#ifdef CONFIG_FEATURE_2_6_MODULES
+                       char module_name[strlen(argv[n]) + 1];
+                       filename2modname(module_name, argv[n]);
+#else
+#define module_name            argv[n]
+#endif
+                       if (syscall(__NR_delete_module, module_name, flags) < 0) {
+                               bb_perror_msg("%s", argv[n]);
+                               ret = EXIT_FAILURE;
+                       }
+               }
+       }
+
+       return(ret);
+}
diff --git a/busybox/networking/Config.in b/busybox/networking/Config.in
new file mode 100644 (file)
index 0000000..42176f0
--- /dev/null
@@ -0,0 +1,634 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Networking Utilities"
+
+config CONFIG_FEATURE_IPV6
+       bool "Enable IPv6 support"
+       default n
+       help
+         Enable IPv6 support to busybox. This makes applets that talk IP
+         able to work with IPv6.
+
+config CONFIG_ARPING
+       bool "arping"
+       default n
+       help
+         Ping hosts by ARP packets
+
+config CONFIG_FTPGET
+       bool "ftpget"
+       default n
+       help
+         Retrieve a remote file via FTP.
+
+config CONFIG_FTPPUT
+       bool "ftpput"
+       default n
+       help
+         Store a remote file via FTP.
+
+config CONFIG_HOSTNAME
+       bool "hostname"
+       default n
+       help
+         Show or set the system's host name
+
+config CONFIG_HTTPD
+       bool "httpd"
+       default n
+       help
+         Serve web pages via an HTTP server.
+
+config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+       bool "  Support using httpd only from inetd"
+       default n
+       depends on CONFIG_HTTPD
+       help
+         This option disables uid and port options for the httpd applet
+         but requires inetd server daemon.
+
+config CONFIG_FEATURE_HTTPD_BASIC_AUTH
+       bool "  Enable Basic http Authentication"
+       default y
+       depends on CONFIG_HTTPD
+       help
+         Utilizes password settings from /etc/httpd.conf for basic
+         authentication on a per url basis.
+
+config CONFIG_FEATURE_HTTPD_AUTH_MD5
+       bool "  Support MD5 crypted passwords for http Authentication"
+       default n
+       depends on CONFIG_FEATURE_HTTPD_BASIC_AUTH
+       help
+         Enables basic per url authentication from /etc/httpd.conf
+         using md5 passwords.
+
+
+if !CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+config CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+       bool "  Support reloading the global config file using hup signal"
+       default n
+       depends on CONFIG_HTTPD
+       help
+         This option enables processing of SIGHUP to reload cached
+         configuration settings.
+
+config CONFIG_FEATURE_HTTPD_SETUID
+       bool "  Enable support -u <user> option"
+       default n
+       depends on CONFIG_HTTPD
+       help
+         This option allows the server to run as a specific user
+         rather than defaulting to the user that starts the server.
+         Use of this option requires special privileges to change to a
+         different user.
+endif
+
+config CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+       bool "  Support loading additional MIME types at run-time"
+       default n
+       depends on CONFIG_HTTPD
+       help
+         This option enables support for additional MIME types at
+         run-time to be specified in the configuration file.
+
+config CONFIG_FEATURE_HTTPD_CGI
+       bool "  Support Common Gateway Interface (CGI)"
+       default y
+       depends on CONFIG_HTTPD
+       help
+         This option allows scripts and executables to be invoked
+         when specific urls are requested.
+
+config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+       bool "  Support the REMOTE_PORT environment variable for CGI"
+       default n
+       depends on CONFIG_FEATURE_HTTPD_CGI
+       help
+         Use of this option can assist scripts in generating
+         references that contain a unique port number.
+
+config CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+       bool "  Enable the -e option for shell script CGI simplification."
+       default y
+       depends on CONFIG_HTTPD
+       help
+         After set, this option allows html encoding arbitrary
+         strings for display of the browser.  Output goes to stdout.
+         For example, httpd -e "<Hello World>" as
+         "&#60Hello&#32World&#62".
+
+config CONFIG_IFCONFIG
+       bool "ifconfig"
+       default n
+       help
+         Ifconfig is used to configure the kernel-resident network interfaces.
+
+config CONFIG_FEATURE_IFCONFIG_STATUS
+       bool "  Enable status reporting output (+7k)"
+       default y
+       depends on CONFIG_IFCONFIG
+       help
+         If ifconfig is called with no arguments it will display the status
+         of the currently active interfaces.
+
+config CONFIG_FEATURE_IFCONFIG_SLIP
+       bool "  Enable slip-specific options \"keepalive\" and \"outfill\""
+       default n
+       depends on CONFIG_IFCONFIG
+       help
+         Allow "keepalive" and "outfill" support for SLIP.  If you're not
+         planning on using serial lines, leave this unchecked.
+
+config CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+       bool "  Enable options \"mem_start\", \"io_addr\", and \"irq\""
+       default n
+       depends on CONFIG_IFCONFIG
+       help
+         Allow the start address for shared memory, start address for I/O,
+         and/or the interrupt line used by the specified device.
+
+config CONFIG_FEATURE_IFCONFIG_HW
+       bool "  Enable option \"hw\" (ether only)"
+       default y
+       depends on CONFIG_IFCONFIG
+       help
+         Set the hardware address of this interface, if the device driver
+         supports  this  operation.  Currently, we only support the 'ether'
+         class.
+
+config CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+       bool "  Set the broadcast automatically"
+       default n
+       depends on CONFIG_IFCONFIG
+       help
+         Setting this will make ifconfig attempt to find the broadcast
+         automatically if the value '+' is used.
+
+config CONFIG_IFUPDOWN
+       bool "ifupdown"
+       default n
+       help
+         Activate or deactivate the specified interfaces.  This applet makes
+         use of either "ifconfig" and "route" or the "ip" command to actually
+         configure network interfaces.  Therefore, you will probably also want
+         to enable either CONFIG_IFCONFIG and CONFIG_ROUTE, or enable
+         CONFIG_FEATURE_IFUPDOWN_IP and the various CONFIG_IP options.  Of
+         course you could use non-busybox versions of these programs, so
+         against my better judgement (since this will surely result in plenty
+         of support questions on the mailing list), I do not force you to
+         enable these additional options.  It is up to you to supply either
+         "ifconfig" and "route" or the "ip" command, either via busybox or via
+         standalone utilities.
+
+config CONFIG_FEATURE_IFUPDOWN_IP
+       bool "  Use ip applet"
+       default n
+       depends on CONFIG_IFUPDOWN
+       help
+         Use the iproute "ip" command to implement "ifup" and "ifdown", rather
+         than the default of using the older 'ifconfig' and 'route' utilities.
+
+config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN
+       bool "  Use busybox ip applet"
+       default y
+       depends on CONFIG_FEATURE_IFUPDOWN_IP
+       select CONFIG_IP
+       select CONFIG_FEATURE_IP_ADDRESS
+       select CONFIG_FEATURE_IP_LINK
+       select CONFIG_FEATURE_IP_ROUTE
+       help
+         Use the busybox iproute "ip" applet to implement "ifupdown".
+
+         If leave this disabled, you must install the full-blown iproute2
+         utility or the  "ifup" and "ifdown" applets will not work.
+
+config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN
+       bool "  Use busybox ifconfig and route applets"
+       default y
+       depends on CONFIG_IFUPDOWN && !CONFIG_FEATURE_IFUPDOWN_IP
+       select CONFIG_IFCONFIG
+       select CONFIG_ROUTE
+       help
+         Use the busybox iproute "ifconfig" and "route" applets to
+         implement the "ifup" and "ifdown" utilities.
+
+         If leave this disabled, you must install the full-blown ifconfig
+         and route utilities, or the  "ifup" and "ifdown" applets will not
+         work.
+
+config CONFIG_FEATURE_IFUPDOWN_IPV4
+       bool "  Enable support for IPv4"
+       default y
+       depends on CONFIG_IFUPDOWN
+       help
+         If you want busybox to talk IPv4, leave this on.
+
+config CONFIG_FEATURE_IFUPDOWN_IPV6
+       bool "  Enable support for IPv6"
+       default n
+       depends on CONFIG_IFUPDOWN
+       help
+         If you need support for IPv6, turn this option on.
+
+config CONFIG_FEATURE_IFUPDOWN_IPX
+       bool "  Enable support for IPX"
+       default n
+       depends on CONFIG_IFUPDOWN
+       help
+         If this option is selected you can use busybox to work with IPX
+         networks.
+
+config CONFIG_FEATURE_IFUPDOWN_MAPPING
+       bool "  Enable mapping support"
+       default n
+       depends on CONFIG_IFUPDOWN
+       help
+         This enables support for the "mapping" stanza, unless you have
+         a weird network setup you don't need it.
+
+config CONFIG_INETD
+       bool "inetd"
+       default n
+       help
+         Internet superserver daemon
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+       bool "  Support echo service"
+       default y
+       depends on CONFIG_INETD
+       help
+         Echo received data internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+       bool "  Support discard service"
+       default y
+       depends on CONFIG_INETD
+       help
+         Internet /dev/null internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+       bool "  Support time service"
+       default y
+       depends on CONFIG_INETD
+       help
+         Return 32 bit time since 1900 internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+       bool "  Support daytime service"
+       default y
+       depends on CONFIG_INETD
+       help
+         Return human-readable time internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+       bool "  Support chargen service"
+       default y
+       depends on CONFIG_INETD
+       help
+         Familiar character generator internal inetd service
+
+
+config CONFIG_IP
+       bool "ip"
+       default n
+       help
+         The "ip" applet is a TCP/IP interface configuration and routing
+         utility.  You generally don't need "ip" to use busybox with
+         TCP/IP.
+
+if CONFIG_IP && CONFIG_IPADDR
+       config CONFIG_FEATURE_IP_ADDRESS
+               default y
+       comment "  address (forced enabled for ipaddr)"
+endif
+if ! (CONFIG_IP && CONFIG_IPADDR)
+       config CONFIG_FEATURE_IP_ADDRESS
+               bool "  address"
+               default y
+               depends on CONFIG_IP
+               help
+                 Address manipulation support for the "ip" applet.
+endif
+
+if CONFIG_IP && CONFIG_IPLINK
+       config CONFIG_FEATURE_IP_LINK
+               default y
+       comment "  link (forced enabled for iplink)"
+endif
+if !(CONFIG_IP && CONFIG_IPLINK)
+       config CONFIG_FEATURE_IP_LINK
+               bool "  link"
+               default y
+               depends on CONFIG_IP
+               help
+                 Configure network devices with "ip".
+endif
+
+if CONFIG_IP && CONFIG_IPROUTE
+       config CONFIG_FEATURE_IP_ROUTE
+               default y
+       comment "  route (forced enabled for iproute)"
+endif
+if !(CONFIG_IP && CONFIG_IPROUTE)
+       config CONFIG_FEATURE_IP_ROUTE
+               bool "  route"
+               default y
+               depends on CONFIG_IP
+               help
+                 Add support for routing table management to "ip".
+endif
+
+if CONFIG_IP && CONFIG_IPTUNNEL
+       config CONFIG_FEATURE_IP_TUNNEL
+               default y
+       comment "  tunnel (forced enabled for iptunnel)"
+endif
+if !(CONFIG_IP && CONFIG_IPTUNNEL)
+       config CONFIG_FEATURE_IP_TUNNEL
+               bool "  tunnel"
+               default n
+               depends on CONFIG_IP
+               help
+                 Add support for tunneling commands to "ip".
+endif
+
+config CONFIG_IPCALC
+       bool "ipcalc"
+       default n
+       help
+         ipcalc takes an IP address and netmask and calculates the
+         resulting broadcast, network, and host range.
+
+config CONFIG_FEATURE_IPCALC_FANCY
+       bool "  Fancy IPCALC, more options, adds 1 kbyte"
+       default y
+       depends on CONFIG_IPCALC
+       help
+         Adds the options hostname, prefix and silent to the output of "ipcalc".
+
+config CONFIG_IPADDR
+       bool "ipaddr"
+       default n
+       help
+         Equivalent to selecting address support to "ip", above.
+
+config CONFIG_IPLINK
+       bool "iplink"
+       default n
+       help
+         Equivalent to selecting link support to "ip", above.
+
+config CONFIG_IPROUTE
+       bool "iproute"
+       default n
+       help
+         Equivalent to selecting route support to "ip", above.
+
+config CONFIG_IPTUNNEL
+       bool "iptunnel"
+       default n
+       help
+         Equivalent to selecting tunnel support to "ip", above.
+
+config CONFIG_NAMEIF
+       bool "nameif"
+       default n
+       help
+         nameif is used to rename network interface by its MAC address.
+         Renamed interfaces MUST be in the down state.
+         It is possible to use a file (default: /etc/mactab)
+         with list of new interface names and MACs.
+         Maximum interface name length: IF_NAMESIZE = 16
+         File fields are separated by space or tab.
+         File format:
+         # Comment
+         new_interface_name    XX:XX:XX:XX:XX:XX
+
+config CONFIG_NC
+       bool "nc"
+       default n
+       help
+         A simple Unix utility which reads and writes data across network
+         connections.
+
+config CONFIG_NETSTAT
+       bool "netstat"
+       default n
+       help
+         netstat prints information about the Linux networking subsystem.
+
+config CONFIG_NSLOOKUP
+       bool "nslookup"
+       default n
+       help
+         nslookup is a tool to query Internet name servers.
+
+config CONFIG_PING
+       bool "ping"
+       default n
+       help
+         ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
+         elicit an ICMP ECHO_RESPONSE from a host or gateway.
+
+config CONFIG_FEATURE_FANCY_PING
+       bool "  Enable fancy ping output"
+       default y
+       depends on CONFIG_PING
+       help
+         Make the output from the ping applet include statistics, and at the
+         same time provide full support for ICMP packets.
+
+config CONFIG_PING6
+       bool "ping6"
+       default n
+       depends on CONFIG_FEATURE_IPV6
+       help
+         This will give you a ping that can talk IPv6.
+
+config CONFIG_FEATURE_FANCY_PING6
+       bool "  Enable fancy ping6 output"
+       default y
+       depends on CONFIG_PING6
+       help
+         Make the output from the ping6 applet include statistics, and at the
+         same time provide full support for ICMP packets.
+
+config CONFIG_ROUTE
+       bool "route"
+       default n
+       help
+         Route displays or manipulates the kernel's IP routing tables.
+
+config CONFIG_TELNET
+       bool "telnet"
+       default n
+       help
+         Telnet is an interface to the TELNET protocol, but is also commonly
+         used to test other simple protocols.
+
+config CONFIG_FEATURE_TELNET_TTYPE
+       bool "  Pass TERM type to remote host"
+       default y
+       depends on CONFIG_TELNET
+       help
+         Setting this option will forward the TERM environment variable to the
+         remote host you are connecting to.  This is useful to make sure that
+         things like ANSI colors and other control sequences behave.
+
+config CONFIG_FEATURE_TELNET_AUTOLOGIN
+       bool "  Pass USER type to remote host"
+       default y
+       depends on CONFIG_TELNET
+       help
+         Setting this option will forward the USER environment variable to the
+         remote host you are connecting to. This is useful when you need to
+         log into a machine without telling the username (autologin). This
+         option enables `-a' and `-l USER' arguments.
+
+config CONFIG_TELNETD
+       bool "telnetd"
+       default n
+       select CONFIG_LOGIN
+       help
+         A daemon for the TELNET protocol, allowing you to log onto the host
+         running the daemon.  Please keep in mind that the TELNET protocol
+         sends passwords in plain text.  If you can't afford the space for an
+         SSH daemon and you trust your network, you may say 'y' here.  As a
+         more secure alternative, you should seriously consider installing the
+         very small Dropbear SSH daemon instead:
+               http://matt.ucc.asn.au/dropbear/dropbear.html
+
+         Note that for busybox telnetd to work you need several things:
+         First of all, your kernel needs:
+                 CONFIG_UNIX98_PTYS=y
+                 CONFIG_DEVPTS_FS=y
+
+         Next, you need a /dev/pts directory on your root filesystem:
+
+                 $ ls -ld /dev/pts
+                 drwxr-xr-x  2 root root 0 Sep 23 13:21 /dev/pts/
+
+         Next you need the pseudo terminal master multiplexer /dev/ptmx:
+
+                 $ ls -la /dev/ptmx
+                 crw-rw-rw-  1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
+
+         Any /dev/ttyp[0-9]* files you may have can be removed.
+         Next, you need to mount the devpts filesystem on /dev/pts using:
+
+                 mount -t devpts devpts /dev/pts
+
+         You need to be sure that Busybox has CONFIG_LOGIN and
+         CONFIG_FEATURE_SUID enabled.  And finally, you should make
+         certain that Busybox has been installed setuid root:
+
+               chown root.root /bin/busybox
+               chmod 4755 /bin/busybox
+
+         with all that done, telnetd _should_ work....
+
+
+config CONFIG_FEATURE_TELNETD_INETD
+       bool "  Support call from inetd only"
+       default n
+       depends on CONFIG_TELNETD
+       help
+         Selecting this will make telnetd only callable from inetd,
+         removing the standalone support.
+
+config CONFIG_TFTP
+       bool "tftp"
+       default n
+       help
+         This enables the Trivial File Transfer Protocol client program.  TFTP
+         is usually used for simple, small transfers such as a root image
+         for a network-enabled bootloader.
+
+config CONFIG_FEATURE_TFTP_GET
+       bool "  Enable \"get\" command"
+       default y
+       depends on CONFIG_TFTP
+       help
+         Add support for the GET command within the TFTP client.  This allows
+         a client to retrieve a file from a TFTP server.
+
+config CONFIG_FEATURE_TFTP_PUT
+       bool "  Enable \"put\" command"
+       default y
+       depends on CONFIG_TFTP
+       help
+         Add support for the PUT command within the TFTP client.  This allows
+         a client to transfer a file to a TFTP server.
+
+config CONFIG_FEATURE_TFTP_BLOCKSIZE
+       bool "  Enable \"blocksize\" command"
+       default n
+       depends on CONFIG_TFTP
+       help
+         Allow the client to specify the desired block size for transfers.
+
+config CONFIG_FEATURE_TFTP_DEBUG
+       bool "  Enable debug"
+       default n
+       depends on CONFIG_TFTP
+       help
+         Enable debug settings for tftp.  This is useful if you're running
+         into problems with tftp as the protocol doesn't help you much when
+         you run into problems.
+
+config CONFIG_TRACEROUTE
+       bool "traceroute"
+       default n
+       help
+         Utility to trace the route of IP packets
+
+config CONFIG_FEATURE_TRACEROUTE_VERBOSE
+       bool "  Enable verbose output"
+       default n
+       depends on CONFIG_TRACEROUTE
+       help
+         Add some verbosity to traceroute.  This includes amongst other things
+         hostnames and ICMP response types.
+
+config CONFIG_VCONFIG
+       bool "vconfig"
+       default n
+       help
+         Creates, removes, and configures VLAN interfaces
+
+config CONFIG_WGET
+       bool "wget"
+       default n
+       help
+         wget is a utility for non-interactive download of files from HTTP,
+         HTTPS, and FTP servers.
+
+config CONFIG_FEATURE_WGET_STATUSBAR
+       bool "  Enable a nifty process meter (+2k)"
+       default y
+       depends on CONFIG_WGET
+       help
+         Enable the transfer progress bar for wget transfers.
+
+config CONFIG_FEATURE_WGET_AUTHENTICATION
+       bool "  Enable HTTP authentication"
+       default y
+       depends on CONFIG_WGET
+       help
+         Support authenticated HTTP transfers.
+
+config CONFIG_FEATURE_WGET_IP6_LITERAL
+       bool "  Enable IPv6 literal addresses"
+       default y
+       depends on CONFIG_WGET
+       help
+         Support IPv6 address literal notation in URLs.
+
+source networking/udhcp/Config.in
+
+endmenu
+
diff --git a/busybox/networking/Makefile b/busybox/networking/Makefile
new file mode 100644 (file)
index 0000000..91726b1
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/networking
+NETWORKING_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/networking/Makefile.in b/busybox/networking/Makefile.in
new file mode 100644 (file)
index 0000000..9bfe901
--- /dev/null
@@ -0,0 +1,68 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+NETWORKING_AR:=networking.a
+ifndef $(NETWORKING_DIR)
+NETWORKING_DIR:=$(top_builddir)/networking/
+endif
+srcdir=$(top_srcdir)/networking
+NETWORKING-y:=
+NETWORKING-$(CONFIG_ARPING)    += arping.o
+NETWORKING-$(CONFIG_FTPGET)    += ftpgetput.o
+NETWORKING-$(CONFIG_FTPPUT)    += ftpgetput.o
+NETWORKING-$(CONFIG_HOSTNAME)  += hostname.o
+NETWORKING-$(CONFIG_HTTPD)     += httpd.o
+NETWORKING-$(CONFIG_IFCONFIG)  += ifconfig.o
+NETWORKING-$(CONFIG_IFUPDOWN)  += ifupdown.o
+NETWORKING-$(CONFIG_INETD)      += inetd.o
+NETWORKING-$(CONFIG_IP)                += ip.o
+NETWORKING-$(CONFIG_IPCALC)    += ipcalc.o
+NETWORKING-$(CONFIG_IPADDR)    += ipaddr.o
+NETWORKING-$(CONFIG_IPLINK)    += iplink.o
+NETWORKING-$(CONFIG_IPROUTE)   += iproute.o
+NETWORKING-$(CONFIG_IPTUNNEL)  += iptunnel.o
+NETWORKING-$(CONFIG_NAMEIF)    += nameif.o
+NETWORKING-$(CONFIG_NC)                += nc.o
+NETWORKING-$(CONFIG_NETSTAT)   += netstat.o
+NETWORKING-$(CONFIG_NSLOOKUP)  += nslookup.o
+NETWORKING-$(CONFIG_PING)      += ping.o
+NETWORKING-$(CONFIG_PING6)     += ping6.o
+NETWORKING-$(CONFIG_ROUTE)     += route.o
+NETWORKING-$(CONFIG_TELNET)    += telnet.o
+NETWORKING-$(CONFIG_TELNETD)   += telnetd.o
+NETWORKING-$(CONFIG_TFTP)      += tftp.o
+NETWORKING-$(CONFIG_TRACEROUTE)        += traceroute.o
+NETWORKING-$(CONFIG_VCONFIG)   += vconfig.o
+NETWORKING-$(CONFIG_WGET)      += wget.o
+
+libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR)
+
+needcrypt-y:=
+needcrypt-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) := y
+
+ifeq ($(needcrypt-y),y)
+  LIBRARIES += -lcrypt
+endif
+
+$(NETWORKING_DIR)$(NETWORKING_AR): $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
+       $(AR) -ro $@ $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
+
+$(NETWORKING_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/networking/arping.c b/busybox/networking/arping.c
new file mode 100644 (file)
index 0000000..7279e86
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * arping.c - Ping hosts by ARP requests/replies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author:     Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
+ */
+
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#include "busybox.h"
+
+#define APPLET_NAME "arping"
+
+static struct in_addr src;
+static struct in_addr dst;
+static struct sockaddr_ll me;
+static struct sockaddr_ll he;
+static struct timeval last;
+static int dad;
+static int unsolicited;
+static int advert;
+static int quiet;
+static int quit_on_reply = 0;
+static int count = -1;
+static int timeout;
+static int unicasting;
+static int s;
+static int broadcast_only;
+static int sent;
+static int brd_sent;
+static int received;
+static int brd_recv;
+static int req_recv;
+
+#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
+                          ((tv1).tv_usec-(tv2).tv_usec)/1000 )
+#if 0
+static void set_signal(int signo, void (*handler) (void))
+{
+       struct sigaction sa;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = (void (*)(int)) handler;
+       sa.sa_flags = SA_RESTART;
+       sigaction(signo, &sa, NULL);
+}
+#endif
+
+static int send_pack(int sock, struct in_addr *src_addr,
+                                        struct in_addr *dst_addr, struct sockaddr_ll *ME,
+                                        struct sockaddr_ll *HE)
+{
+       int err;
+       struct timeval now;
+       unsigned char buf[256];
+       struct arphdr *ah = (struct arphdr *) buf;
+       unsigned char *p = (unsigned char *) (ah + 1);
+
+       ah->ar_hrd = htons(ME->sll_hatype);
+       ah->ar_hrd = htons(ARPHRD_ETHER);
+       ah->ar_pro = htons(ETH_P_IP);
+       ah->ar_hln = ME->sll_halen;
+       ah->ar_pln = 4;
+       ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
+
+       memcpy(p, &ME->sll_addr, ah->ar_hln);
+       p += ME->sll_halen;
+
+       memcpy(p, src_addr, 4);
+       p += 4;
+
+       if (advert)
+               memcpy(p, &ME->sll_addr, ah->ar_hln);
+       else
+               memcpy(p, &HE->sll_addr, ah->ar_hln);
+       p += ah->ar_hln;
+
+       memcpy(p, dst_addr, 4);
+       p += 4;
+
+       gettimeofday(&now, NULL);
+       err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
+       if (err == p - buf) {
+               last = now;
+               sent++;
+               if (!unicasting)
+                       brd_sent++;
+       }
+       return err;
+}
+
+void finish(void)
+{
+       if (!quiet) {
+               printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
+               printf("Received %d repl%s", received, (received > 1) ? "ies" : "y");
+               if (brd_recv || req_recv) {
+                       printf(" (");
+                       if (req_recv)
+                               printf("%d request(s)", req_recv);
+                       if (brd_recv)
+                               printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv);
+                       putchar(')');
+               }
+               putchar('\n');
+               fflush(stdout);
+       }
+       if (dad)
+               exit(!!received);
+       if (unsolicited)
+               exit(0);
+       exit(!received);
+}
+
+void catcher(void)
+{
+       struct timeval tv;
+       static struct timeval start;
+
+       gettimeofday(&tv, NULL);
+
+       if (start.tv_sec == 0)
+               start = tv;
+
+       if (count-- == 0
+               || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500))
+               finish();
+
+       if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) {
+               send_pack(s, &src, &dst, &me, &he);
+               if (count == 0 && unsolicited)
+                       finish();
+       }
+       alarm(1);
+}
+
+int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+{
+       struct timeval tv;
+       struct arphdr *ah = (struct arphdr *) buf;
+       unsigned char *p = (unsigned char *) (ah + 1);
+       struct in_addr src_ip, dst_ip;
+
+       gettimeofday(&tv, NULL);
+
+       /* Filter out wild packets */
+       if (FROM->sll_pkttype != PACKET_HOST &&
+               FROM->sll_pkttype != PACKET_BROADCAST &&
+               FROM->sll_pkttype != PACKET_MULTICAST)
+               return 0;
+
+       /* Only these types are recognised */
+       if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
+               return 0;
+
+       /* ARPHRD check and this darned FDDI hack here :-( */
+       if (ah->ar_hrd != htons(FROM->sll_hatype) &&
+               (FROM->sll_hatype != ARPHRD_FDDI
+                || ah->ar_hrd != htons(ARPHRD_ETHER)))
+               return 0;
+
+       /* Protocol must be IP. */
+       if (ah->ar_pro != htons(ETH_P_IP))
+               return 0;
+       if (ah->ar_pln != 4)
+               return 0;
+       if (ah->ar_hln != me.sll_halen)
+               return 0;
+       if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln))
+               return 0;
+       memcpy(&src_ip, p + ah->ar_hln, 4);
+       memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
+       if (!dad) {
+               if (src_ip.s_addr != dst.s_addr)
+                       return 0;
+               if (src.s_addr != dst_ip.s_addr)
+                       return 0;
+               if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))
+                       return 0;
+       } else {
+               /* DAD packet was:
+                  src_ip = 0 (or some src)
+                  src_hw = ME
+                  dst_ip = tested address
+                  dst_hw = <unspec>
+
+                  We fail, if receive request/reply with:
+                  src_ip = tested_address
+                  src_hw != ME
+                  if src_ip in request was not zero, check
+                  also that it matches to dst_ip, otherwise
+                  dst_ip/dst_hw do not matter.
+                */
+               if (src_ip.s_addr != dst.s_addr)
+                       return 0;
+               if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
+                       return 0;
+               if (src.s_addr && src.s_addr != dst_ip.s_addr)
+                       return 0;
+       }
+       if (!quiet) {
+               int s_printed = 0;
+
+               printf("%s ",
+                          FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast");
+               printf("%s from ",
+                          ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
+               printf("%s ", inet_ntoa(src_ip));
+               printf("[%s]", ether_ntoa((struct ether_addr *) p));
+               if (dst_ip.s_addr != src.s_addr) {
+                       printf("for %s ", inet_ntoa(dst_ip));
+                       s_printed = 1;
+               }
+               if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
+                       if (!s_printed)
+                               printf("for ");
+                       printf("[%s]",
+                                  ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
+               }
+               if (last.tv_sec) {
+                       long usecs = (tv.tv_sec - last.tv_sec) * 1000000 +
+                               tv.tv_usec - last.tv_usec;
+                       long msecs = (usecs + 500) / 1000;
+
+                       usecs -= msecs * 1000 - 500;
+                       printf(" %ld.%03ldms\n", msecs, usecs);
+               } else {
+                       printf(" UNSOLICITED?\n");
+               }
+               fflush(stdout);
+       }
+       received++;
+       if (FROM->sll_pkttype != PACKET_HOST)
+               brd_recv++;
+       if (ah->ar_op == htons(ARPOP_REQUEST))
+               req_recv++;
+       if (quit_on_reply)
+               finish();
+       if (!broadcast_only) {
+               memcpy(he.sll_addr, p, me.sll_halen);
+               unicasting = 1;
+       }
+       return 1;
+}
+
+int arping_main(int argc, char **argv)
+{
+       int socket_errno;
+       int ch;
+       uid_t uid = getuid();
+       char *device = "eth0";
+       int ifindex = 0;
+       char *source = NULL;
+       char *target;
+
+       s = socket(PF_PACKET, SOCK_DGRAM, 0);
+       socket_errno = errno;
+
+       setuid(uid);
+
+       while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) {
+               switch (ch) {
+               case 'b':
+                       broadcast_only = 1;
+                       break;
+               case 'D':
+                       dad++;
+                       quit_on_reply = 1;
+                       break;
+               case 'U':
+                       unsolicited++;
+                       break;
+               case 'A':
+                       advert++;
+                       unsolicited++;
+                       break;
+               case 'q':
+                       quiet++;
+                       break;
+               case 'c':
+                       count = atoi(optarg);
+                       break;
+               case 'w':
+                       timeout = atoi(optarg);
+                       break;
+               case 'I':
+                       if (optarg == NULL)
+                               bb_show_usage();
+                       if (bb_strlen(optarg) > IF_NAMESIZE) {
+                               bb_error_msg("Interface name `%s' must be less than %d", optarg,
+                                                 IF_NAMESIZE);
+                               exit(2);
+                       }
+                       device = optarg;
+                       break;
+               case 'f':
+                       quit_on_reply = 1;
+                       break;
+               case 's':
+                       source = optarg;
+                       break;
+               case 'h':
+               case '?':
+               default:
+                       bb_show_usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc != 1)
+               bb_show_usage();
+
+       target = *argv;
+
+
+       if (s < 0) {
+               bb_error_msg("socket");
+               exit(socket_errno);
+       }
+
+       {
+               struct ifreq ifr;
+
+               memset(&ifr, 0, sizeof(ifr));
+               strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
+               if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+                       bb_error_msg("Interface %s not found", device);
+                       exit(2);
+               }
+               ifindex = ifr.ifr_ifindex;
+
+               if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
+                       bb_error_msg("SIOCGIFFLAGS");
+                       exit(2);
+               }
+               if (!(ifr.ifr_flags & IFF_UP)) {
+                       bb_error_msg("Interface %s is down", device);
+                       exit(2);
+               }
+               if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
+                       bb_error_msg("Interface %s is not ARPable", device);
+                       exit(dad ? 0 : 2);
+               }
+       }
+
+       if (!inet_aton(target, &dst)) {
+               struct hostent *hp;
+
+               hp = gethostbyname2(target, AF_INET);
+               if (!hp) {
+                       bb_error_msg("invalid or unknown target %s", target);
+                       exit(2);
+               }
+               memcpy(&dst, hp->h_addr, 4);
+       }
+
+       if (source && !inet_aton(source, &src)) {
+               bb_error_msg("invalid source address %s", source);
+               exit(2);
+       }
+
+       if (!dad && unsolicited && src.s_addr == 0)
+               src = dst;
+
+       if (!dad || src.s_addr) {
+               struct sockaddr_in saddr;
+               int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+               if (probe_fd < 0) {
+                       bb_error_msg("socket");
+                       exit(2);
+               }
+               if (device) {
+                       if (setsockopt
+                               (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device,
+                                strlen(device) + 1) == -1)
+                               bb_error_msg("WARNING: interface %s is ignored", device);
+               }
+               memset(&saddr, 0, sizeof(saddr));
+               saddr.sin_family = AF_INET;
+               if (src.s_addr) {
+                       saddr.sin_addr = src;
+                       if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
+                               bb_error_msg("bind");
+                               exit(2);
+                       }
+               } else if (!dad) {
+                       int on = 1;
+                       int alen = sizeof(saddr);
+
+                       saddr.sin_port = htons(1025);
+                       saddr.sin_addr = dst;
+
+                       if (setsockopt
+                               (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on,
+                                sizeof(on)) == -1)
+                               perror("WARNING: setsockopt(SO_DONTROUTE)");
+                       if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr))
+                               == -1) {
+                               bb_error_msg("connect");
+                               exit(2);
+                       }
+                       if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) ==
+                               -1) {
+                               bb_error_msg("getsockname");
+                               exit(2);
+                       }
+                       src = saddr.sin_addr;
+               }
+               close(probe_fd);
+       };
+
+       me.sll_family = AF_PACKET;
+       me.sll_ifindex = ifindex;
+       me.sll_protocol = htons(ETH_P_ARP);
+       if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) {
+               bb_error_msg("bind");
+               exit(2);
+       }
+
+       {
+               int alen = sizeof(me);
+
+               if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
+                       bb_error_msg("getsockname");
+                       exit(2);
+               }
+       }
+       if (me.sll_halen == 0) {
+               bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device);
+               exit(dad ? 0 : 2);
+       }
+       he = me;
+       memset(he.sll_addr, -1, he.sll_halen);
+
+       if (!quiet) {
+               printf("ARPING to %s", inet_ntoa(dst));
+               printf(" from %s via %s\n", inet_ntoa(src),
+                          device ? device : "unknown");
+       }
+
+       if (!src.s_addr && !dad) {
+               bb_error_msg("no src address in the non-DAD mode");
+               exit(2);
+       }
+
+       {
+               struct sigaction sa;
+
+               memset(&sa, 0, sizeof(sa));
+               sa.sa_flags = SA_RESTART;
+
+               sa.sa_handler = (void (*)(int)) finish;
+               sigaction(SIGINT, &sa, NULL);
+
+               sa.sa_handler = (void (*)(int)) catcher;
+               sigaction(SIGALRM, &sa, NULL);
+       }
+
+       catcher();
+
+       while (1) {
+               sigset_t sset, osset;
+               char packet[4096];
+               struct sockaddr_ll from;
+               int alen = sizeof(from);
+               int cc;
+
+               if ((cc = recvfrom(s, packet, sizeof(packet), 0,
+                                                  (struct sockaddr *) &from, &alen)) < 0) {
+                       perror("recvfrom");
+                       continue;
+               }
+               sigemptyset(&sset);
+               sigaddset(&sset, SIGALRM);
+               sigaddset(&sset, SIGINT);
+               sigprocmask(SIG_BLOCK, &sset, &osset);
+               recv_pack(packet, cc, &from);
+               sigprocmask(SIG_SETMASK, &osset, NULL);
+       }
+}
diff --git a/busybox/networking/ftpgetput.c b/busybox/networking/ftpgetput.c
new file mode 100644 (file)
index 0000000..f6bd82b
--- /dev/null
@@ -0,0 +1,378 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ftpget
+ *
+ * Mini implementation of FTP to retrieve a remote file.
+ *
+ * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
+ * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Based on wget.c by Chip Rosenthal Covad Communications
+ * <chip@laserlink.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "busybox.h"
+
+typedef struct ftp_host_info_s {
+       char *user;
+       char *password;
+       struct sockaddr_in *s_in;
+} ftp_host_info_t;
+
+static char verbose_flag = 0;
+static char do_continue = 0;
+
+static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
+{
+       if (verbose_flag) {
+               bb_error_msg("cmd %s%s", s1, s2);
+       }
+
+       if (s1) {
+               if (s2) {
+                       fprintf(stream, "%s%s\n", s1, s2);
+               } else {
+                       fprintf(stream, "%s\n", s1);
+               }
+       }
+       do {
+               char *buf_ptr;
+
+               if (fgets(buf, 510, stream) == NULL) {
+                       bb_perror_msg_and_die("fgets()");
+               }
+               buf_ptr = strstr(buf, "\r\n");
+               if (buf_ptr) {
+                       *buf_ptr = '\0';
+               }
+       } while (! isdigit(buf[0]) || buf[3] != ' ');
+
+       return atoi(buf);
+}
+
+static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf)
+{
+       char *buf_ptr;
+       unsigned short port_num;
+
+       buf_ptr = strrchr(buf, ',');
+       *buf_ptr = '\0';
+       port_num = atoi(buf_ptr + 1);
+
+       buf_ptr = strrchr(buf, ',');
+       *buf_ptr = '\0';
+       port_num += atoi(buf_ptr + 1) * 256;
+
+       server->s_in->sin_port=htons(port_num);
+       return(xconnect(server->s_in));
+}
+
+static FILE *ftp_login(ftp_host_info_t *server)
+{
+       FILE *control_stream;
+       char buf[512];
+
+       /* Connect to the command socket */
+       control_stream = fdopen(xconnect(server->s_in), "r+");
+       if (control_stream == NULL) {
+               bb_perror_msg_and_die("Couldnt open control stream");
+       }
+
+       if (ftpcmd(NULL, NULL, control_stream, buf) != 220) {
+               bb_error_msg_and_die("%s", buf + 4);
+       }
+
+       /*  Login to the server */
+       switch (ftpcmd("USER ", server->user, control_stream, buf)) {
+       case 230:
+               break;
+       case 331:
+               if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) {
+                       bb_error_msg_and_die("PASS error: %s", buf + 4);
+               }
+               break;
+       default:
+               bb_error_msg_and_die("USER error: %s", buf + 4);
+       }
+
+       ftpcmd("TYPE I", NULL, control_stream, buf);
+
+       return(control_stream);
+}
+
+#ifdef CONFIG_FTPGET
+static int ftp_recieve(ftp_host_info_t *server, FILE *control_stream,
+               const char *local_path, char *server_path)
+{
+       char buf[512];
+       off_t filesize = 0;
+       int fd_data;
+       int fd_local = -1;
+       off_t beg_range = 0;
+
+       /* Connect to the data socket */
+       if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+               bb_error_msg_and_die("PASV error: %s", buf + 4);
+       }
+       fd_data = xconnect_ftpdata(server, buf);
+
+       if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) {
+               unsigned long value=filesize;
+               if (safe_strtoul(buf + 4, &value))
+                       bb_error_msg_and_die("SIZE error: %s", buf + 4);
+               filesize = value;
+       }
+
+       if ((local_path[0] == '-') && (local_path[1] == '\0')) {
+               fd_local = STDOUT_FILENO;
+               do_continue = 0;
+       }
+
+       if (do_continue) {
+               struct stat sbuf;
+               if (lstat(local_path, &sbuf) < 0) {
+                       bb_perror_msg_and_die("fstat()");
+               }
+               if (sbuf.st_size > 0) {
+                       beg_range = sbuf.st_size;
+               } else {
+                       do_continue = 0;
+               }
+       }
+
+       if (do_continue) {
+               sprintf(buf, "REST %ld", (long)beg_range);
+               if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
+                       do_continue = 0;
+               } else {
+                       filesize -= beg_range;
+               }
+       }
+
+       if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) {
+               bb_error_msg_and_die("RETR error: %s", buf + 4);
+       }
+
+       /* only make a local file if we know that one exists on the remote server */
+       if (fd_local == -1) {
+               if (do_continue) {
+                       fd_local = bb_xopen(local_path, O_APPEND | O_WRONLY);
+               } else {
+                       fd_local = bb_xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY);
+               }
+       }
+
+       /* Copy the file */
+       if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) {
+               exit(EXIT_FAILURE);
+       }
+
+       /* close it all down */
+       close(fd_data);
+       if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+               bb_error_msg_and_die("ftp error: %s", buf + 4);
+       }
+       ftpcmd("QUIT", NULL, control_stream, buf);
+
+       return(EXIT_SUCCESS);
+}
+#endif
+
+#ifdef CONFIG_FTPPUT
+static int ftp_send(ftp_host_info_t *server, FILE *control_stream,
+               const char *server_path, char *local_path)
+{
+       struct stat sbuf;
+       char buf[512];
+       int fd_data;
+       int fd_local;
+       int response;
+
+       /*  Connect to the data socket */
+       if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+               bb_error_msg_and_die("PASV error: %s", buf + 4);
+       }
+       fd_data = xconnect_ftpdata(server, buf);
+
+       if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) {
+               bb_error_msg_and_die("CWD error: %s", buf + 4);
+       }
+
+       /* get the local file */
+       if ((local_path[0] == '-') && (local_path[1] == '\0')) {
+               fd_local = STDIN_FILENO;
+       } else {
+               fd_local = bb_xopen(local_path, O_RDONLY);
+               fstat(fd_local, &sbuf);
+
+               sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size);
+               response = ftpcmd(buf, NULL, control_stream, buf);
+               switch (response) {
+               case 200:
+               case 202:
+                       break;
+               default:
+                       close(fd_local);
+                       bb_error_msg_and_die("ALLO error: %s", buf + 4);
+                       break;
+               }
+       }
+       response = ftpcmd("STOR ", local_path, control_stream, buf);
+       switch (response) {
+       case 125:
+       case 150:
+               break;
+       default:
+               close(fd_local);
+               bb_error_msg_and_die("STOR error: %s", buf + 4);
+       }
+
+       /* transfer the file  */
+       if (bb_copyfd_eof(fd_local, fd_data) == -1) {
+               exit(EXIT_FAILURE);
+       }
+
+       /* close it all down */
+       close(fd_data);
+       if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+               bb_error_msg_and_die("error: %s", buf + 4);
+       }
+       ftpcmd("QUIT", NULL, control_stream, buf);
+
+       return(EXIT_SUCCESS);
+}
+#endif
+
+#define FTPGETPUT_OPT_CONTINUE 1
+#define FTPGETPUT_OPT_VERBOSE  2
+#define FTPGETPUT_OPT_USER     4
+#define FTPGETPUT_OPT_PASSWORD 8
+#define FTPGETPUT_OPT_PORT     16
+
+static const struct option ftpgetput_long_options[] = {
+       {"continue", 1, NULL, 'c'},
+       {"verbose", 0, NULL, 'v'},
+       {"username", 1, NULL, 'u'},
+       {"password", 1, NULL, 'p'},
+       {"port", 1, NULL, 'P'},
+       {0, 0, 0, 0}
+};
+
+int ftpgetput_main(int argc, char **argv)
+{
+       /* content-length of the file */
+       unsigned long opt;
+       char *port = "ftp";
+
+       /* socket to ftp server */
+       FILE *control_stream;
+       struct sockaddr_in s_in;
+
+       /* continue a prev transfer (-c) */
+       ftp_host_info_t *server;
+
+       int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL;
+
+       /* Check to see if the command is ftpget or ftput */
+#ifdef CONFIG_FTPPUT
+# ifdef CONFIG_FTPGET
+       if (bb_applet_name[3] == 'p') {
+               ftp_action = ftp_send;
+       }
+# else
+       ftp_action = ftp_send;
+# endif
+#endif
+#ifdef CONFIG_FTPGET
+# ifdef CONFIG_FTPPUT
+       if (bb_applet_name[3] == 'g') {
+               ftp_action = ftp_recieve;
+       }
+# else
+       ftp_action = ftp_recieve;
+# endif
+#endif
+
+       /* Set default values */
+       server = xmalloc(sizeof(ftp_host_info_t));
+       server->user = "anonymous";
+       server->password = "busybox@";
+       verbose_flag = 0;
+
+       /*
+        * Decipher the command line
+        */
+       bb_applet_long_options = ftpgetput_long_options;
+       opt = bb_getopt_ulflags(argc, argv, "cvu:p:P:", &server->user, &server->password, &port);
+
+       /* Process the non-option command line arguments */
+       if (argc - optind != 3) {
+               bb_show_usage();
+       }
+
+       if (opt & FTPGETPUT_OPT_CONTINUE) {
+               do_continue = 1;
+       }
+       if (opt & FTPGETPUT_OPT_VERBOSE) {
+               verbose_flag = 1;
+       }
+
+       /* We want to do exactly _one_ DNS lookup, since some
+        * sites (i.e. ftp.us.debian.org) use round-robin DNS
+        * and we want to connect to only one IP... */
+       server->s_in = &s_in;
+       bb_lookup_host(&s_in, argv[optind]);
+       s_in.sin_port = bb_lookup_port(port, "tcp", 21);
+       if (verbose_flag) {
+               printf("Connecting to %s[%s]:%d\n",
+                               argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port));
+       }
+
+       /*  Connect/Setup/Configure the FTP session */
+       control_stream = ftp_login(server);
+
+       return(ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2]));
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/networking/hostname.c b/busybox/networking/hostname.c
new file mode 100644 (file)
index 0000000..a00263d
--- /dev/null
@@ -0,0 +1,130 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $Id: hostname.c,v 1.36 2003/07/14 21:21:01 andersen Exp $
+ * Mini hostname implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * adjusted by Erik Andersen <andersen@codepoet.org> to remove
+ * use of long options and GNU getopt.  Improved the usage info.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "busybox.h"
+
+extern char *optarg; /* in unistd.h */
+extern int  optind, opterr, optopt; /* in unistd.h */
+
+static void do_sethostname(char *s, int isfile)
+{
+       FILE *f;
+       char buf[255];
+
+       if (!s)
+               return;
+       if (!isfile) {
+               if (sethostname(s, strlen(s)) < 0) {
+                       if (errno == EPERM)
+                               bb_error_msg_and_die("you must be root to change the hostname");
+                       else
+                               bb_perror_msg_and_die("sethostname");
+               }
+       } else {
+               f = bb_xfopen(s, "r");
+               while (fgets(buf, 255, f) != NULL) {
+                       if (buf[0] =='#') {
+                               continue;
+                       }
+                       chomp(buf);
+                       do_sethostname(buf, 0);
+               }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+               fclose(f);
+#endif
+       }
+}
+
+int hostname_main(int argc, char **argv)
+{
+       int opt;
+       int type = 0;
+       struct hostent *hp;
+       char *filename = NULL;
+       char buf[255];
+       char *p = NULL;
+
+       if (argc < 1)
+               bb_show_usage();
+
+        while ((opt = getopt(argc, argv, "dfisF:")) > 0) {
+                switch (opt) {
+               case 'd':
+               case 'f':
+               case 'i':
+               case 's':
+                       type = opt;
+                       break;
+               case 'F':
+                       filename = optarg;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       /* Output in desired format */
+       if (type != 0) {
+               gethostname(buf, 255);
+               hp = xgethostbyname(buf);
+               p = strchr(hp->h_name, '.');
+               if (type == 'f') {
+                       puts(hp->h_name);
+               } else if (type == 's') {
+                       if (p != NULL) {
+                               *p = 0;
+                       }
+                       puts(hp->h_name);
+               } else if (type == 'd') {
+                       if (p) puts(p + 1);
+               } else if (type == 'i') {
+                       while (hp->h_addr_list[0]) {
+                               printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++)));
+                       }
+                       printf("\n");
+               }
+       }
+       /* Set the hostname */
+       else if (filename != NULL) {
+               do_sethostname(filename, 1);
+       } else if (optind < argc) {
+               do_sethostname(argv[optind], 0);
+       }
+       /* Or if all else fails,
+        * just print the current hostname */
+        else {
+               gethostname(buf, 255);
+               puts(buf);
+       }
+       return(0);
+}
diff --git a/busybox/networking/httpd.c b/busybox/networking/httpd.c
new file mode 100644 (file)
index 0000000..83ded53
--- /dev/null
@@ -0,0 +1,2091 @@
+/*
+ * httpd implementation for busybox
+ *
+ * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
+ * Copyright (C) 2003,2004 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * simplify patch stolen from libbb without using strdup
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *****************************************************************************
+ *
+ * Typical usage:
+ *   for non root user
+ * httpd -p 8080 -h $HOME/public_html
+ *   or for daemon start from rc script with uid=0:
+ * httpd -u www
+ * This is equivalent if www user have uid=80 to
+ * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
+ *
+ *
+ * When a url contains "cgi-bin" it is assumed to be a cgi script.  The
+ * server changes directory to the location of the script and executes it
+ * after setting QUERY_STRING and other environment variables.
+ *
+ * The server can also be invoked as a url arg decoder and html text encoder
+ * as follows:
+ *  foo=`httpd -d $foo`           # decode "Hello%20World" as "Hello World"
+ *  bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"
+ * Note that url encoding for arguments is not the same as html encoding for
+ * presentation.  -d decodes a url-encoded argument while -e encodes in html
+ * for page display.
+ *
+ * httpd.conf has the following format:
+ *
+ * A:172.20.         # Allow address from 172.20.0.0/16
+ * A:10.0.0.0/25     # Allow any address from 10.0.0.0-10.0.0.127
+ * A:10.0.0.0/255.255.255.128  # Allow any address that previous set
+ * A:127.0.0.1       # Allow local loopback connections
+ * D:*               # Deny from other IP connections
+ * /cgi-bin:foo:bar  # Require user foo, pwd bar on urls starting with /cgi-bin/
+ * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
+ * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
+ * .au:audio/basic   # additional mime type for audio.au files
+ *
+ * A/D may be as a/d or allow/deny - first char case insensitive
+ * Deny IP rules take precedence over allow rules.
+ *
+ *
+ * The Deny/Allow IP logic:
+ *
+ *  - Default is to allow all.  No addresses are denied unless
+ *         denied with a D: rule.
+ *  - Order of Deny/Allow rules is significant
+ *  - Deny rules take precedence over allow rules.
+ *  - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
+ *       addresses.
+ *  - Specification of Allow all (A:*) is a no-op
+ *
+ * Example:
+ *   1. Allow only specified addresses
+ *     A:172.20          # Allow any address that begins with 172.20.
+ *     A:10.10.          # Allow any address that begins with 10.10.
+ *     A:127.0.0.1       # Allow local loopback connections
+ *     D:*               # Deny from other IP connections
+ *
+ *   2. Only deny specified addresses
+ *     D:1.2.3.        # deny from 1.2.3.0 - 1.2.3.255
+ *     D:2.3.4.        # deny from 2.3.4.0 - 2.3.4.255
+ *     A:*             # (optional line added for clarity)
+ *
+ * If a sub directory contains a config file it is parsed and merged with
+ * any existing settings as if it was appended to the original configuration.
+ *
+ * subdir paths are relative to the containing subdir and thus cannot
+ * affect the parent rules.
+ *
+ * Note that since the sub dir is parsed in the forked thread servicing the
+ * subdir http request, any merge is discarded when the process exits.  As a
+ * result, the subdir settings only have a lifetime of a single request.
+ *
+ *
+ * If -c is not set, an attempt will be made to open the default
+ * root configuration file.  If -c is set and the file is not found, the
+ * server exits with an error.
+ *
+*/
+
+
+#include <stdio.h>
+#include <ctype.h>         /* for isspace           */
+#include <string.h>
+#include <stdlib.h>        /* for malloc            */
+#include <time.h>
+#include <unistd.h>        /* for close             */
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>    /* for connect and socket*/
+#include <netinet/in.h>    /* for sockaddr_in       */
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>         /* for open modes        */
+#include "busybox.h"
+
+
+static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
+static const char default_path_httpd_conf[] = "/etc";
+static const char httpd_conf[] = "httpd.conf";
+static const char home[] = "./";
+
+#ifdef CONFIG_LFS
+# define cont_l_fmt "%lld"
+#else
+# define cont_l_fmt "%ld"
+#endif
+
+#define TIMEOUT 60
+
+// Note: busybox xfuncs are not used because we want the server to keep running
+//       if something bad happens due to a malformed user request.
+//       As a result, all memory allocation after daemonize
+//       is checked rigorously
+
+//#define DEBUG 1
+
+/* Configure options, disabled by default as custom httpd feature */
+
+/* disabled as optional features */
+//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+//#define CONFIG_FEATURE_HTTPD_SETUID
+//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+
+/* If set, use this server from internet superserver only */
+//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+
+/* You can use this server as standalone, require libbb.a for linking */
+//#define HTTPD_STANDALONE
+
+/* Config options, disable this for do very small module */
+//#define CONFIG_FEATURE_HTTPD_CGI
+//#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
+//#define CONFIG_FEATURE_HTTPD_AUTH_MD5
+
+#ifdef HTTPD_STANDALONE
+/* standalone, enable all features */
+#undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+/* unset config option for remove warning as redefined */
+#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+#undef CONFIG_FEATURE_HTTPD_AUTH_MD5
+#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#undef CONFIG_FEATURE_HTTPD_CGI
+#undef CONFIG_FEATURE_HTTPD_SETUID
+#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+/* enable all features now */
+#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
+#define CONFIG_FEATURE_HTTPD_AUTH_MD5
+#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#define CONFIG_FEATURE_HTTPD_CGI
+#define CONFIG_FEATURE_HTTPD_SETUID
+#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+
+/* require from libbb.a for linking */
+const char *bb_applet_name = "httpd";
+
+void bb_show_usage(void)
+{
+  fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
+                 "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
+  exit(1);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+#undef CONFIG_FEATURE_HTTPD_SETUID  /* use inetd user.group config settings */
+#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP    /* so is not daemon */
+/* inetd set stderr to accepted socket and we can`t true see debug messages */
+#undef DEBUG
+#endif
+
+#define MAX_MEMORY_BUFF 8192    /* IO buffer */
+
+typedef struct HT_ACCESS {
+       char *after_colon;
+       struct HT_ACCESS *next;
+       char before_colon[1];         /* really bigger, must last */
+} Htaccess;
+
+typedef struct HT_ACCESS_IP {
+       unsigned int ip;
+       unsigned int mask;
+       int allow_deny;
+       struct HT_ACCESS_IP *next;
+} Htaccess_IP;
+
+typedef struct
+{
+  char buf[MAX_MEMORY_BUFF];
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  const char *realm;
+  char *remoteuser;
+#endif
+
+  const char *query;
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+  char *referer;
+#endif
+
+  const char *configFile;
+
+  unsigned int rmt_ip;
+#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
+  char rmt_ip_str[16];     /* for set env REMOTE_ADDR */
+#endif
+  unsigned port;           /* server initial port and for
+                             set env REMOTE_PORT */
+  union HTTPD_FOUND {
+       const char *found_mime_type;
+       const char *found_moved_temporarily;
+  } httpd_found;
+
+  off_t ContentLength;          /* -1 - unknown */
+  time_t last_mod;
+
+  Htaccess_IP *ip_a_d;          /* config allow/deny lines */
+  int flg_deny_all;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  Htaccess *auth;               /* config user:password lines */
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+  Htaccess *mime_a;             /* config mime types */
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  int accepted_socket;
+#define a_c_r config->accepted_socket
+#define a_c_w config->accepted_socket
+  int debugHttpd;          /* if seted, don`t stay daemon */
+#else
+#define a_c_r 0
+#define a_c_w 1
+#endif
+  volatile int alarm_signaled;
+
+} HttpdConfig;
+
+static HttpdConfig *config;
+
+static const char request_GET[] = "GET";    /* size algorithic optimize */
+
+static const char* const suffixTable [] = {
+/* Warning: shorted equivalent suffix in one line must be first */
+  ".htm.html", "text/html",
+  ".jpg.jpeg", "image/jpeg",
+  ".gif", "image/gif",
+  ".png", "image/png",
+  ".txt.h.c.cc.cpp", "text/plain",
+  ".css", "text/css",
+  ".wav", "audio/wav",
+  ".avi", "video/x-msvideo",
+  ".qt.mov", "video/quicktime",
+  ".mpe.mpeg", "video/mpeg",
+  ".mid.midi", "audio/midi",
+  ".mp3", "audio/mpeg",
+#if 0                        /* unpopular */
+  ".au", "audio/basic",
+  ".pac", "application/x-ns-proxy-autoconfig",
+  ".vrml.wrl", "model/vrml",
+#endif
+  0, "application/octet-stream" /* default */
+  };
+
+typedef enum
+{
+  HTTP_OK = 200,
+  HTTP_MOVED_TEMPORARILY = 302,
+  HTTP_BAD_REQUEST = 400,       /* malformed syntax */
+  HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+  HTTP_NOT_FOUND = 404,
+  HTTP_FORBIDDEN = 403,
+  HTTP_REQUEST_TIMEOUT = 408,
+  HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
+  HTTP_INTERNAL_SERVER_ERROR = 500,
+#if 0 /* future use */
+  HTTP_CONTINUE = 100,
+  HTTP_SWITCHING_PROTOCOLS = 101,
+  HTTP_CREATED = 201,
+  HTTP_ACCEPTED = 202,
+  HTTP_NON_AUTHORITATIVE_INFO = 203,
+  HTTP_NO_CONTENT = 204,
+  HTTP_MULTIPLE_CHOICES = 300,
+  HTTP_MOVED_PERMANENTLY = 301,
+  HTTP_NOT_MODIFIED = 304,
+  HTTP_PAYMENT_REQUIRED = 402,
+  HTTP_BAD_GATEWAY = 502,
+  HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
+  HTTP_RESPONSE_SETSIZE=0xffffffff
+#endif
+} HttpResponseNum;
+
+typedef struct
+{
+  HttpResponseNum type;
+  const char *name;
+  const char *info;
+} HttpEnumString;
+
+static const HttpEnumString httpResponseNames[] = {
+  { HTTP_OK, "OK" },
+  { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
+  { HTTP_REQUEST_TIMEOUT, "Request Timeout",
+    "No request appeared within a reasonable time period." },
+  { HTTP_NOT_IMPLEMENTED, "Not Implemented",
+    "The requested method is not recognized by this server." },
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  { HTTP_UNAUTHORIZED, "Unauthorized", "" },
+#endif
+  { HTTP_NOT_FOUND, "Not Found",
+    "The requested URL was not found on this server." },
+  { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
+  { HTTP_FORBIDDEN, "Forbidden", "" },
+  { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
+    "Internal Server Error" },
+#if 0                               /* not implemented */
+  { HTTP_CREATED, "Created" },
+  { HTTP_ACCEPTED, "Accepted" },
+  { HTTP_NO_CONTENT, "No Content" },
+  { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
+  { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
+  { HTTP_NOT_MODIFIED, "Not Modified" },
+  { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
+  { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
+#endif
+};
+
+
+static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
+static const char Content_length[] = "Content-length:";
+
+
+static int
+scan_ip (const char **ep, unsigned int *ip, unsigned char endc)
+{
+  const char *p = *ep;
+  int auto_mask = 8;
+  int j;
+
+  *ip = 0;
+  for (j = 0; j < 4; j++) {
+       unsigned int octet;
+
+       if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
+         return -auto_mask;
+       octet = 0;
+       while (*p >= '0' && *p <= '9') {
+         octet *= 10;
+         octet += *p - '0';
+         if (octet > 255)
+               return -auto_mask;
+         p++;
+       }
+       if (*p == '.')
+         p++;
+       if (*p != '/' && *p != 0)
+         auto_mask += 8;
+       *ip = ((*ip) << 8) | octet;
+  }
+  if (*p != 0) {
+       if (*p != endc)
+               return -auto_mask;
+       p++;
+       if(*p == 0)
+               return -auto_mask;
+  }
+  *ep = p;
+  return auto_mask;
+}
+
+static int
+scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask)
+{
+  int i;
+  unsigned int msk;
+
+  i = scan_ip(&ipm, ip, '/');
+  if(i < 0)
+       return i;
+  if(*ipm) {
+       const char *p = ipm;
+
+       i = 0;
+       while (*p) {
+               if (*p < '0' || *p > '9') {
+                       if (*p == '.') {
+                               i = scan_ip (&ipm, mask, 0);
+                               return i != 32;
+                       }
+                       return -1;
+               }
+               i *= 10;
+               i += *p - '0';
+               p++;
+       }
+  }
+  if (i > 32 || i < 0)
+         return -1;
+  msk = 0x80000000;
+  *mask = 0;
+  while (i > 0) {
+       *mask |= msk;
+       msk >>= 1;
+       i--;
+  }
+  return 0;
+}
+
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+static void free_config_lines(Htaccess **pprev)
+{
+    Htaccess *prev = *pprev;
+
+    while( prev ) {
+       Htaccess *cur = prev;
+
+       prev = cur->next;
+       free(cur);
+    }
+    *pprev = NULL;
+}
+#endif
+
+/* flag */
+#define FIRST_PARSE          0
+#define SUBDIR_PARSE         1
+#define SIGNALED_PARSE       2
+#define FIND_FROM_HTTPD_ROOT 3
+/****************************************************************************
+ *
+ > $Function: parse_conf()
+ *
+ * $Description: parse configuration file into in-memory linked list.
+ *
+ * The first non-white character is examined to determine if the config line
+ * is one of the following:
+ *    .ext:mime/type   # new mime type not compiled into httpd
+ *    [adAD]:from      # ip address allow/deny, * for wildcard
+ *    /path:user:pass  # username/password
+ *
+ * Any previous IP rules are discarded.
+ * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
+ * are also discarded.  That is, previous settings are retained if flag is
+ * SUBDIR_PARSE.
+ *
+ * $Parameters:
+ *      (const char *) path . . null for ip address checks, path for password
+ *                              checks.
+ *      (int) flag  . . . . . . the source of the parse request.
+ *
+ * $Return: (None)
+ *
+ ****************************************************************************/
+static void parse_conf(const char *path, int flag)
+{
+    FILE *f;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+    Htaccess *prev, *cur;
+#elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+    Htaccess *cur;
+#endif
+
+    const char *cf = config->configFile;
+    char buf[160];
+    char *p0 = NULL;
+    char *c, *p;
+
+    /* free previous ip setup if present */
+    Htaccess_IP *pip = config->ip_a_d;
+
+    while( pip ) {
+       Htaccess_IP *cur_ipl = pip;
+
+       pip = cur_ipl->next;
+       free(cur_ipl);
+    }
+    config->ip_a_d = NULL;
+
+    config->flg_deny_all = 0;
+
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+    /* retain previous auth and mime config only for subdir parse */
+    if(flag != SUBDIR_PARSE) {
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+       free_config_lines(&config->auth);
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+       free_config_lines(&config->mime_a);
+#endif
+    }
+#endif
+
+    if(flag == SUBDIR_PARSE || cf == NULL) {
+       cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
+       if(cf == NULL) {
+           if(flag == FIRST_PARSE)
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
+           return;
+       }
+       sprintf((char *)cf, "%s/%s", path, httpd_conf);
+    }
+
+    while((f = fopen(cf, "r")) == NULL) {
+       if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
+           /* config file not found, no changes to config */
+           return;
+       }
+       if(config->configFile && flag == FIRST_PARSE) /* if -c option given */
+           bb_perror_msg_and_die("%s", cf);
+       flag = FIND_FROM_HTTPD_ROOT;
+       cf = httpd_conf;
+    }
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+    prev = config->auth;
+#endif
+    /* This could stand some work */
+    while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) {
+       c = NULL;
+       for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
+               if(!isspace(*p0)) {
+                   *p++ = *p0;
+                   if(*p0 == ':' && c == NULL)
+                       c = p;
+               }
+       }
+       *p = 0;
+
+       /* test for empty or strange line */
+       if (c == NULL || *c == 0)
+           continue;
+       p0 = buf;
+       if(*p0 == 'd')
+           *p0 = 'D';
+       if(*c == '*') {
+           if(*p0 == 'D') {
+               /* memorize deny all */
+               config->flg_deny_all++;
+           }
+           /* skip default other "word:*" config lines */
+           continue;
+       }
+
+       if(*p0 == 'a')
+           *p0 = 'A';
+       else if(*p0 != 'D' && *p0 != 'A'
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+          && *p0 != '/'
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+          && *p0 != '.'
+#endif
+         )
+              continue;
+       if(*p0 == 'A' || *p0 == 'D') {
+               /* storing current config IP line */
+               pip = calloc(1, sizeof(Htaccess_IP));
+               if(pip) {
+                   if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) {
+                       /* syntax IP{/mask} error detected, protect all */
+                       *p0 = 'D';
+                       pip->mask = 0;
+                   }
+                   pip->allow_deny = *p0;
+                   if(*p0 == 'D') {
+                       /* Deny:form_IP move top */
+                       pip->next = config->ip_a_d;
+                       config->ip_a_d = pip;
+                   } else {
+                       /* add to bottom A:form_IP config line */
+                       Htaccess_IP *prev_IP = config->ip_a_d;
+
+                       if(prev_IP == NULL) {
+                               config->ip_a_d = pip;
+                       } else {
+                               while(prev_IP->next)
+                                       prev_IP = prev_IP->next;
+                               prev_IP->next = pip;
+                       }
+                   }
+               }
+               continue;
+       }
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+       if(*p0 == '/') {
+           /* make full path from httpd root / curent_path / config_line_path */
+           cf = flag == SUBDIR_PARSE ? path : "";
+           p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
+           if(p0 == NULL)
+               continue;
+           c[-1] = 0;
+           sprintf(p0, "/%s%s", cf, buf);
+
+           /* another call bb_simplify_path */
+           cf = p = p0;
+
+           do {
+                   if (*p == '/') {
+                       if (*cf == '/') {    /* skip duplicate (or initial) slash */
+                           continue;
+                       } else if (*cf == '.') {
+                           if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
+                               continue;
+                           } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
+                               ++cf;
+                               if (p > p0) {
+                                   while (*--p != '/');    /* omit previous dir */
+                               }
+                               continue;
+                           }
+                       }
+                   }
+                   *++p = *cf;
+           } while (*++cf);
+
+           if ((p == p0) || (*p != '/')) {      /* not a trailing slash */
+               ++p;                             /* so keep last character */
+           }
+           *p = 0;
+           sprintf(p0, "%s:%s", p0, c);
+       }
+#endif
+
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+       /* storing current config line */
+       cur = calloc(1, sizeof(Htaccess) + strlen(p0));
+       if(cur) {
+           cf = strcpy(cur->before_colon, p0);
+           c = strchr(cf, ':');
+           *c++ = 0;
+           cur->after_colon = c;
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+           if(*cf == '.') {
+               /* config .mime line move top for overwrite previous */
+               cur->next = config->mime_a;
+               config->mime_a = cur;
+               continue;
+           }
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+           free(p0);
+           if(prev == NULL) {
+               /* first line */
+               config->auth = prev = cur;
+           } else {
+               /* sort path, if current lenght eq or bigger then move up */
+               Htaccess *prev_hti = config->auth;
+               int l = strlen(cf);
+               Htaccess *hti;
+
+               for(hti = prev_hti; hti; hti = hti->next) {
+                   if(l >= strlen(hti->before_colon)) {
+                       /* insert before hti */
+                       cur->next = hti;
+                       if(prev_hti != hti) {
+                           prev_hti->next = cur;
+                       } else {
+                           /* insert as top */
+                           config->auth = cur;
+                       }
+                       break;
+                   }
+                   if(prev_hti != hti)
+                           prev_hti = prev_hti->next;
+               }
+               if(!hti)  {       /* not inserted, add to bottom */
+                   prev->next = cur;
+                   prev = cur;
+               }
+           }
+#endif
+       }
+#endif
+   }
+   fclose(f);
+}
+
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+/****************************************************************************
+ *
+ > $Function: encodeString()
+ *
+ * $Description: Given a string, html encode special characters.
+ *   This is used for the -e command line option to provide an easy way
+ *   for scripts to encode result data without confusing browsers.  The
+ *   returned string pointer is memory allocated by malloc().
+ *
+ * $Parameters:
+ *      (const char *) string . . The first string to encode.
+ *
+ * $Return: (char *) . . . .. . . A pointer to the encoded string.
+ *
+ * $Errors: Returns a null string ("") if memory is not available.
+ *
+ ****************************************************************************/
+static char *encodeString(const char *string)
+{
+  /* take the simple route and encode everything */
+  /* could possibly scan once to get length.     */
+  int len = strlen(string);
+  char *out = malloc(len*5 +1);
+  char *p=out;
+  char ch;
+
+  if (!out) return "";
+  while ((ch = *string++)) {
+    // very simple check for what to encode
+    if (isalnum(ch)) *p++ = ch;
+    else p += sprintf(p, "&#%d", (unsigned char) ch);
+  }
+  *p=0;
+  return out;
+}
+#endif          /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
+
+/****************************************************************************
+ *
+ > $Function: decodeString()
+ *
+ * $Description: Given a URL encoded string, convert it to plain ascii.
+ *   Since decoding always makes strings smaller, the decode is done in-place.
+ *   Thus, callers should strdup() the argument if they do not want the
+ *   argument modified.  The return is the original pointer, allowing this
+ *   function to be easily used as arguments to other functions.
+ *
+ * $Parameters:
+ *      (char *) string . . . The first string to decode.
+ *      (int)    flag   . . . 1 if require decode '+' as ' ' for CGI
+ *
+ * $Return: (char *)  . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static char *decodeString(char *orig, int flag_plus_to_space)
+{
+  /* note that decoded string is always shorter than original */
+  char *string = orig;
+  char *ptr = string;
+
+  while (*ptr)
+  {
+    if (*ptr == '+' && flag_plus_to_space)    { *string++ = ' '; ptr++; }
+    else if (*ptr != '%') *string++ = *ptr++;
+    else  {
+      unsigned int value;
+      sscanf(ptr+1, "%2X", &value);
+      *string++ = value;
+      ptr += 3;
+    }
+  }
+  *string = '\0';
+  return orig;
+}
+
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+/****************************************************************************
+ *
+ > $Function: addEnv()
+ *
+ * $Description: Add an environment variable setting to the global list.
+ *    A NAME=VALUE string is allocated, filled, and added to the list of
+ *    environment settings passed to the cgi execution script.
+ *
+ * $Parameters:
+ *  (char *) name_before_underline - The first part environment variable name.
+ *  (char *) name_after_underline  - The second part environment variable name.
+ *  (char *) value  . . The value to which the env variable is set.
+ *
+ * $Return: (void)
+ *
+ * $Errors: Silently returns if the env runs out of space to hold the new item
+ *
+ ****************************************************************************/
+static void addEnv(const char *name_before_underline,
+                       const char *name_after_underline, const char *value)
+{
+  char *s = NULL;
+  const char *underline;
+
+  if (!value)
+       value = "";
+  underline = *name_after_underline ? "_" : "";
+  asprintf(&s, "%s%s%s=%s", name_before_underline, underline,
+                                       name_after_underline, value);
+  if(s) {
+    putenv(s);
+  }
+}
+
+#if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || !defined(CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY)
+/* set environs SERVER_PORT and REMOTE_PORT */
+static void addEnvPort(const char *port_name)
+{
+      char buf[16];
+
+      sprintf(buf, "%u", config->port);
+      addEnv(port_name, "PORT", buf);
+}
+#endif
+#endif          /* CONFIG_FEATURE_HTTPD_CGI */
+
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+/****************************************************************************
+ *
+ > $Function: decodeBase64()
+ *
+ > $Description: Decode a base 64 data stream as per rfc1521.
+ *    Note that the rfc states that none base64 chars are to be ignored.
+ *    Since the decode always results in a shorter size than the input, it is
+ *    OK to pass the input arg as an output arg.
+ *
+ * $Parameter:
+ *      (char *) Data . . . . A pointer to a base64 encoded string.
+ *                            Where to place the decoded data.
+ *
+ * $Return: void
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static void decodeBase64(char *Data)
+{
+
+  const unsigned char *in = Data;
+  // The decoded size will be at most 3/4 the size of the encoded
+  unsigned long ch = 0;
+  int i = 0;
+
+  while (*in) {
+    int t = *in++;
+
+    if(t >= '0' && t <= '9')
+       t = t - '0' + 52;
+    else if(t >= 'A' && t <= 'Z')
+       t = t - 'A';
+    else if(t >= 'a' && t <= 'z')
+       t = t - 'a' + 26;
+    else if(t == '+')
+       t = 62;
+    else if(t == '/')
+       t = 63;
+    else if(t == '=')
+       t = 0;
+    else
+       continue;
+
+    ch = (ch << 6) | t;
+    i++;
+    if (i == 4) {
+       *Data++ = (char) (ch >> 16);
+       *Data++ = (char) (ch >> 8);
+       *Data++ = (char) ch;
+       i = 0;
+    }
+  }
+  *Data = 0;
+}
+#endif
+
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+/****************************************************************************
+ *
+ > $Function: openServer()
+ *
+ * $Description: create a listen server socket on the designated port.
+ *
+ * $Return: (int)  . . . A connection socket. -1 for errors.
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int openServer(void)
+{
+  struct sockaddr_in lsocket;
+  int fd;
+
+  /* create the socket right now */
+  /* inet_addr() returns a value that is already in network order */
+  memset(&lsocket, 0, sizeof(lsocket));
+  lsocket.sin_family = AF_INET;
+  lsocket.sin_addr.s_addr = INADDR_ANY;
+  lsocket.sin_port = htons(config->port) ;
+  fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (fd >= 0) {
+    /* tell the OS it's OK to reuse a previous address even though */
+    /* it may still be in a close down state.  Allows bind to succeed. */
+    int on = 1;
+#ifdef SO_REUSEPORT
+    setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
+#else
+    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
+#endif
+    if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) {
+      listen(fd, 9);
+      signal(SIGCHLD, SIG_IGN);   /* prevent zombie (defunct) processes */
+    } else {
+       bb_perror_msg_and_die("bind");
+    }
+  } else {
+       bb_perror_msg_and_die("create socket");
+  }
+  return fd;
+}
+#endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+
+/****************************************************************************
+ *
+ > $Function: sendHeaders()
+ *
+ * $Description: Create and send HTTP response headers.
+ *   The arguments are combined and sent as one write operation.  Note that
+ *   IE will puke big-time if the headers are not sent in one packet and the
+ *   second packet is delayed for any reason.
+ *
+ * $Parameter:
+ *      (HttpResponseNum) responseNum . . . The result code to send.
+ *
+ * $Return: (int)  . . . . writing errors
+ *
+ ****************************************************************************/
+static int sendHeaders(HttpResponseNum responseNum)
+{
+  char *buf = config->buf;
+  const char *responseString = "";
+  const char *infoString = 0;
+  const char *mime_type;
+  unsigned int i;
+  time_t timer = time(0);
+  char timeStr[80];
+  int len;
+
+  for (i = 0;
+       i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) {
+               if (httpResponseNames[i].type == responseNum) {
+                       responseString = httpResponseNames[i].name;
+                       infoString = httpResponseNames[i].info;
+                       break;
+               }
+  }
+  /* error message is HTML */
+  mime_type = responseNum == HTTP_OK ?
+               config->httpd_found.found_mime_type : "text/html";
+
+  /* emit the current date */
+  strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
+  len = sprintf(buf,
+       "HTTP/1.0 %d %s\nContent-type: %s\r\n"
+       "Date: %s\r\nConnection: close\r\n",
+         responseNum, responseString, mime_type, timeStr);
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  if (responseNum == HTTP_UNAUTHORIZED) {
+    len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+                                                           config->realm);
+  }
+#endif
+  if(responseNum == HTTP_MOVED_TEMPORARILY) {
+       len += sprintf(buf+len, "Location: %s/%s%s\r\n",
+               config->httpd_found.found_moved_temporarily,
+               (config->query ? "?" : ""),
+               (config->query ? config->query : ""));
+  }
+
+  if (config->ContentLength != -1) {    /* file */
+    strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
+    len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\r\n",
+                             timeStr, Content_length, config->ContentLength);
+  }
+  strcat(buf, "\r\n");
+  len += 2;
+  if (infoString) {
+    len += sprintf(buf+len,
+           "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
+           "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
+           responseNum, responseString,
+           responseNum, responseString, infoString);
+  }
+#ifdef DEBUG
+  if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf);
+#endif
+  return bb_full_write(a_c_w, buf, len);
+}
+
+/****************************************************************************
+ *
+ > $Function: getLine()
+ *
+ * $Description: Read from the socket until an end of line char found.
+ *
+ *   Characters are read one at a time until an eol sequence is found.
+ *
+ * $Return: (int) . . . . number of characters read.  -1 if error.
+ *
+ ****************************************************************************/
+static int getLine(void)
+{
+  int  count = 0;
+  char *buf = config->buf;
+
+  while (read(a_c_r, buf + count, 1) == 1) {
+    if (buf[count] == '\r') continue;
+    if (buf[count] == '\n') {
+      buf[count] = 0;
+      return count;
+    }
+    if(count < (MAX_MEMORY_BUFF-1))      /* check owerflow */
+       count++;
+  }
+  if (count) return count;
+  else return -1;
+}
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+/****************************************************************************
+ *
+ > $Function: sendCgi()
+ *
+ * $Description: Execute a CGI script and send it's stdout back
+ *
+ *   Environment variables are set up and the script is invoked with pipes
+ *   for stdin/stdout.  If a post is being done the script is fed the POST
+ *   data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
+ *
+ * $Parameters:
+ *      (const char *) url . . . . . . The requested URL (with leading /).
+ *      (int bodyLen)  . . . . . . . . Length of the post body.
+ *      (const char *cookie) . . . . . For set HTTP_COOKIE.
+ *      (const char *content_type) . . For set CONTENT_TYPE.
+
+ *
+ * $Return: (char *)  . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int sendCgi(const char *url,
+                  const char *request, int bodyLen, const char *cookie,
+                  const char *content_type)
+{
+  int fromCgi[2];  /* pipe for reading data from CGI */
+  int toCgi[2];    /* pipe for sending data to CGI */
+
+  static char * argp[] = { 0, 0 };
+  int pid = 0;
+  int inFd;
+  int outFd;
+  int firstLine = 1;
+
+  do {
+    if (pipe(fromCgi) != 0) {
+      break;
+    }
+    if (pipe(toCgi) != 0) {
+      break;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+       pid = 0;
+       break;
+    }
+
+    if (!pid) {
+      /* child process */
+      char *script;
+      char *purl = strdup( url );
+      char realpath_buff[MAXPATHLEN];
+
+      if(purl == NULL)
+       _exit(242);
+
+      inFd  = toCgi[0];
+      outFd = fromCgi[1];
+
+      dup2(inFd, 0);  // replace stdin with the pipe
+      dup2(outFd, 1);  // replace stdout with the pipe
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+      if (!config->debugHttpd)
+#endif
+       dup2(outFd, 2);  // replace stderr with the pipe
+
+      close(toCgi[0]);
+      close(toCgi[1]);
+      close(fromCgi[0]);
+      close(fromCgi[1]);
+
+      /*
+       * Find PATH_INFO.
+       */
+      script = purl;
+      while((script = strchr( script + 1, '/' )) != NULL) {
+       /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
+       struct stat sb;
+
+       *script = '\0';
+       if(is_directory(purl + 1, 1, &sb) == 0) {
+               /* not directory, found script.cgi/PATH_INFO */
+               *script = '/';
+               break;
+       }
+       *script = '/';          /* is directory, find next '/' */
+      }
+      addEnv("PATH", "INFO", script);   /* set /PATH_INFO or NULL */
+      addEnv("PATH",           "",         getenv("PATH"));
+      addEnv("REQUEST",        "METHOD",   request);
+      if(config->query) {
+       char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
+       if(uri)
+           sprintf(uri, "%s?%s", purl, config->query);
+       addEnv("REQUEST",        "URI",   uri);
+      } else {
+       addEnv("REQUEST",        "URI",   purl);
+      }
+      if(script != NULL)
+       *script = '\0';         /* reduce /PATH_INFO */
+      /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
+      addEnv("SCRIPT_NAME",    "",         purl);
+      addEnv("QUERY_STRING",   "",         config->query);
+      addEnv("SERVER",         "SOFTWARE", httpdVersion);
+      addEnv("SERVER",         "PROTOCOL", "HTTP/1.0");
+      addEnv("GATEWAY_INTERFACE", "",      "CGI/1.1");
+      addEnv("REMOTE",         "ADDR",     config->rmt_ip_str);
+#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+      addEnvPort("REMOTE");
+#endif
+      if(bodyLen) {
+       char sbl[32];
+
+       sprintf(sbl, "%d", bodyLen);
+       addEnv("CONTENT", "LENGTH", sbl);
+      }
+      if(cookie)
+       addEnv("HTTP", "COOKIE", cookie);
+      if(content_type)
+       addEnv("CONTENT", "TYPE", content_type);
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+      if(config->remoteuser) {
+       addEnv("REMOTE", "USER", config->remoteuser);
+       addEnv("AUTH_TYPE", "", "Basic");
+      }
+#endif
+      if(config->referer)
+       addEnv("HTTP", "REFERER", config->referer);
+
+       /* set execve argp[0] without path */
+      argp[0] = strrchr( purl, '/' ) + 1;
+       /* but script argp[0] must have absolute path and chdiring to this */
+      if(realpath(purl + 1, realpath_buff) != NULL) {
+           script = strrchr(realpath_buff, '/');
+           if(script) {
+               *script = '\0';
+               if(chdir(realpath_buff) == 0) {
+                 *script = '/';
+                 // now run the program.  If it fails,
+                 // use _exit() so no destructors
+                 // get called and make a mess.
+                 execv(realpath_buff, argp);
+               }
+           }
+      }
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+      config->accepted_socket = 1;      /* send to stdout */
+#endif
+      sendHeaders(HTTP_NOT_FOUND);
+      _exit(242);
+    } /* end child */
+
+  } while (0);
+
+  if (pid) {
+    /* parent process */
+    int status;
+    size_t post_readed_size = 0, post_readed_idx = 0;
+
+    inFd  = fromCgi[0];
+    outFd = toCgi[1];
+    close(fromCgi[1]);
+    close(toCgi[0]);
+    signal(SIGPIPE, SIG_IGN);
+
+    while (1) {
+      fd_set readSet;
+      fd_set writeSet;
+      char wbuf[128];
+      int nfound;
+      int count;
+
+      FD_ZERO(&readSet);
+      FD_ZERO(&writeSet);
+      FD_SET(inFd, &readSet);
+      if(bodyLen > 0 || post_readed_size > 0) {
+       FD_SET(outFd, &writeSet);
+       nfound = outFd > inFd ? outFd : inFd;
+       if(post_readed_size == 0) {
+               FD_SET(a_c_r, &readSet);
+               if(nfound < a_c_r)
+                       nfound = a_c_r;
+       }
+      /* Now wait on the set of sockets! */
+       nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL);
+      } else {
+       if(!bodyLen) {
+               close(outFd);
+               bodyLen = -1;
+       }
+       nfound = select(inFd + 1, &readSet, 0, 0, NULL);
+      }
+
+      if (nfound <= 0) {
+       if (waitpid(pid, &status, WNOHANG) > 0) {
+         close(inFd);
+#ifdef DEBUG
+         if (config->debugHttpd) {
+           if (WIFEXITED(status))
+             bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
+           if (WIFSIGNALED(status))
+             bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
+         }
+#endif
+         break;
+       }
+      } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) {
+               count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size);
+               if(count > 0) {
+                       post_readed_size -= count;
+                       post_readed_idx += count;
+                       if(post_readed_size == 0)
+                               post_readed_idx = 0;
+               }
+      } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) {
+               count = bodyLen > sizeof(wbuf) ? sizeof(wbuf) : bodyLen;
+               count = safe_read(a_c_r, wbuf, count);
+               if(count > 0) {
+                       post_readed_size += count;
+                       bodyLen -= count;
+      } else {
+                       bodyLen = 0;    /* closed */
+               }
+      }
+      if(FD_ISSET(inFd, &readSet)) {
+       int s = a_c_w;
+       char *rbuf = config->buf;
+
+#ifndef PIPE_BUF
+# define PIPESIZE 4096          /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+#if PIPESIZE >= MAX_MEMORY_BUFF
+# error "PIPESIZE >= MAX_MEMORY_BUFF"
+#endif
+
+       // There is something to read
+       count = safe_read(inFd, rbuf, PIPESIZE);
+       if (count == 0)
+               break;  /* closed */
+       if (count > 0) {
+         if (firstLine) {
+           rbuf[count] = 0;
+           /* check to see if the user script added headers */
+           if(strncmp(rbuf, "HTTP/1.0 200 OK\n", 4) != 0) {
+             bb_full_write(s, "HTTP/1.0 200 OK\n", 16);
+           }
+           if (strstr(rbuf, "ontent-") == 0) {
+             bb_full_write(s, "Content-type: text/plain\n\n", 26);
+           }
+           firstLine = 0;
+         }
+         if (bb_full_write(s, rbuf, count) != count)
+             break;
+
+#ifdef DEBUG
+         if (config->debugHttpd)
+               fprintf(stderr, "cgi read %d bytes\n", count);
+#endif
+       }
+      }
+    }
+  }
+  return 0;
+}
+#endif          /* CONFIG_FEATURE_HTTPD_CGI */
+
+/****************************************************************************
+ *
+ > $Function: sendFile()
+ *
+ * $Description: Send a file response to an HTTP request
+ *
+ * $Parameter:
+ *      (const char *) url . . The URL requested.
+ *
+ * $Return: (int)  . . . . . . Always 0.
+ *
+ ****************************************************************************/
+static int sendFile(const char *url)
+{
+  char * suffix;
+  int  f;
+  const char * const * table;
+  const char * try_suffix;
+
+  suffix = strrchr(url, '.');
+
+  for (table = suffixTable; *table; table += 2)
+       if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
+               try_suffix += strlen(suffix);
+               if(*try_suffix == 0 || *try_suffix == '.')
+                       break;
+       }
+  /* also, if not found, set default as "application/octet-stream";  */
+  config->httpd_found.found_mime_type = *(table+1);
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+  if (suffix) {
+    Htaccess * cur;
+
+    for (cur = config->mime_a; cur; cur = cur->next) {
+       if(strcmp(cur->before_colon, suffix) == 0) {
+               config->httpd_found.found_mime_type = cur->after_colon;
+               break;
+       }
+    }
+  }
+#endif  /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
+
+#ifdef DEBUG
+    if (config->debugHttpd)
+       fprintf(stderr, "Sending file '%s' Content-type: %s\n",
+                                       url, config->httpd_found.found_mime_type);
+#endif
+
+  f = open(url, O_RDONLY);
+  if (f >= 0) {
+       int count;
+       char *buf = config->buf;
+
+       sendHeaders(HTTP_OK);
+       while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
+               if (bb_full_write(a_c_w, buf, count) != count)
+                       break;
+       }
+       close(f);
+  } else {
+#ifdef DEBUG
+       if (config->debugHttpd)
+               bb_perror_msg("Unable to open '%s'", url);
+#endif
+       sendHeaders(HTTP_NOT_FOUND);
+  }
+
+  return 0;
+}
+
+static int checkPermIP(void)
+{
+    Htaccess_IP * cur;
+
+    /* This could stand some work */
+    for (cur = config->ip_a_d; cur; cur = cur->next) {
+#ifdef DEBUG
+       if (config->debugHttpd) {
+           fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
+           fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
+               (unsigned char)(cur->ip >> 24),
+               (unsigned char)(cur->ip >> 16),
+               (unsigned char)(cur->ip >> 8),
+                               cur->ip & 0xff,
+               (unsigned char)(cur->mask >> 24),
+               (unsigned char)(cur->mask >> 16),
+               (unsigned char)(cur->mask >> 8),
+                               cur->mask & 0xff);
+       }
+#endif
+       if((config->rmt_ip & cur->mask) == cur->ip)
+           return cur->allow_deny == 'A';   /* Allow/Deny */
+    }
+
+    /* if unconfigured, return 1 - access from all */
+    return !config->flg_deny_all;
+}
+
+/****************************************************************************
+ *
+ > $Function: checkPerm()
+ *
+ * $Description: Check the permission file for access password protected.
+ *
+ *   If config file isn't present, everything is allowed.
+ *   Entries are of the form you can see example from header source
+ *
+ * $Parameters:
+ *      (const char *) path  . . . . The file path.
+ *      (const char *) request . . . User information to validate.
+ *
+ * $Return: (int)  . . . . . . . . . 1 if request OK, 0 otherwise.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+static int checkPerm(const char *path, const char *request)
+{
+    Htaccess * cur;
+    const char *p;
+    const char *p0;
+
+    const char *prev = NULL;
+
+    /* This could stand some work */
+    for (cur = config->auth; cur; cur = cur->next) {
+       p0 = cur->before_colon;
+       if(prev != NULL && strcmp(prev, p0) != 0)
+           continue;       /* find next identical */
+       p = cur->after_colon;
+#ifdef DEBUG
+       if (config->debugHttpd)
+           fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request);
+#endif
+       {
+           int l = strlen(p0);
+
+           if(strncmp(p0, path, l) == 0 &&
+                           (l == 1 || path[l] == '/' || path[l] == 0)) {
+               char *u;
+               /* path match found.  Check request */
+               /* for check next /path:user:password */
+               prev = p0;
+               u = strchr(request, ':');
+               if(u == NULL) {
+                       /* bad request, ':' required */
+                       break;
+                       }
+
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+               {
+                       char *cipher;
+                       char *pp;
+
+                       if(strncmp(p, request, u-request) != 0) {
+                               /* user uncompared */
+                               continue;
+                       }
+                       pp = strchr(p, ':');
+                       if(pp && pp[1] == '$' && pp[2] == '1' &&
+                                                pp[3] == '$' && pp[4]) {
+                               pp++;
+                               cipher = pw_encrypt(u+1, pp);
+                               if (strcmp(cipher, pp) == 0)
+                                       goto set_remoteuser_var;   /* Ok */
+                               /* unauthorized */
+                               continue;
+                       }
+               }
+#endif
+               if (strcmp(p, request) == 0) {
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+set_remoteuser_var:
+#endif
+                   config->remoteuser = strdup(request);
+                   if(config->remoteuser)
+                       config->remoteuser[(u - request)] = 0;
+                   return 1;   /* Ok */
+               }
+               /* unauthorized */
+           }
+       }
+    }   /* for */
+
+    return prev == NULL;
+}
+
+#endif  /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+
+/****************************************************************************
+ *
+ > $Function: handleIncoming()
+ *
+ * $Description: Handle an incoming http request.
+ *
+ ****************************************************************************/
+
+static void
+handle_sigalrm( int sig )
+{
+    sendHeaders(HTTP_REQUEST_TIMEOUT);
+    config->alarm_signaled = sig;
+}
+
+/****************************************************************************
+ *
+ > $Function: handleIncoming()
+ *
+ * $Description: Handle an incoming http request.
+ *
+ ****************************************************************************/
+static void handleIncoming(void)
+{
+  char *buf = config->buf;
+  char *url;
+  char *purl;
+  int  blank = -1;
+  char *test;
+  struct stat sb;
+  int ip_allowed;
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+  const char *prequest = request_GET;
+  long length=0;
+  char *cookie = 0;
+  char *content_type = 0;
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  fd_set s_fd;
+  struct timeval tv;
+  int retval;
+#endif
+  struct sigaction sa;
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  int credentials = -1;  /* if not requred this is Ok */
+#endif
+
+  sa.sa_handler = handle_sigalrm;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0; /* no SA_RESTART */
+  sigaction(SIGALRM, &sa, NULL);
+
+  do {
+    int  count;
+
+    (void) alarm( TIMEOUT );
+    if (getLine() <= 0)
+       break;  /* closed */
+
+    purl = strpbrk(buf, " \t");
+    if(purl == NULL) {
+BAD_REQUEST:
+      sendHeaders(HTTP_BAD_REQUEST);
+      break;
+    }
+    *purl = 0;
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+    if(strcasecmp(buf, prequest) != 0) {
+       prequest = "POST";
+       if(strcasecmp(buf, prequest) != 0) {
+           sendHeaders(HTTP_NOT_IMPLEMENTED);
+           break;
+       }
+    }
+#else
+    if(strcasecmp(buf, request_GET) != 0) {
+       sendHeaders(HTTP_NOT_IMPLEMENTED);
+       break;
+    }
+#endif
+    *purl = ' ';
+    count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
+
+    decodeString(buf, 0);
+    if (count < 1 || buf[0] != '/') {
+      /* Garbled request/URL */
+      goto BAD_REQUEST;
+    }
+    url = alloca(strlen(buf) + 12);      /* + sizeof("/index.html\0") */
+    if(url == NULL) {
+       sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
+       break;
+    }
+    strcpy(url, buf);
+    /* extract url args if present */
+    test = strchr(url, '?');
+    if (test) {
+      *test++ = 0;
+      config->query = test;
+    }
+
+    /* algorithm stolen from libbb bb_simplify_path(),
+       but don`t strdup and reducing trailing slash and protect out root */
+    purl = test = url;
+
+    do {
+       if (*purl == '/') {
+           if (*test == '/') {        /* skip duplicate (or initial) slash */
+               continue;
+           } else if (*test == '.') {
+               if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
+                   continue;
+               } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
+                   ++test;
+                   if (purl == url) {
+                       /* protect out root */
+                       goto BAD_REQUEST;
+                   }
+                   while (*--purl != '/');    /* omit previous dir */
+                   continue;
+               }
+           }
+       }
+       *++purl = *test;
+    } while (*++test);
+
+    *++purl = 0;        /* so keep last character */
+    test = purl;        /* end ptr */
+
+    /* If URL is directory, adding '/' */
+    /* If URL is directory, adding '/' */
+    if(test[-1] != '/') {
+           if ( is_directory(url + 1, 1, &sb) ) {
+                   config->httpd_found.found_moved_temporarily = url;
+           }
+    }
+#ifdef DEBUG
+    if (config->debugHttpd)
+       fprintf(stderr, "url='%s', args=%s\n", url, config->query);
+#endif
+
+    test = url;
+    ip_allowed = checkPermIP();
+    while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
+       /* have path1/path2 */
+       *test = '\0';
+       if( is_directory(url + 1, 1, &sb) ) {
+               /* may be having subdir config */
+               parse_conf(url + 1, SUBDIR_PARSE);
+               ip_allowed = checkPermIP();
+       }
+       *test = '/';
+    }
+
+    // read until blank line for HTTP version specified, else parse immediate
+    while (blank >= 0 && alarm(TIMEOUT) >= 0 && (count = getLine()) > 0) {
+
+#ifdef DEBUG
+      if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf);
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+      /* try and do our best to parse more lines */
+      if ((strncasecmp(buf, Content_length, 15) == 0)) {
+       if(prequest != request_GET)
+               length = strtol(buf + 15, 0, 0); // extra read only for POST
+      } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
+               for(test = buf + 7; isspace(*test); test++)
+                       ;
+               cookie = strdup(test);
+      } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) {
+               for(test = buf + 13; isspace(*test); test++)
+                       ;
+               content_type = strdup(test);
+      } else if ((strncasecmp(buf, "Referer:", 8) == 0)) {
+               for(test = buf + 8; isspace(*test); test++)
+                       ;
+               config->referer = strdup(test);
+      }
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+      if (strncasecmp(buf, "Authorization:", 14) == 0) {
+       /* We only allow Basic credentials.
+        * It shows up as "Authorization: Basic <userid:password>" where
+        * the userid:password is base64 encoded.
+        */
+       for(test = buf + 14; isspace(*test); test++)
+               ;
+       if (strncasecmp(test, "Basic", 5) != 0)
+               continue;
+
+       test += 5;  /* decodeBase64() skiping space self */
+       decodeBase64(test);
+       credentials = checkPerm(url, test);
+      }
+#endif          /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+
+    }   /* while extra header reading */
+
+    (void) alarm( 0 );
+    if(config->alarm_signaled)
+       break;
+
+    if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
+               /* protect listing [/path]/httpd_conf or IP deny */
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+FORBIDDEN:      /* protect listing /cgi-bin */
+#endif
+               sendHeaders(HTTP_FORBIDDEN);
+               break;
+    }
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+    if (credentials <= 0 && checkPerm(url, ":") == 0) {
+      sendHeaders(HTTP_UNAUTHORIZED);
+      break;
+    }
+#endif
+
+    if(config->httpd_found.found_moved_temporarily) {
+       sendHeaders(HTTP_MOVED_TEMPORARILY);
+#ifdef DEBUG
+       /* clear unforked memory flag */
+       if(config->debugHttpd)
+               config->httpd_found.found_moved_temporarily = NULL;
+#endif
+       break;
+    }
+
+    test = url + 1;      /* skip first '/' */
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+    /* if strange Content-Length */
+    if (length < 0)
+       break;
+
+    if (strncmp(test, "cgi-bin", 7) == 0) {
+               if(test[7] == '/' && test[8] == 0)
+                       goto FORBIDDEN;     // protect listing cgi-bin/
+               sendCgi(url, prequest, length, cookie, content_type);
+    } else {
+       if (prequest != request_GET)
+               sendHeaders(HTTP_NOT_IMPLEMENTED);
+       else {
+#endif  /* CONFIG_FEATURE_HTTPD_CGI */
+               if(purl[-1] == '/')
+                       strcpy(purl, "index.html");
+               if ( stat(test, &sb ) == 0 ) {
+                       config->ContentLength = sb.st_size;
+                       config->last_mod = sb.st_mtime;
+               }
+               sendFile(test);
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+               /* unset if non inetd looped */
+               config->ContentLength = -1;
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+       }
+    }
+#endif
+
+  } while (0);
+
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+/* from inetd don`t looping: freeing, closing automatic from exit always */
+# ifdef DEBUG
+  if (config->debugHttpd) fprintf(stderr, "closing socket\n");
+# endif
+# ifdef CONFIG_FEATURE_HTTPD_CGI
+  free(cookie);
+  free(content_type);
+  free(config->referer);
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  free(config->remoteuser);
+#endif
+# endif
+  shutdown(a_c_w, SHUT_WR);
+
+  /* Properly wait for remote to closed */
+  FD_ZERO (&s_fd) ;
+  FD_SET (a_c_w, &s_fd) ;
+
+  do {
+    tv.tv_sec = 2 ;
+    tv.tv_usec = 0 ;
+    retval = select (a_c_w + 1, &s_fd, NULL, NULL, &tv);
+  } while (retval > 0 && (read (a_c_w, buf, sizeof (config->buf)) > 0));
+
+  shutdown(a_c_r, SHUT_RD);
+  close(config->accepted_socket);
+#endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+}
+
+/****************************************************************************
+ *
+ > $Function: miniHttpd()
+ *
+ * $Description: The main http server function.
+ *
+ *   Given an open socket fildes, listen for new connections and farm out
+ *   the processing as a forked process.
+ *
+ * $Parameters:
+ *      (int) server. . . The server socket fildes.
+ *
+ * $Return: (int) . . . . Always 0.
+ *
+ ****************************************************************************/
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+static int miniHttpd(int server)
+{
+  fd_set readfd, portfd;
+
+  FD_ZERO(&portfd);
+  FD_SET(server, &portfd);
+
+  /* copy the ports we are watching to the readfd set */
+  while (1) {
+    readfd = portfd;
+
+    /* Now wait INDEFINITELY on the set of sockets! */
+    if (select(server + 1, &readfd, 0, 0, 0) > 0) {
+      if (FD_ISSET(server, &readfd)) {
+       int on;
+       struct sockaddr_in fromAddr;
+
+       socklen_t fromAddrLen = sizeof(fromAddr);
+       int s = accept(server,
+                      (struct sockaddr *)&fromAddr, &fromAddrLen);
+
+       if (s < 0) {
+           continue;
+       }
+       config->accepted_socket = s;
+       config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
+#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
+       sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
+               (unsigned char)(config->rmt_ip >> 24),
+               (unsigned char)(config->rmt_ip >> 16),
+               (unsigned char)(config->rmt_ip >> 8),
+                               config->rmt_ip & 0xff);
+       config->port = ntohs(fromAddr.sin_port);
+#ifdef DEBUG
+       if (config->debugHttpd) {
+           bb_error_msg("connection from IP=%s, port %u\n",
+                                       config->rmt_ip_str, config->port);
+       }
+#endif
+#endif /* CONFIG_FEATURE_HTTPD_CGI */
+
+       /*  set the KEEPALIVE option to cull dead connections */
+       on = 1;
+       setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
+
+       if (config->debugHttpd || fork() == 0) {
+           /* This is the spawned thread */
+#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+           /* protect reload config, may be confuse checking */
+           signal(SIGHUP, SIG_IGN);
+#endif
+           handleIncoming();
+           if(!config->debugHttpd)
+               exit(0);
+       }
+       close(s);
+      }
+    }
+  } // while (1)
+  return 0;
+}
+
+#else
+    /* from inetd */
+
+static int miniHttpd(void)
+{
+  struct sockaddr_in fromAddrLen;
+  socklen_t sinlen = sizeof (struct sockaddr_in);
+
+  getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
+  config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
+#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
+  sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
+               (unsigned char)(config->rmt_ip >> 24),
+               (unsigned char)(config->rmt_ip >> 16),
+               (unsigned char)(config->rmt_ip >> 8),
+                               config->rmt_ip & 0xff);
+#endif
+  config->port = ntohs(fromAddrLen.sin_port);
+  handleIncoming();
+  return 0;
+}
+#endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+
+#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+static void sighup_handler(int sig)
+{
+       /* set and reset */
+       struct sigaction sa;
+
+       parse_conf(default_path_httpd_conf,
+                   sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
+       sa.sa_handler = sighup_handler;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESTART;
+       sigaction(SIGHUP, &sa, NULL);
+}
+#endif
+
+
+static const char httpd_opts[]="c:d:h:"
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+                               "e:"
+#define OPT_INC_1 1
+#else
+#define OPT_INC_1 0
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+                               "r:"
+# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+                               "m:"
+# define OPT_INC_2 2
+# else
+# define OPT_INC_2 1
+#endif
+#else
+#define OPT_INC_2 0
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+                               "p:v"
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+                               "u:"
+#endif
+#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+                                       ;
+
+#define OPT_CONFIG_FILE (1<<0)
+#define OPT_DECODE_URL  (1<<1)
+#define OPT_HOME_HTTPD  (1<<2)
+#define OPT_ENCODE_URL  (1<<(2+OPT_INC_1))
+#define OPT_REALM       (1<<(3+OPT_INC_1))
+#define OPT_MD5         (1<<(4+OPT_INC_1))
+#define OPT_PORT        (1<<(3+OPT_INC_1+OPT_INC_2))
+#define OPT_DEBUG       (1<<(4+OPT_INC_1+OPT_INC_2))
+#define OPT_SETUID      (1<<(5+OPT_INC_1+OPT_INC_2))
+
+
+#ifdef HTTPD_STANDALONE
+int main(int argc, char *argv[])
+#else
+int httpd_main(int argc, char *argv[])
+#endif
+{
+  unsigned long opt;
+  const char *home_httpd = home;
+  char *url_for_decode;
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+  const char *url_for_encode;
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  const char *s_port;
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  int server;
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+  const char *s_uid;
+  long uid = -1;
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+  const char *pass;
+#endif
+
+  config = xcalloc(1, sizeof(*config));
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  config->realm = "Web Server Authentication";
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  config->port = 80;
+#endif
+
+  config->ContentLength = -1;
+
+  opt = bb_getopt_ulflags(argc, argv, httpd_opts,
+                       &(config->configFile), &url_for_decode, &home_httpd
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+                       , &url_for_encode
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+                       , &(config->realm)
+# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+                       , &pass
+# endif
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+                       , &s_port
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+                       , &s_uid
+#endif
+#endif
+    );
+
+  if(opt & OPT_DECODE_URL) {
+      printf("%s", decodeString(url_for_decode, 1));
+      return 0;
+  }
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+  if(opt & OPT_ENCODE_URL) {
+      printf("%s", encodeString(url_for_encode));
+      return 0;
+  }
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+  if(opt & OPT_MD5) {
+      printf("%s\n", pw_encrypt(pass, "$1$"));
+      return 0;
+  }
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+    if(opt & OPT_PORT)
+       config->port = bb_xgetlarg(s_port, 10, 1, 0xffff);
+    config->debugHttpd = opt & OPT_DEBUG;
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+    if(opt & OPT_SETUID) {
+       char *e;
+
+       uid = strtol(s_uid, &e, 0);
+       if(*e != '\0') {
+               /* not integer */
+               uid = my_getpwnam(s_uid);
+       }
+      }
+#endif
+#endif
+
+  if(chdir(home_httpd)) {
+    bb_perror_msg_and_die("can`t chdir to %s", home_httpd);
+  }
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  server = openServer();
+# ifdef CONFIG_FEATURE_HTTPD_SETUID
+  /* drop privileges */
+  if(uid > 0)
+       setuid(uid);
+# endif
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+   {
+       char *p = getenv("PATH");
+       if(p) {
+               p = bb_xstrdup(p);
+       }
+       clearenv();
+       if(p)
+               setenv("PATH", p, 1);
+# ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+       addEnvPort("SERVER");
+# endif
+   }
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+  sighup_handler(0);
+#else
+  parse_conf(default_path_httpd_conf, FIRST_PARSE);
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  if (!config->debugHttpd) {
+    if (daemon(1, 0) < 0)     /* don`t change curent directory */
+       bb_perror_msg_and_die("daemon");
+  }
+  return miniHttpd(server);
+#else
+  return miniHttpd();
+#endif
+}
diff --git a/busybox/networking/ifconfig.c b/busybox/networking/ifconfig.c
new file mode 100644 (file)
index 0000000..4e3df29
--- /dev/null
@@ -0,0 +1,605 @@
+/* ifconfig
+ *
+ * Similar to the standard Unix ifconfig, but with only the necessary
+ * parts for AF_INET, and without any printing of if info (for now).
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ *
+ * Authors of the original ifconfig was:
+ *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * 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.
+ *
+ * $Id: ifconfig.c,v 1.30 2004/03/31 11:30:08 andersen Exp $
+ *
+ */
+
+/*
+ * Heavily modified by Manuel Novoa III       Mar 6, 2001
+ *
+ * From initial port to busybox, removed most of the redundancy by
+ * converting to a table-driven approach.  Added several (optional)
+ * args missing from initial port.
+ *
+ * Still missing:  media, tunnel.
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>            /* strcmp and friends */
+#include <ctype.h>             /* isdigit and friends */
+#include <stddef.h>            /* offsetof */
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_ether.h>
+#endif
+#include "inet_common.h"
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_IFCONFIG_SLIP
+# include <net/if_slip.h>
+#endif
+
+/* I don't know if this is needed for busybox or not.  Anyone? */
+#define QUESTIONABLE_ALIAS_CASE
+
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+# define SIOCSIFTXQLEN      0x8943
+# define SIOCGIFTXQLEN      0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+# define ifr_qlen        ifr_ifru.ifru_mtu
+#endif
+
+#ifndef IFF_DYNAMIC
+# define IFF_DYNAMIC     0x8000        /* dialup device with changing addresses */
+#endif
+
+#ifdef CONFIG_FEATURE_IPV6
+struct in6_ifreq {
+       struct in6_addr ifr6_addr;
+       uint32_t ifr6_prefixlen;
+       int ifr6_ifindex;
+};
+#endif
+
+/*
+ * Here are the bit masks for the "flags" member of struct options below.
+ * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
+ * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
+ */
+#define N_CLR            0x01
+#define M_CLR            0x02
+#define N_SET            0x04
+#define M_SET            0x08
+#define N_ARG            0x10
+#define M_ARG            0x20
+
+#define M_MASK           (M_CLR | M_SET | M_ARG)
+#define N_MASK           (N_CLR | N_SET | N_ARG)
+#define SET_MASK         (N_SET | M_SET)
+#define CLR_MASK         (N_CLR | M_CLR)
+#define SET_CLR_MASK     (SET_MASK | CLR_MASK)
+#define ARG_MASK         (M_ARG | N_ARG)
+
+/*
+ * Here are the bit masks for the "arg_flags" member of struct options below.
+ */
+
+/*
+ * cast type:
+ *   00 int
+ *   01 char *
+ *   02 HOST_COPY in_ether
+ *   03 HOST_COPY INET_resolve
+ */
+#define A_CAST_TYPE      0x03
+/*
+ * map type:
+ *   00 not a map type (mem_start, io_addr, irq)
+ *   04 memstart (unsigned long)
+ *   08 io_addr  (unsigned short)
+ *   0C irq      (unsigned char)
+ */
+#define A_MAP_TYPE       0x0C
+#define A_ARG_REQ        0x10  /* Set if an arg is required. */
+#define A_NETMASK        0x20  /* Set if netmask (check for multiple sets). */
+#define A_SET_AFTER      0x40  /* Set a flag at the end. */
+#define A_COLON_CHK      0x80  /* Is this needed?  See below. */
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+#define A_HOSTNAME      0x100  /* Set if it is ip addr. */
+#define A_BROADCAST     0x200  /* Set if it is broadcast addr. */
+#else
+#define A_HOSTNAME          0
+#define A_BROADCAST         0
+#endif
+
+/*
+ * These defines are for dealing with the A_CAST_TYPE field.
+ */
+#define A_CAST_CHAR_PTR  0x01
+#define A_CAST_RESOLVE   0x01
+#define A_CAST_HOST_COPY 0x02
+#define A_CAST_HOST_COPY_IN_ETHER    A_CAST_HOST_COPY
+#define A_CAST_HOST_COPY_RESOLVE     (A_CAST_HOST_COPY | A_CAST_RESOLVE)
+
+/*
+ * These defines are for dealing with the A_MAP_TYPE field.
+ */
+#define A_MAP_ULONG      0x04  /* memstart */
+#define A_MAP_USHORT     0x08  /* io_addr */
+#define A_MAP_UCHAR      0x0C  /* irq */
+
+/*
+ * Define the bit masks signifying which operations to perform for each arg.
+ */
+
+#define ARG_METRIC       (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MTU          (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_TXQUEUELEN   (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MEM_START    (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IO_ADDR      (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IRQ          (A_ARG_REQ | A_MAP_UCHAR)
+#define ARG_DSTADDR      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
+#define ARG_NETMASK      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
+#define ARG_BROADCAST    (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
+#define ARG_HW           (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
+#define ARG_POINTOPOINT  (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_KEEPALIVE    (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_OUTFILL      (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_HOSTNAME     (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
+#define ARG_ADD_DEL      (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+
+
+/*
+ * Set up the tables.  Warning!  They must have corresponding order!
+ */
+
+struct arg1opt {
+       const char *name;
+       unsigned short selector;
+       unsigned short ifr_offset;
+};
+
+struct options {
+       const char *name;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+       const unsigned int flags:6;
+       const unsigned int arg_flags:10;
+#else
+       const unsigned char flags;
+       const unsigned char arg_flags;
+#endif
+       const unsigned short selector;
+};
+
+#define ifreq_offsetof(x)  offsetof(struct ifreq, x)
+
+static const struct arg1opt Arg1Opt[] = {
+       {"SIOCSIFMETRIC",  SIOCSIFMETRIC,  ifreq_offsetof(ifr_metric)},
+       {"SIOCSIFMTU",     SIOCSIFMTU,     ifreq_offsetof(ifr_mtu)},
+       {"SIOCSIFTXQLEN",  SIOCSIFTXQLEN,  ifreq_offsetof(ifr_qlen)},
+       {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+       {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
+       {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+       {"SIOCSIFHWADDR",  SIOCSIFHWADDR,  ifreq_offsetof(ifr_hwaddr)},
+#endif
+       {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+#ifdef SIOCSKEEPALIVE
+       {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
+#endif
+#ifdef SIOCSOUTFILL
+       {"SIOCSOUTFILL",   SIOCSOUTFILL,   ifreq_offsetof(ifr_data)},
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+       {"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.mem_start)},
+       {"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.base_addr)},
+       {"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.irq)},
+#endif
+       /* Last entry if for unmatched (possibly hostname) arg. */
+#ifdef CONFIG_FEATURE_IPV6
+       {"SIOCSIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+       {"SIOCDIFADDR",    SIOCDIFADDR,    ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+#endif
+       {"SIOCSIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr)},
+};
+
+static const struct options OptArray[] = {
+       {"metric",      N_ARG,         ARG_METRIC,      0},
+       {"mtu",         N_ARG,         ARG_MTU,         0},
+       {"txqueuelen",  N_ARG,         ARG_TXQUEUELEN,  0},
+       {"dstaddr",     N_ARG,         ARG_DSTADDR,     0},
+       {"netmask",     N_ARG,         ARG_NETMASK,     0},
+       {"broadcast",   N_ARG | M_CLR, ARG_BROADCAST,   IFF_BROADCAST},
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+       {"hw",          N_ARG, ARG_HW,                  0},
+#endif
+       {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
+#ifdef SIOCSKEEPALIVE
+       {"keepalive",   N_ARG,         ARG_KEEPALIVE,   0},
+#endif
+#ifdef SIOCSOUTFILL
+       {"outfill",     N_ARG,         ARG_OUTFILL,     0},
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+       {"mem_start",   N_ARG,         ARG_MEM_START,   0},
+       {"io_addr",     N_ARG,         ARG_IO_ADDR,     0},
+       {"irq",         N_ARG,         ARG_IRQ,         0},
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+       {"add",         N_ARG,         ARG_ADD_DEL,     0},
+       {"del",         N_ARG,         ARG_ADD_DEL,     0},
+#endif
+       {"arp",         N_CLR | M_SET, 0,               IFF_NOARP},
+       {"trailers",    N_CLR | M_SET, 0,               IFF_NOTRAILERS},
+       {"promisc",     N_SET | M_CLR, 0,               IFF_PROMISC},
+       {"multicast",   N_SET | M_CLR, 0,               IFF_MULTICAST},
+       {"allmulti",    N_SET | M_CLR, 0,               IFF_ALLMULTI},
+       {"dynamic",     N_SET | M_CLR, 0,               IFF_DYNAMIC},
+       {"up",          N_SET,         0,               (IFF_UP | IFF_RUNNING)},
+       {"down",        N_CLR,         0,               IFF_UP},
+       {NULL,          0,             ARG_HOSTNAME,    (IFF_UP | IFF_RUNNING)}
+};
+
+/*
+ * A couple of prototypes.
+ */
+
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+static int in_ether(char *bufp, struct sockaddr *sap);
+#endif
+
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+extern int interface_opt_a;
+extern int display_interfaces(char *ifname);
+#endif
+
+/*
+ * Our main function.
+ */
+
+int ifconfig_main(int argc, char **argv)
+{
+       struct ifreq ifr;
+       struct sockaddr_in sai;
+#ifdef CONFIG_FEATURE_IPV6
+       struct sockaddr_in6 sai6;
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+       struct sockaddr sa;
+#endif
+       const struct arg1opt *a1op;
+       const struct options *op;
+       int sockfd;                     /* socket fd we use to manipulate stuff with */
+       int goterr;
+       int selector;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+       unsigned int mask;
+       unsigned int did_flags;
+       unsigned int sai_hostname, sai_netmask;
+#else
+       unsigned char mask;
+       unsigned char did_flags;
+#endif
+       char *p;
+       char host[128];
+
+       goterr = 0;
+       did_flags = 0;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+       sai_hostname = 0;
+       sai_netmask = 0;
+#endif
+
+       /* skip argv[0] */
+       ++argv;
+       --argc;
+
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+       if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) {
+               interface_opt_a = 1;
+               --argc;
+               ++argv;
+       }
+#endif
+
+       if (argc <= 1) {
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+               return display_interfaces(argc ? *argv : NULL);
+#else
+               bb_error_msg_and_die
+                       ("ifconfig was not compiled with interface status display support.");
+#endif
+       }
+
+       /* Create a channel to the NET kernel. */
+       if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               bb_perror_msg_and_die("socket");
+       }
+
+       /* get interface name */
+       safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+
+       /* Process the remaining arguments. */
+       while (*++argv != (char *) NULL) {
+               p = *argv;
+               mask = N_MASK;
+               if (*p == '-') {        /* If the arg starts with '-'... */
+                       ++p;            /*    advance past it and */
+                       mask = M_MASK;  /*    set the appropriate mask. */
+               }
+               for (op = OptArray; op->name; op++) {   /* Find table entry. */
+                       if (strcmp(p, op->name) == 0) { /* If name matches... */
+                               if ((mask &= op->flags)) {      /* set the mask and go. */
+                                       goto FOUND_ARG;;
+                               }
+                               /* If we get here, there was a valid arg with an */
+                               /* invalid '-' prefix. */
+                               ++goterr;
+                               goto LOOP;
+                       }
+               }
+
+               /* We fell through, so treat as possible hostname. */
+               a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
+               mask = op->arg_flags;
+               goto HOSTNAME;
+
+         FOUND_ARG:
+               if (mask & ARG_MASK) {
+                       mask = op->arg_flags;
+                       a1op = Arg1Opt + (op - OptArray);
+                       if (mask & A_NETMASK & did_flags) {
+                               bb_show_usage();
+                       }
+                       if (*++argv == NULL) {
+                               if (mask & A_ARG_REQ) {
+                                       bb_show_usage();
+                               } else {
+                                       --argv;
+                                       mask &= A_SET_AFTER;    /* just for broadcast */
+                               }
+                       } else {        /* got an arg so process it */
+                         HOSTNAME:
+                               did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
+                               if (mask & A_CAST_HOST_COPY) {
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+                                       if (mask & A_CAST_RESOLVE) {
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+                                               char *prefix;
+                                               int prefix_len = 0;
+#endif
+
+                                               safe_strncpy(host, *argv, (sizeof host));
+#ifdef CONFIG_FEATURE_IPV6
+                                               if ((prefix = strchr(host, '/'))) {
+                                                       if (safe_strtoi(prefix + 1, &prefix_len) ||
+                                                               (prefix_len < 0) || (prefix_len > 128))
+                                                       {
+                                                               ++goterr;
+                                                               goto LOOP;
+                                                       }
+                                                       *prefix = 0;
+                                               }
+#endif
+
+                                               sai.sin_family = AF_INET;
+                                               sai.sin_port = 0;
+                                               if (!strcmp(host, bb_INET_default)) {
+                                                       /* Default is special, meaning 0.0.0.0. */
+                                                       sai.sin_addr.s_addr = INADDR_ANY;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+                                               } else if (((host[0] == '+') && !host[1]) && (mask & A_BROADCAST) &&
+                                                                  (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)) {
+                                                       /* + is special, meaning broadcast is derived. */
+                                                       sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+                                               } else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) {
+                                                       int sockfd6;
+                                                       struct in6_ifreq ifr6;
+
+                                                       memcpy((char *) &ifr6.ifr6_addr,
+                                                                  (char *) &sai6.sin6_addr,
+                                                                  sizeof(struct in6_addr));
+
+                                                       /* Create a channel to the NET kernel. */
+                                                       if ((sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                                                               bb_perror_msg_and_die("socket6");
+                                                       }
+                                                       if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) {
+                                                               perror("SIOGIFINDEX");
+                                                               ++goterr;
+                                                               continue;
+                                                       }
+                                                       ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+                                                       ifr6.ifr6_prefixlen = prefix_len;
+                                                       if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) {
+                                                               perror(a1op->name);
+                                                               ++goterr;
+                                                       }
+                                                       continue;
+#endif
+                                               } else if (inet_aton(host, &sai.sin_addr) == 0) {
+                                                       /* It's not a dotted quad. */
+                                                       struct hostent *hp;
+                                                       if ((hp = gethostbyname(host)) == (struct hostent *)NULL) {
+                                                               ++goterr;
+                                                               continue;
+                                                       }
+                                                       memcpy((char *) &sai.sin_addr, (char *) hp->h_addr_list[0],
+                                                       sizeof(struct in_addr));
+                                               }
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+                                               if (mask & A_HOSTNAME) {
+                                                       sai_hostname = sai.sin_addr.s_addr;
+                                               }
+                                               if (mask & A_NETMASK) {
+                                                       sai_netmask = sai.sin_addr.s_addr;
+                                               }
+#endif
+                                               p = (char *) &sai;
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+                                       } else {        /* A_CAST_HOST_COPY_IN_ETHER */
+                                               /* This is the "hw" arg case. */
+                                               if (strcmp("ether", *argv) || (*++argv == NULL)) {
+                                                       bb_show_usage();
+                                               }
+                                               safe_strncpy(host, *argv, (sizeof host));
+                                               if (in_ether(host, &sa)) {
+                                                       bb_error_msg("invalid hw-addr %s", host);
+                                                       ++goterr;
+                                                       continue;
+                                               }
+                                               p = (char *) &sa;
+                                       }
+#endif
+                                       memcpy((((char *) (&ifr)) + a1op->ifr_offset),
+                                                  p, sizeof(struct sockaddr));
+                               } else {
+                                       unsigned int i = strtoul(*argv, NULL, 0);
+
+                                       p = ((char *) (&ifr)) + a1op->ifr_offset;
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+                                       if (mask & A_MAP_TYPE) {
+                                               if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) {
+                                                       ++goterr;
+                                                       continue;
+                                               }
+                                               if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) {
+                                                       *((unsigned char *) p) = i;
+                                               } else if (mask & A_MAP_USHORT) {
+                                                       *((unsigned short *) p) = i;
+                                               } else {
+                                                       *((unsigned long *) p) = i;
+                                               }
+                                       } else
+#endif
+                                       if (mask & A_CAST_CHAR_PTR) {
+                                               *((caddr_t *) p) = (caddr_t) i;
+                                       } else {        /* A_CAST_INT */
+                                               *((int *) p) = i;
+                                       }
+                               }
+
+                               if (ioctl(sockfd, a1op->selector, &ifr) < 0) {
+                                       perror(a1op->name);
+                                       ++goterr;
+                                       continue;
+                               }
+#ifdef QUESTIONABLE_ALIAS_CASE
+                               if (mask & A_COLON_CHK) {
+                                       /*
+                                        * Don't do the set_flag() if the address is an alias with
+                                        * a - at the end, since it's deleted already! - Roman
+                                        *
+                                        * Should really use regex.h here, not sure though how well
+                                        * it'll go with the cross-platform support etc.
+                                        */
+                                       char *ptr;
+                                       short int found_colon = 0;
+
+                                       for (ptr = ifr.ifr_name; *ptr; ptr++) {
+                                               if (*ptr == ':') {
+                                                       found_colon++;
+                                               }
+                                       }
+
+                                       if (found_colon && *(ptr - 1) == '-') {
+                                               continue;
+                                       }
+                               }
+#endif
+                       }
+                       if (!(mask & A_SET_AFTER)) {
+                               continue;
+                       }
+                       mask = N_SET;
+               }
+
+               if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+                       perror("SIOCGIFFLAGS");
+                       ++goterr;
+               } else {
+                       selector = op->selector;
+                       if (mask & SET_MASK) {
+                               ifr.ifr_flags |= selector;
+                       } else {
+                               ifr.ifr_flags &= ~selector;
+                       }
+                       if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+                               perror("SIOCSIFFLAGS");
+                               ++goterr;
+                       }
+               }
+         LOOP:
+               continue;
+       }                                       /* end of while-loop */
+
+       return goterr;
+}
+
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(char *bufp, struct sockaddr *sap)
+{
+       unsigned char *ptr;
+       int i, j;
+       unsigned char val;
+       unsigned char c;
+
+       sap->sa_family = ARPHRD_ETHER;
+       ptr = sap->sa_data;
+
+       i = 0;
+       do {
+               j = val = 0;
+
+               /* We might get a semicolon here - not required. */
+               if (i && (*bufp == ':')) {
+                       bufp++;
+               }
+
+               do {
+                       c = *bufp;
+                       if (((unsigned char)(c - '0')) <= 9) {
+                               c -= '0';
+                       } else if (((unsigned char)((c|0x20) - 'a')) <= 5) {
+                               c = (c|0x20) - ('a'-10);
+                       } else if (j && (c == ':' || c == 0)) {
+                               break;
+                       } else {
+                               return -1;
+                       }
+                       ++bufp;
+                       val <<= 4;
+                       val += c;
+               } while (++j < 2);
+               *ptr++ = val;
+       } while (++i < ETH_ALEN);
+
+       return (int) (*bufp);   /* Error if we don't end at end of string. */
+}
+#endif
diff --git a/busybox/networking/ifupdown.c b/busybox/networking/ifupdown.c
new file mode 100644 (file)
index 0000000..1842be5
--- /dev/null
@@ -0,0 +1,1463 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  ifupdown for busybox
+ *  Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>
+ *  Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
+ *
+ *  Based on ifupdown v 0.6.4 by Anthony Towns
+ *  Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
+ *
+ *  Changes to upstream version
+ *  Remove checks for kernel version, assume kernel version 2.2.0 or better.
+ *  Lines in the interfaces file cannot wrap.
+ *  To adhere to the FHS, the default state file is /var/run/ifstate.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* TODO: standardise execute() return codes to return 0 for success and 1 for failure */
+
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libbb.h"
+
+#define MAX_OPT_DEPTH 10
+#define EUNBALBRACK 10001
+#define EUNDEFVAR   10002
+#define EUNBALPER   10000
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+#define MAX_INTERFACE_LENGTH 10
+#endif
+
+#if 0
+#define debug_noise(fmt, args...) printf(fmt, ## args)
+#else
+#define debug_noise(fmt, args...)
+#endif
+
+/* Forward declaration */
+struct interface_defn_t;
+
+typedef int (execfn)(char *command);
+typedef int (command_set)(struct interface_defn_t *ifd, execfn *e);
+
+extern llist_t *llist_add_to_end(llist_t *list_head, char *data)
+{
+       llist_t *new_item, *tmp, *prev;
+
+       new_item = xmalloc(sizeof(llist_t));
+       new_item->data = data;
+       new_item->link = NULL;
+
+       prev = NULL;
+       tmp = list_head;
+       while(tmp) {
+               prev = tmp;
+               tmp = tmp->link;
+       }
+       if (prev) {
+               prev->link = new_item;
+       } else {
+               list_head = new_item;
+       }
+
+       return(list_head);
+}
+
+struct method_t
+{
+       char *name;
+       command_set *up;
+       command_set *down;
+};
+
+struct address_family_t
+{
+       char *name;
+       int n_methods;
+       struct method_t *method;
+};
+
+struct mapping_defn_t
+{
+       struct mapping_defn_t *next;
+
+       int max_matches;
+       int n_matches;
+       char **match;
+
+       char *script;
+
+       int max_mappings;
+       int n_mappings;
+       char **mapping;
+};
+
+struct variable_t
+{
+       char *name;
+       char *value;
+};
+
+struct interface_defn_t
+{
+       struct interface_defn_t *prev;
+       struct interface_defn_t *next;
+
+       char *iface;
+       struct address_family_t *address_family;
+       struct method_t *method;
+
+       int automatic;
+
+       int max_options;
+       int n_options;
+       struct variable_t *option;
+};
+
+struct interfaces_file_t
+{
+       llist_t *autointerfaces;
+       llist_t *ifaces;
+       struct mapping_defn_t *mappings;
+};
+
+static char no_act = 0;
+static char verbose = 0;
+static char **environ = NULL;
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+
+static unsigned int count_bits(unsigned int a)
+{
+       unsigned int result;
+       result = (a & 0x55) + ((a >> 1) & 0x55);
+       result = (result & 0x33) + ((result >> 2) & 0x33);
+       return((result & 0x0F) + ((result >> 4) & 0x0F));
+}
+
+static int count_netmask_bits(char *dotted_quad)
+{
+       unsigned int result, a, b, c, d;
+       /* Found a netmask...  Check if it is dotted quad */
+       if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+               return -1;
+       result = count_bits(a);
+       result += count_bits(b);
+       result += count_bits(c);
+       result += count_bits(d);
+       return ((int)result);
+}
+#endif
+
+static void addstr(char **buf, size_t *len, size_t *pos, char *str, size_t str_length)
+{
+       if (*pos + str_length >= *len) {
+               char *newbuf;
+
+               newbuf = xrealloc(*buf, *len * 2 + str_length + 1);
+               *buf = newbuf;
+               *len = *len * 2 + str_length + 1;
+       }
+
+       while (str_length-- >= 1) {
+               (*buf)[(*pos)++] = *str;
+               str++;
+       }
+       (*buf)[*pos] = '\0';
+}
+
+static int strncmpz(char *l, char *r, size_t llen)
+{
+       int i = strncmp(l, r, llen);
+
+       if (i == 0) {
+               return(-r[llen]);
+       } else {
+               return(i);
+       }
+}
+
+static char *get_var(char *id, size_t idlen, struct interface_defn_t *ifd)
+{
+       int i;
+
+       if (strncmpz(id, "iface", idlen) == 0) {
+               char *result;
+               static char label_buf[20];
+               strncpy(label_buf, ifd->iface, 19);
+               label_buf[19]=0;
+               result = strchr(label_buf, ':');
+               if (result) {
+                       *result=0;
+               }
+               return( label_buf);
+       } else if (strncmpz(id, "label", idlen) == 0) {
+               return (ifd->iface);
+       } else {
+               for (i = 0; i < ifd->n_options; i++) {
+                       if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
+                               return (ifd->option[i].value);
+                       }
+               }
+       }
+
+       return(NULL);
+}
+
+static char *parse(char *command, struct interface_defn_t *ifd)
+{
+
+       char *result = NULL;
+       size_t pos = 0, len = 0;
+       size_t old_pos[MAX_OPT_DEPTH] = { 0 };
+       int okay[MAX_OPT_DEPTH] = { 1 };
+       int opt_depth = 1;
+
+       while (*command) {
+               switch (*command) {
+
+                       default:
+                               addstr(&result, &len, &pos, command, 1);
+                               command++;
+                               break;
+                       case '\\':
+                               if (command[1]) {
+                                       addstr(&result, &len, &pos, command + 1, 1);
+                                       command += 2;
+                               } else {
+                                       addstr(&result, &len, &pos, command, 1);
+                                       command++;
+                               }
+                               break;
+                       case '[':
+                               if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
+                                       old_pos[opt_depth] = pos;
+                                       okay[opt_depth] = 1;
+                                       opt_depth++;
+                                       command += 2;
+                               } else {
+                                       addstr(&result, &len, &pos, "[", 1);
+                                       command++;
+                               }
+                               break;
+                       case ']':
+                               if (command[1] == ']' && opt_depth > 1) {
+                                       opt_depth--;
+                                       if (!okay[opt_depth]) {
+                                               pos = old_pos[opt_depth];
+                                               result[pos] = '\0';
+                                       }
+                                       command += 2;
+                               } else {
+                                       addstr(&result, &len, &pos, "]", 1);
+                                       command++;
+                               }
+                               break;
+                       case '%':
+                               {
+                                       char *nextpercent;
+                                       char *varvalue;
+
+                                       command++;
+                                       nextpercent = strchr(command, '%');
+                                       if (!nextpercent) {
+                                               errno = EUNBALPER;
+                                               free(result);
+                                               return (NULL);
+                                       }
+
+                                       varvalue = get_var(command, nextpercent - command, ifd);
+
+                                       if (varvalue) {
+                                               addstr(&result, &len, &pos, varvalue, bb_strlen(varvalue));
+                                       } else {
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+                                               /* Sigh...  Add a special case for 'ip' to convert from
+                                                * dotted quad to bit count style netmasks.  */
+                                               if (strncmp(command, "bnmask", 6)==0) {
+                                                       int res;
+                                                       varvalue = get_var("netmask", 7, ifd);
+                                                       if (varvalue && (res=count_netmask_bits(varvalue)) > 0) {
+                                                               char argument[255];
+                                                               sprintf(argument, "%d", res);
+                                                               addstr(&result, &len, &pos, argument, bb_strlen(argument));
+                                                               command = nextpercent + 1;
+                                                               break;
+                                                       }
+                                               }
+#endif
+                                               okay[opt_depth - 1] = 0;
+                                       }
+
+                                       command = nextpercent + 1;
+                               }
+                               break;
+               }
+       }
+
+       if (opt_depth > 1) {
+               errno = EUNBALBRACK;
+               free(result);
+               return(NULL);
+       }
+
+       if (!okay[0]) {
+               errno = EUNDEFVAR;
+               free(result);
+               return(NULL);
+       }
+
+       return(result);
+}
+
+static int execute(char *command, struct interface_defn_t *ifd, execfn *exec)
+{
+       char *out;
+       int ret;
+
+       out = parse(command, ifd);
+       if (!out) {
+               return(0);
+       }
+       ret = (*exec) (out);
+
+       free(out);
+       if (ret != 1) {
+               return(0);
+       }
+       return(1);
+}
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
+static int static_up_ipx(struct interface_defn_t *ifd, execfn *exec)
+{
+       return(execute("ipx_interface add %iface% %frame% %netnum%", ifd, exec));
+}
+
+static int static_down_ipx(struct interface_defn_t *ifd, execfn *exec)
+{
+       return(execute("ipx_interface del %iface% %frame%", ifd, exec));
+}
+
+static int dynamic_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       return(execute("ipx_interface add %iface% %frame%", ifd, exec));
+}
+
+static int dynamic_down(struct interface_defn_t *ifd, execfn *exec)
+{
+       return(execute("ipx_interface del %iface% %frame%", ifd, exec));
+}
+
+static struct method_t methods_ipx[] = {
+       { "dynamic", dynamic_up, dynamic_down, },
+       { "static", static_up_ipx, static_down_ipx, },
+};
+
+struct address_family_t addr_ipx = {
+       "ipx",
+       sizeof(methods_ipx) / sizeof(struct method_t),
+       methods_ipx
+};
+#endif /* IFUP_FEATURE_IPX */
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
+static int loopback_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       int result;
+       result =execute("ip addr add ::1 dev %iface%", ifd, exec);
+       result += execute("ip link set %iface% up", ifd, exec);
+       return ((result == 2) ? 2 : 0);
+#else
+       return( execute("ifconfig %iface% add ::1", ifd, exec));
+#endif
+}
+
+static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       return(execute("ip link set %iface% down", ifd, exec));
+#else
+       return(execute("ifconfig %iface% del ::1", ifd, exec));
+#endif
+}
+
+static int static_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+       int result;
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       result = execute("ip addr add %address%/%netmask% dev %iface% [[label %label%]]", ifd, exec);
+       result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec);
+       result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
+#else
+       result = execute("ifconfig %iface% [[media %media%]] [[hw %hwaddress%]] [[mtu %mtu%]] up", ifd, exec);
+       result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
+       result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec);
+#endif
+       return ((result == 3) ? 3 : 0);
+}
+
+static int static_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       return(execute("ip link set %iface% down", ifd, exec));
+#else
+       return(execute("ifconfig %iface% down", ifd, exec));
+#endif
+}
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       int result;
+       result = execute("ip tunnel add %iface% mode sit remote "
+                               "%endpoint% [[local %local%]] [[ttl %ttl%]]", ifd, exec);
+       result += execute("ip link set %iface% up", ifd, exec);
+       result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
+       result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
+       return ((result == 4) ? 4 : 0);
+}
+
+static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
+{
+       return( execute("ip tunnel del %iface%", ifd, exec));
+}
+#endif
+
+static struct method_t methods6[] = {
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       { "v4tunnel", v4tunnel_up, v4tunnel_down, },
+#endif
+       { "static", static_up6, static_down6, },
+       { "loopback", loopback_up6, loopback_down6, },
+};
+
+struct address_family_t addr_inet6 = {
+       "inet6",
+       sizeof(methods6) / sizeof(struct method_t),
+       methods6
+};
+#endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
+static int loopback_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       int result;
+       result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
+       result += execute("ip link set %iface% up", ifd, exec);
+       return ((result == 2) ? 2 : 0);
+#else
+       return( execute("ifconfig %iface% 127.0.0.1 up", ifd, exec));
+#endif
+}
+
+static int loopback_down(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       int result;
+       result = execute("ip addr flush dev %iface%", ifd, exec);
+       result += execute("ip link set %iface% down", ifd, exec);
+       return ((result == 2) ? 2 : 0);
+#else
+       return( execute("ifconfig %iface% 127.0.0.1 down", ifd, exec));
+#endif
+}
+
+static int static_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       int result;
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       result = execute("ip addr add %address%/%bnmask% [[broadcast %broadcast%]] "
+                       "dev %iface% [[peer %pointopoint%]] [[label %label%]]", ifd, exec);
+       result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec);
+       result += execute("[[ ip route add default via %gateway% dev %iface% ]]", ifd, exec);
+       return ((result == 3) ? 3 : 0);
+#else
+       result = execute("ifconfig %iface% %address% netmask %netmask% "
+                               "[[broadcast %broadcast%]]      [[pointopoint %pointopoint%]] "
+                               "[[media %media%]] [[mtu %mtu%]]        [[hw %hwaddress%]] up",
+                               ifd, exec);
+       result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec);
+       return ((result == 2) ? 2 : 0);
+#endif
+}
+
+static int static_down(struct interface_defn_t *ifd, execfn *exec)
+{
+       int result;
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+       result = execute("ip addr flush dev %iface%", ifd, exec);
+       result += execute("ip link set %iface% down", ifd, exec);
+#else
+       result = execute("[[ route del default gw %gateway% %iface% ]]", ifd, exec);
+       result += execute("ifconfig %iface% down", ifd, exec);
+#endif
+       return ((result == 2) ? 2 : 0);
+}
+
+static int execable(char *program)
+{
+       struct stat buf;
+       if (0 == stat(program, &buf)) {
+               if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode)) {
+                       return(1);
+               }
+       }
+       return(0);
+}
+
+static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       if (execable("/sbin/udhcpc")) {
+               return( execute("udhcpc -n -p /var/run/udhcpc.%iface%.pid -i "
+                                       "%iface% [[-H %hostname%]] [[-c %clientid%]]", ifd, exec));
+       } else if (execable("/sbin/pump")) {
+               return( execute("pump -i %iface% [[-h %hostname%]] [[-l %leasehours%]]", ifd, exec));
+       } else if (execable("/sbin/dhclient")) {
+               return( execute("dhclient -pf /var/run/dhclient.%iface%.pid %iface%", ifd, exec));
+       } else if (execable("/sbin/dhcpcd")) {
+               return( execute("dhcpcd [[-h %hostname%]] [[-i %vendor%]] [[-I %clientid%]] "
+                                       "[[-l %leasetime%]] %iface%", ifd, exec));
+       }
+       return(0);
+}
+
+static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+       int result = 0;
+       if (execable("/sbin/udhcpc")) {
+               /* SIGUSR2 forces udhcpc to release the current lease and go inactive,
+                * and SIGTERM causes udhcpc to exit.  Signals are queued and processed
+                * sequentially so we don't need to sleep */
+               result = execute("kill -USR2 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+               result += execute("kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+       } else if (execable("/sbin/pump")) {
+               result = execute("pump -i %iface% -k", ifd, exec);
+       } else if (execable("/sbin/dhclient")) {
+               result = execute("kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", ifd, exec);
+       } else if (execable("/sbin/dhcpcd")) {
+               result = execute("dhcpcd -k %iface%", ifd, exec);
+       }
+       return (result || static_down(ifd, exec));
+}
+
+static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       return( execute("bootpc [[--bootfile %bootfile%]] --dev %iface% "
+                               "[[--server %server%]] [[--hwaddr %hwaddr%]] "
+                               "--returniffail --serverbcast", ifd, exec));
+}
+
+static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       return( execute("pon [[%provider%]]", ifd, exec));
+}
+
+static int ppp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+       return( execute("poff [[%provider%]]", ifd, exec));
+}
+
+static int wvdial_up(struct interface_defn_t *ifd, execfn *exec)
+{
+       return( execute("/sbin/start-stop-daemon --start -x /usr/bin/wvdial "
+                               "-p /var/run/wvdial.%iface% -b -m -- [[ %provider% ]]", ifd, exec));
+}
+
+static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)
+{
+       return( execute("/sbin/start-stop-daemon --stop -x /usr/bin/wvdial "
+                               "-p /var/run/wvdial.%iface% -s 2", ifd, exec));
+}
+
+static struct method_t methods[] =
+{
+       { "wvdial", wvdial_up, wvdial_down, },
+       { "ppp", ppp_up, ppp_down, },
+       { "static", static_up, static_down, },
+       { "bootp", bootp_up, static_down, },
+       { "dhcp", dhcp_up, dhcp_down, },
+       { "loopback", loopback_up, loopback_down, },
+};
+
+struct address_family_t addr_inet =
+{
+       "inet",
+       sizeof(methods) / sizeof(struct method_t),
+       methods
+};
+
+#endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */
+
+static char *next_word(char **buf)
+{
+       unsigned short length;
+       char *word;
+
+       if ((buf == NULL) || (*buf == NULL) || (**buf == '\0')) {
+               return NULL;
+       }
+
+       /* Skip over leading whitespace */
+       word = *buf;
+       while (isspace(*word)) {
+               ++word;
+       }
+
+       /* Skip over comments */
+       if (*word == '#') {
+               return(NULL);
+       }
+
+       /* Find the length of this word */
+       length = strcspn(word, " \t\n");
+       if (length == 0) {
+               return(NULL);
+       }
+       *buf = word + length;
+       /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */
+       if (**buf) {
+               **buf = '\0';
+               (*buf)++;
+       }
+
+       return word;
+}
+
+static struct address_family_t *get_address_family(struct address_family_t *af[], char *name)
+{
+       int i;
+
+       for (i = 0; af[i]; i++) {
+               if (strcmp(af[i]->name, name) == 0) {
+                       return af[i];
+               }
+       }
+       return NULL;
+}
+
+static struct method_t *get_method(struct address_family_t *af, char *name)
+{
+       int i;
+
+       for (i = 0; i < af->n_methods; i++) {
+               if (strcmp(af->method[i].name, name) == 0) {
+                       return &af->method[i];
+               }
+       }
+       return(NULL);
+}
+
+static int duplicate_if(struct interface_defn_t *ifa, struct interface_defn_t *ifb)
+{
+       if (strcmp(ifa->iface, ifb->iface) != 0) {
+               return(0);
+       }
+       if (ifa->address_family != ifb->address_family) {
+               return(0);
+       }
+       return(1);
+}
+
+static const llist_t *find_list_string(const llist_t *list, const char *string)
+{
+       while (list) {
+               if (strcmp(list->data, string) == 0) {
+                       return(list);
+               }
+               list = list->link;
+       }
+       return(NULL);
+}
+
+static struct interfaces_file_t *read_interfaces(const char *filename)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+       struct mapping_defn_t *currmap = NULL;
+#endif
+       struct interface_defn_t *currif = NULL;
+       struct interfaces_file_t *defn;
+       FILE *f;
+       char *firstword;
+       char *buf;
+
+       enum { NONE, IFACE, MAPPING } currently_processing = NONE;
+
+       defn = xmalloc(sizeof(struct interfaces_file_t));
+       defn->autointerfaces = NULL;
+       defn->mappings = NULL;
+       defn->ifaces = NULL;
+
+       f = bb_xfopen(filename, "r");
+
+       while ((buf = bb_get_chomped_line_from_file(f)) != NULL) {
+               char *buf_ptr = buf;
+
+               firstword = next_word(&buf_ptr);
+               if (firstword == NULL) {
+                       free(buf);
+                       continue;       /* blank line */
+               }
+
+               if (strcmp(firstword, "mapping") == 0) {
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+                       currmap = xmalloc(sizeof(struct mapping_defn_t));
+                       currmap->max_matches = 0;
+                       currmap->n_matches = 0;
+                       currmap->match = NULL;
+
+                       while ((firstword = next_word(&buf_ptr)) != NULL) {
+                               if (currmap->max_matches == currmap->n_matches) {
+                                       currmap->max_matches = currmap->max_matches * 2 + 1;
+                                       currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches);
+                               }
+
+                               currmap->match[currmap->n_matches++] = bb_xstrdup(firstword);
+                       }
+                       currmap->max_mappings = 0;
+                       currmap->n_mappings = 0;
+                       currmap->mapping = NULL;
+                       currmap->script = NULL;
+                       {
+                               struct mapping_defn_t **where = &defn->mappings;
+                               while (*where != NULL) {
+                                       where = &(*where)->next;
+                               }
+                               *where = currmap;
+                               currmap->next = NULL;
+                       }
+                       debug_noise("Added mapping\n");
+#endif
+                       currently_processing = MAPPING;
+               } else if (strcmp(firstword, "iface") == 0) {
+                       {
+                               char *iface_name;
+                               char *address_family_name;
+                               char *method_name;
+                               struct address_family_t *addr_fams[] = {
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
+                                       &addr_inet,
+#endif
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
+                                       &addr_inet6,
+#endif
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
+                                       &addr_ipx,
+#endif
+                                       NULL
+                               };
+
+                               currif = xmalloc(sizeof(struct interface_defn_t));
+                               iface_name = next_word(&buf_ptr);
+                               address_family_name = next_word(&buf_ptr);
+                               method_name = next_word(&buf_ptr);
+
+                               if (buf_ptr == NULL) {
+                                       bb_error_msg("too few parameters for line \"%s\"", buf);
+                                       return NULL;
+                               }
+
+                               /* ship any trailing whitespace */
+                               while (isspace(*buf_ptr)) {
+                                       ++buf_ptr;
+                               }
+
+                               if (buf_ptr[0] != '\0') {
+                                       bb_error_msg("too many parameters \"%s\"", buf);
+                                       return NULL;
+                               }
+
+                               currif->iface = bb_xstrdup(iface_name);
+
+                               currif->address_family = get_address_family(addr_fams, address_family_name);
+                               if (!currif->address_family) {
+                                       bb_error_msg("unknown address type \"%s\"", address_family_name);
+                                       return NULL;
+                               }
+
+                               currif->method = get_method(currif->address_family, method_name);
+                               if (!currif->method) {
+                                       bb_error_msg("unknown method \"%s\"", method_name);
+                                       return NULL;
+                               }
+
+                               currif->automatic = 1;
+                               currif->max_options = 0;
+                               currif->n_options = 0;
+                               currif->option = NULL;
+
+                               {
+                                       struct interface_defn_t *tmp;
+                                       llist_t *iface_list;
+                                       iface_list = defn->ifaces;
+                                       while (iface_list) {
+                                               tmp = (struct interface_defn_t *) iface_list->data;
+                                               if (duplicate_if(tmp, currif)) {
+                                                       bb_error_msg("duplicate interface \"%s\"", tmp->iface);
+                                                       return NULL;
+                                               }
+                                               iface_list = iface_list->link;
+                                       }
+
+                                       defn->ifaces = llist_add_to_end(defn->ifaces, (char*)currif);
+                               }
+                               debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
+                       }
+                       currently_processing = IFACE;
+               } else if (strcmp(firstword, "auto") == 0) {
+                       while ((firstword = next_word(&buf_ptr)) != NULL) {
+
+                               /* Check the interface isnt already listed */
+                               if (find_list_string(defn->autointerfaces, firstword)) {
+                                       bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
+                               }
+
+                               /* Add the interface to the list */
+                               defn->autointerfaces = llist_add_to_end(defn->autointerfaces, strdup(firstword));
+                               debug_noise("\nauto %s\n", firstword);
+                       }
+                       currently_processing = NONE;
+               } else {
+                       switch (currently_processing) {
+                               case IFACE:
+                                       {
+                                               int i;
+
+                                               if (bb_strlen(buf_ptr) == 0) {
+                                                       bb_error_msg("option with empty value \"%s\"", buf);
+                                                       return NULL;
+                                               }
+
+                                               if (strcmp(firstword, "up") != 0
+                                                               && strcmp(firstword, "down") != 0
+                                                               && strcmp(firstword, "pre-up") != 0
+                                                               && strcmp(firstword, "post-down") != 0) {
+                                                       for (i = 0; i < currif->n_options; i++) {
+                                                               if (strcmp(currif->option[i].name, firstword) == 0) {
+                                                                       bb_error_msg("duplicate option \"%s\"", buf);
+                                                                       return NULL;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if (currif->n_options >= currif->max_options) {
+                                               struct variable_t *opt;
+
+                                               currif->max_options = currif->max_options + 10;
+                                               opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options);
+                                               currif->option = opt;
+                                       }
+                                       currif->option[currif->n_options].name = bb_xstrdup(firstword);
+                                       currif->option[currif->n_options].value = bb_xstrdup(buf_ptr);
+                                       if (!currif->option[currif->n_options].name) {
+                                               perror(filename);
+                                               return NULL;
+                                       }
+                                       if (!currif->option[currif->n_options].value) {
+                                               perror(filename);
+                                               return NULL;
+                                       }
+                                       debug_noise("\t%s=%s\n", currif->option[currif->n_options].name,
+                                                       currif->option[currif->n_options].value);
+                                       currif->n_options++;
+                                       break;
+                               case MAPPING:
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+                                       if (strcmp(firstword, "script") == 0) {
+                                               if (currmap->script != NULL) {
+                                                       bb_error_msg("duplicate script in mapping \"%s\"", buf);
+                                                       return NULL;
+                                               } else {
+                                                       currmap->script = bb_xstrdup(next_word(&buf_ptr));
+                                               }
+                                       } else if (strcmp(firstword, "map") == 0) {
+                                               if (currmap->max_mappings == currmap->n_mappings) {
+                                                       currmap->max_mappings = currmap->max_mappings * 2 + 1;
+                                                       currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings);
+                                               }
+                                               currmap->mapping[currmap->n_mappings] = bb_xstrdup(next_word(&buf_ptr));
+                                               currmap->n_mappings++;
+                                       } else {
+                                               bb_error_msg("misplaced option \"%s\"", buf);
+                                               return NULL;
+                                       }
+#endif
+                                       break;
+                               case NONE:
+                               default:
+                                       bb_error_msg("misplaced option \"%s\"", buf);
+                                       return NULL;
+                       }
+               }
+               free(buf);
+       }
+       if (ferror(f) != 0) {
+               bb_perror_msg_and_die("%s", filename);
+       }
+       fclose(f);
+
+       return defn;
+}
+
+static char *setlocalenv(char *format, char *name, char *value)
+{
+       char *result;
+       char *here;
+       char *there;
+
+       result = xmalloc(bb_strlen(format) + bb_strlen(name) + bb_strlen(value) + 1);
+
+       sprintf(result, format, name, value);
+
+       for (here = there = result; *there != '=' && *there; there++) {
+               if (*there == '-')
+                       *there = '_';
+               if (isalpha(*there))
+                       *there = toupper(*there);
+
+               if (isalnum(*there) || *there == '_') {
+                       *here = *there;
+                       here++;
+               }
+       }
+       memmove(here, there, bb_strlen(there) + 1);
+
+       return result;
+}
+
+static void set_environ(struct interface_defn_t *iface, char *mode)
+{
+       char **environend;
+       int i;
+       const int n_env_entries = iface->n_options + 5;
+       char **ppch;
+
+       if (environ != NULL) {
+               for (ppch = environ; *ppch; ppch++) {
+                       free(*ppch);
+                       *ppch = NULL;
+               }
+               free(environ);
+               environ = NULL;
+       }
+       environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+       environend = environ;
+       *environend = NULL;
+
+       for (i = 0; i < iface->n_options; i++) {
+               if (strcmp(iface->option[i].name, "up") == 0
+                               || strcmp(iface->option[i].name, "down") == 0
+                               || strcmp(iface->option[i].name, "pre-up") == 0
+                               || strcmp(iface->option[i].name, "post-down") == 0) {
+                       continue;
+               }
+               *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
+               *environend = NULL;
+       }
+
+       *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface);
+       *environend = NULL;
+       *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
+       *environend = NULL;
+       *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name);
+       *environend = NULL;
+       *(environend++) = setlocalenv("%s=%s", "MODE", mode);
+       *environend = NULL;
+       *(environend++) = setlocalenv("%s=%s", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
+       *environend = NULL;
+}
+
+static int doit(char *str)
+{
+       if (verbose || no_act) {
+               printf("%s\n", str);
+       }
+       if (!no_act) {
+               pid_t child;
+               int status;
+
+               fflush(NULL);
+               switch (child = fork()) {
+                       case -1:                /* failure */
+                               return 0;
+                       case 0:         /* child */
+                               execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, environ);
+                               exit(127);
+               }
+               waitpid(child, &status, 0);
+               if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+                       return 0;
+               }
+       }
+       return (1);
+}
+
+static int execute_all(struct interface_defn_t *ifd, execfn *exec, const char *opt)
+{
+       int i;
+       char *buf;
+       for (i = 0; i < ifd->n_options; i++) {
+               if (strcmp(ifd->option[i].name, opt) == 0) {
+                       if (!(*exec) (ifd->option[i].value)) {
+                               return 0;
+                       }
+               }
+       }
+
+       bb_xasprintf(&buf, "run-parts /etc/network/if-%s.d", opt);
+       if ((*exec)(buf) != 1) {
+               return 0;
+       }
+       return 1;
+}
+
+static int check(char *str) {
+       return str != NULL;
+}
+
+static int iface_up(struct interface_defn_t *iface)
+{
+       if (!iface->method->up(iface,check)) return -1;
+       set_environ(iface, "start");
+       if (!execute_all(iface, doit, "pre-up")) return 0;
+       if (!iface->method->up(iface, doit)) return 0;
+       if (!execute_all(iface, doit, "up")) return 0;
+       return 1;
+}
+
+static int iface_down(struct interface_defn_t *iface)
+{
+       if (!iface->method->down(iface,check)) return -1;
+       set_environ(iface, "stop");
+       if (!execute_all(iface, doit, "down")) return 0;
+       if (!iface->method->down(iface, doit)) return 0;
+       if (!execute_all(iface, doit, "post-down")) return 0;
+       return 1;
+}
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+static int popen2(FILE **in, FILE **out, char *command, ...)
+{
+       va_list ap;
+       char *argv[11] = { command };
+       int argc;
+       int infd[2], outfd[2];
+       pid_t pid;
+
+       argc = 1;
+       va_start(ap, command);
+       while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) {
+               argc++;
+       }
+       argv[argc] = NULL;      /* make sure */
+       va_end(ap);
+
+       if (pipe(infd) != 0) {
+               return 0;
+       }
+
+       if (pipe(outfd) != 0) {
+               close(infd[0]);
+               close(infd[1]);
+               return 0;
+       }
+
+       fflush(NULL);
+       switch (pid = fork()) {
+               case -1:                        /* failure */
+                       close(infd[0]);
+                       close(infd[1]);
+                       close(outfd[0]);
+                       close(outfd[1]);
+                       return 0;
+               case 0:                 /* child */
+                       dup2(infd[0], 0);
+                       dup2(outfd[1], 1);
+                       close(infd[0]);
+                       close(infd[1]);
+                       close(outfd[0]);
+                       close(outfd[1]);
+                       execvp(command, argv);
+                       exit(127);
+               default:                        /* parent */
+                       *in = fdopen(infd[1], "w");
+                       *out = fdopen(outfd[0], "r");
+                       close(infd[0]);
+                       close(outfd[1]);
+                       return pid;
+       }
+       /* unreached */
+}
+
+static char *run_mapping(char *physical, struct mapping_defn_t * map)
+{
+       FILE *in, *out;
+       int i, status;
+       pid_t pid;
+
+       char *logical = bb_xstrdup(physical);
+
+       /* Run the mapping script. */
+       pid = popen2(&in, &out, map->script, physical, NULL);
+
+       /* popen2() returns 0 on failure. */
+       if (pid == 0)
+               return logical;
+
+       /* Write mappings to stdin of mapping script. */
+       for (i = 0; i < map->n_mappings; i++) {
+               fprintf(in, "%s\n", map->mapping[i]);
+       }
+       fclose(in);
+       waitpid(pid, &status, 0);
+
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+               /* If the mapping script exited successfully, try to
+                * grab a line of output and use that as the name of the
+                * logical interface. */
+               char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH);
+
+               if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {
+                       /* If we are able to read a line of output from the script,
+                        * remove any trailing whitespace and use this value
+                        * as the name of the logical interface. */
+                       char *pch = new_logical + bb_strlen(new_logical) - 1;
+
+                       while (pch >= new_logical && isspace(*pch))
+                               *(pch--) = '\0';
+
+                       free(logical);
+                       logical = new_logical;
+               } else {
+                       /* If we are UNABLE to read a line of output, discard are
+                        * freshly allocated memory. */
+                       free(new_logical);
+               }
+       }
+
+       fclose(out);
+
+       return logical;
+}
+#endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */
+
+static llist_t *find_iface_state(llist_t *state_list, const char *iface)
+{
+       unsigned short iface_len = bb_strlen(iface);
+       llist_t *search = state_list;
+
+       while (search) {
+               if ((strncmp(search->data, iface, iface_len) == 0) &&
+                               (search->data[iface_len] == '=')) {
+                       return(search);
+               }
+               search = search->link;
+       }
+       return(NULL);
+}
+
+extern int ifupdown_main(int argc, char **argv)
+{
+       int (*cmds) (struct interface_defn_t *) = NULL;
+       struct interfaces_file_t *defn;
+       FILE *state_fp = NULL;
+       llist_t *state_list = NULL;
+       llist_t *target_list = NULL;
+       const char *interfaces = "/etc/network/interfaces";
+       const char *statefile = "/var/run/ifstate";
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+       int run_mappings = 1;
+#endif
+       int do_all = 0;
+       int force = 0;
+       int any_failures = 0;
+       int i;
+
+       if (bb_applet_name[2] == 'u') {
+               /* ifup command */
+               cmds = iface_up;
+       } else {
+               /* ifdown command */
+               cmds = iface_down;
+       }
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+       while ((i = getopt(argc, argv, "i:hvnamf")) != -1)
+#else
+               while ((i = getopt(argc, argv, "i:hvnaf")) != -1)
+#endif
+               {
+                       switch (i) {
+                               case 'i':       /* interfaces */
+                                       interfaces = optarg;
+                                       break;
+                               case 'v':       /* verbose */
+                                       verbose = 1;
+                                       break;
+                               case 'a':       /* all */
+                                       do_all = 1;
+                                       break;
+                               case 'n':       /* no-act */
+                                       no_act = 1;
+                                       break;
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+                               case 'm':       /* no-mappings */
+                                       run_mappings = 0;
+                                       break;
+#endif
+                               case 'f':       /* force */
+                                       force = 1;
+                                       break;
+                               default:
+                                       bb_show_usage();
+                                       break;
+                       }
+               }
+
+       if (argc - optind > 0) {
+               if (do_all) {
+                       bb_show_usage();
+               }
+       } else {
+               if (!do_all) {
+                       bb_show_usage();
+               }
+       }
+
+       debug_noise("reading %s file:\n", interfaces);
+       defn = read_interfaces(interfaces);
+       debug_noise("\ndone reading %s\n\n", interfaces);
+
+       if (!defn) {
+               exit(EXIT_FAILURE);
+       }
+
+       if (no_act) {
+               state_fp = fopen(statefile, "r");
+       }
+
+       /* Create a list of interfaces to work on */
+       if (do_all) {
+               if (cmds == iface_up) {
+                       target_list = defn->autointerfaces;
+               } else {
+#if 0
+                       /* iface_down */
+                       llist_t *new_item;
+                       const llist_t *list = state_list;
+                       while (list) {
+                               new_item = xmalloc(sizeof(llist_t));
+                               new_item->data = strdup(list->data);
+                               new_item->link = NULL;
+                               list = target_list;
+                               if (list == NULL)
+                                       target_list = new_item;
+                               else {
+                                       while (list->link) {
+                                               list = list->link;
+                                       }
+                                       list = new_item;
+                               }
+                               list = list->link;
+                       }
+                       target_list = defn->autointerfaces;
+#else
+
+                       /* iface_down */
+                       const llist_t *list = state_list;
+                       while (list) {
+                               target_list = llist_add_to_end(target_list, strdup(list->data));
+                               list = list->link;
+                       }
+                       target_list = defn->autointerfaces;
+#endif
+               }
+       } else {
+               target_list = llist_add_to_end(target_list, argv[optind]);
+       }
+
+
+       /* Update the interfaces */
+       while (target_list) {
+               llist_t *iface_list;
+               struct interface_defn_t *currif;
+               char *iface;
+               char *liface;
+               char *pch;
+               int okay = 0;
+               int cmds_ret;
+
+               iface = strdup(target_list->data);
+               target_list = target_list->link;
+
+               pch = strchr(iface, '=');
+               if (pch) {
+                       *pch = '\0';
+                       liface = strdup(pch + 1);
+               } else {
+                       liface = strdup(iface);
+               }
+
+               if (!force) {
+                       const llist_t *iface_state = find_iface_state(state_list, iface);
+
+                       if (cmds == iface_up) {
+                               /* ifup */
+                               if (iface_state) {
+                                       bb_error_msg("interface %s already configured", iface);
+                                       continue;
+                               }
+                       } else {
+                               /* ifdown */
+                               if (iface_state) {
+                                       bb_error_msg("interface %s not configured", iface);
+                                       continue;
+                               }
+                       }
+               }
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+               if ((cmds == iface_up) && run_mappings) {
+                       struct mapping_defn_t *currmap;
+
+                       for (currmap = defn->mappings; currmap; currmap = currmap->next) {
+
+                               for (i = 0; i < currmap->n_matches; i++) {
+                                       if (fnmatch(currmap->match[i], liface, 0) != 0)
+                                               continue;
+                                       if (verbose) {
+                                               printf("Running mapping script %s on %s\n", currmap->script, liface);
+                                       }
+                                       liface = run_mapping(iface, currmap);
+                                       break;
+                               }
+                       }
+               }
+#endif
+
+
+               iface_list = defn->ifaces;
+               while (iface_list) {
+                       currif = (struct interface_defn_t *) iface_list->data;
+                       if (strcmp(liface, currif->iface) == 0) {
+                               char *oldiface = currif->iface;
+
+                               okay = 1;
+                               currif->iface = iface;
+
+                               debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
+
+                               /* Call the cmds function pointer, does either iface_up() or iface_down() */
+                               cmds_ret = cmds(currif);
+                               if (cmds_ret == -1) {
+                                       bb_error_msg("Don't seem to have all the variables for %s/%s.",
+                                                       liface, currif->address_family->name);
+                                       any_failures += 1;
+                               } else if (cmds_ret == 0) {
+                                       any_failures += 1;
+                               }
+
+                               currif->iface = oldiface;
+                       }
+                       iface_list = iface_list->link;
+               }
+               if (verbose) {
+                       printf("\n");
+               }
+
+               if (!okay && !force) {
+                       bb_error_msg("Ignoring unknown interface %s", liface);
+                       any_failures += 1;
+               } else {
+                       llist_t *iface_state = find_iface_state(state_list, iface);
+
+                       if (cmds == iface_up) {
+                               char *newiface = xmalloc(bb_strlen(iface) + 1 + bb_strlen(liface) + 1);
+                               sprintf(newiface, "%s=%s", iface, liface);
+                               if (iface_state == NULL) {
+                                       state_list = llist_add_to_end(state_list, newiface);
+                               } else {
+                                       free(iface_state->data);
+                                       iface_state->data = newiface;
+                               }
+                       } else if (cmds == iface_down) {
+                               /* Remove an interface from the linked list */
+                               if (iface_state) {
+                                       /* This needs to be done better */
+                                       free(iface_state->data);
+                                       free(iface_state->link);
+                                       if (iface_state->link) {
+                                               iface_state->data = iface_state->link->data;
+                                               iface_state->link = iface_state->link->link;
+                                       } else {
+                                               iface_state->data = NULL;
+                                               iface_state->link = NULL;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Actually write the new state */
+       if (!no_act) {
+
+               if (state_fp)
+                       fclose(state_fp);
+               state_fp = bb_xfopen(statefile, "a+");
+
+               if (ftruncate(fileno(state_fp), 0) < 0) {
+                       bb_error_msg_and_die("failed to truncate statefile %s: %s", statefile, strerror(errno));
+               }
+
+               rewind(state_fp);
+
+               while (state_list) {
+                       if (state_list->data) {
+                               fputs(state_list->data, state_fp);
+                               fputc('\n', state_fp);
+                       }
+                       state_list = state_list->link;
+               }
+               fflush(state_fp);
+       }
+
+       /* Cleanup */
+       if (state_fp != NULL) {
+               fclose(state_fp);
+               state_fp = NULL;
+       }
+
+       if (any_failures)
+               return 1;
+       return 0;
+}
diff --git a/busybox/networking/inetd.c b/busybox/networking/inetd.c
new file mode 100644 (file)
index 0000000..169cc87
--- /dev/null
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (c) 1983,1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David A. Holland.
+ *
+ * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Inetd - Internet super-server
+ *
+ * This program invokes all internet services as needed.
+ * connection-oriented services are invoked each time a
+ * connection is made, by creating a process.  This process
+ * is passed the connection as file descriptor 0 and is
+ * expected to do a getpeername to find out the source host
+ * and port.
+ *
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0.  Datagram servers may either connect
+ * to their peer, freeing up the original socket for inetd
+ * to receive further messages on, or ``take over the socket'',
+ * processing all arriving datagrams and, eventually, timing
+ * out.  The first type of server is said to be ``multi-threaded'';
+ * the second type of server ``single-threaded''.
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is ``free format'' with fields given in the
+ * order shown below.  Continuation lines for an entry must being with
+ * a space or tab.  All fields must be present in each entry.
+ *
+ *      service name                    must be in /etc/services
+ *      socket type                     stream/dgram/raw/rdm/seqpacket
+ *      protocol                        must be in /etc/protocols
+ *      wait/nowait[.max]               single-threaded/multi-threaded, max #
+ *      user[.group]                    user/group to run daemon as
+ *      server program                  full path name
+ *      server program arguments        maximum of MAXARGS (20)
+ *
+ * RPC services unsupported
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ */
+
+/*
+ * Here's the scoop concerning the user.group feature:
+ *
+ * 1) No group listed.
+ *
+ *      a) for root:    NO setuid() or setgid() is done
+ *
+ *      b) nonroot:     setuid()
+ *                      setgid(primary group as found in passwd)
+ *                      initgroups(name, primary group)
+ *
+ * 2) set-group-option on.
+ *
+ *      a) for root:    NO setuid()
+ *                      setgid(specified group)
+ *                      setgroups(1, specified group)
+ *
+ *      b) nonroot:     setuid()
+ *                      setgid(specified group)
+ *                      initgroups(name, specified group)
+ *
+ * All supplementary groups are discarded at startup in case inetd was
+ * run manually.
+ */
+
+#define __USE_BSD_SIGNAL
+
+#include "busybox.h"
+
+
+#ifndef __linux__
+#ifndef RLIMIT_NOFILE
+#define RLIMIT_NOFILE   RLIMIT_OFILE
+#endif
+#endif
+
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+
+#ifndef OPEN_MAX
+#define OPEN_MAX       64
+#endif
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID  "/var/run/inetd.pid"
+
+#define TOOMANY                40              /* don't start more than TOOMANY */
+#define CNT_INTVL      60              /* servers in CNT_INTVL sec. */
+#define RETRYTIME      (60*10) /* retry after bind or server fail */
+#define MAXARGV                20
+
+#define se_ctrladdr            se_un.se_un_ctrladdr
+#define se_ctrladdr_in se_un.se_un_ctrladdr_in
+#define se_ctrladdr_un se_un.se_un_ctrladdr_un
+
+/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
+#define FD_MARGIN      (8)
+
+/* Check unsupporting builtin */
+#if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \
+       defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \
+       defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME || \
+       defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME || \
+       defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+# define INETD_FEATURE_ENABLED
+#endif
+
+typedef struct servtab_s {
+       char    *se_service;            /* name of service */
+       int     se_socktype;            /* type of socket to use */
+       int     se_family;              /* address family */
+       char    *se_proto;              /* protocol used */
+       short   se_wait;                /* single threaded server */
+       short   se_checked;             /* looked at during merge */
+       char    *se_user;               /* user name to run as */
+       char    *se_group;              /* group name to run as */
+#ifdef INETD_FEATURE_ENABLED
+       const struct  biltin *se_bi;    /* if built-in, description */
+#endif
+       char    *se_server;             /* server program */
+       char    *se_argv[MAXARGV+1];    /* program arguments */
+       int     se_fd;                  /* open descriptor */
+       union {
+               struct  sockaddr se_un_ctrladdr;
+               struct  sockaddr_in se_un_ctrladdr_in;
+               struct  sockaddr_un se_un_ctrladdr_un;
+       } se_un;                        /* bound address */
+       int     se_ctrladdr_size;
+       int     se_max;                 /* max # of instances of this service */
+       int     se_count;               /* number started since se_time */
+       struct  timeval se_time;        /* start of se_count */
+       struct  servtab_s *se_next;
+} servtab_t;
+
+static servtab_t *servtab;
+
+#ifdef INETD_FEATURE_ENABLED
+struct biltin {
+       const char *bi_service;         /* internally provided service name */
+       int bi_socktype;                /* type of socket supported */
+       short bi_fork;          /* 1 if should fork before call */
+       short bi_wait;          /* 1 if should wait for child */
+       void (*bi_fn)(int, servtab_t *); /* fn which performs it */
+};
+
+    /* Echo received data */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+static void echo_stream(int, servtab_t *);
+static void echo_dg(int, servtab_t *);
+#endif
+    /* Internet /dev/null */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+static void discard_stream(int, servtab_t *);
+static void discard_dg(int, servtab_t *);
+#endif
+       /* Return 32 bit time since 1900 */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+static void machtime_stream(int, servtab_t *);
+static void machtime_dg(int, servtab_t *);
+#endif
+       /* Return human-readable time */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+static void daytime_stream(int, servtab_t *);
+static void daytime_dg(int, servtab_t *);
+#endif
+       /* Familiar character generator */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+static void chargen_stream(int, servtab_t *);
+static void chargen_dg(int, servtab_t *);
+#endif
+
+static const struct biltin biltins[] = {
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+       /* Echo received data */
+       { "echo",               SOCK_STREAM,    1, 0,   echo_stream, },
+       { "echo",               SOCK_DGRAM,     0, 0,   echo_dg, },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+       /* Internet /dev/null */
+       { "discard",    SOCK_STREAM,    1, 0,   discard_stream, },
+       { "discard",    SOCK_DGRAM,     0, 0,   discard_dg, },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+       /* Return 32 bit time since 1900 */
+       { "time",               SOCK_STREAM,    0, 0,   machtime_stream, },
+       { "time",               SOCK_DGRAM,     0, 0,   machtime_dg,     },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+       /* Return human-readable time */
+       { "daytime",    SOCK_STREAM,    0, 0,   daytime_stream, },
+       { "daytime",    SOCK_DGRAM,     0, 0,   daytime_dg,     },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+       /* Familiar character generator */
+       { "chargen",    SOCK_STREAM,    1, 0,   chargen_stream, },
+       { "chargen",    SOCK_DGRAM,     0, 0,   chargen_dg,     },
+#endif
+       { NULL, 0, 0, 0, NULL }
+};
+#endif  /* INETD_FEATURE_ENABLED */
+
+#ifdef RLIMIT_NOFILE
+static struct rlimit   rlim_ofile;
+#endif
+
+/* Length of socket listen queue. Should be per-service probably. */
+static int      global_queuelen = 128;
+
+static FILE *fconfig;
+static sigset_t        blockmask;
+static sigset_t        emptymask;
+static fd_set  allsock;
+static int     nsock;
+static int     maxsock;
+static int     timingout;
+static int     rlim_ofile_cur = OPEN_MAX;
+static const char *CONFIG = _PATH_INETDCONF;
+
+static void
+syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
+       __attribute__ ((noreturn, format (printf, 2, 3)));
+
+static void
+syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
+{
+       char buf[50];
+       va_list p;
+
+       va_start(p, msg);
+       vsyslog(LOG_ERR, msg, p);
+       if (se_socktype != SOCK_STREAM)
+               recv(0, buf, sizeof (buf), 0);
+       _exit(1);
+}
+
+static char * inetd_strdup(const char *s)
+{
+       char *ms = strdup(s);
+
+       if(ms == NULL)
+               syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m");
+       return ms;
+}
+
+
+static servtab_t *getconfigent(void)
+{
+       static servtab_t serv;
+       servtab_t *sep = &serv;
+       int argc;
+       char *cp = NULL;
+       char *cp_ptr;
+       char *cp_ptr_ptr = NULL;
+
+more:
+       free(cp);
+       cp = bb_get_chomped_line_from_file(fconfig);
+       if (feof(fconfig)) {
+               free(cp);
+               return (NULL);
+       }
+       if ((cp == NULL) || (*cp == '#')) {
+               goto more;
+       }
+       /* make bind 0.0.0.0 and other zero default */
+       memset((char *)sep, 0, sizeof *sep);
+
+       cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr);
+       if (cp_ptr == NULL) {
+               /* Error */
+               goto more;
+       }
+       sep->se_service = inetd_strdup(cp_ptr);
+
+       cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
+       if (cp_ptr == NULL) {
+               /* Error */
+               goto more;
+       }
+       if (strcmp(cp_ptr, "stream") == 0)
+               sep->se_socktype = SOCK_STREAM;
+       else if (strcmp(cp_ptr, "dgram") == 0)
+               sep->se_socktype = SOCK_DGRAM;
+       else if (strcmp(cp_ptr, "rdm") == 0)
+               sep->se_socktype = SOCK_RDM;
+       else if (strcmp(cp_ptr, "seqpacket") == 0)
+               sep->se_socktype = SOCK_SEQPACKET;
+       else if (strcmp(cp_ptr, "raw") == 0)
+               sep->se_socktype = SOCK_RAW;
+       else
+               sep->se_socktype = -1;
+
+       cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
+       if (cp_ptr == NULL) {
+               /* error */
+               goto more;
+       }
+       if (strcmp(cp_ptr, "unix") == 0) {
+               sep->se_family = AF_UNIX;
+       } else {
+               if (strncmp(cp_ptr, "rpc/", 4) == 0) {
+                       syslog(LOG_ERR, "%s: rpc services not supported",
+                           sep->se_service);
+                       goto more;
+               }
+               sep->se_family = AF_INET;
+       }
+       sep->se_proto = inetd_strdup(cp_ptr);
+
+       cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
+       if (cp_ptr == NULL) {
+               /* error */
+               goto more;
+       }
+       {
+               char *s = strchr(cp_ptr, '.');
+               if (s) {
+                       *s++ = '\0';
+                       sep->se_max = atoi(s);
+               } else
+                       sep->se_max = TOOMANY;
+       }
+       sep->se_wait = strcmp(cp_ptr, "wait") == 0;
+
+       cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
+       if (cp_ptr == NULL) {
+               /* error */
+               goto more;
+       }
+
+       sep->se_user = inetd_strdup(cp_ptr);
+       {
+               char *cp_ptr2 = strchr(sep->se_user, '.');
+
+               if (cp_ptr2) {
+                       *cp_ptr2++ = '\0';
+               }
+               sep->se_group = cp_ptr2;
+       }
+
+       cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
+       if (cp_ptr == NULL) {
+               /* error */
+               goto more;
+       }
+       if (strcmp(cp_ptr, "internal") == 0) {
+#ifdef INETD_FEATURE_ENABLED
+               const struct biltin *bi;
+
+               for (bi = biltins; bi->bi_service; bi++) {
+                       if ((bi->bi_socktype == sep->se_socktype) &&
+                           (strcmp(bi->bi_service, sep->se_service) == 0)) {
+                               break;
+                       }
+               }
+               if (bi->bi_service == 0) {
+                       syslog(LOG_ERR, "internal service %s unknown", sep->se_service);
+                       goto more;
+               }
+               sep->se_bi = bi;
+               sep->se_wait = bi->bi_wait;
+#else
+               syslog(LOG_ERR, "internal service %s unknown", cp_ptr);
+               goto more;
+#endif
+       }
+#ifdef INETD_FEATURE_ENABLED
+       else {
+               sep->se_bi = NULL;
+       }
+#endif
+       sep->se_server = inetd_strdup(cp_ptr);
+
+       argc = 0;
+       while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) {
+               if (argc < MAXARGV) {
+                       sep->se_argv[argc++] = inetd_strdup(cp_ptr);
+               }
+       }
+       free(cp);
+
+       return (sep);
+}
+
+static void freeconfig(servtab_t *cp)
+{
+       int i;
+
+       free(cp->se_service);
+       free(cp->se_proto);
+       free(cp->se_user);
+       /* Note: se_group is part of the newstr'ed se_user */
+       free(cp->se_server);
+       for (i = 0; i < MAXARGV; i++)
+               free(cp->se_argv[i]);
+}
+
+#ifdef INETD_FEATURE_ENABLED
+static char **Argv;
+static char *LastArg;
+
+static void setproctitle(char *a, int s)
+{
+       size_t size;
+       char *cp;
+       struct sockaddr_in sn;
+       char buf[80];
+
+       cp = Argv[0];
+       size = sizeof(sn);
+       if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
+               (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
+       else
+               (void) sprintf(buf, "-%s", a);
+       strncpy(cp, buf, LastArg - cp);
+       cp += strlen(cp);
+       while (cp < LastArg)
+               *cp++ = ' ';
+}
+#endif  /* INETD_FEATURE_ENABLED */
+
+
+static void setup(servtab_t *sep)
+{
+       int on = 1;
+
+       if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
+               syslog(LOG_ERR, "%s/%s: socket: %m",
+                   sep->se_service, sep->se_proto);
+               return;
+       }
+       if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
+                           sizeof(on)) < 0)
+               syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
+       if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
+               syslog(LOG_ERR, "%s/%s: bind: %m",
+                   sep->se_service, sep->se_proto);
+               (void) close(sep->se_fd);
+               sep->se_fd = -1;
+               if (!timingout) {
+                       timingout = 1;
+                       alarm(RETRYTIME);
+               }
+               return;
+       }
+       if (sep->se_socktype == SOCK_STREAM)
+               listen(sep->se_fd, global_queuelen);
+
+       FD_SET(sep->se_fd, &allsock);
+       nsock++;
+       if (sep->se_fd > maxsock) {
+               maxsock = sep->se_fd;
+               if (maxsock > rlim_ofile_cur - FD_MARGIN) {
+#ifdef RLIMIT_NOFILE
+# define FD_CHUNK        32
+                       struct rlimit rl;
+
+                       if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+                               syslog(LOG_ERR, "getrlimit: %m");
+                               return;
+                       }
+                       rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK);
+                       if (rl.rlim_cur <= rlim_ofile_cur) {
+                               syslog(LOG_ERR,
+# if _FILE_OFFSET_BITS == 64
+                                       "bump_nofile: cannot extend file limit, max = %lld",
+# else
+                                       "bump_nofile: cannot extend file limit, max = %ld",
+# endif
+                                       rl.rlim_cur);
+                               return;
+                       }
+
+                       if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+                               syslog(LOG_ERR, "setrlimit: %m");
+                               return;
+                       }
+
+                       rlim_ofile_cur = rl.rlim_cur;
+                       return;
+#else
+                       syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
+                       return;
+#endif /* RLIMIT_NOFILE */
+               }
+       }
+}
+
+static void config(int signum)
+{
+       servtab_t *sep, *cp, **sepp;
+       sigset_t oldmask;
+       unsigned n;
+
+       (void)signum;
+
+       if (fconfig != NULL) {
+               fseek(fconfig, 0L, L_SET);
+       } else {
+               fconfig = fopen(CONFIG, "r");
+               if (fconfig == NULL) {
+                       syslog(LOG_ERR, "%s: %m", CONFIG);
+                       return;
+               }
+       }
+
+       for (sep = servtab; sep; sep = sep->se_next)
+               sep->se_checked = 0;
+       while ((cp = getconfigent()) != NULL) {
+               for (sep = servtab; sep; sep = sep->se_next)
+                       if (strcmp(sep->se_service, cp->se_service) == 0 &&
+                           strcmp(sep->se_proto, cp->se_proto) == 0)
+                               break;
+               if (sep != 0) {
+                       int i;
+
+#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
+
+                       sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
+                       /*
+                        * sep->se_wait may be holding the pid of a daemon
+                        * that we're waiting for.  If so, don't overwrite
+                        * it unless the config file explicitly says don't
+                        * wait.
+                        */
+                       if (
+#ifdef INETD_FEATURE_ENABLED
+                           cp->se_bi == 0 &&
+#endif
+                           (sep->se_wait == 1 || cp->se_wait == 0))
+                               sep->se_wait = cp->se_wait;
+                       if (cp->se_max != sep->se_max)
+                               SWAP(int, cp->se_max, sep->se_max);
+                       if (cp->se_user)
+                               SWAP(char *, sep->se_user, cp->se_user);
+                       if (cp->se_group)
+                               SWAP(char *, sep->se_group, cp->se_group);
+                       if (cp->se_server)
+                               SWAP(char *, sep->se_server, cp->se_server);
+                       for (i = 0; i < MAXARGV; i++)
+                               SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
+#undef SWAP
+                       sigprocmask(SIG_SETMASK, &oldmask, NULL);
+                       // This freeconfig() is probably a bug, since it will try and free()
+                       // each of the argv[] values, which are really just pointers
+                       // into the middle of a single line buffer for the config file.
+                       //freeconfig(cp);       // BUG?
+               } else {
+                       sep = (servtab_t *)xmalloc(sizeof (*sep));
+                       *sep = *cp;
+                       sep->se_fd = -1;
+                       sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+                       sep->se_next = servtab;
+                       servtab = sep;
+                       sigprocmask(SIG_SETMASK, &oldmask, NULL);
+               }
+               sep->se_checked = 1;
+
+               switch (sep->se_family) {
+               case AF_UNIX:
+                       if (sep->se_fd != -1)
+                               break;
+                       (void)unlink(sep->se_service);
+                       n = strlen(sep->se_service);
+                       if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
+                               n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
+                       strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
+                       sep->se_ctrladdr_un.sun_family = AF_UNIX;
+                       sep->se_ctrladdr_size = n +
+                                       sizeof sep->se_ctrladdr_un.sun_family;
+                       setup(sep);
+                       break;
+               case AF_INET:
+                       sep->se_ctrladdr_in.sin_family = AF_INET;
+                       sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
+                       {
+                               u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0);
+
+                               if (port == 0) {
+                                       syslog(LOG_ERR,
+                                           "%s/%s: unknown service",
+                                           sep->se_service, sep->se_proto);
+                                       continue;
+                               }
+                               if (port != sep->se_ctrladdr_in.sin_port) {
+                                       sep->se_ctrladdr_in.sin_port = port;
+                                       if (sep->se_fd != -1) {
+                                               FD_CLR(sep->se_fd, &allsock);
+                                               nsock--;
+                                               (void) close(sep->se_fd);
+                                       }
+                                       sep->se_fd = -1;
+                               }
+                               if (sep->se_fd == -1)
+                                       setup(sep);
+                       }
+               }
+       }
+       if (fconfig) {
+               (void) fclose(fconfig);
+               fconfig = NULL;
+       }
+       /*
+        * Purge anything not looked at above.
+        */
+       sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
+       sepp = &servtab;
+       while ((sep = *sepp) != NULL) {
+               if (sep->se_checked) {
+                       sepp = &sep->se_next;
+                       continue;
+               }
+               *sepp = sep->se_next;
+               if (sep->se_fd != -1) {
+                       FD_CLR(sep->se_fd, &allsock);
+                       nsock--;
+                       (void) close(sep->se_fd);
+               }
+               if (sep->se_family == AF_UNIX)
+                       (void)unlink(sep->se_service);
+               freeconfig(sep);
+               free((char *)sep);
+       }
+       sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
+
+
+static void reapchild(int signum)
+{
+       int status;
+       int pid;
+       servtab_t *sep;
+
+       (void)signum;
+       for (;;) {
+               pid = wait3(&status, WNOHANG, (struct rusage *)0);
+               if (pid <= 0)
+                       break;
+               for (sep = servtab; sep; sep = sep->se_next)
+                       if (sep->se_wait == pid) {
+                               if (WIFEXITED(status) && WEXITSTATUS(status))
+                                       syslog(LOG_WARNING,
+                                           "%s: exit status 0x%x",
+                                           sep->se_server, WEXITSTATUS(status));
+                               else if (WIFSIGNALED(status))
+                                       syslog(LOG_WARNING,
+                                           "%s: exit signal 0x%x",
+                                           sep->se_server, WTERMSIG(status));
+                               sep->se_wait = 1;
+                               FD_SET(sep->se_fd, &allsock);
+                               nsock++;
+                       }
+       }
+}
+
+static void retry(int signum)
+{
+       servtab_t *sep;
+
+       (void)signum;
+       timingout = 0;
+       for (sep = servtab; sep; sep = sep->se_next) {
+               if (sep->se_fd == -1) {
+                       switch (sep->se_family) {
+                       case AF_UNIX:
+                       case AF_INET:
+                               setup(sep);
+                               break;
+                       }
+               }
+       }
+}
+
+static void goaway(int signum)
+{
+       servtab_t *sep;
+
+       (void)signum;
+       for (sep = servtab; sep; sep = sep->se_next)
+               if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
+                       (void)unlink(sep->se_service);
+       (void)unlink(_PATH_INETDPID);
+       exit(0);
+}
+
+
+
+extern int inetd_main(int argc, char *argv[])
+{
+       servtab_t *sep;
+       struct group *grp = NULL;
+       struct sigaction sa;
+       int pid;
+       unsigned long opt;
+       char *sq;
+       gid_t gid;
+
+#ifdef INETD_FEATURE_ENABLED
+       extern char **environ;
+#endif
+
+       gid = getgid();
+       setgroups(1, &gid);
+
+#ifdef INETD_FEATURE_ENABLED
+       Argv = argv;
+       if (environ == 0 || *environ == 0)
+               environ = argv;
+       while (*environ)
+               environ++;
+       LastArg = environ[-1] + strlen(environ[-1]);
+#endif
+
+#if defined(__uClinux__)
+       opt = bb_getopt_ulflags(argc, argv, "q:f", &sq);
+       if (!(opt & 2)) {
+           daemon(0, 0);
+           /* reexec for vfork() do continue parent */
+           vfork_daemon_rexec(argc, argv, "-f");
+       }
+#else
+       opt = bb_getopt_ulflags(argc, argv, "q:", &sq);
+       daemon(0, 0);
+#endif /* uClinux */
+
+       if(opt & 1) {
+                       global_queuelen = atoi(sq);
+                       if (global_queuelen < 8) global_queuelen=8;
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc > 0)
+               CONFIG = argv[0];
+
+       openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+       {
+               FILE *fp;
+
+               if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
+                       fprintf(fp, "%u\n", getpid());
+                       (void)fclose(fp);
+               }
+       }
+
+#ifdef RLIMIT_NOFILE
+       if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
+               syslog(LOG_ERR, "getrlimit: %m");
+       } else {
+               rlim_ofile_cur = rlim_ofile.rlim_cur;
+               if (rlim_ofile_cur == RLIM_INFINITY)    /* ! */
+                       rlim_ofile_cur = OPEN_MAX;
+       }
+#endif
+
+       config(0);
+
+       sigemptyset(&emptymask);
+       sigemptyset(&blockmask);
+       sigaddset(&blockmask, SIGCHLD);
+       sigaddset(&blockmask, SIGHUP);
+       sigaddset(&blockmask, SIGALRM);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_mask = blockmask;
+       sa.sa_handler = retry;
+       sigaction(SIGALRM, &sa, NULL);
+       sa.sa_handler = config;
+       sigaction(SIGHUP, &sa, NULL);
+       sa.sa_handler = reapchild;
+       sigaction(SIGCHLD, &sa, NULL);
+       sa.sa_handler = goaway;
+       sigaction(SIGTERM, &sa, NULL);
+       sa.sa_handler = goaway;
+       sigaction(SIGINT, &sa,  NULL);
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGPIPE, &sa, NULL);
+
+       {
+               /* space for daemons to overwrite environment for ps */
+#define DUMMYSIZE       100
+               char dummy[DUMMYSIZE];
+
+               (void)memset(dummy, 'x', DUMMYSIZE - 1);
+               dummy[DUMMYSIZE - 1] = '\0';
+
+               (void)setenv("inetd_dummy", dummy, 1);
+       }
+
+       for (;;) {
+               fd_set readable;
+               int     ctrl;
+               int n;
+
+               if (nsock == 0) {
+                       sigprocmask(SIG_BLOCK, &blockmask, NULL);
+                       while (nsock == 0) {
+                               sigsuspend(&emptymask);
+                       }
+                       sigprocmask(SIG_SETMASK, &emptymask, NULL);
+               }
+               readable = allsock;
+               n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
+               if (n <= 0) {
+                       if (n < 0 && errno != EINTR) {
+                               syslog(LOG_WARNING, "select: %m");
+                       }
+                       sleep(1);
+                       continue;
+           }
+               for (sep = servtab; n && sep; sep = sep->se_next) {
+                       if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
+                               n--;
+                               if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
+                                       /* Fixed AGC */
+                                       fcntl(sep->se_fd, F_SETFL, O_NDELAY);
+                                       /* --------- */
+                                       ctrl = accept(sep->se_fd, NULL, NULL);
+                                       fcntl(sep->se_fd, F_SETFL, 0);
+                                       if (ctrl < 0) {
+                                               if (errno == EINTR || errno == EWOULDBLOCK) {
+                                                       continue;
+                                               }
+                                               syslog(LOG_WARNING, "accept (for %s): %m",
+                                               sep->se_service);
+                                               continue;
+                                       }
+                               } else {
+                                       ctrl = sep->se_fd;
+                               }
+                               sigprocmask(SIG_BLOCK, &blockmask, NULL);
+                               pid = 0;
+#ifdef INETD_FEATURE_ENABLED
+                               if (sep->se_bi == 0 || sep->se_bi->bi_fork)
+#endif
+                               {
+                                       if (sep->se_count++ == 0) {
+                                               gettimeofday(&sep->se_time, (struct timezone *)0);
+                                       }
+                                       else if (sep->se_count >= sep->se_max) {
+                                               struct timeval now;
+
+                                               gettimeofday(&now, (struct timezone *)0);
+                                               if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
+                                                       sep->se_time = now;
+                                                       sep->se_count = 1;
+                                               } else {
+                                                       syslog(LOG_ERR,
+                                                               "%s/%s server failing (looping), service terminated",
+                                                               sep->se_service, sep->se_proto);
+                                                       FD_CLR(sep->se_fd, &allsock);
+                                                       close(sep->se_fd);
+                                                       sep->se_fd = -1;
+                                                       sep->se_count = 0;
+                                                       nsock--;
+                                                       sigprocmask(SIG_SETMASK, &emptymask, NULL);
+                                                       if (!timingout) {
+                                                               timingout = 1;
+                                                               alarm(RETRYTIME);
+                                                       }
+                                                       continue;
+                                               }
+                                       }
+                                       pid = fork();
+                                       if (pid < 0) {
+                                               syslog(LOG_ERR, "fork: %m");
+                                               if (sep->se_socktype == SOCK_STREAM) {
+                                                       close(ctrl);
+                                               }
+                                               sigprocmask(SIG_SETMASK, &emptymask, NULL);
+                                               sleep(1);
+                                               continue;
+                                       }
+                                       if (pid && sep->se_wait) {
+                                               sep->se_wait = pid;
+                                               FD_CLR(sep->se_fd, &allsock);
+                                               nsock--;
+                                       }
+                               }
+                               sigprocmask(SIG_SETMASK, &emptymask, NULL);
+                               if (pid == 0) {
+#ifdef INETD_FEATURE_ENABLED
+                                       if (sep->se_bi) {
+                                               (*sep->se_bi->bi_fn)(ctrl, sep);
+                                       } else
+#endif
+                                       {
+                                               struct passwd *pwd = getpwnam(sep->se_user);
+                                               if (pwd == NULL) {
+                                                       syslog_err_and_discard_dg(
+                                                               sep->se_socktype,
+                                                               "getpwnam: %s: No such user",
+                                                               sep->se_user);
+                                               }
+                                               if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
+                                                       syslog_err_and_discard_dg(sep->se_socktype,
+                                                               "getgrnam: %s: No such group", sep->se_group);
+                                               }
+                                               /*
+                                                * Ok. There are four cases here:
+                                                *   1. nonroot user, no group specified
+                                                *   2. nonroot user, some group specified
+                                                *   3. root user, no group specified
+                                                *   4. root user, some group specified
+                                                * In cases 2 and 4 we setgid to the specified
+                                                * group. In cases 1 and 2 we run initgroups
+                                                * to run with the groups of the given user.
+                                                * In case 4 we do setgroups to run with the
+                                                * given group. In case 3 we do nothing.
+                                                */
+                                               if (pwd->pw_uid) {
+                                                       if (sep->se_group) {
+                                                               pwd->pw_gid = grp->gr_gid;
+                                                       }
+                                                       setgid((gid_t)pwd->pw_gid);
+                                                       initgroups(pwd->pw_name, pwd->pw_gid);
+                                                       setuid((uid_t)pwd->pw_uid);
+                                               } else if (sep->se_group) {
+                                                       setgid((gid_t)grp->gr_gid);
+                                                       setgroups(1, &grp->gr_gid);
+                                               }
+                                               dup2(ctrl, 0);
+                                               close(ctrl);
+                                               dup2(0, 1);
+                                               dup2(0, 2);
+#ifdef RLIMIT_NOFILE
+                                               if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
+                                                       if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
+                                                               syslog(LOG_ERR,"setrlimit: %m");
+                                                       }
+                                               }
+#endif
+                                               for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) {
+                                                       (void)close(ctrl);
+                                               }
+                                               memset(&sa, 0, sizeof(sa));
+                                               sa.sa_handler = SIG_DFL;
+                                               sigaction(SIGPIPE, &sa, NULL);
+
+                                               execv(sep->se_server, sep->se_argv);
+                                               syslog_err_and_discard_dg(sep->se_socktype, "execv %s: %m", sep->se_server);
+                                       }
+                               }
+                               if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
+                                       close(ctrl);
+                               }
+                       }
+               }
+       }
+}
+
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define BUFSIZE 4096
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+/* Echo service -- echo data back */
+static void echo_stream(int s, servtab_t *sep)
+{
+       char buffer[BUFSIZE];
+       int i;
+
+       setproctitle(sep->se_service, s);
+       while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
+           write(s, buffer, i) > 0)
+               ;
+       exit(0);
+}
+
+/* Echo service -- echo data back */
+static void echo_dg(int s, servtab_t *sep)
+{
+       char buffer[BUFSIZE];
+       int i;
+       size_t size;
+       struct sockaddr sa;
+
+       (void)sep;
+
+       size = sizeof(sa);
+       if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
+               return;
+       (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+/* Discard service -- ignore data */
+static void discard_stream(int s, servtab_t *sep)
+{
+       char buffer[BUFSIZE];
+
+       setproctitle(sep->se_service, s);
+       while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
+                       errno == EINTR)
+               ;
+       exit(0);
+}
+
+/* Discard service -- ignore data */
+static void discard_dg(int s, servtab_t *sep)
+{
+       char buffer[BUFSIZE];
+       (void)sep;
+       read(s, buffer, sizeof(buffer));
+}
+#endif  /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+#include <ctype.h>
+#define LINESIZ 72
+static char ring[128];
+static char *endring;
+
+static void initring(void)
+{
+       int i;
+
+       endring = ring;
+
+       for (i = 0; i <= 128; ++i)
+               if (isprint(i))
+                       *endring++ = i;
+}
+
+/* Character generator */
+static void chargen_stream(int s, servtab_t *sep)
+{
+       char *rs;
+       int len;
+       char text[LINESIZ+2];
+
+       setproctitle(sep->se_service, s);
+
+       if (!endring) {
+               initring();
+               rs = ring;
+       }
+
+       text[LINESIZ] = '\r';
+       text[LINESIZ + 1] = '\n';
+       for (rs = ring;;) {
+               if ((len = endring - rs) >= LINESIZ)
+                       memcpy(rs, text, LINESIZ);
+               else {
+                       memcpy(rs, text, len);
+                       memcpy(ring, text + len, LINESIZ - len);
+               }
+               if (++rs == endring)
+                       rs = ring;
+               if (write(s, text, sizeof(text)) != sizeof(text))
+                       break;
+       }
+       exit(0);
+}
+
+/* Character generator */
+static void chargen_dg(int s, servtab_t *sep)
+{
+       struct sockaddr sa;
+       static char *rs;
+       size_t len, size;
+       char text[LINESIZ+2];
+
+       (void)sep;
+
+       if (endring == 0) {
+               initring();
+               rs = ring;
+       }
+
+       size = sizeof(sa);
+       if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
+               return;
+
+       if ((len = endring - rs) >= LINESIZ)
+               memcpy(rs, text, LINESIZ);
+       else {
+               memcpy(rs, text, len);
+               memcpy(ring, text + len, LINESIZ - len);
+       }
+       if (++rs == endring)
+               rs = ring;
+       text[LINESIZ] = '\r';
+       text[LINESIZ + 1] = '\n';
+       (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+static long machtime(void)
+{
+       struct timeval tv;
+
+       if (gettimeofday(&tv, (struct timezone *)0) < 0) {
+               fprintf(stderr, "Unable to get time of day\n");
+               return (0L);
+       }
+       return (htonl((long)tv.tv_sec + 2208988800UL));
+}
+
+static void machtime_stream(int s, servtab_t *sep)
+{
+       long result;
+       (void)sep;
+
+       result = machtime();
+       write(s, (char *) &result, sizeof(result));
+}
+
+static void machtime_dg(int s, servtab_t *sep)
+{
+       long result;
+       struct sockaddr sa;
+       size_t size;
+       (void)sep;
+
+       size = sizeof(sa);
+       if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
+               return;
+       result = machtime();
+       (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+/* Return human-readable time of day */
+static int human_readable_time_sprintf(char *buffer)
+{
+       time_t clocc = time(NULL);
+
+       return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
+}
+
+static void daytime_stream(int s, servtab_t *sep)
+{
+       char buffer[256];
+       size_t st = human_readable_time_sprintf(buffer);
+
+       (void)sep;
+
+       write(s, buffer, st);
+}
+
+/* Return human-readable time of day */
+static void daytime_dg(int s, servtab_t *sep)
+{
+       char buffer[256];
+       struct sockaddr sa;
+       size_t size;
+
+       (void)sep;
+
+       size = sizeof(sa);
+       if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
+               return;
+       size = human_readable_time_sprintf(buffer);
+       sendto(s, buffer, size, 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */
diff --git a/busybox/networking/ip.c b/busybox/networking/ip.c
new file mode 100644 (file)
index 0000000..cfdbf8b
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+#if 0
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+char * _SL_ = NULL;
+
+void ip_parse_common_args(int *argcp, char ***argvp)
+{
+       int argc = *argcp;
+       char **argv = *argvp;
+
+       while (argc > 1) {
+               char *opt = argv[1];
+
+               if (strcmp(opt,"--") == 0) {
+                       argc--; argv++;
+                       break;
+               }
+
+               if (opt[0] != '-')
+                       break;
+
+               if (opt[1] == '-')
+                       opt++;
+
+               if (matches(opt, "-family") == 0) {
+                       argc--;
+                       argv++;
+                       if (strcmp(argv[1], "inet") == 0)
+                               preferred_family = AF_INET;
+                       else if (strcmp(argv[1], "inet6") == 0)
+                               preferred_family = AF_INET6;
+                       else if (strcmp(argv[1], "link") == 0)
+                               preferred_family = AF_PACKET;
+                       else
+                               invarg(argv[1], "invalid protocol family");
+               } else if (strcmp(opt, "-4") == 0) {
+                       preferred_family = AF_INET;
+               } else if (strcmp(opt, "-6") == 0) {
+                       preferred_family = AF_INET6;
+               } else if (strcmp(opt, "-0") == 0) {
+                       preferred_family = AF_PACKET;
+               } else if (matches(opt, "-oneline") == 0) {
+                       ++oneline;
+               } else {
+                       bb_show_usage();
+               }
+               argc--; argv++;
+       }
+       _SL_ = oneline ? "\\" : "\n" ;
+}
+#endif
+
+int ip_main(int argc, char **argv)
+{
+       int ret = EXIT_FAILURE;
+
+       ip_parse_common_args(&argc, &argv);
+
+       if (argc > 1) {
+#ifdef CONFIG_FEATURE_IP_ADDRESS
+               if (matches(argv[1], "address") == 0) {
+                       ret = do_ipaddr(argc-2, argv+2);
+               }
+#endif
+#ifdef CONFIG_FEATURE_IP_ROUTE
+               if (matches(argv[1], "route") == 0) {
+                       ret = do_iproute(argc-2, argv+2);
+               }
+#endif
+#ifdef CONFIG_FEATURE_IP_LINK
+               if (matches(argv[1], "link") == 0) {
+                       ret = do_iplink(argc-2, argv+2);
+               }
+#endif
+#ifdef CONFIG_FEATURE_IP_TUNNEL
+               if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) {
+                       ret = do_iptunnel(argc-2, argv+2);
+               }
+#endif
+       }
+       if (ret) {
+               bb_show_usage();
+       }
+       return(EXIT_SUCCESS);
+}
diff --git a/busybox/networking/ipaddr.c b/busybox/networking/ipaddr.c
new file mode 100644 (file)
index 0000000..7826194
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int ipaddr_main(int argc, char **argv)
+{
+       ip_parse_common_args(&argc, &argv);
+
+       return do_ipaddr(argc-1, argv+1);
+}
diff --git a/busybox/networking/ipcalc.c b/busybox/networking/ipcalc.c
new file mode 100644 (file)
index 0000000..bcd272b
--- /dev/null
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4 ai: */
+/*
+ * Mini ipcalc implementation for busybox
+ *
+ * By Jordan Crouse <jordan@cosmicpenguin.net>
+ *    Stephan Linz  <linz@li-pro.net>
+ *
+ * This is a complete reimplementation of the ipcalc program
+ * from Red Hat.  I didn't look at their source code, but there
+ * is no denying that this is a loving reimplementation
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "busybox.h"
+
+#define IPCALC_MSG(CMD,ALTCMD) if (mode & SILENT) {ALTCMD;} else {CMD;}
+
+#define CLASS_A_NETMASK        ntohl(0xFF000000)
+#define CLASS_B_NETMASK        ntohl(0xFFFF0000)
+#define CLASS_C_NETMASK        ntohl(0xFFFFFF00)
+
+static unsigned long get_netmask(unsigned long ipaddr)
+{
+       ipaddr = htonl(ipaddr);
+
+       if ((ipaddr & 0xC0000000) == 0xC0000000)
+               return CLASS_C_NETMASK;
+       else if ((ipaddr & 0x80000000) == 0x80000000)
+               return CLASS_B_NETMASK;
+       else if ((ipaddr & 0x80000000) == 0)
+               return CLASS_A_NETMASK;
+       else
+               return 0;
+}
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+static int get_prefix(unsigned long netmask)
+{
+       unsigned long msk = 0x80000000;
+       int ret = 0;
+
+       netmask = htonl(netmask);
+       while(msk) {
+               if (netmask & msk)
+                       ret++;
+               msk >>= 1;
+       }
+       return ret;
+}
+#endif
+
+#define NETMASK   0x01
+#define BROADCAST 0x02
+#define NETWORK   0x04
+#define NETPREFIX 0x08
+#define HOSTNAME  0x10
+#define SILENT    0x20
+
+
+int ipcalc_main(int argc, char **argv)
+{
+       unsigned long mode;
+
+       in_addr_t netmask;
+       in_addr_t broadcast;
+       in_addr_t network;
+       in_addr_t ipaddr;
+       struct in_addr a;
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+       unsigned long netprefix = 0;
+       int have_netmask = 0;
+       char *ipstr, *prefixstr;
+#endif
+
+       static const struct option long_options[] = {
+               {"netmask",             no_argument, NULL, 'm'},
+               {"broadcast",   no_argument, NULL, 'b'},
+               {"network",             no_argument, NULL, 'n'},
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+               {"prefix",              no_argument, NULL, 'p'},
+               {"hostname",    no_argument, NULL, 'h'},
+               {"silent",              no_argument, NULL, 's'},
+#endif
+               {NULL, 0, NULL, 0}
+       };
+
+       bb_applet_long_options = long_options;
+       mode = bb_getopt_ulflags(argc, argv,
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+                       "mbnphs"
+#else
+                       "mbn"
+#endif
+                       );
+       if (mode & (BROADCAST | NETWORK | NETPREFIX)) {
+               if (argc - optind > 2) {
+                       bb_show_usage();
+               }
+       } else {
+               if (argc - optind != 1) {
+                       bb_show_usage();
+               }
+       }
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+       prefixstr = ipstr = argv[optind];
+
+       while(*prefixstr) {
+               if (*prefixstr == '/') {
+                       *prefixstr = (char)0;
+                       prefixstr++;
+                       if (*prefixstr) {
+                               unsigned int msk;
+
+                               if (safe_strtoul(prefixstr, &netprefix) || netprefix > 32) {
+                                       IPCALC_MSG(bb_error_msg_and_die("bad IP prefix: %s\n", prefixstr),
+                                                       exit(EXIT_FAILURE));
+                               }
+                               netmask = 0;
+                               msk = 0x80000000;
+                               while (netprefix > 0) {
+                                       netmask |= msk;
+                                       msk >>= 1;
+                                       netprefix--;
+                               }
+                               netmask = htonl(netmask);
+                               /* Even if it was 0, we will signify that we have a netmask. This allows */
+                               /* for specification of default routes, etc which have a 0 netmask/prefix */
+                               have_netmask = 1;
+                       }
+                       break;
+               }
+               prefixstr++;
+       }
+       ipaddr = inet_aton(ipstr, &a);
+#else
+       ipaddr = inet_aton(argv[optind], &a);
+#endif
+
+       if (ipaddr == 0) {
+               IPCALC_MSG(bb_error_msg_and_die("bad IP address: %s", argv[optind]),
+                               exit(EXIT_FAILURE));
+       }
+       ipaddr = a.s_addr;
+
+       if (argc - optind == 2) {
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+               if (have_netmask) {
+                       IPCALC_MSG(bb_error_msg_and_die("Both prefix and netmask were specified, use one or the other.\n"),
+                                       exit(EXIT_FAILURE));
+               }
+
+#endif
+               netmask = inet_aton(argv[optind + 1], &a);
+               if (netmask == 0) {
+                       IPCALC_MSG(bb_error_msg_and_die("bad netmask: %s", argv[optind + 1]),
+                                       exit(EXIT_FAILURE));
+               }
+               netmask = a.s_addr;
+       } else {
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+
+               if (!have_netmask)
+#endif
+                       /* JHC - If the netmask wasn't provided then calculate it */
+                       netmask = get_netmask(ipaddr);
+       }
+
+       if (mode & NETMASK) {
+               printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask)));
+       }
+
+       if (mode & BROADCAST) {
+               broadcast = (ipaddr & netmask) | ~netmask;
+               printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast)));
+       }
+
+       if (mode & NETWORK) {
+               network = ipaddr & netmask;
+               printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network)));
+       }
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+       if (mode & NETPREFIX) {
+               printf("PREFIX=%i\n", get_prefix(netmask));
+       }
+
+       if (mode & HOSTNAME) {
+               struct hostent *hostinfo;
+               int x;
+
+               hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
+               if (!hostinfo) {
+                       IPCALC_MSG(bb_herror_msg_and_die(
+                                               "cannot find hostname for %s", argv[optind]),);
+                       exit(EXIT_FAILURE);
+               }
+               for (x = 0; hostinfo->h_name[x]; x++) {
+                       hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
+               }
+
+               printf("HOSTNAME=%s\n", hostinfo->h_name);
+       }
+#endif
+
+       return EXIT_SUCCESS;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/networking/iplink.c b/busybox/networking/iplink.c
new file mode 100644 (file)
index 0000000..7207e17
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iplink_main(int argc, char **argv)
+{
+       ip_parse_common_args(&argc, &argv);
+
+       return do_iplink(argc-1, argv+1);
+}
diff --git a/busybox/networking/iproute.c b/busybox/networking/iproute.c
new file mode 100644 (file)
index 0000000..d049a87
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iproute_main(int argc, char **argv)
+{
+       ip_parse_common_args(&argc, &argv);
+
+       return do_iproute(argc-1, argv+1);
+}
diff --git a/busybox/networking/iptunnel.c b/busybox/networking/iptunnel.c
new file mode 100644 (file)
index 0000000..f2b2e8a
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iptunnel_main(int argc, char **argv)
+{
+       ip_parse_common_args(&argc, &argv);
+
+       return do_iptunnel(argc-1, argv+1);
+}
diff --git a/busybox/networking/libiproute/Makefile b/busybox/networking/libiproute/Makefile
new file mode 100644 (file)
index 0000000..d3aefaa
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=../..
+top_builddir=../..
+srcdir=$(top_srcdir)/networking/libiproute
+LIBIPROUTE_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/networking/libiproute/Makefile.in b/busybox/networking/libiproute/Makefile.in
new file mode 100644 (file)
index 0000000..fcc7f48
--- /dev/null
@@ -0,0 +1,84 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LIBIPROUTE_AR:=libiproute.a
+ifndef $(LIBIPROUTE_DIR)
+LIBIPROUTE_DIR:=$(top_builddir)/networking/libiproute/
+endif
+srcdir=$(top_srcdir)/networking/libiproute
+
+LIBIPROUTE-$(CONFIG_IP) += \
+       ip_parse_common_args.o \
+       ipaddress.o \
+       iplink.o \
+       iproute.o \
+       iptunnel.o \
+       libnetlink.o \
+       ll_addr.o \
+       ll_map.o \
+       ll_proto.o \
+       ll_types.o \
+       rt_names.o \
+       rtm_map.o \
+       utils.o
+
+LIBIPROUTE-$(CONFIG_IPADDR) += \
+       ip_parse_common_args.o \
+       ipaddress.o \
+       libnetlink.o \
+       ll_addr.o \
+       ll_map.o \
+       ll_types.o \
+       rt_names.o \
+       utils.o
+
+LIBIPROUTE-$(CONFIG_IPLINK) += \
+       ip_parse_common_args.o \
+       ipaddress.o \
+       iplink.o \
+       libnetlink.o \
+       ll_addr.o \
+       ll_map.o \
+       ll_types.o \
+       rt_names.o \
+       utils.o
+
+LIBIPROUTE-$(CONFIG_IPROUTE) += \
+       ip_parse_common_args.o \
+       iproute.o \
+       libnetlink.o \
+       ll_map.o \
+       rt_names.o \
+       rtm_map.o \
+       utils.o
+
+LIBIPROUTE-$(CONFIG_IPTUNNEL) += \
+       ip_parse_common_args.o \
+       iptunnel.o \
+       rt_names.o \
+       utils.o
+
+libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR)
+
+$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
+       $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
+
+$(LIBIPROUTE_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/networking/libiproute/ip_common.h b/busybox/networking/libiproute/ip_common.h
new file mode 100644 (file)
index 0000000..25e9c6c
--- /dev/null
@@ -0,0 +1,18 @@
+extern int preferred_family;
+extern char * _SL_;
+
+extern void ip_parse_common_args(int *argcp, char ***argvp);
+extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ipaddr_list_or_flush(int argc, char **argv, int flush);
+extern int iproute_monitor(int argc, char **argv);
+extern void iplink_usage(void) __attribute__((noreturn));
+extern void ipneigh_reset_filter(void);
+extern int do_ipaddr(int argc, char **argv);
+extern int do_iproute(int argc, char **argv);
+extern int do_iprule(int argc, char **argv);
+extern int do_ipneigh(int argc, char **argv);
+extern int do_iptunnel(int argc, char **argv);
+extern int do_iplink(int argc, char **argv);
+extern int do_ipmonitor(int argc, char **argv);
+extern int do_multiaddr(int argc, char **argv);
+extern int do_multiroute(int argc, char **argv);
diff --git a/busybox/networking/libiproute/ip_parse_common_args.c b/busybox/networking/libiproute/ip_parse_common_args.c
new file mode 100644 (file)
index 0000000..a76df48
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * ip.c                "ip" utility frontend.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include <string.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+#include "busybox.h"
+
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+char * _SL_ = NULL;
+
+void ip_parse_common_args(int *argcp, char ***argvp)
+{
+       int argc = *argcp;
+       char **argv = *argvp;
+
+       while (argc > 1) {
+               char *opt = argv[1];
+
+               if (strcmp(opt,"--") == 0) {
+                       argc--; argv++;
+                       break;
+               }
+
+               if (opt[0] != '-')
+                       break;
+
+               if (opt[1] == '-')
+                       opt++;
+
+               if (matches(opt, "-family") == 0) {
+                       argc--;
+                       argv++;
+                       if (! argv[1])
+                           bb_show_usage();
+                       if (strcmp(argv[1], "inet") == 0)
+                               preferred_family = AF_INET;
+                       else if (strcmp(argv[1], "inet6") == 0)
+                               preferred_family = AF_INET6;
+                       else if (strcmp(argv[1], "link") == 0)
+                               preferred_family = AF_PACKET;
+                       else
+                               invarg(argv[1], "invalid protocol family");
+               } else if (strcmp(opt, "-4") == 0) {
+                       preferred_family = AF_INET;
+               } else if (strcmp(opt, "-6") == 0) {
+                       preferred_family = AF_INET6;
+               } else if (strcmp(opt, "-0") == 0) {
+                       preferred_family = AF_PACKET;
+               } else if (matches(opt, "-oneline") == 0) {
+                       ++oneline;
+               } else {
+                       bb_show_usage();
+               }
+               argc--; argv++;
+       }
+       _SL_ = oneline ? "\\" : "\n" ;
+       *argcp = argc;
+       *argvp = argv;
+}
diff --git a/busybox/networking/libiproute/ipaddress.c b/busybox/networking/libiproute/ipaddress.c
new file mode 100644 (file)
index 0000000..7e0c757
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * ipaddress.c         "ip address".
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ *     Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <fnmatch.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "libbb.h"
+
+static struct
+{
+       int ifindex;
+       int family;
+       int oneline;
+       int showqueue;
+       inet_prefix pfx;
+       int scope, scopemask;
+       int flags, flagmask;
+       int up;
+       char *label;
+       int flushed;
+       char *flushb;
+       int flushp;
+       int flushe;
+       struct rtnl_handle *rth;
+} filter;
+
+void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+{
+       fprintf(fp, "<");
+       flags &= ~IFF_RUNNING;
+#define _PF(f) if (flags&IFF_##f) { \
+                  flags &= ~IFF_##f ; \
+                  fprintf(fp, #f "%s", flags ? "," : ""); }
+       _PF(LOOPBACK);
+       _PF(BROADCAST);
+       _PF(POINTOPOINT);
+       _PF(MULTICAST);
+       _PF(NOARP);
+#if 0
+       _PF(ALLMULTI);
+       _PF(PROMISC);
+       _PF(MASTER);
+       _PF(SLAVE);
+       _PF(DEBUG);
+       _PF(DYNAMIC);
+       _PF(AUTOMEDIA);
+       _PF(PORTSEL);
+       _PF(NOTRAILERS);
+#endif
+       _PF(UP);
+#undef _PF
+        if (flags)
+               fprintf(fp, "%x", flags);
+       if (mdown)
+               fprintf(fp, ",M-DOWN");
+       fprintf(fp, "> ");
+}
+
+static void print_queuelen(char *name)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s < 0)
+               return;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, name);
+       if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
+               perror("SIOCGIFXQLEN");
+               close(s);
+               return;
+       }
+       close(s);
+
+       if (ifr.ifr_qlen)
+               printf("qlen %d", ifr.ifr_qlen);
+}
+
+static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = (FILE*)arg;
+       struct ifinfomsg *ifi = NLMSG_DATA(n);
+       struct rtattr * tb[IFLA_MAX+1];
+       int len = n->nlmsg_len;
+       unsigned m_flag = 0;
+
+       if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+               return 0;
+
+       len -= NLMSG_LENGTH(sizeof(*ifi));
+       if (len < 0)
+               return -1;
+
+       if (filter.ifindex && ifi->ifi_index != filter.ifindex)
+               return 0;
+       if (filter.up && !(ifi->ifi_flags&IFF_UP))
+               return 0;
+
+       memset(tb, 0, sizeof(tb));
+       parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+       if (tb[IFLA_IFNAME] == NULL) {
+               bb_error_msg("nil ifname");
+               return -1;
+       }
+       if (filter.label &&
+           (!filter.family || filter.family == AF_PACKET) &&
+           fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+               return 0;
+
+       if (n->nlmsg_type == RTM_DELLINK)
+               fprintf(fp, "Deleted ");
+
+       fprintf(fp, "%d: %s", ifi->ifi_index,
+               tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+
+       if (tb[IFLA_LINK]) {
+               SPRINT_BUF(b1);
+               int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+               if (iflink == 0)
+                       fprintf(fp, "@NONE: ");
+               else {
+                       fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+                       m_flag = ll_index_to_flags(iflink);
+                       m_flag = !(m_flag & IFF_UP);
+               }
+       } else {
+               fprintf(fp, ": ");
+       }
+       print_link_flags(fp, ifi->ifi_flags, m_flag);
+
+       if (tb[IFLA_MTU])
+               fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+       if (tb[IFLA_QDISC])
+               fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+#ifdef IFLA_MASTER
+       if (tb[IFLA_MASTER]) {
+               SPRINT_BUF(b1);
+               fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+       }
+#endif
+       if (filter.showqueue)
+               print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
+
+       if (!filter.family || filter.family == AF_PACKET) {
+               SPRINT_BUF(b1);
+               fprintf(fp, "%s", _SL_);
+               fprintf(fp, "    link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+
+               if (tb[IFLA_ADDRESS]) {
+                       fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+                                                     RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+                                                     ifi->ifi_type,
+                                                     b1, sizeof(b1)));
+               }
+               if (tb[IFLA_BROADCAST]) {
+                       if (ifi->ifi_flags&IFF_POINTOPOINT)
+                               fprintf(fp, " peer ");
+                       else
+                               fprintf(fp, " brd ");
+                       fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+                                                     RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+                                                     ifi->ifi_type,
+                                                     b1, sizeof(b1)));
+               }
+       }
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
+
+static int flush_update(void)
+{
+       if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+               perror("Failed to send flush request\n");
+               return -1;
+       }
+       filter.flushp = 0;
+       return 0;
+}
+
+static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = (FILE*)arg;
+       struct ifaddrmsg *ifa = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       struct rtattr * rta_tb[IFA_MAX+1];
+       char abuf[256];
+       SPRINT_BUF(b1);
+
+       if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
+               return 0;
+       len -= NLMSG_LENGTH(sizeof(*ifa));
+       if (len < 0) {
+               bb_error_msg("wrong nlmsg len %d", len);
+               return -1;
+       }
+
+       if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
+               return 0;
+
+       memset(rta_tb, 0, sizeof(rta_tb));
+       parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+
+       if (!rta_tb[IFA_LOCAL])
+               rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+       if (!rta_tb[IFA_ADDRESS])
+               rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+       if (filter.ifindex && filter.ifindex != ifa->ifa_index)
+               return 0;
+       if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+               return 0;
+       if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+               return 0;
+       if (filter.label) {
+               const char *label;
+               if (rta_tb[IFA_LABEL])
+                       label = RTA_DATA(rta_tb[IFA_LABEL]);
+               else
+                       label = ll_idx_n2a(ifa->ifa_index, b1);
+               if (fnmatch(filter.label, label, 0) != 0)
+                       return 0;
+       }
+       if (filter.pfx.family) {
+               if (rta_tb[IFA_LOCAL]) {
+                       inet_prefix dst;
+                       memset(&dst, 0, sizeof(dst));
+                       dst.family = ifa->ifa_family;
+                       memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
+                       if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+                               return 0;
+               }
+       }
+
+       if (filter.flushb) {
+               struct nlmsghdr *fn;
+               if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+                       if (flush_update())
+                               return -1;
+               }
+               fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+               memcpy(fn, n, n->nlmsg_len);
+               fn->nlmsg_type = RTM_DELADDR;
+               fn->nlmsg_flags = NLM_F_REQUEST;
+               fn->nlmsg_seq = ++filter.rth->seq;
+               filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+               filter.flushed++;
+               return 0;
+       }
+
+       if (n->nlmsg_type == RTM_DELADDR)
+               fprintf(fp, "Deleted ");
+
+       if (filter.oneline)
+               fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+       if (ifa->ifa_family == AF_INET)
+               fprintf(fp, "    inet ");
+       else if (ifa->ifa_family == AF_INET6)
+               fprintf(fp, "    inet6 ");
+       else
+               fprintf(fp, "    family %d ", ifa->ifa_family);
+
+       if (rta_tb[IFA_LOCAL]) {
+               fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+                                             RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+                                             RTA_DATA(rta_tb[IFA_LOCAL]),
+                                             abuf, sizeof(abuf)));
+
+               if (rta_tb[IFA_ADDRESS] == NULL ||
+                   memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
+                       fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+               } else {
+                       fprintf(fp, " peer %s/%d ",
+                               rt_addr_n2a(ifa->ifa_family,
+                                           RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+                                           RTA_DATA(rta_tb[IFA_ADDRESS]),
+                                           abuf, sizeof(abuf)),
+                               ifa->ifa_prefixlen);
+               }
+       }
+
+       if (rta_tb[IFA_BROADCAST]) {
+               fprintf(fp, "brd %s ",
+                       rt_addr_n2a(ifa->ifa_family,
+                                   RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
+                                   RTA_DATA(rta_tb[IFA_BROADCAST]),
+                                   abuf, sizeof(abuf)));
+       }
+       if (rta_tb[IFA_ANYCAST]) {
+               fprintf(fp, "any %s ",
+                       rt_addr_n2a(ifa->ifa_family,
+                                   RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
+                                   RTA_DATA(rta_tb[IFA_ANYCAST]),
+                                   abuf, sizeof(abuf)));
+       }
+       fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+       if (ifa->ifa_flags&IFA_F_SECONDARY) {
+               ifa->ifa_flags &= ~IFA_F_SECONDARY;
+               fprintf(fp, "secondary ");
+       }
+       if (ifa->ifa_flags&IFA_F_TENTATIVE) {
+               ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+               fprintf(fp, "tentative ");
+       }
+       if (ifa->ifa_flags&IFA_F_DEPRECATED) {
+               ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+               fprintf(fp, "deprecated ");
+       }
+       if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
+               fprintf(fp, "dynamic ");
+       } else
+               ifa->ifa_flags &= ~IFA_F_PERMANENT;
+       if (ifa->ifa_flags)
+               fprintf(fp, "flags %02x ", ifa->ifa_flags);
+       if (rta_tb[IFA_LABEL])
+               fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+       if (rta_tb[IFA_CACHEINFO]) {
+               struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+               char buf[128];
+               fprintf(fp, "%s", _SL_);
+               if (ci->ifa_valid == 0xFFFFFFFFU)
+                       sprintf(buf, "valid_lft forever");
+               else
+                       sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
+               if (ci->ifa_prefered == 0xFFFFFFFFU)
+                       sprintf(buf+strlen(buf), " preferred_lft forever");
+               else
+                       sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
+               fprintf(fp, "       %s", buf);
+       }
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
+
+
+struct nlmsg_list
+{
+       struct nlmsg_list *next;
+       struct nlmsghdr   h;
+};
+
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
+{
+       for ( ;ainfo ;  ainfo = ainfo->next) {
+               struct nlmsghdr *n = &ainfo->h;
+               struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+               if (n->nlmsg_type != RTM_NEWADDR)
+                       continue;
+
+               if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
+                       return -1;
+
+               if (ifa->ifa_index != ifindex ||
+                   (filter.family && filter.family != ifa->ifa_family))
+                       continue;
+
+               print_addrinfo(NULL, n, fp);
+       }
+       return 0;
+}
+
+
+static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
+       struct nlmsg_list *h;
+       struct nlmsg_list **lp;
+
+       h = malloc(n->nlmsg_len+sizeof(void*));
+       if (h == NULL)
+               return -1;
+
+       memcpy(&h->h, n, n->nlmsg_len);
+       h->next = NULL;
+
+       for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
+       *lp = h;
+
+       ll_remember_index(who, n, NULL);
+       return 0;
+}
+
+static void ipaddr_reset_filter(int _oneline)
+{
+       memset(&filter, 0, sizeof(filter));
+       filter.oneline = _oneline;
+}
+
+extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
+{
+       const char *option[] = { "to", "scope", "up", "label", "dev", 0 };
+       struct nlmsg_list *linfo = NULL;
+       struct nlmsg_list *ainfo = NULL;
+       struct nlmsg_list *l;
+       struct rtnl_handle rth;
+       char *filter_dev = NULL;
+       int no_link = 0;
+
+       ipaddr_reset_filter(oneline);
+       filter.showqueue = 1;
+
+       if (filter.family == AF_UNSPEC)
+               filter.family = preferred_family;
+
+       if (flush) {
+               if (argc <= 0) {
+                       fprintf(stderr, "Flush requires arguments.\n");
+                       return -1;
+               }
+               if (filter.family == AF_PACKET) {
+                       fprintf(stderr, "Cannot flush link addresses.\n");
+                       return -1;
+               }
+       }
+
+       while (argc > 0) {
+               const unsigned short option_num = compare_string_array(option, *argv);
+               switch (option_num) {
+                       case 0: /* to */
+                               NEXT_ARG();
+                               get_prefix(&filter.pfx, *argv, filter.family);
+                               if (filter.family == AF_UNSPEC) {
+                                       filter.family = filter.pfx.family;
+                               }
+                               break;
+                       case 1: /* scope */
+                       {
+                               int scope = 0;
+                               NEXT_ARG();
+                               filter.scopemask = -1;
+                               if (rtnl_rtscope_a2n(&scope, *argv)) {
+                                       if (strcmp(*argv, "all") != 0) {
+                                               invarg("invalid \"scope\"\n", *argv);
+                                       }
+                                       scope = RT_SCOPE_NOWHERE;
+                                       filter.scopemask = 0;
+                               }
+                               filter.scope = scope;
+                               break;
+                       }
+                       case 2: /* up */
+                               filter.up = 1;
+                               break;
+                       case 3: /* label */
+                               NEXT_ARG();
+                               filter.label = *argv;
+                               break;
+                       case 4: /* dev */
+                               NEXT_ARG();
+                       default:
+                               if (filter_dev) {
+                                       duparg2("dev", *argv);
+                               }
+                               filter_dev = *argv;
+               }
+               argv++;
+               argc--;
+       }
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
+               bb_perror_msg_and_die("Cannot send dump request");
+       }
+
+       if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
+               bb_error_msg_and_die("Dump terminated");
+       }
+
+       if (filter_dev) {
+               filter.ifindex = ll_name_to_index(filter_dev);
+               if (filter.ifindex <= 0) {
+                       bb_error_msg("Device \"%s\" does not exist.", filter_dev);
+                       return -1;
+               }
+       }
+
+       if (flush) {
+               int round = 0;
+               char flushb[4096-512];
+
+               filter.flushb = flushb;
+               filter.flushp = 0;
+               filter.flushe = sizeof(flushb);
+               filter.rth = &rth;
+
+               for (;;) {
+                       if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+                               perror("Cannot send dump request");
+                               exit(1);
+                       }
+                       filter.flushed = 0;
+                       if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
+                               fprintf(stderr, "Flush terminated\n");
+                               exit(1);
+                       }
+                       if (filter.flushed == 0) {
+#if 0
+                               if (round == 0)
+                                       fprintf(stderr, "Nothing to flush.\n");
+#endif
+                               fflush(stdout);
+                               return 0;
+                       }
+                       round++;
+                       if (flush_update() < 0)
+                               exit(1);
+               }
+       }
+
+       if (filter.family != AF_PACKET) {
+               if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+                       bb_perror_msg_and_die("Cannot send dump request");
+               }
+
+               if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
+                       bb_error_msg_and_die("Dump terminated");
+               }
+       }
+
+
+       if (filter.family && filter.family != AF_PACKET) {
+               struct nlmsg_list **lp;
+               lp=&linfo;
+
+               if (filter.oneline)
+                       no_link = 1;
+
+               while ((l=*lp)!=NULL) {
+                       int ok = 0;
+                       struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+                       struct nlmsg_list *a;
+
+                       for (a=ainfo; a; a=a->next) {
+                               struct nlmsghdr *n = &a->h;
+                               struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+                               if (ifa->ifa_index != ifi->ifi_index ||
+                                   (filter.family && filter.family != ifa->ifa_family))
+                                       continue;
+                               if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+                                       continue;
+                               if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+                                       continue;
+                               if (filter.pfx.family || filter.label) {
+                                       struct rtattr *tb[IFA_MAX+1];
+                                       memset(tb, 0, sizeof(tb));
+                                       parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
+                                       if (!tb[IFA_LOCAL])
+                                               tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+
+                                       if (filter.pfx.family && tb[IFA_LOCAL]) {
+                                               inet_prefix dst;
+                                               memset(&dst, 0, sizeof(dst));
+                                               dst.family = ifa->ifa_family;
+                                               memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+                                               if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+                                                       continue;
+                                       }
+                                       if (filter.label) {
+                                               SPRINT_BUF(b1);
+                                               const char *label;
+                                               if (tb[IFA_LABEL])
+                                                       label = RTA_DATA(tb[IFA_LABEL]);
+                                               else
+                                                       label = ll_idx_n2a(ifa->ifa_index, b1);
+                                               if (fnmatch(filter.label, label, 0) != 0)
+                                                       continue;
+                                       }
+                               }
+
+                               ok = 1;
+                               break;
+                       }
+                       if (!ok)
+                               *lp = l->next;
+                       else
+                               lp = &l->next;
+               }
+       }
+
+       for (l=linfo; l; l = l->next) {
+               if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
+                       struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+                       if (filter.family != AF_PACKET)
+                               print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
+               }
+               fflush(stdout);
+       }
+
+       exit(0);
+}
+
+static int default_scope(inet_prefix *lcl)
+{
+       if (lcl->family == AF_INET) {
+               if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
+                       return RT_SCOPE_HOST;
+       }
+       return 0;
+}
+
+static int ipaddr_modify(int cmd, int argc, char **argv)
+{
+       const char *option[] = { "peer", "remote", "broadcast", "brd",
+               "anycast", "scope", "dev", "label", "local", 0 };
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct ifaddrmsg        ifa;
+               char                    buf[256];
+       } req;
+       char  *d = NULL;
+       char  *l = NULL;
+       inet_prefix lcl;
+       inet_prefix peer;
+       int local_len = 0;
+       int peer_len = 0;
+       int brd_len = 0;
+       int any_len = 0;
+       int scoped = 0;
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = cmd;
+       req.ifa.ifa_family = preferred_family;
+
+       while (argc > 0) {
+               const unsigned short option_num = compare_string_array(option, *argv);
+               switch (option_num) {
+                       case 0: /* peer */
+                       case 1: /* remote */
+                               NEXT_ARG();
+
+                               if (peer_len) {
+                                       duparg("peer", *argv);
+                               }
+                               get_prefix(&peer, *argv, req.ifa.ifa_family);
+                               peer_len = peer.bytelen;
+                               if (req.ifa.ifa_family == AF_UNSPEC) {
+                                       req.ifa.ifa_family = peer.family;
+                               }
+                               addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
+                               req.ifa.ifa_prefixlen = peer.bitlen;
+                               break;
+                       case 2: /* broadcast */
+                       case 3: /* brd */
+                       {
+                               inet_prefix addr;
+                               NEXT_ARG();
+                               if (brd_len) {
+                                       duparg("broadcast", *argv);
+                               }
+                               if (strcmp(*argv, "+") == 0) {
+                                       brd_len = -1;
+                               }
+                               else if (strcmp(*argv, "-") == 0) {
+                                       brd_len = -2;
+                               } else {
+                                       get_addr(&addr, *argv, req.ifa.ifa_family);
+                                       if (req.ifa.ifa_family == AF_UNSPEC)
+                                               req.ifa.ifa_family = addr.family;
+                                       addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
+                                       brd_len = addr.bytelen;
+                               }
+                               break;
+                       }
+                       case 4: /* anycast */
+                       {
+                               inet_prefix addr;
+                               NEXT_ARG();
+                               if (any_len) {
+                                       duparg("anycast", *argv);
+                               }
+                               get_addr(&addr, *argv, req.ifa.ifa_family);
+                               if (req.ifa.ifa_family == AF_UNSPEC) {
+                                       req.ifa.ifa_family = addr.family;
+                               }
+                               addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
+                               any_len = addr.bytelen;
+                               break;
+                       }
+                       case 5: /* scope */
+                       {
+                               int scope = 0;
+                               NEXT_ARG();
+                               if (rtnl_rtscope_a2n(&scope, *argv)) {
+                                       invarg(*argv, "invalid scope value.");
+                               }
+                               req.ifa.ifa_scope = scope;
+                               scoped = 1;
+                               break;
+                       }
+                       case 6: /* dev */
+                               NEXT_ARG();
+                               d = *argv;
+                               break;
+                       case 7: /* label */
+                               NEXT_ARG();
+                               l = *argv;
+                               addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
+                               break;
+                       case 8: /* local */
+                               NEXT_ARG();
+                       default:
+                               if (local_len) {
+                                       duparg2("local", *argv);
+                               }
+                               get_prefix(&lcl, *argv, req.ifa.ifa_family);
+                               if (req.ifa.ifa_family == AF_UNSPEC) {
+                                       req.ifa.ifa_family = lcl.family;
+                               }
+                               addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
+                               local_len = lcl.bytelen;
+               }
+               argc--;
+               argv++;
+       }
+
+       if (d == NULL) {
+               bb_error_msg("Not enough information: \"dev\" argument is required.");
+               return -1;
+       }
+       if (l && matches(d, l) != 0) {
+               bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l);
+       }
+
+       if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
+               peer = lcl;
+               addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+       }
+       if (req.ifa.ifa_prefixlen == 0)
+               req.ifa.ifa_prefixlen = lcl.bitlen;
+
+       if (brd_len < 0 && cmd != RTM_DELADDR) {
+               inet_prefix brd;
+               int i;
+               if (req.ifa.ifa_family != AF_INET) {
+                       bb_error_msg("Broadcast can be set only for IPv4 addresses");
+                       return -1;
+               }
+               brd = peer;
+               if (brd.bitlen <= 30) {
+                       for (i=31; i>=brd.bitlen; i--) {
+                               if (brd_len == -1)
+                                       brd.data[0] |= htonl(1<<(31-i));
+                               else
+                                       brd.data[0] &= ~htonl(1<<(31-i));
+                       }
+                       addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
+                       brd_len = brd.bytelen;
+               }
+       }
+       if (!scoped && cmd != RTM_DELADDR)
+               req.ifa.ifa_scope = default_scope(&lcl);
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       ll_init_map(&rth);
+
+       if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
+               bb_error_msg("Cannot find device \"%s\"", d);
+               return -1;
+       }
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+               exit(2);
+
+       exit(0);
+}
+
+extern int do_ipaddr(int argc, char **argv)
+{
+       const char *commands[] = { "add", "delete", "list", "show", "lst", "flush", 0 };
+       unsigned short command_num = 2;
+
+       if (*argv) {
+               command_num = compare_string_array(commands, *argv);
+       }
+       switch (command_num) {
+               case 0: /* add */
+                       return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
+               case 1: /* delete */
+                       return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
+               case 2: /* list */
+               case 3: /* show */
+               case 4: /* lst */
+                       return ipaddr_list_or_flush(argc-1, argv+1, 0);
+               case 5: /* flush */
+                       return ipaddr_list_or_flush(argc-1, argv+1, 1);
+       }
+       bb_error_msg_and_die("Unknown command %s", *argv);
+}
diff --git a/busybox/networking/libiproute/iplink.c b/busybox/networking/libiproute/iplink.c
new file mode 100644 (file)
index 0000000..2550c19
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * iplink.c            "ip link".
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/version.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_packet.h>
+#include <netpacket/packet.h>
+
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#include "libbb.h"
+
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME    0x8923          /* set interface name */
+
+static int do_link;
+
+static int on_off(char *msg)
+{
+       bb_error_msg("Error: argument of \"%s\" must be \"on\" or \"off\"", msg);
+       return -1;
+}
+
+static int get_ctl_fd(void)
+{
+       int s_errno;
+       int fd;
+
+       fd = socket(PF_INET, SOCK_DGRAM, 0);
+       if (fd >= 0)
+               return fd;
+       s_errno = errno;
+       fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (fd >= 0)
+               return fd;
+       fd = socket(PF_INET6, SOCK_DGRAM, 0);
+       if (fd >= 0)
+               return fd;
+       errno = s_errno;
+       perror("Cannot create control socket");
+       return -1;
+}
+
+static int do_chflags(char *dev, __u32 flags, __u32 mask)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, dev);
+       fd = get_ctl_fd();
+       if (fd < 0)
+               return -1;
+       err = ioctl(fd, SIOCGIFFLAGS, &ifr);
+       if (err) {
+               perror("SIOCGIFFLAGS");
+               close(fd);
+               return -1;
+       }
+       if ((ifr.ifr_flags^flags)&mask) {
+               ifr.ifr_flags &= ~mask;
+               ifr.ifr_flags |= mask&flags;
+               err = ioctl(fd, SIOCSIFFLAGS, &ifr);
+               if (err)
+                       perror("SIOCSIFFLAGS");
+       }
+       close(fd);
+       return err;
+}
+
+static int do_changename(char *dev, char *newdev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, dev);
+       strcpy(ifr.ifr_newname, newdev);
+       fd = get_ctl_fd();
+       if (fd < 0)
+               return -1;
+       err = ioctl(fd, SIOCSIFNAME, &ifr);
+       if (err) {
+               perror("SIOCSIFNAME");
+               close(fd);
+               return -1;
+       }
+       close(fd);
+       return err;
+#endif
+       return 0;
+}
+
+static int set_qlen(char *dev, int qlen)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = get_ctl_fd();
+       if (s < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev);
+       ifr.ifr_qlen = qlen;
+       if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
+               perror("SIOCSIFXQLEN");
+               close(s);
+               return -1;
+       }
+       close(s);
+
+       return 0;
+}
+
+static int set_mtu(char *dev, int mtu)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = get_ctl_fd();
+       if (s < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev);
+       ifr.ifr_mtu = mtu;
+       if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
+               perror("SIOCSIFMTU");
+               close(s);
+               return -1;
+       }
+       close(s);
+
+       return 0;
+}
+
+static int get_address(char *dev, int *htype)
+{
+       struct ifreq ifr;
+       struct sockaddr_ll me;
+       int alen;
+       int s;
+
+       s = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket(PF_PACKET)");
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev);
+       if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+               perror("SIOCGIFINDEX");
+               close(s);
+               return -1;
+       }
+
+       memset(&me, 0, sizeof(me));
+       me.sll_family = AF_PACKET;
+       me.sll_ifindex = ifr.ifr_ifindex;
+       me.sll_protocol = htons(ETH_P_LOOP);
+       if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
+               perror("bind");
+               close(s);
+               return -1;
+       }
+
+       alen = sizeof(me);
+       if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
+               perror("getsockname");
+               close(s);
+               return -1;
+       }
+       close(s);
+       *htype = me.sll_hatype;
+       return me.sll_halen;
+}
+
+static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+{
+       int alen;
+
+       memset(ifr, 0, sizeof(*ifr));
+       strcpy(ifr->ifr_name, dev);
+       ifr->ifr_hwaddr.sa_family = hatype;
+       alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
+       if (alen < 0)
+               return -1;
+       if (alen != halen) {
+               bb_error_msg("Wrong address (%s) length: expected %d bytes", lla, halen);
+               return -1;
+       }
+       return 0;
+}
+
+static int set_address(struct ifreq *ifr, int brd)
+{
+       int s;
+
+       s = get_ctl_fd();
+       if (s < 0)
+               return -1;
+       if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
+               perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
+               close(s);
+               return -1;
+       }
+       close(s);
+       return 0;
+}
+
+
+static int do_set(int argc, char **argv)
+{
+       char *dev = NULL;
+       __u32 mask = 0;
+       __u32 flags = 0;
+       int qlen = -1;
+       int mtu = -1;
+       char *newaddr = NULL;
+       char *newbrd = NULL;
+       struct ifreq ifr0, ifr1;
+       char *newname = NULL;
+       int htype, halen;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "up") == 0) {
+                       mask |= IFF_UP;
+                       flags |= IFF_UP;
+               } else if (strcmp(*argv, "down") == 0) {
+                       mask |= IFF_UP;
+                       flags &= ~IFF_UP;
+               } else if (strcmp(*argv, "name") == 0) {
+                       NEXT_ARG();
+                       newname = *argv;
+               } else if (strcmp(*argv, "mtu") == 0) {
+                       NEXT_ARG();
+                       if (mtu != -1)
+                               duparg("mtu", *argv);
+                       if (get_integer(&mtu, *argv, 0))
+                               invarg("Invalid \"mtu\" value\n", *argv);
+               } else if (strcmp(*argv, "multicast") == 0) {
+                       NEXT_ARG();
+                       mask |= IFF_MULTICAST;
+                       if (strcmp(*argv, "on") == 0) {
+                               flags |= IFF_MULTICAST;
+                       } else if (strcmp(*argv, "off") == 0) {
+                               flags &= ~IFF_MULTICAST;
+                       } else
+                               return on_off("multicast");
+               } else if (strcmp(*argv, "arp") == 0) {
+                       NEXT_ARG();
+                       mask |= IFF_NOARP;
+                       if (strcmp(*argv, "on") == 0) {
+                               flags &= ~IFF_NOARP;
+                       } else if (strcmp(*argv, "off") == 0) {
+                               flags |= IFF_NOARP;
+                       } else
+                               return on_off("noarp");
+               } else {
+                        if (strcmp(*argv, "dev") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (dev)
+                               duparg2("dev", *argv);
+                       dev = *argv;
+               }
+               argc--; argv++;
+       }
+
+       if (!dev) {
+               bb_error_msg("Not enough of information: \"dev\" argument is required.");
+               exit(-1);
+       }
+
+       if (newaddr || newbrd) {
+               halen = get_address(dev, &htype);
+               if (halen < 0)
+                       return -1;
+               if (newaddr) {
+                       if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
+                               return -1;
+               }
+               if (newbrd) {
+                       if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
+                               return -1;
+               }
+       }
+
+       if (newname && strcmp(dev, newname)) {
+               if (do_changename(dev, newname) < 0)
+                       return -1;
+               dev = newname;
+       }
+       if (qlen != -1) {
+               if (set_qlen(dev, qlen) < 0)
+                       return -1;
+       }
+       if (mtu != -1) {
+               if (set_mtu(dev, mtu) < 0)
+                       return -1;
+       }
+       if (newaddr || newbrd) {
+               if (newbrd) {
+                       if (set_address(&ifr1, 1) < 0)
+                               return -1;
+               }
+               if (newaddr) {
+                       if (set_address(&ifr0, 0) < 0)
+                               return -1;
+               }
+       }
+       if (mask)
+               return do_chflags(dev, flags, mask);
+       return 0;
+}
+
+static int ipaddr_list_link(int argc, char **argv)
+{
+       preferred_family = AF_PACKET;
+       do_link = 1;
+       return ipaddr_list_or_flush(argc, argv, 0);
+}
+
+int do_iplink(int argc, char **argv)
+{
+       if (argc > 0) {
+               if (matches(*argv, "set") == 0)
+                       return do_set(argc-1, argv+1);
+               if (matches(*argv, "show") == 0 ||
+                   matches(*argv, "lst") == 0 ||
+                   matches(*argv, "list") == 0)
+                       return ipaddr_list_link(argc-1, argv+1);
+       } else
+               return ipaddr_list_link(0, NULL);
+
+       bb_error_msg("Command \"%s\" is unknown.", *argv);
+       exit(-1);
+}
diff --git a/busybox/networking/libiproute/iproute.c b/busybox/networking/libiproute/iproute.c
new file mode 100644 (file)
index 0000000..9c57140
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ * iproute.c           "ip route".
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
+ */
+
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "libbb.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+static struct
+{
+       int tb;
+       int flushed;
+       char *flushb;
+       int flushp;
+       int flushe;
+       struct rtnl_handle *rth;
+       int protocol, protocolmask;
+       int scope, scopemask;
+       int type, typemask;
+       int tos, tosmask;
+       int iif, iifmask;
+       int oif, oifmask;
+       int realm, realmmask;
+       inet_prefix rprefsrc;
+       inet_prefix rvia;
+       inet_prefix rdst;
+       inet_prefix mdst;
+       inet_prefix rsrc;
+       inet_prefix msrc;
+} filter;
+
+static int flush_update(void)
+{
+       if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+               perror("Failed to send flush request\n");
+               return -1;
+       }
+       filter.flushp = 0;
+       return 0;
+}
+
+static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = (FILE*)arg;
+       struct rtmsg *r = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       struct rtattr * tb[RTA_MAX+1];
+       char abuf[256];
+       inet_prefix dst;
+       inet_prefix src;
+       int host_len = -1;
+       SPRINT_BUF(b1);
+
+
+       if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
+               fprintf(stderr, "Not a route: %08x %08x %08x\n",
+                       n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+               return 0;
+       }
+       if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
+               return 0;
+       len -= NLMSG_LENGTH(sizeof(*r));
+       if (len < 0) {
+               bb_error_msg("wrong nlmsg len %d", len);
+               return -1;
+       }
+
+       if (r->rtm_family == AF_INET6)
+               host_len = 128;
+       else if (r->rtm_family == AF_INET)
+               host_len = 32;
+
+       if (r->rtm_family == AF_INET6) {
+               if (filter.tb) {
+                       if (filter.tb < 0) {
+                               if (!(r->rtm_flags&RTM_F_CLONED)) {
+                                       return 0;
+                               }
+                       } else {
+                               if (r->rtm_flags&RTM_F_CLONED) {
+                                       return 0;
+                               }
+                               if (filter.tb == RT_TABLE_LOCAL) {
+                                       if (r->rtm_type != RTN_LOCAL) {
+                                               return 0;
+                                       }
+                               } else if (filter.tb == RT_TABLE_MAIN) {
+                                       if (r->rtm_type == RTN_LOCAL) {
+                                               return 0;
+                                       }
+                               } else {
+                                       return 0;
+                               }
+                       }
+               }
+       } else {
+               if (filter.tb > 0 && filter.tb != r->rtm_table) {
+                       return 0;
+               }
+       }
+       if (filter.rdst.family &&
+           (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
+               return 0;
+       }
+       if (filter.mdst.family &&
+           (r->rtm_family != filter.mdst.family ||
+            (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
+               return 0;
+       }
+       if (filter.rsrc.family &&
+           (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
+               return 0;
+       }
+       if (filter.msrc.family &&
+           (r->rtm_family != filter.msrc.family ||
+            (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
+               return 0;
+       }
+
+       memset(tb, 0, sizeof(tb));
+       parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+       if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
+               return 0;
+       if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
+           inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
+               return 0;
+
+       if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
+               return 0;
+       if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
+           inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
+               return 0;
+
+       if (filter.flushb &&
+           r->rtm_family == AF_INET6 &&
+           r->rtm_dst_len == 0 &&
+           r->rtm_type == RTN_UNREACHABLE &&
+           tb[RTA_PRIORITY] &&
+           *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
+               return 0;
+
+       if (filter.flushb) {
+               struct nlmsghdr *fn;
+               if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+                       if (flush_update())
+                               return -1;
+               }
+               fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+               memcpy(fn, n, n->nlmsg_len);
+               fn->nlmsg_type = RTM_DELROUTE;
+               fn->nlmsg_flags = NLM_F_REQUEST;
+               fn->nlmsg_seq = ++filter.rth->seq;
+               filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+               filter.flushed++;
+               return 0;
+       }
+
+       if (n->nlmsg_type == RTM_DELROUTE) {
+               fprintf(fp, "Deleted ");
+       }
+       if (r->rtm_type != RTN_UNICAST && !filter.type) {
+               fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+       }
+
+       if (tb[RTA_DST]) {
+               if (r->rtm_dst_len != host_len) {
+                       fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
+                                                        RTA_PAYLOAD(tb[RTA_DST]),
+                                                        RTA_DATA(tb[RTA_DST]),
+                                                        abuf, sizeof(abuf)),
+                               r->rtm_dst_len
+                               );
+               } else {
+                       fprintf(fp, "%s ", format_host(r->rtm_family,
+                                                      RTA_PAYLOAD(tb[RTA_DST]),
+                                                      RTA_DATA(tb[RTA_DST]),
+                                                      abuf, sizeof(abuf))
+                               );
+               }
+       } else if (r->rtm_dst_len) {
+               fprintf(fp, "0/%d ", r->rtm_dst_len);
+       } else {
+               fprintf(fp, "default ");
+       }
+       if (tb[RTA_SRC]) {
+               if (r->rtm_src_len != host_len) {
+                       fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+                                                        RTA_PAYLOAD(tb[RTA_SRC]),
+                                                        RTA_DATA(tb[RTA_SRC]),
+                                                        abuf, sizeof(abuf)),
+                               r->rtm_src_len
+                               );
+               } else {
+                       fprintf(fp, "from %s ", format_host(r->rtm_family,
+                                                      RTA_PAYLOAD(tb[RTA_SRC]),
+                                                      RTA_DATA(tb[RTA_SRC]),
+                                                      abuf, sizeof(abuf))
+                               );
+               }
+       } else if (r->rtm_src_len) {
+               fprintf(fp, "from 0/%u ", r->rtm_src_len);
+       }
+       if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
+               fprintf(fp, "via %s ",
+                       format_host(r->rtm_family,
+                                   RTA_PAYLOAD(tb[RTA_GATEWAY]),
+                                   RTA_DATA(tb[RTA_GATEWAY]),
+                                   abuf, sizeof(abuf)));
+       }
+       if (tb[RTA_OIF] && filter.oifmask != -1) {
+               fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
+       }
+
+       if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
+               /* Do not use format_host(). It is our local addr
+                  and symbolic name will not be useful.
+                */
+               fprintf(fp, " src %s ",
+                       rt_addr_n2a(r->rtm_family,
+                                   RTA_PAYLOAD(tb[RTA_PREFSRC]),
+                                   RTA_DATA(tb[RTA_PREFSRC]),
+                                   abuf, sizeof(abuf)));
+       }
+       if (tb[RTA_PRIORITY]) {
+               fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
+       }
+       if (r->rtm_family == AF_INET6) {
+               struct rta_cacheinfo *ci = NULL;
+               if (tb[RTA_CACHEINFO]) {
+                       ci = RTA_DATA(tb[RTA_CACHEINFO]);
+               }
+               if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+                       static int hz;
+                       if (!hz) {
+                               hz = get_hz();
+                       }
+                       if (r->rtm_flags & RTM_F_CLONED) {
+                               fprintf(fp, "%s    cache ", _SL_);
+                       }
+                       if (ci->rta_expires) {
+                               fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+                       }
+                       if (ci->rta_error != 0) {
+                               fprintf(fp, " error %d", ci->rta_error);
+                       }
+               } else if (ci) {
+                       if (ci->rta_error != 0)
+                               fprintf(fp, " error %d", ci->rta_error);
+               }
+       }
+       if (tb[RTA_IIF] && filter.iifmask != -1) {
+               fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+       }
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
+
+static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct rtmsg            r;
+               char                    buf[1024];
+       } req;
+       char  mxbuf[256];
+       struct rtattr * mxrta = (void*)mxbuf;
+       unsigned mxlock = 0;
+       char  *d = NULL;
+       int gw_ok = 0;
+       int dst_ok = 0;
+       int proto_ok = 0;
+       int type_ok = 0;
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+       req.n.nlmsg_type = cmd;
+       req.r.rtm_family = preferred_family;
+       req.r.rtm_table = RT_TABLE_MAIN;
+       req.r.rtm_scope = RT_SCOPE_NOWHERE;
+
+       if (cmd != RTM_DELROUTE) {
+               req.r.rtm_protocol = RTPROT_BOOT;
+               req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+               req.r.rtm_type = RTN_UNICAST;
+       }
+
+       mxrta->rta_type = RTA_METRICS;
+       mxrta->rta_len = RTA_LENGTH(0);
+
+       while (argc > 0) {
+               if (strcmp(*argv, "src") == 0) {
+                       inet_prefix addr;
+                       NEXT_ARG();
+                       get_addr(&addr, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC) {
+                               req.r.rtm_family = addr.family;
+                       }
+                       addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
+               } else if (strcmp(*argv, "via") == 0) {
+                       inet_prefix addr;
+                       gw_ok = 1;
+                       NEXT_ARG();
+                       get_addr(&addr, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC) {
+                               req.r.rtm_family = addr.family;
+                       }
+                       addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
+               } else if (strcmp(*argv, "mtu") == 0) {
+                       unsigned mtu;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "lock") == 0) {
+                               mxlock |= (1<<RTAX_MTU);
+                               NEXT_ARG();
+                       }
+                       if (get_unsigned(&mtu, *argv, 0)) {
+                               invarg("\"mtu\" value is invalid\n", *argv);
+                       }
+                       rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+               } else if (matches(*argv, "protocol") == 0) {
+                       int prot;
+                       NEXT_ARG();
+                       if (rtnl_rtprot_a2n(&prot, *argv))
+                               invarg("\"protocol\" value is invalid\n", *argv);
+                       req.r.rtm_protocol = prot;
+                       proto_ok =1;
+               } else if (strcmp(*argv, "dev") == 0 ||
+                          strcmp(*argv, "oif") == 0) {
+                       NEXT_ARG();
+                       d = *argv;
+               } else {
+                       int type;
+                       inet_prefix dst;
+
+                       if (strcmp(*argv, "to") == 0) {
+                               NEXT_ARG();
+                       }
+                       if ((**argv < '0' || **argv > '9') &&
+                           rtnl_rtntype_a2n(&type, *argv) == 0) {
+                               NEXT_ARG();
+                               req.r.rtm_type = type;
+                               type_ok = 1;
+                       }
+
+                       if (dst_ok) {
+                               duparg2("to", *argv);
+                       }
+                       get_prefix(&dst, *argv, req.r.rtm_family);
+                       if (req.r.rtm_family == AF_UNSPEC) {
+                               req.r.rtm_family = dst.family;
+                       }
+                       req.r.rtm_dst_len = dst.bitlen;
+                       dst_ok = 1;
+                       if (dst.bytelen) {
+                               addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+                       }
+               }
+               argc--; argv++;
+       }
+
+       if (rtnl_open(&rth, 0) < 0) {
+               exit(1);
+       }
+
+       if (d)  {
+               int idx;
+
+               ll_init_map(&rth);
+
+               if (d) {
+                       if ((idx = ll_name_to_index(d)) == 0) {
+                               bb_error_msg("Cannot find device \"%s\"", d);
+                               return -1;
+                       }
+                       addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+               }
+       }
+
+       if (mxrta->rta_len > RTA_LENGTH(0)) {
+               if (mxlock) {
+                       rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
+               }
+               addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
+       }
+
+       if (req.r.rtm_family == AF_UNSPEC) {
+               req.r.rtm_family = AF_INET;
+       }
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+               exit(2);
+       }
+
+       return 0;
+}
+
+static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct rtmsg rtm;
+       } req;
+       struct sockaddr_nl nladdr;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       memset(&req, 0, sizeof(req));
+       nladdr.nl_family = AF_NETLINK;
+
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = RTM_GETROUTE;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+       req.rtm.rtm_family = family;
+       req.rtm.rtm_flags |= RTM_F_CLONED;
+
+       return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+static int iproute_flush_cache(void)
+{
+#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
+
+       int len;
+       int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
+       char *buffer = "-1";
+
+       if (flush_fd < 0) {
+               fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
+               return -1;
+       }
+
+       len = strlen (buffer);
+
+       if ((write (flush_fd, (void *)buffer, len)) < len) {
+               fprintf (stderr, "Cannot flush routing cache\n");
+               return -1;
+       }
+       close(flush_fd);
+       return 0;
+}
+
+static void iproute_reset_filter(void)
+{
+       memset(&filter, 0, sizeof(filter));
+       filter.mdst.bitlen = -1;
+       filter.msrc.bitlen = -1;
+}
+
+static int iproute_list_or_flush(int argc, char **argv, int flush)
+{
+       int do_ipv6 = preferred_family;
+       struct rtnl_handle rth;
+       char *id = NULL;
+       char *od = NULL;
+
+       iproute_reset_filter();
+       filter.tb = RT_TABLE_MAIN;
+
+       if (flush && argc <= 0) {
+               fprintf(stderr, "\"ip route flush\" requires arguments.\n");
+               return -1;
+       }
+
+       while (argc > 0) {
+               if (matches(*argv, "protocol") == 0) {
+                       int prot = 0;
+                       NEXT_ARG();
+                       filter.protocolmask = -1;
+                       if (rtnl_rtprot_a2n(&prot, *argv)) {
+                               if (strcmp(*argv, "all") != 0) {
+                                       invarg("invalid \"protocol\"\n", *argv);
+                               }
+                               prot = 0;
+                               filter.protocolmask = 0;
+                       }
+                       filter.protocol = prot;
+               } else if (strcmp(*argv, "dev") == 0 ||
+                          strcmp(*argv, "oif") == 0) {
+                       NEXT_ARG();
+                       od = *argv;
+               } else if (strcmp(*argv, "iif") == 0) {
+                       NEXT_ARG();
+                       id = *argv;
+               } else if (matches(*argv, "from") == 0) {
+                       NEXT_ARG();
+                       if (matches(*argv, "root") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.rsrc, *argv, do_ipv6);
+                       } else if (matches(*argv, "match") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.msrc, *argv, do_ipv6);
+                       } else {
+                               if (matches(*argv, "exact") == 0) {
+                                       NEXT_ARG();
+                               }
+                               get_prefix(&filter.msrc, *argv, do_ipv6);
+                               filter.rsrc = filter.msrc;
+                       }
+               } else {
+                       if (matches(*argv, "to") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (matches(*argv, "root") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.rdst, *argv, do_ipv6);
+                       } else if (matches(*argv, "match") == 0) {
+                               NEXT_ARG();
+                               get_prefix(&filter.mdst, *argv, do_ipv6);
+                       } else {
+                               if (matches(*argv, "exact") == 0) {
+                                       NEXT_ARG();
+                               }
+                               get_prefix(&filter.mdst, *argv, do_ipv6);
+                               filter.rdst = filter.mdst;
+                       }
+               }
+               argc--; argv++;
+       }
+
+       if (do_ipv6 == AF_UNSPEC && filter.tb) {
+               do_ipv6 = AF_INET;
+       }
+
+       if (rtnl_open(&rth, 0) < 0) {
+               exit(1);
+       }
+
+       ll_init_map(&rth);
+
+       if (id || od)  {
+               int idx;
+
+               if (id) {
+                       if ((idx = ll_name_to_index(id)) == 0) {
+                               bb_error_msg("Cannot find device \"%s\"", id);
+                               return -1;
+                       }
+                       filter.iif = idx;
+                       filter.iifmask = -1;
+               }
+               if (od) {
+                       if ((idx = ll_name_to_index(od)) == 0) {
+                               bb_error_msg("Cannot find device \"%s\"", od);
+                       }
+                       filter.oif = idx;
+                       filter.oifmask = -1;
+               }
+       }
+
+       if (flush) {
+               int round = 0;
+               char flushb[4096-512];
+
+               if (filter.tb == -1) {
+                       if (do_ipv6 != AF_INET6)
+                               iproute_flush_cache();
+                       if (do_ipv6 == AF_INET)
+                               return 0;
+               }
+
+               filter.flushb = flushb;
+               filter.flushp = 0;
+               filter.flushe = sizeof(flushb);
+               filter.rth = &rth;
+
+               for (;;) {
+                       if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+                               perror("Cannot send dump request");
+                               return -1;
+                       }
+                       filter.flushed = 0;
+                       if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+                               bb_error_msg("Flush terminated\n");
+                               return -1;
+                       }
+                       if (filter.flushed == 0) {
+                               if (round == 0) {
+                                       if (filter.tb != -1 || do_ipv6 == AF_INET6)
+                                               fprintf(stderr, "Nothing to flush.\n");
+                               }
+                               fflush(stdout);
+                               return 0;
+                       }
+                       round++;
+                       if (flush_update() < 0)
+                               exit(1);
+               }
+       }
+
+       if (filter.tb != -1) {
+               if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+                       bb_perror_msg_and_die("Cannot send dump request");
+               }
+       } else {
+               if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+                       bb_perror_msg_and_die("Cannot send dump request");
+               }
+       }
+
+       if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+               bb_error_msg_and_die("Dump terminated");
+       }
+
+       exit(0);
+}
+
+
+static int iproute_get(int argc, char **argv)
+{
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct rtmsg            r;
+               char                    buf[1024];
+       } req;
+       char  *idev = NULL;
+       char  *odev = NULL;
+       int connected = 0;
+       int from_ok = 0;
+       const char *options[] = { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
+
+       memset(&req, 0, sizeof(req));
+
+       iproute_reset_filter();
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = RTM_GETROUTE;
+       req.r.rtm_family = preferred_family;
+       req.r.rtm_table = 0;
+       req.r.rtm_protocol = 0;
+       req.r.rtm_scope = 0;
+       req.r.rtm_type = 0;
+       req.r.rtm_src_len = 0;
+       req.r.rtm_dst_len = 0;
+       req.r.rtm_tos = 0;
+
+       while (argc > 0) {
+               switch (compare_string_array(options, *argv)) {
+                       case 0: /* from */
+                       {
+                               inet_prefix addr;
+                               NEXT_ARG();
+                               from_ok = 1;
+                               get_prefix(&addr, *argv, req.r.rtm_family);
+                               if (req.r.rtm_family == AF_UNSPEC) {
+                                       req.r.rtm_family = addr.family;
+                               }
+                               if (addr.bytelen) {
+                                       addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
+                               }
+                               req.r.rtm_src_len = addr.bitlen;
+                               break;
+                       }
+                       case 1: /* iif */
+                               NEXT_ARG();
+                               idev = *argv;
+                               break;
+                       case 2: /* oif */
+                       case 3: /* dev */
+                               NEXT_ARG();
+                               odev = *argv;
+                               break;
+                       case 4: /* notify */
+                               req.r.rtm_flags |= RTM_F_NOTIFY;
+                               break;
+                       case 5: /* connected */
+                               connected = 1;
+                               break;
+                       case 6: /* to */
+                               NEXT_ARG();
+                       default:
+                       {
+                               inet_prefix addr;
+                               get_prefix(&addr, *argv, req.r.rtm_family);
+                               if (req.r.rtm_family == AF_UNSPEC) {
+                                       req.r.rtm_family = addr.family;
+                               }
+                               if (addr.bytelen) {
+                                       addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
+                               }
+                               req.r.rtm_dst_len = addr.bitlen;
+                       }
+                       argc--; argv++;
+               }
+       }
+
+       if (req.r.rtm_dst_len == 0) {
+               bb_error_msg_and_die("need at least destination address");
+       }
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       ll_init_map(&rth);
+
+       if (idev || odev)  {
+               int idx;
+
+               if (idev) {
+                       if ((idx = ll_name_to_index(idev)) == 0) {
+                               bb_error_msg("Cannot find device \"%s\"", idev);
+                               return -1;
+                       }
+                       addattr32(&req.n, sizeof(req), RTA_IIF, idx);
+               }
+               if (odev) {
+                       if ((idx = ll_name_to_index(odev)) == 0) {
+                               bb_error_msg("Cannot find device \"%s\"", odev);
+                               return -1;
+                       }
+                       addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+               }
+       }
+
+       if (req.r.rtm_family == AF_UNSPEC) {
+               req.r.rtm_family = AF_INET;
+       }
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+               exit(2);
+       }
+
+       if (connected && !from_ok) {
+               struct rtmsg *r = NLMSG_DATA(&req.n);
+               int len = req.n.nlmsg_len;
+               struct rtattr * tb[RTA_MAX+1];
+
+               if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+                       bb_error_msg_and_die("An error :-)");
+               }
+
+               if (req.n.nlmsg_type != RTM_NEWROUTE) {
+                       bb_error_msg("Not a route?");
+                       return -1;
+               }
+               len -= NLMSG_LENGTH(sizeof(*r));
+               if (len < 0) {
+                       bb_error_msg("Wrong len %d", len);
+                       return -1;
+               }
+
+               memset(tb, 0, sizeof(tb));
+               parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+               if (tb[RTA_PREFSRC]) {
+                       tb[RTA_PREFSRC]->rta_type = RTA_SRC;
+                       r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
+               } else if (!tb[RTA_SRC]) {
+                       bb_error_msg("Failed to connect the route");
+                       return -1;
+               }
+               if (!odev && tb[RTA_OIF]) {
+                       tb[RTA_OIF]->rta_type = 0;
+               }
+               if (tb[RTA_GATEWAY]) {
+                       tb[RTA_GATEWAY]->rta_type = 0;
+               }
+               if (!idev && tb[RTA_IIF]) {
+                       tb[RTA_IIF]->rta_type = 0;
+               }
+               req.n.nlmsg_flags = NLM_F_REQUEST;
+               req.n.nlmsg_type = RTM_GETROUTE;
+
+               if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+                       exit(2);
+               }
+       }
+
+       if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+               bb_error_msg_and_die("An error :-)");
+       }
+
+       exit(0);
+}
+
+int do_iproute(int argc, char **argv)
+{
+       const char *ip_route_commands[] = { "add", "append", "change", "chg",
+               "delete", "del", "get", "list", "show", "prepend", "replace", "test", "flush", 0 };
+       unsigned short command_num = 7;
+       unsigned int flags = 0;
+       int cmd = RTM_NEWROUTE;
+
+       if (*argv) {
+               command_num = compare_string_array(ip_route_commands, *argv);
+       }
+       switch(command_num) {
+               case 0: /* add*/
+                       flags = NLM_F_CREATE|NLM_F_EXCL;
+                       break;
+               case 1: /* append */
+                       flags = NLM_F_CREATE|NLM_F_APPEND;
+                       break;
+               case 2: /* change */
+               case 3: /* chg */
+                       flags = NLM_F_REPLACE;
+                       break;
+               case 4: /* delete */
+               case 5: /* del */
+                       cmd = RTM_DELROUTE;
+                       break;
+               case 6: /* get */
+                       return iproute_get(argc-1, argv+1);
+               case 7: /* list */
+               case 8: /* show */
+                       return iproute_list_or_flush(argc-1, argv+1, 0);
+               case 9: /* prepend */
+                       flags = NLM_F_CREATE;
+               case 10: /* replace */
+                       flags = NLM_F_CREATE|NLM_F_REPLACE;
+               case 11: /* test */
+                       flags = NLM_F_EXCL;
+               case 12: /* flush */
+                       return iproute_list_or_flush(argc-1, argv+1, 1);
+               default:
+                       bb_error_msg_and_die("Unknown command %s", *argv);
+       }
+
+       return iproute_modify(cmd, flags, argc-1, argv+1);
+}
diff --git a/busybox/networking/libiproute/iptunnel.c b/busybox/networking/libiproute/iptunnel.c
new file mode 100644 (file)
index 0000000..f8713e0
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * iptunnel.c         "ip tunnel"
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980930:        do not allow key for ipip/sit
+ * Phil Karn <karn@ka9q.ampr.org>      990408: "pmtudisc" flag
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <asm/types.h>
+#define __constant_htons htons
+#include <linux/if_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "libbb.h"
+
+static int do_ioctl_get_ifindex(char *dev)
+{
+       struct ifreq ifr;
+       int fd;
+
+       strcpy(ifr.ifr_name, dev);
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
+               bb_perror_msg("ioctl");
+               return 0;
+       }
+       close(fd);
+       return ifr.ifr_ifindex;
+}
+
+static int do_ioctl_get_iftype(char *dev)
+{
+       struct ifreq ifr;
+       int fd;
+
+       strcpy(ifr.ifr_name, dev);
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (ioctl(fd, SIOCGIFHWADDR, &ifr)) {
+               bb_perror_msg("ioctl");
+               return -1;
+       }
+       close(fd);
+       return ifr.ifr_addr.sa_family;
+}
+
+
+static char *do_ioctl_get_ifname(int idx)
+{
+       static struct ifreq ifr;
+       int fd;
+
+       ifr.ifr_ifindex = idx;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (ioctl(fd, SIOCGIFNAME, &ifr)) {
+               bb_perror_msg("ioctl");
+               return NULL;
+       }
+       close(fd);
+       return ifr.ifr_name;
+}
+
+
+
+static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       strcpy(ifr.ifr_name, basedev);
+       ifr.ifr_ifru.ifru_data = (void*)p;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCGETTUNNEL, &ifr);
+       if (err) {
+               bb_perror_msg("ioctl");
+       }
+       close(fd);
+       return err;
+}
+
+static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       if (cmd == SIOCCHGTUNNEL && p->name[0]) {
+               strcpy(ifr.ifr_name, p->name);
+       } else {
+               strcpy(ifr.ifr_name, basedev);
+       }
+       ifr.ifr_ifru.ifru_data = (void*)p;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, cmd, &ifr);
+       if (err) {
+               bb_perror_msg("ioctl");
+       }
+       close(fd);
+       return err;
+}
+
+static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p)
+{
+       struct ifreq ifr;
+       int fd;
+       int err;
+
+       if (p->name[0]) {
+               strcpy(ifr.ifr_name, p->name);
+       } else {
+               strcpy(ifr.ifr_name, basedev);
+       }
+       ifr.ifr_ifru.ifru_data = (void*)p;
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       err = ioctl(fd, SIOCDELTUNNEL, &ifr);
+       if (err) {
+               bb_perror_msg("ioctl");
+       }
+       close(fd);
+       return err;
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
+{
+       int count = 0;
+       char medium[IFNAMSIZ];
+       memset(p, 0, sizeof(*p));
+       memset(&medium, 0, sizeof(medium));
+
+       p->iph.version = 4;
+       p->iph.ihl = 5;
+#ifndef IP_DF
+#define IP_DF          0x4000          /* Flag: "Don't Fragment"       */
+#endif
+       p->iph.frag_off = htons(IP_DF);
+
+       while (argc > 0) {
+               if (strcmp(*argv, "mode") == 0) {
+                       NEXT_ARG();
+                       if (strcmp(*argv, "ipip") == 0 ||
+                           strcmp(*argv, "ip/ip") == 0) {
+                               if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
+                                       bb_error_msg("You managed to ask for more than one tunnel mode.");
+                                       exit(-1);
+                               }
+                               p->iph.protocol = IPPROTO_IPIP;
+                       } else if (strcmp(*argv, "gre") == 0 ||
+                                  strcmp(*argv, "gre/ip") == 0) {
+                               if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+                                       bb_error_msg("You managed to ask for more than one tunnel mode.");
+                                       exit(-1);
+                               }
+                               p->iph.protocol = IPPROTO_GRE;
+                       } else if (strcmp(*argv, "sit") == 0 ||
+                                  strcmp(*argv, "ipv6/ip") == 0) {
+                               if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+                                       bb_error_msg("You managed to ask for more than one tunnel mode.");
+                                       exit(-1);
+                               }
+                               p->iph.protocol = IPPROTO_IPV6;
+                       } else {
+                               bb_error_msg("Cannot guess tunnel mode.");
+                               exit(-1);
+                       }
+               } else if (strcmp(*argv, "key") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       p->i_flags |= GRE_KEY;
+                       p->o_flags |= GRE_KEY;
+                       if (strchr(*argv, '.'))
+                               p->i_key = p->o_key = get_addr32(*argv);
+                       else {
+                               if (get_unsigned(&uval, *argv, 0)<0) {
+                                       bb_error_msg("invalid value of \"key\"");
+                                       exit(-1);
+                               }
+                               p->i_key = p->o_key = htonl(uval);
+                       }
+               } else if (strcmp(*argv, "ikey") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       p->i_flags |= GRE_KEY;
+                       if (strchr(*argv, '.'))
+                               p->o_key = get_addr32(*argv);
+                       else {
+                               if (get_unsigned(&uval, *argv, 0)<0) {
+                                       bb_error_msg("invalid value of \"ikey\"");
+                                       exit(-1);
+                               }
+                               p->i_key = htonl(uval);
+                       }
+               } else if (strcmp(*argv, "okey") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       p->o_flags |= GRE_KEY;
+                       if (strchr(*argv, '.'))
+                               p->o_key = get_addr32(*argv);
+                       else {
+                               if (get_unsigned(&uval, *argv, 0)<0) {
+                                       bb_error_msg("invalid value of \"okey\"");
+                                       exit(-1);
+                               }
+                               p->o_key = htonl(uval);
+                       }
+               } else if (strcmp(*argv, "seq") == 0) {
+                       p->i_flags |= GRE_SEQ;
+                       p->o_flags |= GRE_SEQ;
+               } else if (strcmp(*argv, "iseq") == 0) {
+                       p->i_flags |= GRE_SEQ;
+               } else if (strcmp(*argv, "oseq") == 0) {
+                       p->o_flags |= GRE_SEQ;
+               } else if (strcmp(*argv, "csum") == 0) {
+                       p->i_flags |= GRE_CSUM;
+                       p->o_flags |= GRE_CSUM;
+               } else if (strcmp(*argv, "icsum") == 0) {
+                       p->i_flags |= GRE_CSUM;
+               } else if (strcmp(*argv, "ocsum") == 0) {
+                       p->o_flags |= GRE_CSUM;
+               } else if (strcmp(*argv, "nopmtudisc") == 0) {
+                       p->iph.frag_off = 0;
+               } else if (strcmp(*argv, "pmtudisc") == 0) {
+                       p->iph.frag_off = htons(IP_DF);
+               } else if (strcmp(*argv, "remote") == 0) {
+                       NEXT_ARG();
+                       if (strcmp(*argv, "any"))
+                               p->iph.daddr = get_addr32(*argv);
+               } else if (strcmp(*argv, "local") == 0) {
+                       NEXT_ARG();
+                       if (strcmp(*argv, "any"))
+                               p->iph.saddr = get_addr32(*argv);
+               } else if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       strncpy(medium, *argv, IFNAMSIZ-1);
+               } else if (strcmp(*argv, "ttl") == 0) {
+                       unsigned uval;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "inherit") != 0) {
+                               if (get_unsigned(&uval, *argv, 0))
+                                       invarg("invalid TTL\n", *argv);
+                               if (uval > 255)
+                                       invarg("TTL must be <=255\n", *argv);
+                               p->iph.ttl = uval;
+                       }
+               } else if (strcmp(*argv, "tos") == 0 ||
+                          matches(*argv, "dsfield") == 0) {
+                       __u32 uval;
+                       NEXT_ARG();
+                       if (strcmp(*argv, "inherit") != 0) {
+                               if (rtnl_dsfield_a2n(&uval, *argv))
+                                       invarg("bad TOS value", *argv);
+                               p->iph.tos = uval;
+                       } else
+                               p->iph.tos = 1;
+               } else {
+                       if (strcmp(*argv, "name") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (p->name[0])
+                               duparg2("name", *argv);
+                       strncpy(p->name, *argv, IFNAMSIZ);
+                       if (cmd == SIOCCHGTUNNEL && count == 0) {
+                               struct ip_tunnel_parm old_p;
+                               memset(&old_p, 0, sizeof(old_p));
+                               if (do_get_ioctl(*argv, &old_p))
+                                       return -1;
+                               *p = old_p;
+                       }
+               }
+               count++;
+               argc--; argv++;
+       }
+
+
+       if (p->iph.protocol == 0) {
+               if (memcmp(p->name, "gre", 3) == 0)
+                       p->iph.protocol = IPPROTO_GRE;
+               else if (memcmp(p->name, "ipip", 4) == 0)
+                       p->iph.protocol = IPPROTO_IPIP;
+               else if (memcmp(p->name, "sit", 3) == 0)
+                       p->iph.protocol = IPPROTO_IPV6;
+       }
+
+       if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
+               if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
+                       bb_error_msg("Keys are not allowed with ipip and sit.");
+                       return -1;
+               }
+       }
+
+       if (medium[0]) {
+               p->link = do_ioctl_get_ifindex(medium);
+               if (p->link == 0)
+                       return -1;
+       }
+
+       if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+               p->i_key = p->iph.daddr;
+               p->i_flags |= GRE_KEY;
+       }
+       if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+               p->o_key = p->iph.daddr;
+               p->o_flags |= GRE_KEY;
+       }
+       if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
+               bb_error_msg("Broadcast tunnel requires a source address.");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int do_add(int cmd, int argc, char **argv)
+{
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, cmd, &p) < 0)
+               return -1;
+
+       if (p.iph.ttl && p.iph.frag_off == 0) {
+               bb_error_msg("ttl != 0 and noptmudisc are incompatible");
+               return -1;
+       }
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:
+               return do_add_ioctl(cmd, "tunl0", &p);
+       case IPPROTO_GRE:
+               return do_add_ioctl(cmd, "gre0", &p);
+       case IPPROTO_IPV6:
+               return do_add_ioctl(cmd, "sit0", &p);
+       default:
+               bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)");
+               return -1;
+       }
+       return -1;
+}
+
+int do_del(int argc, char **argv)
+{
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
+               return -1;
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:
+               return do_del_ioctl("tunl0", &p);
+       case IPPROTO_GRE:
+               return do_del_ioctl("gre0", &p);
+       case IPPROTO_IPV6:
+               return do_del_ioctl("sit0", &p);
+       default:
+               return do_del_ioctl(p.name, &p);
+       }
+       return -1;
+}
+
+void print_tunnel(struct ip_tunnel_parm *p)
+{
+       char s1[256];
+       char s2[256];
+       char s3[64];
+       char s4[64];
+
+       format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
+       format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
+       inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+       inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+
+       printf("%s: %s/ip  remote %s  local %s ",
+              p->name,
+              p->iph.protocol == IPPROTO_IPIP ? "ip" :
+              (p->iph.protocol == IPPROTO_GRE ? "gre" :
+               (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
+              p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+       if (p->link) {
+               char *n = do_ioctl_get_ifname(p->link);
+               if (n)
+                       printf(" dev %s ", n);
+       }
+       if (p->iph.ttl)
+               printf(" ttl %d ", p->iph.ttl);
+       else
+               printf(" ttl inherit ");
+       if (p->iph.tos) {
+               SPRINT_BUF(b1);
+               printf(" tos");
+               if (p->iph.tos&1)
+                       printf(" inherit");
+               if (p->iph.tos&~1)
+                       printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
+                              rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
+       }
+       if (!(p->iph.frag_off&htons(IP_DF)))
+               printf(" nopmtudisc");
+
+       if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
+               printf(" key %s", s3);
+       else if ((p->i_flags|p->o_flags)&GRE_KEY) {
+               if (p->i_flags&GRE_KEY)
+                       printf(" ikey %s ", s3);
+               if (p->o_flags&GRE_KEY)
+                       printf(" okey %s ", s4);
+       }
+
+       if (p->i_flags&GRE_SEQ)
+               printf("%s  Drop packets out of sequence.\n", _SL_);
+       if (p->i_flags&GRE_CSUM)
+               printf("%s  Checksum in received packet is required.", _SL_);
+       if (p->o_flags&GRE_SEQ)
+               printf("%s  Sequence packets on output.", _SL_);
+       if (p->o_flags&GRE_CSUM)
+               printf("%s  Checksum output packets.", _SL_);
+}
+
+static int do_tunnels_list(struct ip_tunnel_parm *p)
+{
+       char name[IFNAMSIZ];
+       unsigned long  rx_bytes, rx_packets, rx_errs, rx_drops,
+       rx_fifo, rx_frame,
+       tx_bytes, tx_packets, tx_errs, tx_drops,
+       tx_fifo, tx_colls, tx_carrier, rx_multi;
+       int type;
+       struct ip_tunnel_parm p1;
+
+       char buf[512];
+       FILE *fp = fopen("/proc/net/dev", "r");
+       if (fp == NULL) {
+               perror("fopen");
+               return -1;
+       }
+
+       fgets(buf, sizeof(buf), fp);
+       fgets(buf, sizeof(buf), fp);
+
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               char *ptr;
+               buf[sizeof(buf) - 1] = 0;
+               if ((ptr = strchr(buf, ':')) == NULL ||
+                   (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+                       bb_error_msg("Wrong format of /proc/net/dev. Sorry.");
+                       return -1;
+               }
+               if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
+                          &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+                          &rx_fifo, &rx_frame, &rx_multi,
+                          &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+                          &tx_fifo, &tx_colls, &tx_carrier) != 14)
+                       continue;
+               if (p->name[0] && strcmp(p->name, name))
+                       continue;
+               type = do_ioctl_get_iftype(name);
+               if (type == -1) {
+                       bb_error_msg("Failed to get type of [%s]", name);
+                       continue;
+               }
+               if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+                       continue;
+               memset(&p1, 0, sizeof(p1));
+               if (do_get_ioctl(name, &p1))
+                       continue;
+               if ((p->link && p1.link != p->link) ||
+                   (p->name[0] && strcmp(p1.name, p->name)) ||
+                   (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+                   (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+                   (p->i_key && p1.i_key != p->i_key))
+                       continue;
+               print_tunnel(&p1);
+               printf("\n");
+       }
+       return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+       int err;
+       struct ip_tunnel_parm p;
+
+       if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
+               return -1;
+
+       switch (p.iph.protocol) {
+       case IPPROTO_IPIP:
+               err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+               break;
+       case IPPROTO_GRE:
+               err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+               break;
+       case IPPROTO_IPV6:
+               err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
+               break;
+       default:
+               do_tunnels_list(&p);
+               return 0;
+       }
+       if (err)
+               return -1;
+
+       print_tunnel(&p);
+       printf("\n");
+       return 0;
+}
+
+int do_iptunnel(int argc, char **argv)
+{
+       if (argc > 0) {
+               if (matches(*argv, "add") == 0)
+                       return do_add(SIOCADDTUNNEL, argc-1, argv+1);
+               if (matches(*argv, "change") == 0)
+                       return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
+               if (matches(*argv, "del") == 0)
+                       return do_del(argc-1, argv+1);
+               if (matches(*argv, "show") == 0 ||
+                   matches(*argv, "lst") == 0 ||
+                   matches(*argv, "list") == 0)
+                       return do_show(argc-1, argv+1);
+       } else
+               return do_show(0, NULL);
+
+       bb_error_msg("Command \"%s\" is unknown.", *argv);
+       exit(-1);
+}
diff --git a/busybox/networking/libiproute/libnetlink.c b/busybox/networking/libiproute/libnetlink.c
new file mode 100644 (file)
index 0000000..5545be8
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * libnetlink.c        RTnetlink service routines.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+#include "libbb.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+       close(rth->fd);
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+       int addr_len;
+
+       memset(rth, 0, sizeof(rth));
+
+       rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       if (rth->fd < 0) {
+               bb_perror_msg("Cannot open netlink socket");
+               return -1;
+       }
+
+       memset(&rth->local, 0, sizeof(rth->local));
+       rth->local.nl_family = AF_NETLINK;
+       rth->local.nl_groups = subscriptions;
+
+       if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+               bb_perror_msg("Cannot bind netlink socket");
+               return -1;
+       }
+       addr_len = sizeof(rth->local);
+       if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+               bb_perror_msg("Cannot getsockname");
+               return -1;
+       }
+       if (addr_len != sizeof(rth->local)) {
+               bb_error_msg("Wrong address length %d", addr_len);
+               return -1;
+       }
+       if (rth->local.nl_family != AF_NETLINK) {
+               bb_error_msg("Wrong address family %d", rth->local.nl_family);
+               return -1;
+       }
+       rth->seq = time(NULL);
+       return 0;
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct rtgenmsg g;
+       } req;
+       struct sockaddr_nl nladdr;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       req.nlh.nlmsg_len = sizeof(req);
+       req.nlh.nlmsg_type = type;
+       req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       req.nlh.nlmsg_pid = 0;
+       req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+       req.g.rtgen_family = family;
+
+       return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
+{
+       struct sockaddr_nl nladdr;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+       struct nlmsghdr nlh;
+       struct sockaddr_nl nladdr;
+       struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
+       struct msghdr msg = {
+               (void*)&nladdr, sizeof(nladdr),
+               iov,    2,
+               NULL,   0,
+               0
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       nlh.nlmsg_len = NLMSG_LENGTH(len);
+       nlh.nlmsg_type = type;
+       nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+       nlh.nlmsg_pid = 0;
+       nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+       return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+                    int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+                    void *arg1,
+                    int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                    void *arg2)
+{
+       char    buf[8192];
+       struct sockaddr_nl nladdr;
+       struct iovec iov = { buf, sizeof(buf) };
+
+       while (1) {
+               int status;
+               struct nlmsghdr *h;
+
+               struct msghdr msg = {
+                       (void*)&nladdr, sizeof(nladdr),
+                       &iov,   1,
+                       NULL,   0,
+                       0
+               };
+
+               status = recvmsg(rth->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       bb_error_msg("EOF on netlink");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+               }
+
+               h = (struct nlmsghdr*)buf;
+               while (NLMSG_OK(h, status)) {
+                       int err;
+
+                       if (nladdr.nl_pid != 0 ||
+                           h->nlmsg_pid != rth->local.nl_pid ||
+                           h->nlmsg_seq != rth->dump) {
+                               if (junk) {
+                                       err = junk(&nladdr, h, arg2);
+                                       if (err < 0) {
+                                               return err;
+                                       }
+                               }
+                               goto skip_it;
+                       }
+
+                       if (h->nlmsg_type == NLMSG_DONE) {
+                               return 0;
+                       }
+                       if (h->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
+                               if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                                       bb_error_msg("ERROR truncated");
+                               } else {
+                                       errno = -l_err->error;
+                                       bb_perror_msg("RTNETLINK answers");
+                               }
+                               return -1;
+                       }
+                       err = filter(&nladdr, h, arg1);
+                       if (err < 0) {
+                               return err;
+                       }
+
+skip_it:
+                       h = NLMSG_NEXT(h, status);
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       bb_error_msg("Message truncated");
+                       continue;
+               }
+               if (status) {
+                       bb_error_msg_and_die("!!!Remnant of size %d", status);
+               }
+       }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+             unsigned groups, struct nlmsghdr *answer,
+             int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+             void *jarg)
+{
+       int status;
+       unsigned seq;
+       struct nlmsghdr *h;
+       struct sockaddr_nl nladdr;
+       struct iovec iov = { (void*)n, n->nlmsg_len };
+       char   buf[8192];
+       struct msghdr msg = {
+               (void*)&nladdr, sizeof(nladdr),
+               &iov,   1,
+               NULL,   0,
+               0
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = peer;
+       nladdr.nl_groups = groups;
+
+       n->nlmsg_seq = seq = ++rtnl->seq;
+       if (answer == NULL) {
+               n->nlmsg_flags |= NLM_F_ACK;
+       }
+       status = sendmsg(rtnl->fd, &msg, 0);
+
+       if (status < 0) {
+               bb_perror_msg("Cannot talk to rtnetlink");
+               return -1;
+       }
+
+       iov.iov_base = buf;
+
+       while (1) {
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rtnl->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       bb_perror_msg("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       bb_error_msg("EOF on netlink");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+               }
+               for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+                       int l_err;
+                       int len = h->nlmsg_len;
+                       int l = len - sizeof(*h);
+
+                       if (l<0 || len>status) {
+                               if (msg.msg_flags & MSG_TRUNC) {
+                                       bb_error_msg("Truncated message");
+                                       return -1;
+                               }
+                               bb_error_msg_and_die("!!!malformed message: len=%d", len);
+                       }
+
+                       if (nladdr.nl_pid != peer ||
+                           h->nlmsg_pid != rtnl->local.nl_pid ||
+                           h->nlmsg_seq != seq) {
+                               if (junk) {
+                                       l_err = junk(&nladdr, h, jarg);
+                                       if (l_err < 0) {
+                                               return l_err;
+                                       }
+                               }
+                               continue;
+                       }
+
+                       if (h->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                               if (l < sizeof(struct nlmsgerr)) {
+                                       bb_error_msg("ERROR truncated");
+                               } else {
+                                       errno = -err->error;
+                                       if (errno == 0) {
+                                               if (answer) {
+                                                       memcpy(answer, h, h->nlmsg_len);
+                                               }
+                                               return 0;
+                                       }
+                                       bb_perror_msg("RTNETLINK answers");
+                               }
+                               return -1;
+                       }
+                       if (answer) {
+                               memcpy(answer, h, h->nlmsg_len);
+                               return 0;
+                       }
+
+                       bb_error_msg("Unexpected reply!!!");
+
+                       status -= NLMSG_ALIGN(len);
+                       h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       bb_error_msg("Message truncated");
+                       continue;
+               }
+               if (status) {
+                       bb_error_msg_and_die("!!!Remnant of size %d", status);
+               }
+       }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+             int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+             void *jarg)
+{
+       int status;
+       struct nlmsghdr *h;
+       struct sockaddr_nl nladdr;
+       struct iovec iov;
+       char   buf[8192];
+       struct msghdr msg = {
+               (void*)&nladdr, sizeof(nladdr),
+               &iov,   1,
+               NULL,   0,
+               0
+       };
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = 0;
+       nladdr.nl_groups = 0;
+
+
+       iov.iov_base = buf;
+
+       while (1) {
+               iov.iov_len = sizeof(buf);
+               status = recvmsg(rtnl->fd, &msg, 0);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("OVERRUN");
+                       continue;
+               }
+               if (status == 0) {
+                       bb_error_msg("EOF on netlink");
+                       return -1;
+               }
+               if (msg.msg_namelen != sizeof(nladdr)) {
+                       bb_error_msg_and_die("Sender address length == %d", msg.msg_namelen);
+               }
+               for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+                       int err;
+                       int len = h->nlmsg_len;
+                       int l = len - sizeof(*h);
+
+                       if (l<0 || len>status) {
+                               if (msg.msg_flags & MSG_TRUNC) {
+                                       bb_error_msg("Truncated message");
+                                       return -1;
+                               }
+                               bb_error_msg_and_die("!!!malformed message: len=%d", len);
+                       }
+
+                       err = handler(&nladdr, h, jarg);
+                       if (err < 0) {
+                               return err;
+                       }
+
+                       status -= NLMSG_ALIGN(len);
+                       h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+               }
+               if (msg.msg_flags & MSG_TRUNC) {
+                       bb_error_msg("Message truncated");
+                       continue;
+               }
+               if (status) {
+                       bb_error_msg_and_die("!!!Remnant of size %d", status);
+               }
+       }
+}
+
+int rtnl_from_file(FILE *rtnl,
+             int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+             void *jarg)
+{
+       int status;
+       struct sockaddr_nl nladdr;
+       char   buf[8192];
+       struct nlmsghdr *h = (void*)buf;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = 0;
+       nladdr.nl_groups = 0;
+
+       while (1) {
+               int err, len, type;
+               int l;
+
+               status = fread(&buf, 1, sizeof(*h), rtnl);
+
+               if (status < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("rtnl_from_file: fread");
+                       return -1;
+               }
+               if (status == 0)
+                       return 0;
+
+               len = h->nlmsg_len;
+               type= h->nlmsg_type;
+               l = len - sizeof(*h);
+
+               if (l<0 || len>sizeof(buf)) {
+                       bb_error_msg("!!!malformed message: len=%d @%lu",
+                               len, ftell(rtnl));
+                       return -1;
+               }
+
+               status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+               if (status < 0) {
+                       bb_perror_msg("rtnl_from_file: fread");
+                       return -1;
+               }
+               if (status < l) {
+                       bb_error_msg("rtnl-from_file: truncated message");
+                       return -1;
+               }
+
+               err = handler(&nladdr, h, jarg);
+               if (err < 0)
+                       return err;
+       }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *rta;
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+               return -1;
+       rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), &data, 4);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+       return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+{
+       int len = RTA_LENGTH(alen);
+       struct rtattr *rta;
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+               return -1;
+       rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), data, alen);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+       return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *subrta;
+
+       if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+               return -1;
+       }
+       subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+       subrta->rta_type = type;
+       subrta->rta_len = len;
+       memcpy(RTA_DATA(subrta), &data, 4);
+       rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+       return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+{
+       struct rtattr *subrta;
+       int len = RTA_LENGTH(alen);
+
+       if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+               return -1;
+       }
+       subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+       subrta->rta_type = type;
+       subrta->rta_len = len;
+       memcpy(RTA_DATA(subrta), data, alen);
+       rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+       return 0;
+}
+
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+       while (RTA_OK(rta, len)) {
+               if (rta->rta_type <= max) {
+                       tb[rta->rta_type] = rta;
+               }
+               rta = RTA_NEXT(rta,len);
+       }
+       if (len) {
+               bb_error_msg("!!!Deficit %d, rta_len=%d", len, rta->rta_len);
+       }
+       return 0;
+}
diff --git a/busybox/networking/libiproute/libnetlink.h b/busybox/networking/libiproute/libnetlink.h
new file mode 100644 (file)
index 0000000..45d3ad2
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+       int                     fd;
+       struct sockaddr_nl      local;
+       struct sockaddr_nl      peer;
+       __u32                   seq;
+       __u32                   dump;
+};
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+extern int rtnl_dump_filter(struct rtnl_handle *rth,
+                           int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+                           void *arg1,
+                           int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                           void *arg2);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+                    unsigned groups, struct nlmsghdr *answer,
+                    int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                    void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                      void *jarg);
+extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+                      void *jarg);
+
+#endif /* __LIBNETLINK_H__ */
+
diff --git a/busybox/networking/libiproute/linux/pkt_sched.h b/busybox/networking/libiproute/linux/pkt_sched.h
new file mode 100644 (file)
index 0000000..70cbabc
--- /dev/null
@@ -0,0 +1,413 @@
+#ifndef __LINUX_PKT_SCHED_H
+#define __LINUX_PKT_SCHED_H
+
+/* Logical priority bands not depending on specific packet scheduler.
+   Every scheduler will map them to real traffic classes, if it has
+   no more precise mechanism to classify packets.
+
+   These numbers have no special meaning, though their coincidence
+   with obsolete IPv6 values is not occasional :-). New IPv6 drafts
+   preferred full anarchy inspired by diffserv group.
+
+   Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
+   class, actually, as rule it will be handled with more care than
+   filler or even bulk.
+ */
+
+#include <asm/types.h>
+
+#define TC_PRIO_BESTEFFORT             0
+#define TC_PRIO_FILLER                 1
+#define TC_PRIO_BULK                   2
+#define TC_PRIO_INTERACTIVE_BULK       4
+#define TC_PRIO_INTERACTIVE            6
+#define TC_PRIO_CONTROL                        7
+
+#define TC_PRIO_MAX                    15
+
+/* Generic queue statistics, available for all the elements.
+   Particular schedulers may have also their private records.
+ */
+
+struct tc_stats
+{
+       __u64   bytes;                  /* NUmber of enqueues bytes */
+       __u32   packets;                /* Number of enqueued packets   */
+       __u32   drops;                  /* Packets dropped because of lack of resources */
+       __u32   overlimits;             /* Number of throttle events when this
+                                        * flow goes out of allocated bandwidth */
+       __u32   bps;                    /* Current flow byte rate */
+       __u32   pps;                    /* Current flow packet rate */
+       __u32   qlen;
+       __u32   backlog;
+#ifdef __KERNEL__
+       spinlock_t *lock;
+#endif
+};
+
+struct tc_estimator
+{
+       char            interval;
+       unsigned char   ewma_log;
+};
+
+/* "Handles"
+   ---------
+
+    All the traffic control objects have 32bit identifiers, or "handles".
+
+    They can be considered as opaque numbers from user API viewpoint,
+    but actually they always consist of two fields: major and
+    minor numbers, which are interpreted by kernel specially,
+    that may be used by applications, though not recommended.
+
+    F.e. qdisc handles always have minor number equal to zero,
+    classes (or flows) have major equal to parent qdisc major, and
+    minor uniquely identifying class inside qdisc.
+
+    Macros to manipulate handles:
+ */
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+#define TC_H_UNSPEC    (0U)
+#define TC_H_ROOT      (0xFFFFFFFFU)
+#define TC_H_INGRESS    (0xFFFFFFF1U)
+
+struct tc_ratespec
+{
+       unsigned char   cell_log;
+       unsigned char   __reserved;
+       unsigned short  feature;
+       short           addend;
+       unsigned short  mpu;
+       __u32           rate;
+};
+
+/* FIFO section */
+
+struct tc_fifo_qopt
+{
+       __u32   limit;  /* Queue length: bytes for bfifo, packets for pfifo */
+};
+
+/* PRIO section */
+
+#define TCQ_PRIO_BANDS 16
+
+struct tc_prio_qopt
+{
+       int     bands;                  /* Number of bands */
+       __u8    priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
+};
+
+/* CSZ section */
+
+struct tc_csz_qopt
+{
+       int             flows;          /* Maximal number of guaranteed flows */
+       unsigned char   R_log;          /* Fixed point position for round number */
+       unsigned char   delta_log;      /* Log of maximal managed time interval */
+       __u8            priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
+};
+
+struct tc_csz_copt
+{
+       struct tc_ratespec slice;
+       struct tc_ratespec rate;
+       struct tc_ratespec peakrate;
+       __u32           limit;
+       __u32           buffer;
+       __u32           mtu;
+};
+
+enum
+{
+       TCA_CSZ_UNSPEC,
+       TCA_CSZ_PARMS,
+       TCA_CSZ_RTAB,
+       TCA_CSZ_PTAB,
+};
+
+/* TBF section */
+
+struct tc_tbf_qopt
+{
+       struct tc_ratespec rate;
+       struct tc_ratespec peakrate;
+       __u32           limit;
+       __u32           buffer;
+       __u32           mtu;
+};
+
+enum
+{
+       TCA_TBF_UNSPEC,
+       TCA_TBF_PARMS,
+       TCA_TBF_RTAB,
+       TCA_TBF_PTAB,
+};
+
+
+/* TEQL section */
+
+/* TEQL does not require any parameters */
+
+/* SFQ section */
+
+struct tc_sfq_qopt
+{
+       unsigned        quantum;        /* Bytes per round allocated to flow */
+       int             perturb_period; /* Period of hash perturbation */
+       __u32           limit;          /* Maximal packets in queue */
+       unsigned        divisor;        /* Hash divisor  */
+       unsigned        flows;          /* Maximal number of flows  */
+};
+
+/*
+ *  NOTE: limit, divisor and flows are hardwired to code at the moment.
+ *
+ *     limit=flows=128, divisor=1024;
+ *
+ *     The only reason for this is efficiency, it is possible
+ *     to change these parameters in compile time.
+ */
+
+/* RED section */
+
+enum
+{
+       TCA_RED_UNSPEC,
+       TCA_RED_PARMS,
+       TCA_RED_STAB,
+};
+
+struct tc_red_qopt
+{
+       __u32           limit;          /* HARD maximal queue length (bytes)    */
+       __u32           qth_min;        /* Min average length threshold (bytes) */
+       __u32           qth_max;        /* Max average length threshold (bytes) */
+       unsigned char   Wlog;           /* log(W)               */
+       unsigned char   Plog;           /* log(P_max/(qth_max-qth_min)) */
+       unsigned char   Scell_log;      /* cell size for idle damping */
+       unsigned char   flags;
+#define TC_RED_ECN     1
+};
+
+struct tc_red_xstats
+{
+       __u32           early;          /* Early drops */
+       __u32           pdrop;          /* Drops due to queue limits */
+       __u32           other;          /* Drops due to drop() calls */
+       __u32           marked;         /* Marked packets */
+};
+
+/* GRED section */
+
+#define MAX_DPs 16
+
+enum
+{
+       TCA_GRED_UNSPEC,
+       TCA_GRED_PARMS,
+       TCA_GRED_STAB,
+       TCA_GRED_DPS,
+};
+
+#define TCA_SET_OFF TCA_GRED_PARMS
+struct tc_gred_qopt
+{
+       __u32           limit;          /* HARD maximal queue length (bytes)
+*/
+       __u32           qth_min;        /* Min average length threshold (bytes)
+*/
+       __u32           qth_max;        /* Max average length threshold (bytes)
+*/
+       __u32           DP;             /* upto 2^32 DPs */
+       __u32           backlog;
+       __u32           qave;
+       __u32           forced;
+       __u32           early;
+       __u32           other;
+       __u32           pdrop;
+
+       unsigned char   Wlog;           /* log(W)               */
+       unsigned char   Plog;           /* log(P_max/(qth_max-qth_min)) */
+       unsigned char   Scell_log;      /* cell size for idle damping */
+       __u8            prio;           /* prio of this VQ */
+       __u32   packets;
+       __u32   bytesin;
+};
+/* gred setup */
+struct tc_gred_sopt
+{
+       __u32           DPs;
+       __u32           def_DP;
+       __u8            grio;
+};
+
+/* HTB section */
+#define TC_HTB_NUMPRIO         4
+#define TC_HTB_MAXDEPTH                4
+
+struct tc_htb_opt
+{
+       struct tc_ratespec      rate;
+       struct tc_ratespec      ceil;
+       __u32   buffer;
+       __u32   cbuffer;
+       __u32   quantum;        /* out only */
+       __u32   level;          /* out only */
+       __u8    prio;
+       __u8    injectd;        /* inject class distance */
+       __u8    pad[2];
+};
+struct tc_htb_glob
+{
+       __u32 rate2quantum;     /* bps->quantum divisor */
+       __u32 defcls;           /* default class number */
+       __u32 use_dcache;       /* use dequeue cache ? */
+       __u32 debug;            /* debug flags */
+
+
+       /* stats */
+       __u32 deq_rate; /* dequeue rate */
+       __u32 utilz;    /* dequeue utilization */
+       __u32 trials;   /* deq_prio trials per dequeue */
+       __u32 dcache_hits;
+       __u32 direct_pkts; /* count of non shapped packets */
+};
+enum
+{
+       TCA_HTB_UNSPEC,
+       TCA_HTB_PARMS,
+       TCA_HTB_INIT,
+       TCA_HTB_CTAB,
+       TCA_HTB_RTAB,
+};
+struct tc_htb_xstats
+{
+       __u32 lends;
+       __u32 borrows;
+       __u32 giants;   /* too big packets (rate will not be accurate) */
+       __u32 injects;  /* how many times leaf used injected bw */
+       __u32 tokens;
+       __u32 ctokens;
+};
+
+/* CBQ section */
+
+#define TC_CBQ_MAXPRIO         8
+#define TC_CBQ_MAXLEVEL                8
+#define TC_CBQ_DEF_EWMA                5
+
+struct tc_cbq_lssopt
+{
+       unsigned char   change;
+       unsigned char   flags;
+#define TCF_CBQ_LSS_BOUNDED    1
+#define TCF_CBQ_LSS_ISOLATED   2
+       unsigned char   ewma_log;
+       unsigned char   level;
+#define TCF_CBQ_LSS_FLAGS      1
+#define TCF_CBQ_LSS_EWMA       2
+#define TCF_CBQ_LSS_MAXIDLE    4
+#define TCF_CBQ_LSS_MINIDLE    8
+#define TCF_CBQ_LSS_OFFTIME    0x10
+#define TCF_CBQ_LSS_AVPKT      0x20
+       __u32           maxidle;
+       __u32           minidle;
+       __u32           offtime;
+       __u32           avpkt;
+};
+
+struct tc_cbq_wrropt
+{
+       unsigned char   flags;
+       unsigned char   priority;
+       unsigned char   cpriority;
+       unsigned char   __reserved;
+       __u32           allot;
+       __u32           weight;
+};
+
+struct tc_cbq_ovl
+{
+       unsigned char   strategy;
+#define        TC_CBQ_OVL_CLASSIC      0
+#define        TC_CBQ_OVL_DELAY        1
+#define        TC_CBQ_OVL_LOWPRIO      2
+#define        TC_CBQ_OVL_DROP         3
+#define        TC_CBQ_OVL_RCLASSIC     4
+       unsigned char   priority2;
+       __u32           penalty;
+};
+
+struct tc_cbq_police
+{
+       unsigned char   police;
+       unsigned char   __res1;
+       unsigned short  __res2;
+};
+
+struct tc_cbq_fopt
+{
+       __u32           split;
+       __u32           defmap;
+       __u32           defchange;
+};
+
+struct tc_cbq_xstats
+{
+       __u32           borrows;
+       __u32           overactions;
+       __s32           avgidle;
+       __s32           undertime;
+};
+
+enum
+{
+       TCA_CBQ_UNSPEC,
+       TCA_CBQ_LSSOPT,
+       TCA_CBQ_WRROPT,
+       TCA_CBQ_FOPT,
+       TCA_CBQ_OVL_STRATEGY,
+       TCA_CBQ_RATE,
+       TCA_CBQ_RTAB,
+       TCA_CBQ_POLICE,
+};
+
+#define TCA_CBQ_MAX    TCA_CBQ_POLICE
+
+/* dsmark section */
+
+enum {
+       TCA_DSMARK_UNSPEC,
+       TCA_DSMARK_INDICES,
+       TCA_DSMARK_DEFAULT_INDEX,
+       TCA_DSMARK_SET_TC_INDEX,
+       TCA_DSMARK_MASK,
+       TCA_DSMARK_VALUE
+};
+
+#define TCA_DSMARK_MAX TCA_DSMARK_VALUE
+
+/* ATM  section */
+
+enum {
+       TCA_ATM_UNSPEC,
+       TCA_ATM_FD,             /* file/socket descriptor */
+       TCA_ATM_PTR,            /* pointer to descriptor - later */
+       TCA_ATM_HDR,            /* LL header */
+       TCA_ATM_EXCESS,         /* excess traffic class (0 for CLP)  */
+       TCA_ATM_ADDR,           /* PVC address (for output only) */
+       TCA_ATM_STATE           /* VC state (ATM_VS_*; for output only) */
+};
+
+#define TCA_ATM_MAX    TCA_ATM_STATE
+
+#endif
diff --git a/busybox/networking/libiproute/ll_addr.c b/busybox/networking/libiproute/ll_addr.c
new file mode 100644 (file)
index 0000000..ada685f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * ll_addr.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <arpa/inet.h>
+#include <string.h>
+#include <net/if_arp.h>
+#include "utils.h"
+#include "libbb.h"
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
+{
+       int i;
+       int l;
+
+       if (alen == 4 &&
+           (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
+               return inet_ntop(AF_INET, addr, buf, blen);
+       }
+       l = 0;
+       for (i=0; i<alen; i++) {
+               if (i==0) {
+                       snprintf(buf+l, blen, "%02x", addr[i]);
+                       blen -= 2;
+                       l += 2;
+               } else {
+                       snprintf(buf+l, blen, ":%02x", addr[i]);
+                       blen -= 3;
+                       l += 3;
+               }
+       }
+       return buf;
+}
+
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
+{
+       if (strchr(arg, '.')) {
+               inet_prefix pfx;
+               if (get_addr_1(&pfx, arg, AF_INET)) {
+                       bb_error_msg("\"%s\" is invalid lladdr.", arg);
+                       return -1;
+               }
+               if (len < 4) {
+                       return -1;
+               }
+               memcpy(lladdr, pfx.data, 4);
+               return 4;
+       } else {
+               int i;
+
+               for (i=0; i<len; i++) {
+                       int temp;
+                       char *cp = strchr(arg, ':');
+                       if (cp) {
+                               *cp = 0;
+                               cp++;
+                       }
+                       if (sscanf(arg, "%x", &temp) != 1) {
+                               bb_error_msg("\"%s\" is invalid lladdr.", arg);
+                               return -1;
+                       }
+                       if (temp < 0 || temp > 255) {
+                               bb_error_msg("\"%s\" is invalid lladdr.", arg);
+                               return -1;
+                       }
+                       lladdr[i] = temp;
+                       if (!cp) {
+                               break;
+                       }
+                       arg = cp;
+               }
+               return i+1;
+       }
+}
diff --git a/busybox/networking/libiproute/ll_map.c b/busybox/networking/libiproute/ll_map.c
new file mode 100644 (file)
index 0000000..b7a8284
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * ll_map.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+
+#include "libnetlink.h"
+
+struct idxmap
+{
+       struct idxmap * next;
+       int             index;
+       int             type;
+       int             alen;
+       unsigned        flags;
+       unsigned char   addr[8];
+       char            name[16];
+};
+
+static struct idxmap *idxmap[16];
+
+int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       int h;
+       struct ifinfomsg *ifi = NLMSG_DATA(n);
+       struct idxmap *im, **imp;
+       struct rtattr *tb[IFLA_MAX+1];
+
+       if (n->nlmsg_type != RTM_NEWLINK)
+               return 0;
+
+       if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+               return -1;
+
+
+       memset(tb, 0, sizeof(tb));
+       parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+       if (tb[IFLA_IFNAME] == NULL)
+               return 0;
+
+       h = ifi->ifi_index&0xF;
+
+       for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
+               if (im->index == ifi->ifi_index)
+                       break;
+
+       if (im == NULL) {
+               im = malloc(sizeof(*im));
+               if (im == NULL)
+                       return 0;
+               im->next = *imp;
+               im->index = ifi->ifi_index;
+               *imp = im;
+       }
+
+       im->type = ifi->ifi_type;
+       im->flags = ifi->ifi_flags;
+       if (tb[IFLA_ADDRESS]) {
+               int alen;
+               im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+               if (alen > sizeof(im->addr))
+                       alen = sizeof(im->addr);
+               memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+       } else {
+               im->alen = 0;
+               memset(im->addr, 0, sizeof(im->addr));
+       }
+       strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
+       return 0;
+}
+
+const char *ll_idx_n2a(int idx, char *buf)
+{
+       struct idxmap *im;
+
+       if (idx == 0)
+               return "*";
+       for (im = idxmap[idx&0xF]; im; im = im->next)
+               if (im->index == idx)
+                       return im->name;
+       snprintf(buf, 16, "if%d", idx);
+       return buf;
+}
+
+
+const char *ll_index_to_name(int idx)
+{
+       static char nbuf[16];
+
+       return ll_idx_n2a(idx, nbuf);
+}
+
+int ll_index_to_type(int idx)
+{
+       struct idxmap *im;
+
+       if (idx == 0)
+               return -1;
+       for (im = idxmap[idx&0xF]; im; im = im->next)
+               if (im->index == idx)
+                       return im->type;
+       return -1;
+}
+
+unsigned ll_index_to_flags(int idx)
+{
+       struct idxmap *im;
+
+       if (idx == 0)
+               return 0;
+
+       for (im = idxmap[idx&0xF]; im; im = im->next)
+               if (im->index == idx)
+                       return im->flags;
+       return 0;
+}
+
+int ll_name_to_index(char *name)
+{
+       static char ncache[16];
+       static int icache;
+       struct idxmap *im;
+       int i;
+
+       if (name == NULL)
+               return 0;
+       if (icache && strcmp(name, ncache) == 0)
+               return icache;
+       for (i=0; i<16; i++) {
+               for (im = idxmap[i]; im; im = im->next) {
+                       if (strcmp(im->name, name) == 0) {
+                               icache = im->index;
+                               strcpy(ncache, name);
+                               return im->index;
+                       }
+               }
+       }
+       return 0;
+}
+
+int ll_init_map(struct rtnl_handle *rth)
+{
+       if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+               perror("Cannot send dump request");
+               exit(1);
+       }
+
+       if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               exit(1);
+       }
+       return 0;
+}
diff --git a/busybox/networking/libiproute/ll_map.h b/busybox/networking/libiproute/ll_map.h
new file mode 100644 (file)
index 0000000..739f157
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ll_init_map(struct rtnl_handle *rth);
+extern int ll_name_to_index(char *name);
+extern const char *ll_index_to_name(int idx);
+extern const char *ll_idx_n2a(int idx, char *buf);
+extern int ll_index_to_type(int idx);
+extern unsigned ll_index_to_flags(int idx);
+
+#endif /* __LL_MAP_H__ */
diff --git a/busybox/networking/libiproute/ll_proto.c b/busybox/networking/libiproute/ll_proto.c
new file mode 100644 (file)
index 0000000..9b5260b
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * ll_proto.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "utils.h"
+
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#define __PF(f,n) { ETH_P_##f, #n },
+static struct {
+       int id;
+       char *name;
+} llproto_names[] = {
+__PF(LOOP,loop)
+__PF(PUP,pup)
+#ifdef ETH_P_PUPAT
+__PF(PUPAT,pupat)
+#endif
+__PF(IP,ip)
+__PF(X25,x25)
+__PF(ARP,arp)
+__PF(BPQ,bpq)
+#ifdef ETH_P_IEEEPUP
+__PF(IEEEPUP,ieeepup)
+#endif
+#ifdef ETH_P_IEEEPUPAT
+__PF(IEEEPUPAT,ieeepupat)
+#endif
+__PF(DEC,dec)
+__PF(DNA_DL,dna_dl)
+__PF(DNA_RC,dna_rc)
+__PF(DNA_RT,dna_rt)
+__PF(LAT,lat)
+__PF(DIAG,diag)
+__PF(CUST,cust)
+__PF(SCA,sca)
+__PF(RARP,rarp)
+__PF(ATALK,atalk)
+__PF(AARP,aarp)
+__PF(IPX,ipx)
+__PF(IPV6,ipv6)
+#ifdef ETH_P_PPP_DISC
+__PF(PPP_DISC,ppp_disc)
+#endif
+#ifdef ETH_P_PPP_SES
+__PF(PPP_SES,ppp_ses)
+#endif
+#ifdef ETH_P_ATMMPOA
+__PF(ATMMPOA,atmmpoa)
+#endif
+#ifdef ETH_P_ATMFATE
+__PF(ATMFATE,atmfate)
+#endif
+
+__PF(802_3,802_3)
+__PF(AX25,ax25)
+__PF(ALL,all)
+__PF(802_2,802_2)
+__PF(SNAP,snap)
+__PF(DDCMP,ddcmp)
+__PF(WAN_PPP,wan_ppp)
+__PF(PPP_MP,ppp_mp)
+__PF(LOCALTALK,localtalk)
+__PF(PPPTALK,ppptalk)
+__PF(TR_802_2,tr_802_2)
+__PF(MOBITEX,mobitex)
+__PF(CONTROL,control)
+__PF(IRDA,irda)
+#ifdef ETH_P_ECONET
+__PF(ECONET,econet)
+#endif
+
+{ 0x8100, "802.1Q" },
+{ ETH_P_IP, "ipv4" },
+};
+#undef __PF
+
+
+char * ll_proto_n2a(unsigned short id, char *buf, int len)
+{
+        int i;
+
+       id = ntohs(id);
+
+        for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+                 if (llproto_names[i].id == id)
+                       return llproto_names[i].name;
+       }
+        snprintf(buf, len, "[%d]", id);
+        return buf;
+}
+
+int ll_proto_a2n(unsigned short *id, char *buf)
+{
+        int i;
+        for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+                 if (strcasecmp(llproto_names[i].name, buf) == 0) {
+                        *id = htons(llproto_names[i].id);
+                        return 0;
+                }
+       }
+       if (get_u16(id, buf, 0))
+               return -1;
+       *id = htons(*id);
+       return 0;
+}
diff --git a/busybox/networking/libiproute/ll_types.c b/busybox/networking/libiproute/ll_types.c
new file mode 100644 (file)
index 0000000..f39f777
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * ll_types.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include <linux/if_arp.h>
+
+char * ll_type_n2a(int type, char *buf, int len)
+{
+#define __PF(f,n) { ARPHRD_##f, #n },
+static struct {
+       int type;
+       char *name;
+} arphrd_names[] = {
+{ 0, "generic" },
+__PF(ETHER,ether)
+__PF(EETHER,eether)
+__PF(AX25,ax25)
+__PF(PRONET,pronet)
+__PF(CHAOS,chaos)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802,ieee802)
+#else
+__PF(IEEE802,tr)
+#endif
+__PF(ARCNET,arcnet)
+__PF(APPLETLK,atalk)
+__PF(DLCI,dlci)
+#ifdef ARPHRD_ATM
+__PF(ATM,atm)
+#endif
+__PF(METRICOM,metricom)
+#ifdef ARPHRD_IEEE1394
+__PF(IEEE1394,ieee1394)
+#endif
+
+__PF(SLIP,slip)
+__PF(CSLIP,cslip)
+__PF(SLIP6,slip6)
+__PF(CSLIP6,cslip6)
+__PF(RSRVD,rsrvd)
+__PF(ADAPT,adapt)
+__PF(ROSE,rose)
+__PF(X25,x25)
+#ifdef ARPHRD_HWX25
+__PF(HWX25,hwx25)
+#endif
+__PF(PPP,ppp)
+__PF(HDLC,hdlc)
+__PF(LAPB,lapb)
+#ifdef ARPHRD_DDCMP
+__PF(DDCMP,ddcmp)
+__PF(RAWHDLC,rawhdlc)
+#endif
+
+__PF(TUNNEL,ipip)
+__PF(TUNNEL6,tunnel6)
+__PF(FRAD,frad)
+__PF(SKIP,skip)
+__PF(LOOPBACK,loopback)
+__PF(LOCALTLK,ltalk)
+__PF(FDDI,fddi)
+__PF(BIF,bif)
+__PF(SIT,sit)
+__PF(IPDDP,ip/ddp)
+__PF(IPGRE,gre)
+__PF(PIMREG,pimreg)
+__PF(HIPPI,hippi)
+__PF(ASH,ash)
+__PF(ECONET,econet)
+__PF(IRDA,irda)
+__PF(FCPP,fcpp)
+__PF(FCAL,fcal)
+__PF(FCPL,fcpl)
+__PF(FCFABRIC,fcfb0)
+__PF(FCFABRIC+1,fcfb1)
+__PF(FCFABRIC+2,fcfb2)
+__PF(FCFABRIC+3,fcfb3)
+__PF(FCFABRIC+4,fcfb4)
+__PF(FCFABRIC+5,fcfb5)
+__PF(FCFABRIC+6,fcfb6)
+__PF(FCFABRIC+7,fcfb7)
+__PF(FCFABRIC+8,fcfb8)
+__PF(FCFABRIC+9,fcfb9)
+__PF(FCFABRIC+10,fcfb10)
+__PF(FCFABRIC+11,fcfb11)
+__PF(FCFABRIC+12,fcfb12)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802_TR,tr)
+#endif
+#ifdef ARPHRD_IEEE80211
+__PF(IEEE80211,ieee802.11)
+#endif
+#ifdef ARPHRD_VOID
+__PF(VOID,void)
+#endif
+};
+#undef __PF
+
+        int i;
+        for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
+                 if (arphrd_names[i].type == type)
+                       return arphrd_names[i].name;
+       }
+        snprintf(buf, len, "[%d]", type);
+        return buf;
+}
diff --git a/busybox/networking/libiproute/rt_names.c b/busybox/networking/libiproute/rt_names.c
new file mode 100644 (file)
index 0000000..d503645
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * rt_names.c          rtnetlink names DB.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdint.h>
+
+static void rtnl_tab_initialize(char *file, char **tab, int size)
+{
+       char buf[512];
+       FILE *fp;
+
+       fp = fopen(file, "r");
+       if (!fp)
+               return;
+       while (fgets(buf, sizeof(buf), fp)) {
+               char *p = buf;
+               int id;
+               char namebuf[512];
+
+               while (*p == ' ' || *p == '\t')
+                       p++;
+               if (*p == '#' || *p == '\n' || *p == 0)
+                       continue;
+               if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 &&
+                   sscanf(p, "0x%x %s #", &id, namebuf) != 2 &&
+                   sscanf(p, "%d %s\n", &id, namebuf) != 2 &&
+                   sscanf(p, "%d %s #", &id, namebuf) != 2) {
+                       fprintf(stderr, "Database %s is corrupted at %s\n",
+                               file, p);
+                       return;
+               }
+
+               if (id<0 || id>size)
+                       continue;
+
+               tab[id] = strdup(namebuf);
+       }
+       fclose(fp);
+}
+
+
+static char * rtnl_rtprot_tab[256] = {
+       "none",
+       "redirect",
+       "kernel",
+       "boot",
+       "static",
+       NULL,
+       NULL,
+       NULL,
+       "gated",
+       "ra",
+       "mrt",
+       "zebra",
+       "bird",
+};
+
+
+
+static int rtnl_rtprot_init;
+
+static void rtnl_rtprot_initialize(void)
+{
+       rtnl_rtprot_init = 1;
+       rtnl_tab_initialize("/etc/iproute2/rt_protos",
+                           rtnl_rtprot_tab, 256);
+}
+
+char * rtnl_rtprot_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtprot_tab[id]) {
+               if (!rtnl_rtprot_init)
+                       rtnl_rtprot_initialize();
+       }
+       if (rtnl_rtprot_tab[id])
+               return rtnl_rtprot_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+int rtnl_rtprot_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtprot_init)
+               rtnl_rtprot_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtprot_tab[i] &&
+                   strcmp(rtnl_rtprot_tab[i], arg) == 0) {
+                       cache = rtnl_rtprot_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
+
+
+static char * rtnl_rtscope_tab[256] = {
+       "global",
+};
+
+static int rtnl_rtscope_init;
+
+static void rtnl_rtscope_initialize(void)
+{
+       rtnl_rtscope_init = 1;
+       rtnl_rtscope_tab[255] = "nowhere";
+       rtnl_rtscope_tab[254] = "host";
+       rtnl_rtscope_tab[253] = "link";
+       rtnl_rtscope_tab[200] = "site";
+       rtnl_tab_initialize("/etc/iproute2/rt_scopes",
+                           rtnl_rtscope_tab, 256);
+}
+
+char * rtnl_rtscope_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtscope_tab[id]) {
+               if (!rtnl_rtscope_init)
+                       rtnl_rtscope_initialize();
+       }
+       if (rtnl_rtscope_tab[id])
+               return rtnl_rtscope_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+int rtnl_rtscope_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtscope_init)
+               rtnl_rtscope_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtscope_tab[i] &&
+                   strcmp(rtnl_rtscope_tab[i], arg) == 0) {
+                       cache = rtnl_rtscope_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
+
+
+static char * rtnl_rtrealm_tab[256] = {
+       "unknown",
+};
+
+static int rtnl_rtrealm_init;
+
+static void rtnl_rtrealm_initialize(void)
+{
+       rtnl_rtrealm_init = 1;
+       rtnl_tab_initialize("/etc/iproute2/rt_realms",
+                           rtnl_rtrealm_tab, 256);
+}
+
+char * rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtrealm_tab[id]) {
+               if (!rtnl_rtrealm_init)
+                       rtnl_rtrealm_initialize();
+       }
+       if (rtnl_rtrealm_tab[id])
+               return rtnl_rtrealm_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtrealm_init)
+               rtnl_rtrealm_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtrealm_tab[i] &&
+                   strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
+                       cache = rtnl_rtrealm_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
+
+
+static char * rtnl_rttable_tab[256] = {
+       "unspec",
+};
+
+static int rtnl_rttable_init;
+
+static void rtnl_rttable_initialize(void)
+{
+       rtnl_rttable_init = 1;
+       rtnl_rttable_tab[255] = "local";
+       rtnl_rttable_tab[254] = "main";
+       rtnl_tab_initialize("/etc/iproute2/rt_tables",
+                           rtnl_rttable_tab, 256);
+}
+
+char * rtnl_rttable_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rttable_tab[id]) {
+               if (!rtnl_rttable_init)
+                       rtnl_rttable_initialize();
+       }
+       if (rtnl_rttable_tab[id])
+               return rtnl_rttable_tab[id];
+       snprintf(buf, len, "%d", id);
+       return buf;
+}
+
+int rtnl_rttable_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rttable_init)
+               rtnl_rttable_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rttable_tab[i] &&
+                   strcmp(rtnl_rttable_tab[i], arg) == 0) {
+                       cache = rtnl_rttable_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       i = strtoul(arg, &end, 0);
+       if (!end || end == arg || *end || i > 255)
+               return -1;
+       *id = i;
+       return 0;
+}
+
+
+static char * rtnl_rtdsfield_tab[256] = {
+       "0",
+};
+
+static int rtnl_rtdsfield_init;
+
+static void rtnl_rtdsfield_initialize(void)
+{
+       rtnl_rtdsfield_init = 1;
+       rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+                           rtnl_rtdsfield_tab, 256);
+}
+
+char * rtnl_dsfield_n2a(int id, char *buf, int len)
+{
+       if (id<0 || id>=256) {
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+       if (!rtnl_rtdsfield_tab[id]) {
+               if (!rtnl_rtdsfield_init)
+                       rtnl_rtdsfield_initialize();
+       }
+       if (rtnl_rtdsfield_tab[id])
+               return rtnl_rtdsfield_tab[id];
+       snprintf(buf, len, "0x%02x", id);
+       return buf;
+}
+
+
+int rtnl_dsfield_a2n(uint32_t *id, char *arg)
+{
+       static char *cache = NULL;
+       static unsigned long res;
+       char *end;
+       int i;
+
+       if (cache && strcmp(cache, arg) == 0) {
+               *id = res;
+               return 0;
+       }
+
+       if (!rtnl_rtdsfield_init)
+               rtnl_rtdsfield_initialize();
+
+       for (i=0; i<256; i++) {
+               if (rtnl_rtdsfield_tab[i] &&
+                   strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
+                       cache = rtnl_rtdsfield_tab[i];
+                       res = i;
+                       *id = res;
+                       return 0;
+               }
+       }
+
+       res = strtoul(arg, &end, 16);
+       if (!end || end == arg || *end || res > 255)
+               return -1;
+       *id = res;
+       return 0;
+}
+
diff --git a/busybox/networking/libiproute/rt_names.h b/busybox/networking/libiproute/rt_names.h
new file mode 100644 (file)
index 0000000..97bc616
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef RT_NAMES_H_
+#define RT_NAMES_H_ 1
+
+#include <stdint.h>
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+const char* rtnl_rttable_n2a(int id, char *buf, int len);
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+int rtnl_rtprot_a2n(int *id, char *arg);
+int rtnl_rtscope_a2n(int *id, char *arg);
+int rtnl_rttable_a2n(int *id, char *arg);
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+
+const char *inet_proto_n2a(int proto, char *buf, int len);
+int inet_proto_a2n(char *buf);
+
+
+const char * ll_type_n2a(int type, char *buf, int len);
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
+
+const char * ll_proto_n2a(unsigned short id, char *buf, int len);
+int ll_proto_a2n(unsigned short *id, char *buf);
+
+
+#endif
diff --git a/busybox/networking/libiproute/rtm_map.c b/busybox/networking/libiproute/rtm_map.c
new file mode 100644 (file)
index 0000000..5f6a9e6
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * rtm_map.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+char *rtnl_rtntype_n2a(int id, char *buf, int len)
+{
+       switch (id) {
+       case RTN_UNSPEC:
+               return "none";
+       case RTN_UNICAST:
+               return "unicast";
+       case RTN_LOCAL:
+               return "local";
+       case RTN_BROADCAST:
+               return "broadcast";
+       case RTN_ANYCAST:
+               return "anycast";
+       case RTN_MULTICAST:
+               return "multicast";
+       case RTN_BLACKHOLE:
+               return "blackhole";
+       case RTN_UNREACHABLE:
+               return "unreachable";
+       case RTN_PROHIBIT:
+               return "prohibit";
+       case RTN_THROW:
+               return "throw";
+       case RTN_NAT:
+               return "nat";
+       case RTN_XRESOLVE:
+               return "xresolve";
+       default:
+               snprintf(buf, len, "%d", id);
+               return buf;
+       }
+}
+
+
+int rtnl_rtntype_a2n(int *id, char *arg)
+{
+       char *end;
+       unsigned long res;
+
+       if (strcmp(arg, "local") == 0)
+               res = RTN_LOCAL;
+       else if (strcmp(arg, "nat") == 0)
+               res = RTN_NAT;
+       else if (matches(arg, "broadcast") == 0 ||
+                strcmp(arg, "brd") == 0)
+               res = RTN_BROADCAST;
+       else if (matches(arg, "anycast") == 0)
+               res = RTN_ANYCAST;
+       else if (matches(arg, "multicast") == 0)
+               res = RTN_MULTICAST;
+       else if (matches(arg, "prohibit") == 0)
+               res = RTN_PROHIBIT;
+       else if (matches(arg, "unreachable") == 0)
+               res = RTN_UNREACHABLE;
+       else if (matches(arg, "blackhole") == 0)
+               res = RTN_BLACKHOLE;
+       else if (matches(arg, "xresolve") == 0)
+               res = RTN_XRESOLVE;
+       else if (matches(arg, "unicast") == 0)
+               res = RTN_UNICAST;
+       else if (strcmp(arg, "throw") == 0)
+               res = RTN_THROW;
+       else {
+               res = strtoul(arg, &end, 0);
+               if (!end || end == arg || *end || res > 255)
+                       return -1;
+       }
+       *id = res;
+       return 0;
+}
+
+int get_rt_realms(__u32 *realms, char *arg)
+{
+       __u32 realm = 0;
+       char *p = strchr(arg, '/');
+
+       *realms = 0;
+       if (p) {
+               *p = 0;
+               if (rtnl_rtrealm_a2n(realms, arg)) {
+                       *p = '/';
+                       return -1;
+               }
+               *realms <<= 16;
+               *p = '/';
+               arg = p+1;
+       }
+       if (*arg && rtnl_rtrealm_a2n(&realm, arg))
+               return -1;
+       *realms |= realm;
+       return 0;
+}
diff --git a/busybox/networking/libiproute/rtm_map.h b/busybox/networking/libiproute/rtm_map.h
new file mode 100644 (file)
index 0000000..70bda7d
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __RTM_MAP_H__
+#define __RTM_MAP_H__ 1
+
+char *rtnl_rtntype_n2a(int id, char *buf, int len);
+int rtnl_rtntype_a2n(int *id, char *arg);
+
+int get_rt_realms(__u32 *realms, char *arg);
+
+
+#endif /* __RTM_MAP_H__ */
diff --git a/busybox/networking/libiproute/utils.c b/busybox/networking/libiproute/utils.c
new file mode 100644 (file)
index 0000000..fa15486
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * utils.c
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "utils.h"
+#include "libbb.h"
+
+int get_integer(int *val, char *arg, int base)
+{
+       long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtol(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_unsigned(unsigned *val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_u32(__u32 * val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_u16(__u16 * val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_u8(__u8 * val, char *arg, int base)
+{
+       unsigned long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtoul(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0xFF)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_s16(__s16 * val, char *arg, int base)
+{
+       long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtol(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_s8(__s8 * val, char *arg, int base)
+{
+       long res;
+       char *ptr;
+
+       if (!arg || !*arg)
+               return -1;
+       res = strtol(arg, &ptr, base);
+       if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
+               return -1;
+       *val = res;
+       return 0;
+}
+
+int get_addr_1(inet_prefix * addr, char *name, int family)
+{
+       char *cp;
+       unsigned char *ap = (unsigned char *) addr->data;
+       int i;
+
+       memset(addr, 0, sizeof(*addr));
+
+       if (strcmp(name, "default") == 0 ||
+               strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
+               addr->family = family;
+               addr->bytelen = (family == AF_INET6 ? 16 : 4);
+               addr->bitlen = -1;
+               return 0;
+       }
+
+       if (strchr(name, ':')) {
+               addr->family = AF_INET6;
+               if (family != AF_UNSPEC && family != AF_INET6)
+                       return -1;
+               if (inet_pton(AF_INET6, name, addr->data) <= 0)
+                       return -1;
+               addr->bytelen = 16;
+               addr->bitlen = -1;
+               return 0;
+       }
+
+       addr->family = AF_INET;
+       if (family != AF_UNSPEC && family != AF_INET)
+               return -1;
+       addr->bytelen = 4;
+       addr->bitlen = -1;
+       for (cp = name, i = 0; *cp; cp++) {
+               if (*cp <= '9' && *cp >= '0') {
+                       ap[i] = 10 * ap[i] + (*cp - '0');
+                       continue;
+               }
+               if (*cp == '.' && ++i <= 3)
+                       continue;
+               return -1;
+       }
+       return 0;
+}
+
+int get_prefix_1(inet_prefix * dst, char *arg, int family)
+{
+       int err;
+       unsigned plen;
+       char *slash;
+
+       memset(dst, 0, sizeof(*dst));
+
+       if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) {
+               dst->family = family;
+               dst->bytelen = 0;
+               dst->bitlen = 0;
+               return 0;
+       }
+
+       slash = strchr(arg, '/');
+       if (slash)
+               *slash = 0;
+       err = get_addr_1(dst, arg, family);
+       if (err == 0) {
+               switch (dst->family) {
+               case AF_INET6:
+                       dst->bitlen = 128;
+                       break;
+               default:
+               case AF_INET:
+                       dst->bitlen = 32;
+               }
+               if (slash) {
+                       if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) {
+                               err = -1;
+                               goto done;
+                       }
+                       dst->bitlen = plen;
+               }
+       }
+  done:
+       if (slash)
+               *slash = '/';
+       return err;
+}
+
+int get_addr(inet_prefix * dst, char *arg, int family)
+{
+       if (family == AF_PACKET) {
+               bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
+       }
+       if (get_addr_1(dst, arg, family)) {
+               bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
+       }
+       return 0;
+}
+
+int get_prefix(inet_prefix * dst, char *arg, int family)
+{
+       if (family == AF_PACKET) {
+               bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
+       }
+       if (get_prefix_1(dst, arg, family)) {
+               bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
+       }
+       return 0;
+}
+
+__u32 get_addr32(char *name)
+{
+       inet_prefix addr;
+
+       if (get_addr_1(&addr, name, AF_INET)) {
+               bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name);
+       }
+       return addr.data[0];
+}
+
+void incomplete_command()
+{
+       bb_error_msg("Command line is not complete. Try option \"help\"");
+       exit(-1);
+}
+
+void invarg(char *msg, char *arg)
+{
+       bb_error_msg("argument \"%s\" is wrong: %s", arg, msg);
+       exit(-1);
+}
+
+void duparg(char *key, char *arg)
+{
+       bb_error_msg("duplicate \"%s\": \"%s\" is the second value.", key, arg);
+       exit(-1);
+}
+
+void duparg2(char *key, char *arg)
+{
+       bb_error_msg("either \"%s\" is duplicate, or \"%s\" is a garbage.", key, arg);
+       exit(-1);
+}
+
+int matches(char *cmd, char *pattern)
+{
+       int len = strlen(cmd);
+
+       if (len > strlen(pattern)) {
+               return -1;
+       }
+       return memcmp(pattern, cmd, len);
+}
+
+int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
+{
+       __u32 *a1 = a->data;
+       __u32 *a2 = b->data;
+       int words = bits >> 0x05;
+
+       bits &= 0x1f;
+
+       if (words)
+               if (memcmp(a1, a2, words << 2))
+                       return -1;
+
+       if (bits) {
+               __u32 w1, w2;
+               __u32 mask;
+
+               w1 = a1[words];
+               w2 = a2[words];
+
+               mask = htonl((0xffffffff) << (0x20 - bits));
+
+               if ((w1 ^ w2) & mask)
+                       return 1;
+       }
+
+       return 0;
+}
+
+int __iproute2_hz_internal;
+
+int __get_hz(void)
+{
+       int hz = 0;
+       FILE *fp = fopen("/proc/net/psched", "r");
+
+       if (fp) {
+               unsigned nom, denom;
+
+               if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+                       if (nom == 1000000)
+                               hz = denom;
+               fclose(fp);
+       }
+       if (hz)
+               return hz;
+       return sysconf(_SC_CLK_TCK);
+}
+
+const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
+{
+       switch (af) {
+       case AF_INET:
+       case AF_INET6:
+               return inet_ntop(af, addr, buf, buflen);
+       default:
+               return "???";
+       }
+}
+
+
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+#ifdef RESOLVE_HOSTNAMES
+       if (resolve_hosts) {
+               struct hostent *h_ent;
+
+               if (len <= 0) {
+                       switch (af) {
+                       case AF_INET:
+                               len = 4;
+                               break;
+                       case AF_INET6:
+                               len = 16;
+                               break;
+                       default:;
+                       }
+               }
+               if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
+                       snprintf(buf, buflen - 1, "%s", h_ent->h_name);
+                       return buf;
+               }
+       }
+#endif
+       return rt_addr_n2a(af, len, addr, buf, buflen);
+}
diff --git a/busybox/networking/libiproute/utils.h b/busybox/networking/libiproute/utils.h
new file mode 100644 (file)
index 0000000..e79e177
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#include <asm/types.h>
+#include <resolv.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "rtm_map.h"
+
+extern int preferred_family;
+extern int show_stats;
+extern int show_details;
+extern int show_raw;
+extern int resolve_hosts;
+extern int oneline;
+extern char * _SL_;
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP    50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH     51
+#endif
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x)  char x[SPRINT_BSIZE]
+
+extern void incomplete_command(void) __attribute__((noreturn));
+
+#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
+
+typedef struct
+{
+       __u8 family;
+       __u8 bytelen;
+       __s16 bitlen;
+       __u32 data[4];
+} inet_prefix;
+
+#define DN_MAXADDL 20
+#ifndef AF_DECnet
+#define AF_DECnet 12
+#endif
+
+struct dn_naddr
+{
+        unsigned short          a_len;
+        unsigned char a_addr[DN_MAXADDL];
+};
+
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+       uint32_t ipx_net;
+       uint8_t  ipx_node[IPX_NODE_LEN];
+};
+
+extern __u32 get_addr32(char *name);
+extern int get_addr_1(inet_prefix *dst, char *arg, int family);
+extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
+extern int get_addr(inet_prefix *dst, char *arg, int family);
+extern int get_prefix(inet_prefix *dst, char *arg, int family);
+
+extern int get_integer(int *val, char *arg, int base);
+extern int get_unsigned(unsigned *val, char *arg, int base);
+#define get_byte get_u8
+#define get_ushort get_u16
+#define get_short get_s16
+extern int get_u32(__u32 *val, char *arg, int base);
+extern int get_u16(__u16 *val, char *arg, int base);
+extern int get_s16(__s16 *val, char *arg, int base);
+extern int get_u8(__u8 *val, char *arg, int base);
+extern int get_s8(__s8 *val, char *arg, int base);
+
+extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
+
+void invarg(char *, char *) __attribute__((noreturn));
+void duparg(char *, char *) __attribute__((noreturn));
+void duparg2(char *, char *) __attribute__((noreturn));
+int matches(char *arg, char *pattern);
+extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
+int dnet_pton(int af, const char *src, void *addr);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
+
+extern int __iproute2_hz_internal;
+extern int __get_hz(void);
+
+static __inline__ int get_hz(void)
+{
+       if (__iproute2_hz_internal == 0)
+               __iproute2_hz_internal = __get_hz();
+       return __iproute2_hz_internal;
+}
+
+#endif /* __UTILS_H__ */
diff --git a/busybox/networking/nameif.c b/busybox/networking/nameif.c
new file mode 100644 (file)
index 0000000..10f13b4
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * nameif.c - Naming Interfaces based on MAC address for busybox.
+ *
+ * Written 2000 by Andi Kleen.
+ * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
+ *                     Glenn McGrath <bug1@iinet.net.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ */
+
+
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+
+#include "busybox.h"
+
+/* Older versions of net/if.h do not appear to define IF_NAMESIZE. */
+#ifndef IF_NAMESIZE
+#  ifdef IFNAMSIZ
+#    define IF_NAMESIZE IFNAMSIZ
+#  else
+#    define IF_NAMESIZE 16
+#  endif
+#endif
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME    0x8923  /* set interface name */
+
+/* Octets in one Ethernet addr, from <linux/if_ether.h> */
+#define ETH_ALEN       6
+
+#ifndef ifr_newname
+#define ifr_newname ifr_ifru.ifru_slave
+#endif
+
+typedef struct mactable_s {
+       struct mactable_s *next;
+       struct mactable_s *prev;
+       char *ifname;
+       struct ether_addr *mac;
+} mactable_t;
+
+static unsigned char use_syslog;
+
+static void serror(const char *s, ...) __attribute__ ((noreturn));
+
+static void serror(const char *s, ...)
+{
+       va_list ap;
+
+       va_start(ap, s);
+
+       if (use_syslog) {
+               openlog(bb_applet_name, 0, LOG_LOCAL0);
+               vsyslog(LOG_ERR, s, ap);
+               closelog();
+       } else {
+               bb_verror_msg(s, ap);
+               putc('\n', stderr);
+       }
+
+       va_end(ap);
+
+       exit(EXIT_FAILURE);
+}
+
+/* Check ascii str_macaddr, convert and copy to *mac */
+struct ether_addr *cc_macaddr(char *str_macaddr)
+{
+       struct ether_addr *lmac, *mac;
+
+       lmac = ether_aton(str_macaddr);
+       if (lmac == NULL)
+               serror("cannot parse MAC %s", str_macaddr);
+       mac = xmalloc(ETH_ALEN);
+       memcpy(mac, lmac, ETH_ALEN);
+
+       return mac;
+}
+
+int nameif_main(int argc, char **argv)
+{
+       mactable_t *clist = NULL;
+       FILE *ifh;
+       const char *fname = "/etc/mactab";
+       char *line;
+       int ctl_sk;
+       int opt;
+       int if_index = 1;
+       mactable_t *ch;
+
+
+       while ((opt = getopt(argc, argv, "c:s")) != -1) {
+               switch (opt) {
+               case 'c':
+                       fname = optarg;
+                       break;
+               case 's':
+                       use_syslog = 1;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       if ((argc - optind) & 1)
+               bb_show_usage();
+
+       if (optind < argc) {
+               char **a = argv + optind;
+
+               while (*a) {
+
+                       if (strlen(*a) > IF_NAMESIZE)
+                               serror("interface name `%s' too long", *a);
+                       ch = xcalloc(1, sizeof(mactable_t));
+                       ch->ifname = bb_xstrdup(*a++);
+                       ch->mac = cc_macaddr(*a++);
+                       if (clist)
+                               clist->prev = ch;
+                       ch->next = clist;
+                       clist = ch;
+               }
+       } else {
+               ifh = bb_xfopen(fname, "r");
+
+               while ((line = bb_get_line_from_file(ifh)) != NULL) {
+                       char *line_ptr;
+                       size_t name_length;
+
+                       line_ptr = line + strspn(line, " \t");
+                       if ((line_ptr[0] == '#') || (line_ptr[0] == '\n'))
+                               continue;
+                       name_length = strcspn(line_ptr, " \t");
+                       ch = xcalloc(1, sizeof(mactable_t));
+                       ch->ifname = bb_xstrndup(line_ptr, name_length);
+                       if (name_length > IF_NAMESIZE)
+                               serror("interface name `%s' too long", ch->ifname);
+                       line_ptr += name_length;
+                       line_ptr += strspn(line_ptr, " \t");
+                       name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:");
+                       line_ptr[name_length] = '\0';
+                       ch->mac = cc_macaddr(line_ptr);
+                       if (clist)
+                               clist->prev = ch;
+                       ch->next = clist;
+                       clist = ch;
+                       free(line);
+               }
+               fclose(ifh);
+       }
+
+       if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+               serror("socket: %m");
+
+       while (clist) {
+               struct ifreq ifr;
+
+               bzero(&ifr, sizeof(struct ifreq));
+               if_index++;
+               ifr.ifr_ifindex = if_index;
+
+               /* Get ifname by index or die */
+               if (ioctl(ctl_sk, SIOCGIFNAME, &ifr))
+                       break;
+
+               /* Has this device hwaddr? */
+               if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr))
+                       continue;
+
+               /* Search for mac like in ifr.ifr_hwaddr.sa_data */
+               for (ch = clist; ch; ch = ch->next)
+                       if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN))
+                               break;
+
+               /* Nothing found for current ifr.ifr_hwaddr.sa_data */
+               if (ch == NULL)
+                       continue;
+
+               strcpy(ifr.ifr_newname, ch->ifname);
+               if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0)
+                       serror("cannot change ifname %s to %s: %m",
+                                  ifr.ifr_name, ch->ifname);
+
+               /* Remove list entry of renamed interface */
+               if (ch->prev != NULL) {
+                       (ch->prev)->next = ch->next;
+               } else {
+                       clist = ch->next;
+               }
+               if (ch->next != NULL)
+                       (ch->next)->prev = ch->prev;
+#ifdef CONFIG_FEATURE_CLEAN_UP
+               free(ch->ifname);
+               free(ch->mac);
+               free(ch);
+#endif
+       }
+
+       return 0;
+}
diff --git a/busybox/networking/nc.c b/busybox/networking/nc.c
new file mode 100644 (file)
index 0000000..3099763
--- /dev/null
@@ -0,0 +1,177 @@
+/* vi: set sw=4 ts=4: */
+/*  nc: mini-netcat - built from the ground up for LRP
+    Copyright (C) 1998  Charles P. Wright
+
+    0.0.1   6K      It works.
+    0.0.2   5K      Smaller and you can also check the exit condition if you wish.
+    0.0.3          Uses select()
+
+    19980918 Busy Boxed! Dave Cinege
+    19990512 Uses Select. Charles P. Wright
+    19990513 Fixes stdin stupidity and uses buffers.  Charles P. Wright
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+#define GAPING_SECURITY_HOLE
+
+int nc_main(int argc, char **argv)
+{
+       int do_listen = 0, lport = 0, delay = 0, tmpfd, opt, sfd, x;
+       char buf[BUFSIZ];
+#ifdef GAPING_SECURITY_HOLE
+       char * pr00gie = NULL;
+#endif
+
+       struct sockaddr_in address;
+       struct hostent *hostinfo;
+
+       fd_set readfds, testfds;
+
+       while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) {
+               switch (opt) {
+                       case 'l':
+                               do_listen++;
+                               break;
+                       case 'p':
+                               lport = bb_lookup_port(optarg, "tcp", 0);
+                               break;
+                       case 'i':
+                               delay = atoi(optarg);
+                               break;
+#ifdef GAPING_SECURITY_HOLE
+                       case 'e':
+                               pr00gie = optarg;
+                               break;
+#endif
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+#ifdef GAPING_SECURITY_HOLE
+       if (pr00gie) {
+               /* won't need stdin */
+               close (STDIN_FILENO);      
+       }
+#endif /* GAPING_SECURITY_HOLE */
+
+
+       if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc))
+               bb_show_usage();
+
+       if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+               bb_perror_msg_and_die("socket");
+       x = 1;
+       if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
+               bb_perror_msg_and_die ("reuseaddr failed");
+       address.sin_family = AF_INET;
+
+       if (lport != 0) {
+               memset(&address.sin_addr, 0, sizeof(address.sin_addr));
+               address.sin_port = lport;
+
+               if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
+                       bb_perror_msg_and_die("bind");
+       }
+
+       if (do_listen) {
+               socklen_t addrlen = sizeof(address);
+
+               if (listen(sfd, 1) < 0)
+                       bb_perror_msg_and_die("listen");
+
+               if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0)
+                       bb_perror_msg_and_die("accept");
+
+               close(sfd);
+               sfd = tmpfd;
+       } else {
+               hostinfo = xgethostbyname(argv[optind]);
+
+               address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
+               address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0);
+
+               if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
+                       bb_perror_msg_and_die("connect");
+       }
+
+#ifdef GAPING_SECURITY_HOLE
+       /* -e given? */
+       if (pr00gie) {
+               dup2(sfd, 0);
+               close(sfd);
+               dup2 (0, 1);
+               dup2 (0, 2);
+               execl (pr00gie, pr00gie, NULL);
+               /* Don't print stuff or it will go over the wire.... */
+               _exit(-1);
+       }
+#endif /* GAPING_SECURITY_HOLE */
+
+
+       FD_ZERO(&readfds);
+       FD_SET(sfd, &readfds);
+       FD_SET(STDIN_FILENO, &readfds);
+
+       while (1) {
+               int fd;
+               int ofd;
+               int nread;
+
+               testfds = readfds;
+
+               if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
+                       bb_perror_msg_and_die("select");
+
+               for (fd = 0; fd < FD_SETSIZE; fd++) {
+                       if (FD_ISSET(fd, &testfds)) {
+                               if ((nread = safe_read(fd, buf, sizeof(buf))) < 0)
+                                       bb_perror_msg_and_die("read");
+
+                               if (fd == sfd) {
+                                       if (nread == 0)
+                                               exit(0);
+                                       ofd = STDOUT_FILENO;
+                               } else {
+                                       if (nread == 0)
+                                               shutdown(sfd, 1);
+                                       ofd = sfd;
+                               }
+
+                               if (bb_full_write(ofd, buf, nread) < 0)
+                                       bb_perror_msg_and_die("write");
+                               if (delay > 0) {
+                                       sleep(delay);
+                               }
+                       }
+               }
+       }
+}
diff --git a/busybox/networking/netstat.c b/busybox/networking/netstat.c
new file mode 100644 (file)
index 0000000..bc1ed05
--- /dev/null
@@ -0,0 +1,661 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini netstat implementation(s) for busybox
+ * based in part on the netstat implementation from net-tools.
+ *
+ * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "inet_common.h"
+#include "busybox.h"
+#include "pwd_.h"
+
+#ifdef CONFIG_ROUTE
+extern void displayroutes(int noresolve, int netstatfmt);
+#endif
+
+#define NETSTAT_CONNECTED      0x01
+#define NETSTAT_LISTENING      0x02
+#define NETSTAT_NUMERIC                0x04
+#define NETSTAT_TCP                    0x10
+#define NETSTAT_UDP                    0x20
+#define NETSTAT_RAW                    0x40
+#define NETSTAT_UNIX           0x80
+
+static int flags = NETSTAT_CONNECTED |
+                       NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
+
+#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
+#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
+#define PROGNAME_WIDTH2(s) #s
+
+#define PRG_HASH_SIZE 211
+
+enum {
+    TCP_ESTABLISHED = 1,
+    TCP_SYN_SENT,
+    TCP_SYN_RECV,
+    TCP_FIN_WAIT1,
+    TCP_FIN_WAIT2,
+    TCP_TIME_WAIT,
+    TCP_CLOSE,
+    TCP_CLOSE_WAIT,
+    TCP_LAST_ACK,
+    TCP_LISTEN,
+    TCP_CLOSING                        /* now a valid state */
+};
+
+static const char * const tcp_state[] =
+{
+    "",
+    "ESTABLISHED",
+    "SYN_SENT",
+    "SYN_RECV",
+    "FIN_WAIT1",
+    "FIN_WAIT2",
+    "TIME_WAIT",
+    "CLOSE",
+    "CLOSE_WAIT",
+    "LAST_ACK",
+    "LISTEN",
+    "CLOSING"
+};
+
+typedef enum {
+    SS_FREE = 0,               /* not allocated                */
+    SS_UNCONNECTED,            /* unconnected to any socket    */
+    SS_CONNECTING,             /* in process of connecting     */
+    SS_CONNECTED,              /* connected to socket          */
+    SS_DISCONNECTING           /* in process of disconnecting  */
+} socket_state;
+
+#define SO_ACCEPTCON    (1<<16)        /* performed a listen           */
+#define SO_WAITDATA     (1<<17)        /* wait data to read            */
+#define SO_NOSPACE      (1<<18)        /* no space to write            */
+
+static char *itoa(unsigned int i)
+{
+       /* 21 digits plus null terminator, good for 64-bit or smaller ints */
+       static char local[22];
+       char *p = &local[21];
+       *p-- = '\0';
+       do {
+               *p-- = '0' + i % 10;
+               i /= 10;
+       } while (i > 0);
+       return p + 1;
+}
+
+static char *get_sname(int port, const char *proto, int num)
+{
+       char *str=itoa(ntohs(port));
+       if (num) {
+       } else {
+               struct servent *se=getservbyport(port,proto);
+               if (se)
+                       str=se->s_name;
+       }
+       if (!port) {
+               str="*";
+       }
+       return str;
+}
+
+static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
+{
+       char *port_name;
+
+#ifdef CONFIG_FEATURE_IPV6
+       if (addr->sa_family == AF_INET6) {
+               INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
+                                          (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0);
+       } else
+#endif
+       {
+       INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
+               0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
+               0xffffffff);
+       }
+       port_name=get_sname(htons(port), proto, numeric);
+       if ((strlen(ip_port) + strlen(port_name)) > 22)
+               ip_port[22 - strlen(port_name)] = '\0';
+       ip_port+=strlen(ip_port);
+       strcat(ip_port, ":");
+       strcat(ip_port, port_name);
+}
+
+static void tcp_do_one(int lnr, const char *line)
+{
+       char local_addr[64], rem_addr[64];
+       const char *state_str;
+       char more[512];
+       int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#ifdef CONFIG_FEATURE_IPV6
+       struct sockaddr_in6 localaddr, remaddr;
+       char addr6[INET6_ADDRSTRLEN];
+       struct in6_addr in6;
+#else
+       struct sockaddr_in localaddr, remaddr;
+#endif
+       unsigned long rxq, txq, time_len, retr, inode;
+
+       if (lnr == 0)
+               return;
+
+       more[0] = '\0';
+       num = sscanf(line,
+                                "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+                                &d, local_addr, &local_port,
+                                rem_addr, &rem_port, &state,
+                                &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+       if (strlen(local_addr) > 8) {
+#ifdef CONFIG_FEATURE_IPV6
+               sscanf(local_addr, "%08X%08X%08X%08X",
+                          &in6.s6_addr32[0], &in6.s6_addr32[1],
+                          &in6.s6_addr32[2], &in6.s6_addr32[3]);
+               inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+               sscanf(rem_addr, "%08X%08X%08X%08X",
+                          &in6.s6_addr32[0], &in6.s6_addr32[1],
+                          &in6.s6_addr32[2], &in6.s6_addr32[3]);
+               inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+               localaddr.sin6_family = AF_INET6;
+               remaddr.sin6_family = AF_INET6;
+#endif
+       } else {
+               sscanf(local_addr, "%X",
+                          &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+               sscanf(rem_addr, "%X",
+                          &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+               ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+               ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+       }
+
+       if (num < 10) {
+               bb_error_msg("warning, got bogus tcp line.");
+               return;
+       }
+       state_str = tcp_state[state];
+       if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
+               (!rem_port && (flags&NETSTAT_LISTENING)))
+       {
+               snprint_ip_port(local_addr, sizeof(local_addr),
+                                               (struct sockaddr *) &localaddr, local_port,
+                                               "tcp", flags&NETSTAT_NUMERIC);
+
+               snprint_ip_port(rem_addr, sizeof(rem_addr),
+                                               (struct sockaddr *) &remaddr, rem_port,
+                                               "tcp", flags&NETSTAT_NUMERIC);
+
+               printf("tcp   %6ld %6ld %-23s %-23s %-12s\n",
+                          rxq, txq, local_addr, rem_addr, state_str);
+
+       }
+}
+
+static void udp_do_one(int lnr, const char *line)
+{
+       char local_addr[64], rem_addr[64];
+       char *state_str, more[512];
+       int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#ifdef CONFIG_FEATURE_IPV6
+       struct sockaddr_in6 localaddr, remaddr;
+       char addr6[INET6_ADDRSTRLEN];
+       struct in6_addr in6;
+#else
+       struct sockaddr_in localaddr, remaddr;
+#endif
+       unsigned long rxq, txq, time_len, retr, inode;
+
+       if (lnr == 0)
+               return;
+
+       more[0] = '\0';
+       num = sscanf(line,
+                                "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+                                &d, local_addr, &local_port,
+                                rem_addr, &rem_port, &state,
+                                &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+       if (strlen(local_addr) > 8) {
+#ifdef CONFIG_FEATURE_IPV6
+        /* Demangle what the kernel gives us */
+               sscanf(local_addr, "%08X%08X%08X%08X",
+                          &in6.s6_addr32[0], &in6.s6_addr32[1],
+                          &in6.s6_addr32[2], &in6.s6_addr32[3]);
+               inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+               sscanf(rem_addr, "%08X%08X%08X%08X",
+                          &in6.s6_addr32[0], &in6.s6_addr32[1],
+                          &in6.s6_addr32[2], &in6.s6_addr32[3]);
+               inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+               localaddr.sin6_family = AF_INET6;
+               remaddr.sin6_family = AF_INET6;
+#endif
+       } else {
+               sscanf(local_addr, "%X",
+                          &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+               sscanf(rem_addr, "%X",
+                          &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+               ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+               ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+       }
+
+       if (num < 10) {
+               bb_error_msg("warning, got bogus udp line.");
+               return;
+       }
+       switch (state) {
+               case TCP_ESTABLISHED:
+                       state_str = "ESTABLISHED";
+                       break;
+
+               case TCP_CLOSE:
+                       state_str = "";
+                       break;
+
+               default:
+                       state_str = "UNKNOWN";
+                       break;
+       }
+
+#ifdef CONFIG_FEATURE_IPV6
+# define notnull(A) (((A.sin6_family == AF_INET6) &&            \
+                                        ((A.sin6_addr.s6_addr32[0]) ||            \
+                                         (A.sin6_addr.s6_addr32[1]) ||            \
+                                         (A.sin6_addr.s6_addr32[2]) ||            \
+                                         (A.sin6_addr.s6_addr32[3]))) ||          \
+                                       ((A.sin6_family == AF_INET) &&             \
+                                        ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#else
+# define notnull(A) (A.sin_addr.s_addr)
+#endif
+       if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
+               (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
+       {
+               snprint_ip_port(local_addr, sizeof(local_addr),
+                                               (struct sockaddr *) &localaddr, local_port,
+                                               "udp", flags&NETSTAT_NUMERIC);
+
+               snprint_ip_port(rem_addr, sizeof(rem_addr),
+                                               (struct sockaddr *) &remaddr, rem_port,
+                                               "udp", flags&NETSTAT_NUMERIC);
+
+               printf("udp   %6ld %6ld %-23s %-23s %-12s\n",
+                          rxq, txq, local_addr, rem_addr, state_str);
+
+       }
+}
+
+static void raw_do_one(int lnr, const char *line)
+{
+       char local_addr[64], rem_addr[64];
+       char *state_str, more[512];
+       int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#ifdef CONFIG_FEATURE_IPV6
+       struct sockaddr_in6 localaddr, remaddr;
+       char addr6[INET6_ADDRSTRLEN];
+       struct in6_addr in6;
+#else
+       struct sockaddr_in localaddr, remaddr;
+#endif
+       unsigned long rxq, txq, time_len, retr, inode;
+
+       if (lnr == 0)
+               return;
+
+       more[0] = '\0';
+       num = sscanf(line,
+                                "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+                                &d, local_addr, &local_port,
+                                rem_addr, &rem_port, &state,
+                                &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+       if (strlen(local_addr) > 8) {
+#ifdef CONFIG_FEATURE_IPV6
+               sscanf(local_addr, "%08X%08X%08X%08X",
+                          &in6.s6_addr32[0], &in6.s6_addr32[1],
+                          &in6.s6_addr32[2], &in6.s6_addr32[3]);
+               inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+               sscanf(rem_addr, "%08X%08X%08X%08X",
+                          &in6.s6_addr32[0], &in6.s6_addr32[1],
+                          &in6.s6_addr32[2], &in6.s6_addr32[3]);
+               inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+               inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+               localaddr.sin6_family = AF_INET6;
+               remaddr.sin6_family = AF_INET6;
+#endif
+       } else {
+               sscanf(local_addr, "%X",
+                          &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+               sscanf(rem_addr, "%X",
+                          &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+               ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+               ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+       }
+
+       if (num < 10) {
+               bb_error_msg("warning, got bogus raw line.");
+               return;
+       }
+       state_str=itoa(state);
+
+#ifdef CONFIG_FEATURE_IPV6
+# define notnull(A) (((A.sin6_family == AF_INET6) &&            \
+                                        ((A.sin6_addr.s6_addr32[0]) ||            \
+                                         (A.sin6_addr.s6_addr32[1]) ||            \
+                                         (A.sin6_addr.s6_addr32[2]) ||            \
+                                         (A.sin6_addr.s6_addr32[3]))) ||          \
+                                       ((A.sin6_family == AF_INET) &&             \
+                                        ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#else
+# define notnull(A) (A.sin_addr.s_addr)
+#endif
+       if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
+               (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
+       {
+               snprint_ip_port(local_addr, sizeof(local_addr),
+                                               (struct sockaddr *) &localaddr, local_port,
+                                               "raw", flags&NETSTAT_NUMERIC);
+
+               snprint_ip_port(rem_addr, sizeof(rem_addr),
+                                               (struct sockaddr *) &remaddr, rem_port,
+                                               "raw", flags&NETSTAT_NUMERIC);
+
+               printf("raw   %6ld %6ld %-23s %-23s %-12s\n",
+                          rxq, txq, local_addr, rem_addr, state_str);
+
+       }
+}
+
+#define HAS_INODE 1
+
+static void unix_do_one(int nr, const char *line)
+{
+       static int has = 0;
+       char path[PATH_MAX], ss_flags[32];
+       char *ss_proto, *ss_state, *ss_type;
+       int num, state, type, inode;
+       void *d;
+       unsigned long refcnt, proto, unix_flags;
+
+       if (nr == 0) {
+               if (strstr(line, "Inode"))
+                       has |= HAS_INODE;
+               return;
+       }
+       path[0] = '\0';
+       num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
+                                &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
+       if (num < 6) {
+               bb_error_msg("warning, got bogus unix line.");
+               return;
+       }
+       if (!(has & HAS_INODE))
+               snprintf(path,sizeof(path),"%d",inode);
+
+       if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
+               if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
+                       if (!(flags&NETSTAT_LISTENING))
+                               return;
+               } else {
+                       if (!(flags&NETSTAT_CONNECTED))
+                               return;
+               }
+       }
+
+       switch (proto) {
+               case 0:
+                       ss_proto = "unix";
+                       break;
+
+               default:
+                       ss_proto = "??";
+       }
+
+       switch (type) {
+               case SOCK_STREAM:
+                       ss_type = "STREAM";
+                       break;
+
+               case SOCK_DGRAM:
+                       ss_type = "DGRAM";
+                       break;
+
+               case SOCK_RAW:
+                       ss_type = "RAW";
+                       break;
+
+               case SOCK_RDM:
+                       ss_type = "RDM";
+                       break;
+
+               case SOCK_SEQPACKET:
+                       ss_type = "SEQPACKET";
+                       break;
+
+               default:
+                       ss_type = "UNKNOWN";
+       }
+
+       switch (state) {
+               case SS_FREE:
+                       ss_state = "FREE";
+                       break;
+
+               case SS_UNCONNECTED:
+                       /*
+                        * Unconnected sockets may be listening
+                        * for something.
+                        */
+                       if (unix_flags & SO_ACCEPTCON) {
+                               ss_state = "LISTENING";
+                       } else {
+                               ss_state = "";
+                       }
+                       break;
+
+               case SS_CONNECTING:
+                       ss_state = "CONNECTING";
+                       break;
+
+               case SS_CONNECTED:
+                       ss_state = "CONNECTED";
+                       break;
+
+               case SS_DISCONNECTING:
+                       ss_state = "DISCONNECTING";
+                       break;
+
+               default:
+                       ss_state = "UNKNOWN";
+       }
+
+       strcpy(ss_flags, "[ ");
+       if (unix_flags & SO_ACCEPTCON)
+               strcat(ss_flags, "ACC ");
+       if (unix_flags & SO_WAITDATA)
+               strcat(ss_flags, "W ");
+       if (unix_flags & SO_NOSPACE)
+               strcat(ss_flags, "N ");
+
+       strcat(ss_flags, "]");
+
+       printf("%-5s %-6ld %-11s %-10s %-13s ",
+                  ss_proto, refcnt, ss_flags, ss_type, ss_state);
+       if (has & HAS_INODE)
+               printf("%-6d ",inode);
+       else
+               printf("-      ");
+       puts(path);
+}
+
+#define _PATH_PROCNET_UDP "/proc/net/udp"
+#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
+#define _PATH_PROCNET_TCP "/proc/net/tcp"
+#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
+#define _PATH_PROCNET_RAW "/proc/net/raw"
+#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
+#define _PATH_PROCNET_UNIX "/proc/net/unix"
+
+static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
+{
+       char buffer[8192];
+       int lnr = 0;
+       FILE *procinfo;
+
+       procinfo = fopen(file, "r");
+       if (procinfo == NULL) {
+               if (errno != ENOENT) {
+                       perror(file);
+               } else {
+               bb_error_msg("no support for `%s' on this system.", name);
+               }
+       } else {
+               do {
+                       if (fgets(buffer, sizeof(buffer), procinfo))
+                               (proc)(lnr++, buffer);
+               } while (!feof(procinfo));
+               fclose(procinfo);
+       }
+}
+
+/*
+ * Our main function.
+ */
+
+int netstat_main(int argc, char **argv)
+{
+       int opt;
+       int new_flags=0;
+       int showroute = 0, extended = 0;
+#ifdef CONFIG_FEATURE_IPV6
+       int inet=1;
+       int inet6=1;
+#else
+# define inet 1
+# define inet6 0
+#endif
+       while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
+               switch (opt) {
+               case 'l':
+                       flags &= ~NETSTAT_CONNECTED;
+                       flags |= NETSTAT_LISTENING;
+                       break;
+               case 'a':
+                       flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
+                       break;
+               case 'n':
+                       flags |= NETSTAT_NUMERIC;
+                       break;
+               case 'r':
+                       showroute = 1;
+                       break;
+               case 'e':
+                       extended = 1;
+                       break;
+               case 't':
+                       new_flags |= NETSTAT_TCP;
+                       break;
+               case 'u':
+                       new_flags |= NETSTAT_UDP;
+                       break;
+               case 'w':
+                       new_flags |= NETSTAT_RAW;
+                       break;
+               case 'x':
+                       new_flags |= NETSTAT_UNIX;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       if ( showroute ) {
+#ifdef CONFIG_ROUTE
+               displayroutes ( flags & NETSTAT_NUMERIC, !extended );
+               return 0;
+#else
+               bb_error_msg_and_die( "-r (display routing table) is not compiled in." );
+#endif
+       }
+
+       if (new_flags) {
+               flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
+               flags |= new_flags;
+       }
+       if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
+               printf("Active Internet connections "); /* xxx */
+
+               if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
+                       printf("(servers and established)");
+               else {
+                       if (flags&NETSTAT_LISTENING)
+                               printf("(only servers)");
+                       else
+                               printf("(w/o servers)");
+               }
+               printf("\nProto Recv-Q Send-Q Local Address           Foreign Address         State      \n");
+       }
+       if (inet && flags&NETSTAT_TCP)
+               do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
+#ifdef CONFIG_FEATURE_IPV6
+       if (inet6 && flags&NETSTAT_TCP)
+               do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one);
+#endif
+       if (inet && flags&NETSTAT_UDP)
+               do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
+#ifdef CONFIG_FEATURE_IPV6
+       if (inet6 && flags&NETSTAT_UDP)
+               do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one);
+#endif
+       if (inet && flags&NETSTAT_RAW)
+               do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
+#ifdef CONFIG_FEATURE_IPV6
+       if (inet6 && flags&NETSTAT_RAW)
+               do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one);
+#endif
+       if (flags&NETSTAT_UNIX) {
+               printf("Active UNIX domain sockets ");
+               if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
+                       printf("(servers and established)");
+               else {
+                       if (flags&NETSTAT_LISTENING)
+                               printf("(only servers)");
+                       else
+                               printf("(w/o servers)");
+               }
+
+               printf("\nProto RefCnt Flags       Type       State         I-Node Path\n");
+               do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
+       }
+       return 0;
+}
diff --git a/busybox/networking/nslookup.c b/busybox/networking/nslookup.c
new file mode 100644 (file)
index 0000000..936fa33
--- /dev/null
@@ -0,0 +1,206 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini nslookup implementation for busybox
+ *
+ * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * Correct default name server display and explicit name server option
+ * added by Ben Zeckel <bzeckel@hmc.edu> June 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <stdint.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <arpa/inet.h>
+#include "busybox.h"
+
+/*
+ |  I'm only implementing non-interactive mode;
+ |  I totally forgot nslookup even had an interactive mode.
+ */
+
+/* only works for IPv4 */
+static int addr_fprint(char *addr)
+{
+       uint8_t split[4];
+       uint32_t ip;
+       uint32_t *x = (uint32_t *) addr;
+
+       ip = ntohl(*x);
+       split[0] = (ip & 0xff000000) >> 24;
+       split[1] = (ip & 0x00ff0000) >> 16;
+       split[2] = (ip & 0x0000ff00) >> 8;
+       split[3] = (ip & 0x000000ff);
+       printf("%d.%d.%d.%d", split[0], split[1], split[2], split[3]);
+       return 0;
+}
+
+/* takes the NULL-terminated array h_addr_list, and
+ * prints its contents appropriately
+ */
+static int addr_list_fprint(char **h_addr_list)
+{
+       int i, j;
+       char *addr_string = (h_addr_list[1])
+               ? "Addresses: " : "Address:   ";
+
+       printf("%s ", addr_string);
+       for (i = 0, j = 0; h_addr_list[i]; i++, j++) {
+               addr_fprint(h_addr_list[i]);
+
+               /* real nslookup does this */
+               if (j == 4) {
+                       if (h_addr_list[i + 1]) {
+                               printf("\n          ");
+                       }
+                       j = 0;
+               } else {
+                       if (h_addr_list[i + 1]) {
+                               printf(", ");
+                       }
+               }
+
+       }
+       printf("\n");
+       return 0;
+}
+
+/* print the results as nslookup would */
+static struct hostent *hostent_fprint(struct hostent *host, const char *server_host)
+{
+       if (host) {
+               printf("%s     %s\n", server_host, host->h_name);
+               addr_list_fprint(host->h_addr_list);
+       } else {
+               printf("*** Unknown host\n");
+       }
+       return host;
+}
+
+/* changes a c-string matching the perl regex \d+\.\d+\.\d+\.\d+
+ * into a uint32_t
+ */
+static uint32_t str_to_addr(const char *addr)
+{
+       uint32_t split[4];
+       uint32_t ip;
+
+       sscanf(addr, "%d.%d.%d.%d",
+                  &split[0], &split[1], &split[2], &split[3]);
+
+       /* assuming sscanf worked */
+       ip = (split[0] << 24) |
+               (split[1] << 16) | (split[2] << 8) | (split[3]);
+
+       return htonl(ip);
+}
+
+/* gethostbyaddr wrapper */
+static struct hostent *gethostbyaddr_wrapper(const char *address)
+{
+       struct in_addr addr;
+
+       addr.s_addr = str_to_addr(address);
+       return gethostbyaddr((char *) &addr, 4, AF_INET);       /* IPv4 only for now */
+}
+
+/* lookup the default nameserver and display it */
+static inline void server_print(void)
+{
+       struct sockaddr_in def = _res.nsaddr_list[0];
+       char *ip = inet_ntoa(def.sin_addr);
+
+       hostent_fprint(gethostbyaddr_wrapper(ip), "Server:");
+       printf("\n");
+}
+
+/* alter the global _res nameserver structure to use
+   an explicit dns server instead of what is in /etc/resolv.h */
+static inline void set_default_dns(char *server)
+{
+       struct in_addr server_in_addr;
+
+       if(inet_aton(server,&server_in_addr))
+       {
+               _res.nscount = 1;
+               _res.nsaddr_list[0].sin_addr = server_in_addr;
+       }
+}
+
+/* naive function to check whether char *s is an ip address */
+static int is_ip_address(const char *s)
+{
+       while (*s) {
+               if ((isdigit(*s)) || (*s == '.')) {
+                       s++;
+                       continue;
+               }
+               return 0;
+       }
+       return 1;
+}
+
+/* ________________________________________________________________________ */
+int nslookup_main(int argc, char **argv)
+{
+       struct hostent *host;
+
+       /*
+       * initialize DNS structure _res used in printing the default
+       * name server and in the explicit name server option feature.
+       */
+
+       res_init();
+
+       /*
+       * We allow 1 or 2 arguments.
+       * The first is the name to be looked up and the second is an
+       * optional DNS server with which to do the lookup.
+       * More than 3 arguments is an error to follow the pattern of the
+       * standard nslookup
+       */
+
+       if (argc < 2 || *argv[1]=='-' || argc > 3)
+               bb_show_usage();
+       else if(argc == 3)
+               set_default_dns(argv[2]);
+
+       server_print();
+       if (is_ip_address(argv[1])) {
+               host = gethostbyaddr_wrapper(argv[1]);
+       } else {
+               host = xgethostbyname(argv[1]);
+       }
+       hostent_fprint(host, "Name:  ");
+       if (host) {
+               return EXIT_SUCCESS;
+       }
+       return EXIT_FAILURE;
+}
+
+/* $Id: nslookup.c,v 1.33 2004/10/13 07:25:01 andersen Exp $ */
diff --git a/busybox/networking/ping.c b/busybox/networking/ping.c
new file mode 100644 (file)
index 0000000..50f3930
--- /dev/null
@@ -0,0 +1,472 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+
+static const int DEFDATALEN = 56;
+static const int MAXIPLEN = 60;
+static const int MAXICMPLEN = 76;
+static const int MAXPACKET = 65468;
+#define        MAX_DUP_CHK     (8 * 128)
+static const int MAXWAIT = 10;
+static const int PINGINTERVAL = 1;             /* second */
+
+#define O_QUIET         (1 << 0)
+
+#define        A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
+#define        B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
+#define        SET(bit)        (A(bit) |= B(bit))
+#define        CLR(bit)        (A(bit) &= (~B(bit)))
+#define        TST(bit)        (A(bit) & B(bit))
+
+static void ping(const char *host);
+
+/* common routines */
+static int in_cksum(unsigned short *buf, int sz)
+{
+       int nleft = sz;
+       int sum = 0;
+       unsigned short *w = buf;
+       unsigned short ans = 0;
+
+       while (nleft > 1) {
+               sum += *w++;
+               nleft -= 2;
+       }
+
+       if (nleft == 1) {
+               *(unsigned char *) (&ans) = *(unsigned char *) w;
+               sum += ans;
+       }
+
+       sum = (sum >> 16) + (sum & 0xFFFF);
+       sum += (sum >> 16);
+       ans = ~sum;
+       return (ans);
+}
+
+/* simple version */
+#ifndef CONFIG_FEATURE_FANCY_PING
+static char *hostname = NULL;
+static void noresp(int ign)
+{
+       printf("No response from %s\n", hostname);
+       exit(EXIT_FAILURE);
+}
+
+static void ping(const char *host)
+{
+       struct hostent *h;
+       struct sockaddr_in pingaddr;
+       struct icmp *pkt;
+       int pingsock, c;
+       char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+       pingsock = create_icmp_socket();
+
+       memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+       pingaddr.sin_family = AF_INET;
+       h = xgethostbyname(host);
+       memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
+       hostname = h->h_name;
+
+       pkt = (struct icmp *) packet;
+       memset(pkt, 0, sizeof(packet));
+       pkt->icmp_type = ICMP_ECHO;
+       pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
+
+       c = sendto(pingsock, packet, sizeof(packet), 0,
+                          (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
+
+       if (c < 0 || c != sizeof(packet))
+               bb_perror_msg_and_die("sendto");
+
+       signal(SIGALRM, noresp);
+       alarm(5);                                       /* give the host 5000ms to respond */
+       /* listen for replies */
+       while (1) {
+               struct sockaddr_in from;
+               size_t fromlen = sizeof(from);
+
+               if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
+                                                 (struct sockaddr *) &from, &fromlen)) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("recvfrom");
+                       continue;
+               }
+               if (c >= 76) {                  /* ip + icmp */
+                       struct iphdr *iphdr = (struct iphdr *) packet;
+
+                       pkt = (struct icmp *) (packet + (iphdr->ihl << 2));     /* skip ip hdr */
+                       if (pkt->icmp_type == ICMP_ECHOREPLY)
+                               break;
+               }
+       }
+       printf("%s is alive!\n", hostname);
+       return;
+}
+
+extern int ping_main(int argc, char **argv)
+{
+       argc--;
+       argv++;
+       if (argc < 1)
+               bb_show_usage();
+       ping(*argv);
+       return EXIT_SUCCESS;
+}
+
+#else /* ! CONFIG_FEATURE_FANCY_PING */
+/* full(er) version */
+static struct sockaddr_in pingaddr;
+static int pingsock = -1;
+static int datalen; /* intentionally uninitialized to work around gcc bug */
+
+static long ntransmitted, nreceived, nrepeats, pingcount;
+static int myid, options;
+static unsigned long tmin = ULONG_MAX, tmax, tsum;
+static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct hostent *hostent;
+
+static void sendping(int);
+static void pingstats(int);
+static void unpack(char *, int, struct sockaddr_in *);
+
+/**************************************************************************/
+
+static void pingstats(int junk)
+{
+       int status;
+
+       signal(SIGINT, SIG_IGN);
+
+       printf("\n--- %s ping statistics ---\n", hostent->h_name);
+       printf("%ld packets transmitted, ", ntransmitted);
+       printf("%ld packets received, ", nreceived);
+       if (nrepeats)
+               printf("%ld duplicates, ", nrepeats);
+       if (ntransmitted)
+               printf("%ld%% packet loss\n",
+                          (ntransmitted - nreceived) * 100 / ntransmitted);
+       if (nreceived)
+               printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
+                          tmin / 10, tmin % 10,
+                          (tsum / (nreceived + nrepeats)) / 10,
+                          (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
+       if (nreceived != 0)
+               status = EXIT_SUCCESS;
+       else
+               status = EXIT_FAILURE;
+       exit(status);
+}
+
+static void sendping(int junk)
+{
+       struct icmp *pkt;
+       int i;
+       char packet[datalen + 8];
+
+       pkt = (struct icmp *) packet;
+
+       pkt->icmp_type = ICMP_ECHO;
+       pkt->icmp_code = 0;
+       pkt->icmp_cksum = 0;
+       pkt->icmp_seq = ntransmitted++;
+       pkt->icmp_id = myid;
+       CLR(pkt->icmp_seq % MAX_DUP_CHK);
+
+       gettimeofday((struct timeval *) &packet[8], NULL);
+       pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
+
+       i = sendto(pingsock, packet, sizeof(packet), 0,
+                          (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
+
+       if (i < 0)
+               bb_perror_msg_and_die("sendto");
+       else if ((size_t)i != sizeof(packet))
+               bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
+                          (int)sizeof(packet));
+
+       signal(SIGALRM, sendping);
+       if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
+               alarm(PINGINTERVAL);
+       } else {                                        /* done, wait for the last ping to come back */
+               /* todo, don't necessarily need to wait so long... */
+               signal(SIGALRM, pingstats);
+               alarm(MAXWAIT);
+       }
+}
+
+static char *icmp_type_name (int id)
+{
+       switch (id) {
+       case ICMP_ECHOREPLY:            return "Echo Reply";
+       case ICMP_DEST_UNREACH:         return "Destination Unreachable";
+       case ICMP_SOURCE_QUENCH:        return "Source Quench";
+       case ICMP_REDIRECT:             return "Redirect (change route)";
+       case ICMP_ECHO:                         return "Echo Request";
+       case ICMP_TIME_EXCEEDED:        return "Time Exceeded";
+       case ICMP_PARAMETERPROB:        return "Parameter Problem";
+       case ICMP_TIMESTAMP:            return "Timestamp Request";
+       case ICMP_TIMESTAMPREPLY:       return "Timestamp Reply";
+       case ICMP_INFO_REQUEST:         return "Information Request";
+       case ICMP_INFO_REPLY:           return "Information Reply";
+       case ICMP_ADDRESS:                      return "Address Mask Request";
+       case ICMP_ADDRESSREPLY:         return "Address Mask Reply";
+       default:                                        return "unknown ICMP type";
+       }
+}
+
+static void unpack(char *buf, int sz, struct sockaddr_in *from)
+{
+       struct icmp *icmppkt;
+       struct iphdr *iphdr;
+       struct timeval tv, *tp;
+       int hlen, dupflag;
+       unsigned long triptime;
+
+       gettimeofday(&tv, NULL);
+
+       /* check IP header */
+       iphdr = (struct iphdr *) buf;
+       hlen = iphdr->ihl << 2;
+       /* discard if too short */
+       if (sz < (datalen + ICMP_MINLEN))
+               return;
+
+       sz -= hlen;
+       icmppkt = (struct icmp *) (buf + hlen);
+
+       if (icmppkt->icmp_id != myid)
+           return;                             /* not our ping */
+
+       if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
+           ++nreceived;
+               tp = (struct timeval *) icmppkt->icmp_data;
+
+               if ((tv.tv_usec -= tp->tv_usec) < 0) {
+                       --tv.tv_sec;
+                       tv.tv_usec += 1000000;
+               }
+               tv.tv_sec -= tp->tv_sec;
+
+               triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
+               tsum += triptime;
+               if (triptime < tmin)
+                       tmin = triptime;
+               if (triptime > tmax)
+                       tmax = triptime;
+
+               if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
+                       ++nrepeats;
+                       --nreceived;
+                       dupflag = 1;
+               } else {
+                       SET(icmppkt->icmp_seq % MAX_DUP_CHK);
+                       dupflag = 0;
+               }
+
+               if (options & O_QUIET)
+                       return;
+
+               printf("%d bytes from %s: icmp_seq=%u", sz,
+                          inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
+                          icmppkt->icmp_seq);
+               printf(" ttl=%d", iphdr->ttl);
+               printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
+               if (dupflag)
+                       printf(" (DUP!)");
+               printf("\n");
+       } else
+               if (icmppkt->icmp_type != ICMP_ECHO)
+                       bb_error_msg("Warning: Got ICMP %d (%s)",
+                                       icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
+}
+
+static void ping(const char *host)
+{
+       char packet[datalen + MAXIPLEN + MAXICMPLEN];
+       int sockopt;
+
+       pingsock = create_icmp_socket();
+
+       memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+       pingaddr.sin_family = AF_INET;
+       hostent = xgethostbyname(host);
+       if (hostent->h_addrtype != AF_INET)
+               bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
+
+       memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
+
+       /* enable broadcast pings */
+       sockopt = 1;
+       setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       /* set recv buf for broadcast pings */
+       sockopt = 48 * 1024;
+       setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       printf("PING %s (%s): %d data bytes\n",
+                  hostent->h_name,
+                  inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
+                  datalen);
+
+       signal(SIGINT, pingstats);
+
+       /* start the ping's going ... */
+       sendping(0);
+
+       /* listen for replies */
+       while (1) {
+               struct sockaddr_in from;
+               socklen_t fromlen = (socklen_t) sizeof(from);
+               int c;
+
+               if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
+                                                 (struct sockaddr *) &from, &fromlen)) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("recvfrom");
+                       continue;
+               }
+               unpack(packet, c, &from);
+               if (pingcount > 0 && nreceived >= pingcount)
+                       break;
+       }
+       pingstats(0);
+}
+
+extern int ping_main(int argc, char **argv)
+{
+       char *thisarg;
+
+       datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+
+       argc--;
+       argv++;
+       options = 0;
+       /* Parse any options */
+       while (argc >= 1 && **argv == '-') {
+               thisarg = *argv;
+               thisarg++;
+               switch (*thisarg) {
+               case 'q':
+                       options |= O_QUIET;
+                       break;
+               case 'c':
+                       if (--argc <= 0)
+                               bb_show_usage();
+                       argv++;
+                       pingcount = atoi(*argv);
+                       break;
+               case 's':
+                       if (--argc <= 0)
+                               bb_show_usage();
+                       argv++;
+                       datalen = atoi(*argv);
+                       break;
+               default:
+                       bb_show_usage();
+               }
+               argc--;
+               argv++;
+       }
+       if (argc < 1)
+               bb_show_usage();
+
+       myid = getpid() & 0xFFFF;
+       ping(*argv);
+       return EXIT_SUCCESS;
+}
+#endif /* ! CONFIG_FEATURE_FANCY_PING */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/networking/ping6.c b/busybox/networking/ping6.c
new file mode 100644 (file)
index 0000000..72867f3
--- /dev/null
@@ -0,0 +1,515 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $Id: ping6.c,v 1.6 2004/03/15 08:28:48 andersen Exp $
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * This version is an adaptation of ping.c from busybox.
+ * The code was modified by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>                            /* offsetof */
+#include "busybox.h"
+
+static const int DEFDATALEN = 56;
+static const int MAXIPLEN = 60;
+static const int MAXICMPLEN = 76;
+static const int MAXPACKET = 65468;
+#define        MAX_DUP_CHK     (8 * 128)
+static const int MAXWAIT = 10;
+static const int PINGINTERVAL = 1;             /* second */
+
+#define O_QUIET         (1 << 0)
+#define O_VERBOSE       (1 << 1)
+
+#define        A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
+#define        B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
+#define        SET(bit)        (A(bit) |= B(bit))
+#define        CLR(bit)        (A(bit) &= (~B(bit)))
+#define        TST(bit)        (A(bit) & B(bit))
+
+static void ping(const char *host);
+
+/* simple version */
+#ifndef CONFIG_FEATURE_FANCY_PING6
+static struct hostent *h;
+
+void noresp(int ign)
+{
+       printf("No response from %s\n", h->h_name);
+       exit(EXIT_FAILURE);
+}
+
+static void ping(const char *host)
+{
+       struct sockaddr_in6 pingaddr;
+       struct icmp6_hdr *pkt;
+       int pingsock, c;
+       int sockopt;
+       char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+       pingsock = create_icmp6_socket();
+
+       memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+       pingaddr.sin6_family = AF_INET6;
+       h = xgethostbyname2(host, AF_INET6);
+       memcpy(&pingaddr.sin6_addr, h->h_addr, sizeof(pingaddr.sin6_addr));
+
+       pkt = (struct icmp6_hdr *) packet;
+       memset(pkt, 0, sizeof(packet));
+       pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+
+       sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+       setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       c = sendto(pingsock, packet, sizeof(packet), 0,
+                          (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
+
+       if (c < 0 || c != sizeof(packet))
+               bb_perror_msg_and_die("sendto");
+
+       signal(SIGALRM, noresp);
+       alarm(5);                                       /* give the host 5000ms to respond */
+       /* listen for replies */
+       while (1) {
+               struct sockaddr_in6 from;
+               size_t fromlen = sizeof(from);
+
+               if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
+                                                 (struct sockaddr *) &from, &fromlen)) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("recvfrom");
+                       continue;
+               }
+               if (c >= 8) {                   /* icmp6_hdr */
+                       pkt = (struct icmp6_hdr *) packet;
+                       if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
+                               break;
+               }
+       }
+       printf("%s is alive!\n", h->h_name);
+       return;
+}
+
+extern int ping6_main(int argc, char **argv)
+{
+       argc--;
+       argv++;
+       if (argc < 1)
+               bb_show_usage();
+       ping(*argv);
+       return EXIT_SUCCESS;
+}
+
+#else /* ! CONFIG_FEATURE_FANCY_PING6 */
+/* full(er) version */
+static struct sockaddr_in6 pingaddr;
+static int pingsock = -1;
+static int datalen; /* intentionally uninitialized to work around gcc bug */
+static char* ifname;
+
+static long ntransmitted, nreceived, nrepeats, pingcount;
+static int myid, options;
+static unsigned long tmin = ULONG_MAX, tmax, tsum;
+static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+# ifdef CONFIG_FEATURE_FANCY_PING
+extern
+# endif
+       struct hostent *hostent;
+
+static void sendping(int);
+static void pingstats(int);
+static void unpack(char *, int, struct sockaddr_in6 *, int);
+
+/**************************************************************************/
+
+static void pingstats(int junk)
+{
+       int status;
+
+       signal(SIGINT, SIG_IGN);
+
+       printf("\n--- %s ping statistics ---\n", hostent->h_name);
+       printf("%ld packets transmitted, ", ntransmitted);
+       printf("%ld packets received, ", nreceived);
+       if (nrepeats)
+               printf("%ld duplicates, ", nrepeats);
+       if (ntransmitted)
+               printf("%ld%% packet loss\n",
+                          (ntransmitted - nreceived) * 100 / ntransmitted);
+       if (nreceived)
+               printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
+                          tmin / 10, tmin % 10,
+                          (tsum / (nreceived + nrepeats)) / 10,
+                          (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
+       if (nreceived != 0)
+               status = EXIT_SUCCESS;
+       else
+               status = EXIT_FAILURE;
+       exit(status);
+}
+
+static void sendping(int junk)
+{
+       struct icmp6_hdr *pkt;
+       int i;
+       char packet[datalen + 8];
+
+       pkt = (struct icmp6_hdr *) packet;
+
+       pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+       pkt->icmp6_code = 0;
+       pkt->icmp6_cksum = 0;
+       pkt->icmp6_seq = ntransmitted++;
+       pkt->icmp6_id = myid;
+       CLR(pkt->icmp6_seq % MAX_DUP_CHK);
+
+       gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
+
+       i = sendto(pingsock, packet, sizeof(packet), 0,
+                          (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
+
+       if (i < 0)
+               bb_perror_msg_and_die("sendto");
+       else if ((size_t)i != sizeof(packet))
+               bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
+                          (int)sizeof(packet));
+
+       signal(SIGALRM, sendping);
+       if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
+               alarm(PINGINTERVAL);
+       } else {                                        /* done, wait for the last ping to come back */
+               /* todo, don't necessarily need to wait so long... */
+               signal(SIGALRM, pingstats);
+               alarm(MAXWAIT);
+       }
+}
+
+static char *icmp6_type_name (int id)
+{
+       switch (id) {
+       case ICMP6_DST_UNREACH:                         return "Destination Unreachable";
+       case ICMP6_PACKET_TOO_BIG:                      return "Packet too big";
+       case ICMP6_TIME_EXCEEDED:                       return "Time Exceeded";
+       case ICMP6_PARAM_PROB:                          return "Parameter Problem";
+       case ICMP6_ECHO_REPLY:                          return "Echo Reply";
+       case ICMP6_ECHO_REQUEST:                        return "Echo Request";
+       case ICMP6_MEMBERSHIP_QUERY:            return "Membership Query";
+       case ICMP6_MEMBERSHIP_REPORT:           return "Membership Report";
+       case ICMP6_MEMBERSHIP_REDUCTION:        return "Membership Reduction";
+       default:                                                        return "unknown ICMP type";
+       }
+}
+
+static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
+{
+       struct icmp6_hdr *icmppkt;
+       struct timeval tv, *tp;
+       int dupflag;
+       unsigned long triptime;
+       char buf[INET6_ADDRSTRLEN];
+
+       gettimeofday(&tv, NULL);
+
+       /* discard if too short */
+       if (sz < (datalen + sizeof(struct icmp6_hdr)))
+               return;
+
+       icmppkt = (struct icmp6_hdr *) packet;
+
+       if (icmppkt->icmp6_id != myid)
+           return;                             /* not our ping */
+
+       if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
+           ++nreceived;
+               tp = (struct timeval *) &icmppkt->icmp6_data8[4];
+
+               if ((tv.tv_usec -= tp->tv_usec) < 0) {
+                       --tv.tv_sec;
+                       tv.tv_usec += 1000000;
+               }
+               tv.tv_sec -= tp->tv_sec;
+
+               triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
+               tsum += triptime;
+               if (triptime < tmin)
+                       tmin = triptime;
+               if (triptime > tmax)
+                       tmax = triptime;
+
+               if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) {
+                       ++nrepeats;
+                       --nreceived;
+                       dupflag = 1;
+               } else {
+                       SET(icmppkt->icmp6_seq % MAX_DUP_CHK);
+                       dupflag = 0;
+               }
+
+               if (options & O_QUIET)
+                       return;
+
+               printf("%d bytes from %s: icmp6_seq=%u", sz,
+                          inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
+                                                buf, sizeof(buf)),
+                          icmppkt->icmp6_seq);
+               printf(" ttl=%d time=%lu.%lu ms", hoplimit,
+                          triptime / 10, triptime % 10);
+               if (dupflag)
+                       printf(" (DUP!)");
+               printf("\n");
+       } else
+               if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
+                       bb_error_msg("Warning: Got ICMP %d (%s)",
+                                       icmppkt->icmp6_type, icmp6_type_name (icmppkt->icmp6_type));
+}
+
+static void ping(const char *host)
+{
+       char packet[datalen + MAXIPLEN + MAXICMPLEN];
+       char buf[INET6_ADDRSTRLEN];
+       int sockopt;
+       struct msghdr msg;
+       struct sockaddr_in6 from;
+       struct iovec iov;
+       char control_buf[CMSG_SPACE(36)];
+
+       pingsock = create_icmp6_socket();
+
+       memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+       pingaddr.sin6_family = AF_INET6;
+       hostent = xgethostbyname2(host, AF_INET6);
+       if (hostent->h_addrtype != AF_INET6)
+               bb_error_msg_and_die("unknown address type; only AF_INET6 is currently supported.");
+
+       memcpy(&pingaddr.sin6_addr, hostent->h_addr, sizeof(pingaddr.sin6_addr));
+
+#ifdef ICMP6_FILTER
+       {
+               struct icmp6_filter filt;
+               if (!(options & O_VERBOSE)) {
+                       ICMP6_FILTER_SETBLOCKALL(&filt);
+#if 0
+                       if ((options & F_FQDN) || (options & F_FQDNOLD) ||
+                               (options & F_NODEADDR) || (options & F_SUPTYPES))
+                               ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
+                       else
+#endif
+                               ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+               } else {
+                       ICMP6_FILTER_SETPASSALL(&filt);
+               }
+               if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+                                          sizeof(filt)) < 0)
+                       bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
+       }
+#endif /*ICMP6_FILTER*/
+
+       /* enable broadcast pings */
+       sockopt = 1;
+       setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       /* set recv buf for broadcast pings */
+       sockopt = 48 * 1024;
+       setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+       setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       sockopt = 1;
+       setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, (char *) &sockopt,
+                          sizeof(sockopt));
+
+       if (ifname) {
+               if ((pingaddr.sin6_scope_id = if_nametoindex(ifname)) == 0)
+                       bb_error_msg_and_die("%s: invalid interface name", ifname);
+       }
+
+       printf("PING %s (%s): %d data bytes\n",
+                  hostent->h_name,
+                          inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
+                                                buf, sizeof(buf)),
+                  datalen);
+
+       signal(SIGINT, pingstats);
+
+       /* start the ping's going ... */
+       sendping(0);
+
+       /* listen for replies */
+       msg.msg_name=&from;
+       msg.msg_namelen=sizeof(from);
+       msg.msg_iov=&iov;
+       msg.msg_iovlen=1;
+       msg.msg_control=control_buf;
+       iov.iov_base=packet;
+       iov.iov_len=sizeof(packet);
+       while (1) {
+               int c;
+               struct cmsghdr *cmsgptr = NULL;
+               int hoplimit=-1;
+               msg.msg_controllen=sizeof(control_buf);
+
+               if ((c = recvmsg(pingsock, &msg, 0)) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       bb_perror_msg("recvfrom");
+                       continue;
+               }
+               for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+                        cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+                       if (cmsgptr->cmsg_level == SOL_IPV6 &&
+                               cmsgptr->cmsg_type == IPV6_HOPLIMIT ) {
+                               hoplimit=*(int*)CMSG_DATA(cmsgptr);
+                       }
+               }
+               unpack(packet, c, &from, hoplimit);
+               if (pingcount > 0 && nreceived >= pingcount)
+                       break;
+       }
+       pingstats(0);
+}
+
+extern int ping6_main(int argc, char **argv)
+{
+       char *thisarg;
+
+       datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+
+       argc--;
+       argv++;
+       options = 0;
+       /* Parse any options */
+       while (argc >= 1 && **argv == '-') {
+               thisarg = *argv;
+               thisarg++;
+               switch (*thisarg) {
+               case 'v':
+                       options &= ~O_QUIET;
+                       options |= O_VERBOSE;
+                       break;
+               case 'q':
+                       options &= ~O_VERBOSE;
+                       options |= O_QUIET;
+                       break;
+               case 'c':
+                       if (--argc <= 0)
+                               bb_show_usage();
+                       argv++;
+                       pingcount = atoi(*argv);
+                       break;
+               case 's':
+                       if (--argc <= 0)
+                               bb_show_usage();
+                       argv++;
+                       datalen = atoi(*argv);
+                       break;
+               case 'I':
+                       if (--argc <= 0)
+                               bb_show_usage();
+                       argv++;
+                       ifname = *argv;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+               argc--;
+               argv++;
+       }
+       if (argc < 1)
+               bb_show_usage();
+
+       myid = getpid() & 0xFFFF;
+       ping(*argv);
+       return EXIT_SUCCESS;
+}
+#endif /* ! CONFIG_FEATURE_FANCY_PING6 */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/networking/route.c b/busybox/networking/route.c
new file mode 100644 (file)
index 0000000..5c092f4
--- /dev/null
@@ -0,0 +1,714 @@
+/* route
+ *
+ * Similar to the standard Unix route, but with only the necessary
+ * parts for AF_INET and AF_INET6
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ * Author of the original route:
+ *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *              (derived from FvK's 'route.c     1.70    01/04/94')
+ *
+ * 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.
+ *
+ * $Id: route.c,v 1.26 2004/03/19 23:27:08 mjn3 Exp $
+ *
+ * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
+ * adjustments by Larry Doolittle  <LRDoolittle@lbl.gov>
+ *
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+/* 2004/03/09  Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Rewritten to fix several bugs, add additional error checking, and
+ * remove ridiculous amounts of bloat.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <net/route.h>
+#include <net/if.h>
+#include "busybox.h"
+#include "inet_common.h"
+
+#ifndef RTF_UP
+/* Keep this in sync with /usr/src/linux/include/linux/route.h */
+#define RTF_UP          0x0001 /* route usable                 */
+#define RTF_GATEWAY     0x0002 /* destination is a gateway     */
+#define RTF_HOST        0x0004 /* host entry (net otherwise)   */
+#define RTF_REINSTATE   0x0008 /* reinstate route after tmout  */
+#define RTF_DYNAMIC     0x0010 /* created dyn. (by redirect)   */
+#define RTF_MODIFIED    0x0020 /* modified dyn. (by redirect)  */
+#define RTF_MTU         0x0040 /* specific MTU for this route  */
+#ifndef RTF_MSS
+#define RTF_MSS         RTF_MTU        /* Compatibility :-(            */
+#endif
+#define RTF_WINDOW      0x0080 /* per route window clamping    */
+#define RTF_IRTT        0x0100 /* Initial round trip time      */
+#define RTF_REJECT      0x0200 /* Reject route                 */
+#endif
+
+#if defined (SIOCADDRTOLD) || defined (RTF_IRTT)       /* route */
+#define HAVE_NEW_ADDRT 1
+#endif
+
+#if HAVE_NEW_ADDRT
+#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
+#define full_mask(x) (x)
+#else
+#define mask_in_addr(x) ((x).rt_genmask)
+#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
+#endif
+
+/* The RTACTION entries must agree with tbl_verb[] below! */
+#define RTACTION_ADD           1
+#define RTACTION_DEL           2
+
+/* For the various tbl_*[] arrays, the 1st byte is the offset to
+ * the next entry and the 2nd byte is return value. */
+
+#define NET_FLAG                       1
+#define HOST_FLAG                      2
+
+/* We remap '-' to '#' to avoid problems with getopt. */
+static const char tbl_hash_net_host[] =
+       "\007\001#net\0"
+/*     "\010\002#host\0" */
+       "\007\002#host"                         /* Since last, we can save a byte. */
+;
+
+#define KW_TAKES_ARG           020
+#define KW_SETS_FLAG           040
+
+#define KW_IPVx_METRIC         020
+#define KW_IPVx_NETMASK                021
+#define KW_IPVx_GATEWAY                022
+#define KW_IPVx_MSS                    023
+#define KW_IPVx_WINDOW         024
+#define KW_IPVx_IRTT           025
+#define KW_IPVx_DEVICE         026
+
+#define KW_IPVx_FLAG_ONLY      040
+#define KW_IPVx_REJECT         040
+#define KW_IPVx_MOD                    041
+#define KW_IPVx_DYN                    042
+#define KW_IPVx_REINSTATE      043
+
+static const char tbl_ipvx[] =
+       /* 020 is the "takes an arg" bit */
+#if HAVE_NEW_ADDRT
+       "\011\020metric\0"
+#endif
+       "\012\021netmask\0"
+       "\005\022gw\0"
+       "\012\022gateway\0"
+       "\006\023mss\0"
+       "\011\024window\0"
+#ifdef RTF_IRTT
+       "\007\025irtt\0"
+#endif
+       "\006\026dev\0"
+       "\011\026device\0"
+       /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
+#ifdef RTF_REJECT
+       "\011\040reject\0"
+#endif
+       "\006\041mod\0"
+       "\006\042dyn\0"
+/*     "\014\043reinstate\0" */
+       "\013\043reinstate"                     /* Since last, we can save a byte. */
+;
+
+static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+#ifdef RTF_REJECT
+       RTF_REJECT,
+#endif
+       RTF_MODIFIED,
+       RTF_DYNAMIC,
+       RTF_REINSTATE
+};
+
+static int kw_lookup(const char *kwtbl, char ***pargs)
+{
+       if (**pargs) {
+               do {
+                       if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
+                               *pargs += 1;
+                               if (kwtbl[1] & KW_TAKES_ARG) {
+                                       if (!**pargs) { /* No more args! */
+                                               bb_show_usage();
+                                       }
+                                       *pargs += 1; /* Calling routine will use args[-1]. */
+                               }
+                               return kwtbl[1];
+                       }
+                       kwtbl += *kwtbl;
+               } while (*kwtbl);
+       }
+       return 0;
+}
+
+/* Add or delete a route, depending on action. */
+
+static void INET_setroute(int action, char **args)
+{
+       struct rtentry rt;
+       const char *netmask;
+       int skfd, isnet, xflag;
+
+       assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
+
+       /* Grab the -net or -host options.  Remember they were transformed. */
+       xflag = kw_lookup(tbl_hash_net_host, &args);
+
+       /* If we did grab -net or -host, make sure we still have an arg left. */
+       if (*args == NULL) {
+               bb_show_usage();
+       }
+
+       /* Clean out the RTREQ structure. */
+       memset((char *) &rt, 0, sizeof(struct rtentry));
+
+       {
+               const char *target = *args++;
+
+               /* Prefer hostname lookup is -host flag (xflag==1) was given. */
+               isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+                                                        (xflag & HOST_FLAG));
+               if (isnet < 0) {
+                       bb_error_msg_and_die("resolving %s", target);
+               }
+
+       }
+
+       if (xflag) {            /* Reinit isnet if -net or -host was specified. */
+               isnet = (xflag & NET_FLAG);
+       }
+
+       /* Fill in the other fields. */
+       rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
+
+       netmask = bb_INET_default;
+
+       while (*args) {
+               int k = kw_lookup(tbl_ipvx, &args);
+               const char *args_m1 = args[-1];
+
+               if (k & KW_IPVx_FLAG_ONLY) {
+                       rt.rt_flags |= flags_ipvx[k & 3];
+                       continue;
+               }
+
+#if HAVE_NEW_ADDRT
+               if (k == KW_IPVx_METRIC) {
+                       rt.rt_metric = bb_xgetularg10(args_m1) + 1;
+                       continue;
+               }
+#endif
+
+               if (k == KW_IPVx_NETMASK) {
+                       struct sockaddr mask;
+
+                       if (mask_in_addr(rt)) {
+                               bb_show_usage();
+                       }
+
+                       netmask = args_m1;
+                       isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
+                       if (isnet < 0) {
+                               bb_error_msg_and_die("resolving %s", netmask);
+                       }
+                       rt.rt_genmask = full_mask(mask);
+                       continue;
+               }
+
+               if (k == KW_IPVx_GATEWAY) {
+                       if (rt.rt_flags & RTF_GATEWAY) {
+                               bb_show_usage();
+                       }
+
+                       isnet = INET_resolve(args_m1,
+                                                                (struct sockaddr_in *) &rt.rt_gateway, 1);
+                       rt.rt_flags |= RTF_GATEWAY;
+
+                       if (isnet) {
+                               if (isnet < 0) {
+                                       bb_error_msg_and_die("resolving %s", args_m1);
+                               }
+                               bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
+                       }
+                       continue;
+               }
+
+               if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
+                       rt.rt_flags |= RTF_MSS;
+                       rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768);
+                       continue;
+               }
+
+               if (k == KW_IPVx_WINDOW) {      /* Check valid window bounds. */
+                       rt.rt_flags |= RTF_WINDOW;
+                       rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX);
+                       continue;
+               }
+
+#ifdef RTF_IRTT
+               if (k == KW_IPVx_IRTT) {
+                       rt.rt_flags |= RTF_IRTT;
+                       rt.rt_irtt = bb_xgetularg10(args_m1);
+                       rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100);     /* FIXME */
+#if 0                                  /* FIXME: do we need to check anything of this? */
+                       if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
+                               bb_error_msg_and_die("bad irtt");
+                       }
+#endif
+                       continue;
+               }
+#endif
+
+               /* Device is special in that it can be the last arg specified
+                * and doesn't requre the dev/device keyword in that case. */
+               if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+                       /* Don't use args_m1 here since args may have changed! */
+                       rt.rt_dev = args[-1];
+                       continue;
+               }
+
+               /* Nothing matched. */
+               bb_show_usage();
+       }
+
+#ifdef RTF_REJECT
+       if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
+               rt.rt_dev = "lo";
+       }
+#endif
+
+       /* sanity checks.. */
+       if (mask_in_addr(rt)) {
+               unsigned long mask = mask_in_addr(rt);
+
+               mask = ~ntohl(mask);
+               if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
+                       bb_error_msg_and_die("netmask %.8x and host route conflict",
+                                                                (unsigned int) mask);
+               }
+               if (mask & (mask + 1)) {
+                       bb_error_msg_and_die("bogus netmask %s", netmask);
+               }
+               mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
+               if (mask & ~mask_in_addr(rt)) {
+                       bb_error_msg_and_die("netmask and route address conflict");
+               }
+       }
+
+       /* Fill out netmask if still unset */
+       if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
+               mask_in_addr(rt) = 0xffffffff;
+       }
+
+       /* Create a socket to the INET kernel. */
+       if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               bb_perror_msg_and_die("socket");
+       }
+
+       if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
+               bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
+       }
+
+       /* Don't bother closing, as we're exiting after we return anyway. */
+       /* close(skfd); */
+}
+
+#ifdef CONFIG_FEATURE_IPV6
+
+static void INET6_setroute(int action, char **args)
+{
+       struct sockaddr_in6 sa6;
+       struct in6_rtmsg rt;
+       int prefix_len, skfd;
+       const char *devname;
+
+       assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
+
+       {
+               /* We know args isn't NULL from the check in route_main. */
+               const char *target = *args++;
+
+               if (strcmp(target, "default") == 0) {
+                       prefix_len = 0;
+                       memset(&sa6, 0, sizeof(sa6));
+               } else {
+                       char *cp;
+                       if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
+                               *cp = 0;
+                               prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128);
+                       } else {
+                               prefix_len = 128;
+                       }
+                       if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+                               bb_error_msg_and_die("resolving %s", target);
+                       }
+               }
+       }
+
+       /* Clean out the RTREQ structure. */
+       memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
+
+       memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+       /* Fill in the other fields. */
+       rt.rtmsg_dst_len = prefix_len;
+       rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
+       rt.rtmsg_metric = 1;
+
+       devname = NULL;
+
+       while (*args) {
+               int k = kw_lookup(tbl_ipvx, &args);
+               const char *args_m1 = args[-1];
+
+               if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
+                       rt.rtmsg_flags |= flags_ipvx[k & 3];
+                       continue;
+               }
+
+               if (k == KW_IPVx_METRIC) {
+                       rt.rtmsg_metric = bb_xgetularg10(args_m1);
+                       continue;
+               }
+
+               if (k == KW_IPVx_GATEWAY) {
+                       if (rt.rtmsg_flags & RTF_GATEWAY) {
+                               bb_show_usage();
+                       }
+
+                       if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
+                               bb_error_msg_and_die("resolving %s", args_m1);
+                       }
+                       memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+                                  sizeof(struct in6_addr));
+                       rt.rtmsg_flags |= RTF_GATEWAY;
+                       continue;
+               }
+
+               /* Device is special in that it can be the last arg specified
+                * and doesn't requre the dev/device keyword in that case. */
+               if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+                       /* Don't use args_m1 here since args may have changed! */
+                       devname = args[-1];
+                       continue;
+               }
+
+               /* Nothing matched. */
+               bb_show_usage();
+       }
+
+       /* Create a socket to the INET6 kernel. */
+       if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+               bb_perror_msg_and_die("socket");
+       }
+
+       rt.rtmsg_ifindex = 0;
+
+       if (devname) {
+               struct ifreq ifr;
+               memset(&ifr, 0, sizeof(ifr));
+               strcpy(ifr.ifr_name, devname);
+
+               if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
+                       bb_perror_msg_and_die("SIOGIFINDEX");
+               }
+               rt.rtmsg_ifindex = ifr.ifr_ifindex;
+       }
+
+       /* Tell the kernel to accept this route. */
+       if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
+               bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
+       }
+
+       /* Don't bother closing, as we're exiting after we return anyway. */
+       /* close(skfd); */
+}
+#endif
+
+static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */
+       RTF_GATEWAY,
+       RTF_HOST,
+       RTF_REINSTATE,
+       RTF_DYNAMIC,
+       RTF_MODIFIED,
+#ifdef CONFIG_FEATURE_IPV6
+       RTF_DEFAULT,
+       RTF_ADDRCONF,
+       RTF_CACHE
+#endif
+};
+
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
+
+static const char flagchars[] =                /* Must agree with flagvals[]. */
+       "GHRDM"
+#ifdef CONFIG_FEATURE_IPV6
+       "DAC"
+#endif
+;
+
+static
+#ifndef CONFIG_FEATURE_IPV6
+__inline
+#endif
+void set_flags(char *flagstr, int flags)
+{
+       int i;
+
+       *flagstr++ = 'U';
+
+       for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) {
+               if (flags & flagvals[i]) {
+                       ++flagstr;
+               }
+       }
+}
+
+void displayroutes(int noresolve, int netstatfmt)
+{
+       char devname[64], flags[16], sdest[16], sgw[16];
+       unsigned long int d, g, m;
+       int flgs, ref, use, metric, mtu, win, ir;
+       struct sockaddr_in s_addr;
+       struct in_addr mask;
+
+       FILE *fp = bb_xfopen("/proc/net/route", "r");
+
+       bb_printf("Kernel IP routing table\n"
+                         "Destination     Gateway         Genmask"
+                         "         Flags %s Iface\n",
+                         netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
+
+       if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
+               goto ERROR;                /* Empty or missing line, or read error. */
+       }
+       while (1) {
+               int r;
+               r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
+                                  devname, &d, &g, &flgs, &ref, &use, &metric, &m,
+                                  &mtu, &win, &ir);
+               if (r != 11) {
+                       if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+                               break;
+                       }
+               ERROR:
+                       bb_error_msg_and_die("fscanf");
+               }
+
+               if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
+                       continue;
+               }
+
+               set_flags(flags, (flgs & IPV4_MASK));
+#ifdef RTF_REJECT
+               if (flgs & RTF_REJECT) {
+                       flags[0] = '!';
+               }
+#endif
+
+               memset(&s_addr, 0, sizeof(struct sockaddr_in));
+               s_addr.sin_family = AF_INET;
+               s_addr.sin_addr.s_addr = d;
+               INET_rresolve(sdest, sizeof(sdest), &s_addr,
+                                         (noresolve | 0x8000), m);     /* Default instead of *. */
+
+               s_addr.sin_addr.s_addr = g;
+               INET_rresolve(sgw, sizeof(sgw), &s_addr,
+                                         (noresolve | 0x4000), m);     /* Host instead of net. */
+
+               mask.s_addr = m;
+               bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+               if (netstatfmt) {
+                       bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
+               } else {
+                       bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
+               }
+       }
+}
+
+#ifdef CONFIG_FEATURE_IPV6
+
+static void INET6_displayroutes(int noresolve)
+{
+       char addr6[128], naddr6[128];
+       /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
+        * We read the non-delimited strings into the tail of the buffer
+        * using fscanf and then modify the buffer by shifting forward
+        * while inserting ':'s and the nul terminator for the first string.
+        * Hence the strings are at addr6x and addr6x+40.  This generates
+        * _much_ less code than the previous (upstream) approach. */
+       char addr6x[80];
+       char iface[16], flags[16];
+       int iflags, metric, refcnt, use, prefix_len, slen;
+       struct sockaddr_in6 snaddr6;
+
+       FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r");
+
+       bb_printf("Kernel IPv6 routing table\n%-44s%-40s"
+                         "Flags Metric Ref    Use Iface\n",
+                         "Destination", "Next Hop");
+
+       while (1) {
+               int r;
+               r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
+                                  addr6x+14, &prefix_len, &slen, addr6x+40+7,
+                                  &metric, &use, &refcnt, &iflags, iface);
+               if (r != 9) {
+                       if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+                               break;
+                       }
+               ERROR:
+                       bb_error_msg_and_die("fscanf");
+               }
+
+               /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
+                * For now, always do this to validate the proc route format, even
+                * if the interface is down. */
+               {
+                       int i = 0;
+                       char *p = addr6x+14;
+
+                       do {
+                               if (!*p) {
+                                       if (i==40) { /* nul terminator for 1st address? */
+                                               addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
+                                               ++p;    /* Skip and continue. */
+                                               continue;
+                                       }
+                                       goto ERROR;
+                               }
+                               addr6x[i++] = *p++;
+                               if (!((i+1)%5)) {
+                                       addr6x[i++] = ':';
+                               }
+                       } while (i < 40+28+7);
+               }
+
+               if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
+                       continue;
+               }
+
+               set_flags(flags, (iflags & IPV6_MASK));
+
+               r = 0;
+               do {
+                       inet_pton(AF_INET6, addr6x + r,
+                                         (struct sockaddr *) &snaddr6.sin6_addr);
+                       snaddr6.sin6_family = AF_INET6;
+                       INET6_rresolve(naddr6, sizeof(naddr6),
+                                                  (struct sockaddr_in6 *) &snaddr6,
+#if 0
+                                                  (noresolve | 0x8000) /* Default instead of *. */
+#else
+                                                  0x0fff /* Apparently, upstream never resolves. */
+#endif
+                                                  );
+
+                       if (!r) {                       /* 1st pass */
+                               snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
+                               r += 40;
+                       } else {                        /* 2nd pass */
+                               /* Print the info. */
+                               bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+                                                 addr6, naddr6, flags, metric, refcnt, use, iface);
+                               break;
+                       }
+               } while (1);
+       }
+}
+
+#endif
+
+#define ROUTE_OPT_A                    0x01
+#define ROUTE_OPT_n                    0x02
+#define ROUTE_OPT_e                    0x04
+#define ROUTE_OPT_INET6                0x08 /* Not an actual option. See below. */
+
+/* 1st byte is offset to next entry offset.  2nd byte is return value. */
+static const char tbl_verb[] =         /* 2nd byte matches RTACTION_* code */
+       "\006\001add\0"
+       "\006\002del\0"
+/*     "\011\002delete\0" */
+       "\010\002delete"                        /* Since last, we can save a byte. */
+;
+
+int route_main(int argc, char **argv)
+{
+       unsigned long opt;
+       int what;
+       char *family;
+
+       /* First, remap '-net' and '-host' to avoid getopt problems. */
+       {
+               char **p = argv;
+
+               while (*++p) {
+                       if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) {
+                               p[0][0] = '#';
+                       }
+               }
+       }
+
+       opt = bb_getopt_ulflags(argc, argv, "A:ne", &family);
+
+       if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) {
+#ifdef CONFIG_FEATURE_IPV6
+               if (strcmp(family, "inet6") == 0) {
+                       opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
+               } else
+#endif
+               bb_show_usage();
+       }
+
+       argv += optind;
+
+       /* No more args means display the routing table. */
+       if (!*argv) {
+               int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
+#ifdef CONFIG_FEATURE_IPV6
+               if (opt & ROUTE_OPT_INET6)
+                       INET6_displayroutes(noresolve);
+               else
+#endif
+                       displayroutes(noresolve, opt & ROUTE_OPT_e);
+
+               bb_xferror_stdout();
+               bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+       }
+
+       /* Check verb.  At the moment, must be add, del, or delete. */
+       what = kw_lookup(tbl_verb, &argv);
+       if (!what || !*argv) {          /* Unknown verb or no more args. */
+               bb_show_usage();
+       }
+
+#ifdef CONFIG_FEATURE_IPV6
+       if (opt & ROUTE_OPT_INET6)
+               INET6_setroute(what, argv);
+       else
+#endif
+               INET_setroute(what, argv);
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/networking/telnet.c b/busybox/networking/telnet.c
new file mode 100644 (file)
index 0000000..6ad7712
--- /dev/null
@@ -0,0 +1,769 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * telnet implementation for busybox
+ *
+ * Author: Tomi Ollila <too@iki.fi>
+ * Copyright (C) 1994-2000 by Tomi Ollila
+ *
+ * Created: Thu Apr  7 13:29:41 1994 too
+ * Last modified: Fri Jun  9 14:34:24 2000 too
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * HISTORY
+ * Revision 3.1  1994/04/17  11:31:54  too
+ * initial revision
+ * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
+ * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
+ * <jam@ltsp.org>
+ * Modified 2004/02/11 to add ability to pass the USER variable to remote host
+ * by Fernando Silveira <swrh@gmx.net>
+ *
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <arpa/telnet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "busybox.h"
+
+#if 0
+static const int DOTRACE = 1;
+#endif
+
+#ifdef DOTRACE
+#include <arpa/inet.h> /* for inet_ntoa()... */
+#define TRACE(x, y) do { if (x) printf y; } while (0)
+#else
+#define TRACE(x, y)
+#endif
+
+#if 0
+#define USE_POLL
+#include <sys/poll.h>
+#else
+#include <sys/time.h>
+#endif
+
+#define DATABUFSIZE  128
+#define IACBUFSIZE   128
+
+static const int CHM_TRY = 0;
+static const int CHM_ON = 1;
+static const int CHM_OFF = 2;
+
+static const int UF_ECHO = 0x01;
+static const int UF_SGA = 0x02;
+
+enum {
+       TS_0 = 1,
+       TS_IAC = 2,
+       TS_OPT = 3,
+       TS_SUB1 = 4,
+       TS_SUB2 = 5,
+};
+
+#define WriteCS(fd, str) write(fd, str, sizeof str -1)
+
+typedef unsigned char byte;
+
+/* use globals to reduce size ??? */ /* test this hypothesis later */
+static struct Globalvars {
+       int             netfd; /* console fd:s are 0 and 1 (and 2) */
+    /* same buffer used both for network and console read/write */
+       char    buf[DATABUFSIZE]; /* allocating so static size is smaller */
+       byte    telstate; /* telnet negotiation state from network input */
+       byte    telwish;  /* DO, DONT, WILL, WONT */
+       byte    charmode;
+       byte    telflags;
+       byte    gotsig;
+       /* buffer to handle telnet negotiations */
+       char    iacbuf[IACBUFSIZE];
+       short   iaclen; /* could even use byte */
+       struct termios termios_def;
+       struct termios termios_raw;
+} G;
+
+#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
+
+#ifdef USE_GLOBALVAR_PTR
+struct Globalvars * Gptr;
+#define G (*Gptr)
+#endif
+
+static inline void iacflush(void)
+{
+       write(G.netfd, G.iacbuf, G.iaclen);
+       G.iaclen = 0;
+}
+
+/* Function prototypes */
+static void rawmode(void);
+static void cookmode(void);
+static void do_linemode(void);
+static void will_charmode(void);
+static void telopt(byte c);
+static int subneg(byte c);
+
+/* Some globals */
+static int one = 1;
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+static char *ttype;
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+static const char *autologin;
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static int win_width, win_height;
+#endif
+
+static void doexit(int ev)
+{
+       cookmode();
+       exit(ev);
+}
+
+static void conescape(void)
+{
+       char b;
+
+       if (G.gotsig)   /* came from line  mode... go raw */
+               rawmode();
+
+       WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
+                       " l     go to line mode\r\n"
+                       " c     go to character mode\r\n"
+                       " z     suspend telnet\r\n"
+                       " e     exit telnet\r\n");
+
+       if (read(0, &b, 1) <= 0)
+               doexit(1);
+
+       switch (b)
+       {
+       case 'l':
+               if (!G.gotsig)
+               {
+                       do_linemode();
+                       goto rrturn;
+               }
+               break;
+       case 'c':
+               if (G.gotsig)
+               {
+                       will_charmode();
+                       goto rrturn;
+               }
+               break;
+       case 'z':
+               cookmode();
+               kill(0, SIGTSTP);
+               rawmode();
+               break;
+       case 'e':
+               doexit(0);
+       }
+
+       WriteCS(1, "continuing...\r\n");
+
+       if (G.gotsig)
+               cookmode();
+
+ rrturn:
+       G.gotsig = 0;
+
+}
+static void handlenetoutput(int len)
+{
+       /*      here we could do smart tricks how to handle 0xFF:s in output
+        *      stream  like writing twice every sequence of FF:s (thus doing
+        *      many write()s. But I think interactive telnet application does
+        *      not need to be 100% 8-bit clean, so changing every 0xff:s to
+        *      0x7f:s
+        *
+        *      2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
+        *      I don't agree.
+        *      first - I cannot use programs like sz/rz
+        *      second - the 0x0D is sent as one character and if the next
+        *               char is 0x0A then it's eaten by a server side.
+        *      third - whay doy you have to make 'many write()s'?
+        *              I don't understand.
+        *      So I implemented it. It's realy useful for me. I hope that
+        *      others people will find it interesting to.
+        */
+
+       int i, j;
+       byte * p = G.buf;
+       byte outbuf[4*DATABUFSIZE];
+
+       for (i = len, j = 0; i > 0; i--, p++)
+       {
+               if (*p == 0x1d)
+               {
+                       conescape();
+                       return;
+               }
+               outbuf[j++] = *p;
+               if (*p == 0xff)
+                   outbuf[j++] = 0xff;
+               else if (*p == 0x0d)
+                   outbuf[j++] = 0x00;
+       }
+       if (j > 0 )
+           write(G.netfd, outbuf, j);
+}
+
+
+static void handlenetinput(int len)
+{
+       int i;
+       int cstart = 0;
+
+       for (i = 0; i < len; i++)
+       {
+               byte c = G.buf[i];
+
+               if (G.telstate == 0) /* most of the time state == 0 */
+               {
+                       if (c == IAC)
+                       {
+                               cstart = i;
+                               G.telstate = TS_IAC;
+                       }
+               }
+               else
+                       switch (G.telstate)
+                        {
+                        case TS_0:
+                                if (c == IAC)
+                                        G.telstate = TS_IAC;
+                                else
+                                        G.buf[cstart++] = c;
+                                break;
+
+                        case TS_IAC:
+                                if (c == IAC) /* IAC IAC -> 0xFF */
+                                {
+                                        G.buf[cstart++] = c;
+                                        G.telstate = TS_0;
+                                        break;
+                                }
+                                /* else */
+                                switch (c)
+                                {
+                                case SB:
+                                        G.telstate = TS_SUB1;
+                                        break;
+                                case DO:
+                                case DONT:
+                                case WILL:
+                                case WONT:
+                                        G.telwish =  c;
+                                        G.telstate = TS_OPT;
+                                        break;
+                                default:
+                                        G.telstate = TS_0;     /* DATA MARK must be added later */
+                                }
+                                break;
+                        case TS_OPT: /* WILL, WONT, DO, DONT */
+                                telopt(c);
+                                G.telstate = TS_0;
+                                break;
+                        case TS_SUB1: /* Subnegotiation */
+                        case TS_SUB2: /* Subnegotiation */
+                                if (subneg(c))
+                                        G.telstate = TS_0;
+                                break;
+                        }
+       }
+       if (G.telstate)
+       {
+               if (G.iaclen)                   iacflush();
+               if (G.telstate == TS_0) G.telstate = 0;
+
+               len = cstart;
+       }
+
+       if (len)
+               write(1, G.buf, len);
+}
+
+
+/* ******************************* */
+
+static inline void putiac(int c)
+{
+       G.iacbuf[G.iaclen++] = c;
+}
+
+
+static void putiac2(byte wwdd, byte c)
+{
+       if (G.iaclen + 3 > IACBUFSIZE)
+               iacflush();
+
+       putiac(IAC);
+       putiac(wwdd);
+       putiac(c);
+}
+
+#if 0
+static void putiac1(byte c)
+{
+       if (G.iaclen + 2 > IACBUFSIZE)
+               iacflush();
+
+       putiac(IAC);
+       putiac(c);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+static void putiac_subopt(byte c, char *str)
+{
+       int     len = strlen(str) + 6;   // ( 2 + 1 + 1 + strlen + 2 )
+
+       if (G.iaclen + len > IACBUFSIZE)
+               iacflush();
+
+       putiac(IAC);
+       putiac(SB);
+       putiac(c);
+       putiac(0);
+
+       while(*str)
+               putiac(*str++);
+
+       putiac(IAC);
+       putiac(SE);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+static void putiac_subopt_autologin(void)
+{
+       int len = strlen(autologin) + 6;        // (2 + 1 + 1 + strlen + 2)
+       char *user = "USER";
+
+       if (G.iaclen + len > IACBUFSIZE)
+               iacflush();
+
+       putiac(IAC);
+       putiac(SB);
+       putiac(TELOPT_NEW_ENVIRON);
+       putiac(TELQUAL_IS);
+       putiac(NEW_ENV_VAR);
+
+       while(*user)
+               putiac(*user++);
+
+       putiac(NEW_ENV_VALUE);
+
+       while(*autologin)
+               putiac(*autologin++);
+
+       putiac(IAC);
+       putiac(SE);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static void putiac_naws(byte c, int x, int y)
+{
+       if (G.iaclen + 9 > IACBUFSIZE)
+               iacflush();
+
+       putiac(IAC);
+       putiac(SB);
+       putiac(c);
+
+       putiac((x >> 8) & 0xff);
+       putiac(x & 0xff);
+       putiac((y >> 8) & 0xff);
+       putiac(y & 0xff);
+
+       putiac(IAC);
+       putiac(SE);
+}
+#endif
+
+/* void putiacstring (subneg strings) */
+
+/* ******************************* */
+
+static char const escapecharis[] = "\r\nEscape character is ";
+
+static void setConMode(void)
+{
+       if (G.telflags & UF_ECHO)
+       {
+               if (G.charmode == CHM_TRY) {
+                       G.charmode = CHM_ON;
+                       printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
+                       rawmode();
+               }
+       }
+       else
+       {
+               if (G.charmode != CHM_OFF) {
+                       G.charmode = CHM_OFF;
+                       printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
+                       cookmode();
+               }
+       }
+}
+
+/* ******************************* */
+
+static void will_charmode(void)
+{
+       G.charmode = CHM_TRY;
+       G.telflags |= (UF_ECHO | UF_SGA);
+       setConMode();
+
+       putiac2(DO, TELOPT_ECHO);
+       putiac2(DO, TELOPT_SGA);
+       iacflush();
+}
+
+static void do_linemode(void)
+{
+       G.charmode = CHM_TRY;
+       G.telflags &= ~(UF_ECHO | UF_SGA);
+       setConMode();
+
+       putiac2(DONT, TELOPT_ECHO);
+       putiac2(DONT, TELOPT_SGA);
+       iacflush();
+}
+
+/* ******************************* */
+
+static inline void to_notsup(char c)
+{
+       if      (G.telwish == WILL)     putiac2(DONT, c);
+       else if (G.telwish == DO)       putiac2(WONT, c);
+}
+
+static inline void to_echo(void)
+{
+       /* if server requests ECHO, don't agree */
+       if      (G.telwish == DO) {     putiac2(WONT, TELOPT_ECHO);     return; }
+       else if (G.telwish == DONT)     return;
+
+       if (G.telflags & UF_ECHO)
+       {
+               if (G.telwish == WILL)
+                       return;
+       }
+       else
+               if (G.telwish == WONT)
+                       return;
+
+       if (G.charmode != CHM_OFF)
+               G.telflags ^= UF_ECHO;
+
+       if (G.telflags & UF_ECHO)
+               putiac2(DO, TELOPT_ECHO);
+       else
+               putiac2(DONT, TELOPT_ECHO);
+
+       setConMode();
+       WriteCS(1, "\r\n");  /* sudden modec */
+}
+
+static inline void to_sga(void)
+{
+       /* daemon always sends will/wont, client do/dont */
+
+       if (G.telflags & UF_SGA)
+       {
+               if (G.telwish == WILL)
+                       return;
+       }
+       else
+               if (G.telwish == WONT)
+                       return;
+
+       if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
+               putiac2(DO, TELOPT_SGA);
+       else
+               putiac2(DONT, TELOPT_SGA);
+
+       return;
+}
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+static inline void to_ttype(void)
+{
+       /* Tell server we will (or won't) do TTYPE */
+
+       if(ttype)
+               putiac2(WILL, TELOPT_TTYPE);
+       else
+               putiac2(WONT, TELOPT_TTYPE);
+
+       return;
+}
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+static inline void to_new_environ(void)
+{
+       /* Tell server we will (or will not) do AUTOLOGIN */
+
+       if (autologin)
+               putiac2(WILL, TELOPT_NEW_ENVIRON);
+       else
+               putiac2(WONT, TELOPT_NEW_ENVIRON);
+
+       return;
+}
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static inline void to_naws(void)
+{
+       /* Tell server we will do NAWS */
+       putiac2(WILL, TELOPT_NAWS);
+       return;
+}
+#endif
+
+static void telopt(byte c)
+{
+       switch (c)
+       {
+               case TELOPT_ECHO:               to_echo();      break;
+               case TELOPT_SGA:                to_sga();       break;
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+               case TELOPT_TTYPE:              to_ttype();break;
+#endif
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+               case TELOPT_NEW_ENVIRON:        to_new_environ();       break;
+#endif
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+               case TELOPT_NAWS:               to_naws();
+                                                               putiac_naws(c, win_width, win_height);
+                                                               break;
+#endif
+               default:                                to_notsup(c);
+                                                               break;
+       }
+}
+
+
+/* ******************************* */
+
+/* subnegotiation -- ignore all (except TTYPE,NAWS) */
+
+static int subneg(byte c)
+{
+       switch (G.telstate)
+       {
+       case TS_SUB1:
+               if (c == IAC)
+                       G.telstate = TS_SUB2;
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+               else
+               if (c == TELOPT_TTYPE)
+                       putiac_subopt(TELOPT_TTYPE,ttype);
+#endif
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+               else
+               if (c == TELOPT_NEW_ENVIRON)
+                       putiac_subopt_autologin();
+#endif
+               break;
+       case TS_SUB2:
+               if (c == SE)
+                       return TRUE;
+               G.telstate = TS_SUB1;
+               /* break; */
+       }
+       return FALSE;
+}
+
+/* ******************************* */
+
+static void fgotsig(int sig)
+{
+       G.gotsig = sig;
+}
+
+
+static void rawmode(void)
+{
+       tcsetattr(0, TCSADRAIN, &G.termios_raw);
+}
+
+static void cookmode(void)
+{
+       tcsetattr(0, TCSADRAIN, &G.termios_def);
+}
+
+extern int telnet_main(int argc, char** argv)
+{
+       int len;
+       struct sockaddr_in s_in;
+#ifdef USE_POLL
+       struct pollfd ufds[2];
+#else
+       fd_set readfds;
+       int maxfd;
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+       int opt;
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+       get_terminal_width_height(0, &win_width, &win_height);
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+    ttype = getenv("TERM");
+#endif
+
+       memset(&G, 0, sizeof G);
+
+       if (tcgetattr(0, &G.termios_def) < 0)
+               exit(1);
+
+       G.termios_raw = G.termios_def;
+       cfmakeraw(&G.termios_raw);
+
+       if (argc < 2)
+               bb_show_usage();
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+       autologin = NULL;
+       while ((opt = getopt(argc, argv, "al:")) != EOF) {
+               switch (opt) {
+                       case 'l':
+                               autologin = optarg;
+                               break;
+                       case 'a':
+                               autologin = getenv("USER");
+                               break;
+                       case '?':
+                               bb_show_usage();
+                               break;
+               }
+       }
+       if (optind < argc) {
+               bb_lookup_host(&s_in, argv[optind++]);
+               s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] :
+                               "telnet", "tcp", 23);
+               if (optind < argc)
+                       bb_show_usage();
+       } else
+               bb_show_usage();
+#else
+       bb_lookup_host(&s_in, argv[1]);
+       s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23);
+#endif
+
+       G.netfd = xconnect(&s_in);
+
+       setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
+
+       signal(SIGINT, fgotsig);
+
+#ifdef USE_POLL
+       ufds[0].fd = 0; ufds[1].fd = G.netfd;
+       ufds[0].events = ufds[1].events = POLLIN;
+#else
+       FD_ZERO(&readfds);
+       FD_SET(0, &readfds);
+       FD_SET(G.netfd, &readfds);
+       maxfd = G.netfd + 1;
+#endif
+
+       while (1)
+       {
+#ifndef USE_POLL
+               fd_set rfds = readfds;
+
+               switch (select(maxfd, &rfds, NULL, NULL, NULL))
+#else
+               switch (poll(ufds, 2, -1))
+#endif
+               {
+               case 0:
+                       /* timeout */
+               case -1:
+                       /* error, ignore and/or log something, bay go to loop */
+                       if (G.gotsig)
+                               conescape();
+                       else
+                               sleep(1);
+                       break;
+               default:
+
+#ifdef USE_POLL
+                       if (ufds[0].revents) /* well, should check POLLIN, but ... */
+#else
+                       if (FD_ISSET(0, &rfds))
+#endif
+                       {
+                               len = read(0, G.buf, DATABUFSIZE);
+
+                               if (len <= 0)
+                                       doexit(0);
+
+                               TRACE(0, ("Read con: %d\n", len));
+
+                               handlenetoutput(len);
+                       }
+
+#ifdef USE_POLL
+                       if (ufds[1].revents) /* well, should check POLLIN, but ... */
+#else
+                       if (FD_ISSET(G.netfd, &rfds))
+#endif
+                       {
+                               len = read(G.netfd, G.buf, DATABUFSIZE);
+
+                               if (len <= 0)
+                               {
+                                       WriteCS(1, "Connection closed by foreign host.\r\n");
+                                       doexit(1);
+                               }
+                               TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
+
+                               handlenetinput(len);
+                       }
+               }
+       }
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/networking/telnetd.c b/busybox/networking/telnetd.c
new file mode 100644 (file)
index 0000000..491c66f
--- /dev/null
@@ -0,0 +1,660 @@
+/* $Id: telnetd.c,v 1.13 2004/09/14 17:24:58 bug1 Exp $
+ *
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+ *
+ * This file is distributed under the Gnu Public License (GPL),
+ * please see the file LICENSE for further information.
+ *
+ * ---------------------------------------------------------------------------
+ * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
+ ****************************************************************************
+ *
+ * The telnetd manpage says it all:
+ *
+ *   Telnetd operates by allocating a pseudo-terminal device (see pty(4))  for
+ *   a client, then creating a login process which has the slave side of the
+ *   pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
+ *   master side of the pseudo-terminal, implementing the telnet protocol and
+ *   passing characters between the remote client and the login process.
+ *
+ * Vladimir Oleynik <dzo@simtreas.ru> 2001
+ *     Set process group corrections, initial busybox port
+ */
+
+/*#define DEBUG 1 */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <termios.h>
+#ifdef DEBUG
+#define TELCMDS
+#define TELOPTS
+#endif
+#include <arpa/telnet.h>
+#include <ctype.h>
+#include <sys/syslog.h>
+
+#include "busybox.h"
+
+#define BUFSIZE 4000
+
+#ifdef CONFIG_LOGIN
+static const char *loginpath = "/bin/login";
+#else
+static const char *loginpath;
+#endif
+static const char *issuefile = "/etc/issue.net";
+
+/* shell name and arguments */
+
+static const char *argv_init[] = {NULL, NULL};
+
+/* structure that describes a session */
+
+struct tsession {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+       int sockfd_read, sockfd_write, ptyfd;
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+       struct tsession *next;
+       int sockfd, ptyfd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+       int shell_pid;
+       /* two circular buffers */
+       char *buf1, *buf2;
+       int rdidx1, wridx1, size1;
+       int rdidx2, wridx2, size2;
+};
+
+/*
+
+   This is how the buffers are used. The arrows indicate the movement
+   of data.
+
+   +-------+     wridx1++     +------+     rdidx1++     +----------+
+   |       | <--------------  | buf1 | <--------------  |          |
+   |       |     size1--      +------+     size1++      |          |
+   |  pty  |                                            |  socket  |
+   |       |     rdidx2++     +------+     wridx2++     |          |
+   |       |  --------------> | buf2 |  --------------> |          |
+   +-------+     size2++      +------+     size2--      +----------+
+
+   Each session has got two buffers.
+
+*/
+
+static int maxfd;
+
+static struct tsession *sessions;
+
+
+/*
+
+   Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
+   and must be removed so as to not be interpreted by the terminal).  Make an
+   uninterrupted string of characters fit for the terminal.  Do this by packing
+   all characters meant for the terminal sequentially towards the end of bf.
+
+   Return a pointer to the beginning of the characters meant for the terminal.
+   and make *num_totty the number of characters that should be sent to
+   the terminal.
+
+   Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
+   past (bf + len) then that IAC will be left unprocessed and *processed will be
+   less than len.
+
+   FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
+   what is the escape character?  We aren't handling that situation here.
+
+   CR-LF ->'s CR mapping is also done here, for convenience
+
+  */
+static char *
+remove_iacs(struct tsession *ts, int *pnum_totty) {
+       unsigned char *ptr0 = ts->buf1 + ts->wridx1;
+       unsigned char *ptr = ptr0;
+       unsigned char *totty = ptr;
+       unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
+       int processed;
+       int num_totty;
+
+       while (ptr < end) {
+               if (*ptr != IAC) {
+                       int c = *ptr;
+                       *totty++ = *ptr++;
+                       /* We now map \r\n ==> \r for pragmatic reasons.
+                        * Many client implementations send \r\n when
+                        * the user hits the CarriageReturn key.
+                        */
+                       if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
+                               ptr++;
+               }
+               else {
+                       /*
+                        * TELOPT_NAWS support!
+                        */
+                       if ((ptr+2) >= end) {
+                               /* only the beginning of the IAC is in the
+                               buffer we were asked to process, we can't
+                               process this char. */
+                               break;
+                       }
+
+                       /*
+                        * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
+                        */
+                       else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
+                               struct winsize ws;
+                               if ((ptr+8) >= end)
+                                       break;  /* incomplete, can't process */
+                               ws.ws_col = (ptr[3] << 8) | ptr[4];
+                               ws.ws_row = (ptr[5] << 8) | ptr[6];
+                               (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
+                               ptr += 9;
+                       }
+                       else {
+                               /* skip 3-byte IAC non-SB cmd */
+#ifdef DEBUG
+                               fprintf(stderr, "Ignoring IAC %s,%s\n",
+                                       TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
+#endif
+                               ptr += 3;
+                       }
+               }
+       }
+
+       processed = ptr - ptr0;
+       num_totty = totty - ptr0;
+       /* the difference between processed and num_to tty
+          is all the iacs we removed from the stream.
+          Adjust buf1 accordingly. */
+       ts->wridx1 += processed - num_totty;
+       ts->size1 -= processed - num_totty;
+       *pnum_totty = num_totty;
+       /* move the chars meant for the terminal towards the end of the
+       buffer. */
+       return memmove(ptr - num_totty, ptr0, num_totty);
+}
+
+
+static int
+getpty(char *line)
+{
+       int p;
+#ifdef CONFIG_FEATURE_DEVPTS
+       p = open("/dev/ptmx", 2);
+       if (p > 0) {
+               grantpt(p);
+               unlockpt(p);
+               strcpy(line, ptsname(p));
+               return(p);
+       }
+#else
+       struct stat stb;
+       int i;
+       int j;
+
+       strcpy(line, "/dev/ptyXX");
+
+       for (i = 0; i < 16; i++) {
+               line[8] = "pqrstuvwxyzabcde"[i];
+               line[9] = '0';
+               if (stat(line, &stb) < 0) {
+                       continue;
+               }
+               for (j = 0; j < 16; j++) {
+                       line[9] = j < 10 ? j + '0' : j - 10 + 'a';
+                       if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
+                               line[5] = 't';
+                               return p;
+                       }
+               }
+       }
+#endif /* CONFIG_FEATURE_DEVPTS */
+       return -1;
+}
+
+
+static void
+send_iac(struct tsession *ts, unsigned char command, int option)
+{
+       /* We rely on that there is space in the buffer for now.  */
+       char *b = ts->buf2 + ts->rdidx2;
+       *b++ = IAC;
+       *b++ = command;
+       *b++ = option;
+       ts->rdidx2 += 3;
+       ts->size2 += 3;
+}
+
+
+static struct tsession *
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+make_new_session(void)
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+make_new_session(int sockfd)
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+{
+       struct termios termbuf;
+       int pty, pid;
+       char tty_name[32];
+       struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
+
+       ts->buf1 = (char *)(&ts[1]);
+       ts->buf2 = ts->buf1 + BUFSIZE;
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+       ts->sockfd_read = 0;
+       ts->sockfd_write = 1;
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+       ts->sockfd = sockfd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+       ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
+       ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
+
+       /* Got a new connection, set up a tty and spawn a shell.  */
+
+       pty = getpty(tty_name);
+
+       if (pty < 0) {
+               syslog(LOG_ERR, "All network ports in use!");
+               return 0;
+       }
+
+       if (pty > maxfd)
+               maxfd = pty;
+
+       ts->ptyfd = pty;
+
+       /* Make the telnet client understand we will echo characters so it
+        * should not do it locally. We don't tell the client to run linemode,
+        * because we want to handle line editing and tab completion and other
+        * stuff that requires char-by-char support.
+        */
+
+       send_iac(ts, DO, TELOPT_ECHO);
+       send_iac(ts, DO, TELOPT_NAWS);
+       send_iac(ts, DO, TELOPT_LFLOW);
+       send_iac(ts, WILL, TELOPT_ECHO);
+       send_iac(ts, WILL, TELOPT_SGA);
+
+
+       if ((pid = fork()) < 0) {
+               syslog(LOG_ERR, "Can`t forking");
+       }
+       if (pid == 0) {
+               /* In child, open the child's side of the tty.  */
+               int i;
+
+               for(i = 0; i <= maxfd; i++)
+                       close(i);
+               /* make new process group */
+               setsid();
+
+               if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
+                       syslog(LOG_ERR, "Could not open tty");
+                       exit(1);
+                       }
+               dup(0);
+               dup(0);
+
+               tcsetpgrp(0, getpid());
+
+               /* The pseudo-terminal allocated to the client is configured to operate in
+                * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
+                */
+
+               tcgetattr(0, &termbuf);
+               termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
+               termbuf.c_oflag |= ONLCR|XTABS;
+               termbuf.c_iflag |= ICRNL;
+               termbuf.c_iflag &= ~IXOFF;
+               /*termbuf.c_lflag &= ~ICANON;*/
+               tcsetattr(0, TCSANOW, &termbuf);
+
+               print_login_issue(issuefile, NULL);
+
+               /* exec shell, with correct argv and env */
+               execv(loginpath, (char *const *)argv_init);
+
+               /* NOT REACHED */
+               syslog(LOG_ERR, "execv error");
+               exit(1);
+       }
+
+       ts->shell_pid = pid;
+
+       return ts;
+}
+
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+static void
+free_session(struct tsession *ts)
+{
+       struct tsession *t = sessions;
+
+       /* Unlink this telnet session from the session list.  */
+       if(t == ts)
+               sessions = ts->next;
+       else {
+               while(t->next != ts)
+                       t = t->next;
+               t->next = ts->next;
+       }
+
+       kill(ts->shell_pid, SIGKILL);
+
+       wait4(ts->shell_pid, NULL, 0, NULL);
+
+       close(ts->ptyfd);
+       close(ts->sockfd);
+
+       if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+               maxfd--;
+       if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+               maxfd--;
+
+       free(ts);
+}
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+int
+telnetd_main(int argc, char **argv)
+{
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+       struct sockaddr_in sa;
+       int master_fd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+       fd_set rdfdset, wrfdset;
+       int selret;
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+       int on = 1;
+       int portnbr = 23;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+       int c;
+       static const char options[] =
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+               "f:l:";
+#else /* CONFIG_EATURE_TELNETD_INETD */
+               "f:l:p:";
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+       int maxlen, w, r;
+
+#ifndef CONFIG_LOGIN
+       loginpath = DEFAULT_SHELL;
+#endif
+
+       for (;;) {
+               c = getopt( argc, argv, options);
+               if (c == EOF) break;
+               switch (c) {
+                       case 'f':
+                               issuefile = optarg;
+                               break;
+                       case 'l':
+                               loginpath = optarg;
+                               break;
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+                       case 'p':
+                               portnbr = atoi(optarg);
+                               break;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       if (access(loginpath, X_OK) < 0) {
+               bb_error_msg_and_die ("'%s' unavailable.", loginpath);
+       }
+
+       argv_init[0] = loginpath;
+
+       openlog(bb_applet_name, 0, LOG_USER);
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+       maxfd = 1;
+       sessions = make_new_session();
+#else /* CONFIG_EATURE_TELNETD_INETD */
+       sessions = 0;
+
+       /* Grab a TCP socket.  */
+
+       master_fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (master_fd < 0) {
+               bb_perror_msg_and_die("socket");
+       }
+       (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+       /* Set it to listen to specified port.  */
+
+       memset((void *)&sa, 0, sizeof(sa));
+       sa.sin_family = AF_INET;
+       sa.sin_port = htons(portnbr);
+
+       if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+               bb_perror_msg_and_die("bind");
+       }
+
+       if (listen(master_fd, 1) < 0) {
+               bb_perror_msg_and_die("listen");
+       }
+
+       if (daemon(0, 0) < 0)
+               bb_perror_msg_and_die("daemon");
+
+
+       maxfd = master_fd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+       do {
+               struct tsession *ts;
+
+               FD_ZERO(&rdfdset);
+               FD_ZERO(&wrfdset);
+
+               /* select on the master socket, all telnet sockets and their
+                * ptys if there is room in their respective session buffers.
+                */
+
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+               FD_SET(master_fd, &rdfdset);
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+               ts = sessions;
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+               while (ts) {
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                       /* buf1 is used from socket to pty
+                        * buf2 is used from pty to socket
+                        */
+                       if (ts->size1 > 0) {
+                               FD_SET(ts->ptyfd, &wrfdset);  /* can write to pty */
+                       }
+                       if (ts->size1 < BUFSIZE) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                               FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                               FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                       }
+                       if (ts->size2 > 0) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                               FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                               FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                       }
+                       if (ts->size2 < BUFSIZE) {
+                               FD_SET(ts->ptyfd, &rdfdset);  /* can read from pty */
+                       }
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+                       ts = ts->next;
+               }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+               selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
+
+               if (!selret)
+                       break;
+
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+               /* First check for and accept new sessions.  */
+               if (FD_ISSET(master_fd, &rdfdset)) {
+                       int fd, salen;
+
+                       salen = sizeof(sa);
+                       if ((fd = accept(master_fd, (struct sockaddr *)&sa,
+                                               &salen)) < 0) {
+                               continue;
+                       } else {
+                               /* Create a new session and link it into
+                                       our active list.  */
+                               struct tsession *new_ts = make_new_session(fd);
+                               if (new_ts) {
+                                       new_ts->next = sessions;
+                                       sessions = new_ts;
+                                       if (fd > maxfd)
+                                               maxfd = fd;
+                               } else {
+                                       close(fd);
+                               }
+                       }
+               }
+
+               /* Then check for data tunneling.  */
+
+               ts = sessions;
+               while (ts) { /* For all sessions...  */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+                       struct tsession *next = ts->next; /* in case we free ts. */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+                       if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
+                               int num_totty;
+                               char *ptr;
+                               /* Write to pty from buffer 1.  */
+
+                               ptr = remove_iacs(ts, &num_totty);
+
+                               w = write(ts->ptyfd, ptr, num_totty);
+                               if (w < 0) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                                       exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                                       free_session(ts);
+                                       ts = next;
+                                       continue;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                               }
+                               ts->wridx1 += w;
+                               ts->size1 -= w;
+                               if (ts->wridx1 == BUFSIZE)
+                                       ts->wridx1 = 0;
+                       }
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                       if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                       if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                               /* Write to socket from buffer 2.  */
+                               maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                               w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
+                               if (w < 0)
+                                       exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                               w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
+                               if (w < 0) {
+                                       free_session(ts);
+                                       ts = next;
+                                       continue;
+                               }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                               ts->wridx2 += w;
+                               ts->size2 -= w;
+                               if (ts->wridx2 == BUFSIZE)
+                                       ts->wridx2 = 0;
+                       }
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                       if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                       if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                               /* Read from socket to buffer 1. */
+                               maxlen = MIN(BUFSIZE - ts->rdidx1,
+                                               BUFSIZE - ts->size1);
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                               r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
+                               if (!r || (r < 0 && errno != EINTR))
+                                       exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                               r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
+                               if (!r || (r < 0 && errno != EINTR)) {
+                                       free_session(ts);
+                                       ts = next;
+                                       continue;
+                               }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                               if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
+                                       r--;
+                                       if(!r)
+                                               continue;
+                               }
+                               ts->rdidx1 += r;
+                               ts->size1 += r;
+                               if (ts->rdidx1 == BUFSIZE)
+                                       ts->rdidx1 = 0;
+                       }
+
+                       if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
+                               /* Read from pty to buffer 2.  */
+                               maxlen = MIN(BUFSIZE - ts->rdidx2,
+                                               BUFSIZE - ts->size2);
+                               r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
+                               if (!r || (r < 0 && errno != EINTR)) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+                                       exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+                                       free_session(ts);
+                                       ts = next;
+                                       continue;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+                               }
+                               ts->rdidx2 += r;
+                               ts->size2 += r;
+                               if (ts->rdidx2 == BUFSIZE)
+                                       ts->rdidx2 = 0;
+                       }
+
+                       if (ts->size1 == 0) {
+                               ts->rdidx1 = 0;
+                               ts->wridx1 = 0;
+                       }
+                       if (ts->size2 == 0) {
+                               ts->rdidx2 = 0;
+                               ts->wridx2 = 0;
+                       }
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+                       ts = next;
+               }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+       } while (1);
+
+       return 0;
+}
diff --git a/busybox/networking/tftp.c b/busybox/networking/tftp.c
new file mode 100644 (file)
index 0000000..3c94731
--- /dev/null
@@ -0,0 +1,584 @@
+/* ------------------------------------------------------------------------- */
+/* tftp.c                                                                    */
+/*                                                                           */
+/* A simple tftp client for busybox.                                         */
+/* Tries to follow RFC1350.                                                  */
+/* Only "octet" mode supported.                                              */
+/* Optional blocksize negotiation (RFC2347 + RFC2348)                        */
+/*                                                                           */
+/* Copyright (C) 2001 Magnus Damm <damm@opensource.se>                       */
+/*                                                                           */
+/* Parts of the code based on:                                               */
+/*                                                                           */
+/* atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>   */
+/*                        and Remi Lefebvre <remi@debian.org>                */
+/*                                                                           */
+/* utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>                         */
+/*                                                                           */
+/* This program is free software; you can redistribute it and/or modify      */
+/* it under the terms of the GNU General Public License as published by      */
+/* the Free Software Foundation; either version 2 of the License, or         */
+/* (at your option) any later version.                                       */
+/*                                                                           */
+/* This program is distributed in the hope that it will be useful,           */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
+/* General Public License for more details.                                  */
+/*                                                                           */
+/* You should have received a copy of the GNU General Public License         */
+/* along with this program; if not, write to the Free Software               */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
+/*                                                                           */
+/* ------------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "busybox.h"
+
+//#define CONFIG_FEATURE_TFTP_DEBUG
+
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT 5             /* seconds */
+
+/* opcodes we support */
+
+#define TFTP_RRQ   1
+#define TFTP_WRQ   2
+#define TFTP_DATA  3
+#define TFTP_ACK   4
+#define TFTP_ERROR 5
+#define TFTP_OACK  6
+
+static const char *tftp_bb_error_msg[] = {
+       "Undefined error",
+       "File not found",
+       "Access violation",
+       "Disk full or allocation error",
+       "Illegal TFTP operation",
+       "Unknown transfer ID",
+       "File already exists",
+       "No such user"
+};
+
+const int tftp_cmd_get = 1;
+const int tftp_cmd_put = 2;
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+
+static int tftp_blocksize_check(int blocksize, int bufsize)
+{
+        /* Check if the blocksize is valid:
+        * RFC2348 says between 8 and 65464,
+        * but our implementation makes it impossible
+        * to use blocksizes smaller than 22 octets.
+        */
+
+        if ((bufsize && (blocksize > bufsize)) ||
+           (blocksize < 8) || (blocksize > 65464)) {
+               bb_error_msg("bad blocksize");
+               return 0;
+       }
+
+       return blocksize;
+}
+
+static char *tftp_option_get(char *buf, int len, char *option)
+{
+        int opt_val = 0;
+       int opt_found = 0;
+       int k;
+
+       while (len > 0) {
+
+               /* Make sure the options are terminated correctly */
+
+               for (k = 0; k < len; k++) {
+                       if (buf[k] == '\0') {
+                               break;
+                       }
+               }
+
+               if (k >= len) {
+                       break;
+               }
+
+               if (opt_val == 0) {
+                       if (strcasecmp(buf, option) == 0) {
+                               opt_found = 1;
+                       }
+               }
+               else {
+                       if (opt_found) {
+                               return buf;
+                       }
+               }
+
+               k++;
+
+               buf += k;
+               len -= k;
+
+               opt_val ^= 1;
+       }
+
+       return NULL;
+}
+
+#endif
+
+static inline int tftp(const int cmd, const struct hostent *host,
+       const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
+{
+       const int cmd_get = cmd & tftp_cmd_get;
+       const int cmd_put = cmd & tftp_cmd_put;
+       const int bb_tftp_num_retries = 5;
+
+       struct sockaddr_in sa;
+       struct sockaddr_in from;
+       struct timeval tv;
+       socklen_t fromlen;
+       fd_set rfds;
+       char *cp;
+       unsigned short tmp;
+       int socketfd;
+       int len;
+       int opcode = 0;
+       int finished = 0;
+       int timeout = bb_tftp_num_retries;
+       unsigned short block_nr = 1;
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+       int want_option_ack = 0;
+#endif
+
+       /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
+        * size varies meaning BUFFERS_GO_ON_STACK would fail */
+       char *buf=xmalloc(tftp_bufsize + 4);
+
+       tftp_bufsize += 4;
+
+       if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+               bb_perror_msg("socket");
+               return EXIT_FAILURE;
+       }
+
+       len = sizeof(sa);
+
+       memset(&sa, 0, len);
+       bind(socketfd, (struct sockaddr *)&sa, len);
+
+       sa.sin_family = host->h_addrtype;
+       sa.sin_port = port;
+       memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
+                  sizeof(sa.sin_addr));
+
+       /* build opcode */
+
+       if (cmd_get) {
+               opcode = TFTP_RRQ;
+       }
+
+       if (cmd_put) {
+               opcode = TFTP_WRQ;
+       }
+
+       while (1) {
+
+               cp = buf;
+
+               /* first create the opcode part */
+
+               *((unsigned short *) cp) = htons(opcode);
+
+               cp += 2;
+
+               /* add filename and mode */
+
+               if ((cmd_get && (opcode == TFTP_RRQ)) ||
+                       (cmd_put && (opcode == TFTP_WRQ))) {
+                        int too_long = 0;
+
+                       /* see if the filename fits into buf */
+                       /* and fill in packet                */
+
+                       len = strlen(remotefile) + 1;
+
+                       if ((cp + len) >= &buf[tftp_bufsize - 1]) {
+                               too_long = 1;
+                       }
+                       else {
+                               safe_strncpy(cp, remotefile, len);
+                               cp += len;
+                       }
+
+                       if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
+                               bb_error_msg("too long remote-filename");
+                               break;
+                       }
+
+                       /* add "mode" part of the package */
+
+                       memcpy(cp, "octet", 6);
+                       cp += 6;
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+
+                       len = tftp_bufsize - 4; /* data block size */
+
+                       if (len != TFTP_BLOCKSIZE_DEFAULT) {
+
+                               if ((&buf[tftp_bufsize - 1] - cp) < 15) {
+                                       bb_error_msg("too long remote-filename");
+                                       break;
+                               }
+
+                               /* add "blksize" + number of blocks  */
+
+                               memcpy(cp, "blksize", 8);
+                               cp += 8;
+
+                               cp += snprintf(cp, 6, "%d", len) + 1;
+
+                               want_option_ack = 1;
+                       }
+#endif
+               }
+
+               /* add ack and data */
+
+               if ((cmd_get && (opcode == TFTP_ACK)) ||
+                       (cmd_put && (opcode == TFTP_DATA))) {
+
+                       *((unsigned short *) cp) = htons(block_nr);
+
+                       cp += 2;
+
+                       block_nr++;
+
+                       if (cmd_put && (opcode == TFTP_DATA)) {
+                               len = bb_full_read(localfd, cp, tftp_bufsize - 4);
+
+                               if (len < 0) {
+                                       bb_perror_msg("read");
+                                       break;
+                               }
+
+                               if (len != (tftp_bufsize - 4)) {
+                                       finished++;
+                               }
+
+                               cp += len;
+                       }
+               }
+
+
+               /* send packet */
+
+
+               timeout = bb_tftp_num_retries;  /* re-initialize */
+               do {
+
+                       len = cp - buf;
+
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+                       fprintf(stderr, "sending %u bytes\n", len);
+                       for (cp = buf; cp < &buf[len]; cp++)
+                               fprintf(stderr, "%02x ", (unsigned char)*cp);
+                       fprintf(stderr, "\n");
+#endif
+                       if (sendto(socketfd, buf, len, 0,
+                                       (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+                               bb_perror_msg("send");
+                               len = -1;
+                               break;
+                       }
+
+
+                       if (finished && (opcode == TFTP_ACK)) {
+                               break;
+                       }
+
+                       /* receive packet */
+
+                       memset(&from, 0, sizeof(from));
+                       fromlen = sizeof(from);
+
+                       tv.tv_sec = TFTP_TIMEOUT;
+                       tv.tv_usec = 0;
+
+                       FD_ZERO(&rfds);
+                       FD_SET(socketfd, &rfds);
+
+                       switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
+                       case 1:
+                               len = recvfrom(socketfd, buf, tftp_bufsize, 0,
+                                               (struct sockaddr *) &from, &fromlen);
+
+                               if (len < 0) {
+                                       bb_perror_msg("recvfrom");
+                                       break;
+                               }
+
+                               timeout = 0;
+
+                               if (sa.sin_port == port) {
+                                       sa.sin_port = from.sin_port;
+                               }
+                               if (sa.sin_port == from.sin_port) {
+                                       break;
+                               }
+
+                               /* fall-through for bad packets! */
+                               /* discard the packet - treat as timeout */
+                               timeout = bb_tftp_num_retries;
+
+                       case 0:
+                               bb_error_msg("timeout");
+
+                               timeout--;
+                               if (timeout == 0) {
+                                       len = -1;
+                                       bb_error_msg("last timeout");
+                               }
+                               break;
+
+                       default:
+                               bb_perror_msg("select");
+                               len = -1;
+                       }
+
+               } while (timeout && (len >= 0));
+
+               if ((finished) || (len < 0)) {
+                       break;
+               }
+
+               /* process received packet */
+
+
+               opcode = ntohs(*((unsigned short *) buf));
+               tmp = ntohs(*((unsigned short *) &buf[2]));
+
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+               fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
+#endif
+
+               if (opcode == TFTP_ERROR) {
+                       char *msg = NULL;
+
+                       if (buf[4] != '\0') {
+                               msg = &buf[4];
+                               buf[tftp_bufsize - 1] = '\0';
+                       } else if (tmp < (sizeof(tftp_bb_error_msg)
+                                         / sizeof(char *))) {
+
+                               msg = (char *) tftp_bb_error_msg[tmp];
+                       }
+
+                       if (msg) {
+                               bb_error_msg("server says: %s", msg);
+                       }
+
+                       break;
+               }
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+               if (want_option_ack) {
+
+                        want_option_ack = 0;
+
+                        if (opcode == TFTP_OACK) {
+
+                                /* server seems to support options */
+
+                                char *res;
+
+                                res = tftp_option_get(&buf[2], len-2,
+                                                      "blksize");
+
+                                if (res) {
+                                        int blksize = atoi(res);
+                       
+                                        if (tftp_blocksize_check(blksize,
+                                                          tftp_bufsize - 4)) {
+
+                                                if (cmd_put) {
+                                                        opcode = TFTP_DATA;
+                                                }
+                                                else {
+                                                        opcode = TFTP_ACK;
+                                                }
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+                                                fprintf(stderr, "using blksize %u\n", blksize);
+#endif
+                                                tftp_bufsize = blksize + 4;
+                                                block_nr = 0;
+                                                continue;
+                                        }
+                                }
+                                /* FIXME:
+                                 * we should send ERROR 8 */
+                                bb_error_msg("bad server option");
+                                break;
+                        }
+
+                        bb_error_msg("warning: blksize not supported by server"
+                                  " - reverting to 512");
+
+                        tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
+               }
+#endif
+
+               if (cmd_get && (opcode == TFTP_DATA)) {
+
+                       if (tmp == block_nr) {
+                       
+                               len = bb_full_write(localfd, &buf[4], len - 4);
+
+                               if (len < 0) {
+                                       bb_perror_msg("write");
+                                       break;
+                               }
+
+                               if (len != (tftp_bufsize - 4)) {
+                                       finished++;
+                               }
+
+                               opcode = TFTP_ACK;
+                               continue;
+                       }
+               }
+
+               if (cmd_put && (opcode == TFTP_ACK)) {
+
+                       if (tmp == (unsigned short)(block_nr - 1)) {
+                               if (finished) {
+                                       break;
+                               }
+
+                               opcode = TFTP_DATA;
+                               continue;
+                       }
+               }
+       }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       close(socketfd);
+
+        free(buf);
+#endif
+
+       return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int tftp_main(int argc, char **argv)
+{
+       struct hostent *host = NULL;
+       const char *localfile = NULL;
+       const char *remotefile = NULL;
+       int port;
+       int cmd = 0;
+       int fd = -1;
+       int flags = 0;
+       int opt;
+       int result;
+       int blocksize = TFTP_BLOCKSIZE_DEFAULT;
+
+       /* figure out what to pass to getopt */
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+#define BS "b:"
+#else
+#define BS
+#endif
+
+#ifdef CONFIG_FEATURE_TFTP_GET
+#define GET "g"
+#else
+#define GET
+#endif
+
+#ifdef CONFIG_FEATURE_TFTP_PUT
+#define PUT "p"
+#else
+#define PUT
+#endif
+
+       while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
+               switch (opt) {
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+               case 'b':
+                       blocksize = atoi(optarg);
+                       if (!tftp_blocksize_check(blocksize, 0)) {
+                                return EXIT_FAILURE;
+                       }
+                       break;
+#endif
+#ifdef CONFIG_FEATURE_TFTP_GET
+               case 'g':
+                       cmd = tftp_cmd_get;
+                       flags = O_WRONLY | O_CREAT | O_TRUNC;
+                       break;
+#endif
+#ifdef CONFIG_FEATURE_TFTP_PUT
+               case 'p':
+                       cmd = tftp_cmd_put;
+                       flags = O_RDONLY;
+                       break;
+#endif
+               case 'l':
+                       localfile = optarg;
+                       break;
+               case 'r':
+                       remotefile = optarg;
+                       break;
+               }
+       }
+
+       if ((cmd == 0) || (optind == argc)) {
+               bb_show_usage();
+       }
+       if(localfile && strcmp(localfile, "-") == 0) {
+           fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
+       }
+       if(localfile == NULL)
+           localfile = remotefile;
+       if(remotefile == NULL)
+           remotefile = localfile;
+       if (fd==-1) {
+           fd = open(localfile, flags, 0644);
+       }
+       if (fd < 0) {
+               bb_perror_msg_and_die("local file");
+       }
+
+       host = xgethostbyname(argv[optind]);
+       port = bb_lookup_port(argv[optind + 1], "udp", 69);
+
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+       fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
+               "localfile \"%s\".\n",
+               inet_ntoa(*((struct in_addr *) host->h_addr)),
+               remotefile, localfile);
+#endif
+
+       result = tftp(cmd, host, remotefile, fd, port, blocksize);
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
+           close(fd);
+       }
+#endif
+       return(result);
+}
diff --git a/busybox/networking/traceroute.c b/busybox/networking/traceroute.c
new file mode 100644 (file)
index 0000000..44ffdf0
--- /dev/null
@@ -0,0 +1,548 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson.
+ *
+ * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+ * 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.
+ */
+
+/*
+ * traceroute host  - trace the route ip packets follow going to "host".
+ * Notes
+ * -----
+ * This program must be run by root or be setuid.  (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * I stole the idea for this program from Steve Deering.  Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis.  I don't know (or care) who came up with
+ * the idea first.  I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself.  Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless.  Maybe one day...
+ *
+ *  -- Van Jacobson (van@helios.ee.lbl.gov)
+ *     Tue Dec 20 03:50:13 PST 1988
+ */
+
+#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
+#undef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG   /* not in documentation man */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "inet_common.h"
+#include <netdb.h>
+#include <endian.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+
+#define MAXPACKET       65535   /* max ip packet size */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN  64
+#endif
+
+/*
+ * format of a (udp) probe packet.
+ */
+struct opacket {
+       struct ip ip;
+       struct udphdr udp;
+       u_char seq;             /* sequence number of this packet */
+       u_char ttl;             /* ttl packet left with */
+       struct timeval tv;      /* time packet left */
+};
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION       4
+
+
+#include "busybox.h"
+
+static u_char  packet[512];            /* last inbound (icmp) packet */
+static struct opacket  *outpacket;     /* last output (udp) packet */
+
+static int s;                          /* receive (icmp) socket file descriptor */
+static int sndsock;                    /* send (udp) socket file descriptor */
+
+static struct sockaddr whereto;        /* Who to try to reach */
+static int datalen;                    /* How much data */
+
+static char *hostname;
+
+static int max_ttl = 30;
+static u_short ident;
+static u_short port = 32768+666;       /* start udp dest port # for probe packets */
+
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+static int verbose;
+#endif
+static int waittime = 5;               /* time to wait for response (in seconds) */
+static int nflag;                      /* print addresses numerically */
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+static inline void
+inetname(struct sockaddr_in *from)
+{
+       char *cp;
+       static char domain[MAXHOSTNAMELEN + 1];
+       char name[MAXHOSTNAMELEN + 1];
+       static int first = 1;
+       const char *ina;
+
+       if (first && !nflag) {
+               first = 0;
+               if (getdomainname(domain, MAXHOSTNAMELEN) != 0)
+                       domain[0] = 0;
+       }
+       cp = 0;
+       if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
+               if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) {
+                       if ((cp = strchr(name, '.')) &&
+                           !strcmp(cp + 1, domain))
+                               *cp = 0;
+                       cp = (char *)name;
+               }
+       }
+       ina = inet_ntoa(from->sin_addr);
+       if (nflag)
+               printf(" %s", ina);
+       else
+               printf(" %s (%s)", (cp ? cp : ina), ina);
+}
+
+static inline void
+print(u_char *buf, int cc, struct sockaddr_in *from)
+{
+       struct ip *ip;
+       int hlen;
+
+       ip = (struct ip *) buf;
+       hlen = ip->ip_hl << 2;
+       cc -= hlen;
+
+       inetname(from);
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+       if (verbose)
+               printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
+#endif
+}
+
+static inline double
+deltaT(struct timeval *t1p, struct timeval *t2p)
+{
+       double dt;
+
+       dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+            (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+       return (dt);
+}
+
+static inline int
+wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
+{
+       fd_set fds;
+       static struct timeval wait;
+       int cc = 0;
+       int fromlen = sizeof (*from);
+
+       FD_ZERO(&fds);
+       FD_SET(sock, &fds);
+       if (reset_timer) {
+               /*
+                * traceroute could hang if someone else has a ping
+                * running and our ICMP reply gets dropped but we don't
+                * realize it because we keep waking up to handle those
+                * other ICMP packets that keep coming in.  To fix this,
+                * "reset_timer" will only be true if the last packet that
+                * came in was for us or if this is the first time we're
+                * waiting for a reply since sending out a probe.  Note
+                * that this takes advantage of the select() feature on
+                * Linux where the remaining timeout is written to the
+                * struct timeval area.
+                */
+               wait.tv_sec = waittime;
+               wait.tv_usec = 0;
+       }
+
+       if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
+               cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
+                           (struct sockaddr *)from, &fromlen);
+
+       return(cc);
+}
+
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+static inline const char *
+pr_type(u_char t)
+{
+       static const char * const ttab[] = {
+       "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
+       "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
+       "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
+       "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
+       "Info Reply"
+       };
+
+       if(t > 16)
+               return("OUT-OF-RANGE");
+
+       return(ttab[t]);
+}
+#endif
+
+static inline int
+packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
+{
+       struct icmp *icp;
+       u_char type, code;
+       int hlen;
+       struct ip *ip;
+
+       ip = (struct ip *) buf;
+       hlen = ip->ip_hl << 2;
+       if (cc < hlen + ICMP_MINLEN) {
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+               if (verbose)
+                       printf("packet too short (%d bytes) from %s\n", cc,
+                               inet_ntoa(from->sin_addr));
+#endif
+               return (0);
+       }
+       cc -= hlen;
+       icp = (struct icmp *)(buf + hlen);
+       type = icp->icmp_type; code = icp->icmp_code;
+       if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
+           type == ICMP_UNREACH) {
+               struct ip *hip;
+               struct udphdr *up;
+
+               hip = &icp->icmp_ip;
+               hlen = hip->ip_hl << 2;
+               up = (struct udphdr *)((u_char *)hip + hlen);
+               if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
+                   up->source == htons(ident) &&
+                   up->dest == htons(port+seq))
+                       return (type == ICMP_TIMXCEED? -1 : code+1);
+       }
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+       if (verbose) {
+               int i;
+               u_long *lp = (u_long *)&icp->icmp_ip;
+
+               printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
+                       cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
+                       type, pr_type(type), icp->icmp_code);
+               for (i = 4; i < cc ; i += sizeof(long))
+                       printf("%2d: x%8.8lx\n", i, *lp++);
+       }
+#endif
+       return(0);
+}
+
+static void             /* not inline */
+send_probe(int seq, int ttl)
+{
+       struct opacket *op = outpacket;
+       struct ip *ip = &op->ip;
+       struct udphdr *up = &op->udp;
+       int i;
+       struct timezone tz;
+
+       ip->ip_off = 0;
+       ip->ip_hl = sizeof(*ip) >> 2;
+       ip->ip_p = IPPROTO_UDP;
+       ip->ip_len = datalen;
+       ip->ip_ttl = ttl;
+       ip->ip_v = IPVERSION;
+       ip->ip_id = htons(ident+seq);
+
+       up->source = htons(ident);
+       up->dest = htons(port+seq);
+       up->len = htons((u_short)(datalen - sizeof(struct ip)));
+       up->check = 0;
+
+       op->seq = seq;
+       op->ttl = ttl;
+       (void) gettimeofday(&op->tv, &tz);
+
+       i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
+                  sizeof(struct sockaddr));
+       if (i < 0 || i != datalen)  {
+               if (i<0)
+                       perror("sendto");
+               printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
+                       datalen, i);
+               (void) fflush(stdout);
+       }
+}
+
+
+int
+#ifndef CONFIG_TRACEROUTE
+main(int argc, char *argv[])
+#else
+traceroute_main(int argc, char *argv[])
+#endif
+{
+       extern char *optarg;
+       extern int optind;
+       struct hostent *hp;
+       struct sockaddr_in from, *to;
+       int ch, i, on, probe, seq, tos, ttl;
+
+       int options = 0;                /* socket options */
+       char *source = 0;
+       int nprobes = 3;
+
+       on = 1;
+       seq = tos = 0;
+       to = (struct sockaddr_in *)&whereto;
+       while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
+               switch(ch) {
+               case 'd':
+#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
+                       options |= SO_DEBUG;
+#endif
+                       break;
+               case 'm':
+                       max_ttl = atoi(optarg);
+                       if (max_ttl <= 1)
+                               bb_error_msg_and_die("max ttl must be >1.");
+                       break;
+               case 'n':
+                       nflag++;
+                       break;
+               case 'p':
+                       port = atoi(optarg);
+                       if (port < 1)
+                               bb_error_msg_and_die("port must be >0.");
+                       break;
+               case 'q':
+                       nprobes = atoi(optarg);
+                       if (nprobes < 1)
+                               bb_error_msg_and_die("nprobes must be >0.");
+                       break;
+               case 'r':
+                       options |= SO_DONTROUTE;
+                       break;
+               case 's':
+                       /*
+                        * set the ip source address of the outbound
+                        * probe (e.g., on a multi-homed host).
+                        */
+                       source = optarg;
+                       break;
+               case 't':
+                       tos = atoi(optarg);
+                       if (tos < 0 || tos > 255)
+                               bb_error_msg_and_die("tos must be 0 to 255.");
+                       break;
+               case 'v':
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+                       verbose++;
+#endif
+                       break;
+               case 'w':
+                       waittime = atoi(optarg);
+                       if (waittime <= 1)
+                               bb_error_msg_and_die("wait must be >1 sec.");
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1)
+               bb_show_usage();
+
+       setlinebuf (stdout);
+
+       memset(&whereto, 0, sizeof(struct sockaddr));
+       hp = xgethostbyname(*argv);
+                       to->sin_family = hp->h_addrtype;
+       memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
+                       hostname = (char *)hp->h_name;
+       if (*++argv)
+               datalen = atoi(*argv);
+       if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
+               bb_error_msg_and_die("packet size must be 0 <= s < %d.",
+                   MAXPACKET - sizeof(struct opacket));
+       datalen += sizeof(struct opacket);
+       outpacket = (struct opacket *)xmalloc((unsigned)datalen);
+       memset(outpacket, 0, datalen);
+       outpacket->ip.ip_dst = to->sin_addr;
+       outpacket->ip.ip_tos = tos;
+       outpacket->ip.ip_v = IPVERSION;
+       outpacket->ip.ip_id = 0;
+
+       ident = (getpid() & 0xffff) | 0x8000;
+
+       if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+               bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+
+       s = create_icmp_socket();
+
+#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
+       if (options & SO_DEBUG)
+               (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
+                                 (char *)&on, sizeof(on));
+#endif
+       if (options & SO_DONTROUTE)
+               (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
+                                 (char *)&on, sizeof(on));
+#ifdef SO_SNDBUF
+       if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
+                      sizeof(datalen)) < 0)
+               bb_perror_msg_and_die("SO_SNDBUF");
+#endif
+#ifdef IP_HDRINCL
+       if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
+                      sizeof(on)) < 0)
+               bb_perror_msg_and_die("IP_HDRINCL");
+#endif
+#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
+       if (options & SO_DEBUG)
+               (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+                                 (char *)&on, sizeof(on));
+#endif
+       if (options & SO_DONTROUTE)
+               (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+                                 (char *)&on, sizeof(on));
+
+       if (source) {
+               memset(&from, 0, sizeof(struct sockaddr));
+               from.sin_family = AF_INET;
+               from.sin_addr.s_addr = inet_addr(source);
+               if (from.sin_addr.s_addr == -1)
+                       bb_error_msg_and_die("unknown host %s", source);
+               outpacket->ip.ip_src = from.sin_addr;
+#ifndef IP_HDRINCL
+               if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
+                       bb_perror_msg_and_die("bind");
+#endif
+       }
+
+       fprintf(stderr, "traceroute to %s (%s)", hostname,
+               inet_ntoa(to->sin_addr));
+       if (source)
+               fprintf(stderr, " from %s", source);
+       fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
+
+       for (ttl = 1; ttl <= max_ttl; ++ttl) {
+               u_long lastaddr = 0;
+               int got_there = 0;
+               int unreachable = 0;
+
+               printf("%2d ", ttl);
+               for (probe = 0; probe < nprobes; ++probe) {
+                       int cc, reset_timer;
+                       struct timeval t1, t2;
+                       struct timezone tz;
+                       struct ip *ip;
+
+                       (void) gettimeofday(&t1, &tz);
+                       send_probe(++seq, ttl);
+                       reset_timer = 1;
+                       while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
+                               (void) gettimeofday(&t2, &tz);
+                               if ((i = packet_ok(packet, cc, &from, seq))) {
+                                       reset_timer = 1;
+                                       if (from.sin_addr.s_addr != lastaddr) {
+                                               print(packet, cc, &from);
+                                               lastaddr = from.sin_addr.s_addr;
+                                       }
+                                       printf("  %g ms", deltaT(&t1, &t2));
+                                       switch(i - 1) {
+                                       case ICMP_UNREACH_PORT:
+                                               ip = (struct ip *)packet;
+                                               if (ip->ip_ttl <= 1)
+                                                       printf(" !");
+                                               ++got_there;
+                                               break;
+                                       case ICMP_UNREACH_NET:
+                                               ++unreachable;
+                                               printf(" !N");
+                                               break;
+                                       case ICMP_UNREACH_HOST:
+                                               ++unreachable;
+                                               printf(" !H");
+                                               break;
+                                       case ICMP_UNREACH_PROTOCOL:
+                                               ++got_there;
+                                               printf(" !P");
+                                               break;
+                                       case ICMP_UNREACH_NEEDFRAG:
+                                               ++unreachable;
+                                               printf(" !F");
+                                               break;
+                                       case ICMP_UNREACH_SRCFAIL:
+                                               ++unreachable;
+                                               printf(" !S");
+                                               break;
+                                       }
+                                       break;
+                               } else
+                                       reset_timer = 0;
+                       }
+                       if (cc == 0)
+                               printf(" *");
+                       (void) fflush(stdout);
+               }
+               putchar('\n');
+               if (got_there || unreachable >= nprobes-1)
+                       return 0;
+       }
+
+       return 0;
+}
diff --git a/busybox/networking/udhcp/AUTHORS b/busybox/networking/udhcp/AUTHORS
new file mode 100644 (file)
index 0000000..f3f4336
--- /dev/null
@@ -0,0 +1,13 @@
+udhcp server/client package
+-----------------------
+
+Russ Dill <Russ.Dill@asu.edu>
+Matthew Ramsay <matthewr@moreton.com.au>
+Chris Trew <christ@moreton.com.au>
+
+Other Credits:
+--------------
+Moreton Bay    (http://www.moretonbay.com/)
+Vladimir Oleynik <dzo@simtrea.ru> Size optimizations
+
+
diff --git a/busybox/networking/udhcp/COPYING b/busybox/networking/udhcp/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/busybox/networking/udhcp/ChangeLog b/busybox/networking/udhcp/ChangeLog
new file mode 100644 (file)
index 0000000..bf2982f
--- /dev/null
@@ -0,0 +1,260 @@
+0.9.9 (pending)
++ Various other size optimizations (Vladimir)
++ Change strerror(errno) to %m (Vladimir N. Oleynik <dzo@simtreas.ru>)
++ Fixed a little endian problem in mton (Bastian Blank <waldi@debian.org>)
++ Fixed a arpping alignment problem (Rui He <rhe@3eti.com>)
++ Added sanity check for max_leases (udhcp bug #1285) (me)
++ Finally got rid of the trailing space in enviromental vars (me)
++ added an new enviromental variable: $mask. It contains the number
+  of subnet bits for tools like ip route that require it.
+  (Bastian Blank <waldi@debian.org>, me)
+
+0.9.8 (021031)
++ split up README files (me)
++ use /dev/urandom to seed xid's (instead of time(0)) (me)
++ fixed renew behavior (me)
++ udhcp now fits nicely into busybox
+  (Glenn McGrath <bug1@iinet.net.au> as well as myself)
++ updated client manpage (me)
++ both client and server now use sockets for signal handling,
+  hopefully, this will be the last needed change in signal
+  handling, I'm fairly certain all the possible races are now
+  closed. (me)
++ The server now restarts the auto_time timer when it receives
+  a SIGUSR1 (write out config file). (me)
++ Improve signal handling (David Poole)
++ Fix to config file parsing (Matt Kraai)
++ Fix load lease logic (me)
++ Fix clear_lease logic (me)
++ -h is now an alias for -H (udhcp bug #1253)
++ Shorter timeout on not receiving offers (me)
++ Improved signal behavior by client (me)
++ Would never assign end address (Keith Smith <keith@ksmith.com>)
++ Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net)
+  udhcp bug#1256
++ Fixed reading of client id (David Poole <davep@portsmith.com>)
++ change sys_errlist[] to strerror() as it aparently doesn't exist
+  (Erik Andersen <andersee@codepoet.org>)
++ fixed get_raw_packet so it returns -2 on non fatal errors
+  (Ted Lemon <Ted.Lemon@nominum.com>)
++ Improved (hopefully) NAKing behavior (me)
++ Added -b option (Jouni Malinen)
++ Compute checksums correctly on big endian hosts
+  (Jouni Malinen <jkmaline@cc.hut.fi>)
+
+0.9.7 (020526)
++ Use add_lease in read_leases, sanitizes leases more, and clears out exprired
+  ones if there is no more room (me)
++ Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747)
++ Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049)
++ Added script hook for DHCPNAK (nak), as well as providing the message option
+  (me)
++ Generate the paramaters request list by seeing what options in options.c are
+  ored with OPTION_REQ in options.c
++ Fix dhcp renew forgetfullness on client (bug #1230)
++ Fix dhcp release bug on client (bug #1231)
++ Set option request list for DHCP renew (bug #1233)
++ Set BOOTREQUEST/REPLY properly
++ Change client-identifier field to popularly expected behavior (me)
++ Only reopen port on errors (me)
++ Change fork/close/setsid structures to daemon() (me)
++ Allow user to specify udhcpd config file at run time (Steven, me)
++ Write pidfile after changing it (Steven CTR Carr <Steven.CTR.Carr@tc.faa.gov>)
++ Added env var docs to udhcpc man page (Matt)
++ Standardized lowercase udhcp in documentation (me)
++ Accept packets without a UDP checksum (me)
++ Accept packets with extra garbage (me)
++ Better error handling in files.c (me)
++ Combined read_interface function to reduce COMBINED_BINARY size (me)
++ Drop calc_length(), some servers choke on smaller packets (me)
++ Try to clean some fat out (me)
+
+0.9.6 (011001)
++ Added bootp paramaters to server (me)
++ Added bootp paramaters to client (me)
++ Added vendor id to client (me)
++ Better pidfile handling in client and server (me)
++ Added man pages (Matt Kraai <kraai@alumni.carnegiemellon.edu>)
+
+0.9.5 (010914)
++ Fixed $HOME and $PATH env passing (me)
++ Fixed client to only listen for raw packets on correct interface (me)
++ added --quit,-q option to quit after a lease is obtained (me)
++ Fixed 100% CPU utilization by client when interface is down (me)
+
+0.9.4 (010827)
++ Force broadcast to broken clients that request unicast (ie, MSFT 98)
++ Make install rules (Adam J. Richter <adam@yggdrasil.com>)
++ One scripts, instead of many (Adam)
++ Removed script paramater info files (env vars only) (Adam)
++ Controlling of forking behavior in client (Adam)
++ General script.c/dhcpc.c cleanups (Adam)
+
+0.9.3 (010820)
++ Increased debugging verbosity (me)
++ Cut trailing whitespace when reading config file (me)
++ added hostname option to client (me)
++ fixed a strncpy bug in script.c (me)
++ fixed a leaky socket in dhcpc.c (me)
++ fixed a leaky socket in dhcpd.c (me)
+
+0.9.2 (010810)
++ Added raw sockets to client (me)
++ alignment fixes (Mark Huang)
++ compiler warning fixes (Mark Huang)
++ client now sends parameter list (Mark Huang/me)
++ added ipttl option
++ Does now not request broadcast packets
+
+0.9.1 (010806)
++ Added udhcpc client
++ reorganized functions/files
++ listening socket now only binds to one interface
+
+0.9.0 (010720) Major rewrite, current changes, goals:
++ should not segfault on bogus packets.
++ Options can be read from sname and file fields.
++ supports all DHCP messages (release, decline, inform).
++ IP block is now specified by a range of IP's.
++ Leases file now contains lease time (relative, or absolute).
++ Just about any DHCP option is now supported.
++ DNS entries are no longer read from resolv.conf
++ Lease file can be written periodically when the process receives a SIGUSR1
++ arpping should be supported on all arches.
++ support for DHCP relays.
++ DHCP messages can be unicast if the client requests it.
++ many, many, many other things.
+
+0.8.29 (000323)
++ stable(?) release
+
+
+0.8.28 (000323)
++ removed alarm as it was causing server to go down
++ removed debugging
++ break down dhcpd.c into manageable files
+
+
+0.8.27 (000221)
++ OFFER also sends gateway/subnet (for picky dhcp clients)
++ multiple DNS now handled from resolv.conf if available
++ multiple WINS (from dhcpd.conf)
+
+0.8.25 (000120)
++ now compiles *and* runs on a generic linux system
+       tested with a windows 98 client and the sample config
+       files in the samples directory.
+
+0.8.24 (000117)
++ makeiplist tool has basic functionality in place
++ new sample config files
++ route add -host 255.255.255.255 dev eth0 added for generic linux
+
+0.8.23 (000117)
++ NETtel specific fix for ignoring dhcp requests on 2nd interface
+
+0.8.22 (000113)
++ minor changes to compile under a generic linux system
++ minor config file location changes for a generic linux system
++ makeiplist fixes.. still incomplete.. but etting closer
+
+0.8.21 (000113)
++ now sends the correct server ip instead of hardcoded value
++ minor debugging fixes for critical messages
+
+0.8.20 (000106)
++ cut out dhcp server checking.. this was causing dialout ppp
+       sessions with idle time set to never time out.
++ also removed the 10 second pause before launching.. as this
+       was originally to stop it replying to a dhcp client
+       on a NETtel which was really a bad way to do it in the
+       first place :-)
+
+0.8.19 (000104)
++ fixes for route add -host on a machine that needs to run both
+       a DHCP client and server (dual eth box)
+
+0.8.18 (991220)
+
++ Race conditions fixed by disabling alarm whilst the server is busy
++ Fixed continous clearing of the offered array so that it is only cleared
+  when it is dirty - (could change the position of when dirty is set)
+
+0.8.17 (991212)
+
+- has problems clearing out the offered array
+
+0.8.16 (991203)
++ Non blocking error is changes to informational as it is not really
+  an error
+
+0.8.15 (991129)
++ Servs the dns field 3 times (Nettel only) so that windows servers
+  dont time out whilst nettel is booting
+
+0.8.14 (991126)
++ added owner check for the offered array so clean out time may be
+  increased
++ added new func to print out chadder/MAC
+
+0.8.13 (991125)
++ added win95 support (w95 changed xid halfway through conversation)
++ had to change the offered array to use hardware addresses instead of xid
++ fixed re offered bug
++ added more debugging
+
+0.8.12 (991111)
++ debugging was real bad.. cleaned up a bit.. needs overhaul
+
+
+0.8.11 (991110)
++ fixed up offeredAddr array to actually be used now!! offeredAddr is
+       used to see if another simultaneous connecting client was offered
+       an address that we are about to offer another client (multiple
+       client bug)
++ removed re_offered variable as it breaks multiple client support
++ added lease time to ACK -- doesn't work if in OFFER
++ decreased internal array clear delay to 60 seconds
++ minor findAddr bug (returning -1 instead of 0)
++ if clients xid already in offeredAddr offer the same addr and don't add a
+       new addr to offered (caused by a client issuing multiple DISCOVERs)
+
+0.8.10 (991105)
++ \n bug in arpping
++ minor debugging changes (removed printfs etc)
++ started browseiplist (not finished)
+
+0.8.9 (19991105)
++ fixed options array size bug (options were cut off)
+
+0.8.8 (19991105)
++ ignores requests from dhcpcd on the same machine
+
+0.8.7 (19991104)
++ don't die if we can't bind to search for existing DHCP server
++ slightly more verbose syslogging
+
+0.8.6 (19991103)
++ added makeiplist (not finished -- core dumps)
++ minor debug changes
+
+0.8.5 (19991029)
++ exits if another DHCP server is already on the network
++ added Linux Makefile
+
+0.8.4 (19991026)
++ minor bug fix in findaddr preventing an addr being found
+
+0.8.3 (19991025)
++ fixed up debugging
++ minor hwaddr issues
+
+0.8.2 (19991022)
++ free leases (new arpping code from dhcpcd)
++ fixed bug where crashes if no leases/iplist file
++ syslogging and debugging switch
++ serve DNS from resolv.conf
++ fixed bug where new lease added if same mac offered
++ now checks the ip is free b4 offering
++ now supports wins server
+
diff --git a/busybox/networking/udhcp/Config.in b/busybox/networking/udhcp/Config.in
new file mode 100644 (file)
index 0000000..fc07a9b
--- /dev/null
@@ -0,0 +1,62 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "udhcp Server/Client"
+
+config CONFIG_UDHCPD
+       bool "udhcp Server (udhcpd)"
+       default n
+       help
+         uDHCPd is a DHCP server geared primarily toward embedded systems,
+         while striving to be fully functional and RFC compliant.
+
+         See http://udhcp.busybox.net for further details.
+
+config CONFIG_UDHCPC
+       bool "udhcp Client (udhcpc)"
+       default n
+       help
+         uDHCPc is a DHCP client geared primarily toward embedded systems,
+         while striving to be fully functional and RFC compliant.
+
+         The udhcp client negotiates a lease with the DHCP server and
+         notifies a set of scripts when a lease is obtained or lost.
+
+         See http://udhcp.busybox.net for further details.
+
+config CONFIG_DUMPLEASES
+       bool "Lease display utility (dumpleases)"
+       default n
+       depends on CONFIG_UDHCPD
+       help
+         dumpleases displays the leases written out by the udhcpd server.
+         Lease times are stored in the file by time remaining in lease, or
+         by the absolute time that it expires in seconds from epoch.
+
+         See http://udhcp.busybox.net for further details.
+
+config CONFIG_FEATURE_UDHCP_SYSLOG
+       bool "  Log udhcp messages to syslog (instead of stdout)"
+       default n
+       depends on CONFIG_UDHCPD || CONFIG_UDHCPC
+       help
+         If selected, udhcpd will log all its messages to syslog, otherwise,
+         it will attempt to log them to stdout.
+
+         See http://udhcp.busybox.net for further details.
+
+config CONFIG_FEATURE_UDHCP_DEBUG
+       bool "  Compile udhcp with noisy debugging messages"
+       default n
+       depends on CONFIG_UDHCPD || CONFIG_UDHCPC
+       help
+         If selected, udhcpd will output extra debugging output.  If using
+         this option, compile uDHCP with "-g", and do not fork the daemon to
+         the background.
+
+         See http://udhcp.busybox.net for further details.
+
+endmenu
+
diff --git a/busybox/networking/udhcp/Makefile b/busybox/networking/udhcp/Makefile
new file mode 100644 (file)
index 0000000..3d32db5
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=../..
+top_builddir=../..
+srcdir=$(top_srcdir)/networking/udhcp
+UDHCP_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/networking/udhcp/Makefile.in b/busybox/networking/udhcp/Makefile.in
new file mode 100644 (file)
index 0000000..94750f6
--- /dev/null
@@ -0,0 +1,54 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+UDHCP_AR:=udhcp.a
+ifndef $(UDHCP_DIR)
+UDHCP_DIR:=$(top_builddir)/networking/udhcp/
+endif
+srcdir=$(top_srcdir)/networking/udhcp
+
+#ok, so I forgot how to do an or, but this is a quick and dirty hack
+ifeq ($(CONFIG_UDHCPC), y)
+CONFIG_UDHCP_SHARED=y
+else
+ifeq ($(CONFIG_UDHCPD), y)
+CONFIG_UDHCP_SHARED=y
+else
+CONFIG_UDHCP_SHARED=n
+endif
+endif
+
+UDHCP-y:=
+UDHCP-$(CONFIG_UDHCP_SHARED)    += common.c options.c packet.c pidfile.c \
+                                  signalpipe.c socket.c
+UDHCP-$(CONFIG_UDHCPC)         += dhcpc.c clientpacket.c clientsocket.c \
+                                  script.c
+UDHCP-$(CONFIG_UDHCPD)         += dhcpd.c arpping.c files.c leases.c \
+                                  serverpacket.c static_leases.c
+UDHCP-$(CONFIG_DUMPLEASES)     += dumpleases.c
+UDHCP_OBJS=$(patsubst %.c,$(UDHCP_DIR)%.o, $(UDHCP-y))
+
+libraries-y+=$(UDHCP_DIR)$(UDHCP_AR)
+
+$(UDHCP_DIR)$(UDHCP_AR): $(UDHCP_OBJS)
+       $(AR) -ro $@ $(UDHCP_OBJS)
+
+$(UDHCP_OBJS): $(UDHCP_DIR)%.o : $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DIN_BUSYBOX -c $< -o $@
+
diff --git a/busybox/networking/udhcp/README b/busybox/networking/udhcp/README
new file mode 100644 (file)
index 0000000..dd99294
--- /dev/null
@@ -0,0 +1,53 @@
+udhcp server/client package readme
+-------------------------
+
+The udhcp server/client package is primarily geared towards embedded
+systems. It does however, strive to be fully functional, and RFC
+compliant.
+
+
+compile time options
+-------------------
+
+The Makefile contains three of the compile time options:
+
+       UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra
+       debugging output, compile with -g, and not fork to the background when
+       run.
+       UDHCP_SYSLOG: If UDHCP_SYSLOG is defined, udhcpd will log all its
+       messages syslog, otherwise, it will attempt to log them to stdout.
+
+       COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd,
+       is created. If called as udhcpd, the dhcp server will be started.
+       If called as udhcpc, the dhcp client will be started.
+
+dhcpd.h contains the other three compile time options:
+
+       LEASE_TIME: The default lease time if not specified in the config
+       file.
+
+       LEASES_FILE: The default file for storing leases.
+
+       DHCPD_CONFIG_FILE: The defualt config file to use.
+
+options.c contains a set of dhcp options for the client:
+
+       name[10]: The name of the option as it will appear in scripts
+
+       flags: The type of option, as well as if it will be requested
+       by the client (OPTION_REQ)
+
+       code: The DHCP code for this option
+
+
+busybox drop-in
+--------------
+udhcp is now a drop-in component for busybox (http://busybox.net).
+To update busybox to the latest revision, simply do a:
+
+cp *.[ch] README AUTHORS COPYING ChangeLog TODO \
+       <busybox_source>/networking/udhcp
+
+The only two files udhcp does not provide are config.in and
+Makefile.in, so these may need to be updated from time to time.
+
diff --git a/busybox/networking/udhcp/README.dumpleases b/busybox/networking/udhcp/README.dumpleases
new file mode 100644 (file)
index 0000000..6367710
--- /dev/null
@@ -0,0 +1,17 @@
+udhcp lease dump (dumpleases)
+----------------------------
+
+dumpleases displays the leases written out by the udhcpd server. Lease
+times are stored in the file by time remaining in lease (for systems
+without clock that works when there is no power), or by the absolute
+time that it expires in seconds from epoch. dumpleases accepts the
+following command line options:
+
+-a, --absolute         Interpret lease times as expiration time.
+-r, --remaining                Interpret lease times as remaining time.
+-f, --file=FILE                Read lease information from FILE.
+-h, --help             Display help.
+
+Note that if udhcpd has not written a leases file recently, the output
+of may not be up to date.
+
diff --git a/busybox/networking/udhcp/README.udhcpc b/busybox/networking/udhcp/README.udhcpc
new file mode 100644 (file)
index 0000000..d720a37
--- /dev/null
@@ -0,0 +1,141 @@
+udhcp client (udhcpc)
+--------------------
+
+The udhcp client negotiates a lease with the DHCP server and notifies
+a set of scripts when a leases is obtained or lost.
+
+
+command line options
+-------------------
+
+The command line options for the udhcp client are:
+
+-c, --clientid=CLIENTID         Client identifier
+-H, --hostname=HOSTNAME         Client hostname
+-h,                            Alias for -H
+-f, --foreground                Do not fork after getting lease
+-b, --background                Fork to background if lease cannot be
+                                immediately negotiated.
+-i, --interface=INTERFACE       Interface to use (default: eth0)
+-n, --now                       Exit with failure if lease cannot be
+                                immediately negotiated.
+-p, --pidfile=file              Store process ID of daemon in file
+-q, --quit                      Quit after obtaining lease
+-r, --request=IP                IP address to request (default: none)
+-s, --script=file               Run file at dhcp events (default:
+                                /usr/share/udhcpc/default.script)
+-v, --version                   Display version
+
+
+If the requested IP address cannot be obtained, the client accepts the
+address that the server offers.
+
+
+udhcp client scripts
+-------------------
+
+When an event occurs, udhcpc calls the action script. udhcpc never does
+any configuration of the network interface itself, but instead relies on
+a set of scripts. The script by default is
+/usr/share/udhcpc/default.script but this can be changed via the command
+line arguments. The three possible arguments to the script are:
+
+       deconfig: This argument is used when udhcpc starts, and
+       when a leases is lost. The script must put the interface in an
+       up, but deconfigured state, ie: ifconfig $interface 0.0.0.0.
+
+       bound: This argument is used when udhcpc moves from an
+       unbound, to a bound state. All of the paramaters are set in
+       enviromental variables, The script should configure the interface,
+       and set any other relavent parameters (default gateway, dns server,
+       etc).
+
+       renew: This argument is used when a DHCP lease is renewed. All of
+       the paramaters are set in enviromental variables. This argument is
+       used when the interface is already configured, so the IP address,
+       will not change, however, the other DHCP paramaters, such as the
+       default gateway, subnet mask, and dns server may change.
+
+       nak: This argument is used with udhcpc receives a NAK message.
+       The script with the deconfig argument will be called directly
+       afterwards, so no changes to the network interface are neccessary.
+       This hook is provided for purely informational purposes (the
+       message option may contain a reason for the NAK).
+
+The paramaters for enviromental variables are as follows:
+
+       $HOME           - The set $HOME env or "/"
+       $PATH           - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin"
+       $1              - What action the script should perform
+       interface       - The interface this was obtained on
+       ip              - The obtained IP
+       mask            - The number of bits in the netmask (ie: 24)
+       siaddr          - The bootp next server option
+       sname           - The bootp server name option
+       boot_file       - The bootp boot file option
+       subnet          - The assigend subnet mask
+       timezone        - Offset in seconds from UTC
+       router          - A list of routers
+       timesvr         - A list of time servers
+       namesvr         - A list of IEN 116 name servers
+       dns             - A list of DNS server
+       logsvr          - A list of MIT-LCS UDP log servers
+       cookiesvr       - A list of RFC 865 cookie servers
+       lprsvr          - A list of LPR servers
+       hostname        - The assigned hostname
+       bootsize        - The length in 512 octect blocks of the bootfile
+       domain          - The domain name of the network
+       swapsvr         - The IP address of the client's swap server
+       rootpath        - The path name of the client's root disk
+       ipttl           - The TTL to use for this network
+       mtu             - The MTU to use for this network
+       broadcast       - The broadcast address for this network
+       ntpsrv          - A list of NTP servers
+       wins            - A list of WINS servers
+       lease           - The lease time, in seconds
+       dhcptype        - DHCP message type (safely ignored)
+       serverid        - The IP of the server
+       message         - Reason for a DHCPNAK
+       tftp            - The TFTP server name
+       bootfile        - The bootfile name
+
+additional options are easily added in options.c.
+
+
+note on udhcpc's random seed
+---------------------------
+
+udhcpc will seed its random number generator (used for generating xid's)
+by reading /dev/urandom. If you have a lot of embedded systems on the same
+network, with no entropy, you can either seed /dev/urandom by a method of
+your own, or doing the following on startup:
+
+ifconfig eth0 > /dev/urandom
+
+in order to seed /dev/urandom with some data (mac address) unique to your
+system. If reading /dev/urandom fails, udhcpc will fall back to its old
+behavior of seeding with time(0).
+
+
+signals accepted by udhcpc
+-------------------------
+
+udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state,
+and SIGUSR2 will force a release of the current lease, and cause udhcpc to
+go into an inactive state (until it is killed, or receives a SIGUSR1). You do
+not need to sleep between sending signals, as signals received are processed
+sequencially in the order they are received.
+
+
+compile time options
+-------------------
+
+options.c contains a set of dhcp options for the client:
+
+       name[10]: The name of the option as it will appear in scripts
+
+       flags: The type of option, as well as if it will be requested
+       by the client (OPTION_REQ)
+
+       code: The DHCP code for this option
+
diff --git a/busybox/networking/udhcp/README.udhcpd b/busybox/networking/udhcp/README.udhcpd
new file mode 100644 (file)
index 0000000..169de78
--- /dev/null
@@ -0,0 +1,59 @@
+udhcp server (udhcpd)
+--------------------
+
+The only command line argument to udhcpd is an optional specifed
+config file. If no config file is specified, udhcpd uses the default
+config file, /etc/udhcpd.conf. Ex:
+
+udhcpd /etc/udhcpd.eth1.conf
+
+The udhcp server employs a number of simple config files:
+
+udhcpd.leases
+------------
+
+The udhcpd.leases behavior is designed for an embedded system. The
+file is written either every auto_time seconds, or when a SIGUSR1
+is received (the auto_time timer restarts if a SIGUSR1 is received).
+If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will
+finish writing the leases file and wait for the aftermentioned script
+to be executed and finish before quiting, so you do not need to sleep
+between sending signals. When the file is written, a script can be
+optionally called to commit the file to flash. Lease times are stored
+in the file by time remaining in lease (for systems without clock
+that works when there is no power), or by the absolute time that it
+expires in seconds from epoch. In the remaining format, expired leases
+are stored as zero. The file is of the format:
+
+16 byte MAC
+4 byte ip address
+u32 expire time
+16 byte MAC
+4 byte ip address
+u32 expire time
+.
+etc.
+
+example: hexdump udhcpd.leases
+
+0000000 1000 c95a 27d9 0000 0000 0000 0000 0000
+0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000
+0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29
+0000030
+
+
+udhcpd.conf
+----------
+
+The format is fairly simple, there is a sample file with all the
+available options and comments describing them in samples/udhcpd.conf
+
+compile time options
+-------------------
+
+dhcpd.h contains the other two compile time options:
+
+       LEASE_TIME: The default lease time if not specified in the config
+       file.
+
+       DHCPD_CONFIG_FILE: The defualt config file to use.
diff --git a/busybox/networking/udhcp/TODO b/busybox/networking/udhcp/TODO
new file mode 100644 (file)
index 0000000..6febe5a
--- /dev/null
@@ -0,0 +1,16 @@
+TODO
+----
++ Check for valid IP, netmask, hostname, paths, strings, etc
++ Integrade README.*'s with manpages
++ using time(0) breaks if the system clock changes, find a portable solution
++ make failure of reading functions revert to previous value, not the default
++ sanity code for option[OPT_LEN]
++ fix aliasing (ie: eth0:0)
++ better standard linux distro support
++ make sure packet generation works on a wide varitey of arches
++ Interoperability testing
++ Hooks within the DHCP server
+       * Server notification when a lease is added/removed
++ Additional bootp support in client/server
++ Make serverid option in server configurable
++ Possibly add failure message to DHCP NAK
diff --git a/busybox/networking/udhcp/arpping.c b/busybox/networking/udhcp/arpping.c
new file mode 100644 (file)
index 0000000..7cc2be4
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * arpping.c
+ *
+ * Mostly stolen from: dhcpcd - DHCP client daemon
+ * by Yoichi Hariguchi <yoichi@fore.com>
+ */
+
+#include <sys/time.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "dhcpd.h"
+#include "arpping.h"
+#include "common.h"
+
+/* args:       yiaddr - what IP to ping
+ *             ip - our ip
+ *             mac - our arp address
+ *             interface - interface to use
+ * retn:       1 addr free
+ *             0 addr used
+ *             -1 error
+ */
+
+/* FIXME: match response against chaddr */
+int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
+{
+
+       int     timeout = 2;
+       int     optval = 1;
+       int     s;                      /* socket */
+       int     rv = 1;                 /* return value */
+       struct sockaddr addr;           /* for interface name */
+       struct arpMsg   arp;
+       fd_set          fdset;
+       struct timeval  tm;
+       time_t          prevTime;
+
+
+       if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) {
+#ifdef IN_BUSYBOX
+               LOG(LOG_ERR, bb_msg_can_not_create_raw_socket);
+#else
+               LOG(LOG_ERR, "Could not open raw socket");
+#endif
+               return -1;
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
+               LOG(LOG_ERR, "Could not setsocketopt on raw socket");
+               close(s);
+               return -1;
+       }
+
+       /* send arp request */
+       memset(&arp, 0, sizeof(arp));
+       memcpy(arp.h_dest, MAC_BCAST_ADDR, 6);          /* MAC DA */
+       memcpy(arp.h_source, mac, 6);                   /* MAC SA */
+       arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
+       arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
+       arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
+       arp.hlen = 6;                                   /* hardware address length */
+       arp.plen = 4;                                   /* protocol address length */
+       arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
+       memcpy(arp.sInaddr, &ip, sizeof(ip));           /* source IP address */
+       memcpy(arp.sHaddr, mac, 6);                     /* source hardware address */
+       memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr));   /* target IP address */
+
+       memset(&addr, 0, sizeof(addr));
+       strcpy(addr.sa_data, interface);
+       if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
+               rv = 0;
+
+       /* wait arp reply, and check it */
+       tm.tv_usec = 0;
+       prevTime = uptime();
+       while (timeout > 0) {
+               FD_ZERO(&fdset);
+               FD_SET(s, &fdset);
+               tm.tv_sec = timeout;
+               if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
+                       DEBUG(LOG_ERR, "Error on ARPING request: %m");
+                       if (errno != EINTR) rv = 0;
+               } else if (FD_ISSET(s, &fdset)) {
+                       if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
+                       if (arp.operation == htons(ARPOP_REPLY) &&
+                           bcmp(arp.tHaddr, mac, 6) == 0 &&
+                           *((uint32_t *) arp.sInaddr) == yiaddr) {
+                               DEBUG(LOG_INFO, "Valid arp reply receved for this address");
+                               rv = 0;
+                               break;
+                       }
+               }
+               timeout -= uptime() - prevTime;
+               prevTime = uptime();
+       }
+       close(s);
+       DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V");      
+       return rv;
+}
diff --git a/busybox/networking/udhcp/arpping.h b/busybox/networking/udhcp/arpping.h
new file mode 100644 (file)
index 0000000..6f27d9f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * arpping .h
+ */
+
+#ifndef ARPPING_H
+#define ARPPING_H
+
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+struct arpMsg {
+       /* Ethernet header */
+       u_char   h_dest[6];                     /* destination ether addr */
+       u_char   h_source[6];                   /* source ether addr */
+       u_short  h_proto;                       /* packet type ID field */
+
+       /* ARP packet */
+       uint16_t htype;                         /* hardware type (must be ARPHRD_ETHER) */
+       uint16_t ptype;                         /* protocol type (must be ETH_P_IP) */
+       uint8_t  hlen;                          /* hardware address length (must be 6) */
+       uint8_t  plen;                          /* protocol address length (must be 4) */
+       uint16_t operation;                     /* ARP opcode */
+       uint8_t  sHaddr[6];                     /* sender's hardware address */
+       uint8_t  sInaddr[4];                    /* sender's IP address */
+       uint8_t  tHaddr[6];                     /* target's hardware address */
+       uint8_t  tInaddr[4];                    /* target's IP address */
+       uint8_t  pad[18];                       /* pad for min. Ethernet payload (60 bytes) */
+} __attribute__ ((packed));
+
+/* function prototypes */
+int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface);
+
+#endif
diff --git a/busybox/networking/udhcp/clientpacket.c b/busybox/networking/udhcp/clientpacket.c
new file mode 100644 (file)
index 0000000..ec96601
--- /dev/null
@@ -0,0 +1,248 @@
+/* clientpacket.c
+ *
+ * Packet generation and dispatching functions for the DHCP client.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+
+#include "dhcpd.h"
+#include "clientpacket.h"
+#include "options.h"
+#include "dhcpc.h"
+#include "common.h"
+
+
+/* Create a random xid */
+unsigned long random_xid(void)
+{
+       static int initialized;
+       if (!initialized) {
+               int fd;
+               unsigned long seed;
+
+               fd = open("/dev/urandom", 0);
+               if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) {
+                       LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m");
+                       seed = time(0);
+               }
+               if (fd >= 0) close(fd);
+               srand(seed);
+               initialized++;
+       }
+       return rand();
+}
+
+
+/* initialize a packet with the proper defaults */
+static void init_packet(struct dhcpMessage *packet, char type)
+{
+       struct vendor  {
+               char vendor, length;
+               char str[sizeof("udhcp "VERSION)];
+       } vendor_id = { DHCP_VENDOR,  sizeof("udhcp "VERSION) - 1, "udhcp "VERSION};
+
+       init_header(packet, type);
+       memcpy(packet->chaddr, client_config.arp, 6);
+       add_option_string(packet->options, client_config.clientid);
+       if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
+       add_option_string(packet->options, (uint8_t *) &vendor_id);
+}
+
+
+/* Add a parameter request list for stubborn DHCP servers. Pull the data
+ * from the struct in options.c. Don't do bounds checking here because it
+ * goes towards the head of the packet. */
+static void add_requests(struct dhcpMessage *packet)
+{
+       int end = end_option(packet->options);
+       int i, len = 0;
+
+       packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+       for (i = 0; dhcp_options[i].code; i++)
+               if (dhcp_options[i].flags & OPTION_REQ)
+                       packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
+       packet->options[end + OPT_LEN] = len;
+       packet->options[end + OPT_DATA + len] = DHCP_END;
+
+}
+
+
+/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
+int send_discover(unsigned long xid, unsigned long requested)
+{
+       struct dhcpMessage packet;
+
+       init_packet(&packet, DHCPDISCOVER);
+       packet.xid = xid;
+       if (requested)
+               add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+
+       add_requests(&packet);
+       LOG(LOG_DEBUG, "Sending discover...");
+       return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+                               SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+}
+
+
+/* Broadcasts a DHCP request message */
+int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
+{
+       struct dhcpMessage packet;
+       struct in_addr addr;
+
+       init_packet(&packet, DHCPREQUEST);
+       packet.xid = xid;
+
+       add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+       add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+       add_requests(&packet);
+       addr.s_addr = requested;
+       LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
+       return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+                               SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+}
+
+
+/* Unicasts or broadcasts a DHCP renew message */
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
+{
+       struct dhcpMessage packet;
+       int ret = 0;
+
+       init_packet(&packet, DHCPREQUEST);
+       packet.xid = xid;
+       packet.ciaddr = ciaddr;
+
+       add_requests(&packet);
+       LOG(LOG_DEBUG, "Sending renew...");
+       if (server)
+               ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+       else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+                               SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+       return ret;
+}
+
+
+/* Unicasts a DHCP release message */
+int send_release(unsigned long server, unsigned long ciaddr)
+{
+       struct dhcpMessage packet;
+
+       init_packet(&packet, DHCPRELEASE);
+       packet.xid = random_xid();
+       packet.ciaddr = ciaddr;
+
+       add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
+       add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+       LOG(LOG_DEBUG, "Sending release...");
+       return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+}
+
+
+/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
+int get_raw_packet(struct dhcpMessage *payload, int fd)
+{
+       int bytes;
+       struct udp_dhcp_packet packet;
+       uint32_t source, dest;
+       uint16_t check;
+
+       memset(&packet, 0, sizeof(struct udp_dhcp_packet));
+       bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
+       if (bytes < 0) {
+               DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
+               usleep(500000); /* possible down interface, looping condition */
+               return -1;
+       }
+
+       if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
+               DEBUG(LOG_INFO, "message too short, ignoring");
+               return -2;
+       }
+
+       if (bytes < ntohs(packet.ip.tot_len)) {
+               DEBUG(LOG_INFO, "Truncated packet");
+               return -2;
+       }
+
+       /* ignore any extra garbage bytes */
+       bytes = ntohs(packet.ip.tot_len);
+
+       /* Make sure its the right packet for us, and that it passes sanity checks */
+       if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
+           packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
+           bytes > (int) sizeof(struct udp_dhcp_packet) ||
+           ntohs(packet.udp.len) != (uint16_t) (bytes - sizeof(packet.ip))) {
+               DEBUG(LOG_INFO, "unrelated/bogus packet");
+               return -2;
+       }
+
+       /* check IP checksum */
+       check = packet.ip.check;
+       packet.ip.check = 0;
+       if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
+               DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
+               return -1;
+       }
+
+       /* verify the UDP checksum by replacing the header with a psuedo header */
+       source = packet.ip.saddr;
+       dest = packet.ip.daddr;
+       check = packet.udp.check;
+       packet.udp.check = 0;
+       memset(&packet.ip, 0, sizeof(packet.ip));
+
+       packet.ip.protocol = IPPROTO_UDP;
+       packet.ip.saddr = source;
+       packet.ip.daddr = dest;
+       packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
+       if (check && check != checksum(&packet, bytes)) {
+               DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
+               return -2;
+       }
+
+       memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
+
+       if (ntohl(payload->cookie) != DHCP_MAGIC) {
+               LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
+               return -2;
+       }
+       DEBUG(LOG_INFO, "oooooh!!! got some!");
+       return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+
+}
diff --git a/busybox/networking/udhcp/clientpacket.h b/busybox/networking/udhcp/clientpacket.h
new file mode 100644 (file)
index 0000000..8e5441b
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _CLIENTPACKET_H
+#define _CLIENTPACKET_H
+
+#include "packet.h"
+
+unsigned long random_xid(void);
+int send_discover(unsigned long xid, unsigned long requested);
+int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
+int send_release(unsigned long server, unsigned long ciaddr);
+int get_raw_packet(struct dhcpMessage *payload, int fd);
+
+#endif
diff --git a/busybox/networking/udhcp/clientsocket.c b/busybox/networking/udhcp/clientsocket.c
new file mode 100644 (file)
index 0000000..7c1b6e8
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * clientsocket.c -- DHCP client socket creation
+ *
+ * udhcp client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "clientsocket.h"
+#include "common.h"
+
+
+int raw_socket(int ifindex)
+{
+       int fd;
+       struct sockaddr_ll sock;
+
+       DEBUG(LOG_INFO, "Opening raw socket on ifindex %d", ifindex);
+       if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+               DEBUG(LOG_ERR, "socket call failed: %m");
+               return -1;
+       }
+
+       sock.sll_family = AF_PACKET;
+       sock.sll_protocol = htons(ETH_P_IP);
+       sock.sll_ifindex = ifindex;
+       if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
+               DEBUG(LOG_ERR, "bind call failed: %m");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
diff --git a/busybox/networking/udhcp/clientsocket.h b/busybox/networking/udhcp/clientsocket.h
new file mode 100644 (file)
index 0000000..17a55c1
--- /dev/null
@@ -0,0 +1,7 @@
+/* clientsocket.h */
+#ifndef _CLIENTSOCKET_H
+#define _CLIENTSOCKET_H
+
+int raw_socket(int ifindex);
+
+#endif
diff --git a/busybox/networking/udhcp/common.c b/busybox/networking/udhcp/common.c
new file mode 100644 (file)
index 0000000..bf2ac44
--- /dev/null
@@ -0,0 +1,162 @@
+/* common.c
+ *
+ * Functions for debugging and logging as well as some other
+ * simple helper functions.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> 2001-2003
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <paths.h>
+#include <sys/socket.h>
+#include <stdarg.h>
+
+#include "common.h"
+#include "pidfile.h"
+
+
+static int daemonized;
+
+long uptime(void)
+{
+       struct sysinfo info;
+       sysinfo(&info);
+       return info.uptime;
+}
+
+
+/*
+ * This function makes sure our first socket calls
+ * aren't going to fd 1 (printf badness...) and are
+ * not later closed by daemon()
+ */
+static inline void sanitize_fds(void)
+{
+       int zero;
+       if ((zero = open(_PATH_DEVNULL, O_RDWR, 0)) < 0) return;
+       while (zero < 3) zero = dup(zero);
+       close(zero);
+}
+
+
+void background(const char *pidfile)
+{
+#ifdef __uClinux__
+       LOG(LOG_ERR, "Cannot background in uclinux (yet)");
+#else /* __uClinux__ */
+       int pid_fd;
+
+       /* hold lock during fork. */
+       pid_fd = pidfile_acquire(pidfile);
+       if (daemon(0, 0) == -1) {
+               perror("fork");
+               exit(1);
+       }
+       daemonized++;
+       pidfile_write_release(pid_fd);
+#endif /* __uClinux__ */
+}
+
+
+#ifdef UDHCP_SYSLOG
+void udhcp_logging(int level, const char *fmt, ...)
+{
+       va_list p;
+       va_list p2;
+
+       va_start(p, fmt);
+       __va_copy(p2, p);
+       if(!daemonized) {
+               vprintf(fmt, p);
+               putchar('\n');
+       }
+       vsyslog(level, fmt, p2);
+       va_end(p);
+}
+
+
+void start_log_and_pid(const char *client_server, const char *pidfile)
+{
+       int pid_fd;
+
+       /* Make sure our syslog fd isn't overwritten */
+       sanitize_fds();
+
+       /* do some other misc startup stuff while we are here to save bytes */
+       pid_fd = pidfile_acquire(pidfile);
+       pidfile_write_release(pid_fd);
+
+       /* equivelent of doing a fflush after every \n */
+       setlinebuf(stdout);
+
+       openlog(client_server, LOG_PID | LOG_CONS, LOG_LOCAL0);
+       udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
+}
+
+
+#else
+
+
+static char *syslog_level_msg[] = {
+       [LOG_EMERG]   = "EMERGENCY!",
+       [LOG_ALERT]   = "ALERT!",
+       [LOG_CRIT]    = "critical!",
+       [LOG_WARNING] = "warning",
+       [LOG_ERR]     = "error",
+       [LOG_INFO]    = "info",
+       [LOG_DEBUG]   = "debug"
+};
+
+
+void udhcp_logging(int level, const char *fmt, ...)
+{
+       va_list p;
+
+       va_start(p, fmt);
+       if(!daemonized) {
+               printf("%s, ", syslog_level_msg[level]);
+               vprintf(fmt, p);
+               putchar('\n');
+       }
+       va_end(p);
+}
+
+
+void start_log_and_pid(const char *client_server, const char *pidfile)
+{
+       int pid_fd;
+
+       /* Make sure our syslog fd isn't overwritten */
+       sanitize_fds();
+
+       /* do some other misc startup stuff while we are here to save bytes */
+       pid_fd = pidfile_acquire(pidfile);
+       pidfile_write_release(pid_fd);
+
+       /* equivelent of doing a fflush after every \n */
+       setlinebuf(stdout);
+
+       udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
+}
+#endif
+
diff --git a/busybox/networking/udhcp/common.h b/busybox/networking/udhcp/common.h
new file mode 100644 (file)
index 0000000..ca19a24
--- /dev/null
@@ -0,0 +1,56 @@
+/* common.h
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include "version.h"
+#include "libbb_udhcp.h"
+
+
+#ifndef UDHCP_SYSLOG
+enum syslog_levels {
+       LOG_EMERG = 0,
+       LOG_ALERT,
+       LOG_CRIT,
+       LOG_WARNING,
+       LOG_ERR,
+       LOG_INFO,
+       LOG_DEBUG
+};
+#else
+#include <syslog.h>
+#endif
+
+long uptime(void);
+void background(const char *pidfile);
+void start_log_and_pid(const char *client_server, const char *pidfile);
+void background(const char *pidfile);
+void udhcp_logging(int level, const char *fmt, ...);
+                                                            
+#define LOG(level, str, args...) udhcp_logging(level, str, ## args)
+
+#ifdef UDHCP_DEBUG
+# define DEBUG(level, str, args...) LOG(level, str, ## args)
+#else
+# define DEBUG(level, str, args...) do {;} while(0)
+#endif
+
+#endif
diff --git a/busybox/networking/udhcp/dhcpc.c b/busybox/networking/udhcp/dhcpc.c
new file mode 100644 (file)
index 0000000..449b517
--- /dev/null
@@ -0,0 +1,517 @@
+/* dhcpc.c
+ *
+ * udhcp DHCP client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/time.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+#include "clientpacket.h"
+#include "clientsocket.h"
+#include "script.h"
+#include "socket.h"
+#include "common.h"
+#include "signalpipe.h"
+
+static int state;
+static unsigned long requested_ip; /* = 0 */
+static unsigned long server_addr;
+static unsigned long timeout;
+static int packet_num; /* = 0 */
+static int fd = -1;
+
+#define LISTEN_NONE 0
+#define LISTEN_KERNEL 1
+#define LISTEN_RAW 2
+static int listen_mode;
+
+struct client_config_t client_config = {
+       /* Default options. */
+       abort_if_no_lease: 0,
+       foreground: 0,
+       quit_after_lease: 0,
+       background_if_no_lease: 0,
+       interface: "eth0",
+       pidfile: NULL,
+       script: DEFAULT_SCRIPT,
+       clientid: NULL,
+       hostname: NULL,
+       ifindex: 0,
+       arp: "\0\0\0\0\0\0",            /* appease gcc-3.0 */
+};
+
+#ifndef IN_BUSYBOX
+static void __attribute__ ((noreturn)) show_usage(void)
+{
+       printf(
+"Usage: udhcpc [OPTIONS]\n\n"
+"  -c, --clientid=CLIENTID         Client identifier\n"
+"  -H, --hostname=HOSTNAME         Client hostname\n"
+"  -h                              Alias for -H\n"
+"  -f, --foreground                Do not fork after getting lease\n"
+"  -b, --background                Fork to background if lease cannot be\n"
+"                                  immediately negotiated.\n"
+"  -i, --interface=INTERFACE       Interface to use (default: eth0)\n"
+"  -n, --now                       Exit with failure if lease cannot be\n"
+"                                  immediately negotiated.\n"
+"  -p, --pidfile=file              Store process ID of daemon in file\n"
+"  -q, --quit                      Quit after obtaining lease\n"
+"  -r, --request=IP                IP address to request (default: none)\n"
+"  -s, --script=file               Run file at dhcp events (default:\n"
+"                                  " DEFAULT_SCRIPT ")\n"
+"  -v, --version                   Display version\n"
+       );
+       exit(0);
+}
+#else
+#define show_usage bb_show_usage
+extern void show_usage(void) __attribute__ ((noreturn));
+#endif
+
+
+/* just a little helper */
+static void change_mode(int new_mode)
+{
+       DEBUG(LOG_INFO, "entering %s listen mode",
+               new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
+       if (fd >= 0) close(fd);
+       fd = -1;
+       listen_mode = new_mode;
+}
+
+
+/* perform a renew */
+static void perform_renew(void)
+{
+       LOG(LOG_INFO, "Performing a DHCP renew");
+       switch (state) {
+       case BOUND:
+               change_mode(LISTEN_KERNEL);
+       case RENEWING:
+       case REBINDING:
+               state = RENEW_REQUESTED;
+               break;
+       case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
+               run_script(NULL, "deconfig");
+       case REQUESTING:
+       case RELEASED:
+               change_mode(LISTEN_RAW);
+               state = INIT_SELECTING;
+               break;
+       case INIT_SELECTING:
+               break;
+       }
+
+       /* start things over */
+       packet_num = 0;
+
+       /* Kill any timeouts because the user wants this to hurry along */
+       timeout = 0;
+}
+
+
+/* perform a release */
+static void perform_release(void)
+{
+       char buffer[16];
+       struct in_addr temp_addr;
+
+       /* send release packet */
+       if (state == BOUND || state == RENEWING || state == REBINDING) {
+               temp_addr.s_addr = server_addr;
+               sprintf(buffer, "%s", inet_ntoa(temp_addr));
+               temp_addr.s_addr = requested_ip;
+               LOG(LOG_INFO, "Unicasting a release of %s to %s",
+                               inet_ntoa(temp_addr), buffer);
+               send_release(server_addr, requested_ip); /* unicast */
+               run_script(NULL, "deconfig");
+       }
+       LOG(LOG_INFO, "Entering released state");
+
+       change_mode(LISTEN_NONE);
+       state = RELEASED;
+       timeout = 0x7fffffff;
+}
+
+
+static void client_background(void)
+{
+       background(client_config.pidfile);
+       client_config.foreground = 1; /* Do not fork again. */
+       client_config.background_if_no_lease = 0;
+}
+
+
+#ifdef COMBINED_BINARY
+int udhcpc_main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+       uint8_t *temp, *message;
+       unsigned long t1 = 0, t2 = 0, xid = 0;
+       unsigned long start = 0, lease;
+       fd_set rfds;
+       int retval;
+       struct timeval tv;
+       int c, len;
+       struct dhcpMessage packet;
+       struct in_addr temp_addr;
+       long now;
+       int max_fd;
+       int sig;
+
+       static const struct option arg_options[] = {
+               {"clientid",    required_argument,      0, 'c'},
+               {"foreground",  no_argument,            0, 'f'},
+               {"background",  no_argument,            0, 'b'},
+               {"hostname",    required_argument,      0, 'H'},
+               {"hostname",    required_argument,      0, 'h'},
+               {"interface",   required_argument,      0, 'i'},
+               {"now",         no_argument,            0, 'n'},
+               {"pidfile",     required_argument,      0, 'p'},
+               {"quit",        no_argument,            0, 'q'},
+               {"request",     required_argument,      0, 'r'},
+               {"script",      required_argument,      0, 's'},
+               {"version",     no_argument,            0, 'v'},
+               {0, 0, 0, 0}
+       };
+
+       /* get options */
+       while (1) {
+               int option_index = 0;
+               c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
+               if (c == -1) break;
+
+               switch (c) {
+               case 'c':
+                       len = strlen(optarg) > 255 ? 255 : strlen(optarg);
+                       if (client_config.clientid) free(client_config.clientid);
+                       client_config.clientid = xmalloc(len + 2);
+                       client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
+                       client_config.clientid[OPT_LEN] = len;
+                       client_config.clientid[OPT_DATA] = '\0';
+                       strncpy(client_config.clientid + OPT_DATA, optarg, len);
+                       break;
+               case 'f':
+                       client_config.foreground = 1;
+                       break;
+               case 'b':
+                       client_config.background_if_no_lease = 1;
+                       break;
+               case 'h':
+               case 'H':
+                       len = strlen(optarg) > 255 ? 255 : strlen(optarg);
+                       if (client_config.hostname) free(client_config.hostname);
+                       client_config.hostname = xmalloc(len + 2);
+                       client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
+                       client_config.hostname[OPT_LEN] = len;
+                       strncpy(client_config.hostname + 2, optarg, len);
+                       break;
+               case 'i':
+                       client_config.interface =  optarg;
+                       break;
+               case 'n':
+                       client_config.abort_if_no_lease = 1;
+                       break;
+               case 'p':
+                       client_config.pidfile = optarg;
+                       break;
+               case 'q':
+                       client_config.quit_after_lease = 1;
+                       break;
+               case 'r':
+                       requested_ip = inet_addr(optarg);
+                       break;
+               case 's':
+                       client_config.script = optarg;
+                       break;
+               case 'v':
+                       printf("udhcpcd, version %s\n\n", VERSION);
+                       return 0;
+                       break;
+               default:
+                       show_usage();
+               }
+       }
+
+       /* Start the log, sanitize fd's, and write a pid file */
+       start_log_and_pid("udhcpc", client_config.pidfile);
+
+       if (read_interface(client_config.interface, &client_config.ifindex,
+                          NULL, client_config.arp) < 0)
+               return 1;
+
+       if (!client_config.clientid) {
+               client_config.clientid = xmalloc(6 + 3);
+               client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
+               client_config.clientid[OPT_LEN] = 7;
+               client_config.clientid[OPT_DATA] = 1;
+               memcpy(client_config.clientid + 3, client_config.arp, 6);
+       }
+
+       /* setup the signal pipe */
+       udhcp_sp_setup();
+
+       state = INIT_SELECTING;
+       run_script(NULL, "deconfig");
+       change_mode(LISTEN_RAW);
+
+       for (;;) {
+
+               tv.tv_sec = timeout - uptime();
+               tv.tv_usec = 0;
+
+               if (listen_mode != LISTEN_NONE && fd < 0) {
+                       if (listen_mode == LISTEN_KERNEL)
+                               fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
+                       else
+                               fd = raw_socket(client_config.ifindex);
+                       if (fd < 0) {
+                               LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
+                               return 0;
+                       }
+               }
+               max_fd = udhcp_sp_fd_set(&rfds, fd);
+
+               if (tv.tv_sec > 0) {
+                       DEBUG(LOG_INFO, "Waiting on select...");
+                       retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
+               } else retval = 0; /* If we already timed out, fall through */
+
+               now = uptime();
+               if (retval == 0) {
+                       /* timeout dropped to zero */
+                       switch (state) {
+                       case INIT_SELECTING:
+                               if (packet_num < 3) {
+                                       if (packet_num == 0)
+                                               xid = random_xid();
+
+                                       /* send discover packet */
+                                       send_discover(xid, requested_ip); /* broadcast */
+
+                                       timeout = now + ((packet_num == 2) ? 4 : 2);
+                                       packet_num++;
+                               } else {
+                                       run_script(NULL, "leasefail");
+                                       if (client_config.background_if_no_lease) {
+                                               LOG(LOG_INFO, "No lease, forking to background.");
+                                               client_background();
+                                       } else if (client_config.abort_if_no_lease) {
+                                               LOG(LOG_INFO, "No lease, failing.");
+                                               return 1;
+                                       }
+                                       /* wait to try again */
+                                       packet_num = 0;
+                                       timeout = now + 60;
+                               }
+                               break;
+                       case RENEW_REQUESTED:
+                       case REQUESTING:
+                               if (packet_num < 3) {
+                                       /* send request packet */
+                                       if (state == RENEW_REQUESTED)
+                                               send_renew(xid, server_addr, requested_ip); /* unicast */
+                                       else send_selecting(xid, server_addr, requested_ip); /* broadcast */
+
+                                       timeout = now + ((packet_num == 2) ? 10 : 2);
+                                       packet_num++;
+                               } else {
+                                       /* timed out, go back to init state */
+                                       if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
+                                       state = INIT_SELECTING;
+                                       timeout = now;
+                                       packet_num = 0;
+                                       change_mode(LISTEN_RAW);
+                               }
+                               break;
+                       case BOUND:
+                               /* Lease is starting to run out, time to enter renewing state */
+                               state = RENEWING;
+                               change_mode(LISTEN_KERNEL);
+                               DEBUG(LOG_INFO, "Entering renew state");
+                               /* fall right through */
+                       case RENEWING:
+                               /* Either set a new T1, or enter REBINDING state */
+                               if ((t2 - t1) <= (lease / 14400 + 1)) {
+                                       /* timed out, enter rebinding state */
+                                       state = REBINDING;
+                                       timeout = now + (t2 - t1);
+                                       DEBUG(LOG_INFO, "Entering rebinding state");
+                               } else {
+                                       /* send a request packet */
+                                       send_renew(xid, server_addr, requested_ip); /* unicast */
+
+                                       t1 = (t2 - t1) / 2 + t1;
+                                       timeout = t1 + start;
+                               }
+                               break;
+                       case REBINDING:
+                               /* Either set a new T2, or enter INIT state */
+                               if ((lease - t2) <= (lease / 14400 + 1)) {
+                                       /* timed out, enter init state */
+                                       state = INIT_SELECTING;
+                                       LOG(LOG_INFO, "Lease lost, entering init state");
+                                       run_script(NULL, "deconfig");
+                                       timeout = now;
+                                       packet_num = 0;
+                                       change_mode(LISTEN_RAW);
+                               } else {
+                                       /* send a request packet */
+                                       send_renew(xid, 0, requested_ip); /* broadcast */
+
+                                       t2 = (lease - t2) / 2 + t2;
+                                       timeout = t2 + start;
+                               }
+                               break;
+                       case RELEASED:
+                               /* yah, I know, *you* say it would never happen */
+                               timeout = 0x7fffffff;
+                               break;
+                       }
+               } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
+                       /* a packet is ready, read it */
+
+                       if (listen_mode == LISTEN_KERNEL)
+                               len = get_packet(&packet, fd);
+                       else len = get_raw_packet(&packet, fd);
+
+                       if (len == -1 && errno != EINTR) {
+                               DEBUG(LOG_INFO, "error on read, %m, reopening socket");
+                               change_mode(listen_mode); /* just close and reopen */
+                       }
+                       if (len < 0) continue;
+
+                       if (packet.xid != xid) {
+                               DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
+                                       (unsigned long) packet.xid, xid);
+                               continue;
+                       }
+
+                       if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+                               DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
+                               continue;
+                       }
+
+                       switch (state) {
+                       case INIT_SELECTING:
+                               /* Must be a DHCPOFFER to one of our xid's */
+                               if (*message == DHCPOFFER) {
+                                       if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
+                                               memcpy(&server_addr, temp, 4);
+                                               xid = packet.xid;
+                                               requested_ip = packet.yiaddr;
+
+                                               /* enter requesting state */
+                                               state = REQUESTING;
+                                               timeout = now;
+                                               packet_num = 0;
+                                       } else {
+                                               DEBUG(LOG_ERR, "No server ID in message");
+                                       }
+                               }
+                               break;
+                       case RENEW_REQUESTED:
+                       case REQUESTING:
+                       case RENEWING:
+                       case REBINDING:
+                               if (*message == DHCPACK) {
+                                       if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
+                                               LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
+                                               lease = 60 * 60;
+                                       } else {
+                                               memcpy(&lease, temp, 4);
+                                               lease = ntohl(lease);
+                                       }
+
+                                       /* enter bound state */
+                                       t1 = lease / 2;
+
+                                       /* little fixed point for n * .875 */
+                                       t2 = (lease * 0x7) >> 3;
+                                       temp_addr.s_addr = packet.yiaddr;
+                                       LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
+                                               inet_ntoa(temp_addr), lease);
+                                       start = now;
+                                       timeout = t1 + start;
+                                       requested_ip = packet.yiaddr;
+                                       run_script(&packet,
+                                                  ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
+
+                                       state = BOUND;
+                                       change_mode(LISTEN_NONE);
+                                       if (client_config.quit_after_lease)
+                                               return 0;
+                                       if (!client_config.foreground)
+                                               client_background();
+
+                               } else if (*message == DHCPNAK) {
+                                       /* return to init state */
+                                       LOG(LOG_INFO, "Received DHCP NAK");
+                                       run_script(&packet, "nak");
+                                       if (state != REQUESTING)
+                                               run_script(NULL, "deconfig");
+                                       state = INIT_SELECTING;
+                                       timeout = now;
+                                       requested_ip = 0;
+                                       packet_num = 0;
+                                       change_mode(LISTEN_RAW);
+                                       sleep(3); /* avoid excessive network traffic */
+                               }
+                               break;
+                       /* case BOUND, RELEASED: - ignore all packets */
+                       }
+               } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
+                       switch (sig) {
+                       case SIGUSR1:
+                               perform_renew();
+                               break;
+                       case SIGUSR2:
+                               perform_release();
+                               break;
+                       case SIGTERM:
+                               LOG(LOG_INFO, "Received SIGTERM");
+                               return 0;
+                       }
+               } else if (retval == -1 && errno == EINTR) {
+                       /* a signal was caught */
+               } else {
+                       /* An error occured */
+                       DEBUG(LOG_ERR, "Error on select");
+               }
+
+       }
+       return 0;
+}
diff --git a/busybox/networking/udhcp/dhcpc.h b/busybox/networking/udhcp/dhcpc.h
new file mode 100644 (file)
index 0000000..9c4aa95
--- /dev/null
@@ -0,0 +1,37 @@
+/* dhcpc.h */
+#ifndef _DHCPC_H
+#define _DHCPC_H
+
+#define DEFAULT_SCRIPT  "/usr/share/udhcpc/default.script"
+
+/* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */
+#include "libbb_udhcp.h"
+
+#define INIT_SELECTING 0
+#define REQUESTING     1
+#define BOUND          2
+#define RENEWING       3
+#define REBINDING      4
+#define INIT_REBOOT    5
+#define RENEW_REQUESTED 6
+#define RELEASED       7
+
+
+struct client_config_t {
+       char foreground;                /* Do not fork */
+       char quit_after_lease;          /* Quit after obtaining lease */
+       char abort_if_no_lease;         /* Abort if no lease */
+       char background_if_no_lease;    /* Fork to background if no lease */
+       char *interface;                /* The name of the interface to use */
+       char *pidfile;                  /* Optionally store the process ID */
+       char *script;                   /* User script to run at dhcp events */
+       uint8_t *clientid;              /* Optional client id to use */
+       uint8_t *hostname;              /* Optional hostname to use */
+       int ifindex;                    /* Index number of the interface to use */
+       uint8_t arp[6];                 /* Our arp address */
+};
+
+extern struct client_config_t client_config;
+
+
+#endif
diff --git a/busybox/networking/udhcp/dhcpd.c b/busybox/networking/udhcp/dhcpd.c
new file mode 100644 (file)
index 0000000..ab3ddfe
--- /dev/null
@@ -0,0 +1,273 @@
+/* dhcpd.c
+ *
+ * udhcp Server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ *                     Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "dhcpd.h"
+#include "arpping.h"
+#include "socket.h"
+#include "options.h"
+#include "files.h"
+#include "serverpacket.h"
+#include "common.h"
+#include "signalpipe.h"
+#include "static_leases.h"
+
+
+/* globals */
+struct dhcpOfferedAddr *leases;
+struct server_config_t server_config;
+
+
+#ifdef COMBINED_BINARY
+int udhcpd_main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+       fd_set rfds;
+       struct timeval tv;
+       int server_socket = -1;
+       int bytes, retval;
+       struct dhcpMessage packet;
+       uint8_t *state;
+       uint8_t *server_id, *requested;
+       uint32_t server_id_align, requested_align;
+       unsigned long timeout_end;
+       struct option_set *option;
+       struct dhcpOfferedAddr *lease;
+       struct dhcpOfferedAddr static_lease;
+       int max_sock;
+       unsigned long num_ips;
+
+       uint32_t static_lease_ip;
+
+       memset(&server_config, 0, sizeof(struct server_config_t));
+       read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
+
+       /* Start the log, sanitize fd's, and write a pid file */
+       start_log_and_pid("udhcpd", server_config.pidfile);
+
+       if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
+               memcpy(&server_config.lease, option->data + 2, 4);
+               server_config.lease = ntohl(server_config.lease);
+       }
+       else server_config.lease = LEASE_TIME;
+
+       /* Sanity check */
+       num_ips = ntohl(server_config.end) - ntohl(server_config.start);
+       if (server_config.max_leases > num_ips) {
+               LOG(LOG_ERR, "max_leases value (%lu) not sane, "
+                       "setting to %lu instead",
+                       server_config.max_leases, num_ips);
+               server_config.max_leases = num_ips;
+       }
+
+       leases = xcalloc(server_config.max_leases, sizeof(struct dhcpOfferedAddr));
+       read_leases(server_config.lease_file);
+
+       if (read_interface(server_config.interface, &server_config.ifindex,
+                          &server_config.server, server_config.arp) < 0)
+               return 1;
+
+#ifndef UDHCP_DEBUG
+       background(server_config.pidfile); /* hold lock during fork. */
+#endif
+
+       /* Setup the signal pipe */
+       udhcp_sp_setup();
+
+       timeout_end = time(0) + server_config.auto_time;
+       while(1) { /* loop until universe collapses */
+
+               if (server_socket < 0)
+                       if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) {
+                               LOG(LOG_ERR, "FATAL: couldn't create server socket, %m");
+                               return 2;
+                       }
+
+               max_sock = udhcp_sp_fd_set(&rfds, server_socket);
+               if (server_config.auto_time) {
+                       tv.tv_sec = timeout_end - time(0);
+                       tv.tv_usec = 0;
+               }
+               if (!server_config.auto_time || tv.tv_sec > 0) {
+                       retval = select(max_sock + 1, &rfds, NULL, NULL,
+                                       server_config.auto_time ? &tv : NULL);
+               } else retval = 0; /* If we already timed out, fall through */
+
+               if (retval == 0) {
+                       write_leases();
+                       timeout_end = time(0) + server_config.auto_time;
+                       continue;
+               } else if (retval < 0 && errno != EINTR) {
+                       DEBUG(LOG_INFO, "error on select");
+                       continue;
+               }
+
+               switch (udhcp_sp_read(&rfds)) {
+               case SIGUSR1:
+                       LOG(LOG_INFO, "Received a SIGUSR1");
+                       write_leases();
+                       /* why not just reset the timeout, eh */
+                       timeout_end = time(0) + server_config.auto_time;
+                       continue;
+               case SIGTERM:
+                       LOG(LOG_INFO, "Received a SIGTERM");
+                       return 0;
+               case 0: break;          /* no signal */
+               default: continue;      /* signal or error (probably EINTR) */
+               }
+
+               if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
+                       if (bytes == -1 && errno != EINTR) {
+                               DEBUG(LOG_INFO, "error on read, %m, reopening socket");
+                               close(server_socket);
+                               server_socket = -1;
+                       }
+                       continue;
+               }
+
+               if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+                       DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
+                       continue;
+               }
+
+               /* Look for a static lease */
+               static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
+
+               if(static_lease_ip)
+               {
+                       printf("Found static lease: %x\n", static_lease_ip);
+
+                       memcpy(&static_lease.chaddr, &packet.chaddr, 16);
+                       static_lease.yiaddr = static_lease_ip;
+                       static_lease.expires = 0;
+
+                       lease = &static_lease;
+
+               }
+               else
+               {
+               lease = find_lease_by_chaddr(packet.chaddr);
+               }
+
+               switch (state[0]) {
+               case DHCPDISCOVER:
+                       DEBUG(LOG_INFO,"received DISCOVER");
+
+                       if (sendOffer(&packet) < 0) {
+                               LOG(LOG_ERR, "send OFFER failed");
+                       }
+                       break;
+               case DHCPREQUEST:
+                       DEBUG(LOG_INFO, "received REQUEST");
+
+                       requested = get_option(&packet, DHCP_REQUESTED_IP);
+                       server_id = get_option(&packet, DHCP_SERVER_ID);
+
+                       if (requested) memcpy(&requested_align, requested, 4);
+                       if (server_id) memcpy(&server_id_align, server_id, 4);
+
+                       if (lease) {
+                               if (server_id) {
+                                       /* SELECTING State */
+                                       DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
+                                       if (server_id_align == server_config.server && requested &&
+                                           requested_align == lease->yiaddr) {
+                                               sendACK(&packet, lease->yiaddr);
+                                       }
+                               } else {
+                                       if (requested) {
+                                               /* INIT-REBOOT State */
+                                               if (lease->yiaddr == requested_align)
+                                                       sendACK(&packet, lease->yiaddr);
+                                               else sendNAK(&packet);
+                                       } else {
+                                               /* RENEWING or REBINDING State */
+                                               if (lease->yiaddr == packet.ciaddr)
+                                                       sendACK(&packet, lease->yiaddr);
+                                               else {
+                                                       /* don't know what to do!!!! */
+                                                       sendNAK(&packet);
+                                               }
+                                       }
+                               }
+
+                       /* what to do if we have no record of the client */
+                       } else if (server_id) {
+                               /* SELECTING State */
+
+                       } else if (requested) {
+                               /* INIT-REBOOT State */
+                               if ((lease = find_lease_by_yiaddr(requested_align))) {
+                                       if (lease_expired(lease)) {
+                                               /* probably best if we drop this lease */
+                                               memset(lease->chaddr, 0, 16);
+                                       /* make some contention for this address */
+                                       } else sendNAK(&packet);
+                               } else if (requested_align < server_config.start ||
+                                          requested_align > server_config.end) {
+                                       sendNAK(&packet);
+                               } /* else remain silent */
+
+                       } else {
+                                /* RENEWING or REBINDING State */
+                       }
+                       break;
+               case DHCPDECLINE:
+                       DEBUG(LOG_INFO,"received DECLINE");
+                       if (lease) {
+                               memset(lease->chaddr, 0, 16);
+                               lease->expires = time(0) + server_config.decline_time;
+                       }
+                       break;
+               case DHCPRELEASE:
+                       DEBUG(LOG_INFO,"received RELEASE");
+                       if (lease) lease->expires = time(0);
+                       break;
+               case DHCPINFORM:
+                       DEBUG(LOG_INFO,"received INFORM");
+                       send_inform(&packet);
+                       break;
+               default:
+                       LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
+               }
+       }
+
+       return 0;
+}
+
diff --git a/busybox/networking/udhcp/dhcpd.h b/busybox/networking/udhcp/dhcpd.h
new file mode 100644 (file)
index 0000000..c47f6aa
--- /dev/null
@@ -0,0 +1,140 @@
+/* dhcpd.h */
+#ifndef _DHCPD_H
+#define _DHCPD_H
+
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "libbb_udhcp.h"
+#include "leases.h"
+#include "version.h"
+
+/************************************/
+/* Defaults _you_ may want to tweak */
+/************************************/
+
+/* the period of time the client is allowed to use that address */
+#define LEASE_TIME              (60*60*24*10) /* 10 days of seconds */
+#define LEASES_FILE            "/var/lib/misc/udhcpd.leases"
+
+/* where to find the DHCP server configuration file */
+#define DHCPD_CONF_FILE         "/etc/udhcpd.conf"
+
+/*****************************************************************/
+/* Do not modify below here unless you know what you are doing!! */
+/*****************************************************************/
+
+/* DHCP protocol -- see RFC 2131 */
+#define SERVER_PORT            67
+#define CLIENT_PORT            68
+
+#define DHCP_MAGIC             0x63825363
+
+/* DHCP option codes (partial list) */
+#define DHCP_PADDING           0x00
+#define DHCP_SUBNET            0x01
+#define DHCP_TIME_OFFSET       0x02
+#define DHCP_ROUTER            0x03
+#define DHCP_TIME_SERVER       0x04
+#define DHCP_NAME_SERVER       0x05
+#define DHCP_DNS_SERVER                0x06
+#define DHCP_LOG_SERVER                0x07
+#define DHCP_COOKIE_SERVER     0x08
+#define DHCP_LPR_SERVER                0x09
+#define DHCP_HOST_NAME         0x0c
+#define DHCP_BOOT_SIZE         0x0d
+#define DHCP_DOMAIN_NAME       0x0f
+#define DHCP_SWAP_SERVER       0x10
+#define DHCP_ROOT_PATH         0x11
+#define DHCP_IP_TTL            0x17
+#define DHCP_MTU               0x1a
+#define DHCP_BROADCAST         0x1c
+#define DHCP_NTP_SERVER                0x2a
+#define DHCP_WINS_SERVER       0x2c
+#define DHCP_REQUESTED_IP      0x32
+#define DHCP_LEASE_TIME                0x33
+#define DHCP_OPTION_OVER       0x34
+#define DHCP_MESSAGE_TYPE      0x35
+#define DHCP_SERVER_ID         0x36
+#define DHCP_PARAM_REQ         0x37
+#define DHCP_MESSAGE           0x38
+#define DHCP_MAX_SIZE          0x39
+#define DHCP_T1                        0x3a
+#define DHCP_T2                        0x3b
+#define DHCP_VENDOR            0x3c
+#define DHCP_CLIENT_ID         0x3d
+
+#define DHCP_END               0xFF
+
+
+#define BOOTREQUEST            1
+#define BOOTREPLY              2
+
+#define ETH_10MB               1
+#define ETH_10MB_LEN           6
+
+#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 BROADCAST_FLAG         0x8000
+
+#define OPTION_FIELD           0
+#define FILE_FIELD             1
+#define SNAME_FIELD            2
+
+/* miscellaneous defines */
+#define MAC_BCAST_ADDR         (uint8_t *) "\xff\xff\xff\xff\xff\xff"
+#define OPT_CODE 0
+#define OPT_LEN 1
+#define OPT_DATA 2
+
+struct option_set {
+       uint8_t *data;
+       struct option_set *next;
+};
+
+struct static_lease {
+       uint8_t *mac;
+       uint32_t *ip;
+       struct static_lease *next;
+};
+
+struct server_config_t {
+       uint32_t server;                /* Our IP, in network order */
+       uint32_t start;         /* Start address of leases, network order */
+       uint32_t end;                   /* End of leases, network order */
+       struct option_set *options;     /* List of DHCP options loaded from the config file */
+       char *interface;                /* The name of the interface to use */
+       int ifindex;                    /* Index number of the interface to use */
+       uint8_t arp[6];         /* Our arp address */
+       unsigned long lease;            /* lease time in seconds (host order) */
+       unsigned long max_leases;       /* maximum number of leases (including reserved address) */
+       char remaining;                 /* should the lease file be interpreted as lease time remaining, or
+                                        * as the time the lease expires */
+       unsigned long auto_time;        /* how long should udhcpd wait before writing a config file.
+                                        * if this is zero, it will only write one on SIGUSR1 */
+       unsigned long decline_time;     /* how long an address is reserved if a client returns a
+                                        * decline message */
+       unsigned long conflict_time;    /* how long an arp conflict offender is leased for */
+       unsigned long offer_time;       /* how long an offered address is reserved */
+       unsigned long min_lease;        /* minimum lease a client can request*/
+       char *lease_file;
+       char *pidfile;
+       char *notify_file;              /* What to run whenever leases are written */
+       uint32_t siaddr;                /* next server bootp option */
+       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 */
+};
+
+extern struct server_config_t server_config;
+extern struct dhcpOfferedAddr *leases;
+
+
+#endif
diff --git a/busybox/networking/udhcp/dumpleases.c b/busybox/networking/udhcp/dumpleases.c
new file mode 100644 (file)
index 0000000..a9036df
--- /dev/null
@@ -0,0 +1,110 @@
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <time.h>
+
+#include "dhcpd.h"
+#include "leases.h"
+#include "libbb_udhcp.h"
+
+#define REMAINING 0
+#define ABSOLUTE 1
+
+
+#ifndef IN_BUSYBOX
+static void __attribute__ ((noreturn)) show_usage(void)
+{
+       printf(
+"Usage: dumpleases -f <file> -[r|a]\n\n"
+"  -f, --file=FILENAME             Leases file to load\n"
+"  -r, --remaining                 Interepret lease times as time remaing\n"
+"  -a, --absolute                  Interepret lease times as expire time\n");
+       exit(0);
+}
+#else
+#define show_usage bb_show_usage
+#endif
+
+
+#ifdef IN_BUSYBOX
+int dumpleases_main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+       FILE *fp;
+       int i, c, mode = REMAINING;
+       long expires;
+       const char *file = LEASES_FILE;
+       struct dhcpOfferedAddr lease;
+       struct in_addr addr;
+
+       static const struct option options[] = {
+               {"absolute", 0, 0, 'a'},
+               {"remaining", 0, 0, 'r'},
+               {"file", 1, 0, 'f'},
+               {0, 0, 0, 0}
+       };
+
+       while (1) {
+               int option_index = 0;
+               c = getopt_long(argc, argv, "arf:", options, &option_index);
+               if (c == -1) break;
+
+               switch (c) {
+               case 'a': mode = ABSOLUTE; break;
+               case 'r': mode = REMAINING; break;
+               case 'f':
+                       file = optarg;
+                       break;
+               default:
+                       show_usage();
+               }
+       }
+
+       fp = xfopen(file, "r");
+
+       printf("Mac Address       IP-Address      Expires %s\n", mode == REMAINING ? "in" : "at");
+       /*     "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */
+       while (fread(&lease, sizeof(lease), 1, fp)) {
+
+               for (i = 0; i < 6; i++) {
+                       printf("%02x", lease.chaddr[i]);
+                       if (i != 5) printf(":");
+               }
+               addr.s_addr = lease.yiaddr;
+               printf(" %-15s", inet_ntoa(addr));
+               expires = ntohl(lease.expires);
+               printf(" ");
+               if (mode == REMAINING) {
+                       if (!expires) printf("expired\n");
+                       else {
+                               if (expires > 60*60*24) {
+                                       printf("%ld days, ", expires / (60*60*24));
+                                       expires %= 60*60*24;
+                               }
+                               if (expires > 60*60) {
+                                       printf("%ld hours, ", expires / (60*60));
+                                       expires %= 60*60;
+                               }
+                               if (expires > 60) {
+                                       printf("%ld minutes, ", expires / 60);
+                                       expires %= 60;
+                               }
+                               printf("%ld seconds\n", expires);
+                       }
+               } else printf("%s", ctime(&expires));
+       }
+       fclose(fp);
+
+       return 0;
+}
diff --git a/busybox/networking/udhcp/files.c b/busybox/networking/udhcp/files.c
new file mode 100644 (file)
index 0000000..73a3bbc
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * files.c -- DHCP server file manipulation *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <netdb.h>
+
+#include <netinet/ether.h>
+#include "static_leases.h"
+
+#include "dhcpd.h"
+#include "files.h"
+#include "options.h"
+#include "common.h"
+
+/*
+ * Domain names may have 254 chars, and string options can be 254
+ * chars long. However, 80 bytes will be enough for most, and won't
+ * hog up memory. If you have a special application, change it
+ */
+#define READ_CONFIG_BUF_SIZE 80
+
+/* on these functions, make sure you datatype matches */
+static int read_ip(const char *line, void *arg)
+{
+       struct in_addr *addr = arg;
+       struct hostent *host;
+       int retval = 1;
+
+       if (!inet_aton(line, addr)) {
+               if ((host = gethostbyname(line)))
+                       addr->s_addr = *((unsigned long *) host->h_addr_list[0]);
+               else retval = 0;
+       }
+       return retval;
+}
+
+static int read_mac(const char *line, void *arg)
+{
+       uint8_t *mac_bytes = arg;
+       struct ether_addr *temp_ether_addr;
+       int retval = 1;
+
+       temp_ether_addr = ether_aton(line);
+
+       if(temp_ether_addr == NULL)
+               retval = 0;
+       else
+               memcpy(mac_bytes, temp_ether_addr, 6);
+
+       return retval;
+}
+
+
+static int read_str(const char *line, void *arg)
+{
+       char **dest = arg;
+
+       if (*dest) free(*dest);
+       *dest = strdup(line);
+
+       return 1;
+}
+
+
+static int read_u32(const char *line, void *arg)
+{
+       uint32_t *dest = arg;
+       char *endptr;
+       *dest = strtoul(line, &endptr, 0);
+       return endptr[0] == '\0';
+}
+
+
+static int read_yn(const char *line, void *arg)
+{
+       char *dest = arg;
+       int retval = 1;
+
+       if (!strcasecmp("yes", line))
+               *dest = 1;
+       else if (!strcasecmp("no", line))
+               *dest = 0;
+       else retval = 0;
+
+       return retval;
+}
+
+
+/* read a dhcp option and add it to opt_list */
+static int read_opt(const char *const_line, void *arg)
+{
+       struct option_set **opt_list = arg;
+       char *opt, *val, *endptr;
+       struct dhcp_option *option;
+       int retval = 0, length;
+       char buffer[8];
+       char *line;
+       uint16_t *result_u16 = (uint16_t *) buffer;
+       uint32_t *result_u32 = (uint32_t *) buffer;
+
+       /* Cheat, the only const line we'll actually get is "" */
+       line = (char *) const_line;
+       if (!(opt = strtok(line, " \t="))) return 0;
+
+       for (option = dhcp_options; option->code; option++)
+               if (!strcasecmp(option->name, opt))
+                       break;
+
+       if (!option->code) return 0;
+
+       do {
+               if (!(val = strtok(NULL, ", \t"))) break;
+               length = option_lengths[option->flags & TYPE_MASK];
+               retval = 0;
+               opt = buffer; /* new meaning for variable opt */
+               switch (option->flags & TYPE_MASK) {
+               case OPTION_IP:
+                       retval = read_ip(val, buffer);
+                       break;
+               case OPTION_IP_PAIR:
+                       retval = read_ip(val, buffer);
+                       if (!(val = strtok(NULL, ", \t/-"))) retval = 0;
+                       if (retval) retval = read_ip(val, buffer + 4);
+                       break;
+               case OPTION_STRING:
+                       length = strlen(val);
+                       if (length > 0) {
+                               if (length > 254) length = 254;
+                               opt = val;
+                               retval = 1;
+                       }
+                       break;
+               case OPTION_BOOLEAN:
+                       retval = read_yn(val, buffer);
+                       break;
+               case OPTION_U8:
+                       buffer[0] = strtoul(val, &endptr, 0);
+                       retval = (endptr[0] == '\0');
+                       break;
+               case OPTION_U16:
+                       *result_u16 = htons(strtoul(val, &endptr, 0));
+                       retval = (endptr[0] == '\0');
+                       break;
+               case OPTION_S16:
+                       *result_u16 = htons(strtol(val, &endptr, 0));
+                       retval = (endptr[0] == '\0');
+                       break;
+               case OPTION_U32:
+                       *result_u32 = htonl(strtoul(val, &endptr, 0));
+                       retval = (endptr[0] == '\0');
+                       break;
+               case OPTION_S32:
+                       *result_u32 = htonl(strtol(val, &endptr, 0));
+                       retval = (endptr[0] == '\0');
+                       break;
+               default:
+                       break;
+               }
+               if (retval)
+                       attach_option(opt_list, option, opt, length);
+       } while (retval && option->flags & OPTION_LIST);
+       return retval;
+}
+
+static int read_staticlease(const char *const_line, void *arg)
+{
+
+       char *line;
+       char *mac_string;
+       char *ip_string;
+       uint8_t *mac_bytes;
+       uint32_t *ip;
+
+
+       /* Allocate memory for addresses */
+       mac_bytes = xmalloc(sizeof(unsigned char) * 8);
+       ip = xmalloc(sizeof(uint32_t));
+
+       /* Read mac */
+       line = (char *) const_line;
+       mac_string = strtok(line, " \t");
+       read_mac(mac_string, mac_bytes);
+
+       /* Read ip */
+       ip_string = strtok(NULL, " \t");
+       read_ip(ip_string, ip);
+
+       addStaticLease(arg, mac_bytes, ip);
+
+#ifdef UDHCP_DEBUG
+       printStaticLeases(arg);
+#endif
+
+       return 1;
+
+}
+
+
+static const struct config_keyword keywords[] = {
+       /* keyword      handler   variable address              default */
+       {"start",       read_ip,  &(server_config.start),       "192.168.0.20"},
+       {"end",         read_ip,  &(server_config.end),         "192.168.0.254"},
+       {"interface",   read_str, &(server_config.interface),   "eth0"},
+       {"option",      read_opt, &(server_config.options),     ""},
+       {"opt",         read_opt, &(server_config.options),     ""},
+       {"max_leases",  read_u32, &(server_config.max_leases),  "254"},
+       {"remaining",   read_yn,  &(server_config.remaining),   "yes"},
+       {"auto_time",   read_u32, &(server_config.auto_time),   "7200"},
+       {"decline_time",read_u32, &(server_config.decline_time),"3600"},
+       {"conflict_time",read_u32,&(server_config.conflict_time),"3600"},
+       {"offer_time",  read_u32, &(server_config.offer_time),  "60"},
+       {"min_lease",   read_u32, &(server_config.min_lease),   "60"},
+       {"lease_file",  read_str, &(server_config.lease_file),  LEASES_FILE},
+       {"pidfile",     read_str, &(server_config.pidfile),     "/var/run/udhcpd.pid"},
+       {"notify_file", read_str, &(server_config.notify_file), ""},
+       {"siaddr",      read_ip,  &(server_config.siaddr),      "0.0.0.0"},
+       {"sname",       read_str, &(server_config.sname),       ""},
+       {"boot_file",   read_str, &(server_config.boot_file),   ""},
+       {"static_lease",read_staticlease, &(server_config.static_leases),       ""},
+       /*ADDME: static lease */
+       {"",            NULL,     NULL,                         ""}
+};
+
+
+int read_config(const char *file)
+{
+       FILE *in;
+       char buffer[READ_CONFIG_BUF_SIZE], *token, *line;
+#ifdef UDHCP_DEBUG
+       char orig[READ_CONFIG_BUF_SIZE];
+#endif
+       int i, lm = 0;
+
+       for (i = 0; keywords[i].keyword[0]; i++)
+               if (keywords[i].def[0])
+                       keywords[i].handler(keywords[i].def, keywords[i].var);
+
+       if (!(in = fopen(file, "r"))) {
+               LOG(LOG_ERR, "unable to open config file: %s", file);
+               return 0;
+       }
+
+       while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
+               lm++;
+               if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0';
+#ifdef UDHCP_DEBUG
+               strcpy(orig, buffer);
+#endif
+               if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0';
+
+               if (!(token = strtok(buffer, " \t"))) continue;
+               if (!(line = strtok(NULL, ""))) continue;
+
+               /* eat leading whitespace */
+               line = line + strspn(line, " \t=");
+               /* eat trailing whitespace */
+               for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--);
+               line[i] = '\0';
+
+               for (i = 0; keywords[i].keyword[0]; i++)
+                       if (!strcasecmp(token, keywords[i].keyword))
+                               if (!keywords[i].handler(line, keywords[i].var)) {
+                                       LOG(LOG_ERR, "Failure parsing line %d of %s", lm, file);
+                                       DEBUG(LOG_ERR, "unable to parse '%s'", orig);
+                                       /* reset back to the default value */
+                                       keywords[i].handler(keywords[i].def, keywords[i].var);
+                               }
+       }
+       fclose(in);
+       return 1;
+}
+
+
+void write_leases(void)
+{
+       FILE *fp;
+       unsigned int i;
+       char buf[255];
+       time_t curr = time(0);
+       unsigned long tmp_time;
+
+       if (!(fp = fopen(server_config.lease_file, "w"))) {
+               LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file);
+               return;
+       }
+
+       for (i = 0; i < server_config.max_leases; i++) {
+               if (leases[i].yiaddr != 0) {
+
+                       /* screw with the time in the struct, for easier writing */
+                       tmp_time = leases[i].expires;
+
+                       if (server_config.remaining) {
+                               if (lease_expired(&(leases[i])))
+                                       leases[i].expires = 0;
+                               else leases[i].expires -= curr;
+                       } /* else stick with the time we got */
+                       leases[i].expires = htonl(leases[i].expires);
+                       fwrite(&leases[i], sizeof(struct dhcpOfferedAddr), 1, fp);
+
+                       /* Then restore it when done. */
+                       leases[i].expires = tmp_time;
+               }
+       }
+       fclose(fp);
+
+       if (server_config.notify_file) {
+               sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file);
+               system(buf);
+       }
+}
+
+
+void read_leases(const char *file)
+{
+       FILE *fp;
+       unsigned int i = 0;
+       struct dhcpOfferedAddr lease;
+
+       if (!(fp = fopen(file, "r"))) {
+               LOG(LOG_ERR, "Unable to open %s for reading", file);
+               return;
+       }
+
+       while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) {
+               /* ADDME: is it a static lease */
+               if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
+                       lease.expires = ntohl(lease.expires);
+                       if (!server_config.remaining) lease.expires -= time(0);
+                       if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
+                               LOG(LOG_WARNING, "Too many leases while loading %s\n", file);
+                               break;
+                       }
+                       i++;
+               }
+       }
+       DEBUG(LOG_INFO, "Read %d leases", i);
+       fclose(fp);
+}
diff --git a/busybox/networking/udhcp/files.h b/busybox/networking/udhcp/files.h
new file mode 100644 (file)
index 0000000..279738a
--- /dev/null
@@ -0,0 +1,17 @@
+/* files.h */
+#ifndef _FILES_H
+#define _FILES_H
+
+struct config_keyword {
+       const char *keyword;
+       int (* const handler)(const char *line, void *var);
+       void *var;
+       const char *def;
+};
+
+
+int read_config(const char *file);
+void write_leases(void);
+void read_leases(const char *file);
+
+#endif
diff --git a/busybox/networking/udhcp/frontend.c b/busybox/networking/udhcp/frontend.c
new file mode 100644 (file)
index 0000000..fa77ab9
--- /dev/null
@@ -0,0 +1,16 @@
+#include <string.h>
+
+extern int udhcpd_main(int argc, char *argv[]);
+extern int udhcpc_main(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+       int ret = 0;
+       char *base = strrchr(argv[0], '/');
+
+       if (strstr(base ? (base + 1) : argv[0], "dhcpd"))
+               ret = udhcpd_main(argc, argv);
+       else ret = udhcpc_main(argc, argv);
+
+       return ret;
+}
diff --git a/busybox/networking/udhcp/leases.c b/busybox/networking/udhcp/leases.c
new file mode 100644 (file)
index 0000000..4da21a2
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * leases.c -- tools to manage DHCP leases
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <time.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "dhcpd.h"
+#include "files.h"
+#include "options.h"
+#include "leases.h"
+#include "arpping.h"
+#include "common.h"
+
+#include "static_leases.h"
+
+
+uint8_t blank_chaddr[] = {[0 ... 15] = 0};
+
+/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
+void clear_lease(uint8_t *chaddr, uint32_t yiaddr)
+{
+       unsigned int i, j;
+
+       for (j = 0; j < 16 && !chaddr[j]; j++);
+
+       for (i = 0; i < server_config.max_leases; i++)
+               if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
+                   (yiaddr && leases[i].yiaddr == yiaddr)) {
+                       memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
+               }
+}
+
+
+/* add a lease into the table, clearing out any old ones */
+struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease)
+{
+       struct dhcpOfferedAddr *oldest;
+
+       /* clean out any old ones */
+       clear_lease(chaddr, yiaddr);
+
+       oldest = oldest_expired_lease();
+
+       if (oldest) {
+               memcpy(oldest->chaddr, chaddr, 16);
+               oldest->yiaddr = yiaddr;
+               oldest->expires = time(0) + lease;
+       }
+
+       return oldest;
+}
+
+
+/* true if a lease has expired */
+int lease_expired(struct dhcpOfferedAddr *lease)
+{
+       return (lease->expires < (unsigned long) time(0));
+}
+
+
+/* Find the oldest expired lease, NULL if there are no expired leases */
+struct dhcpOfferedAddr *oldest_expired_lease(void)
+{
+       struct dhcpOfferedAddr *oldest = NULL;
+       unsigned long oldest_lease = time(0);
+       unsigned int i;
+
+
+       for (i = 0; i < server_config.max_leases; i++)
+               if (oldest_lease > leases[i].expires) {
+                       oldest_lease = leases[i].expires;
+                       oldest = &(leases[i]);
+               }
+       return oldest;
+
+}
+
+
+/* Find the first lease that matches chaddr, NULL if no match */
+struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr)
+{
+       unsigned int i;
+
+       for (i = 0; i < server_config.max_leases; i++)
+               if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
+
+       return NULL;
+}
+
+
+/* Find the first lease that matches yiaddr, NULL is no match */
+struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr)
+{
+       unsigned int i;
+
+       for (i = 0; i < server_config.max_leases; i++)
+               if (leases[i].yiaddr == yiaddr) return &(leases[i]);
+
+       return NULL;
+}
+
+
+/* check is an IP is taken, if it is, add it to the lease table */
+static int check_ip(uint32_t addr)
+{
+       struct in_addr temp;
+
+       if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
+               temp.s_addr = addr;
+               LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds",
+                       inet_ntoa(temp), server_config.conflict_time);
+               add_lease(blank_chaddr, addr, server_config.conflict_time);
+               return 1;
+       } else return 0;
+}
+
+
+/* find an assignable address, it check_expired is true, we check all the expired leases as well.
+ * Maybe this should try expired leases by age... */
+uint32_t find_address(int check_expired)
+{
+       uint32_t addr, ret;
+       struct dhcpOfferedAddr *lease = NULL;
+
+       addr = ntohl(server_config.start); /* addr is in host order here */
+       for (;addr <= ntohl(server_config.end); addr++) {
+
+               /* ie, 192.168.55.0 */
+               if (!(addr & 0xFF)) continue;
+
+               /* ie, 192.168.55.255 */
+               if ((addr & 0xFF) == 0xFF) continue;
+
+               /* Only do if it isn't an assigned as a static lease */
+               if(!reservedIp(server_config.static_leases, htonl(addr)))
+               {
+
+               /* lease is not taken */
+               ret = htonl(addr);
+               if ((!(lease = find_lease_by_yiaddr(ret)) ||
+
+                    /* or it expired and we are checking for expired leases */
+                    (check_expired  && lease_expired(lease))) &&
+
+                    /* and it isn't on the network */
+                    !check_ip(ret)) {
+                       return ret;
+                       break;
+               }
+       }
+       }
+       return 0;
+}
diff --git a/busybox/networking/udhcp/leases.h b/busybox/networking/udhcp/leases.h
new file mode 100644 (file)
index 0000000..b13fa72
--- /dev/null
@@ -0,0 +1,23 @@
+/* leases.h */
+#ifndef _LEASES_H
+#define _LEASES_H
+
+
+struct dhcpOfferedAddr {
+       uint8_t chaddr[16];
+       uint32_t yiaddr;        /* network order */
+       uint32_t expires;       /* host order */
+};
+
+extern uint8_t blank_chaddr[];
+
+void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
+struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
+int lease_expired(struct dhcpOfferedAddr *lease);
+struct dhcpOfferedAddr *oldest_expired_lease(void);
+struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
+struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
+uint32_t find_address(int check_expired);
+
+
+#endif
diff --git a/busybox/networking/udhcp/libbb_udhcp.h b/busybox/networking/udhcp/libbb_udhcp.h
new file mode 100644 (file)
index 0000000..51e1571
--- /dev/null
@@ -0,0 +1,54 @@
+/* libbb_udhcp.h - busybox compatability wrapper */
+
+/* bit of a hack, do this no matter what the order of the includes.
+ * (for busybox) */
+
+#ifdef CONFIG_INSTALL_NO_USR
+#undef DEFAULT_SCRIPT
+#define DEFAULT_SCRIPT  "/share/udhcpc/default.script"
+#endif
+
+#ifndef _LIBBB_UDHCP_H
+#define _LIBBB_UDHCP_H
+
+#ifdef IN_BUSYBOX
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
+#define UDHCP_SYSLOG
+#endif
+
+#ifdef CONFIG_FEATURE_UDHCP_DEBUG
+#define UDHCP_DEBUG
+#endif
+
+#define COMBINED_BINARY
+#include "version.h"
+
+#define xfopen bb_xfopen
+
+#else /* ! BB_VER */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+
+#define TRUE                   1
+#define FALSE                  0
+
+#define xmalloc malloc
+#define xcalloc calloc
+
+static inline FILE *xfopen(const char *file, const char *mode)
+{
+       FILE *fp;
+       if (!(fp = fopen(file, mode))) {
+               perror("could not open input file");
+               exit(0);
+       }
+       return fp;
+}
+
+#endif /* BB_VER */
+
+#endif /* _LIBBB_UDHCP_H */
diff --git a/busybox/networking/udhcp/options.c b/busybox/networking/udhcp/options.c
new file mode 100644 (file)
index 0000000..d75bc5a
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * options.c -- DHCP server option packet tools
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dhcpd.h"
+#include "files.h"
+#include "options.h"
+#include "common.h"
+
+
+/* supported options are easily added here */
+struct dhcp_option dhcp_options[] = {
+       /* name[10]     flags                                   code */
+       {"subnet",      OPTION_IP | OPTION_REQ,                 0x01},
+       {"timezone",    OPTION_S32,                             0x02},
+       {"router",      OPTION_IP | OPTION_LIST | OPTION_REQ,   0x03},
+       {"timesvr",     OPTION_IP | OPTION_LIST,                0x04},
+       {"namesvr",     OPTION_IP | OPTION_LIST,                0x05},
+       {"dns",         OPTION_IP | OPTION_LIST | OPTION_REQ,   0x06},
+       {"logsvr",      OPTION_IP | OPTION_LIST,                0x07},
+       {"cookiesvr",   OPTION_IP | OPTION_LIST,                0x08},
+       {"lprsvr",      OPTION_IP | OPTION_LIST,                0x09},
+       {"hostname",    OPTION_STRING | OPTION_REQ,             0x0c},
+       {"bootsize",    OPTION_U16,                             0x0d},
+       {"domain",      OPTION_STRING | OPTION_REQ,             0x0f},
+       {"swapsvr",     OPTION_IP,                              0x10},
+       {"rootpath",    OPTION_STRING,                          0x11},
+       {"ipttl",       OPTION_U8,                              0x17},
+       {"mtu",         OPTION_U16,                             0x1a},
+       {"broadcast",   OPTION_IP | OPTION_REQ,                 0x1c},
+       {"ntpsrv",      OPTION_IP | OPTION_LIST,                0x2a},
+       {"wins",        OPTION_IP | OPTION_LIST,                0x2c},
+       {"requestip",   OPTION_IP,                              0x32},
+       {"lease",       OPTION_U32,                             0x33},
+       {"dhcptype",    OPTION_U8,                              0x35},
+       {"serverid",    OPTION_IP,                              0x36},
+       {"message",     OPTION_STRING,                          0x38},
+       {"tftp",        OPTION_STRING,                          0x42},
+       {"bootfile",    OPTION_STRING,                          0x43},
+       {"",            0x00,                           0x00}
+};
+
+/* Lengths of the different option types */
+int option_lengths[] = {
+       [OPTION_IP] =           4,
+       [OPTION_IP_PAIR] =      8,
+       [OPTION_BOOLEAN] =      1,
+       [OPTION_STRING] =       1,
+       [OPTION_U8] =           1,
+       [OPTION_U16] =          2,
+       [OPTION_S16] =          2,
+       [OPTION_U32] =          4,
+       [OPTION_S32] =          4
+};
+
+
+/* get an option with bounds checking (warning, not aligned). */
+uint8_t *get_option(struct dhcpMessage *packet, int code)
+{
+       int i, length;
+       uint8_t *optionptr;
+       int over = 0, done = 0, curr = OPTION_FIELD;
+
+       optionptr = packet->options;
+       i = 0;
+       length = 308;
+       while (!done) {
+               if (i >= length) {
+                       LOG(LOG_WARNING, "bogus packet, option fields too long.");
+                       return NULL;
+               }
+               if (optionptr[i + OPT_CODE] == code) {
+                       if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+                               LOG(LOG_WARNING, "bogus packet, option fields too long.");
+                               return NULL;
+                       }
+                       return optionptr + i + 2;
+               }
+               switch (optionptr[i + OPT_CODE]) {
+               case DHCP_PADDING:
+                       i++;
+                       break;
+               case DHCP_OPTION_OVER:
+                       if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+                               LOG(LOG_WARNING, "bogus packet, option fields too long.");
+                               return NULL;
+                       }
+                       over = optionptr[i + 3];
+                       i += optionptr[OPT_LEN] + 2;
+                       break;
+               case DHCP_END:
+                       if (curr == OPTION_FIELD && over & FILE_FIELD) {
+                               optionptr = packet->file;
+                               i = 0;
+                               length = 128;
+                               curr = FILE_FIELD;
+                       } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
+                               optionptr = packet->sname;
+                               i = 0;
+                               length = 64;
+                               curr = SNAME_FIELD;
+                       } else done = 1;
+                       break;
+               default:
+                       i += optionptr[OPT_LEN + i] + 2;
+               }
+       }
+       return NULL;
+}
+
+
+/* return the position of the 'end' option (no bounds checking) */
+int end_option(uint8_t *optionptr)
+{
+       int i = 0;
+
+       while (optionptr[i] != DHCP_END) {
+               if (optionptr[i] == DHCP_PADDING) i++;
+               else i += optionptr[i + OPT_LEN] + 2;
+       }
+       return i;
+}
+
+
+/* add an option string to the options (an option string contains an option code,
+ * length, then data) */
+int add_option_string(uint8_t *optionptr, uint8_t *string)
+{
+       int end = end_option(optionptr);
+
+       /* end position + string length + option code/length + end option */
+       if (end + string[OPT_LEN] + 2 + 1 >= 308) {
+               LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
+               return 0;
+       }
+       DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
+       memcpy(optionptr + end, string, string[OPT_LEN] + 2);
+       optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
+       return string[OPT_LEN] + 2;
+}
+
+
+/* add a one to four byte option to a packet */
+int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
+{
+       char length = 0;
+       int i;
+       uint8_t option[2 + 4];
+       uint8_t *u8;
+       uint16_t *u16;
+       uint32_t *u32;
+       uint32_t aligned;
+       u8 = (uint8_t *) &aligned;
+       u16 = (uint16_t *) &aligned;
+       u32 = &aligned;
+
+       for (i = 0; dhcp_options[i].code; i++)
+               if (dhcp_options[i].code == code) {
+                       length = option_lengths[dhcp_options[i].flags & TYPE_MASK];
+               }
+
+       if (!length) {
+               DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
+               return 0;
+       }
+
+       option[OPT_CODE] = code;
+       option[OPT_LEN] = length;
+
+       switch (length) {
+               case 1: *u8 =  data; break;
+               case 2: *u16 = data; break;
+               case 4: *u32 = data; break;
+       }
+       memcpy(option + 2, &aligned, length);
+       return add_option_string(optionptr, option);
+}
+
+
+/* find option 'code' in opt_list */
+struct option_set *find_option(struct option_set *opt_list, char code)
+{
+       while (opt_list && opt_list->data[OPT_CODE] < code)
+               opt_list = opt_list->next;
+
+       if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
+       else return NULL;
+}
+
+
+/* add an option to the opt_list */
+void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
+{
+       struct option_set *existing, *new, **curr;
+
+       /* add it to an existing option */
+       if ((existing = find_option(*opt_list, option->code))) {
+               DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
+               if (option->flags & OPTION_LIST) {
+                       if (existing->data[OPT_LEN] + length <= 255) {
+                               existing->data = realloc(existing->data,
+                                               existing->data[OPT_LEN] + length + 2);
+                               memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
+                               existing->data[OPT_LEN] += length;
+                       } /* else, ignore the data, we could put this in a second option in the future */
+               } /* else, ignore the new data */
+       } else {
+               DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
+
+               /* make a new option */
+               new = xmalloc(sizeof(struct option_set));
+               new->data = xmalloc(length + 2);
+               new->data[OPT_CODE] = option->code;
+               new->data[OPT_LEN] = length;
+               memcpy(new->data + 2, buffer, length);
+
+               curr = opt_list;
+               while (*curr && (*curr)->data[OPT_CODE] < option->code)
+                       curr = &(*curr)->next;
+
+               new->next = *curr;
+               *curr = new;
+       }
+}
diff --git a/busybox/networking/udhcp/options.h b/busybox/networking/udhcp/options.h
new file mode 100644 (file)
index 0000000..fbe54c8
--- /dev/null
@@ -0,0 +1,40 @@
+/* options.h */
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
+
+#include "packet.h"
+
+#define TYPE_MASK      0x0F
+
+enum {
+       OPTION_IP=1,
+       OPTION_IP_PAIR,
+       OPTION_STRING,
+       OPTION_BOOLEAN,
+       OPTION_U8,
+       OPTION_U16,
+       OPTION_S16,
+       OPTION_U32,
+       OPTION_S32
+};
+
+#define OPTION_REQ     0x10 /* have the client request this option */
+#define OPTION_LIST    0x20 /* There can be a list of 1 or more of these */
+
+struct dhcp_option {
+       char name[10];
+       char flags;
+       uint8_t code;
+};
+
+extern struct dhcp_option dhcp_options[];
+extern int option_lengths[];
+
+uint8_t *get_option(struct dhcpMessage *packet, int code);
+int end_option(uint8_t *optionptr);
+int add_option_string(uint8_t *optionptr, uint8_t *string);
+int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
+struct option_set *find_option(struct option_set *opt_list, char code);
+void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);
+
+#endif
diff --git a/busybox/networking/udhcp/packet.c b/busybox/networking/udhcp/packet.c
new file mode 100644 (file)
index 0000000..1c6bb0c
--- /dev/null
@@ -0,0 +1,202 @@
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+#include <errno.h>
+
+#include "packet.h"
+#include "dhcpd.h"
+#include "options.h"
+#include "common.h"
+
+
+void init_header(struct dhcpMessage *packet, char type)
+{
+       memset(packet, 0, sizeof(struct dhcpMessage));
+       switch (type) {
+       case DHCPDISCOVER:
+       case DHCPREQUEST:
+       case DHCPRELEASE:
+       case DHCPINFORM:
+               packet->op = BOOTREQUEST;
+               break;
+       case DHCPOFFER:
+       case DHCPACK:
+       case DHCPNAK:
+               packet->op = BOOTREPLY;
+       }
+       packet->htype = ETH_10MB;
+       packet->hlen = ETH_10MB_LEN;
+       packet->cookie = htonl(DHCP_MAGIC);
+       packet->options[0] = DHCP_END;
+       add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
+}
+
+
+/* read a packet from socket fd, return -1 on read error, -2 on packet error */
+int get_packet(struct dhcpMessage *packet, int fd)
+{
+       int bytes;
+       int i;
+       const char broken_vendors[][8] = {
+               "MSFT 98",
+               ""
+       };
+       char unsigned *vendor;
+
+       memset(packet, 0, sizeof(struct dhcpMessage));
+       bytes = read(fd, packet, sizeof(struct dhcpMessage));
+       if (bytes < 0) {
+               DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring");
+               return -1;
+       }
+
+       if (ntohl(packet->cookie) != DHCP_MAGIC) {
+               LOG(LOG_ERR, "received bogus message, ignoring");
+               return -2;
+       }
+       DEBUG(LOG_INFO, "Received a packet");
+
+       if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
+               for (i = 0; broken_vendors[i][0]; i++) {
+                       if (vendor[OPT_LEN - 2] == (uint8_t) strlen(broken_vendors[i]) &&
+                           !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) {
+                               DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
+                                       broken_vendors[i]);
+                               packet->flags |= htons(BROADCAST_FLAG);
+                       }
+               }
+       }
+                       
+
+       return bytes;
+}
+
+
+uint16_t checksum(void *addr, int count)
+{
+       /* Compute Internet Checksum for "count" bytes
+        *         beginning at location "addr".
+        */
+       register int32_t sum = 0;
+       uint16_t *source = (uint16_t *) addr;
+
+       while (count > 1)  {
+               /*  This is the inner loop */
+               sum += *source++;
+               count -= 2;
+       }
+
+       /*  Add left-over byte, if any */
+       if (count > 0) {
+               /* Make sure that the left-over byte is added correctly both
+                * with little and big endian hosts */
+               uint16_t tmp = 0;
+               *(uint8_t *) (&tmp) = * (uint8_t *) source;
+               sum += tmp;
+       }
+       /*  Fold 32-bit sum to 16 bits */
+       while (sum >> 16)
+               sum = (sum & 0xffff) + (sum >> 16);
+
+       return ~sum;
+}
+
+
+/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
+int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
+                  uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
+{
+       int fd;
+       int result;
+       struct sockaddr_ll dest;
+       struct udp_dhcp_packet packet;
+
+       if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+               DEBUG(LOG_ERR, "socket call failed: %m");
+               return -1;
+       }
+
+       memset(&dest, 0, sizeof(dest));
+       memset(&packet, 0, sizeof(packet));
+
+       dest.sll_family = AF_PACKET;
+       dest.sll_protocol = htons(ETH_P_IP);
+       dest.sll_ifindex = ifindex;
+       dest.sll_halen = 6;
+       memcpy(dest.sll_addr, dest_arp, 6);
+       if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
+               DEBUG(LOG_ERR, "bind call failed: %m");
+               close(fd);
+               return -1;
+       }
+
+       packet.ip.protocol = IPPROTO_UDP;
+       packet.ip.saddr = source_ip;
+       packet.ip.daddr = dest_ip;
+       packet.udp.source = htons(source_port);
+       packet.udp.dest = htons(dest_port);
+       packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
+       packet.ip.tot_len = packet.udp.len;
+       memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
+       packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
+
+       packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
+       packet.ip.ihl = sizeof(packet.ip) >> 2;
+       packet.ip.version = IPVERSION;
+       packet.ip.ttl = IPDEFTTL;
+       packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
+
+       result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
+       if (result <= 0) {
+               DEBUG(LOG_ERR, "write on socket failed: %m");
+       }
+       close(fd);
+       return result;
+}
+
+
+/* Let the kernel do all the work for packet generation */
+int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
+                  uint32_t dest_ip, int dest_port)
+{
+       int n = 1;
+       int fd, result;
+       struct sockaddr_in client;
+
+       if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+               return -1;
+
+       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
+               return -1;
+
+       memset(&client, 0, sizeof(client));
+       client.sin_family = AF_INET;
+       client.sin_port = htons(source_port);
+       client.sin_addr.s_addr = source_ip;
+
+       if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
+               return -1;
+
+       memset(&client, 0, sizeof(client));
+       client.sin_family = AF_INET;
+       client.sin_port = htons(dest_port);
+       client.sin_addr.s_addr = dest_ip;
+
+       if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
+               return -1;
+
+       result = write(fd, payload, sizeof(struct dhcpMessage));
+       close(fd);
+       return result;
+}
diff --git a/busybox/networking/udhcp/packet.h b/busybox/networking/udhcp/packet.h
new file mode 100644 (file)
index 0000000..f5859e8
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _PACKET_H
+#define _PACKET_H
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+struct dhcpMessage {
+       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 siaddr;
+       uint32_t giaddr;
+       uint8_t chaddr[16];
+       uint8_t sname[64];
+       uint8_t file[128];
+       uint32_t cookie;
+       uint8_t options[308]; /* 312 - cookie */
+};
+
+struct udp_dhcp_packet {
+       struct iphdr ip;
+       struct udphdr udp;
+       struct dhcpMessage data;
+};
+
+void init_header(struct dhcpMessage *packet, char type);
+int get_packet(struct dhcpMessage *packet, int fd);
+uint16_t checksum(void *addr, int count);
+int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
+                  uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
+int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
+                  uint32_t dest_ip, int dest_port);
+
+
+#endif
diff --git a/busybox/networking/udhcp/pidfile.c b/busybox/networking/udhcp/pidfile.c
new file mode 100644 (file)
index 0000000..2e0c753
--- /dev/null
@@ -0,0 +1,75 @@
+/* pidfile.c
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pidfile.h"
+#include "common.h"
+
+static char *saved_pidfile;
+
+static void pidfile_delete(void)
+{
+       if (saved_pidfile) unlink(saved_pidfile);
+}
+
+
+int pidfile_acquire(const char *pidfile)
+{
+       int pid_fd;
+       if (!pidfile) return -1;
+
+       pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
+       if (pid_fd < 0) {
+               LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile);
+       } else {
+               lockf(pid_fd, F_LOCK, 0);
+               if (!saved_pidfile)
+                       atexit(pidfile_delete);
+               saved_pidfile = (char *) pidfile;
+       }
+
+       return pid_fd;
+}
+
+
+void pidfile_write_release(int pid_fd)
+{
+       FILE *out;
+
+       if (pid_fd < 0) return;
+
+       if ((out = fdopen(pid_fd, "w")) != NULL) {
+               fprintf(out, "%d\n", getpid());
+               fclose(out);
+       }
+       lockf(pid_fd, F_UNLCK, 0);
+       close(pid_fd);
+}
+
+
+
+
diff --git a/busybox/networking/udhcp/pidfile.h b/busybox/networking/udhcp/pidfile.h
new file mode 100644 (file)
index 0000000..ea97d1d
--- /dev/null
@@ -0,0 +1,25 @@
+/* pidfile.h
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+int pidfile_acquire(const char *pidfile);
+void pidfile_write_release(int pid_fd);
+
diff --git a/busybox/networking/udhcp/script.c b/busybox/networking/udhcp/script.c
new file mode 100644 (file)
index 0000000..820fbb0
--- /dev/null
@@ -0,0 +1,233 @@
+/* script.c
+ *
+ * Functions to call the DHCP client notification scripts
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "options.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "common.h"
+
+/* get a rough idea of how long an option will be (rounding up...) */
+static const int max_option_length[] = {
+       [OPTION_IP] =           sizeof("255.255.255.255 "),
+       [OPTION_IP_PAIR] =      sizeof("255.255.255.255 ") * 2,
+       [OPTION_STRING] =       1,
+       [OPTION_BOOLEAN] =      sizeof("yes "),
+       [OPTION_U8] =           sizeof("255 "),
+       [OPTION_U16] =          sizeof("65535 "),
+       [OPTION_S16] =          sizeof("-32768 "),
+       [OPTION_U32] =          sizeof("4294967295 "),
+       [OPTION_S32] =          sizeof("-2147483684 "),
+};
+
+
+static inline int upper_length(int length, int opt_index)
+{
+       return max_option_length[opt_index] *
+               (length / option_lengths[opt_index]);
+}
+
+
+static int sprintip(char *dest, char *pre, uint8_t *ip)
+{
+       return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
+}
+
+
+/* really simple implementation, just count the bits */
+static int mton(struct in_addr *mask)
+{
+       int i;
+       unsigned long bits = ntohl(mask->s_addr);
+       /* too bad one can't check the carry bit, etc in c bit
+        * shifting */
+       for (i = 0; i < 32 && !((bits >> i) & 1); i++);
+       return 32 - i;
+}
+
+
+/* Fill dest with the text of option 'option'. */
+static void fill_options(char *dest, uint8_t *option, struct dhcp_option *type_p)
+{
+       int type, optlen;
+       uint16_t val_u16;
+       int16_t val_s16;
+       uint32_t val_u32;
+       int32_t val_s32;
+       int len = option[OPT_LEN - 2];
+
+       dest += sprintf(dest, "%s=", type_p->name);
+
+       type = type_p->flags & TYPE_MASK;
+       optlen = option_lengths[type];
+       for(;;) {
+               switch (type) {
+               case OPTION_IP_PAIR:
+                       dest += sprintip(dest, "", option);
+                       *(dest++) = '/';
+                       option += 4;
+                       optlen = 4;
+               case OPTION_IP: /* Works regardless of host byte order. */
+                       dest += sprintip(dest, "", option);
+                       break;
+               case OPTION_BOOLEAN:
+                       dest += sprintf(dest, *option ? "yes" : "no");
+                       break;
+               case OPTION_U8:
+                       dest += sprintf(dest, "%u", *option);
+                       break;
+               case OPTION_U16:
+                       memcpy(&val_u16, option, 2);
+                       dest += sprintf(dest, "%u", ntohs(val_u16));
+                       break;
+               case OPTION_S16:
+                       memcpy(&val_s16, option, 2);
+                       dest += sprintf(dest, "%d", ntohs(val_s16));
+                       break;
+               case OPTION_U32:
+                       memcpy(&val_u32, option, 4);
+                       dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
+                       break;
+               case OPTION_S32:
+                       memcpy(&val_s32, option, 4);
+                       dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
+                       break;
+               case OPTION_STRING:
+                       memcpy(dest, option, len);
+                       dest[len] = '\0';
+                       return;  /* Short circuit this case */
+               }
+               option += optlen;
+               len -= optlen;
+               if (len <= 0) break;
+               dest += sprintf(dest, " ");
+       }
+}
+
+
+/* put all the parameters into an environment */
+static char **fill_envp(struct dhcpMessage *packet)
+{
+       int num_options = 0;
+       int i, j;
+       char **envp;
+       uint8_t *temp;
+       struct in_addr subnet;
+       char over = 0;
+
+       if (packet == NULL)
+               num_options = 0;
+       else {
+               for (i = 0; dhcp_options[i].code; i++)
+                       if (get_option(packet, dhcp_options[i].code)) {
+                               num_options++;
+                               if (dhcp_options[i].code == DHCP_SUBNET)
+                                       num_options++; /* for mton */
+                       }
+               if (packet->siaddr) num_options++;
+               if ((temp = get_option(packet, DHCP_OPTION_OVER)))
+                       over = *temp;
+               if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
+               if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
+       }
+
+       envp = xcalloc(sizeof(char *), num_options + 5);
+       j = 0;
+       asprintf(&envp[j++], "interface=%s", client_config.interface);
+       asprintf(&envp[j++], "%s=%s", "PATH",
+               getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
+       asprintf(&envp[j++], "%s=%s", "HOME", getenv("HOME") ? : "/");
+
+       if (packet == NULL) return envp;
+
+       envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
+       sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
+
+
+       for (i = 0; dhcp_options[i].code; i++) {
+               if (!(temp = get_option(packet, dhcp_options[i].code)))
+                       continue;
+               envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2],
+                       dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2);
+               fill_options(envp[j++], temp, &dhcp_options[i]);
+
+               /* Fill in a subnet bits option for things like /24 */
+               if (dhcp_options[i].code == DHCP_SUBNET) {
+                       memcpy(&subnet, temp, 4);
+                       asprintf(&envp[j++], "mask=%d", mton(&subnet));
+               }
+       }
+       if (packet->siaddr) {
+               envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
+               sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
+       }
+       if (!(over & FILE_FIELD) && packet->file[0]) {
+               /* watch out for invalid packets */
+               packet->file[sizeof(packet->file) - 1] = '\0';
+               asprintf(&envp[j++], "boot_file=%s", packet->file);
+       }
+       if (!(over & SNAME_FIELD) && packet->sname[0]) {
+               /* watch out for invalid packets */
+               packet->sname[sizeof(packet->sname) - 1] = '\0';
+               asprintf(&envp[j++], "sname=%s", packet->sname);
+       }
+       return envp;
+}
+
+
+/* Call a script with a par file and env vars */
+void run_script(struct dhcpMessage *packet, const char *name)
+{
+       int pid;
+       char **envp, **curr;
+
+       if (client_config.script == NULL)
+               return;
+
+       DEBUG(LOG_INFO, "vforking and execle'ing %s", client_config.script);
+
+       envp = fill_envp(packet);
+       /* call script */
+       pid = vfork();
+       if (pid) {
+               waitpid(pid, NULL, 0);
+               for (curr = envp; *curr; curr++) free(*curr);
+               free(envp);
+               return;
+       } else if (pid == 0) {
+               /* close fd's? */
+
+               /* exec script */
+               execle(client_config.script, client_config.script,
+                      name, NULL, envp);
+               LOG(LOG_ERR, "script %s failed: %m", client_config.script);
+               exit(1);
+       }
+}
diff --git a/busybox/networking/udhcp/script.h b/busybox/networking/udhcp/script.h
new file mode 100644 (file)
index 0000000..87a20cc
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _SCRIPT_H
+#define _SCRIPT_H
+
+void run_script(struct dhcpMessage *packet, const char *name);
+
+#endif
diff --git a/busybox/networking/udhcp/serverpacket.c b/busybox/networking/udhcp/serverpacket.c
new file mode 100644 (file)
index 0000000..75d55bd
--- /dev/null
@@ -0,0 +1,275 @@
+/* serverpacket.c
+ *
+ * Construct and send DHCP server packets
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <time.h>
+
+#include "serverpacket.h"
+#include "dhcpd.h"
+#include "options.h"
+#include "common.h"
+#include "static_leases.h"
+
+/* send a packet to giaddr using the kernel ip stack */
+static int send_packet_to_relay(struct dhcpMessage *payload)
+{
+       DEBUG(LOG_INFO, "Forwarding packet to relay");
+
+       return kernel_packet(payload, server_config.server, SERVER_PORT,
+                       payload->giaddr, SERVER_PORT);
+}
+
+
+/* send a packet to a specific arp address and ip address by creating our own ip packet */
+static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
+{
+       uint8_t *chaddr;
+       uint32_t ciaddr;
+
+       if (force_broadcast) {
+               DEBUG(LOG_INFO, "broadcasting packet to client (NAK)");
+               ciaddr = INADDR_BROADCAST;
+               chaddr = MAC_BCAST_ADDR;
+       } else if (payload->ciaddr) {
+               DEBUG(LOG_INFO, "unicasting packet to client ciaddr");
+               ciaddr = payload->ciaddr;
+               chaddr = payload->chaddr;
+       } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
+               DEBUG(LOG_INFO, "broadcasting packet to client (requested)");
+               ciaddr = INADDR_BROADCAST;
+               chaddr = MAC_BCAST_ADDR;
+       } else {
+               DEBUG(LOG_INFO, "unicasting packet to client yiaddr");
+               ciaddr = payload->yiaddr;
+               chaddr = payload->chaddr;
+       }
+       return raw_packet(payload, server_config.server, SERVER_PORT,
+                       ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
+}
+
+
+/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
+static int send_packet(struct dhcpMessage *payload, int force_broadcast)
+{
+       int ret;
+
+       if (payload->giaddr)
+               ret = send_packet_to_relay(payload);
+       else ret = send_packet_to_client(payload, force_broadcast);
+       return ret;
+}
+
+
+static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
+{
+       init_header(packet, type);
+       packet->xid = oldpacket->xid;
+       memcpy(packet->chaddr, oldpacket->chaddr, 16);
+       packet->flags = oldpacket->flags;
+       packet->giaddr = oldpacket->giaddr;
+       packet->ciaddr = oldpacket->ciaddr;
+       add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
+}
+
+
+/* add in the bootp options */
+static void add_bootp_options(struct dhcpMessage *packet)
+{
+       packet->siaddr = server_config.siaddr;
+       if (server_config.sname)
+               strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1);
+       if (server_config.boot_file)
+               strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1);
+}
+
+
+/* send a DHCP OFFER to a DHCP DISCOVER */
+int sendOffer(struct dhcpMessage *oldpacket)
+{
+       struct dhcpMessage packet;
+       struct dhcpOfferedAddr *lease = NULL;
+       uint32_t req_align, lease_time_align = server_config.lease;
+       uint8_t *req, *lease_time;
+       struct option_set *curr;
+       struct in_addr addr;
+
+       uint32_t static_lease_ip;
+
+       init_packet(&packet, oldpacket, DHCPOFFER);
+
+       static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr);
+
+       /* ADDME: if static, short circuit */
+       if(!static_lease_ip)
+       {
+       /* the client is in our lease/offered table */
+       if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) {
+               if (!lease_expired(lease))
+                       lease_time_align = lease->expires - time(0);
+               packet.yiaddr = lease->yiaddr;
+
+       /* Or the client has a requested ip */
+       } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) &&
+
+                  /* Don't look here (ugly hackish thing to do) */
+                  memcpy(&req_align, req, 4) &&
+
+                  /* and the ip is in the lease range */
+                  ntohl(req_align) >= ntohl(server_config.start) &&
+                  ntohl(req_align) <= ntohl(server_config.end) &&
+               
+                       !static_lease_ip &&  /* Check that its not a static lease */
+                       /* and is not already taken/offered */
+                  ((!(lease = find_lease_by_yiaddr(req_align)) ||
+               
+                  /* or its taken, but expired */ /* ADDME: or maybe in here */
+                  lease_expired(lease)))) {
+                               packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */
+
+                       /* otherwise, find a free IP */
+       } else {
+                       /* Is it a static lease? (No, because find_address skips static lease) */
+               packet.yiaddr = find_address(0);
+
+               /* try for an expired lease */
+               if (!packet.yiaddr) packet.yiaddr = find_address(1);
+       }
+
+       if(!packet.yiaddr) {
+               LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned");
+               return -1;
+       }
+
+       if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
+               LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned");
+               return -1;
+       }
+
+       if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
+               memcpy(&lease_time_align, lease_time, 4);
+               lease_time_align = ntohl(lease_time_align);
+               if (lease_time_align > server_config.lease)
+                       lease_time_align = server_config.lease;
+       }
+
+       /* Make sure we aren't just using the lease time from the previous offer */
+       if (lease_time_align < server_config.min_lease)
+               lease_time_align = server_config.lease;
+       }
+       /* ADDME: end of short circuit */
+       else
+       {
+               /* It is a static lease... use it */
+               packet.yiaddr = static_lease_ip;
+       }
+
+       add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+
+       curr = server_config.options;
+       while (curr) {
+               if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+                       add_option_string(packet.options, curr->data);
+               curr = curr->next;
+       }
+
+       add_bootp_options(&packet);
+
+       addr.s_addr = packet.yiaddr;
+       LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr));
+       return send_packet(&packet, 0);
+}
+
+
+int sendNAK(struct dhcpMessage *oldpacket)
+{
+       struct dhcpMessage packet;
+
+       init_packet(&packet, oldpacket, DHCPNAK);
+
+       DEBUG(LOG_INFO, "sending NAK");
+       return send_packet(&packet, 1);
+}
+
+
+int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr)
+{
+       struct dhcpMessage packet;
+       struct option_set *curr;
+       uint8_t *lease_time;
+       uint32_t lease_time_align = server_config.lease;
+       struct in_addr addr;
+
+       init_packet(&packet, oldpacket, DHCPACK);
+       packet.yiaddr = yiaddr;
+
+       if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
+               memcpy(&lease_time_align, lease_time, 4);
+               lease_time_align = ntohl(lease_time_align);
+               if (lease_time_align > server_config.lease)
+                       lease_time_align = server_config.lease;
+               else if (lease_time_align < server_config.min_lease)
+                       lease_time_align = server_config.lease;
+       }
+
+       add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+
+       curr = server_config.options;
+       while (curr) {
+               if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+                       add_option_string(packet.options, curr->data);
+               curr = curr->next;
+       }
+
+       add_bootp_options(&packet);
+
+       addr.s_addr = packet.yiaddr;
+       LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr));
+
+       if (send_packet(&packet, 0) < 0)
+               return -1;
+
+       add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
+
+       return 0;
+}
+
+
+int send_inform(struct dhcpMessage *oldpacket)
+{
+       struct dhcpMessage packet;
+       struct option_set *curr;
+
+       init_packet(&packet, oldpacket, DHCPACK);
+
+       curr = server_config.options;
+       while (curr) {
+               if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+                       add_option_string(packet.options, curr->data);
+               curr = curr->next;
+       }
+
+       add_bootp_options(&packet);
+
+       return send_packet(&packet, 0);
+}
diff --git a/busybox/networking/udhcp/serverpacket.h b/busybox/networking/udhcp/serverpacket.h
new file mode 100644 (file)
index 0000000..9024928
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _SERVERPACKET_H
+#define _SERVERPACKET_H
+
+#include "packet.h"
+
+int sendOffer(struct dhcpMessage *oldpacket);
+int sendNAK(struct dhcpMessage *oldpacket);
+int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr);
+int send_inform(struct dhcpMessage *oldpacket);
+
+
+#endif
diff --git a/busybox/networking/udhcp/signalpipe.c b/busybox/networking/udhcp/signalpipe.c
new file mode 100644 (file)
index 0000000..074c8a6
--- /dev/null
@@ -0,0 +1,78 @@
+/* signalpipe.c
+ *
+ * Signal pipe infrastructure. A reliable way of delivering signals.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+
+#include "signalpipe.h"
+#include "common.h"
+
+static int signal_pipe[2];
+
+static void signal_handler(int sig)
+{
+       if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
+               DEBUG(LOG_ERR, "Could not send signal: %m");
+}
+
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+void udhcp_sp_setup(void)
+{
+       socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
+       signal(SIGUSR1, signal_handler);
+       signal(SIGUSR2, signal_handler);
+       signal(SIGTERM, signal_handler);
+}
+
+
+/* Quick little function to setup the rfds. Will return the
+ * max_fd for use with select. Limited in that you can only pass
+ * one extra fd */
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
+{
+       FD_ZERO(rfds);
+       FD_SET(signal_pipe[0], rfds);
+       if (extra_fd >= 0) FD_SET(extra_fd, rfds);
+       return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
+}
+
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int udhcp_sp_read(fd_set *rfds)
+{
+       int sig;
+
+       if (!FD_ISSET(signal_pipe[0], rfds))
+               return 0;
+
+       if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
+               return -1;
+
+       return sig;
+}
diff --git a/busybox/networking/udhcp/signalpipe.h b/busybox/networking/udhcp/signalpipe.h
new file mode 100644 (file)
index 0000000..70c1e57
--- /dev/null
@@ -0,0 +1,22 @@
+/* signalpipe.h
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+void udhcp_sp_setup(void);
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
+int udhcp_sp_read(fd_set *rfds);
diff --git a/busybox/networking/udhcp/socket.c b/busybox/networking/udhcp/socket.c
new file mode 100644 (file)
index 0000000..7b05752
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * socket.c -- DHCP server client/server socket creation
+ *
+ * udhcp client/server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ *                     Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <errno.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "socket.h"
+#include "common.h"
+
+int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
+{
+       int fd;
+       struct ifreq ifr;
+       struct sockaddr_in *our_ip;
+
+       memset(&ifr, 0, sizeof(struct ifreq));
+       if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
+               ifr.ifr_addr.sa_family = AF_INET;
+               strcpy(ifr.ifr_name, interface);
+
+               if (addr) {
+                       if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
+                               our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+                               *addr = our_ip->sin_addr.s_addr;
+                               DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
+                       } else {
+                               LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m");
+                               return -1;
+                       }
+               }
+
+               if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
+                       DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
+                       *ifindex = ifr.ifr_ifindex;
+               } else {
+                       LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m");
+                       return -1;
+               }
+               if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
+                       memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
+                       DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
+                               arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
+               } else {
+                       LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m");
+                       return -1;
+               }
+       } else {
+               LOG(LOG_ERR, "socket failed!: %m");
+               return -1;
+       }
+       close(fd);
+       return 0;
+}
+
+
+int listen_socket(uint32_t ip, int port, char *inf)
+{
+       struct ifreq interface;
+       int fd;
+       struct sockaddr_in addr;
+       int n = 1;
+
+       DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s", ip, port, inf);
+       if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+               DEBUG(LOG_ERR, "socket call failed: %m");
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(port);
+       addr.sin_addr.s_addr = ip;
+
+       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) {
+               close(fd);
+               return -1;
+       }
+       if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) {
+               close(fd);
+               return -1;
+       }
+
+       strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ);
+       if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
diff --git a/busybox/networking/udhcp/socket.h b/busybox/networking/udhcp/socket.h
new file mode 100644 (file)
index 0000000..66179d4
--- /dev/null
@@ -0,0 +1,8 @@
+/* socket.h */
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
+int listen_socket(uint32_t ip, int port, char *inf);
+
+#endif
diff --git a/busybox/networking/udhcp/static_leases.c b/busybox/networking/udhcp/static_leases.c
new file mode 100644 (file)
index 0000000..1124d39
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * static_leases.c -- Couple of functions to assist with storing and
+ * retrieving data for static leases
+ *
+ * Wade Berrier <wberrier@myrealbox.com> September 2004
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "static_leases.h"
+#include "dhcpd.h"
+
+/* Takes the address of the pointer to the static_leases linked list,
+ *   Address to a 6 byte mac address
+ *   Address to a 4 byte ip address */
+int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip)
+{
+
+       struct static_lease *cur;
+       struct static_lease *new_static_lease;
+
+       /* Build new node */
+       new_static_lease = xmalloc(sizeof(struct static_lease));
+       new_static_lease->mac = mac;
+       new_static_lease->ip = ip;
+       new_static_lease->next = NULL;
+
+       /* If it's the first node to be added... */
+       if(*lease_struct == NULL)
+       {
+               *lease_struct = new_static_lease;
+       }
+       else
+       {
+               cur = *lease_struct;
+               while(cur->next != NULL)
+               {
+                       cur = cur->next;
+               }
+
+               cur->next = new_static_lease;
+       }
+
+       return 1;
+
+}
+
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg)
+{
+       uint32_t return_ip;
+       struct static_lease *cur = lease_struct;
+       uint8_t *mac = arg;
+
+       return_ip = 0;
+
+       while(cur != NULL)
+       {
+               /* If the client has the correct mac  */
+               if(memcmp(cur->mac, mac, 6) == 0)
+               {
+                       return_ip = *(cur->ip);
+               }
+
+               cur = cur->next;
+       }
+
+       return return_ip;
+
+}
+
+/* Check to see if an ip is reserved as a static ip */
+uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip)
+{
+       struct static_lease *cur = lease_struct;
+
+       uint32_t return_val = 0;
+
+       while(cur != NULL)
+       {
+               /* If the client has the correct ip  */
+               if(*cur->ip == ip)
+                       return_val = 1;
+
+               cur = cur->next;
+       }
+
+       return return_val;
+
+}
+
+#ifdef UDHCP_DEBUG
+/* Print out static leases just to check what's going on */
+/* Takes the address of the pointer to the static_leases linked list */
+void printStaticLeases(struct static_lease **arg)
+{
+       /* Get a pointer to the linked list */
+       struct static_lease *cur = *arg;
+
+       while(cur != NULL)
+       {
+               /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */
+               printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac));
+               /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */
+               printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip));
+
+               cur = cur->next;
+       }
+
+
+}
+#endif
+
+
+
diff --git a/busybox/networking/udhcp/static_leases.h b/busybox/networking/udhcp/static_leases.h
new file mode 100644 (file)
index 0000000..d06520b
--- /dev/null
@@ -0,0 +1,25 @@
+/* static_leases.h */
+#ifndef _STATIC_LEASES_H
+#define _STATIC_LEASES_H
+
+#include "dhcpd.h"
+
+/* Config file will pass static lease info to this function which will add it
+ * to a data structure that can be searched later */
+int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip);
+
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg);
+
+/* Check to see if an ip is reserved as a static ip */
+uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip);
+
+#ifdef UDHCP_DEBUG
+/* Print out static leases just to check what's going on */
+void printStaticLeases(struct static_lease **lease_struct);
+#endif
+
+#endif
+
+
+
diff --git a/busybox/networking/udhcp/version.h b/busybox/networking/udhcp/version.h
new file mode 100644 (file)
index 0000000..3862539
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _UDHCP_VERSION_H
+#define _UDHCP_VERSION_H
+
+#define VERSION "0.9.9-pre"
+
+#endif
diff --git a/busybox/networking/vconfig.c b/busybox/networking/vconfig.c
new file mode 100644 (file)
index 0000000..bbd2987
--- /dev/null
@@ -0,0 +1,184 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vconfig implementation for busybox
+ *
+ * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <limits.h>
+#include "busybox.h"
+
+/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */
+enum vlan_ioctl_cmds {
+       ADD_VLAN_CMD,
+       DEL_VLAN_CMD,
+       SET_VLAN_INGRESS_PRIORITY_CMD,
+       SET_VLAN_EGRESS_PRIORITY_CMD,
+       GET_VLAN_INGRESS_PRIORITY_CMD,
+       GET_VLAN_EGRESS_PRIORITY_CMD,
+       SET_VLAN_NAME_TYPE_CMD,
+       SET_VLAN_FLAG_CMD
+};
+enum vlan_name_types {
+       VLAN_NAME_TYPE_PLUS_VID, /* Name will look like:  vlan0005 */
+       VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like:  eth1.0005 */
+       VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like:  vlan5 */
+       VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like:  eth0.5 */
+       VLAN_NAME_TYPE_HIGHEST
+};
+
+struct vlan_ioctl_args {
+       int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
+       char device1[24];
+
+        union {
+               char device2[24];
+               int VID;
+               unsigned int skb_priority;
+               unsigned int name_type;
+               unsigned int bind_type;
+               unsigned int flag; /* Matches vlan_dev_info flags */
+        } u;
+
+       short vlan_qos;
+};
+
+#define VLAN_GROUP_ARRAY_LEN 4096
+#define SIOCSIFVLAN    0x8983          /* Set 802.1Q VLAN options      */
+
+/* On entry, table points to the length of the current string plus
+ * nul terminator plus data length for the subsequent entry.  The
+ * return value is the last data entry for the matching string. */
+static const char *xfind_str(const char *table, const char *str)
+{
+       while (strcasecmp(str, table+1) != 0) {
+               if (!*(table += table[0])) {
+                       bb_show_usage();
+               }
+       }
+       return table - 1;
+}
+
+static const char cmds[] = {
+       4, ADD_VLAN_CMD, 7,
+       'a', 'd', 'd', 0,
+       3, DEL_VLAN_CMD, 7,
+       'r', 'e', 'm', 0,
+       3, SET_VLAN_NAME_TYPE_CMD, 17,
+       's', 'e', 't', '_',
+       'n', 'a', 'm', 'e', '_',
+       't', 'y', 'p', 'e', 0,
+       4, SET_VLAN_FLAG_CMD, 12,
+       's', 'e', 't', '_',
+       'f', 'l', 'a', 'g', 0,
+       5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
+       's', 'e', 't', '_',
+       'e', 'g', 'r', 'e', 's', 's', '_',
+       'm', 'a', 'p', 0,
+       5, SET_VLAN_INGRESS_PRIORITY_CMD, 16,
+       's', 'e', 't', '_',
+       'i', 'n', 'g', 'r', 'e', 's', 's', '_',
+       'm', 'a', 'p', 0,
+};
+
+static const char name_types[] = {
+       VLAN_NAME_TYPE_PLUS_VID, 16,
+       'V', 'L', 'A', 'N',
+       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+       0,
+       VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
+       'V', 'L', 'A', 'N',
+       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+       '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+       VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
+       'D', 'E', 'V',
+       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+       0,
+       VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20,
+       'D', 'E', 'V',
+       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+       '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+};
+
+static const char conf_file_name[] = "/proc/net/vlan/config";
+
+int vconfig_main(int argc, char **argv)
+{
+       struct vlan_ioctl_args ifr;
+       const char *p;
+       int fd;
+
+       if (argc < 3) {
+               bb_show_usage();
+       }
+
+       /* Don't bother closing the filedes.  It will be closed on cleanup. */
+       if (open(conf_file_name, O_RDONLY) < 0) { /* Is 802.1q is present? */
+           bb_perror_msg_and_die("open %s", conf_file_name);
+       }
+
+       memset(&ifr, 0, sizeof(struct vlan_ioctl_args));
+
+       ++argv;
+       p = xfind_str(cmds+2, *argv);
+       ifr.cmd = *p;
+       if (argc != p[-1]) {
+               bb_show_usage();
+       }
+
+       if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */
+               ifr.u.name_type = *xfind_str(name_types+1, argv[1]);
+       } else {
+               if (strlen(argv[1]) >= IF_NAMESIZE) {
+                       bb_error_msg_and_die("if_name >= %d chars\n", IF_NAMESIZE);
+               }
+               strcpy(ifr.device1, argv[1]);
+               p = argv[2];
+
+               /* I suppose one could try to combine some of the function calls below,
+                * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized
+                * (unsigned) int members of a unions.  But because of the range checking,
+                * doing so wouldn't save that much space and would also make maintainence
+                * more of a pain. */
+               if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */
+                       ifr.u.flag = bb_xgetularg10_bnd(p, 0, 1);
+               } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
+                       ifr.u.VID = bb_xgetularg10_bnd(p, 0, VLAN_GROUP_ARRAY_LEN-1);
+               } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
+                       ifr.u.skb_priority = bb_xgetularg10_bnd(p, 0, ULONG_MAX);
+                       ifr.vlan_qos = bb_xgetularg10_bnd(argv[3], 0, 7);
+               }
+       }
+
+       if (((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+               || (ioctl(fd, SIOCSIFVLAN, &ifr) < 0)
+               ) {
+               bb_perror_msg_and_die("socket or ioctl error for %s", *argv);
+       }
+
+       return 0;
+}
+
diff --git a/busybox/networking/wget.c b/busybox/networking/wget.c
new file mode 100644 (file)
index 0000000..45acef4
--- /dev/null
@@ -0,0 +1,868 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wget - retrieve a file using HTTP or FTP
+ *
+ * Chip Rosenthal Covad Communications <chip@laserlink.net>
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <getopt.h>
+
+#include "busybox.h"
+
+struct host_info {
+       char *host;
+       int port;
+       char *path;
+       int is_ftp;
+       char *user;
+};
+
+static void parse_url(char *url, struct host_info *h);
+static FILE *open_socket(struct sockaddr_in *s_in);
+static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
+static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);
+
+/* Globals (can be accessed from signal handlers */
+static off_t filesize = 0;             /* content-length of the file */
+static int chunked = 0;                        /* chunked transfer encoding */
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+static void progressmeter(int flag);
+static char *curfile;                  /* Name of current file being transferred. */
+static struct timeval start;   /* Time a transfer started. */
+static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */
+/* For progressmeter() -- number of seconds before xfer considered "stalled" */
+static const int STALLTIME = 5;
+#endif
+
+static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue)
+{
+       if (output != stdout && do_continue==0) {
+               fclose(output);
+               unlink(fname_out);
+       }
+}
+
+/* Read NMEMB elements of SIZE bytes into PTR from STREAM.  Returns the
+ * number of elements read, and a short count if an eof or non-interrupt
+ * error is encountered.  */
+static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       size_t ret = 0;
+
+       do {
+               clearerr(stream);
+               ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
+       } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+       return ret;
+}
+
+/* Write NMEMB elements of SIZE bytes from PTR to STREAM.  Returns the
+ * number of elements written, and a short count if an eof or non-interrupt
+ * error is encountered.  */
+static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       size_t ret = 0;
+
+       do {
+               clearerr(stream);
+               ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
+       } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+       return ret;
+}
+
+/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.
+ * Returns S, or NULL if an eof or non-interrupt error is encountered.  */
+static char *safe_fgets(char *s, int size, FILE *stream)
+{
+       char *ret;
+
+       do {
+               clearerr(stream);
+               ret = fgets(s, size, stream);
+       } while (ret == NULL && ferror(stream) && errno == EINTR);
+
+       return ret;
+}
+
+#define close_delete_and_die(s...) { \
+       close_and_delete_outfile(output, fname_out, do_continue); \
+       bb_error_msg_and_die(s); }
+
+
+#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
+/*
+ *  Base64-encode character string
+ *  oops... isn't something similar in uuencode.c?
+ *  It would be better to use already existing code
+ */
+char *base64enc(unsigned char *p, char *buf, int len) {
+
+        char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+                    "0123456789+/";
+               char *s = buf;
+
+        while(*p) {
+                               if (s >= buf+len-4)
+                                       bb_error_msg_and_die("buffer overflow");
+                *(s++) = al[(*p >> 2) & 0x3F];
+                *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
+                *s = *(s+1) = '=';
+                *(s+2) = 0;
+                if (! *(++p)) break;
+                *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
+                if (! *(++p)) break;
+                *(s++) = al[*(p++) & 0x3F];
+        }
+
+               return buf;
+}
+#endif
+
+#define WGET_OPT_CONTINUE      1
+#define WGET_OPT_QUIET 2
+#define WGET_OPT_PASSIVE       4
+#define WGET_OPT_OUTNAME       8
+#define WGET_OPT_HEADER        16
+#define WGET_OPT_PREFIX        32
+#define WGET_OPT_PROXY 64
+
+static const struct option wget_long_options[] = {
+       { "continue",        0, NULL, 'c' },
+       { "quiet",           0, NULL, 'q' },
+       { "passive-ftp",     0, NULL, 139 },
+       { "output-document", 1, NULL, 'O' },
+       { "header",              1, NULL, 131 },
+       { "directory-prefix",1, NULL, 'P' },
+       { "proxy",           1, NULL, 'Y' },
+       { 0,                 0, 0, 0 }
+};
+
+int wget_main(int argc, char **argv)
+{
+       int n, try=5, status;
+       unsigned long opt;
+       int port;
+       char *proxy = 0;
+       char *dir_prefix=NULL;
+       char *s, buf[512];
+       struct stat sbuf;
+       char extra_headers[1024];
+       char *extra_headers_ptr = extra_headers;
+       int extra_headers_left = sizeof(extra_headers);
+       struct host_info server, target;
+       struct sockaddr_in s_in;
+       llist_t *headers_llist = NULL;
+
+       FILE *sfp = NULL;                       /* socket to web/ftp server                     */
+       FILE *dfp = NULL;                       /* socket to ftp server (data)          */
+       char *fname_out = NULL;         /* where to direct output (-O)          */
+       int do_continue = 0;            /* continue a prev transfer (-c)        */
+       long beg_range = 0L;            /*   range at which continue begins     */
+       int got_clen = 0;                       /* got content-length: from server      */
+       FILE *output;                           /* socket to web server                         */
+       int quiet_flag = FALSE;         /* Be verry, verry quiet...                     */
+       int use_proxy = 1;          /* Use proxies if env vars are set  */
+       char *proxy_flag = "on";        /* Use proxies if env vars are set  */
+
+       /*
+        * Crack command line.
+        */
+       bb_opt_complementaly = "\203*";
+       bb_applet_long_options = wget_long_options;
+       opt = bb_getopt_ulflags(argc, argv, "cq\213O:\203:P:Y:", &fname_out, &headers_llist, &dir_prefix, &proxy_flag);
+       if (opt & WGET_OPT_CONTINUE) {
+               ++do_continue;
+       }
+       if (opt & WGET_OPT_QUIET) {
+               quiet_flag = TRUE;
+       }
+       if (strcmp(proxy_flag, "off") == 0) {
+               /* Use the proxy if necessary. */
+               use_proxy = 0;
+       }
+       if (opt & WGET_OPT_HEADER) {
+               while (headers_llist) {
+                       int arglen = strlen(headers_llist->data);
+                       if (extra_headers_left - arglen - 2 <= 0)
+                               bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen);
+                       strcpy(extra_headers_ptr, headers_llist->data);
+                       extra_headers_ptr += arglen;
+                       extra_headers_left -= ( arglen + 2 );
+                       *extra_headers_ptr++ = '\r';
+                       *extra_headers_ptr++ = '\n';
+                       *(extra_headers_ptr + 1) = 0;
+                       headers_llist = headers_llist->link;
+               }
+       }
+       if (argc - optind != 1)
+                       bb_show_usage();
+
+       parse_url(argv[optind], &target);
+       server.host = target.host;
+       server.port = target.port;
+
+       /*
+        * Use the proxy if necessary.
+        */
+       if (use_proxy) {
+               proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+               if (proxy && *proxy) {
+                       parse_url(bb_xstrdup(proxy), &server);
+               } else {
+                       use_proxy = 0;
+               }
+       }
+
+       /* Guess an output filename */
+       if (!fname_out) {
+               // Dirty hack. Needed because bb_get_last_path_component
+               // will destroy trailing / by storing '\0' in last byte!
+               if(target.path[strlen(target.path)-1]!='/') {
+                       fname_out =
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+                               curfile =
+#endif
+                               bb_get_last_path_component(target.path);
+               }
+               if (fname_out==NULL || strlen(fname_out)<1) {
+                       fname_out =
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+                               curfile =
+#endif
+                               "index.html";
+               }
+               if (dir_prefix != NULL)
+                       fname_out = concat_path_file(dir_prefix, fname_out);
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+       } else {
+               curfile = bb_get_last_path_component(fname_out);
+#endif
+       }
+       if (do_continue && !fname_out)
+               bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
+
+
+       /*
+        * Open the output file stream.
+        */
+       if (strcmp(fname_out, "-") == 0) {
+               output = stdout;
+               quiet_flag = TRUE;
+       } else {
+               output = bb_xfopen(fname_out, (do_continue ? "a" : "w"));
+       }
+
+       /*
+        * Determine where to start transfer.
+        */
+       if (do_continue) {
+               if (fstat(fileno(output), &sbuf) < 0)
+                       bb_perror_msg_and_die("fstat()");
+               if (sbuf.st_size > 0)
+                       beg_range = sbuf.st_size;
+               else
+                       do_continue = 0;
+       }
+
+       /* We want to do exactly _one_ DNS lookup, since some
+        * sites (i.e. ftp.us.debian.org) use round-robin DNS
+        * and we want to connect to only one IP... */
+       bb_lookup_host(&s_in, server.host);
+       s_in.sin_port = server.port;
+       if (quiet_flag==FALSE) {
+               fprintf(stdout, "Connecting to %s[%s]:%d\n",
+                               server.host, inet_ntoa(s_in.sin_addr), ntohs(server.port));
+       }
+
+       if (use_proxy || !target.is_ftp) {
+               /*
+                *  HTTP session
+                */
+               do {
+                       got_clen = chunked = 0;
+
+                       if (! --try)
+                               close_delete_and_die("too many redirections");
+
+                       /*
+                        * Open socket to http server
+                        */
+                       if (sfp) fclose(sfp);
+                       sfp = open_socket(&s_in);
+
+                       /*
+                        * Send HTTP request.
+                        */
+                       if (use_proxy) {
+                               const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n";
+#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL
+                               if (strchr (target.host, ':'))
+                                       format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n";
+#endif
+                               fprintf(sfp, format,
+                                       target.is_ftp ? "f" : "ht", target.host,
+                                       ntohs(target.port), target.path);
+                       } else {
+                               fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+                       }
+
+                       fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host);
+
+#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
+                       if (target.user) {
+                               fprintf(sfp, "Authorization: Basic %s\r\n",
+                                       base64enc(target.user, buf, sizeof(buf)));
+                       }
+                       if (use_proxy && server.user) {
+                               fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+                                       base64enc(server.user, buf, sizeof(buf)));
+                       }
+#endif
+
+                       if (do_continue)
+                               fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range);
+                       if(extra_headers_left < sizeof(extra_headers))
+                               fputs(extra_headers,sfp);
+                       fprintf(sfp,"Connection: close\r\n\r\n");
+
+                       /*
+                       * Retrieve HTTP response line and check for "200" status code.
+                       */
+read_response:
+                       if (fgets(buf, sizeof(buf), sfp) == NULL)
+                               close_delete_and_die("no response from server");
+
+                       for (s = buf ; *s != '\0' && !isspace(*s) ; ++s)
+                       ;
+                       for ( ; isspace(*s) ; ++s)
+                       ;
+                       switch (status = atoi(s)) {
+                               case 0:
+                               case 100:
+                                       while (gethdr(buf, sizeof(buf), sfp, &n) != NULL);
+                                       goto read_response;
+                               case 200:
+                                       if (do_continue && output != stdout)
+                                               output = freopen(fname_out, "w", output);
+                                       do_continue = 0;
+                                       break;
+                               case 300:       /* redirection */
+                               case 301:
+                               case 302:
+                               case 303:
+                                       break;
+                               case 206:
+                                       if (do_continue)
+                                               break;
+                                       /*FALLTHRU*/
+                               default:
+                                       chomp(buf);
+                                       close_delete_and_die("server returned error %d: %s", atoi(s), buf);
+                       }
+
+                       /*
+                        * Retrieve HTTP headers.
+                        */
+                       while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
+                               if (strcasecmp(buf, "content-length") == 0) {
+                                       unsigned long value;
+                                       if (safe_strtoul(s, &value)) {
+                                               close_delete_and_die("content-length %s is garbage", s);
+                                       }
+                                       filesize = value;
+                                       got_clen = 1;
+                                       continue;
+                               }
+                               if (strcasecmp(buf, "transfer-encoding") == 0) {
+                                       if (strcasecmp(s, "chunked") == 0) {
+                                               chunked = got_clen = 1;
+                                       } else {
+                                       close_delete_and_die("server wants to do %s transfer encoding", s);
+                                       }
+                               }
+                               if (strcasecmp(buf, "location") == 0) {
+                                       if (s[0] == '/')
+                                               target.path = bb_xstrdup(s+1);
+                                       else {
+                                               parse_url(bb_xstrdup(s), &target);
+                                               if (use_proxy == 0) {
+                                                       server.host = target.host;
+                                                       server.port = target.port;
+                                               }
+                                               bb_lookup_host(&s_in, server.host);
+                                               s_in.sin_port = server.port;
+                                               break;
+                                       }
+                               }
+                       }
+               } while(status >= 300);
+
+               dfp = sfp;
+       }
+       else
+       {
+               /*
+                *  FTP session
+                */
+               if (! target.user)
+                       target.user = bb_xstrdup("anonymous:busybox@");
+
+               sfp = open_socket(&s_in);
+               if (ftpcmd(NULL, NULL, sfp, buf) != 220)
+                       close_delete_and_die("%s", buf+4);
+
+               /*
+                * Splitting username:password pair,
+                * trying to log in
+                */
+               s = strchr(target.user, ':');
+               if (s)
+                       *(s++) = '\0';
+               switch(ftpcmd("USER ", target.user, sfp, buf)) {
+                       case 230:
+                               break;
+                       case 331:
+                               if (ftpcmd("PASS ", s, sfp, buf) == 230)
+                                       break;
+                               /* FALLTHRU (failed login) */
+                       default:
+                               close_delete_and_die("ftp login: %s", buf+4);
+               }
+
+               ftpcmd("CDUP", NULL, sfp, buf);
+               ftpcmd("TYPE I", NULL, sfp, buf);
+
+               /*
+                * Querying file size
+                */
+               if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) {
+                       unsigned long value;
+                       if (safe_strtoul(buf+4, &value)) {
+                               close_delete_and_die("SIZE value is garbage");
+                       }
+                       filesize = value;
+                       got_clen = 1;
+               }
+
+               /*
+                * Entering passive mode
+                */
+               if (ftpcmd("PASV", NULL, sfp, buf) !=  227)
+                       close_delete_and_die("PASV: %s", buf+4);
+               s = strrchr(buf, ',');
+               *s = 0;
+               port = atoi(s+1);
+               s = strrchr(buf, ',');
+               port += atoi(s+1) * 256;
+               s_in.sin_port = htons(port);
+               dfp = open_socket(&s_in);
+
+               if (do_continue) {
+                       sprintf(buf, "REST %ld", beg_range);
+                       if (ftpcmd(buf, NULL, sfp, buf) != 350) {
+                               if (output != stdout)
+                                       output = freopen(fname_out, "w", output);
+                               do_continue = 0;
+                       } else
+                               filesize -= beg_range;
+               }
+
+               if (ftpcmd("RETR /", target.path, sfp, buf) > 150)
+                       close_delete_and_die("RETR: %s", buf+4);
+
+       }
+
+
+       /*
+        * Retrieve file
+        */
+       if (chunked) {
+               fgets(buf, sizeof(buf), dfp);
+               filesize = strtol(buf, (char **) NULL, 16);
+       }
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+       if (quiet_flag==FALSE)
+               progressmeter(-1);
+#endif
+       do {
+               while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, ((chunked || got_clen) && (filesize < sizeof(buf)) ? filesize : sizeof(buf)), dfp)) > 0) {
+                       if (safe_fwrite(buf, 1, n, output) != n) {
+                               bb_perror_msg_and_die("write error");
+                       }
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+                       statbytes+=n;
+#endif
+                       if (got_clen) {
+                               filesize -= n;
+                       }
+               }
+
+               if (chunked) {
+                       safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+                       safe_fgets(buf, sizeof(buf), dfp);
+                       filesize = strtol(buf, (char **) NULL, 16);
+                       if (filesize==0) {
+                               chunked = 0; /* all done! */
+                       }
+               }
+
+               if (n == 0 && ferror(dfp)) {
+                       bb_perror_msg_and_die("network read error");
+               }
+       } while (chunked);
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+       if (quiet_flag==FALSE)
+               progressmeter(1);
+#endif
+       if ((use_proxy == 0) && target.is_ftp) {
+               fclose(dfp);
+               if (ftpcmd(NULL, NULL, sfp, buf) != 226)
+                       bb_error_msg_and_die("ftp error: %s", buf+4);
+               ftpcmd("QUIT", NULL, sfp, buf);
+       }
+       exit(EXIT_SUCCESS);
+}
+
+
+void parse_url(char *url, struct host_info *h)
+{
+       char *cp, *sp, *up, *pp;
+
+       if (strncmp(url, "http://", 7) == 0) {
+               h->port = bb_lookup_port("http", "tcp", 80);
+               h->host = url + 7;
+               h->is_ftp = 0;
+       } else if (strncmp(url, "ftp://", 6) == 0) {
+               h->port = bb_lookup_port("ftp", "tfp", 21);
+               h->host = url + 6;
+               h->is_ftp = 1;
+       } else
+               bb_error_msg_and_die("not an http or ftp url: %s", url);
+
+       sp = strchr(h->host, '/');
+       if (sp) {
+               *sp++ = '\0';
+               h->path = sp;
+       } else
+               h->path = bb_xstrdup("");
+
+       up = strrchr(h->host, '@');
+       if (up != NULL) {
+               h->user = h->host;
+               *up++ = '\0';
+               h->host = up;
+       } else
+               h->user = NULL;
+
+       pp = h->host;
+
+#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL
+       if (h->host[0] == '[') {
+               char *ep;
+
+               ep = h->host + 1;
+               while (*ep == ':' || isxdigit (*ep))
+                       ep++;
+               if (*ep == ']') {
+                       h->host++;
+                       *ep = '\0';
+                       pp = ep + 1;
+               }
+       }
+#endif
+
+       cp = strchr(pp, ':');
+       if (cp != NULL) {
+               *cp++ = '\0';
+               h->port = htons(atoi(cp));
+       }
+}
+
+
+FILE *open_socket(struct sockaddr_in *s_in)
+{
+       FILE *fp;
+
+       fp = fdopen(xconnect(s_in), "r+");
+       if (fp == NULL)
+               bb_perror_msg_and_die("fdopen()");
+
+       return fp;
+}
+
+
+char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
+{
+       char *s, *hdrval;
+       int c;
+
+       *istrunc = 0;
+
+       /* retrieve header line */
+       if (fgets(buf, bufsiz, fp) == NULL)
+               return NULL;
+
+       /* see if we are at the end of the headers */
+       for (s = buf ; *s == '\r' ; ++s)
+               ;
+       if (s[0] == '\n')
+               return NULL;
+
+       /* convert the header name to lower case */
+       for (s = buf ; isalnum(*s) || *s == '-' ; ++s)
+               *s = tolower(*s);
+
+       /* verify we are at the end of the header name */
+       if (*s != ':')
+               bb_error_msg_and_die("bad header line: %s", buf);
+
+       /* locate the start of the header value */
+       for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s)
+               ;
+       hdrval = s;
+
+       /* locate the end of header */
+       while (*s != '\0' && *s != '\r' && *s != '\n')
+               ++s;
+
+       /* end of header found */
+       if (*s != '\0') {
+               *s = '\0';
+               return hdrval;
+       }
+
+       /* Rats!  The buffer isn't big enough to hold the entire header value. */
+       while (c = getc(fp), c != EOF && c != '\n')
+               ;
+       *istrunc = 1;
+       return hdrval;
+}
+
+static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)
+{
+       if (s1) {
+               if (!s2) s2="";
+               fprintf(fp, "%s%s\r\n", s1, s2);
+               fflush(fp);
+       }
+
+       do {
+               char *buf_ptr;
+
+               if (fgets(buf, 510, fp) == NULL) {
+                       bb_perror_msg_and_die("fgets()");
+               }
+               buf_ptr = strstr(buf, "\r\n");
+               if (buf_ptr) {
+                       *buf_ptr = '\0';
+               }
+       } while (! isdigit(buf[0]) || buf[3] != ' ');
+
+       return atoi(buf);
+}
+
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+/* Stuff below is from BSD rcp util.c, as added to openshh.
+ * Original copyright notice is retained at the end of this file.
+ *
+ */
+
+
+static int
+getttywidth(void)
+{
+       int width=0;
+       get_terminal_width_height(0, &width, NULL);
+       return (width);
+}
+
+static void
+updateprogressmeter(int ignore)
+{
+       int save_errno = errno;
+
+       progressmeter(0);
+       errno = save_errno;
+}
+
+static void
+alarmtimer(int wait)
+{
+       struct itimerval itv;
+
+       itv.it_value.tv_sec = wait;
+       itv.it_value.tv_usec = 0;
+       itv.it_interval = itv.it_value;
+       setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+
+static void
+progressmeter(int flag)
+{
+       static const char prefixes[] = " KMGTP";
+       static struct timeval lastupdate;
+       static off_t lastsize, totalsize;
+       struct timeval now, td, wait;
+       off_t cursize, abbrevsize;
+       double elapsed;
+       int ratio, barlength, i, remaining;
+       char buf[256];
+
+       if (flag == -1) {
+               (void) gettimeofday(&start, (struct timezone *) 0);
+               lastupdate = start;
+               lastsize = 0;
+               totalsize = filesize; /* as filesize changes.. */
+       }
+
+       (void) gettimeofday(&now, (struct timezone *) 0);
+       cursize = statbytes;
+       if (totalsize != 0 && !chunked) {
+               ratio = 100.0 * cursize / totalsize;
+               ratio = MAX(ratio, 0);
+               ratio = MIN(ratio, 100);
+       } else
+               ratio = 100;
+
+       snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio);
+
+       barlength = getttywidth() - 51;
+       if (barlength > 0) {
+               i = barlength * ratio / 100;
+               snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                        "|%.*s%*s|", i,
+                        "*****************************************************************************"
+                        "*****************************************************************************",
+                        barlength - i, "");
+       }
+       i = 0;
+       abbrevsize = cursize;
+       while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
+               i++;
+               abbrevsize >>= 10;
+       }
+       snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ",
+            (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' :
+                'B');
+
+       timersub(&now, &lastupdate, &wait);
+       if (cursize > lastsize) {
+               lastupdate = now;
+               lastsize = cursize;
+               if (wait.tv_sec >= STALLTIME) {
+                       start.tv_sec += wait.tv_sec;
+                       start.tv_usec += wait.tv_usec;
+               }
+               wait.tv_sec = 0;
+       }
+       timersub(&now, &start, &td);
+       elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+       if (wait.tv_sec >= STALLTIME) {
+               snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                        " - stalled -");
+       } else if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalsize || chunked) {
+               snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                        "   --:-- ETA");
+       } else {
+               remaining = (int) (totalsize / (statbytes / elapsed) - elapsed);
+               i = remaining / 3600;
+               if (i)
+                       snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                                "%2d:", i);
+               else
+                       snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                                "   ");
+               i = remaining % 3600;
+               snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                        "%02d:%02d ETA", i / 60, i % 60);
+       }
+       write(STDERR_FILENO, buf, strlen(buf));
+
+       if (flag == -1) {
+               struct sigaction sa;
+               sa.sa_handler = updateprogressmeter;
+               sigemptyset(&sa.sa_mask);
+               sa.sa_flags = SA_RESTART;
+               sigaction(SIGALRM, &sa, NULL);
+               alarmtimer(1);
+       } else if (flag == 1) {
+               alarmtimer(0);
+               statbytes = 0;
+               putc('\n', stderr);
+       }
+}
+#endif
+
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
+ * much of which was blatantly stolen from openssh.  */
+
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $Id: wget.c,v 1.75 2004/10/08 08:27:40 andersen Exp $
+ */
+
+
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/patches/cmp_n.diff b/busybox/patches/cmp_n.diff
new file mode 100644 (file)
index 0000000..fc4661c
--- /dev/null
@@ -0,0 +1,377 @@
+Index: coreutils/Config.in
+===================================================================
+RCS file: /var/cvs/busybox/coreutils/Config.in,v
+retrieving revision 1.24
+diff -u -r1.24 Config.in
+--- a/coreutils/Config.in      15 Mar 2004 08:28:19 -0000      1.24
++++ b/coreutils/Config.in      31 Mar 2004 11:51:17 -0000
+@@ -59,6 +59,21 @@
+         cmp is used to compare two files and returns the result
+         to standard output.
++config CONFIG_FEATURE_CMP_SKIP
++      bool "  Enable optional arguments SKIP1 and SKIP2"
++      default n
++      depends on CONFIG_CMP
++      help
++        SKIP1 and SKIP2 specify how many bytes to ignore
++        at the start of each file.
++
++config CONFIG_FEATURE_CMP_LIMIT
++      bool "  Enable limit of inputs"
++      default n
++      depends on CONFIG_CMP
++      help
++        Enable cmp option (-n).
++
+ config CONFIG_CP
+       bool "cp"
+       default n
+Index: coreutils/cmp.c
+===================================================================
+RCS file: /var/cvs/busybox/coreutils/cmp.c,v
+retrieving revision 1.9
+diff -u -r1.9 cmp.c
+--- a/coreutils/cmp.c  19 Mar 2003 09:11:32 -0000      1.9
++++ b/coreutils/cmp.c  31 Mar 2004 11:51:17 -0000
+@@ -39,6 +39,12 @@
+ #include <unistd.h>
+ #include "busybox.h"
++#ifdef CONFIG_FEATURE_CMP_SKIP
++#define MAX_OPTIONAL_ARGS     3
++#else
++#define MAX_OPTIONAL_ARGS     1
++#endif
++
+ static FILE *cmp_xfopen_input(const char *filename)
+ {
+       FILE *fp;
+@@ -58,12 +64,57 @@
+ static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n";       /* nicer gnu format */
+ #endif
+-static const char opt_chars[] = "sl";
++#ifdef CONFIG_FEATURE_CMP_LIMIT
++#define OPTCHR_LIMIT  "n:"
++#define OPTARG_LIMIT  ,&limit_str
++#else
++#define OPTCHR_LIMIT
++#define OPTARG_LIMIT
++#endif
++
++static const char opt_chars[] = "sl" OPTCHR_LIMIT;
+ enum {
+       OPT_s = 1,
+-      OPT_l = 2
++      OPT_l = 2,
++      OPT_n = 4
++};
++
++#ifdef CONFIG_LFS
++#define SUFFIX_STRUCT         suffix_mult64
++#define PARSE_FUNC            bb_xgetllarg10_sfx
++#else
++#define SUFFIX_STRUCT         suffix_mult
++#define PARSE_FUNC            bb_xgetlarg10_sfx
++#endif
++
++#if defined(CONFIG_FEATURE_CMP_SKIP) || defined(CONFIG_FEATURE_CMP_LIMIT)
++static const struct SUFFIX_STRUCT suffixes[] = {
++      { "k", 1UL << 10 },
++      { "M", 1UL << 20 },
++      { "G", 1UL << 30 },
++#ifdef CONFIG_LFS
++      { "T", 1ULL << 40 },
++      { "P", 1ULL << 50 },
++      { "E", 1ULL << 60 },
++#endif
++      { NULL, 0 }
+ };
++#endif
++
++#ifdef CONFIG_FEATURE_CMP_SKIP
++static void skip_input(FILE *fp, off_t skip, const char *filename)
++{
++      if (skip > 0 && fseeko(fp, skip, SEEK_CUR) != 0) {
++              while (skip-- > 0) {
++                      if (getc(fp) == EOF) {
++                              bb_xferror(fp, filename);
++                              break;
++                      }
++              }
++      }
++}
++#endif
+ int cmp_main(int argc, char **argv)
+ {
+@@ -73,12 +124,26 @@
+       int c1, c2, char_pos, line_pos;
+       int opt_flags;
+       int exit_val = 0;
++#ifdef CONFIG_FEATURE_CMP_SKIP
++      off_t skip1 = 0, skip2 = 0;
++#endif
++#ifdef CONFIG_FEATURE_CMP_LIMIT
++      off_t limit = -1;
++      char *limit_str;
++#endif
+       bb_default_error_retval = 2;    /* 1 is returned if files are different. */
+-      opt_flags = bb_getopt_ulflags(argc, argv, opt_chars);
++      opt_flags = bb_getopt_ulflags(argc, argv, opt_chars OPTARG_LIMIT);
+-      if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > 1)) {
++#ifdef CONFIG_FEATURE_CMP_LIMIT
++      if (opt_flags & OPT_n) {
++              limit = PARSE_FUNC(limit_str, suffixes);
++              opt_flags &= 3;
++      }
++#endif
++
++      if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > MAX_OPTIONAL_ARGS)) {
+               bb_show_usage();
+       }
+@@ -87,6 +152,13 @@
+       filename2 = "-";
+       if (*++argv) {
+               filename2 = *argv;
++#ifdef CONFIG_FEATURE_CMP_SKIP
++              if (*++argv) {
++                      skip1 = PARSE_FUNC(*argv, suffixes);
++                      if (*++argv)
++                              skip2 = PARSE_FUNC(*argv, suffixes);
++              }
++#endif
+       }
+       fp2 = cmp_xfopen_input(filename2);
+@@ -98,6 +170,11 @@
+               return 0;
+       }
++#ifdef CONFIG_FEATURE_CMP_SKIP
++      skip_input(fp1, skip1, filename1);
++      skip_input(fp2, skip2, filename2);
++#endif
++
+       fmt = fmt_differ;
+       if (opt_flags == OPT_l) {
+               fmt = fmt_l_opt;
+@@ -106,6 +183,10 @@
+       char_pos = 0;
+       line_pos = 1;
+       do {
++#ifdef CONFIG_FEATURE_CMP_LIMIT
++              if (limit-- == 0)
++                      break;
++#endif
+               c1 = getc(fp1);
+               c2 = getc(fp2);
+               ++char_pos;
+Index: include/usage.h
+===================================================================
+RCS file: /var/cvs/busybox/include/usage.h,v
+retrieving revision 1.198
+diff -u -r1.198 usage.h
+--- a/include/usage.h  29 Mar 2004 08:20:08 -0000      1.198
++++ b/include/usage.h  31 Mar 2004 11:51:17 -0000
+@@ -186,14 +186,29 @@
+ #define clear_full_usage \
+       "Clear screen."
++#ifdef CONFIG_FEATURE_CMP_SKIP
++#define USAGE_CMP_SKIP(a) a
++#else
++#define USAGE_CMP_SKIP(a)
++#endif
++
++#ifdef CONFIG_FEATURE_CMP_LIMIT
++#define USAGE_CMP_LIMIT(a) a
++#else
++#define USAGE_CMP_LIMIT(a)
++#endif
++
+ #define cmp_trivial_usage \
+-      "[OPTION]... FILE1 [FILE2]"
++      "[OPTION]... FILE1 [FILE2" USAGE_CMP_SKIP(" [SKIP1 [SKIP2]]") "]"
+ #define cmp_full_usage \
+-      "Compare files.\n\n" \
++      "Compare files.\n" \
++      USAGE_CMP_SKIP("SKIP1 and SKIP2 are the number of bytes to skip in each file.\n") \
++      "\n" \
+       "Options:\n" \
+-      "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \
+-      "\t\t  for all differing bytes.\n" \
+-      "\t-s\tquiet mode - do not print"
++      "\t-l\t\tWrite the byte numbers (decimal) and values (octal)\n" \
++      "\t\t\t  for all differing bytes.\n" \
++      USAGE_CMP_LIMIT("\t-n LIMIT\tCompare at most LIMIT bytes.\n") \
++      "\t-s\t\tquiet mode - do not print"
+ #define cp_trivial_usage \
+       "[OPTION]... SOURCE DEST"
+Index: include/libbb.h
+===================================================================
+RCS file: /var/cvs/busybox/include/libbb.h,v
+retrieving revision 1.129
+diff -u -r1.129 libbb.h
+--- a/include/libbb.h  15 Mar 2004 08:28:38 -0000      1.129
++++ b/include/libbb.h  31 Mar 2004 11:51:17 -0000
+@@ -217,6 +217,21 @@
+                                                               const struct suffix_mult *suffixes);
+ extern long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes);
++struct suffix_mult64 {
++      const char *suffix;
++      unsigned long long mult;
++};
++
++extern unsigned long long bb_xgetullarg_bnd_sfx(const char *arg, int base,
++                                                                                unsigned long long lower,
++                                                                                unsigned long long upper,
++                                                                                const struct suffix_mult64 *suffixes);
++
++extern long long bb_xgetllarg_bnd_sfx(const char *arg, int base,
++                                                              long long lower,
++                                                              long long upper,
++                                                              const struct suffix_mult64 *suffixes);
++extern long long bb_xgetllarg10_sfx(const char *arg, const struct suffix_mult64 *suffixes);
+ //#warning pitchable now?
+ extern unsigned long bb_xparse_number(const char *numstr,
+Index: libbb/Makefile.in
+===================================================================
+RCS file: /var/cvs/busybox/libbb/Makefile.in,v
+retrieving revision 1.34
+diff -u -r1.34 Makefile.in
+--- a/libbb/Makefile.in        6 Mar 2004 22:11:45 -0000       1.34
++++ b/libbb/Makefile.in        31 Mar 2004 11:51:17 -0000
+@@ -70,7 +70,8 @@
+ LIBBB_MSRC3:=$(LIBBB_DIR)xgetularg.c
+ LIBBB_MOBJ3:=xgetularg_bnd_sfx.o xgetlarg_bnd_sfx.o getlarg10_sfx.o \
+-      xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o
++      xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o \
++      xgetullarg_bnd_sfx.o xgetllarg_bnd_sfx.o xgetllarg10_sfx.o
+ LIBBB_MSRC4:=$(LIBBB_DIR)/safe_strtol.c
+ LIBBB_MOBJ4:=safe_strtoi.o safe_strtod.o safe_strtol.o safe_strtoul.o
+Index: libbb/xgetularg.c
+===================================================================
+RCS file: /var/cvs/busybox/libbb/xgetularg.c,v
+retrieving revision 1.2
+diff -u -r1.2 xgetularg.c
+--- a/libbb/xgetularg.c        15 Mar 2004 08:28:44 -0000      1.2
++++ b/libbb/xgetularg.c        31 Mar 2004 11:51:17 -0000
+@@ -158,3 +158,106 @@
+       return bb_xgetularg10_bnd(arg, 0, ULONG_MAX);
+ }
+ #endif
++
++#ifdef L_xgetullarg_bnd_sfx
++extern
++unsigned long long bb_xgetullarg_bnd_sfx(const char *arg, int base,
++                                                                 unsigned long long lower,
++                                                                 unsigned long long upper,
++                                                                 const struct suffix_mult64 *suffixes)
++{
++      unsigned long long r;
++      int old_errno;
++      char *e;
++
++      assert(arg);
++
++      /* Disallow '-' and any leading whitespace.  Speed isn't critical here
++       * since we're parsing commandline args.  So make sure we get the
++       * actual isspace function rather than a larger macro implementaion. */
++      if ((*arg == '-') || (isspace)(*arg)) {
++              bb_show_usage();
++      }
++
++      /* Since this is a lib function, we're not allowed to reset errno to 0.
++       * Doing so could break an app that is deferring checking of errno.
++       * So, save the old value so that we can restore it if successful. */
++      old_errno = errno;
++      errno = 0;
++      r = strtoull(arg, &e, base);
++      /* Do the initial validity check.  Note: The standards do not
++       * guarantee that errno is set if no digits were found.  So we
++       * must test for this explicitly. */
++      if (errno || (arg == e)) {      /* error or no digits */
++              bb_show_usage();
++      }
++      errno = old_errno;      /* Ok.  So restore errno. */
++
++      /* Do optional suffix parsing.  Allow 'empty' suffix tables.
++       * Note that we also all nul suffixes with associated multipliers,
++       * to allow for scaling of the arg by some default multiplier. */
++
++      if (suffixes) {
++              while (suffixes->suffix) {
++                      if (strcmp(suffixes->suffix, e) == 0) {
++                              if (ULONG_LONG_MAX / suffixes->mult < r) {      /* Overflow! */
++                                      bb_show_usage();
++                              }
++                              ++e;
++                              r *= suffixes->mult;
++                              break;
++                      }
++                      ++suffixes;
++              }
++      }
++
++      /* Finally, check for illegal trailing chars and range limits. */
++      /* Note: although we allow leading space (via stroul), trailing space
++       * is an error.  It would be easy enough to allow though if desired. */
++      if (*e || (r < lower) || (r > upper)) {
++              bb_show_usage();
++      }
++
++      return r;
++}
++#endif
++
++#ifdef L_xgetllarg_bnd_sfx
++extern
++long long bb_xgetllarg_bnd_sfx(const char *arg, int base,
++                                               long long lower,
++                                               long long upper,
++                                               const struct suffix_mult64 *suffixes)
++{
++      unsigned long long u = LONG_LONG_MAX;
++      long long r;
++      const char *p = arg;
++
++      if ((*p == '-') && (p[1] != '+')) {
++              ++p;
++#if LONG_LONG_MAX == (-(LONG_LONG_MIN + 1))
++              ++u;    /* two's complement */
++#endif
++      }
++
++      r = bb_xgetullarg_bnd_sfx(p, base, 0, u, suffixes);
++
++      if (*arg == '-') {
++              r = -r;
++      }
++
++      if ((r < lower) || (r > upper)) {
++              bb_show_usage();
++      }
++
++      return r;
++}
++#endif
++
++#ifdef L_xgetllarg10_sfx
++extern
++long long bb_xgetllarg10_sfx(const char *arg, const struct suffix_mult64 *suffixes)
++{
++      return bb_xgetllarg_bnd_sfx(arg, 10, LONG_LONG_MIN, LONG_LONG_MAX, suffixes);
++}
++#endif
diff --git a/busybox/patches/dd_ibs_and_obs.diff b/busybox/patches/dd_ibs_and_obs.diff
new file mode 100644 (file)
index 0000000..3bbf4b9
--- /dev/null
@@ -0,0 +1,252 @@
+This patch adds support of ibs= and obs= to dd.
+----
+Hideki IWAMOTO  h-iwamoto@kit.hi-ho.ne.jp
+
+
+--- a/include/usage.h  29 Mar 2004 08:20:08 -0000      1.198
++++ b/include/usage.h  4 Apr 2004 07:15:21 -0000
+@@ -309,13 +309,15 @@
+       "64\n"
+ #define dd_trivial_usage \
+-      "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \
+-      "\t  [seek=N] [conv=notrunc|noerror|sync]"
++      "[if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N]\n" \
++      "\t  [skip=N] [seek=N] [conv=notrunc|noerror|sync]"
+ #define dd_full_usage \
+       "Copy a file, converting and formatting according to options\n\n" \
+       "\tif=FILE\t\tread from FILE instead of stdin\n" \
+       "\tof=FILE\t\twrite to FILE instead of stdout\n" \
+-      "\tbs=N\t\tread and write N bytes at a time\n" \
++      "\tibs=N\t\tread N bytes at a time\n" \
++      "\tobs=N\t\twrite N bytes at a time\n" \
++      "\tbs=N\t\tforce ibs=N and obs=N\n" \
+       "\tcount=N\t\tcopy only N input blocks\n" \
+       "\tskip=N\t\tskip N input blocks\n" \
+       "\tseek=N\t\tskip N output blocks\n" \
+@@ -323,6 +325,8 @@
+       "\tconv=noerror\tcontinue after read errors\n" \
+       "\tconv=sync\tpad blocks with zeros\n" \
+       "\n" \
++      "If the bs= expr operand is not specified, the input is processed and collected\n" \
++      "into full-sized output blocks until the end of the input is reached.\n" \
+       "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \
+       "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)."
+ #define dd_example_usage \
+--- a/coreutils/dd.c   30 Jan 2004 22:24:32 -0000      1.55
++++ b/coreutils/dd.c   4 Apr 2004 07:15:21 -0000
+@@ -30,6 +30,10 @@
+ #include <fcntl.h>
+ #include "busybox.h"
++#define C_NOERROR     0x0001
++#define C_TRUNC               0x0002
++#define C_SYNC                0x0004
++#define C_TWOBUFS     0x0008
+ static const struct suffix_mult dd_suffixes[] = {
+       { "c", 1 },
+@@ -51,13 +55,13 @@
+       size_t in_full = 0;
+       size_t in_part = 0;
+       size_t count = -1;
+-      size_t bs = 512;
++      size_t ibs = 512;
++      size_t obs = 512;
++      size_t oc = 0;
+       ssize_t n;
+       off_t seek = 0;
+       off_t skip = 0;
+-      int sync_flag = FALSE;
+-      int noerror = FALSE;
+-      int trunc_flag = TRUE;
++      unsigned int dd_flags = C_TWOBUFS | C_TRUNC;
+       int oflag;
+       int ifd;
+       int ofd;
+@@ -65,11 +69,18 @@
+       const char *infile = NULL;
+       const char *outfile = NULL;
+       char *buf;
++      char *ibuf;
++      char *obuf;
+       for (i = 1; i < argc; i++) {
+-              if (strncmp("bs=", argv[i], 3) == 0)
+-                      bs = bb_xparse_number(argv[i]+3, dd_suffixes);
+-              else if (strncmp("count=", argv[i], 6) == 0)
++              if (strncmp("ibs=", argv[i], 4) == 0)
++                      ibs = bb_xparse_number(argv[i]+4, dd_suffixes);
++              else if (strncmp("obs=", argv[i], 4) == 0)
++                      obs = bb_xparse_number(argv[i]+4, dd_suffixes);
++              else if (strncmp("bs=", argv[i], 3) == 0) {
++                      ibs = obs = bb_xparse_number(argv[i]+3, dd_suffixes);
++                      dd_flags &= ~C_TWOBUFS;
++              } else if (strncmp("count=", argv[i], 6) == 0)
+                       count = bb_xparse_number(argv[i]+6, dd_suffixes);
+               else if (strncmp("seek=", argv[i], 5) == 0)
+                       seek = bb_xparse_number(argv[i]+5, dd_suffixes);
+@@ -83,13 +94,13 @@
+                       buf = argv[i]+5;
+                       while (1) {
+                               if (strncmp("notrunc", buf, 7) == 0) {
+-                                      trunc_flag = FALSE;
++                                      dd_flags &= ~C_TRUNC;
+                                       buf += 7;
+                               } else if (strncmp("sync", buf, 4) == 0) {
+-                                      sync_flag = TRUE;
++                                      dd_flags |= C_SYNC;
+                                       buf += 4;
+                               } else if (strncmp("noerror", buf, 7) == 0) {
+-                                      noerror = TRUE;
++                                      dd_flags |= C_NOERROR;
+                                       buf += 7;
+                               } else {
+                                       bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5);
+@@ -103,7 +114,12 @@
+                       bb_show_usage();
+       }
+-      buf = xmalloc(bs);
++      ibuf = xmalloc(ibs);
++
++      if (dd_flags & C_TWOBUFS)
++              obuf = xmalloc(obs);
++      else
++              obuf = ibuf;
+       if (infile != NULL) {
+               ifd = bb_xopen(infile, O_RDONLY);
+@@ -115,7 +131,7 @@
+       if (outfile != NULL) {
+               oflag = O_WRONLY | O_CREAT;
+-              if (!seek && trunc_flag) {
++              if (!seek && (dd_flags & C_TRUNC)) {
+                       oflag |= O_TRUNC;
+               }
+@@ -123,8 +139,8 @@
+                       bb_perror_msg_and_die("%s", outfile);
+               }
+-              if (seek && trunc_flag) {
+-                      if (ftruncate(ofd, seek * bs) < 0) {
++              if (seek && (dd_flags & C_TRUNC)) {
++                      if (ftruncate(ofd, seek * obs) < 0) {
+                               struct stat st;
+                               if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) ||
+@@ -139,52 +155,88 @@
+       }
+       if (skip) {
+-              if (lseek(ifd, skip * bs, SEEK_CUR) < 0) {
+-                      bb_perror_msg_and_die("%s", infile);
++              if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
++                      while (skip-- > 0) {
++                              n = safe_read(ifd, ibuf, ibs);
++                              if (n < 0)
++                                      bb_perror_msg_and_die("%s", infile);
++                              if (n == 0)
++                                      break;
++                      }
+               }
+       }
+       if (seek) {
+-              if (lseek(ofd, seek * bs, SEEK_CUR) < 0) {
++              if (lseek(ofd, seek * obs, SEEK_CUR) < 0) {
+                       bb_perror_msg_and_die("%s", outfile);
+               }
+       }
+       while (in_full + in_part != count) {
+-              if (noerror) {
++              if (dd_flags & C_NOERROR) {
+                       /* Pre-zero the buffer when doing the noerror thing */
+-                      memset(buf, '\0', bs);
++                      memset(ibuf, '\0', ibs);
++              }
++
++              n = safe_read(ifd, ibuf, ibs);
++              if (n == 0) {
++                      break;
+               }
+-              n = safe_read(ifd, buf, bs);
+               if (n < 0) {
+-                      if (noerror) {
+-                              n = bs;
++                      if (dd_flags & C_NOERROR) {
++                              n = ibs;
+                               bb_perror_msg("%s", infile);
+                       } else {
+                               bb_perror_msg_and_die("%s", infile);
+                       }
+               }
+-              if (n == 0) {
+-                      break;
+-              }
+-              if (n == bs) {
++              if (n == ibs) {
+                       in_full++;
+               } else {
+                       in_part++;
++                      if (dd_flags & C_SYNC) {
++                              memset(ibuf + n, '\0', ibs - n);
++                              n = ibs;
++                      }
+               }
+-              if (sync_flag) {
+-                      memset(buf + n, '\0', bs - n);
+-                      n = bs;
++
++              if (dd_flags & C_TWOBUFS) {
++                      size_t d;
++                      char *tmp = ibuf;
++
++                      while (n) {
++                              d = obs - oc;
++                              if (d > n)
++                                      d = n;
++                              memcpy(obuf + oc, tmp, d);
++                              n -= d;
++                              tmp += d;
++                              oc += d;
++                              if (oc == obs) {
++                                      if (bb_full_write(ofd, obuf, obs) < 0) {
++                                              bb_perror_msg_and_die("%s", outfile);
++                                      }
++                                      out_full++;
++                                      oc = 0;
++                              }
++                      }
++              } else {
++                      if (bb_full_write(ofd, ibuf, n) < 0) {
++                              bb_perror_msg_and_die("%s", outfile);
++                      }
++                      if (n == ibs) {
++                              out_full++;
++                      } else {
++                              out_part++;
++                      }
+               }
+-              n = bb_full_write(ofd, buf, n);
+-              if (n < 0) {
++      }
++
++      if (oc) {
++              if (bb_full_write(ofd, obuf, oc) < 0) {
+                       bb_perror_msg_and_die("%s", outfile);
+               }
+-              if (n == bs) {
+-                      out_full++;
+-              } else {
+-                      out_part++;
+-              }
++              out_part++;
+       }
+       if (close (ifd) < 0) {
+
+
diff --git a/busybox/patches/eject.diff b/busybox/patches/eject.diff
new file mode 100644 (file)
index 0000000..fcc234d
--- /dev/null
@@ -0,0 +1,164 @@
+Index: AUTHORS
+===================================================================
+RCS file: /var/cvs/busybox/AUTHORS,v
+retrieving revision 1.40
+diff -u -r1.40 AUTHORS
+--- a/AUTHORS  9 Oct 2003 21:19:21 -0000       1.40
++++ b/AUTHORS  5 Mar 2004 07:23:17 -0000
+@@ -8,6 +8,9 @@
+
+ -----------
+
++Peter Willis <psyphreak@phreaker.net>
++    eject
++
+ Emanuele Aina <emanuele.aina@tiscali.it>
+       run-parts
+
+Index: coreutils/Config.in
+===================================================================
+RCS file: /var/cvs/busybox/coreutils/Config.in,v
+retrieving revision 1.23
+diff -u -r1.23 Config.in
+--- a/coreutils/Config.in      5 Mar 2004 06:47:25 -0000       1.23
++++ b/coreutils/Config.in      5 Mar 2004 07:23:18 -0000
+@@ -164,6 +164,13 @@
+         a command; without options it displays the current
+         environment.
+
++config CONFIG_EJECT
++      bool "eject"
++      default n
++      help
++        ejects a cdrom drive.
++        defaults to /dev/cdrom
++
+ config CONFIG_EXPR
+       bool "expr"
+       default n
+Index: coreutils/Makefile.in
+===================================================================
+RCS file: /var/cvs/busybox/coreutils/Makefile.in,v
+retrieving revision 1.8
+diff -u -r1.8 Makefile.in
+--- a/coreutils/Makefile.in    27 Jan 2004 09:22:20 -0000      1.8
++++ b/coreutils/Makefile.in    5 Mar 2004 07:23:18 -0000
+@@ -41,6 +41,7 @@
+ COREUTILS-$(CONFIG_DU)        += du.o
+ COREUTILS-$(CONFIG_ECHO)      += echo.o
+ COREUTILS-$(CONFIG_ENV)       += env.o
++COREUTILS-$(CONFIG_EJECT)     += eject.o
+ COREUTILS-$(CONFIG_EXPR)      += expr.o
+ COREUTILS-$(CONFIG_FALSE)     += false.o
+ COREUTILS-$(CONFIG_FOLD)      += fold.o
+Index: coreutils/eject.c
+===================================================================
+RCS file: coreutils/eject.c
+diff -N coreutils/eject.c
+--- /dev/null  1 Jan 1970 00:00:00 -0000
++++ b/coreutils/eject.c        5 Mar 2004 07:23:21 -0000
+@@ -0,0 +1,66 @@
++/*
++ * eject implementation for busybox
++ *
++ * Copyright (C) 2004  Peter Willis <psyphreak@phreaker.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++/*
++ * This is a simple hack of eject based on something Erik posted in #uclibc.
++ * Most of the dirty work blatantly ripped off from cat.c =)
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include "busybox.h"
++#include <linux/cdrom.h> // needs to be after busybox.h or compile problems arise
++
++#define DEFAULT_CDROM "/dev/cdrom"
++
++extern int eject_main(int argc, char **argv)
++{
++      int fd;
++      int flag = CDROMEJECT;
++      int i = 1;
++      char *device = NULL;
++
++      /*
++       * i'm too lazy to learn bb_getopt_ulflags and this is obscenely large
++       * for just some argument parsing so mjn3 can clean it up later.
++       * sorry, but PlumpOS 7.0-pre2 needs this asap :-/
++       */
++      while (++i <= argc) {
++              if ( (! strncmp(argv[i-1],"-t",2)) || (! strncmp(argv[i-1],"--trayclose",11)) ) {
++                      flag = CDROMCLOSETRAY;
++              } else {
++                      device = argv[i-1];
++              }
++      }
++      if ( (fd = open(device == NULL ? DEFAULT_CDROM : device, O_RDONLY | O_NONBLOCK) ) < 0 ) {
++              perror("eject: Can't open device");
++              return(EXIT_FAILURE);
++      }
++      if (ioctl(fd, flag)) {
++              perror("eject: Can't eject cdrom");
++              return(EXIT_FAILURE);
++      }
++      return EXIT_SUCCESS;
++}
+Index: include/applets.h
+===================================================================
+RCS file: /var/cvs/busybox/include/applets.h,v
+retrieving revision 1.111
+diff -u -r1.111 applets.h
+--- a/include/applets.h        27 Jan 2004 09:22:20 -0000      1.111
++++ b/include/applets.h        5 Mar 2004 07:23:21 -0000
+@@ -178,6 +178,9 @@
+ #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
+       APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_EJECT
++      APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_ENV
+       APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+Index: include/usage.h
+===================================================================
+RCS file: /var/cvs/busybox/include/usage.h,v
+retrieving revision 1.191
+diff -u -r1.191 usage.h
+--- a/include/usage.h  25 Feb 2004 10:35:55 -0000      1.191
++++ b/include/usage.h  5 Mar 2004 07:23:29 -0000
+@@ -537,6 +537,13 @@
+       "\t-, -i\tstart with an empty environment\n" \
+       "\t-u\tremove variable from the environment\n"
+
++#define eject_trivial_usage \
++      "[-t] [FILE]"
++#define eject_full_usage \
++      "Ejects the specified FILE or /dev/cdrom if FILE is unspecified.\n\n" \
++      "Options:\n" \
++      "\t-t, --trayclose \tclose tray\n"
++
+ #define expr_trivial_usage \
+       "EXPRESSION"
+ #define expr_full_usage \
diff --git a/busybox/patches/makdevs_table.diff b/busybox/patches/makdevs_table.diff
new file mode 100644 (file)
index 0000000..1041608
--- /dev/null
@@ -0,0 +1,294 @@
+Index: include/usage.h
+===================================================================
+RCS file: /var/cvs/busybox/include/usage.h,v
+retrieving revision 1.211
+diff -u -r1.211 usage.h
+--- a/include/usage.h  26 May 2004 22:09:37 -0000      1.211
++++ b/include/usage.h  5 Jun 2004 07:51:26 -0000
+@@ -1536,6 +1536,7 @@
+ #define lsmod_full_usage \
+       "List the currently loaded kernel modules."
++#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF
+ #define makedevs_trivial_usage \
+       "NAME TYPE MAJOR MINOR FIRST LAST [s]"
+ #define makedevs_full_usage \
+@@ -1555,6 +1556,18 @@
+       "[creates ttyS2-ttyS63]\n" \
+       "# makedevs /dev/hda b 3 0 0 8 s\n" \
+       "[creates hda,hda1-hda8]\n"
++#endif
++
++#ifdef CONFIG_FEATURE_MAKEDEVS_TABLE
++#define makedevs_trivial_usage \
++      "[-r rootdir] [device_table]"
++#define makedevs_full_usage \
++      "Creates a batch of special files as specified in a device table\n" \
++      "The device table has one line per device group, each group is of\n" \
++      "the format\n" \
++      "\ttype mode user group major minor start increment count\n" \
++      "a '-' may be used for blank entries\n"
++#endif
+ #ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+ #define USAGE_MD5_SHA1_SUM_CHECK(a) a
+Index: miscutils/Config.in
+===================================================================
+RCS file: /var/cvs/busybox/miscutils/Config.in,v
+retrieving revision 1.14
+diff -u -r1.14 Config.in
+--- a/miscutils/Config.in      15 Mar 2004 08:28:46 -0000      1.14
++++ b/miscutils/Config.in      5 Jun 2004 07:51:26 -0000
+@@ -143,10 +143,32 @@
+       bool "makedevs"
+       default n
+       help
+-        'makedevs' is a utility used and created by the Linux Router Project.
+-        It creates a large number of device special files (/dev devices)
+-        rather quickly, and can be considerably faster then running mknod a
+-        zillion times.
++        'makedevs' is a utility used to create a batch of devices with
++        one command.
++        .
++        There are two choices for command line behaviour, the interface
++        as used by LEAF/Linux Router Project, or a device table file.
++        . 
++        'leaf' is traditionally what busybox follows, it allows multiple
++        devices of a particluar type to be created per command.
++        e.g. /dev/hda[0-9]
++          Device properties are passed as command line arguments.
++        .
++        'table' reads device properties from a file or stdin, allowing
++        a batch of unrelated devices to be makde with one command. 
++          User/group names are allowed as an alternative to uid/gid.
++
++choice
++      prompt "Choose makedevs behaviour"
++      default CONFIG_FEATURE_MAKDEVS_TABLE
++
++config CONFIG_FEATURE_MAKEDEVS_LEAF
++        bool "leaf"
++
++config CONFIG_FEATURE_MAKEDEVS_TABLE
++      bool "table"
++
++endchoice
+ config CONFIG_MT
+       bool "mt"
+Index: miscutils/makedevs.c
+===================================================================
+RCS file: /var/cvs/busybox/miscutils/makedevs.c,v
+retrieving revision 1.16
+diff -u -r1.16 makedevs.c
+--- a/miscutils/makedevs.c     15 Mar 2004 08:28:46 -0000      1.16
++++ b/miscutils/makedevs.c     5 Jun 2004 07:51:26 -0000
+@@ -1,20 +1,27 @@
+ /* vi: set sw=4 ts=4: */
+-/*
+- * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
+- *
+- * makedevs
+- * Make ranges of device files quickly.
+- * known bugs: can't deal with alpha ranges
+- */
++#include <sys/types.h>
++
++#include <fcntl.h>
++#include <getopt.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <fcntl.h>
++#include <time.h>
+ #include <unistd.h>
+-#include <sys/types.h>
++
+ #include "busybox.h"
++#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF
++
++/*
++ * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
++ *
++ * makedevs
++ * Make ranges of device files quickly.
++ * known bugs: can't deal with alpha ranges
++ */
++ 
+ int makedevs_main(int argc, char **argv)
+ {
+       mode_t mode;
+@@ -69,24 +76,153 @@
+       return 0;
+ }
++#elif defined CONFIG_FEATURE_MAKEDEVS_TABLE
++
+ /*
+-And this is what this program replaces. The shell is too slow!
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2 as
++ *  published by the Free Software Foundation.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU Library General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ */
++
++static const struct option makedevs_long_options[] = {
++      {"root", 1, NULL, 'r'},
++      {0, 0, 0, 0}
++};
+-makedev () {
+-local basedev=$1; local S=$2; local E=$3
+-local major=$4; local Sminor=$5; local type=$6
+-local sbase=$7
+-
+-      if [ ! "$sbase" = "" ]; then
+-              mknod "$basedev" $type $major $Sminor
+-              S=`expr $S + 1`
+-              Sminor=`expr $Sminor + 1`
+-      fi
+-
+-      while [ $S -le $E ]; do
+-              mknod "$basedev$S" $type $major $Sminor
+-              S=`expr $S + 1`
+-              Sminor=`expr $Sminor + 1`
+-      done
++extern int makedevs_main(int argc, char **argv)
++{
++      FILE *table;
++      int opt;
++      char *rootdir = "./";
++      char *line;
++      int ret = EXIT_SUCCESS;
++
++      bb_opt_complementaly = "d~r";
++      bb_applet_long_options = makedevs_long_options;
++      opt = bb_getopt_ulflags(argc, argv, "d:r:", &rootdir, &rootdir);
++
++      if (optind + 1 == argc) {
++              table = bb_xfopen(argv[optind], "r");
++      } else {
++              table = stdin;
++      }
++
++      if (chdir(rootdir) == -1) {
++              bb_perror_msg_and_die("Couldnt chdor to %s", rootdir);
++      }
++
++      umask(0);
++
++      while ((line = bb_get_chomped_line_from_file(table))) {
++              char type;
++              unsigned int mode = 0755;
++              unsigned int major = 0;
++              unsigned int minor = 0;
++              unsigned int count = 0;
++              unsigned int increment = 0;
++              unsigned int start = 0;
++              char name[41];
++              char user[41];
++              char group[41];
++              char *full_name;
++              uid_t uid;
++              gid_t gid;
++
++              if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name,
++                      &type, &mode, user, group, &major,
++                      &minor, &start, &increment, &count)) ||
++                      ((major | minor | start | count | increment) > 255)) {
++                      bb_error_msg("Ignoring invalid line\n%s\n", line);
++                      ret = EXIT_FAILURE;
++                      continue;
++              }
++              if (name[0] == '#') {
++                      continue;
++              }
++              if (group) {
++                      gid = get_ug_id(group, my_getgrnam);
++              } else {
++                      gid = getgid();
++              }
++              if (user) {
++                      uid = get_ug_id(user, my_getpwnam);
++              } else {
++                      uid = getuid();
++              }
++              full_name = concat_path_file(rootdir, name);
++
++              if (type == 'd') {
++                      bb_make_directory(full_name, mode | S_IFDIR, 0);
++                      if (chown(full_name, uid, gid) == -1) {
++                              bb_perror_msg("chown failed for %s", full_name);
++                              ret = EXIT_FAILURE;
++                              goto loop;
++                      }
++              } else {
++                      dev_t rdev;
++
++                      if (type == 'p') {
++                              mode |= S_IFIFO;
++                      }
++                      else if (type == 'c') {
++                              mode |= S_IFCHR;
++                      }
++                      else if (type == 'b') {
++                              mode |= S_IFBLK;
++                      } else {
++                              bb_error_msg("Unsupported file type %c", type);
++                              ret = EXIT_FAILURE;
++                              goto loop;
++                      }
++
++                      if (count > 0) {
++                              int i;
++                              char *full_name_inc;
++
++                              full_name_inc = xmalloc(strlen(full_name) + 4);
++                              for (i = start; i < count; i++) {
++                                      sprintf(full_name_inc, "%s%d", full_name, i);
++                                      rdev = (major << 8) + minor + (i * increment - start);
++                                      if (mknod(full_name_inc, mode, rdev) == -1) {
++                                              bb_perror_msg("Couldnt create node %s", full_name_inc);
++                                              ret = EXIT_FAILURE;
++                                      }
++                                      else if (chown(full_name_inc, uid, gid) == -1) {
++                                              bb_perror_msg("chown failed for %s", full_name_inc);
++                                              ret = EXIT_FAILURE;
++                                      }
++                              }
++                              free(full_name_inc);
++                      } else {
++                              rdev = (major << 8) + minor;
++                              if (mknod(full_name, mode, rdev) == -1) {
++                                      bb_perror_msg("Couldnt create node %s", full_name);
++                                      ret = EXIT_FAILURE;
++                              }
++                              else if (chown(full_name, uid, gid) == -1) {
++                                      bb_perror_msg("chown failed for %s", full_name);
++                                      ret = EXIT_FAILURE;
++                              }
++                      }
++              }
++loop:
++              free(line);
++              free(full_name);
++      }
++      fclose(table);
++
++      return 0;
+ }
+-*/
++#else
++# error makdedevs configuration error, either leaf or table must be selected
++#endif
diff --git a/busybox/patches/rpm2cpio_bzip2.patch b/busybox/patches/rpm2cpio_bzip2.patch
new file mode 100644 (file)
index 0000000..151dd9f
--- /dev/null
@@ -0,0 +1,63 @@
+diff -ur busybox/archival/Config.in busybox/archival/Config.in
+--- busybox/archival/Config.in Sun May 23 09:15:37 2004
++++ busybox/archival/Config.in Sun May 23 09:15:58 2004
+@@ -127,6 +127,14 @@
+       help
+         Converts an RPM file into a CPIO archive.
++config CONFIG_FEATURE_RPM2CPIO_BZIP2
++      bool "  Support bzip2 decompression"
++      default n
++      depends on CONFIG_RPM2CPIO
++      help
++        If you enable this option you'll be able to extract
++        rpms compressed with bzip2.
++
+ config CONFIG_RPM
+       bool "rpm"
+       default n
+diff -ur busybox/archival/libunarchive/Makefile.in busybox/archival/libunarchive/Makefile.in
+--- busybox/archival/libunarchive/Makefile.in  Sun May 23 09:15:04 2004
++++ busybox/archival/libunarchive/Makefile.in  Sun May 23 09:16:42 2004
+@@ -65,6 +65,7 @@
+ LIBUNARCHIVE-$(CONFIG_GUNZIP) += $(GUNZIP_FILES)
+ LIBUNARCHIVE-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o
+ LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o
++LIBUNARCHIVE-$(CONFIG_FEATURE_RPM2CPIO_BZIP2) += decompress_bunzip2.o
+ LIBUNARCHIVE-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o
+ LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o
+ LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o
+diff -ur busybox/archival/rpm2cpio.c busybox/archival/rpm2cpio.c
+--- busybox/archival/rpm2cpio.c        Sun May 23 09:15:04 2004
++++ busybox/archival/rpm2cpio.c        Sun May 23 09:19:03 2004
+@@ -91,14 +91,26 @@
+       skip_header(rpm_fd);
+       bb_xread_all(rpm_fd, &magic, 2);
+-      if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+-              bb_error_msg_and_die("Invalid gzip magic");
++      if ((magic[0] == 0x1f) || (magic[1] == 0x8b)) {
++              check_header_gzip(rpm_fd);
++              if (inflate_gunzip(rpm_fd, fileno(stdout)) != 0)
++                      bb_error_msg("Error inflating (gzip)");
+       }
+-      check_header_gzip(rpm_fd);
+-      if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) {
+-              bb_error_msg("Error inflating");
++      if ((magic[0] == 'B') && (magic[1] == 'Z')) {
++#ifdef CONFIG_FEATURE_RPM2CPIO_BZIP2
++              /* return to position before magic (eek..!) */
++              lseek(rpm_fd, -2, SEEK_CUR);
++              if(uncompressStream(rpm_fd, fileno(stdout)) != 0)
++                      bb_error_msg("Error inflating (bzip2)");
++#else
++              bb_error_msg_and_die("bzip2 not supported");
++#endif
+       }
++
++      else
++              bb_error_msg_and_die("not gzip or bzip2 compressed");
++  
+       close(rpm_fd);
diff --git a/busybox/patches/tftp_timeout_multicast.diff b/busybox/patches/tftp_timeout_multicast.diff
new file mode 100644 (file)
index 0000000..ca431fc
--- /dev/null
@@ -0,0 +1,1053 @@
+Index: AUTHORS
+===================================================================
+RCS file: /var/cvs/busybox/AUTHORS,v
+retrieving revision 1.40
+diff -u -r1.40 AUTHORS
+--- a/AUTHORS  9 Oct 2003 21:19:21 -0000       1.40
++++ b/AUTHORS  5 Mar 2004 15:45:47 -0000
+@@ -92,6 +92,9 @@
+     Original author of BusyBox in 1995, 1996. Some of his code can
+     still be found hiding here and there...
+
++John Powers <jpp@ti.com>
++    Added multicast option (rfc2090) and timeout option (rfc2349) to tftp.
++
+ Tim Riker <Tim@Rikers.org>
+     bug fixes, member of fan club
+
+Index: include/usage.h
+===================================================================
+RCS file: /var/cvs/busybox/include/usage.h,v
+retrieving revision 1.191
+diff -u -r1.191 usage.h
+--- a/include/usage.h  25 Feb 2004 10:35:55 -0000      1.191
++++ b/include/usage.h  5 Mar 2004 15:45:59 -0000
+@@ -2492,6 +2492,21 @@
+ #else
+   #define USAGE_TFTP_BS(a)
+ #endif
++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
++  #define USAGE_TFTP_TIMEOUT(a) a
++#else
++  #define USAGE_TFTP_TIMEOUT(a)
++#endif
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++  #define USAGE_TFTP_MULTICAST(a) a
++#else
++  #define USAGE_TFTP_MULTICAST(a)
++#endif
++#ifdef CONFIG_FEATURE_TFTP_DEBUG
++  #define USAGE_TFTP_DEBUG(a) a
++#else
++  #define USAGE_TFTP_DEBUG(a)
++#endif
+
+ #define tftp_trivial_usage \
+       "[OPTION]... HOST [PORT]"
+@@ -2508,6 +2523,16 @@
+       ) \
+       USAGE_TFTP_BS( \
+       "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \
++      ) \
++      USAGE_TFTP_TIMEOUT( \
++      "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \
++      "\t-t SEC\tServer timeout SEC seconds\n" \
++      ) \
++      USAGE_TFTP_MULTICAST( \
++      "\t-m\tMulticast get file.\n" \
++      ) \
++      USAGE_TFTP_DEBUG( \
++      "\t-D\tPrint debug messages.\n" \
+       )
+ #define time_trivial_usage \
+       "[OPTION]... COMMAND [ARGS...]"
+Index: networking/Config.in
+===================================================================
+RCS file: /var/cvs/busybox/networking/Config.in,v
+retrieving revision 1.27
+diff -u -r1.27 Config.in
+--- a/networking/Config.in     22 Feb 2004 12:25:47 -0000      1.27
++++ b/networking/Config.in     5 Mar 2004 15:45:59 -0000
+@@ -522,6 +522,13 @@
+         Add support for the GET command within the TFTP client.  This allows
+         a client to retrieve a file from a TFTP server.
+
++config CONFIG_FEATURE_TFTP_MULTICAST
++      bool "  Enable \"multicast\" option"
++      default n
++      depends on CONFIG_FEATURE_TFTP_GET
++      help
++        Allow the client to receive multicast file transfers.
++
+ config CONFIG_FEATURE_TFTP_PUT
+       bool "  Enable \"put\" command"
+       default y
+@@ -531,12 +538,19 @@
+         a client to transfer a file to a TFTP server.
+
+ config CONFIG_FEATURE_TFTP_BLOCKSIZE
+-      bool "  Enable \"blocksize\" command"
++      bool "  Enable \"blksize\" option"
+       default n
+       depends on CONFIG_TFTP
+       help
+         Allow the client to specify the desired block size for transfers.
+
++config CONFIG_FEATURE_TFTP_TIMEOUT
++      bool "  Enable \"timeout\" option"
++      default n
++      depends on CONFIG_TFTP
++      help
++        Allow the client to negotiate timeout option with server.
++
+ config CONFIG_FEATURE_TFTP_DEBUG
+       bool "  Enable debug"
+       default n
+Index: networking/tftp.c
+===================================================================
+RCS file: /var/cvs/busybox/networking/tftp.c,v
+retrieving revision 1.25
+diff -u -r1.25 tftp.c
+--- a/networking/tftp.c        5 Mar 2004 13:04:39 -0000       1.25
++++ b/networking/tftp.c        5 Mar 2004 15:46:00 -0000
+@@ -1,11 +1,26 @@
++/* vi: set sw=4 ts=4: */
+ /* ------------------------------------------------------------------------- */
+ /* tftp.c                                                                    */
++/* Copyright (c) 2003, 2004 Texas Instruments                                */
++/*                                                                           */
++/* This package is free software;  you can redistribute it and/or            */
++/* modify it under the terms of the license found in the file                */
++/* named COPYING that should have accompanied this file.                     */
++/*                                                                           */
++/* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR             */
++/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED            */
++/* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.       */
+ /*                                                                           */
+ /* A simple tftp client for busybox.                                         */
+ /* Tries to follow RFC1350.                                                  */
+ /* Only "octet" mode supported.                                              */
+ /* Optional blocksize negotiation (RFC2347 + RFC2348)                        */
+ /*                                                                           */
++/* New features added at Texas Instruments, October 2003                     */
++/* Author: John Powers                                                       */
++/* Multicast option: rfc2090                                                 */
++/* Timeout option: rfc2349                                                   */
++/*                                                                           */
+ /* Copyright (C) 2001 Magnus Damm <damm@opensource.se>                       */
+ /*                                                                           */
+ /* Parts of the code based on:                                               */
+@@ -46,8 +61,20 @@
+
+ #include "busybox.h"
+
++#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT)
++  #define TFTP_OPTIONS
++#endif
++
+ //#define CONFIG_FEATURE_TFTP_DEBUG
+
++#ifdef CONFIG_FEATURE_TFTP_DEBUG
++      static void printtime(void);
++      #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);}
++      int debug = 0;
++#else
++      #define dprintf(fmt...)
++#endif
++
+ #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+ #define TFTP_TIMEOUT 5             /* seconds */
+
+@@ -68,12 +95,24 @@
+       "Illegal TFTP operation",
+       "Unknown transfer ID",
+       "File already exists",
+-      "No such user"
++      "No such user",
++#ifdef TFTP_OPTIONS
++      "Unsupported option",
++#endif
+ };
+
+ const int tftp_cmd_get = 1;
+ const int tftp_cmd_put = 2;
+
++
++struct tftp_option {
++      int multicast;
++      int blksize;
++      int client_timeout;
++      int server_timeout;
++};
++
++
+ #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+
+ static int tftp_blocksize_check(int blocksize, int bufsize)
+@@ -93,16 +132,158 @@
+       return blocksize;
+ }
+
++#endif
++
++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
++
++static int
++tftp_timeout_check(int timeout)
++{
++      /* Check if timeout seconds is valid:
++       * RFC2349 says between 1 and 255.
++       */
++
++      if (timeout < 1 || timeout > 255) {
++              bb_error_msg("bad timeout value");
++              return 0;
++      }
++      return timeout;
++}
++
++#endif
++
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++static int
++tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive)
++{
++      /* Option string contains comma delimited addr,port,active.
++       * addr = multicast IP address
++       * port = port number
++       * active = 1 if active client
++       *          0 if passive client
++       *
++       * Addr and port will be empty fields when the server notifies a
++       * passive client that it is now the active client.
++       *
++       * The host address string must be freed by the caller. Neither host
++       * nor port will be set/changed if the input fields are empty.
++       *
++       * If any tokenization errors occur in the opt string, the host
++       * address string is automatically freed.
++       *
++       * Return 0 if any tokenization error, 1 if all parameters are good.
++       */
++
++      char *token = NULL;
++      char *parse_buf = NULL;
++      char *tokenv = NULL;
++      char *host = NULL;
++      int port;
++      int active;
++
++      parse_buf = bb_xstrdup(opt);
++
++      dprintf("multicast option=%s\n", opt);
++
++      /* IP address */
++      if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) {
++              dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf);
++              free(parse_buf);
++              return 0;
++      }
++      if (strlen(token) > 0)
++              *phost = host = bb_xstrdup(token);
++
++      /* Port */
++      if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
++              dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv);
++              goto token_error;
++      }
++      if (strlen(token) > 0) {
++              port = atoi(token);
++              if (port < 0 || port > 0xFFFF) {
++                      dprintf("tftp_multicast_check: bad port number (%d)\n", port);
++                      goto token_error;
++              }
++              *pport = htons(port);
++      }
++
++      /* Active/passive */
++      if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
++              dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv);
++              goto token_error;
++      }
++      active = atoi(token);
++      if (active != 0 && active != 1) {
++              dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active);
++              goto token_error;
++      }
++      *pactive = active;
++
++      free(parse_buf);
++      return 1;
++
++token_error:
++      free(parse_buf);
++      if (host != NULL)
++              free(host);
++      *phost = NULL;
++      return 0;
++
++}
++
++#define VECTOR_QUANTUM_WIDTH 8
++#define VECTOR_QUANTUM_ALL_ONES ((1<<VECTOR_QUANTUM_WIDTH)-1)
++
++static void inline
++bit_set(int bit, unsigned char *vector)
++{
++      int offset = bit / VECTOR_QUANTUM_WIDTH;
++      int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
++      vector[offset] |= mask;
++}
++
++static int inline
++bit_isset(int bit, const unsigned char *vector)
++{
++      int offset = bit / VECTOR_QUANTUM_WIDTH;
++      int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
++      return vector[offset] & mask ? 1 : 0;
++}
++
++static int inline
++bit_lmz(const unsigned char *vector)
++{
++      /* Return number of left-most zero in bit vector */
++      const unsigned char *vp = vector;
++      int i;
++      unsigned char velem;
++
++      while (*vp == VECTOR_QUANTUM_ALL_ONES)
++              vp++;
++      velem = *vp;
++      for (i = 0; i < VECTOR_QUANTUM_WIDTH; i++) {
++              if ((velem & (1 << i)) == 0)
++                      break;
++      }
++      dprintf("bit_lmz: block=%d\n", (vp - vector)*VECTOR_QUANTUM_WIDTH + i);
++      return (vp - vector)*VECTOR_QUANTUM_WIDTH + i;
++}
++
++#endif
++
++
++
++#ifdef TFTP_OPTIONS
++
+ static char *tftp_option_get(char *buf, int len, char *option)
+ {
+-        int opt_val = 0;
++      int opt_val = 0;
+       int opt_found = 0;
+       int k;
+-
+-      while (len > 0) {
+
++      while (len > 0) {
+               /* Make sure the options are terminated correctly */
+-
+               for (k = 0; k < len; k++) {
+                       if (buf[k] == '\0') {
+                               break;
+@@ -117,9 +298,8 @@
+                       if (strcasecmp(buf, option) == 0) {
+                               opt_found = 1;
+                       }
+-              }
+-              else {
+-                      if (opt_found) {
++              } else {
++                      if (opt_found) {
+                               return buf;
+                       }
+               }
+@@ -138,7 +318,8 @@
+ #endif
+
+ static inline int tftp(const int cmd, const struct hostent *host,
+-      const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
++      const char *remotefile, int localfd, const unsigned short port,
++      struct tftp_option *option)
+ {
+       const int cmd_get = cmd & tftp_cmd_get;
+       const int cmd_put = cmd & tftp_cmd_put;
+@@ -155,18 +336,29 @@
+       int len;
+       int opcode = 0;
+       int finished = 0;
+-      int timeout = bb_tftp_num_retries;
++      int retry = bb_tftp_num_retries;
+       unsigned short block_nr = 1;
+
+-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+-      int want_option_ack = 0;
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++      struct hostent *mchost;
++      struct sockaddr_in mcsa;
++      char *mchostname;
++      unsigned short mcport;
++      unsigned char *mcblockmap = NULL;
++      int master_client = 1;
++      int mcfd = -1;
++      int mcmaxblock = 0x10000;
++      int ack_oack = 0;
++#else
++      #define master_client 1
++    #define ack_oack 0
+ #endif
+
+       /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
+        * size varies meaning BUFFERS_GO_ON_STACK would fail */
+-      char *buf=xmalloc(tftp_bufsize + 4);
++      char *buf=xmalloc(option->blksize + 4);
+
+-      tftp_bufsize += 4;
++      int tftp_bufsize = option->blksize + 4;
+
+       if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+               bb_perror_msg("socket");
+@@ -183,15 +375,21 @@
+       memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
+                  sizeof(sa.sin_addr));
+
+-      /* build opcode */
+-
+-      if (cmd_get) {
+-              opcode = TFTP_RRQ;
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++      if (option->multicast) {
++              const int bmsize = 0x10000 / VECTOR_QUANTUM_WIDTH;
++              if ((mcfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
++                      bb_perror_msg("multicast socket");
++                      return EXIT_FAILURE;
++              }
++              mcblockmap = xmalloc(bmsize+1);
++              memset(mcblockmap, 0, bmsize+1);
+       }
++#endif
+
+-      if (cmd_put) {
+-              opcode = TFTP_WRQ;
+-      }
++      /* build opcode */
++
++      opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ;
+
+       while (1) {
+
+@@ -203,7 +401,7 @@
+
+               cp += 2;
+
+-              /* add filename and mode */
++              /* First packet of file transfer includes file name, mode, and options */
+
+               if ((cmd_get && (opcode == TFTP_RRQ)) ||
+                       (cmd_put && (opcode == TFTP_WRQ))) {
+@@ -223,7 +421,7 @@
+                       }
+
+                       if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
+-                              bb_error_msg("too long remote-filename");
++                              bb_error_msg("too long: remote filename");
+                               break;
+                       }
+
+@@ -238,8 +436,8 @@
+
+                       if (len != TFTP_BLOCKSIZE_DEFAULT) {
+
+-                              if ((&buf[tftp_bufsize - 1] - cp) < 15) {
+-                                      bb_error_msg("too long remote-filename");
++                              if ((&buf[tftp_bufsize - 1] - cp) < 15) {
++                                      bb_error_msg("buffer too small for blksize option");
+                                       break;
+                               }
+
+@@ -249,16 +447,65 @@
+                               cp += 8;
+
+                               cp += snprintf(cp, 6, "%d", len) + 1;
++                      }
++#endif
++
++
++
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++
++                      if (option->multicast) {
++                              if ((&buf[tftp_bufsize - 1] - cp) < 12) {
++                                      bb_error_msg("buffer too small for multicast option");
++                                      break;
++                              }
++
++                              /* add "multicast" option */
+
+-                              want_option_ack = 1;
++                              memcpy(cp, "multicast\0", 11);
++                              cp += 11;
++
++                              option->multicast = 0;  /* turn back on when server accepts option */
++                              ack_oack = 1;   /* acknowledge OACK */
+                       }
++
+ #endif
++
++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
++
++                      if (option->server_timeout != TFTP_TIMEOUT) {
++                              if ((&buf[tftp_bufsize - 1] - cp) < 12) {
++                                      bb_error_msg("buffer too small for timeout option");
++                                      break;
++                              }
++
++                              /* add "timeout" option */
++
++                              memcpy(cp, "timeout", 8);
++                              cp += 8;
++
++                              cp += snprintf(cp, 4, "%d", option->server_timeout) + 1;
++                      }
++#endif
++
+               }
+
+               /* add ack and data */
+
+-              if ((cmd_get && (opcode == TFTP_ACK)) ||
+-                      (cmd_put && (opcode == TFTP_DATA))) {
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++              else if (option->multicast && opcode == TFTP_ACK) {
++                      if (master_client || ack_oack) {
++                              int blocknum = bit_lmz(mcblockmap);
++                              *((unsigned short *) cp) = htons(blocknum);
++                              cp += 2;
++                              if (blocknum >= mcmaxblock)
++                                      finished = 1;
++                              dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": "");
++                      }
++              }
++#endif
++              else if ((cmd_get && opcode == TFTP_ACK) ||
++                      (cmd_put && opcode == TFTP_DATA)) {
+
+                       *((unsigned short *) cp) = htons(block_nr);
+
+@@ -275,7 +522,7 @@
+                               }
+
+                               if (len != (tftp_bufsize - 4)) {
+-                                      finished++;
++                                      finished = 1;
+                               }
+
+                               cp += len;
+@@ -283,82 +530,119 @@
+               }
+
+
+-              /* send packet */
++              /* send packet and receive reply */
+
+
+-              timeout = bb_tftp_num_retries;  /* re-initialize */
++              retry = bb_tftp_num_retries;  /* re-initialize */
+               do {
+-
++                      int selectrc;
+                       len = cp - buf;
+
+-#ifdef CONFIG_FEATURE_TFTP_DEBUG
+-                      fprintf(stderr, "sending %u bytes\n", len);
+-                      for (cp = buf; cp < &buf[len]; cp++)
+-                              fprintf(stderr, "%02x ", (unsigned char)*cp);
+-                      fprintf(stderr, "\n");
+-#endif
+-                      if (sendto(socketfd, buf, len, 0,
+-                                      (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+-                              bb_perror_msg("send");
+-                              len = -1;
+-                              break;
+-                      }
+-
++                      /* send packet */
++                      if ((len > 2) && (! option->multicast || master_client || ack_oack)) {
+
+-                      if (finished && (opcode == TFTP_ACK)) {
+-                              break;
++#ifdef CONFIG_FEATURE_TFTP_DEBUG
++                              dprintf("sending %u bytes\n", len);
++                              for (cp = buf; cp < &buf[len]; cp++)
++                                      if (debug)
++                                              printf("%02x ", *(unsigned char *)cp);
++                              if (debug)
++                                      printf("\n");
++#endif
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++                              ack_oack = 0;
++#endif
++                              if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
++                                      bb_perror_msg("send");
++                                      len = -1;
++                                      break;
++                              }
++                              if (finished && opcode == TFTP_ACK) {
++                                      break;
++                              }
+                       }
+
+-                      /* receive packet */
++                      /* receive reply packet */
+
+                       memset(&from, 0, sizeof(from));
+                       fromlen = sizeof(from);
+
+-                      tv.tv_sec = TFTP_TIMEOUT;
++                      tv.tv_sec = option->client_timeout;
+                       tv.tv_usec = 0;
+
+                       FD_ZERO(&rfds);
+                       FD_SET(socketfd, &rfds);
++                      dprintf("set to receive from socketfd (%d)\n", socketfd);
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++                      if (option->multicast) {
++                              FD_SET(mcfd, &rfds);
++                              dprintf("set to receive from mcfd (%d)\n", mcfd);
++                      }
++#endif
+
+-                      switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
+-                      case 1:
+-                              len = recvfrom(socketfd, buf, tftp_bufsize, 0,
+-                                              (struct sockaddr *) &from, &fromlen);
+-
+-                              if (len < 0) {
+-                                      bb_perror_msg("recvfrom");
+-                                      break;
++                      dprintf("select\n");
++                      selectrc = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
++                      if (selectrc > 0) {
++                              /* A packet was received */
++                              if (FD_ISSET(socketfd, &rfds)) { /* Unicast packet */
++                                      dprintf("from socketfd\n");
++                                      len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
++
++                                      if (len < 0) {
++                                              bb_perror_msg("recvfrom");
++                                      } else {
++                                              if (sa.sin_port == port) {
++                                                      sa.sin_port = from.sin_port;
++                                              }
++                                              if (sa.sin_port == from.sin_port) {
++                                                      retry = 0;
++                                              } else {
++                                                      /* bad packet */
++                                                      /* discard the packet - treat as timeout */
++                                                      retry = bb_tftp_num_retries;
++                                                      bb_error_msg("timeout");
++                                              }
++                                      }
+                               }
+
+-                              timeout = 0;
+-
+-                              if (sa.sin_port == port) {
+-                                      sa.sin_port = from.sin_port;
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++                              else if (option->multicast && FD_ISSET(mcfd, &rfds)) { /* Multicast packet */
++                                      dprintf("from mcfd\n");
++                                      len = recvfrom(mcfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
++                                      if (len < 0) {
++                                              bb_perror_msg("multicast recvfrom");
++                                      } else {
++                                              if (mcsa.sin_port == mcport) {
++                                                      mcsa.sin_port = from.sin_port;
++                                              }
++                                              if (mcsa.sin_port == from.sin_port) {
++                                                      retry = 0;
++                                              } else {
++                                                      retry = bb_tftp_num_retries;
++                                                      bb_error_msg("multicast timeout");
++                                              }
++                                      }
+                               }
+-                              if (sa.sin_port == from.sin_port) {
+-                                      break;
+-                              }
+-
+-                              /* fall-through for bad packets! */
+-                              /* discard the packet - treat as timeout */
+-                              timeout = bb_tftp_num_retries;
++#endif
+
+-                      case 0:
++                      } else if (selectrc == 0) {
++                              /* Time out */
++                              dprintf("timeout\n");
+                               bb_error_msg("timeout");
+
+-                              timeout--;
+-                              if (timeout == 0) {
++                              retry--;
++                              if (retry == 0) {
+                                       len = -1;
+                                       bb_error_msg("last timeout");
+                               }
+-                              break;
+-
+-                      default:
++                      } else {
++                              /* Error condition */
++                              dprintf("error\n");
+                               bb_perror_msg("select");
+                               len = -1;
+                       }
+
+-              } while (timeout && (len >= 0));
++              } while (retry && len >= 0);
+
+               if ((finished) || (len < 0)) {
+                       break;
+@@ -370,9 +654,8 @@
+               opcode = ntohs(*((unsigned short *) buf));
+               tmp = ntohs(*((unsigned short *) &buf[2]));
+
+-#ifdef CONFIG_FEATURE_TFTP_DEBUG
+-              fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
+-#endif
++              dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp);
++              dprintf("master_client=%d\n", master_client);
+
+               if (opcode == TFTP_ERROR) {
+                       char *msg = NULL;
+@@ -393,55 +676,116 @@
+                       break;
+               }
+
+-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+-              if (want_option_ack) {
++#ifdef TFTP_OPTIONS
+
+-                       want_option_ack = 0;
++              if (opcode == TFTP_OACK) {
+
+-                       if (opcode == TFTP_OACK) {
++                      /* server seems to support options */
+
+-                               /* server seems to support options */
++                      char *res;
++
++                      block_nr = 0;           /* acknowledge option packet with block number 0 */
++                      opcode = cmd_put ? TFTP_DATA : TFTP_ACK;
+
+-                               char *res;
+
+-                               res = tftp_option_get(&buf[2], len-2,
+-                                                     "blksize");
++#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
++                      res = tftp_option_get(&buf[2], len-2, "blksize");
+
+-                               if (res) {
+-                                       int blksize = atoi(res);
+-                      
+-                                       if (tftp_blocksize_check(blksize,
+-                                                         tftp_bufsize - 4)) {
++                      if (res) {
++                              int blksize = atoi(res);
+
+-                                               if (cmd_put) {
+-                                                       opcode = TFTP_DATA;
+-                                               }
+-                                               else {
+-                                                       opcode = TFTP_ACK;
+-                                               }
+-#ifdef CONFIG_FEATURE_TFTP_DEBUG
+-                                               fprintf(stderr, "using blksize %u\n", blksize);
++                              if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
++                                      dprintf("using blksize %d\n", blksize);
++                                      tftp_bufsize = blksize + 4;
++                                      free(buf);
++                                      buf = xmalloc(tftp_bufsize);
++                              } else {
++                                      bb_error_msg("bad blksize %d", blksize);
++                                      break;
++                              }
++                      }
+ #endif
+-                                               tftp_bufsize = blksize + 4;
+-                                               block_nr = 0;
+-                                               continue;
+-                                       }
+-                               }
+-                               /* FIXME:
+-                                * we should send ERROR 8 */
+-                               bb_error_msg("bad server option");
+-                               break;
+-                       }
+
+-                       bb_error_msg("warning: blksize not supported by server"
+-                                 " - reverting to 512");
+
+-                       tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++                      res = tftp_option_get(&buf[2], len-2, "multicast");
++
++                      if (res) {
++                              ack_oack = 1;
++                              if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) {
++                                      struct ip_mreq mreq;
++                                      struct in_addr mcaddr;
++
++                                      dprintf("using multicast\n");
++
++                                      mchost = xgethostbyname(mchostname);
++                                      if (mchost) {
++                                              memcpy(&mcaddr, mchost->h_addr, mchost->h_length);
++                                              if (! IN_MULTICAST(ntohl(mcaddr.s_addr))) {
++                                                      bb_error_msg("bad multicast address: %s", mchostname);
++                                                      break;
++                                              }
++                                      } else {
++                                              bb_error_msg("bad multicast address: %s", mchostname);
++                                              break;
++                                      }
++
++                                      memset(&mcsa, 0, sizeof(mcsa));
++                                      mcsa.sin_family = AF_INET;
++                                      mcsa.sin_addr.s_addr = htonl(INADDR_ANY);
++                                      mcsa.sin_port = mcport;
++
++                                      bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa));
++
++                                      mreq.imr_multiaddr.s_addr = mcaddr.s_addr;
++                                      mreq.imr_interface.s_addr = htonl(INADDR_ANY);
++
++                                      if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
++                                      {
++                                              bb_error_msg("setsockopt");
++                                              break;
++                                      }
++
++                                      option->multicast = 1;
++                              } else {
++                                      bb_error_msg("bad multicast option value: %s", res);
++                                      break;
++                              }
++                      }
++#endif
++
+               }
++              else
+ #endif
+
+               if (cmd_get && (opcode == TFTP_DATA)) {
+
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++                      if (option->multicast) {
++                              int bn = tmp - 1;
++                              /* Do I need this block? */
++                              if (! bit_isset(bn, mcblockmap)) {
++                                      lseek(localfd, bn*(tftp_bufsize-4), SEEK_SET);
++                                      len = write(localfd, &buf[4], len-4);
++                                      if (len < 0) {
++                                              bb_perror_msg("write");
++                                              break;
++                                      }
++                                      bit_set(bn, mcblockmap);
++                                      if (len != (tftp_bufsize-4)) {
++                                              mcmaxblock = tmp;
++                                              dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4);
++                                      }
++                                      opcode = TFTP_ACK;
++                              }
++                              /* Do not acknowledge block if I already have a copy of the block. A situation can arise when the server
++                               * and client timeout nearly simultaneously. The server retransmits the block at the same time the client
++                               * re-requests the block. From then on out, each block is transmitted twice--not a good use of bandwidth.
++                               */
++                      }
++                      else
++#endif
++
+                       if (tmp == block_nr) {
+                       
+                               len = write(localfd, &buf[4], len - 4);
+@@ -452,15 +796,14 @@
+                               }
+
+                               if (len != (tftp_bufsize - 4)) {
+-                                      finished++;
++                                      finished = 1;
+                               }
+
+                               opcode = TFTP_ACK;
+-                              continue;
+                       }
+               }
+
+-              if (cmd_put && (opcode == TFTP_ACK)) {
++              else if (cmd_put && opcode == TFTP_ACK) {
+
+                       if (tmp == (unsigned short)(block_nr - 1)) {
+                               if (finished) {
+@@ -468,15 +811,19 @@
+                               }
+
+                               opcode = TFTP_DATA;
+-                              continue;
+                       }
+               }
+       }
+
+ #ifdef CONFIG_FEATURE_CLEAN_UP
+       close(socketfd);
++      free(buf);
++
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++      if (mcblockmap != NULL)
++              free(mcblockmap);
++#endif
+
+-        free(buf);
+ #endif
+
+       return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+@@ -487,13 +834,18 @@
+       struct hostent *host = NULL;
+       char *localfile = NULL;
+       char *remotefile = NULL;
+-      int port;
++      unsigned short port;
+       int cmd = 0;
+       int fd = -1;
+       int flags = 0;
+       int opt;
+       int result;
+-      int blocksize = TFTP_BLOCKSIZE_DEFAULT;
++      struct tftp_option option = {
++              .multicast              = 0,
++              .blksize                = TFTP_BLOCKSIZE_DEFAULT,
++              .client_timeout = TFTP_TIMEOUT,
++              .server_timeout = TFTP_TIMEOUT,
++      };
+
+       /* figure out what to pass to getopt */
+
+@@ -515,13 +867,45 @@
+ #define PUT
+ #endif
+
+-      while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
++#define TO "T:t:"
++#else
++#define TO
++#endif
++
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++#define MC "m"
++#else
++#define MC
++#endif
++
++#ifdef CONFIG_FEATURE_TFTP_DEBUG
++#define DB "D"
++#else
++#define DB
++#endif
++
++      while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) {
+               switch (opt) {
+ #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+               case 'b':
+-                      blocksize = atoi(optarg);
+-                      if (!tftp_blocksize_check(blocksize, 0)) {
+-                                return EXIT_FAILURE;
++                      option.blksize = atoi(optarg);
++                      if (!tftp_blocksize_check(option.blksize, 0)) {
++                              return EXIT_FAILURE;
++                      }
++                      break;
++#endif
++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
++              case 'T':
++                      option.client_timeout = atoi(optarg);
++                      if (!tftp_timeout_check(option.client_timeout)) {
++                              return EXIT_FAILURE;
++                      }
++                      break;
++              case 't':
++                      option.server_timeout = atoi(optarg);
++                      if (!tftp_timeout_check(option.server_timeout)) {
++                              return EXIT_FAILURE;
+                       }
+                       break;
+ #endif
+@@ -537,18 +921,34 @@
+                       flags = O_RDONLY;
+                       break;
+ #endif
++#ifdef CONFIG_FEATURE_TFTP_MULTICAST
++              case 'm':
++                      option.multicast = 1;   /* receive multicast file */
++                      break;
++#endif
++#ifdef CONFIG_FEATURE_TFTP_DEBUG
++              case 'D':
++                      debug = 1;
++                      break;
++#endif
+               case 'l':
+                       localfile = bb_xstrdup(optarg);
+                       break;
+               case 'r':
+                       remotefile = bb_xstrdup(optarg);
+                       break;
++              default:
++                      bb_show_usage();
+               }
+       }
+
+       if ((cmd == 0) || (optind == argc)) {
+               bb_show_usage();
+       }
++      if (cmd == tftp_cmd_put && option.multicast) {
++              fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n");
++              exit(EXIT_FAILURE);
++      }
+       if(localfile && strcmp(localfile, "-") == 0) {
+           fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
+       }
+@@ -566,14 +966,12 @@
+       host = xgethostbyname(argv[optind]);
+       port = bb_lookup_port(argv[optind + 1], "udp", 69);
+
+-#ifdef CONFIG_FEATURE_TFTP_DEBUG
+-      fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
++      dprintf("using server \"%s\", remotefile \"%s\", "
+               "localfile \"%s\".\n",
+               inet_ntoa(*((struct in_addr *) host->h_addr)),
+               remotefile, localfile);
+-#endif
+
+-      result = tftp(cmd, host, remotefile, fd, port, blocksize);
++      result = tftp(cmd, host, remotefile, fd, port, &option);
+
+ #ifdef CONFIG_FEATURE_CLEAN_UP
+       if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
+@@ -582,3 +980,18 @@
+ #endif
+       return(result);
+ }
++
++
++#ifdef CONFIG_FEATURE_TFTP_DEBUG
++
++#include <sys/time.h>
++
++static void
++printtime(void)
++{
++      struct timeval tv;
++      gettimeofday(&tv, NULL);
++      printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec);
++}
++
++#endif
diff --git a/busybox/patches/top_system_cpu.diff b/busybox/patches/top_system_cpu.diff
new file mode 100644 (file)
index 0000000..5d213e7
--- /dev/null
@@ -0,0 +1,51 @@
+diff -purN busybox.ori/include/libbb.h busybox/include/libbb.h
+--- busybox.ori/include/libbb.h        2004-03-21 14:39:35.000000000 +0100
++++ busybox-1.0/include/libbb.h        2004-03-21 14:45:35.000000000 +0100
+@@ -447,6 +447,7 @@ typedef struct {
+       int ppid;
+ #ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       unsigned pcpu;
++      unsigned pscpu;
+       unsigned long stime, utime;
+ #endif
+       char *cmd;
+diff -purN busybox.ori/procps/top.c busybox/procps/top.c
+--- busybox.ori/procps/top.c   2004-03-21 14:40:09.000000000 +0100
++++ busybox-1.0/procps/top.c   2004-03-21 17:27:52.961951448 +0100
+@@ -289,6 +289,15 @@ static void do_stats(void)
+           i = 999;
+       cur->pcpu = i;
++      /*
++       * Calculate percent of system time from cpu time
++       */
++      if (systime != 0) {
++          cur->pscpu = 100 * total_time / systime;
++      } else {
++          cur->pscpu = 0;
++      }
++
+     }
+     /*
+@@ -393,7 +402,7 @@ static void display_status(int count, in
+       
+ #ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       /* what info of the processes is shown */
+-      printf("\n\e[7m  PID USER     STATUS   RSS  PPID %%CPU %%MEM COMMAND\e[0m\n");
++      printf("\n\e[7m  PID USER     STATUS   RSS  PPID %%CPU %%SCPU %%MEM COMMAND\e[0m\n");
+ #else
+       printf("\n\e[7m  PID USER     STATUS   RSS  PPID %%MEM COMMAND\e[0m\n");
+ #endif
+@@ -410,9 +419,9 @@ static void display_status(int count, in
+               else
+                       sprintf(rss_str_buf, "%7ld", s->rss);
+ #ifdef FEATURE_CPU_USAGE_PERCENTAGE
+-              printf("%5d %-8s %s  %s %5d %2d.%d %2u.%u ",
++              printf("%5d %-8s %s  %s %5d %2d.%d %2d.%d %2u.%u ",
+                       s->pid, s->user, s->state, rss_str_buf, s->ppid,
+-                      s->pcpu/10, s->pcpu%10, pmem/10, pmem%10);
++                      s->pcpu/10, s->pcpu%10,s->pscpu/10, s->pscpu%10, pmem/10, pmem%10);
+ #else
+               printf("%5d %-8s %s  %s %5d %2u.%u ",
+                       s->pid, s->user, s->state, rss_str_buf, s->ppid,
diff --git a/busybox/patches/udhcp_additional_items.diff b/busybox/patches/udhcp_additional_items.diff
new file mode 100644 (file)
index 0000000..933be2a
--- /dev/null
@@ -0,0 +1,126 @@
+Index: include/usage.h
+===================================================================
+RCS file: /var/cvs/busybox/include/usage.h,v
+retrieving revision 1.191
+diff -u -r1.191 usage.h
+--- a/include/usage.h  25 Feb 2004 10:35:55 -0000      1.191
++++ b/include/usage.h  5 Mar 2004 14:32:45 -0000
+@@ -2606,6 +2606,7 @@
+       "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
+       "\t-q,\t--quit\tQuit after obtaining lease\n" \
+       "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
++      "\t-R,\t--require=NAME\tAdd NAME to request\n" \
+       "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
+       "\t-v,\t--version\tDisplay version"
+
+Index: networking/udhcp/README.udhcpc
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpc,v
+retrieving revision 1.3
+diff -u -r1.3 README.udhcpc
+--- a/networking/udhcp/README.udhcpc   11 Dec 2002 21:12:44 -0000      1.3
++++ b/networking/udhcp/README.udhcpc   5 Mar 2004 14:32:46 -0000
+@@ -22,6 +22,7 @@
+ -p, --pidfile=file              Store process ID of daemon in file
+ -q, --quit                      Quit after obtaining lease
+ -r, --request=IP                IP address to request (default: none)
++-R, --require=NAME              Add NAME to request
+ -s, --script=file               Run file at dhcp events (default:
+                                 /usr/share/udhcpc/default.script)
+ -v, --version                   Display version
+@@ -101,6 +102,8 @@
+
+ additional options are easily added in options.c.
+
++By default, only a few basic items are requested. To request additional
++items use the -R option. Example: "-R rootpath"
+
+ note on udhcpc's random seed
+ ---------------------------
+Index: networking/udhcp/dhcpc.c
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/dhcpc.c,v
+retrieving revision 1.16
+diff -u -r1.16 dhcpc.c
+--- a/networking/udhcp/dhcpc.c 30 Jan 2004 23:45:12 -0000      1.16
++++ b/networking/udhcp/dhcpc.c 5 Mar 2004 14:32:46 -0000
+@@ -88,6 +88,7 @@
+ "  -p, --pidfile=file              Store process ID of daemon in file\n"
+ "  -q, --quit                      Quit after obtaining lease\n"
+ "  -r, --request=IP                IP address to request (default: none)\n"
++"  -R, --require=NAME              Add NAME to the request\n"
+ "  -s, --script=file               Run file at dhcp events (default:\n"
+ "                                  " DEFAULT_SCRIPT ")\n"
+ "  -v, --version                   Display version\n"
+@@ -203,6 +204,7 @@
+               {"pidfile",     required_argument,      0, 'p'},
+               {"quit",        no_argument,            0, 'q'},
+               {"request",     required_argument,      0, 'r'},
++              {"require",     required_argument,      0, 'R'},
+               {"script",      required_argument,      0, 's'},
+               {"version",     no_argument,            0, 'v'},
+               {0, 0, 0, 0}
+@@ -211,7 +213,7 @@
+       /* get options */
+       while (1) {
+               int option_index = 0;
+-              c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
++              c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:R:s:v", arg_options, &option_index);
+               if (c == -1) break;
+
+               switch (c) {
+@@ -254,6 +256,11 @@
+               case 'r':
+                       requested_ip = inet_addr(optarg);
+                       break;
++              case 'R':
++                      if (require_option(optarg)) {
++                              fprintf(stderr,"WARNING: %s unknown/not-supported (Ignored)\n", optarg );
++                      }
++                      break;
+               case 's':
+                       client_config.script = optarg;
+                       break;
+Index: networking/udhcp/options.c
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/options.c,v
+retrieving revision 1.7
+diff -u -r1.7 options.c
+--- a/networking/udhcp/options.c       30 Jan 2004 23:45:12 -0000      1.7
++++ b/networking/udhcp/options.c       5 Mar 2004 14:32:46 -0000
+@@ -57,7 +57,19 @@
+       [OPTION_S32] =          4
+ };
+
+-
++/* find and mark requested item as required */
++int require_option(char *name)
++{
++      int i;
++      for (i = 0; dhcp_options[i].code; i++) {
++              if (strcmp(name, dhcp_options[i].name) == 0 ){
++                      dhcp_options[i].flags |= OPTION_REQ;
++                      return 0;
++              }
++      }
++      return 1;
++}
++
+ /* get an option with bounds checking (warning, not aligned). */
+ uint8_t *get_option(struct dhcpMessage *packet, int code)
+ {
+Index: networking/udhcp/options.h
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/options.h,v
+retrieving revision 1.5
+diff -u -r1.5 options.h
+--- a/networking/udhcp/options.h       30 Jan 2004 23:45:12 -0000      1.5
++++ b/networking/udhcp/options.h       5 Mar 2004 14:32:46 -0000
+@@ -30,6 +30,7 @@
+ extern struct dhcp_option dhcp_options[];
+ extern int option_lengths[];
+
++int require_option(char *name);
+ uint8_t *get_option(struct dhcpMessage *packet, int code);
+ int end_option(uint8_t *optionptr);
+ int add_option_string(uint8_t *optionptr, uint8_t *string);
diff --git a/busybox/patches/udhcp_config_paths.diff b/busybox/patches/udhcp_config_paths.diff
new file mode 100644 (file)
index 0000000..1d3a6b4
--- /dev/null
@@ -0,0 +1,333 @@
+Index: include/usage.h
+===================================================================
+RCS file: /var/cvs/busybox/include/usage.h,v
+retrieving revision 1.191
+diff -u -r1.191 usage.h
+--- a/include/usage.h  25 Feb 2004 10:35:55 -0000      1.191
++++ b/include/usage.h  5 Mar 2004 13:20:11 -0000
+@@ -2606,7 +2606,8 @@
+       "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
+       "\t-q,\t--quit\tQuit after obtaining lease\n" \
+       "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
+-      "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
++      "\t-s,\t--script=file\tRun file at dhcp events (default: " \
++      CONFIG_UDHCPC_SCRIPT_PATH ")\n" \
+       "\t-v,\t--version\tDisplay version"
+
+ #define udhcpd_trivial_usage \
+Index: networking/udhcp/AUTHORS
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/AUTHORS,v
+retrieving revision 1.3
+diff -u -r1.3 AUTHORS
+--- a/networking/udhcp/AUTHORS 18 Dec 2003 22:25:38 -0000      1.3
++++ b/networking/udhcp/AUTHORS 5 Mar 2004 13:20:11 -0000
+@@ -10,5 +10,5 @@
+ Moreton Bay   (http://www.moretonbay.com/)
+ Vladimir Oleynik <dzo@simtrea.ru> Size optimizations
+
+-
++Tony J. White <tjw@tjw.org> additional busybox build options
+
+Index: networking/udhcp/Config.in
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/Config.in,v
+retrieving revision 1.5
+diff -u -r1.5 Config.in
+--- a/networking/udhcp/Config.in       22 Oct 2003 09:58:38 -0000      1.5
++++ b/networking/udhcp/Config.in       5 Mar 2004 13:20:11 -0000
+@@ -58,5 +58,62 @@
+
+         See http://udhcp.busybox.net for further details.
+
++menu "udhcpd Configuration Options"
++      depends on CONFIG_UDHCPD
++
++config CONFIG_UDHCPD_CONF_PATH
++      string "Path to default udhcpd.conf"
++      default "/etc/udhcpd.conf"
++      depends on CONFIG_UDHCPD
++      help
++        The full path to udhcpd's default configuration file.
++        (default is: /etc/udhcpd.conf)
++
++config CONFIG_UDHCPD_LEASE_PATH
++      string "Path to default udhcpd.leases"
++      default "/var/lib/misc/udhcpd.leases"
++      depends on CONFIG_UDHCPD
++      help
++        The full path to udhcpd's default leases file.
++        (default is: /var/lib/misc/udhcpd.leases)
++
++config CONFIG_UDHCPD_PID_PATH
++      string "Path to default udhcpd PID file"
++      default "/var/run/udhcpd.pid"
++      depends on CONFIG_UDHCPD
++      help
++        The full path to udhcpd's default pid file.
++        (default is: /var/run/udhcpd.pid)
++
++endmenu
++
++menu "udhcpc Configuration Options"
++      depends on CONFIG_UDHCPC
++
++config CONFIG_UDHCPC_SCRIPT_PATH
++      string "Path to default udhcpc event script"
++      depends on CONFIG_UDHCPC
++      help
++        The full path to udhcpc's default event script file.
++        (default is: /usr/share/udhcpc/default.script OR
++        /share/udhcpc/default.script if CONFIG_INSTALL_NO_USR is set)
++      
++        When udhcpc is started it executes this script to take care
++        of system tasks after it completes DHCP communication.  Such
++        tasks include putting network interfaces up or down, setting
++        DNS info, adding routing information, etc.
++
++if CONFIG_INSTALL_NO_USR
++config CONFIG_UDHCPC_SCRIPT_PATH
++      default "/share/udhcpc/default.script"
++endif
++
++if !CONFIG_INSTALL_NO_USR
++config CONFIG_UDHCPC_SCRIPT_PATH
++      default "/usr/share/udhcpc/default.script"
++endif
++
++endmenu
++
+ endmenu
+
+Index: networking/udhcp/README
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/README,v
+retrieving revision 1.3
+diff -u -r1.3 README
+--- a/networking/udhcp/README  18 Dec 2003 22:25:38 -0000      1.3
++++ b/networking/udhcp/README  5 Mar 2004 13:20:11 -0000
+@@ -9,27 +9,42 @@
+ compile time options
+ -------------------
+
+-The Makefile contains three of the compile time options:
++The following options can be adjusted when configuring busybox:
+
+-      UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra
+-      debugging output, compile with -g, and not fork to the background when
+-      run.
+-      UDHCP_SYSLOG: If UDHCP_SYSLOG is defined, udhcpd will log all its
+-      messages syslog, otherwise, it will attempt to log them to stdout.
+-
+-      COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd,
+-      is created. If called as udhcpd, the dhcp server will be started.
+-      If called as udhcpc, the dhcp client will be started.
+-
+-dhcpd.h contains the other three compile time options:
+-
+-      LEASE_TIME: The default lease time if not specified in the config
+-      file.
++      CONFIG_FEATURE_UDHCP_DEBUG:
++        If this is defined, udhcpd will output extra debugging output,
++        compile with -g, and not fork to the background when run.
+
+-      LEASES_FILE: The default file for storing leases.
+-
+-      DHCPD_CONFIG_FILE: The defualt config file to use.
++      CONFIG_FEATURE_UDHCP_SYSLOG:
++        If this is defined, udhcpd will log all its messages syslog,
++        otherwise, it will attempt to log them to stdout.
++
++      CONFIG_UDHCPD_CONF_PATH:
++        The full path to udhcpd's default configuration file.
++
++      CONFIG_UDHCPD_LEASE_PATH:
++        The full path to udhcpd's default leases file.
++
++      CONFIG_UDHCPD_PID_PATH:
++        The full path to udhcpd's default pid file.
++
++      CONFIG_UDHCPC_SCRIPT_PATH:
++        The full path to udhcpc's default event script file.
++        (default is: /usr/share/udhcpc/default.script)
++
++        When udhcpc is started it executes this script to take care
++        of system tasks after it completes DHCP communication.  Such
++        tasks include putting network interfaces up or down, setting
++        DNS info, adding routing information, etc.
++
++
++dhcpd.h contains the another compile time option:
+
++      LEASE_TIME:
++        The default lease time if not specified in the config file.
++        This option can also be changed at runtime with the 'lease'
++        configuration option.
++
+ options.c contains a set of dhcp options for the client:
+
+       name[10]: The name of the option as it will appear in scripts
+Index: networking/udhcp/README.udhcpc
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpc,v
+retrieving revision 1.3
+diff -u -r1.3 README.udhcpc
+--- a/networking/udhcp/README.udhcpc   11 Dec 2002 21:12:44 -0000      1.3
++++ b/networking/udhcp/README.udhcpc   5 Mar 2004 13:20:11 -0000
+@@ -23,7 +23,8 @@
+ -q, --quit                      Quit after obtaining lease
+ -r, --request=IP                IP address to request (default: none)
+ -s, --script=file               Run file at dhcp events (default:
+-                                /usr/share/udhcpc/default.script)
++                                /usr/share/udhcpc/default.script or
++                              CONFIG_UDHCPC_SCRIPT_PATH at build time)
+ -v, --version                   Display version
+
+
+Index: networking/udhcp/README.udhcpd
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpd,v
+retrieving revision 1.1
+diff -u -r1.1 README.udhcpd
+--- a/networking/udhcp/README.udhcpd   31 Oct 2002 19:21:27 -0000      1.1
++++ b/networking/udhcp/README.udhcpd   5 Mar 2004 13:20:11 -0000
+@@ -50,10 +50,14 @@
+
+ compile time options
+ -------------------
++
++During busybox configuration, you can change the default paths for
++udhcpd.conf, udhcpd.leases, and udhcpd.pid files.  See README for
++more details.
+
+-dhcpd.h contains the other two compile time options:
++dhcpd.h contains the compile time option:
+
+       LEASE_TIME: The default lease time if not specified in the config
+       file.
++
+
+-      DHCPD_CONFIG_FILE: The defualt config file to use.
+Index: networking/udhcp/dhcpc.h
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/dhcpc.h,v
+retrieving revision 1.4
+diff -u -r1.4 dhcpc.h
+--- a/networking/udhcp/dhcpc.h 30 Jan 2004 23:45:12 -0000      1.4
++++ b/networking/udhcp/dhcpc.h 5 Mar 2004 13:20:11 -0000
+@@ -2,7 +2,11 @@
+ #ifndef _DHCPC_H
+ #define _DHCPC_H
+
+-#define DEFAULT_SCRIPT  "/usr/share/udhcpc/default.script"
++#ifdef CONFIG_UDHCPC_SCRIPT_PATH
++ #define DEFAULT_SCRIPT  CONFIG_UDHCPC_SCRIPT_PATH
++#else
++ #define DEFAULT_SCRIPT  "/usr/share/udhcpc/default.script"
++#endif
+
+ /* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */
+ #include "libbb_udhcp.h"
+Index: networking/udhcp/dhcpd.c
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.c,v
+retrieving revision 1.5
+diff -u -r1.5 dhcpd.c
+--- a/networking/udhcp/dhcpd.c 30 Jan 2004 23:45:12 -0000      1.5
++++ b/networking/udhcp/dhcpd.c 5 Mar 2004 13:20:11 -0000
+@@ -70,6 +70,13 @@
+       struct dhcpOfferedAddr *lease;
+       int max_sock;
+       unsigned long num_ips;
++      int daemonize = 1;
++
++      while (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"--foreground")==0) {
++              daemonize = 0;
++              argv++;
++              argc--;
++      }
+
+       memset(&server_config, 0, sizeof(struct server_config_t));
+       read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
+@@ -99,9 +106,8 @@
+                          &server_config.server, server_config.arp) < 0)
+               return 1;
+
+-#ifndef UDHCP_DEBUG
+-      background(server_config.pidfile); /* hold lock during fork. */
+-#endif
++      if(daemonize)
++              background(server_config.pidfile); /* hold lock during fork. */
+
+       /* Setup the signal pipe */
+       udhcp_sp_setup();
+Index: networking/udhcp/dhcpd.h
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.h,v
+retrieving revision 1.5
+diff -u -r1.5 dhcpd.h
+--- a/networking/udhcp/dhcpd.h 30 Jan 2004 23:45:12 -0000      1.5
++++ b/networking/udhcp/dhcpd.h 5 Mar 2004 13:20:12 -0000
+@@ -15,11 +15,25 @@
+
+ /* the period of time the client is allowed to use that address */
+ #define LEASE_TIME              (60*60*24*10) /* 10 days of seconds */
+-#define LEASES_FILE           "/var/lib/misc/udhcpd.leases"
++
++#ifdef CONFIG_UDHCPD_LEASE_PATH
++ #define LEASES_FILE            CONFIG_UDHCPD_LEASE_PATH
++#else
++ #define LEASES_FILE          "/var/lib/misc/udhcpd.leases"
++#endif
+
+ /* where to find the DHCP server configuration file */
+-#define DHCPD_CONF_FILE         "/etc/udhcpd.conf"
++#ifdef CONFIG_UDHCPD_CONF_PATH
++ #define DHCPD_CONF_FILE         CONFIG_UDHCPD_CONF_PATH
++#else
++ #define DHCPD_CONF_FILE         "/etc/udhcpd.conf"
++#endif
+
++#ifdef CONFIG_UDHCPD_PID_PATH
++ #define DHCPD_PID_FILE         CONFIG_UDHCPD_PID_PATH
++#else
++ #define DHCPD_PID_FILE         "/var/run/udhcpd.pid"
++#endif
+ /*****************************************************************/
+ /* Do not modify below here unless you know what you are doing!! */
+ /*****************************************************************/
+Index: networking/udhcp/files.c
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/files.c,v
+retrieving revision 1.13
+diff -u -r1.13 files.c
+--- a/networking/udhcp/files.c 30 Jan 2004 23:45:12 -0000      1.13
++++ b/networking/udhcp/files.c 5 Mar 2004 13:20:13 -0000
+@@ -166,7 +166,7 @@
+       {"offer_time",  read_u32, &(server_config.offer_time),  "60"},
+       {"min_lease",   read_u32, &(server_config.min_lease),   "60"},
+       {"lease_file",  read_str, &(server_config.lease_file),  LEASES_FILE},
+-      {"pidfile",     read_str, &(server_config.pidfile),     "/var/run/udhcpd.pid"},
++      {"pidfile",     read_str, &(server_config.pidfile),     DHCPD_PID_FILE},
+       {"notify_file", read_str, &(server_config.notify_file), ""},
+       {"siaddr",      read_ip,  &(server_config.siaddr),      "0.0.0.0"},
+       {"sname",       read_str, &(server_config.sname),       ""},
+Index: networking/udhcp/libbb_udhcp.h
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/libbb_udhcp.h,v
+retrieving revision 1.5
+diff -u -r1.5 libbb_udhcp.h
+--- a/networking/udhcp/libbb_udhcp.h   18 Dec 2003 22:25:38 -0000      1.5
++++ b/networking/udhcp/libbb_udhcp.h   5 Mar 2004 13:20:13 -0000
+@@ -3,11 +3,6 @@
+ /* bit of a hack, do this no matter what the order of the includes.
+  * (for busybox) */
+
+-#ifdef CONFIG_INSTALL_NO_USR
+-#undef DEFUALT_SCRIPT
+-#define DEFAULT_SCRIPT  "/share/udhcpc/default.script"
+-#endif
+-
+ #ifndef _LIBBB_UDHCP_H
+ #define _LIBBB_UDHCP_H
+
diff --git a/busybox/patches/udhcpd_foreground.diff b/busybox/patches/udhcpd_foreground.diff
new file mode 100644 (file)
index 0000000..3b8c7eb
--- /dev/null
@@ -0,0 +1,33 @@
+Index: ./networking/udhcp/dhcpd.c
+===================================================================
+RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.c,v
+retrieving revision 1.5
+diff -u -r1.5 dhcpd.c
+--- a/./networking/udhcp/dhcpd.c       30 Jan 2004 23:45:12 -0000      1.5
++++ b/./networking/udhcp/dhcpd.c       5 Mar 2004 13:09:05 -0000
+@@ -70,6 +70,13 @@
+       struct dhcpOfferedAddr *lease;
+       int max_sock;
+       unsigned long num_ips;
++      int daemonize = 1;
++
++      while (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"--foreground")==0) {
++              daemonize = 0;
++              argv++;
++              argc--;
++      }
+
+       memset(&server_config, 0, sizeof(struct server_config_t));
+       read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
+@@ -99,9 +106,8 @@
+                          &server_config.server, server_config.arp) < 0)
+               return 1;
+
+-#ifndef UDHCP_DEBUG
+-      background(server_config.pidfile); /* hold lock during fork. */
+-#endif
++      if(daemonize)
++              background(server_config.pidfile); /* hold lock during fork. */
+
+       /* Setup the signal pipe */
+       udhcp_sp_setup();
diff --git a/busybox/procps/Config.in b/busybox/procps/Config.in
new file mode 100644 (file)
index 0000000..8d55797
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Process Utilities"
+
+config CONFIG_FREE
+       bool "free"
+       default n
+       help
+         free displays the total amount of free and used physical and swap
+         memory in the system, as well as the buffers used by the kernel.
+         The shared memory column should be ignored; it is obsolete.
+
+config CONFIG_KILL
+       bool "kill"
+       default n
+       help
+         The command kill sends the specified signal to the specified
+         process or process group.  If no signal is specified, the TERM
+         signal is sent.
+
+config CONFIG_KILLALL
+       bool "killall"
+       default n
+       depends on CONFIG_KILL
+       help
+         killall sends a signal to all processes running any of the
+         specified commands.  If no signal name is specified, SIGTERM is
+         sent.
+
+config CONFIG_PIDOF
+       bool "pidof"
+       default n
+       help
+         Pidof finds the process id's (pids) of the named programs. It prints
+         those id's on the standard output.
+
+config CONFIG_PS
+       bool "ps"
+       default n
+       help
+         ps gives a snapshot of the current processes.
+
+config CONFIG_RENICE
+       bool "renice"
+       default n
+       help
+         Renice alters the scheduling priority of one or more running
+         processes.
+
+config CONFIG_TOP
+       bool "top"
+       default n
+       help
+         The top program provides a dynamic real-time view of a running
+         system.
+
+config FEATURE_CPU_USAGE_PERCENTAGE
+       bool "  Support showing CPU usage percentage (add 2k bytes)"
+       default y
+       depends on CONFIG_TOP
+       help
+         Make top display CPU usage.
+
+config CONFIG_UPTIME
+       bool "uptime"
+       default n
+       help
+         uptime gives a one line display of the current time, how long
+         the system has been running, how many users are currently logged
+         on, and the system load averages for the past 1, 5, and 15 minutes.
+
+config CONFIG_SYSCTL
+       bool "sysctl"
+       default n
+       help
+         sysctl - configure kernel parameters at runtime
+
+endmenu
+
diff --git a/busybox/procps/Makefile b/busybox/procps/Makefile
new file mode 100644 (file)
index 0000000..1cc8804
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/procps
+PROCPS_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/procps/Makefile.in b/busybox/procps/Makefile.in
new file mode 100644 (file)
index 0000000..ced29a1
--- /dev/null
@@ -0,0 +1,43 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+PROCPS_AR:=procps.a
+ifndef $(PROCPS_DIR)
+PROCPS_DIR:=$(top_builddir)/procps/
+endif
+srcdir=$(top_srcdir)/procps
+
+PROCPS-y:=
+PROCPS-$(CONFIG_FREE)          += free.o
+PROCPS-$(CONFIG_KILL)          += kill.o
+PROCPS-$(CONFIG_PIDOF)         += pidof.o
+PROCPS-$(CONFIG_PS)            += ps.o
+PROCPS-$(CONFIG_RENICE)                += renice.o
+PROCPS-$(CONFIG_SYSCTL)                += sysctl.o
+PROCPS-$(CONFIG_TOP)           += top.o
+PROCPS-$(CONFIG_UPTIME)                += uptime.o
+
+libraries-y+=$(PROCPS_DIR)$(PROCPS_AR)
+
+$(PROCPS_DIR)$(PROCPS_AR): $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y))
+       $(AR) -ro $@ $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y))
+
+$(PROCPS_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/procps/free.c b/busybox/procps/free.c
new file mode 100644 (file)
index 0000000..4fb047d
--- /dev/null
@@ -0,0 +1,84 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini free implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* getopt not needed */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int free_main(int argc, char **argv)
+{
+       struct sysinfo info;
+       sysinfo(&info);
+
+       /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
+       if (info.mem_unit==0) {
+               info.mem_unit=1;
+       }
+       if ( info.mem_unit == 1 ) {
+               info.mem_unit=1024;
+
+               /* TODO:  Make all this stuff not overflow when mem >= 4 Gib */
+               info.totalram/=info.mem_unit;
+               info.freeram/=info.mem_unit;
+#ifndef __uClinux__
+               info.totalswap/=info.mem_unit;
+               info.freeswap/=info.mem_unit;
+#endif
+               info.sharedram/=info.mem_unit;
+               info.bufferram/=info.mem_unit;
+       } else {
+               info.mem_unit/=1024;
+               /* TODO:  Make all this stuff not overflow when mem >= 4 Gib */
+               info.totalram*=info.mem_unit;
+               info.freeram*=info.mem_unit;
+#ifndef __uClinux__
+               info.totalswap*=info.mem_unit;
+               info.freeswap*=info.mem_unit;
+#endif
+               info.sharedram*=info.mem_unit;
+               info.bufferram*=info.mem_unit;
+       }
+
+       if (argc > 1 && **(argv + 1) == '-')
+               bb_show_usage();
+
+       printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free",
+                       "shared", "buffers");
+
+       printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram,
+                       info.totalram-info.freeram, info.freeram,
+                       info.sharedram, info.bufferram);
+
+#ifndef __uClinux__
+       printf("%6s%13ld%13ld%13ld\n", "Swap:", info.totalswap,
+                       info.totalswap-info.freeswap, info.freeswap);
+
+       printf("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap,
+                       (info.totalram-info.freeram)+(info.totalswap-info.freeswap),
+                       info.freeram+info.freeswap);
+#endif
+       return EXIT_SUCCESS;
+}
+
diff --git a/busybox/procps/kill.c b/busybox/procps/kill.c
new file mode 100644 (file)
index 0000000..25a8d01
--- /dev/null
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini kill/killall implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include "busybox.h"
+
+#define KILL 0
+#define KILLALL 1
+
+extern int kill_main(int argc, char **argv)
+{
+       int whichApp, signo = SIGTERM;
+       const char *name;
+       int errors = 0;
+
+#ifdef CONFIG_KILLALL
+       int quiet=0;
+       /* Figure out what we are trying to do here */
+       whichApp = (strcmp(bb_applet_name, "killall") == 0)? KILLALL : KILL;
+#else
+       whichApp = KILL;
+#endif
+
+       /* Parse any options */
+       if (argc < 2)
+               bb_show_usage();
+
+       if(argv[1][0] != '-'){
+               argv++;
+               argc--;
+               goto do_it_now;
+       }
+
+       /* The -l option, which prints out signal names. */
+       if(argv[1][1]=='l' && argv[1][2]=='\0'){
+               if(argc==2) {
+                       /* Print the whole signal list */
+                       int col = 0;
+                       for(signo=1; signo < NSIG; signo++) {
+                               name = u_signal_names(0, &signo, 1);
+                               if(name==NULL)  /* unnamed */
+                                       continue;
+                               col += printf("%2d) %-16s", signo, name);
+                               if (col > 60) {
+                                       printf("\n");
+                                       col = 0;
+                               }
+                       }
+                       printf("\n");
+
+               } else {
+                       for(argv++; *argv; argv++) {
+                               name = u_signal_names(*argv, &signo, -1);
+                               if(name!=NULL)
+                                       printf("%s\n", name);
+                       }
+               }
+               /* If they specified -l, were all done */
+               return EXIT_SUCCESS;
+       }
+
+#ifdef CONFIG_KILLALL  
+       /* The -q quiet option */
+       if(argv[1][1]=='q' && argv[1][2]=='\0'){
+               quiet++;
+               argv++;
+               argc--;
+               if(argc<2 || argv[1][0] != '-'){
+                       goto do_it_now;
+               }
+       }
+#endif
+
+       if(!u_signal_names(argv[1]+1, &signo, 0))
+               bb_error_msg_and_die( "bad signal name '%s'", argv[1]+1);
+       argv+=2;
+       argc-=2;
+
+do_it_now:
+
+       if (whichApp == KILL) {
+               /* Looks like they want to do a kill. Do that */
+               while (--argc >= 0) {
+                       int pid;
+
+                       if (!isdigit(**argv))
+                               bb_error_msg_and_die( "Bad PID '%s'", *argv);
+                       pid = strtol(*argv, NULL, 0);
+                       if (kill(pid, signo) != 0) {
+                               bb_perror_msg( "Could not kill pid '%d'", pid);
+                               errors++;
+                       }
+                       argv++;
+               }
+
+       }
+#ifdef CONFIG_KILLALL
+       else {
+               pid_t myPid=getpid();
+               /* Looks like they want to do a killall.  Do that */
+               while (--argc >= 0) {
+                       long* pidList;
+
+                       pidList = find_pid_by_name(*argv);
+                       if (!pidList || *pidList<=0) {
+                               errors++;
+                               if (quiet==0)
+                                       bb_error_msg( "%s: no process killed", *argv);
+                       } else {
+                               long *pl;
+
+                               for(pl = pidList; *pl !=0 ; pl++) {
+                                       if (*pl==myPid)
+                                               continue;
+                                       if (kill(*pl, signo) != 0) {
+                                               errors++;
+                                               if (quiet==0)
+                                                       bb_perror_msg( "Could not kill pid '%ld'", *pl);
+                                       }
+                               }
+                       }
+                       free(pidList);
+                       argv++;
+               }
+       }
+#endif
+       return errors;
+}
diff --git a/busybox/procps/pidof.c b/busybox/procps/pidof.c
new file mode 100644 (file)
index 0000000..413864a
--- /dev/null
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pidof implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include "busybox.h"
+
+
+extern int pidof_main(int argc, char **argv)
+{
+       int opt, n = 0;
+       int single_flag = 0;
+       int fail = 1;
+
+       /* do normal option parsing */
+       while ((opt = getopt(argc, argv, "s")) > 0) {
+               switch (opt) {
+                       case 's':
+                               single_flag = 1;
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       /* Looks like everything is set to go. */
+       while(optind < argc) {
+               long *pidList;
+               long *pl;
+
+               pidList = find_pid_by_name(argv[optind]);
+               for(pl = pidList; *pl > 0; pl++) {
+                       printf("%s%ld", (n++ ? " " : ""), *pl);
+                       fail = 0;
+                       if (single_flag)
+                               break;
+               }
+               free(pidList);
+               optind++;
+
+       }
+       printf("\n");
+
+       return fail ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/busybox/procps/ps.c b/busybox/procps/ps.c
new file mode 100644 (file)
index 0000000..0b60331
--- /dev/null
@@ -0,0 +1,109 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ps implementation(s) for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+#ifdef CONFIG_SELINUX
+#include <fs_secure.h>
+#include <ss.h>
+#include <flask_util.h>          /* for is_flask_enabled() */
+#endif
+
+static const int TERMINAL_WIDTH = 79;      /* not 80 in case terminal has linefold bug */
+
+
+
+extern int ps_main(int argc, char **argv)
+{
+       procps_status_t * p;
+       int i, len;
+       int terminal_width = TERMINAL_WIDTH;
+
+#ifdef CONFIG_SELINUX
+       int use_selinux = 0;
+       security_id_t sid;
+       if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") )
+               use_selinux = 1;
+#endif
+
+       get_terminal_width_height(0, &terminal_width, NULL);
+       /* Go one less... */
+       terminal_width--;
+
+#ifdef CONFIG_SELINUX
+       if(use_selinux)
+               printf("  PID Context                          Stat Command\n");
+       else
+#endif
+       printf("  PID  Uid     VmSize Stat Command\n");
+#ifdef CONFIG_SELINUX
+       while ((p = procps_scan(1, use_selinux, &sid)) != 0) {
+#else
+       while ((p = procps_scan(1)) != 0) {
+#endif
+               char *namecmd = p->cmd;
+
+#ifdef CONFIG_SELINUX
+               if(use_selinux)
+               {
+                       char sbuf[128];
+                       len = sizeof(sbuf);
+                       if(security_sid_to_context(sid, (security_context_t)&sbuf, &len))
+                               strcpy(sbuf, "unknown");
+
+                       len = printf("%5d %-32s %s ", p->pid, sbuf, p->state);
+               }
+               else
+#endif
+               if(p->rss == 0)
+                       len = printf("%5d %-8s        %s ", p->pid, p->user, p->state);
+               else
+                       len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state);
+               i = terminal_width-len;
+
+               if(namecmd != 0 && namecmd[0] != 0) {
+                       if(i < 0)
+               i = 0;
+                       if(strlen(namecmd) > i)
+                               namecmd[i] = 0;
+                       printf("%s\n", namecmd);
+               } else {
+                       namecmd = p->short_cmd;
+                       if(i < 2)
+                               i = 2;
+                       if(strlen(namecmd) > (i-2))
+                               namecmd[i-2] = 0;
+                       printf("[%s]\n", namecmd);
+               }
+               free(p->cmd);
+       }
+       return EXIT_SUCCESS;
+}
+
diff --git a/busybox/procps/renice.c b/busybox/procps/renice.c
new file mode 100644 (file)
index 0000000..a6f0820
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Mini renice implementation for busybox
+ *
+ *
+ * Copyright (C) 2000 Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "busybox.h"
+
+
+extern int renice_main(int argc, char **argv)
+{
+       int prio, status = EXIT_SUCCESS;
+
+       if (argc < 3)   bb_show_usage();
+
+       prio = atoi(*++argv);
+       if (prio > 20)          prio = 20;
+       if (prio < -20)         prio = -20;
+
+       while (*++argv) {
+               int ps = atoi(*argv);
+               int oldp = getpriority(PRIO_PROCESS, ps);
+
+               if (setpriority(PRIO_PROCESS, ps, prio) == 0) {
+                       printf("%d: old priority %d, new priority %d\n", ps, oldp, prio );
+               } else {
+                       bb_perror_msg("%d: setpriority", ps);
+                       status = EXIT_FAILURE;
+               }
+       }
+
+       return status;
+}
diff --git a/busybox/procps/sysctl.c b/busybox/procps/sysctl.c
new file mode 100644 (file)
index 0000000..359dcc0
--- /dev/null
@@ -0,0 +1,352 @@
+
+/*
+ * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
+ *
+ *
+ * "Copyright 1999 George Staikos
+ * This file may be used subject to the terms and conditions of the
+ * GNU General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details."
+ *
+ * Changelog:
+ *     v1.01:
+ *             - added -p <preload> to preload values from a file
+ *     v1.01.1
+ *             - busybox applet aware by <solar@gentoo.org>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "busybox.h"
+
+/*
+ *    Function Prototypes
+ */
+static int sysctl_read_setting(const char *setting, int output);
+static int sysctl_write_setting(const char *setting, int output);
+static int sysctl_preload_file(const char *filename, int output);
+static int sysctl_display_all(const char *path, int output, int show_table);
+
+/*
+ *    Globals...
+ */
+static const char PROC_PATH[] = "/proc/sys/";
+static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
+
+/* error messages */
+static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n";
+static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n";
+static const char ERR_NO_EQUALS[] =
+       "error: '%s' must be of the form name=value\n";
+static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n";
+static const char ERR_UNKNOWN_WRITING[] =
+       "error: unknown error %d setting key '%s'\n";
+static const char ERR_UNKNOWN_READING[] =
+       "error: unknown error %d reading key '%s'\n";
+static const char ERR_PERMISSION_DENIED[] =
+       "error: permission denied on key '%s'\n";
+static const char ERR_OPENING_DIR[] = "error: unable to open directory '%s'\n";
+static const char ERR_PRELOAD_FILE[] =
+       "error: unable to open preload file '%s'\n";
+static const char WARN_BAD_LINE[] =
+       "warning: %s(%d): invalid syntax, continuing...\n";
+
+
+static void dwrite_str(int fd, const char *buf)
+{
+       write(fd, buf, strlen(buf));
+}
+
+/*
+ *    sysctl_main()...
+ */
+int sysctl_main(int argc, char **argv)
+{
+       int retval = 0;
+       int output = 1;
+       int write_mode = 0;
+       int switches_allowed = 1;
+
+       if (argc < 2)
+               bb_show_usage();
+
+       argv++;
+
+       for (; argv && *argv && **argv; argv++) {
+               if (switches_allowed && **argv == '-') {        /* we have a switch */
+                       switch ((*argv)[1]) {
+                       case 'n':
+                               output = 0;
+                               break;
+                       case 'w':
+                               write_mode = 1;
+                               switches_allowed = 0;
+                               break;
+                       case 'p':
+                               argv++;
+                               return
+                                       sysctl_preload_file(((argv && *argv
+                                                                                 && **argv) ? *argv :
+                                                                                DEFAULT_PRELOAD), output);
+                       case 'a':
+                       case 'A':
+                               switches_allowed = 0;
+                               return sysctl_display_all(PROC_PATH, output,
+                                                                                 ((*argv)[1] == 'a') ? 0 : 1);
+                       case 'h':
+                       case '?':
+                               bb_show_usage();
+                       default:
+                               bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv);
+                               bb_show_usage();
+                       }
+               } else {
+                       switches_allowed = 0;
+                       if (write_mode)
+                               retval = sysctl_write_setting(*argv, output);
+                       else
+                               sysctl_read_setting(*argv, output);
+               }
+       }
+       return retval;
+}                                              /* end sysctl_main() */
+
+
+
+/*
+ *     sysctl_preload_file
+ *     preload the sysctl's from a conf file
+ *           - we parse the file and then reform it (strip out whitespace)
+ */
+#define PRELOAD_BUF 256
+
+int sysctl_preload_file(const char *filename, int output)
+{
+       int lineno = 0;
+       char oneline[PRELOAD_BUF];
+       char buffer[PRELOAD_BUF];
+       char *name, *value, *ptr;
+       FILE *fp = NULL;
+
+       if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
+               bb_error_msg_and_die(ERR_PRELOAD_FILE, filename);
+       }
+
+       while (fgets(oneline, sizeof(oneline) - 1, fp)) {
+               oneline[sizeof(oneline) - 1] = 0;
+               lineno++;
+               trim(oneline);
+               ptr = (char *) oneline;
+
+               if (*ptr == '#' || *ptr == ';')
+                       continue;
+
+               if (bb_strlen(ptr) < 2)
+                       continue;
+
+               name = strtok(ptr, "=");
+               if (!name || !*name) {
+                       bb_error_msg(WARN_BAD_LINE, filename, lineno);
+                       continue;
+               }
+
+               trim(name);
+
+               value = strtok(NULL, "\n\r");
+               if (!value || !*value) {
+                       bb_error_msg(WARN_BAD_LINE, filename, lineno);
+                       continue;
+               }
+
+               while ((*value == ' ' || *value == '\t') && *value != 0)
+                       value++;
+               strcpy(buffer, name);
+               strcat(buffer, "=");
+               strcat(buffer, value);
+               sysctl_write_setting(buffer, output);
+       }
+       fclose(fp);
+       return 0;
+}                                              /* end sysctl_preload_file() */
+
+
+/*
+ *     Write a single sysctl setting
+ */
+int sysctl_write_setting(const char *setting, int output)
+{
+       int retval = 0;
+       const char *name = setting;
+       const char *value;
+       const char *equals;
+       char *tmpname, *outname, *cptr;
+       int fd = -1;
+
+       if (!name)                      /* probably dont' want to display this  err */
+               return 0;
+
+       if (!(equals = strchr(setting, '='))) {
+               bb_error_msg(ERR_NO_EQUALS, setting);
+               return -1;
+       }
+
+       value = equals + sizeof(char);  /* point to the value in name=value */
+
+       if (!*name || !*value || name == equals) {
+               bb_error_msg(ERR_MALFORMED_SETTING, setting);
+               return -2;
+       }
+
+       bb_xasprintf(&tmpname, "%s%.*s", PROC_PATH, (equals - name), name);
+       outname = bb_xstrdup(tmpname + strlen(PROC_PATH));
+
+       while ((cptr = strchr(tmpname, '.')) != NULL)
+               *cptr = '/';
+
+       while ((cptr = strchr(outname, '/')) != NULL)
+               *cptr = '.';
+
+       if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+               switch (errno) {
+               case ENOENT:
+                       bb_error_msg(ERR_INVALID_KEY, outname);
+                       break;
+               case EACCES:
+                       bb_perror_msg(ERR_PERMISSION_DENIED, outname);
+                       break;
+               default:
+                       bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
+                       break;
+               }
+               retval = -1;
+       } else {
+               dwrite_str(fd, value);
+               close(fd);
+               if (output) {
+                       dwrite_str(STDOUT_FILENO, outname);
+                       dwrite_str(STDOUT_FILENO, " = ");
+               }
+               dwrite_str(STDOUT_FILENO, value);
+               dwrite_str(STDOUT_FILENO, "\n");
+       }
+
+       /* cleanup */
+       free(tmpname);
+       free(outname);
+       return retval;
+}                                              /* end sysctl_write_setting() */
+
+
+/*
+ *     Read a sysctl setting
+ *
+ */
+int sysctl_read_setting(const char *setting, int output)
+{
+       int retval = 0;
+       char *tmpname, *outname, *cptr;
+       char inbuf[1025];
+       const char *name = setting;
+       FILE *fp;
+
+       if (!setting || !*setting)
+               bb_error_msg(ERR_INVALID_KEY, setting);
+
+       tmpname = concat_path_file(PROC_PATH, name);
+       outname = bb_xstrdup(tmpname + strlen(PROC_PATH));
+
+       while ((cptr = strchr(tmpname, '.')) != NULL)
+               *cptr = '/';
+       while ((cptr = strchr(outname, '/')) != NULL)
+               *cptr = '.';
+
+       if ((fp = fopen(tmpname, "r")) == NULL) {
+               switch (errno) {
+               case ENOENT:
+                       bb_error_msg(ERR_INVALID_KEY, outname);
+                       break;
+               case EACCES:
+                       bb_error_msg(ERR_PERMISSION_DENIED, outname);
+                       break;
+               default:
+                       bb_error_msg(ERR_UNKNOWN_READING, errno, outname);
+                       break;
+               }
+               retval = -1;
+       } else {
+               while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
+                       if (output) {
+                               dwrite_str(STDOUT_FILENO, outname);
+                               dwrite_str(STDOUT_FILENO, " = ");
+                       }
+                       dwrite_str(STDOUT_FILENO, inbuf);
+               }
+               fclose(fp);
+       }
+
+       free(tmpname);
+       free(outname);
+       return retval;
+}                                              /* end sysctl_read_setting() */
+
+
+
+/*
+ *     Display all the sysctl settings
+ *
+ */
+int sysctl_display_all(const char *path, int output, int show_table)
+{
+       int retval = 0;
+       int retval2;
+       DIR *dp;
+       struct dirent *de;
+       char *tmpdir;
+       struct stat ts;
+
+       if (!(dp = opendir(path))) {
+               bb_perror_msg(ERR_OPENING_DIR, path);
+               retval = -1;
+       } else {
+               while ((de = readdir(dp)) != NULL) {
+                       tmpdir = concat_subpath_file(path, de->d_name);
+                       if(tmpdir == NULL)
+                               continue;
+                       if ((retval2 = stat(tmpdir, &ts)) != 0)
+                               bb_perror_msg(tmpdir);
+                       else {
+                               if (S_ISDIR(ts.st_mode)) {
+                                       sysctl_display_all(tmpdir, output, show_table);
+                               } else
+                                       retval |=
+                                               sysctl_read_setting(tmpdir + bb_strlen(PROC_PATH),
+                                                                                       output);
+
+                       }
+                       free(tmpdir);
+               }                               /* end while */
+               closedir(dp);
+       }
+
+       return retval;
+}                                              /* end sysctl_display_all() */
+
+#ifdef STANDALONE_SYSCTL
+int main(int argc, char **argv)
+{
+       return sysctl_main(argc, argv);
+}
+const char *bb_applet_name = "sysctl";
+#endif
diff --git a/busybox/procps/top.c b/busybox/procps/top.c
new file mode 100644 (file)
index 0000000..5963c35
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * A tiny 'top' utility.
+ *
+ * This is written specifically for the linux /proc/<PID>/stat(m)
+ * files format.
+
+ * This reads the PIDs of all processes and their status and shows
+ * the status of processes (first ones that fit to screen) at given
+ * intervals.
+ *
+ * NOTES:
+ * - At startup this changes to /proc, all the reads are then
+ *   relative to that.
+ *
+ * (C) Eero Tamminen <oak at welho dot com>
+ *
+ * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
+ */
+
+/* Original code Copyrights */
+/*
+ * Copyright (c) 1992 Branko Lankester
+ * Copyright (c) 1992 Roger Binns
+ * Copyright (C) 1994-1996 Charles L. Blake.
+ * Copyright (C) 1992-1998 Michael K. Johnson
+ * May be distributed under the conditions of the
+ * GNU Library General Public License
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+/* get page info */
+#include <asm/page.h>
+#include "busybox.h"
+
+//#define FEATURE_CPU_USAGE_PERCENTAGE  /* + 2k */
+
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+#include <time.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <netinet/in.h>  /* htons */
+#endif
+
+
+typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q);
+
+static procps_status_t *top;   /* Hehe */
+static int ntop;
+
+
+static int pid_sort (procps_status_t *P, procps_status_t *Q)
+{
+    return (Q->pid - P->pid);
+}
+
+static int mem_sort (procps_status_t *P, procps_status_t *Q)
+{
+    return (int)(Q->rss - P->rss);
+}
+
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+
+#define sort_depth 3
+static cmp_t sort_function[sort_depth];
+
+static int pcpu_sort (procps_status_t *P, procps_status_t *Q)
+{
+    return (Q->pcpu - P->pcpu);
+}
+
+static int time_sort (procps_status_t *P, procps_status_t *Q)
+{
+    return (int)((Q->stime + Q->utime) - (P->stime + P->utime));
+}
+
+int mult_lvl_cmp(void* a, void* b) {
+    int i, cmp_val;
+
+    for(i = 0; i < sort_depth; i++) {
+       cmp_val = (*sort_function[i])(a, b);
+       if (cmp_val != 0)
+           return cmp_val;
+    }
+    return 0;
+}
+
+/* This structure stores some critical information from one frame to
+   the next. mostly used for sorting. Added cumulative and resident fields. */
+struct save_hist {
+    int ticks;
+    int pid;
+    int utime;
+    int stime;
+};
+
+/*
+ * Calculates percent cpu usage for each task.
+ */
+
+static struct save_hist *save_history;
+
+static unsigned long Hertz;
+
+/***********************************************************************
+ * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
+ * is the kernel clock tick rate. One of these units is called a jiffy.
+ * The HZ value used in the kernel may vary according to hacker desire.
+ * According to Linus Torvalds, this is not true. He considers the values
+ * in /proc as being in architecture-dependent units that have no relation
+ * to the kernel clock tick rate. Examination of the kernel source code
+ * reveals that opinion as wishful thinking.
+ *
+ * In any case, we need the HZ constant as used in /proc. (the real HZ value
+ * may differ, but we don't care) There are several ways we could get HZ:
+ *
+ * 1. Include the kernel header file. If it changes, recompile this library.
+ * 2. Use the sysconf() function. When HZ changes, recompile the C library!
+ * 3. Ask the kernel. This is obviously correct...
+ *
+ * Linus Torvalds won't let us ask the kernel, because he thinks we should
+ * not know the HZ value. Oh well, we don't have to listen to him.
+ * Someone smuggled out the HZ value. :-)
+ *
+ * This code should work fine, even if Linus fixes the kernel to match his
+ * stated behavior. The code only fails in case of a partial conversion.
+ *
+ */
+
+#define FILE_TO_BUF(filename, fd) do{                           \
+    if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) {    \
+       bb_perror_msg_and_die("/proc not be mounted?");            \
+    }                                                           \
+    lseek(fd, 0L, SEEK_SET);                                    \
+    if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) {        \
+       bb_perror_msg_and_die("%s", filename);                     \
+    }                                                           \
+    buf[local_n] = '\0';                                        \
+}while(0)
+
+#define FILE_TO_BUF2(filename, fd) do{                          \
+    lseek(fd, 0L, SEEK_SET);                                    \
+    if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) {        \
+       bb_perror_msg_and_die("%s", filename);                     \
+    }                                                           \
+    buf[local_n] = '\0';                                        \
+}while(0)
+
+static void init_Hertz_value(void) {
+  unsigned long user_j, nice_j, sys_j, other_j;  /* jiffies (clock ticks) */
+  double up_1, up_2, seconds;
+  unsigned long jiffies, h;
+  char buf[80];
+  int uptime_fd = -1;
+  int stat_fd = -1;
+
+  long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
+
+  if(smp_num_cpus<1) smp_num_cpus=1;
+  do {
+    int local_n;
+
+    FILE_TO_BUF("uptime", uptime_fd);
+    up_1 = strtod(buf, 0);
+    FILE_TO_BUF("stat", stat_fd);
+    sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
+    FILE_TO_BUF2("uptime", uptime_fd);
+    up_2 = strtod(buf, 0);
+  } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
+
+  close(uptime_fd);
+  close(stat_fd);
+
+  jiffies = user_j + nice_j + sys_j + other_j;
+  seconds = (up_1 + up_2) / 2;
+  h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
+  /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
+  switch(h){
+  case   30 ...   34 :  Hertz =   32; break; /* ia64 emulator */
+  case   48 ...   52 :  Hertz =   50; break;
+  case   58 ...   62 :  Hertz =   60; break;
+  case   63 ...   65 :  Hertz =   64; break; /* StrongARM /Shark */
+  case   95 ...  105 :  Hertz =  100; break; /* normal Linux */
+  case  124 ...  132 :  Hertz =  128; break; /* MIPS, ARM */
+  case  195 ...  204 :  Hertz =  200; break; /* normal << 1 */
+  case  253 ...  260 :  Hertz =  256; break;
+  case  295 ...  304 :  Hertz =  300; break; /* 3 cpus */
+  case  393 ...  408 :  Hertz =  400; break; /* normal << 2 */
+  case  495 ...  504 :  Hertz =  500; break; /* 5 cpus */
+  case  595 ...  604 :  Hertz =  600; break; /* 6 cpus */
+  case  695 ...  704 :  Hertz =  700; break; /* 7 cpus */
+  case  790 ...  808 :  Hertz =  800; break; /* normal << 3 */
+  case  895 ...  904 :  Hertz =  900; break; /* 9 cpus */
+  case  990 ... 1010 :  Hertz = 1000; break; /* ARM */
+  case 1015 ... 1035 :  Hertz = 1024; break; /* Alpha, ia64 */
+  case 1095 ... 1104 :  Hertz = 1100; break; /* 11 cpus */
+  case 1180 ... 1220 :  Hertz = 1200; break; /* Alpha */
+  default:
+    /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
+    Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
+  }
+}
+
+static void do_stats(void)
+{
+    struct timeval t;
+    static struct timeval oldtime;
+    struct timezone timez;
+    float elapsed_time;
+
+    procps_status_t *cur;
+    int total_time, i, n;
+    static int prev_count;
+    int systime, usrtime, pid;
+
+    struct save_hist *New_save_hist;
+
+    /*
+     * Finds the current time (in microseconds) and calculates the time
+     * elapsed since the last update.
+     */
+    gettimeofday(&t, &timez);
+    elapsed_time = (t.tv_sec - oldtime.tv_sec)
+       + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
+    oldtime.tv_sec  = t.tv_sec;
+    oldtime.tv_usec = t.tv_usec;
+
+    New_save_hist  = alloca(sizeof(struct save_hist)*ntop);
+    /*
+     * Make a pass through the data to get stats.
+     */
+    for(n = 0; n < ntop; n++) {
+       cur = top + n;
+
+       /*
+        * Calculate time in cur process.  Time is sum of user time
+        * (usrtime) plus system time (systime).
+        */
+       systime = cur->stime;
+       usrtime = cur->utime;
+       pid = cur->pid;
+       total_time = systime + usrtime;
+       New_save_hist[n].ticks = total_time;
+       New_save_hist[n].pid = pid;
+       New_save_hist[n].stime = systime;
+       New_save_hist[n].utime = usrtime;
+
+       /* find matching entry from previous pass */
+       for (i = 0; i < prev_count; i++) {
+           if (save_history[i].pid == pid) {
+               total_time -= save_history[i].ticks;
+               systime -= save_history[i].stime;
+               usrtime -= save_history[i].utime;
+               break;
+           }
+       }
+
+       /*
+        * Calculate percent cpu time for cur task.
+        */
+       i = (total_time * 10 * 100/Hertz) / elapsed_time;
+       if (i > 999)
+           i = 999;
+       cur->pcpu = i;
+
+    }
+
+    /*
+     * Save cur frame's information.
+     */
+    free(save_history);
+    save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist,
+                                               sizeof(struct save_hist)*n);
+    prev_count = n;
+    qsort(top, n, sizeof(procps_status_t), (void*)mult_lvl_cmp);
+}
+#else
+static cmp_t sort_function;
+#endif /* FEATURE_CPU_USAGE_PERCENTAGE */
+
+/* display generic info (meminfo / loadavg) */
+static unsigned long display_generic(void)
+{
+       FILE *fp;
+       char buf[80];
+       float avg1, avg2, avg3;
+       unsigned long total, used, mfree, shared, buffers, cached;
+       unsigned int needs_conversion = 1;
+
+       /* read memory info */
+       fp = bb_xfopen("meminfo", "r");
+
+       /*
+        * Old kernels (such as 2.4.x) had a nice summary of memory info that
+        * we could parse, however this is gone entirely in 2.6. Try parsing
+        * the old way first, and if that fails, parse each field manually.
+        *
+        * First, we read in the first line. Old kernels will have bogus
+        * strings we don't care about, whereas new kernels will start right
+        * out with MemTotal:
+        *                              -- PFM.
+        */
+       if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
+               fgets(buf, sizeof(buf), fp);    /* skip first line */
+
+               fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
+                  &total, &used, &mfree, &shared, &buffers, &cached);
+       } else {
+               /*
+                * Revert to manual parsing, which incidentally already has the
+                * sizes in kilobytes. This should be safe for both 2.4 and
+                * 2.6.
+                */
+               needs_conversion = 0;
+
+               fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
+
+               /*
+                * MemShared: is no longer present in 2.6. Report this as 0,
+                * to maintain consistent behavior with normal procps.
+                */
+               if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
+                       shared = 0;
+
+               fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
+               fscanf(fp, "Cached: %lu %s\n", &cached, buf);
+
+               used = total - mfree;
+       }
+       fclose(fp);
+
+       /* read load average */
+       fp = bb_xfopen("loadavg", "r");
+       if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) {
+               bb_error_msg_and_die("failed to read '%s'", "loadavg");
+       }
+       fclose(fp);
+
+       if (needs_conversion) {
+               /* convert to kilobytes */
+               used /= 1024;
+               mfree /= 1024;
+               shared /= 1024;
+               buffers /= 1024;
+               cached /= 1024;
+               total /= 1024;
+       }
+
+       /* output memory info and load average */
+       /* clear screen & go to top */
+       printf("\e[H\e[J" "Mem: "
+              "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n",
+              used, mfree, shared, buffers, cached);
+       printf("Load average: %.2f, %.2f, %.2f    "
+                       "(State: S=sleeping R=running, W=waiting)\n",
+              avg1, avg2, avg3);
+       return total;
+}
+
+
+/* display process statuses */
+static void display_status(int count, int col)
+{
+       procps_status_t *s = top;
+       char rss_str_buf[8];
+       unsigned long total_memory = display_generic();
+
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       /* what info of the processes is shown */
+       printf("\n\e[7m  PID USER     STATUS   RSS  PPID %%CPU %%MEM COMMAND\e[0m\n");
+#else
+       printf("\n\e[7m  PID USER     STATUS   RSS  PPID %%MEM COMMAND\e[0m\n");
+#endif
+
+       while (count--) {
+               char *namecmd = s->short_cmd;
+               int pmem;
+
+               pmem = 1000.0 * s->rss / total_memory;
+               if (pmem > 999) pmem = 999;
+
+               if(s->rss > 10*1024)
+                       sprintf(rss_str_buf, "%6ldM", s->rss/1024);
+               else
+                       sprintf(rss_str_buf, "%7ld", s->rss);
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+               printf("%5d %-8s %s  %s %5d %2d.%d %2u.%u ",
+                       s->pid, s->user, s->state, rss_str_buf, s->ppid,
+                       s->pcpu/10, s->pcpu%10, pmem/10, pmem%10);
+#else
+               printf("%5d %-8s %s  %s %5d %2u.%u ",
+                       s->pid, s->user, s->state, rss_str_buf, s->ppid,
+                       pmem/10, pmem%10);
+#endif
+                       if(strlen(namecmd) > col)
+                               namecmd[col] = 0;
+                       printf("%s\n", namecmd);
+               s++;
+       }
+}
+
+static void clearmems(void)
+{
+       free(top);
+       top = 0;
+       ntop = 0;
+}
+
+#if defined CONFIG_FEATURE_USE_TERMIOS
+#include <termios.h>
+#include <sys/time.h>
+#include <signal.h>
+
+
+static struct termios initial_settings;
+
+static void reset_term(void)
+{
+       tcsetattr(0, TCSANOW, (void *) &initial_settings);
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       clearmems();
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       free(save_history);
+#endif
+#endif /* CONFIG_FEATURE_CLEAN_UP */
+}
+
+static void sig_catcher (int sig)
+{
+       reset_term();
+}
+#endif /* CONFIG_FEATURE_USE_TERMIOS */
+
+
+int top_main(int argc, char **argv)
+{
+       int opt, interval, lines, col;
+#if defined CONFIG_FEATURE_USE_TERMIOS
+       struct termios new_settings;
+       struct timeval tv;
+       fd_set readfds;
+       unsigned char c;
+       struct sigaction sa;
+#endif /* CONFIG_FEATURE_USE_TERMIOS */
+
+       /* Default update rate is 5 seconds */
+       interval = 5;
+
+       /* do normal option parsing */
+       while ((opt = getopt(argc, argv, "d:")) > 0) {
+           switch (opt) {
+               case 'd':
+                   interval = atoi(optarg);
+                   break;
+               default:
+                   bb_show_usage();
+           }
+       }
+
+       /* Default to 25 lines - 5 lines for status */
+       lines = 25 - 5;
+       /* Default CMD format size */
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       col = 35 - 6;
+#else
+       col = 35;
+#endif
+       /* change to /proc */
+       if (chdir("/proc") < 0) {
+               bb_perror_msg_and_die("chdir('/proc')");
+       }
+#if defined CONFIG_FEATURE_USE_TERMIOS
+       tcgetattr(0, (void *) &initial_settings);
+       memcpy(&new_settings, &initial_settings, sizeof(struct termios));
+       new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */
+       /* Turn off echoing */
+       new_settings.c_lflag &= ~(ECHO | ECHONL);
+
+       signal (SIGTERM, sig_catcher);
+       sigaction (SIGTERM, (struct sigaction *) 0, &sa);
+       sa.sa_flags |= SA_RESTART;
+       sa.sa_flags &= ~SA_INTERRUPT;
+       sigaction (SIGTERM, &sa, (struct sigaction *) 0);
+       sigaction (SIGINT, &sa, (struct sigaction *) 0);
+       tcsetattr(0, TCSANOW, (void *) &new_settings);
+       atexit(reset_term);
+
+       get_terminal_width_height(0, &col, &lines);
+       if (lines > 4) {
+           lines -= 5;
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+           col = col - 80 + 35 - 6;
+#else
+           col = col - 80 + 35;
+#endif
+       }
+#endif /* CONFIG_FEATURE_USE_TERMIOS */
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+       sort_function[0] = pcpu_sort;
+       sort_function[1] = mem_sort;
+       sort_function[2] = time_sort;
+#else
+       sort_function = mem_sort;
+#endif
+       while (1) {
+               /* read process IDs & status for all the processes */
+               procps_status_t * p;
+
+#ifdef CONFIG_SELINUX
+               while ((p = procps_scan(0, 0, NULL) ) != 0) {
+#else
+               while ((p = procps_scan(0)) != 0) {
+#endif
+                       int n = ntop;
+
+                       top = xrealloc(top, (++ntop)*sizeof(procps_status_t));
+                       memcpy(top + n, p, sizeof(procps_status_t));
+               }
+               if (ntop == 0) {
+               bb_perror_msg_and_die("scandir('/proc')");
+       }
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+               if(!Hertz) {
+                       init_Hertz_value();
+                       do_stats();
+                       sleep(1);
+                       clearmems();
+                       continue;
+       }
+               do_stats();
+#else
+               qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function);
+#endif
+               opt = lines;
+               if (opt > ntop) {
+                       opt = ntop;
+               }
+               /* show status for each of the processes */
+               display_status(opt, col);
+#if defined CONFIG_FEATURE_USE_TERMIOS
+               tv.tv_sec = interval;
+               tv.tv_usec = 0;
+               FD_ZERO (&readfds);
+               FD_SET (0, &readfds);
+               select (1, &readfds, NULL, NULL, &tv);
+               if (FD_ISSET (0, &readfds)) {
+                       if (read (0, &c, 1) <= 0) {   /* signal */
+               return EXIT_FAILURE;
+       }
+                       if(c == 'q' || c == initial_settings.c_cc[VINTR])
+                               return EXIT_SUCCESS;
+                       if(c == 'M') {
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+                               sort_function[0] = mem_sort;
+                               sort_function[1] = pcpu_sort;
+                               sort_function[2] = time_sort;
+#else
+                               sort_function = mem_sort;
+#endif
+                       }
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+                       if(c == 'P') {
+                               sort_function[0] = pcpu_sort;
+                               sort_function[1] = mem_sort;
+                               sort_function[2] = time_sort;
+                       }
+                       if(c == 'T') {
+                               sort_function[0] = time_sort;
+                               sort_function[1] = mem_sort;
+                               sort_function[2] = pcpu_sort;
+                       }
+#endif
+                       if(c == 'N') {
+#ifdef FEATURE_CPU_USAGE_PERCENTAGE
+                               sort_function[0] = pid_sort;
+#else
+                               sort_function = pid_sort;
+#endif
+                       }
+               }
+#else
+               sleep(interval);
+#endif                                  /* CONFIG_FEATURE_USE_TERMIOS */
+               clearmems();
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/procps/uptime.c b/busybox/procps/uptime.c
new file mode 100644 (file)
index 0000000..38d58af
--- /dev/null
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini uptime implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* This version of uptime doesn't display the number of users on the system,
+ * since busybox init doesn't mess with utmp.  For folks using utmp that are
+ * just dying to have # of users reported, feel free to write it as some type
+ * of CONFIG_FEATURE_UTMP_SUPPORT #define
+ */
+
+/* getopt not needed */
+
+
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+static const int FSHIFT = 16;              /* nr of bits of precision */
+#define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+
+
+extern int uptime_main(int argc, char **argv)
+{
+       int updays, uphours, upminutes;
+       struct sysinfo info;
+       struct tm *current_time;
+       time_t current_secs;
+
+       time(&current_secs);
+       current_time = localtime(&current_secs);
+
+       sysinfo(&info);
+
+       printf(" %02d:%02d:%02d up ",
+                       current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
+       updays = (int) info.uptime / (60*60*24);
+       if (updays)
+               printf("%d day%s, ", updays, (updays != 1) ? "s" : "");
+       upminutes = (int) info.uptime / 60;
+       uphours = (upminutes / 60) % 24;
+       upminutes %= 60;
+       if(uphours)
+               printf("%2d:%02d, ", uphours, upminutes);
+       else
+               printf("%d min, ", upminutes);
+
+       printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n",
+                       LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]),
+                       LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]),
+                       LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2]));
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/scripts/.cvsignore b/busybox/scripts/.cvsignore
new file mode 100644 (file)
index 0000000..07fa550
--- /dev/null
@@ -0,0 +1,2 @@
+mkdep
+split-include
diff --git a/busybox/scripts/config/.cvsignore b/busybox/scripts/config/.cvsignore
new file mode 100644 (file)
index 0000000..e8bf7a7
--- /dev/null
@@ -0,0 +1,8 @@
+conf
+mconf
+lkc_defs.h
+lex.zconf.c
+zconf.tab.h
+zconf.tab.c
+lex.backup
+zconf.output
diff --git a/busybox/scripts/config/Kconfig-language.txt b/busybox/scripts/config/Kconfig-language.txt
new file mode 100644 (file)
index 0000000..493749b
--- /dev/null
@@ -0,0 +1,255 @@
+Introduction
+------------
+
+The configuration database is collection of configuration options
+organized in a tree structure:
+
+       +- Code maturity level options
+       |  +- Prompt for development and/or incomplete code/drivers
+       +- General setup
+       |  +- Networking support
+       |  +- System V IPC
+       |  +- BSD Process Accounting
+       |  +- Sysctl support
+       +- Loadable module support
+       |  +- Enable loadable module support
+       |     +- Set version information on all module symbols
+       |     +- Kernel module loader
+       +- ...
+
+Every entry has its own dependencies. These dependencies are used
+to determine the visible of an entry. Any child entry is only
+visible if its parent entry is also visible.
+
+Menu entries
+------------
+
+Most entries define a config option, all other entries help to organize
+them. A single configuration option is defined like this:
+
+config MODVERSIONS
+       bool "Set version information on all module symbols"
+       depends MODULES
+       help
+         Usually, modules have to be recompiled whenever you switch to a new
+         kernel.  ...
+
+Every line starts with a key word and can be followed by multiple
+arguments.  "config" starts a new config entry. The following lines
+define attributes for this config option. Attributes can be the type of
+the config option, input prompt, dependencies, help text and default
+values. A config option can be defined multiple times with the same
+name, but every definition can have only a single input prompt and the
+type must not conflict.
+
+Menu attributes
+---------------
+
+A menu entry can have a number of attributes. Not all of them are
+applicable everywhere (see syntax).
+
+- type definition: "bool"/"tristate"/"string"/"hex"/"integer"
+  Every config option must have a type. There are only two basic types:
+  tristate and string, the other types base on these two. The type
+  definition optionally accepts an input prompt, so these two examples
+  are equivalent:
+
+       bool "Networking support"
+  and
+       bool
+       prompt "Networking support"
+
+- input prompt: "prompt" <prompt> ["if" <expr>]
+  Every menu entry can have at most one prompt, which is used to display
+  to the user. Optionally dependencies only for this prompt can be added
+  with "if".
+
+- default value: "default" <symbol> ["if" <expr>]
+  A config option can have any number of default values. If multiple
+  default values are visible, only the first defined one is active.
+  Default values are not limited to the menu entry, where they are
+  defined, this means the default can be defined somewhere else or be
+  overriden by an earlier definition.
+  The default value is only assigned to the config symbol if no other
+  value was set by the user (via the input prompt above). If an input
+  prompt is visible the default value is presented to the user and can
+  be overridden by him.
+  Optionally dependencies only for this default value can be added with
+  "if".
+
+- dependencies: "depends on"/"requires" <expr>
+  This defines a dependency for this menu entry. If multiple
+  dependencies are defined they are connected with '&&'. Dependencies
+  are applied to all other options within this menu entry (which also
+  accept "if" expression), so these two examples are equivalent:
+
+       bool "foo" if BAR
+       default y if BAR
+  and
+       depends on BAR
+       bool "foo"
+       default y
+
+- help text: "help"
+  This defines a help text. The end of the help text is determined by
+  the level indentation, this means it ends at the first line which has
+  a smaller indentation than the first line of the help text.
+
+
+Menu dependencies
+-----------------
+
+Dependencies define the visibility of a menu entry and can also reduce
+the input range of tristate symbols. The tristate logic used in the
+expressions uses one more state than normal boolean logic to express the
+module state. Dependency expressions have the following syntax:
+
+<expr> ::= <symbol>                             (1)
+           <symbol> '=' <symbol>                (2)
+           <symbol> '!=' <symbol>               (3)
+           '(' <expr> ')'                       (4)
+           '!' <expr>                           (5)
+           <expr> '||' <expr>                   (6)
+           <expr> '&&' <expr>                   (7)
+
+Expressions are listed in decreasing order of precedence.
+
+(1) Convert the symbol into an expression. Boolean and tristate symbols
+    are simply converted into the respective expression values. All
+    other symbol types result in 'n'.
+(2) If the values of both symbols are equal, it returns 'y',
+    otherwise 'n'.
+(3) If the values of both symbols are equal, it returns 'n',
+    otherwise 'y'.
+(4) Returns the value of the expression. Used to override precedence.
+(5) Returns the result of (2-/expr/).
+(6) Returns the result of min(/expr/, /expr/).
+(7) Returns the result of max(/expr/, /expr/).
+
+An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2
+respectively for calculations). A menu entry becomes visible when it's
+expression evaluates to 'm' or 'y'.
+
+There are two type of symbols: constant and nonconstant symbols.
+Nonconstant symbols are the most common ones and are defined with the
+'config' statement. Nonconstant symbols consist entirely of alphanumeric
+characters or underscores.
+Constant symbols are only part of expressions. Constant symbols are
+always surrounded by single or double quotes. Within the quote any
+other character is allowed and the quotes can be escaped using '\'.
+
+Menu structure
+--------------
+
+The position of a menu entry in the tree is determined in two ways. First
+it can be specified explicitely:
+
+menu "Network device support"
+       depends NET
+
+config NETDEVICES
+       ...
+
+endmenu
+
+All entries within the "menu" ... "endmenu" block become a submenu of
+"Network device support". All subentries inherit the dependencies from
+the menu entry, e.g. this means the dependency "NET" is added to the
+dependency list of the config option NETDEVICES.
+
+The other way to generate the menu structure is done by analyzing the
+dependencies. If a menu entry somehow depends on the previous entry, it
+can be made a submenu of it. First the the previous (parent) symbol must
+be part of the dependency list and then one of these two condititions
+must be true:
+- the child entry must become invisible, if the parent is set to 'n'
+- the child entry must only be visible, if the parent is visible
+
+config MODULES
+       bool "Enable loadable module support"
+
+config MODVERSIONS
+       bool "Set version information on all module symbols"
+       depends MODULES
+
+comment "module support disabled"
+       depends !MODULES
+
+MODVERSIONS directly depends on MODULES, this means it's only visible if
+MODULES is different from 'n'. The comment on the other hand is always
+visible when MODULES it's visible (the (empty) dependency of MODULES is
+also part of the comment dependencies).
+
+
+Kconfig syntax
+--------------
+
+The configuration file describes a series of menu entries, where every
+line starts with a keyword (except help texts). The following keywords
+end a menu entry:
+- config
+- choice/endchoice
+- comment
+- menu/endmenu
+- if/endif
+- source
+The first four also start the definition of a menu entry.
+
+config:
+
+       "config" <symbol>
+       <config options>
+
+This defines a config symbol <symbol> and accepts any of above
+attributes as options.
+
+choices:
+
+       "choice"
+       <choice options>
+       <choice block>
+       "endchoice"
+
+This defines a choice group and accepts any of above attributes as
+options. A choice can only be of type bool or tristate, while a boolean
+choice only allows a single config entry to be selected, a tristate
+choice also allows any number of config entries to be set to 'm'. This
+can be used if multiple drivers for a single hardware exists and only a
+single driver can be compiled/loaded into the kernel, but all drivers
+can be compiled as modules.
+A choice accepts another option "optional", which allows to set the
+choice to 'n' and no entry needs to be selected.
+
+comment:
+
+       "comment" <prompt>
+       <comment options>
+
+This defines a comment which is displayed to the user during the
+configuration process and is also echoed to the output files. The only
+possible options are dependencies.
+
+menu:
+
+       "menu" <prompt>
+       <menu options>
+       <menu block>
+       "endmenu"
+
+This defines a menu block, see "Menu structure" above for more
+information. The only possible options are dependencies.
+
+if:
+
+       "if" <expr>
+       <if block>
+       "endif"
+
+This defines an if block. The dependency expression <expr> is appended
+to all enclosed menu entries.
+
+source:
+
+       "source" <prompt>
+
+This reads the specified configuration file. This file is always parsed.
diff --git a/busybox/scripts/config/Makefile b/busybox/scripts/config/Makefile
new file mode 100644 (file)
index 0000000..c0b5b9d
--- /dev/null
@@ -0,0 +1,111 @@
+# Makefile for BusyBox
+#
+# Copyright (C) 2002 Erik Andersen <andersen@codepoet.org>
+
+top_srcdir=../..
+top_builddir=../..
+srcdir=$(top_srcdir)/scripts/config
+include $(top_builddir)/Rules.mak
+
+all: ncurses conf mconf
+
+LIBS = -lncurses
+ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h))
+       HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"
+else
+ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h))
+       HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"
+else
+ifeq (/usr/local/include/ncurses/ncurses.h, $(wildcard /usr/local/include/ncurses/ncurses.h))
+       HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="<ncurses.h>"
+else
+ifeq (/usr/local/include/ncurses/curses.h, $(wildcard /usr/local/include/ncurses/curses.h))
+       HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"
+else
+ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h))
+       HOSTNCURSES += -DCURSES_LOC="<ncurses.h>"
+else
+       HOSTNCURSES += -DCURSES_LOC="<curses.h>"
+endif
+endif
+endif
+endif
+endif
+
+CONF_SRC  =conf.c
+MCONF_SRC =mconf.c checklist.c menubox.c textbox.c yesno.c inputbox.c util.c msgbox.c
+SHARED_SRC=zconf.tab.c
+SHARED_DEPS:=$(srcdir)/lkc.h $(srcdir)/lkc_proto.h \
+  lkc_defs.h $(srcdir)/expr.h zconf.tab.h
+CONF_OBJS =$(patsubst %.c,%.o, $(CONF_SRC))
+MCONF_OBJS=$(patsubst %.c,%.o, $(MCONF_SRC))
+SHARED_OBJS=$(patsubst %.c,%.o, $(SHARED_SRC))
+
+conf: $(CONF_OBJS) $(SHARED_OBJS)
+       $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@
+
+mconf: $(MCONF_OBJS) $(SHARED_OBJS)
+       $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@ $(LIBS)
+
+$(CONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS)
+       $(HOSTCC) $(HOSTCFLAGS) -I. -c $< -o $@
+
+$(MCONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS)
+       $(HOSTCC) $(HOSTCFLAGS) $(HOSTNCURSES) -I. -c $< -o $@
+
+lkc_defs.h: $(srcdir)/lkc_proto.h
+       @sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/'
+
+###
+# The following requires flex/bison
+# By default we use the _shipped versions, uncomment the
+# following line if you are modifying the flex/bison src.
+#LKC_GENPARSER := 1
+
+ifdef LKC_GENPARSER
+
+%.tab.c %.tab.h: $(srcdir)/%.y
+       bison -t -d -v -b $* -p $(notdir $*) $<
+
+lex.%.c: $(srcdir)/%.l
+       flex -P$(notdir $*) -o$@ $<
+else
+
+lex.zconf.o: lex.zconf.c $(SHARED_DEPS)
+       $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -c $< -o $@
+
+lex.zconf.c: $(srcdir)/lex.zconf.c_shipped
+       cp $< $@
+
+zconf.tab.c: $(srcdir)/zconf.tab.c_shipped
+       cp $< $@
+
+zconf.tab.h: $(srcdir)/zconf.tab.h_shipped
+       cp $< $@
+endif
+
+zconf.tab.o: zconf.tab.c lex.zconf.c $(srcdir)/confdata.c $(srcdir)/expr.c \
+             $(srcdir)/symbol.c $(srcdir)/menu.c $(SHARED_DEPS)
+       $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -I. -c $< -o $@
+
+.PHONY: ncurses
+
+ncurses:
+       @echo "main() {}" > lxtemp.c
+       @if $(HOSTCC) lxtemp.c $(LIBS) ; then \
+               rm -f lxtemp.c a.out; \
+       else \
+               rm -f lxtemp.c; \
+               echo -e "\007" ;\
+               echo ">> Unable to find the Ncurses libraries." ;\
+               echo ">>" ;\
+               echo ">> You must have Ncurses installed in order" ;\
+               echo ">> to use 'make menuconfig'" ;\
+               echo ;\
+               exit 1 ;\
+       fi
+
+clean:
+       rm -f *.o *~ core $(TARGETS) $(MCONF_OBJS) $(CONF_OBJS) \
+               conf mconf zconf.tab.c zconf.tab.h lex.zconf.c lkc_defs.h
+
diff --git a/busybox/scripts/config/checklist.c b/busybox/scripts/config/checklist.c
new file mode 100644 (file)
index 0000000..4dbd166
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ *  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, checkflag;
+
+/*
+ * Print list item
+ */
+static void
+print_item (WINDOW * win, const char *item, int status,
+           int choice, int selected)
+{
+    int i;
+
+    /* Clear 'residue' of last item */
+    wattrset (win, menubox_attr);
+    wmove (win, choice, 0);
+    for (i = 0; i < list_width; i++)
+       waddch (win, ' ');
+
+    wmove (win, choice, check_x);
+    wattrset (win, selected ? check_selected_attr : check_attr);
+    if (checkflag == FLAG_CHECK)
+       wprintw (win, "[%c]", status ? 'X' : ' ');
+    else
+       wprintw (win, "(%c)", status ? 'X' : ' ');
+
+    wattrset (win, selected ? tag_selected_attr : tag_attr);
+    mvwaddch(win, choice, item_x, item[0]);
+    wattrset (win, selected ? item_selected_attr : item_attr);
+    waddstr (win, (char *)item+1);
+    if (selected) {
+       wmove (win, choice, check_x+1);
+       wrefresh (win);
+    }
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void
+print_arrows (WINDOW * win, int choice, int item_no, int scroll,
+               int y, int x, int height)
+{
+    wmove(win, y, x);
+
+    if (scroll > 0) {
+       wattrset (win, uarrow_attr);
+       waddch (win, ACS_UARROW);
+       waddstr (win, "(-)");
+    }
+    else {
+       wattrset (win, menubox_attr);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+    }
+
+   y = y + height + 1;
+   wmove(win, y, x);
+
+   if ((height < item_no) && (scroll + choice < item_no - 1)) {
+       wattrset (win, darrow_attr);
+       waddch (win, ACS_DARROW);
+       waddstr (win, "(+)");
+    }
+    else {
+       wattrset (win, menubox_border_attr);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+   }
+}
+
+/*
+ *  Display the termination buttons
+ */
+static void
+print_buttons( WINDOW *dialog, int height, int width, int selected)
+{
+    int x = width / 2 - 11;
+    int y = height - 2;
+
+    print_button (dialog, "Select", y, x, selected == 0);
+    print_button (dialog, " Help ", y, x + 14, selected == 1);
+
+    wmove(dialog, y, x+1 + 14*selected);
+    wrefresh (dialog);
+}
+
+/*
+ * Display a dialog box with a list of options that can be turned on or off
+ * The `flag' parameter is used to select between radiolist and checklist.
+ */
+int
+dialog_checklist (const char *title, const char *prompt, int height, int width,
+       int list_height, int item_no, struct dialog_list_item ** items,
+       int flag)
+
+{
+    int i, x, y, box_x, box_y;
+    int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
+    WINDOW *dialog, *list;
+
+    checkflag = flag;
+
+    /* Allocate space for storing item on/off status */
+    if ((status = malloc (sizeof (int) * item_no)) == NULL) {
+       endwin ();
+       fprintf (stderr,
+                "\nCan't allocate memory in dialog_checklist().\n");
+       exit (-1);
+    }
+
+    /* Initializes status */
+    for (i = 0; i < item_no; i++) {
+       status[i] = (items[i]->selected == 1); /* ON */
+       if ((!choice && status[i]) || items[i]->selected == 2) /* SELECTED */
+            choice = i + 1;
+    }
+    if (choice)
+           choice--;
+
+    max_choice = MIN (list_height, item_no);
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+       waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL && strlen(title) >= width-2 ) {
+       /* truncate long title -- mec */
+       char * title2 = malloc(width-2+1);
+       memcpy( title2, title, width-2 );
+       title2[width-2] = '\0';
+       title = title2;
+    }
+
+    if (title != NULL) {
+       wattrset (dialog, title_attr);
+       mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+       waddstr (dialog, (char *)title);
+       waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    list_width = width - 6;
+    box_y = height - list_height - 5;
+    box_x = (width - list_width) / 2 - 1;
+
+    /* create new window for the list */
+    list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1);
+
+    keypad (list, TRUE);
+
+    /* draw a box around the list items */
+    draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2,
+             menubox_border_attr, menubox_attr);
+
+    /* Find length of longest item in order to center checklist */
+    check_x = 0;
+    for (i = 0; i < item_no; i++)
+       check_x = MAX (check_x, + strlen (items[i]->name) + 4);
+
+    check_x = (list_width - check_x) / 2;
+    item_x = check_x + 4;
+
+    if (choice >= list_height) {
+       scroll = choice - list_height + 1;
+       choice -= scroll;
+    }
+
+    /* Print the list */
+    for (i = 0; i < max_choice; i++) {
+       print_item (list, items[scroll + i]->name,
+                   status[i+scroll], i, i == choice);
+    }
+
+    print_arrows(dialog, choice, item_no, scroll,
+                       box_y, box_x + check_x + 5, list_height);
+
+    print_buttons(dialog, height, width, 0);
+
+    wnoutrefresh (list);
+    wnoutrefresh (dialog);
+    doupdate ();
+
+    while (key != ESC) {
+       key = wgetch (dialog);
+
+       for (i = 0; i < max_choice; i++)
+            if (toupper(key) == toupper(items[scroll + i]->name[0]))
+                break;
+
+
+       if ( i < max_choice || key == KEY_UP || key == KEY_DOWN ||
+           key == '+' || key == '-' ) {
+           if (key == KEY_UP || key == '-') {
+               if (!choice) {
+                   if (!scroll)
+                       continue;
+                   /* Scroll list down */
+                   if (list_height > 1) {
+                       /* De-highlight current first item */
+                       print_item (list, items[scroll]->name,
+                                       status[scroll], 0, FALSE);
+                       scrollok (list, TRUE);
+                       wscrl (list, -1);
+                       scrollok (list, FALSE);
+                   }
+                   scroll--;
+                   print_item (list, items[scroll]->name,
+                               status[scroll], 0, TRUE);
+                   wnoutrefresh (list);
+
+                   print_arrows(dialog, choice, item_no, scroll,
+                               box_y, box_x + check_x + 5, list_height);
+
+                   wrefresh (dialog);
+
+                   continue;   /* wait for another key press */
+               } else
+                   i = choice - 1;
+           } else if (key == KEY_DOWN || key == '+') {
+               if (choice == max_choice - 1) {
+                   if (scroll + choice >= item_no - 1)
+                       continue;
+                   /* Scroll list up */
+                   if (list_height > 1) {
+                       /* De-highlight current last item before scrolling up */
+                       print_item (list, items[scroll + max_choice - 1]->name,
+                                   status[scroll + max_choice - 1],
+                                   max_choice - 1, FALSE);
+                       scrollok (list, TRUE);
+                       scroll (list);
+                       scrollok (list, FALSE);
+                   }
+                   scroll++;
+                   print_item (list, items[scroll + max_choice - 1]->name,
+                               status[scroll + max_choice - 1],
+                               max_choice - 1, TRUE);
+                   wnoutrefresh (list);
+
+                   print_arrows(dialog, choice, item_no, scroll,
+                               box_y, box_x + check_x + 5, list_height);
+
+                   wrefresh (dialog);
+
+                   continue;   /* wait for another key press */
+               } else
+                   i = choice + 1;
+           }
+           if (i != choice) {
+               /* De-highlight current item */
+               print_item (list, items[scroll + choice]->name,
+                           status[scroll + choice], choice, FALSE);
+               /* Highlight new item */
+               choice = i;
+               print_item (list, items[scroll + choice]->name,
+                           status[scroll + choice], choice, TRUE);
+               wnoutrefresh (list);
+               wrefresh (dialog);
+           }
+           continue;           /* wait for another key press */
+       }
+       switch (key) {
+       case 'H':
+       case 'h':
+       case '?':
+           for (i = 0; i < item_no; i++)
+               items[i]->selected = 0;
+           items[scroll + choice]->selected = 1;
+           delwin (dialog);
+           free (status);
+           return 1;
+       case TAB:
+       case KEY_LEFT:
+       case KEY_RIGHT:
+           button = ((key == KEY_LEFT ? --button : ++button) < 0)
+                       ? 1 : (button > 1 ? 0 : button);
+
+           print_buttons(dialog, height, width, button);
+           wrefresh (dialog);
+           break;
+       case 'S':
+       case 's':
+       case ' ':
+       case '\n':
+           if (!button) {
+               if (flag == FLAG_CHECK) {
+                   status[scroll + choice] = !status[scroll + choice];
+                   wmove (list, choice, check_x);
+                   wattrset (list, check_selected_attr);
+                   wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' ');
+               } else {
+                   if (!status[scroll + choice]) {
+                       for (i = 0; i < item_no; i++)
+                           status[i] = 0;
+                       status[scroll + choice] = 1;
+                       for (i = 0; i < max_choice; i++)
+                           print_item (list, items[scroll + i]->name,
+                                       status[scroll + i], i, i == choice);
+                   }
+               }
+               wnoutrefresh (list);
+               wrefresh (dialog);
+
+               for (i = 0; i < item_no; i++) {
+                       items[i]->selected = status[i];
+               }
+            } else {
+                   for (i = 0; i < item_no; i++)
+                           items[i]->selected = 0;
+                   items[scroll + choice]->selected = 1;
+           }
+           delwin (dialog);
+           free (status);
+           return button;
+       case 'X':
+       case 'x':
+           key = ESC;
+       case ESC:
+           break;
+       }
+
+       /* Now, update everything... */
+       doupdate ();
+    }
+
+
+    delwin (dialog);
+    free (status);
+    return -1;                 /* ESC pressed */
+}
diff --git a/busybox/scripts/config/colors.h b/busybox/scripts/config/colors.h
new file mode 100644 (file)
index 0000000..d34dd37
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ *  colors.h -- color attribute definitions
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ *   Default color definitions
+ *
+ *   *_FG = foreground
+ *   *_BG = background
+ *   *_HL = highlight?
+ */
+#define SCREEN_FG                    COLOR_CYAN
+#define SCREEN_BG                    COLOR_BLUE
+#define SCREEN_HL                    TRUE
+
+#define SHADOW_FG                    COLOR_BLACK
+#define SHADOW_BG                    COLOR_BLACK
+#define SHADOW_HL                    TRUE
+
+#define DIALOG_FG                    COLOR_BLACK
+#define DIALOG_BG                    COLOR_WHITE
+#define DIALOG_HL                    FALSE
+
+#define TITLE_FG                     COLOR_YELLOW
+#define TITLE_BG                     COLOR_WHITE
+#define TITLE_HL                     TRUE
+
+#define BORDER_FG                    COLOR_WHITE
+#define BORDER_BG                    COLOR_WHITE
+#define BORDER_HL                    TRUE
+
+#define BUTTON_ACTIVE_FG             COLOR_WHITE
+#define BUTTON_ACTIVE_BG             COLOR_BLUE
+#define BUTTON_ACTIVE_HL             TRUE
+
+#define BUTTON_INACTIVE_FG           COLOR_BLACK
+#define BUTTON_INACTIVE_BG           COLOR_WHITE
+#define BUTTON_INACTIVE_HL           FALSE
+
+#define BUTTON_KEY_ACTIVE_FG         COLOR_WHITE
+#define BUTTON_KEY_ACTIVE_BG         COLOR_BLUE
+#define BUTTON_KEY_ACTIVE_HL         TRUE
+
+#define BUTTON_KEY_INACTIVE_FG       COLOR_RED
+#define BUTTON_KEY_INACTIVE_BG       COLOR_WHITE
+#define BUTTON_KEY_INACTIVE_HL       FALSE
+
+#define BUTTON_LABEL_ACTIVE_FG       COLOR_YELLOW
+#define BUTTON_LABEL_ACTIVE_BG       COLOR_BLUE
+#define BUTTON_LABEL_ACTIVE_HL       TRUE
+
+#define BUTTON_LABEL_INACTIVE_FG     COLOR_BLACK
+#define BUTTON_LABEL_INACTIVE_BG     COLOR_WHITE
+#define BUTTON_LABEL_INACTIVE_HL     TRUE
+
+#define INPUTBOX_FG                  COLOR_BLACK
+#define INPUTBOX_BG                  COLOR_WHITE
+#define INPUTBOX_HL                  FALSE
+
+#define INPUTBOX_BORDER_FG           COLOR_BLACK
+#define INPUTBOX_BORDER_BG           COLOR_WHITE
+#define INPUTBOX_BORDER_HL           FALSE
+
+#define SEARCHBOX_FG                 COLOR_BLACK
+#define SEARCHBOX_BG                 COLOR_WHITE
+#define SEARCHBOX_HL                 FALSE
+
+#define SEARCHBOX_TITLE_FG           COLOR_YELLOW
+#define SEARCHBOX_TITLE_BG           COLOR_WHITE
+#define SEARCHBOX_TITLE_HL           TRUE
+
+#define SEARCHBOX_BORDER_FG          COLOR_WHITE
+#define SEARCHBOX_BORDER_BG          COLOR_WHITE
+#define SEARCHBOX_BORDER_HL          TRUE
+
+#define POSITION_INDICATOR_FG        COLOR_YELLOW
+#define POSITION_INDICATOR_BG        COLOR_WHITE
+#define POSITION_INDICATOR_HL        TRUE
+
+#define MENUBOX_FG                   COLOR_BLACK
+#define MENUBOX_BG                   COLOR_WHITE
+#define MENUBOX_HL                   FALSE
+
+#define MENUBOX_BORDER_FG            COLOR_WHITE
+#define MENUBOX_BORDER_BG            COLOR_WHITE
+#define MENUBOX_BORDER_HL            TRUE
+
+#define ITEM_FG                      COLOR_BLACK
+#define ITEM_BG                      COLOR_WHITE
+#define ITEM_HL                      FALSE
+
+#define ITEM_SELECTED_FG             COLOR_WHITE
+#define ITEM_SELECTED_BG             COLOR_BLUE
+#define ITEM_SELECTED_HL             TRUE
+
+#define TAG_FG                       COLOR_YELLOW
+#define TAG_BG                       COLOR_WHITE
+#define TAG_HL                       TRUE
+
+#define TAG_SELECTED_FG              COLOR_YELLOW
+#define TAG_SELECTED_BG              COLOR_BLUE
+#define TAG_SELECTED_HL              TRUE
+
+#define TAG_KEY_FG                   COLOR_YELLOW
+#define TAG_KEY_BG                   COLOR_WHITE
+#define TAG_KEY_HL                   TRUE
+
+#define TAG_KEY_SELECTED_FG          COLOR_YELLOW
+#define TAG_KEY_SELECTED_BG          COLOR_BLUE
+#define TAG_KEY_SELECTED_HL          TRUE
+
+#define CHECK_FG                     COLOR_BLACK
+#define CHECK_BG                     COLOR_WHITE
+#define CHECK_HL                     FALSE
+
+#define CHECK_SELECTED_FG            COLOR_WHITE
+#define CHECK_SELECTED_BG            COLOR_BLUE
+#define CHECK_SELECTED_HL            TRUE
+
+#define UARROW_FG                    COLOR_GREEN
+#define UARROW_BG                    COLOR_WHITE
+#define UARROW_HL                    TRUE
+
+#define DARROW_FG                    COLOR_GREEN
+#define DARROW_BG                    COLOR_WHITE
+#define DARROW_HL                    TRUE
+
+/* End of default color definitions */
+
+#define C_ATTR(x,y)                  ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
+#define COLOR_NAME_LEN               10
+#define COLOR_COUNT                  8
+
+/*
+ * Global variables
+ */
+
+typedef struct {
+    char name[COLOR_NAME_LEN];
+    int value;
+} color_names_st;
+
+extern color_names_st color_names[];
+extern int color_table[][3];
diff --git a/busybox/scripts/config/conf.c b/busybox/scripts/config/conf.c
new file mode 100644 (file)
index 0000000..3b0c1c1
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ * 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 <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[] = "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 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';
+               line[1] = 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) {
+               if (sym_is_changable(sym) && !sym_has_value(sym)) {
+                       if (!conf_cnt++)
+                               printf("*\n* Restart config...\n*\n");
+                       rootEntry = menu_get_parent_menu(menu);
+                       conf(rootEntry);
+               }
+               if (sym_is_choice(sym) && sym_get_tristate_value(sym) != mod)
+                       return;
+       }
+
+       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 '?':
+                       printf("%s [-o|-s] config\n", av[0]);
+                       exit(0);
+               }
+       }
+       name = av[i];
+       if (!name) {
+               printf("%s: configuration file missing\n", av[0]);
+       }
+       conf_parse(name);
+       //zconfdump(stdout);
+       switch (input_mode) {
+       case set_default:
+               if (!defconfig_file)
+                       defconfig_file = conf_get_default_confname();
+               if (conf_read(defconfig_file)) {
+                       printf("***\n"
+                               "*** Can't find default configuration \"%s\"!\n"
+                               "***\n", defconfig_file);
+                       exit(1);
+               }
+               break;
+       case ask_silent:
+               if (stat(".config", &tmpstat)) {
+                       printf("***\n"
+                               "*** You have not yet configured BusyBox!\n"
+                               "***\n"
+                               "*** Please run some configurator (e.g. \"make oldconfig\" or\n"
+                               "*** \"make menuconfig\" or \"make config\").\n"
+                               "***\n");
+                       exit(1);
+               }
+       case ask_all:
+       case ask_new:
+               conf_read(NULL);
+               break;
+       default:
+               break;
+       }
+
+       if (input_mode != ask_silent) {
+               rootEntry = &rootmenu;
+               conf(&rootmenu);
+               if (input_mode == ask_all) {
+                       input_mode = ask_silent;
+                       valid_stdin = 1;
+               }
+       }
+       do {
+               conf_cnt = 0;
+               check_conf(&rootmenu);
+       } while (conf_cnt);
+       if (conf_write(NULL)) {
+               fprintf(stderr, "\n*** Error during writing of the BusyBox configuration.\n\n");
+               return 1;
+       }
+       return 0;
+}
diff --git a/busybox/scripts/config/confdata.c b/busybox/scripts/config/confdata.c
new file mode 100644 (file)
index 0000000..fd3a345
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+const char conf_def_filename[] = ".config";
+
+const char conf_defname[] = "sysdeps/linux/defconfig";
+
+const char *conf_confnames[] = {
+       ".config",
+       conf_defname,
+       NULL,
+};
+
+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(const char *name)
+{
+       FILE *in = NULL;
+       char line[1024];
+       char *p, *p2;
+       int lineno = 0;
+       struct symbol *sym;
+       struct property *prop;
+       struct expr *e;
+       int i;
+
+       if (name) {
+               in = zconf_fopen(name);
+       } else {
+               const char **names = conf_confnames;
+               while ((name = *names++)) {
+                       name = conf_expand_value(name);
+                       in = zconf_fopen(name);
+                       if (in) {
+                               printf("#\n"
+                                      "# using defaults found in %s\n"
+                                      "#\n", name);
+                               break;
+                       }
+               }
+       }
+
+       if (!in)
+               return 1;
+
+       for_all_symbols(i, sym) {
+               sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
+               sym->flags &= ~SYMBOL_VALID;
+               switch (sym->type) {
+               case S_INT:
+               case S_HEX:
+               case S_STRING:
+                       if (sym->user.val)
+                               free(sym->user.val);
+               default:
+                       sym->user.val = NULL;
+                       sym->user.tri = no;
+               }
+       }
+
+       while (fgets(line, sizeof(line), in)) {
+               lineno++;
+               sym = NULL;
+               switch (line[0]) {
+               case '#':
+                       if (line[1]!=' ')
+                               continue;
+                       p = strchr(line + 2, ' ');
+                       if (!p)
+                               continue;
+                       *p++ = 0;
+                       if (strncmp(p, "is not set", 10))
+                               continue;
+                       sym = sym_find(line + 2);
+                       if (!sym) {
+                               fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line + 2);
+                               break;
+                       }
+                       switch (sym->type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               sym->user.tri = no;
+                               sym->flags &= ~SYMBOL_NEW;
+                               break;
+                       default:
+                               ;
+                       }
+                       break;
+
+               case 'A' ... 'Z':
+                       p = strchr(line, '=');
+                       if (!p)
+                               continue;
+                       *p++ = 0;
+                       p2 = strchr(p, '\n');
+                       if (p2)
+                               *p2 = 0;
+                       sym = sym_find(line);
+                       if (!sym) {
+                               fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line);
+                               break;
+                       }
+                       switch (sym->type) {
+                       case S_TRISTATE:
+                               if (p[0] == 'm') {
+                                       sym->user.tri = mod;
+                                       sym->flags &= ~SYMBOL_NEW;
+                                       break;
+                               }
+                       case S_BOOLEAN:
+                               if (p[0] == 'y') {
+                                       sym->user.tri = yes;
+                                       sym->flags &= ~SYMBOL_NEW;
+                                       break;
+                               }
+                               if (p[0] == 'n') {
+                                       sym->user.tri = no;
+                                       sym->flags &= ~SYMBOL_NEW;
+                                       break;
+                               }
+                               break;
+                       case S_STRING:
+                               if (*p++ != '"')
+                                       break;
+                               for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+                                       if (*p2 == '"') {
+                                               *p2 = 0;
+                                               break;
+                                       }
+                                       memmove(p2, p2 + 1, strlen(p2));
+                               }
+                               if (!p2) {
+                                       fprintf(stderr, "%s:%d: invalid string found\n", name, lineno);
+                                       exit(1);
+                               }
+                       case S_INT:
+                       case S_HEX:
+                               if (sym_string_valid(sym, p)) {
+                                       sym->user.val = strdup(p);
+                                       sym->flags &= ~SYMBOL_NEW;
+                               } else {
+                                       fprintf(stderr, "%s:%d: symbol value '%s' invalid for %s\n", name, lineno, p, sym->name);
+                                       exit(1);
+                               }
+                               break;
+                       default:
+                               ;
+                       }
+                       break;
+               case '\n':
+                       break;
+               default:
+                       continue;
+               }
+               if (sym && sym_is_choice_value(sym)) {
+                       struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+                       switch (sym->user.tri) {
+                       case no:
+                               break;
+                       case mod:
+                               if (cs->user.tri == yes)
+                                       /* warn? */;
+                               break;
+                       case yes:
+                               if (cs->user.tri != no)
+                                       /* warn? */;
+                               cs->user.val = sym;
+                               break;
+                       }
+                       cs->user.tri = E_OR(cs->user.tri, sym->user.tri);
+                       cs->flags &= ~SYMBOL_NEW;
+               }
+       }
+       fclose(in);
+
+       if (modules_sym)
+               sym_calc_value(modules_sym);
+       for_all_symbols(i, sym) {
+               sym_calc_value(sym);
+               if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
+                       if (sym->visible == no)
+                               sym->flags |= SYMBOL_NEW;
+                       switch (sym->type) {
+                       case S_STRING:
+                       case S_INT:
+                       case S_HEX:
+                               if (!sym_string_within_range(sym, sym->user.val))
+                                       sym->flags |= SYMBOL_NEW;
+                       default:
+                               break;
+                       }
+               }
+               if (!sym_is_choice(sym))
+                       continue;
+               prop = sym_get_choice_prop(sym);
+               for (e = prop->expr; e; e = e->left.expr)
+                       if (e->right.sym->visible != no)
+                               sym->flags |= e->right.sym->flags & SYMBOL_NEW;
+       }
+
+       sym_change_count = 1;
+
+       return 0;
+}
+
+int conf_write(const char *name)
+{
+       FILE *out, *out_h;
+       struct symbol *sym;
+       struct menu *menu;
+       const char *basename;
+       char dirname[128], tmpname[128], newname[128];
+       int type, l;
+       const char *str;
+
+       dirname[0] = 0;
+       if (name && name[0]) {
+               struct stat st;
+               char *slash;
+
+               if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
+                       strcpy(dirname, name);
+                       strcat(dirname, "/");
+                       basename = conf_def_filename;
+               } else if ((slash = strrchr(name, '/'))) {
+                       int size = slash - name + 1;
+                       memcpy(dirname, name, size);
+                       dirname[size] = 0;
+                       if (slash[1])
+                               basename = slash + 1;
+                       else
+                               basename = conf_def_filename;
+               } else
+                       basename = name;
+       } else
+               basename = conf_def_filename;
+
+       sprintf(newname, "%s.tmpconfig.%d", dirname, getpid());
+       out = fopen(newname, "w");
+       if (!out)
+               return 1;
+       out_h = NULL;
+       if (!name) {
+               out_h = fopen(".tmpconfig.h", "w");
+               if (!out_h)
+                       return 1;
+       }
+       fprintf(out, "#\n"
+                    "# Automatically generated make config: don't edit\n"
+                    "#\n");
+       if (out_h) {
+               fprintf(out_h, "/*\n"
+                            " * Automatically generated header file: don't edit\n"
+                            " */\n\n"
+                            "#define AUTOCONF_INCLUDED\n\n"
+                            "/* Version Number */\n"
+                            "#define BB_VER \"%s\"\n"
+                            "#define BB_BT \"%s\"\n",
+                            getenv("VERSION"),
+                            getenv("BUILDTIME"));
+               if (getenv("EXTRA_VERSION"))
+                       fprintf(out_h, "#define BB_EXTRA_VERSION \"%s\"\n",
+                                    getenv("EXTRA_VERSION"));
+               fprintf(out_h, "\n");
+       }
+
+       if (!sym_change_count)
+               sym_clear_all_valid();
+
+       menu = rootmenu.list;
+       while (menu) {
+               sym = menu->sym;
+               if (!sym) {
+                       if (!menu_is_visible(menu))
+                               goto next;
+                       str = menu_get_prompt(menu);
+                       fprintf(out, "\n"
+                                    "#\n"
+                                    "# %s\n"
+                                    "#\n", str);
+                       if (out_h)
+                               fprintf(out_h, "\n"
+                                              "/*\n"
+                                              " * %s\n"
+                                              " */\n", str);
+               } else if (!(sym->flags & SYMBOL_CHOICE)) {
+                       sym_calc_value(sym);
+                       if (!(sym->flags & SYMBOL_WRITE))
+                               goto next;
+                       sym->flags &= ~SYMBOL_WRITE;
+                       type = sym->type;
+                       if (type == S_TRISTATE) {
+                               sym_calc_value(modules_sym);
+                               if (modules_sym->curr.tri == no)
+                                       type = S_BOOLEAN;
+                       }
+                       switch (type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               switch (sym_get_tristate_value(sym)) {
+                               case no:
+                                       fprintf(out, "# %s is not set\n", sym->name);
+                                       if (out_h)
+                                               fprintf(out_h, "#undef %s\n", sym->name);
+                                       break;
+                               case mod:
+#if 0
+                                       fprintf(out, "%s=m\n", sym->name);
+                                       if (out_h)
+                                               fprintf(out_h, "#define %s_MODULE 1\n", sym->name);
+#endif
+                                       break;
+                               case yes:
+                                       fprintf(out, "%s=y\n", sym->name);
+                                       if (out_h)
+                                               fprintf(out_h, "#define %s 1\n", sym->name);
+                                       break;
+                               }
+                               break;
+                       case S_STRING:
+                               // fix me
+                               str = sym_get_string_value(sym);
+                               fprintf(out, "%s=\"", sym->name);
+                               if (out_h)
+                                       fprintf(out_h, "#define %s \"", sym->name);
+                               do {
+                                       l = strcspn(str, "\"\\");
+                                       if (l) {
+                                               fwrite(str, l, 1, out);
+                                               if (out_h)
+                                                       fwrite(str, l, 1, out_h);
+                                       }
+                                       str += l;
+                                       while (*str == '\\' || *str == '"') {
+                                               fprintf(out, "\\%c", *str);
+                                               if (out_h)
+                                                       fprintf(out_h, "\\%c", *str);
+                                               str++;
+                                       }
+                               } while (*str);
+                               fputs("\"\n", out);
+                               if (out_h)
+                                       fputs("\"\n", out_h);
+                               break;
+                       case S_HEX:
+                               str = sym_get_string_value(sym);
+                               if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+                                       fprintf(out, "%s=%s\n", sym->name, str);
+                                       if (out_h)
+                                               fprintf(out_h, "#define %s 0x%s\n", sym->name, str);
+                                       break;
+                               }
+                       case S_INT:
+                               str = sym_get_string_value(sym);
+                               fprintf(out, "%s=%s\n", sym->name, str);
+                               if (out_h)
+                                       fprintf(out_h, "#define %s %s\n", sym->name, str);
+                               break;
+                       }
+               }
+
+       next:
+               if (menu->list) {
+                       menu = menu->list;
+                       continue;
+               }
+               if (menu->next)
+                       menu = menu->next;
+               else while ((menu = menu->parent)) {
+                       if (menu->next) {
+                               menu = menu->next;
+                               break;
+                       }
+               }
+       }
+       fclose(out);
+       if (out_h) {
+               fclose(out_h);
+               rename(".tmpconfig.h", "include/config.h");
+               file_write_dep(NULL);
+       }
+       if (!name || basename != conf_def_filename) {
+               if (!name)
+                       name = conf_def_filename;
+               sprintf(tmpname, "%s.old", name);
+               rename(name, tmpname);
+       }
+       sprintf(tmpname, "%s%s", dirname, basename);
+       if (rename(newname, tmpname))
+               return 1;
+
+       sym_change_count = 0;
+
+       return 0;
+}
diff --git a/busybox/scripts/config/dialog.h b/busybox/scripts/config/dialog.h
new file mode 100644 (file)
index 0000000..6486cc8
--- /dev/null
@@ -0,0 +1,196 @@
+
+/*
+ *  dialog.h -- common declarations for all dialog modules
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef CURSES_LOC
+#include CURSES_LOC
+
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked.  This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses.  The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef  wbkgdset
+#define wbkgdset(w,p) /*nothing*/
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
+#define ESC 27
+#define TAB 9
+#define MAX_LEN 2048
+#define BUF_SIZE (10*1024)
+#define MIN(x,y) (x < y ? x : y)
+#define MAX(x,y) (x > y ? x : y)
+
+
+#ifndef ACS_ULCORNER
+#define ACS_ULCORNER '+'
+#endif
+#ifndef ACS_LLCORNER
+#define ACS_LLCORNER '+'
+#endif
+#ifndef ACS_URCORNER
+#define ACS_URCORNER '+'
+#endif
+#ifndef ACS_LRCORNER
+#define ACS_LRCORNER '+'
+#endif
+#ifndef ACS_HLINE
+#define ACS_HLINE '-'
+#endif
+#ifndef ACS_VLINE
+#define ACS_VLINE '|'
+#endif
+#ifndef ACS_LTEE
+#define ACS_LTEE '+'
+#endif
+#ifndef ACS_RTEE
+#define ACS_RTEE '+'
+#endif
+#ifndef ACS_UARROW
+#define ACS_UARROW '^'
+#endif
+#ifndef ACS_DARROW
+#define ACS_DARROW 'v'
+#endif
+
+/*
+ * Attribute names
+ */
+#define screen_attr                   attributes[0]
+#define shadow_attr                   attributes[1]
+#define dialog_attr                   attributes[2]
+#define title_attr                    attributes[3]
+#define border_attr                   attributes[4]
+#define button_active_attr            attributes[5]
+#define button_inactive_attr          attributes[6]
+#define button_key_active_attr        attributes[7]
+#define button_key_inactive_attr      attributes[8]
+#define button_label_active_attr      attributes[9]
+#define button_label_inactive_attr    attributes[10]
+#define inputbox_attr                 attributes[11]
+#define inputbox_border_attr          attributes[12]
+#define searchbox_attr                attributes[13]
+#define searchbox_title_attr          attributes[14]
+#define searchbox_border_attr         attributes[15]
+#define position_indicator_attr       attributes[16]
+#define menubox_attr                  attributes[17]
+#define menubox_border_attr           attributes[18]
+#define item_attr                     attributes[19]
+#define item_selected_attr            attributes[20]
+#define tag_attr                      attributes[21]
+#define tag_selected_attr             attributes[22]
+#define tag_key_attr                  attributes[23]
+#define tag_key_selected_attr         attributes[24]
+#define check_attr                    attributes[25]
+#define check_selected_attr           attributes[26]
+#define uarrow_attr                   attributes[27]
+#define darrow_attr                   attributes[28]
+
+/* number of attributes */
+#define ATTRIBUTE_COUNT               29
+
+/*
+ * Global variables
+ */
+extern bool use_colors;
+
+extern chtype attributes[];
+#endif
+
+extern char *backtitle;
+
+struct dialog_list_item {
+       char *name;
+       int namelen;
+       char *tag;
+       int selected; /* Set to 1 by dialog_*() function. */
+};
+
+/*
+ * Function prototypes
+ */
+
+void init_dialog (void);
+void end_dialog (void);
+void dialog_clear (void);
+#ifdef CURSES_LOC
+void attr_clear (WINDOW * win, int height, int width, chtype attr);
+void color_setup (void);
+void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x);
+void print_button (WINDOW * win, const char *label, int y, int x, int selected);
+void 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);
+#endif
+
+int first_alpha (const char *string, const char *exempt);
+int dialog_yesno (const char *title, const char *prompt, int height, int width);
+int dialog_msgbox (const char *title, const char *prompt, int height,
+               int width, int pause);
+int dialog_textbox (const char *title, const char *file, int height, int width);
+int dialog_menu (const char *title, const char *prompt, int height, int width,
+               int menu_height, const char *choice, int item_no,
+               struct dialog_list_item ** items);
+int dialog_checklist (const char *title, const char *prompt, int height,
+               int width, int list_height, int item_no,
+               struct dialog_list_item ** items, int flag);
+extern unsigned char dialog_input_result[];
+int dialog_inputbox (const char *title, const char *prompt, int height,
+               int width, const char *init);
+
+struct dialog_list_item *first_sel_item(int item_no,
+               struct dialog_list_item ** items);
+
+/*
+ * 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')
+ */
+#ifdef CURSES_LOC
+#define M_EVENT (KEY_MAX+1)
+#endif
+
+
+/*
+ * The `flag' parameter in checklist is used to select between
+ * radiolist and checklist
+ */
+#define FLAG_CHECK 1
+#define FLAG_RADIO 0
diff --git a/busybox/scripts/config/expr.c b/busybox/scripts/config/expr.c
new file mode 100644 (file)
index 0000000..10f4523
--- /dev/null
@@ -0,0 +1,1089 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define DEBUG_EXPR     0
+
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = E_SYMBOL;
+       e->left.sym = sym;
+       return e;
+}
+
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = type;
+       e->left.expr = ce;
+       return e;
+}
+
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = type;
+       e->left.expr = e1;
+       e->right.expr = e2;
+       return e;
+}
+
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
+{
+       struct expr *e = malloc(sizeof(*e));
+       memset(e, 0, sizeof(*e));
+       e->type = type;
+       e->left.sym = s1;
+       e->right.sym = s2;
+       return e;
+}
+
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
+{
+       if (!e1)
+               return e2;
+       return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
+}
+
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
+{
+       if (!e1)
+               return e2;
+       return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
+}
+
+struct expr *expr_copy(struct expr *org)
+{
+       struct expr *e;
+
+       if (!org)
+               return NULL;
+
+       e = malloc(sizeof(*org));
+       memcpy(e, org, sizeof(*org));
+       switch (org->type) {
+       case E_SYMBOL:
+               e->left = org->left;
+               break;
+       case E_NOT:
+               e->left.expr = expr_copy(org->left.expr);
+               break;
+       case E_EQUAL:
+       case E_UNEQUAL:
+               e->left.sym = org->left.sym;
+               e->right.sym = org->right.sym;
+               break;
+       case E_AND:
+       case E_OR:
+       case E_CHOICE:
+               e->left.expr = expr_copy(org->left.expr);
+               e->right.expr = expr_copy(org->right.expr);
+               break;
+       default:
+               printf("can't copy type %d\n", e->type);
+               free(e);
+               e = NULL;
+               break;
+       }
+
+       return e;
+}
+
+void expr_free(struct expr *e)
+{
+       if (!e)
+               return;
+
+       switch (e->type) {
+       case E_SYMBOL:
+               break;
+       case E_NOT:
+               expr_free(e->left.expr);
+               return;
+       case E_EQUAL:
+       case E_UNEQUAL:
+               break;
+       case E_OR:
+       case E_AND:
+               expr_free(e->left.expr);
+               expr_free(e->right.expr);
+               break;
+       default:
+               printf("how to free type %d?\n", e->type);
+               break;
+       }
+       free(e);
+}
+
+static int trans_count;
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+       if (e1->type == type) {
+               __expr_eliminate_eq(type, &e1->left.expr, &e2);
+               __expr_eliminate_eq(type, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               __expr_eliminate_eq(type, &e1, &e2->left.expr);
+               __expr_eliminate_eq(type, &e1, &e2->right.expr);
+               return;
+       }
+       if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+           e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO)))
+               return;
+       if (!expr_eq(e1, e2))
+               return;
+       trans_count++;
+       expr_free(e1); expr_free(e2);
+       switch (type) {
+       case E_OR:
+               e1 = expr_alloc_symbol(&symbol_no);
+               e2 = expr_alloc_symbol(&symbol_no);
+               break;
+       case E_AND:
+               e1 = expr_alloc_symbol(&symbol_yes);
+               e2 = expr_alloc_symbol(&symbol_yes);
+               break;
+       default:
+               ;
+       }
+}
+
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+{
+       if (!e1 || !e2)
+               return;
+       switch (e1->type) {
+       case E_OR:
+       case E_AND:
+               __expr_eliminate_eq(e1->type, ep1, ep2);
+       default:
+               ;
+       }
+       if (e1->type != e2->type) switch (e2->type) {
+       case E_OR:
+       case E_AND:
+               __expr_eliminate_eq(e2->type, ep1, ep2);
+       default:
+               ;
+       }
+       e1 = expr_eliminate_yn(e1);
+       e2 = expr_eliminate_yn(e2);
+}
+
+#undef e1
+#undef e2
+
+int expr_eq(struct expr *e1, struct expr *e2)
+{
+       int res, old_count;
+
+       if (e1->type != e2->type)
+               return 0;
+       switch (e1->type) {
+       case E_EQUAL:
+       case E_UNEQUAL:
+               return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
+       case E_SYMBOL:
+               return e1->left.sym == e2->left.sym;
+       case E_NOT:
+               return expr_eq(e1->left.expr, e2->left.expr);
+       case E_AND:
+       case E_OR:
+               e1 = expr_copy(e1);
+               e2 = expr_copy(e2);
+               old_count = trans_count;
+               expr_eliminate_eq(&e1, &e2);
+               res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+                      e1->left.sym == e2->left.sym);
+               expr_free(e1);
+               expr_free(e2);
+               trans_count = old_count;
+               return res;
+       case E_CHOICE:
+       case E_RANGE:
+       case E_NONE:
+               /* panic */;
+       }
+
+       if (DEBUG_EXPR) {
+               expr_fprint(e1, stdout);
+               printf(" = ");
+               expr_fprint(e2, stdout);
+               printf(" ?\n");
+       }
+
+       return 0;
+}
+
+struct expr *expr_eliminate_yn(struct expr *e)
+{
+       struct expr *tmp;
+
+       if (e) switch (e->type) {
+       case E_AND:
+               e->left.expr = expr_eliminate_yn(e->left.expr);
+               e->right.expr = expr_eliminate_yn(e->right.expr);
+               if (e->left.expr->type == E_SYMBOL) {
+                       if (e->left.expr->left.sym == &symbol_no) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_no;
+                               e->right.expr = NULL;
+                               return e;
+                       } else if (e->left.expr->left.sym == &symbol_yes) {
+                               free(e->left.expr);
+                               tmp = e->right.expr;
+                               *e = *(e->right.expr);
+                               free(tmp);
+                               return e;
+                       }
+               }
+               if (e->right.expr->type == E_SYMBOL) {
+                       if (e->right.expr->left.sym == &symbol_no) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_no;
+                               e->right.expr = NULL;
+                               return e;
+                       } else if (e->right.expr->left.sym == &symbol_yes) {
+                               free(e->right.expr);
+                               tmp = e->left.expr;
+                               *e = *(e->left.expr);
+                               free(tmp);
+                               return e;
+                       }
+               }
+               break;
+       case E_OR:
+               e->left.expr = expr_eliminate_yn(e->left.expr);
+               e->right.expr = expr_eliminate_yn(e->right.expr);
+               if (e->left.expr->type == E_SYMBOL) {
+                       if (e->left.expr->left.sym == &symbol_no) {
+                               free(e->left.expr);
+                               tmp = e->right.expr;
+                               *e = *(e->right.expr);
+                               free(tmp);
+                               return e;
+                       } else if (e->left.expr->left.sym == &symbol_yes) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_yes;
+                               e->right.expr = NULL;
+                               return e;
+                       }
+               }
+               if (e->right.expr->type == E_SYMBOL) {
+                       if (e->right.expr->left.sym == &symbol_no) {
+                               free(e->right.expr);
+                               tmp = e->left.expr;
+                               *e = *(e->left.expr);
+                               free(tmp);
+                               return e;
+                       } else if (e->right.expr->left.sym == &symbol_yes) {
+                               expr_free(e->left.expr);
+                               expr_free(e->right.expr);
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_yes;
+                               e->right.expr = NULL;
+                               return e;
+                       }
+               }
+               break;
+       default:
+               ;
+       }
+       return e;
+}
+
+/*
+ * bool FOO!=n => FOO
+ */
+struct expr *expr_trans_bool(struct expr *e)
+{
+       if (!e)
+               return NULL;
+       switch (e->type) {
+       case E_AND:
+       case E_OR:
+       case E_NOT:
+               e->left.expr = expr_trans_bool(e->left.expr);
+               e->right.expr = expr_trans_bool(e->right.expr);
+               break;
+       case E_UNEQUAL:
+               // FOO!=n -> FOO
+               if (e->left.sym->type == S_TRISTATE) {
+                       if (e->right.sym == &symbol_no) {
+                               e->type = E_SYMBOL;
+                               e->right.sym = NULL;
+                       }
+               }
+               break;
+       default:
+               ;
+       }
+       return e;
+}
+
+/*
+ * e1 || e2 -> ?
+ */
+struct expr *expr_join_or(struct expr *e1, struct expr *e2)
+{
+       struct expr *tmp;
+       struct symbol *sym1, *sym2;
+
+       if (expr_eq(e1, e2))
+               return expr_copy(e1);
+       if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+               return NULL;
+       if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+               return NULL;
+       if (e1->type == E_NOT) {
+               tmp = e1->left.expr;
+               if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+                       return NULL;
+               sym1 = tmp->left.sym;
+       } else
+               sym1 = e1->left.sym;
+       if (e2->type == E_NOT) {
+               if (e2->left.expr->type != E_SYMBOL)
+                       return NULL;
+               sym2 = e2->left.expr->left.sym;
+       } else
+               sym2 = e2->left.sym;
+       if (sym1 != sym2)
+               return NULL;
+       if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+               return NULL;
+       if (sym1->type == S_TRISTATE) {
+               if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+                   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+                    (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
+                       // (a='y') || (a='m') -> (a!='n')
+                       return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
+               }
+               if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+                   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+                    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
+                       // (a='y') || (a='n') -> (a!='m')
+                       return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
+               }
+               if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+                   ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+                    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
+                       // (a='m') || (a='n') -> (a!='y')
+                       return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
+               }
+       }
+       if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+               if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
+                   (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
+                       return expr_alloc_symbol(&symbol_yes);
+       }
+
+       if (DEBUG_EXPR) {
+               printf("optimize (");
+               expr_fprint(e1, stdout);
+               printf(") || (");
+               expr_fprint(e2, stdout);
+               printf(")?\n");
+       }
+       return NULL;
+}
+
+struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+{
+       struct expr *tmp;
+       struct symbol *sym1, *sym2;
+
+       if (expr_eq(e1, e2))
+               return expr_copy(e1);
+       if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+               return NULL;
+       if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+               return NULL;
+       if (e1->type == E_NOT) {
+               tmp = e1->left.expr;
+               if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+                       return NULL;
+               sym1 = tmp->left.sym;
+       } else
+               sym1 = e1->left.sym;
+       if (e2->type == E_NOT) {
+               if (e2->left.expr->type != E_SYMBOL)
+                       return NULL;
+               sym2 = e2->left.expr->left.sym;
+       } else
+               sym2 = e2->left.sym;
+       if (sym1 != sym2)
+               return NULL;
+       if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+               return NULL;
+
+       if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
+           (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
+               // (a) && (a='y') -> (a='y')
+               return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+       if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
+           (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
+               // (a) && (a!='n') -> (a)
+               return expr_alloc_symbol(sym1);
+
+       if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
+           (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
+               // (a) && (a!='m') -> (a='y')
+               return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+       if (sym1->type == S_TRISTATE) {
+               if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
+                       // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+                       sym2 = e1->right.sym;
+                       if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+                               return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+                                                            : expr_alloc_symbol(&symbol_no);
+               }
+               if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
+                       // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+                       sym2 = e2->right.sym;
+                       if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+                               return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+                                                            : expr_alloc_symbol(&symbol_no);
+               }
+               if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+                          ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+                           (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
+                       // (a!='y') && (a!='n') -> (a='m')
+                       return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
+
+               if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+                          ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+                           (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
+                       // (a!='y') && (a!='m') -> (a='n')
+                       return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
+
+               if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+                          ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+                           (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
+                       // (a!='m') && (a!='n') -> (a='m')
+                       return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+               if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
+                   (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
+                   (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
+                   (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
+                       return NULL;
+       }
+
+       if (DEBUG_EXPR) {
+               printf("optimize (");
+               expr_fprint(e1, stdout);
+               printf(") && (");
+               expr_fprint(e2, stdout);
+               printf(")?\n");
+       }
+       return NULL;
+}
+
+static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+       struct expr *tmp;
+
+       if (e1->type == type) {
+               expr_eliminate_dups1(type, &e1->left.expr, &e2);
+               expr_eliminate_dups1(type, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               expr_eliminate_dups1(type, &e1, &e2->left.expr);
+               expr_eliminate_dups1(type, &e1, &e2->right.expr);
+               return;
+       }
+       if (e1 == e2)
+               return;
+
+       switch (e1->type) {
+       case E_OR: case E_AND:
+               expr_eliminate_dups1(e1->type, &e1, &e1);
+       default:
+               ;
+       }
+
+       switch (type) {
+       case E_OR:
+               tmp = expr_join_or(e1, e2);
+               if (tmp) {
+                       expr_free(e1); expr_free(e2);
+                       e1 = expr_alloc_symbol(&symbol_no);
+                       e2 = tmp;
+                       trans_count++;
+               }
+               break;
+       case E_AND:
+               tmp = expr_join_and(e1, e2);
+               if (tmp) {
+                       expr_free(e1); expr_free(e2);
+                       e1 = expr_alloc_symbol(&symbol_yes);
+                       e2 = tmp;
+                       trans_count++;
+               }
+               break;
+       default:
+               ;
+       }
+#undef e1
+#undef e2
+}
+
+static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+       struct expr *tmp, *tmp1, *tmp2;
+
+       if (e1->type == type) {
+               expr_eliminate_dups2(type, &e1->left.expr, &e2);
+               expr_eliminate_dups2(type, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               expr_eliminate_dups2(type, &e1, &e2->left.expr);
+               expr_eliminate_dups2(type, &e1, &e2->right.expr);
+       }
+       if (e1 == e2)
+               return;
+
+       switch (e1->type) {
+       case E_OR:
+               expr_eliminate_dups2(e1->type, &e1, &e1);
+               // (FOO || BAR) && (!FOO && !BAR) -> n
+               tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+               tmp2 = expr_copy(e2);
+               tmp = expr_extract_eq_and(&tmp1, &tmp2);
+               if (expr_is_yes(tmp1)) {
+                       expr_free(e1);
+                       e1 = expr_alloc_symbol(&symbol_no);
+                       trans_count++;
+               }
+               expr_free(tmp2);
+               expr_free(tmp1);
+               expr_free(tmp);
+               break;
+       case E_AND:
+               expr_eliminate_dups2(e1->type, &e1, &e1);
+               // (FOO && BAR) || (!FOO || !BAR) -> y
+               tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+               tmp2 = expr_copy(e2);
+               tmp = expr_extract_eq_or(&tmp1, &tmp2);
+               if (expr_is_no(tmp1)) {
+                       expr_free(e1);
+                       e1 = expr_alloc_symbol(&symbol_yes);
+                       trans_count++;
+               }
+               expr_free(tmp2);
+               expr_free(tmp1);
+               expr_free(tmp);
+               break;
+       default:
+               ;
+       }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_eliminate_dups(struct expr *e)
+{
+       int oldcount;
+       if (!e)
+               return e;
+
+       oldcount = trans_count;
+       while (1) {
+               trans_count = 0;
+               switch (e->type) {
+               case E_OR: case E_AND:
+                       expr_eliminate_dups1(e->type, &e, &e);
+                       expr_eliminate_dups2(e->type, &e, &e);
+               default:
+                       ;
+               }
+               if (!trans_count)
+                       break;
+               e = expr_eliminate_yn(e);
+       }
+       trans_count = oldcount;
+       return e;
+}
+
+struct expr *expr_transform(struct expr *e)
+{
+       struct expr *tmp;
+
+       if (!e)
+               return NULL;
+       switch (e->type) {
+       case E_EQUAL:
+       case E_UNEQUAL:
+       case E_SYMBOL:
+       case E_CHOICE:
+               break;
+       default:
+               e->left.expr = expr_transform(e->left.expr);
+               e->right.expr = expr_transform(e->right.expr);
+       }
+
+       switch (e->type) {
+       case E_EQUAL:
+               if (e->left.sym->type != S_BOOLEAN)
+                       break;
+               if (e->right.sym == &symbol_no) {
+                       e->type = E_NOT;
+                       e->left.expr = expr_alloc_symbol(e->left.sym);
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_mod) {
+                       printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
+                       e->type = E_SYMBOL;
+                       e->left.sym = &symbol_no;
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_yes) {
+                       e->type = E_SYMBOL;
+                       e->right.sym = NULL;
+                       break;
+               }
+               break;
+       case E_UNEQUAL:
+               if (e->left.sym->type != S_BOOLEAN)
+                       break;
+               if (e->right.sym == &symbol_no) {
+                       e->type = E_SYMBOL;
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_mod) {
+                       printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
+                       e->type = E_SYMBOL;
+                       e->left.sym = &symbol_yes;
+                       e->right.sym = NULL;
+                       break;
+               }
+               if (e->right.sym == &symbol_yes) {
+                       e->type = E_NOT;
+                       e->left.expr = expr_alloc_symbol(e->left.sym);
+                       e->right.sym = NULL;
+                       break;
+               }
+               break;
+       case E_NOT:
+               switch (e->left.expr->type) {
+               case E_NOT:
+                       // !!a -> a
+                       tmp = e->left.expr->left.expr;
+                       free(e->left.expr);
+                       free(e);
+                       e = tmp;
+                       e = expr_transform(e);
+                       break;
+               case E_EQUAL:
+               case E_UNEQUAL:
+                       // !a='x' -> a!='x'
+                       tmp = e->left.expr;
+                       free(e);
+                       e = tmp;
+                       e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+                       break;
+               case E_OR:
+                       // !(a || b) -> !a && !b
+                       tmp = e->left.expr;
+                       e->type = E_AND;
+                       e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+                       tmp->type = E_NOT;
+                       tmp->right.expr = NULL;
+                       e = expr_transform(e);
+                       break;
+               case E_AND:
+                       // !(a && b) -> !a || !b
+                       tmp = e->left.expr;
+                       e->type = E_OR;
+                       e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+                       tmp->type = E_NOT;
+                       tmp->right.expr = NULL;
+                       e = expr_transform(e);
+                       break;
+               case E_SYMBOL:
+                       if (e->left.expr->left.sym == &symbol_yes) {
+                               // !'y' -> 'n'
+                               tmp = e->left.expr;
+                               free(e);
+                               e = tmp;
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_no;
+                               break;
+                       }
+                       if (e->left.expr->left.sym == &symbol_mod) {
+                               // !'m' -> 'm'
+                               tmp = e->left.expr;
+                               free(e);
+                               e = tmp;
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_mod;
+                               break;
+                       }
+                       if (e->left.expr->left.sym == &symbol_no) {
+                               // !'n' -> 'y'
+                               tmp = e->left.expr;
+                               free(e);
+                               e = tmp;
+                               e->type = E_SYMBOL;
+                               e->left.sym = &symbol_yes;
+                               break;
+                       }
+                       break;
+               default:
+                       ;
+               }
+               break;
+       default:
+               ;
+       }
+       return e;
+}
+
+int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+{
+       if (!dep)
+               return 0;
+
+       switch (dep->type) {
+       case E_AND:
+       case E_OR:
+               return expr_contains_symbol(dep->left.expr, sym) ||
+                      expr_contains_symbol(dep->right.expr, sym);
+       case E_SYMBOL:
+               return dep->left.sym == sym;
+       case E_EQUAL:
+       case E_UNEQUAL:
+               return dep->left.sym == sym ||
+                      dep->right.sym == sym;
+       case E_NOT:
+               return expr_contains_symbol(dep->left.expr, sym);
+       default:
+               ;
+       }
+       return 0;
+}
+
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+{
+       if (!dep)
+               return false;
+
+       switch (dep->type) {
+       case E_AND:
+               return expr_depends_symbol(dep->left.expr, sym) ||
+                      expr_depends_symbol(dep->right.expr, sym);
+       case E_SYMBOL:
+               return dep->left.sym == sym;
+       case E_EQUAL:
+               if (dep->left.sym == sym) {
+                       if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
+                               return true;
+               }
+               break;
+       case E_UNEQUAL:
+               if (dep->left.sym == sym) {
+                       if (dep->right.sym == &symbol_no)
+                               return true;
+               }
+               break;
+       default:
+               ;
+       }
+       return false;
+}
+
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2)
+{
+       struct expr *tmp = NULL;
+       expr_extract_eq(E_AND, &tmp, ep1, ep2);
+       if (tmp) {
+               *ep1 = expr_eliminate_yn(*ep1);
+               *ep2 = expr_eliminate_yn(*ep2);
+       }
+       return tmp;
+}
+
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2)
+{
+       struct expr *tmp = NULL;
+       expr_extract_eq(E_OR, &tmp, ep1, ep2);
+       if (tmp) {
+               *ep1 = expr_eliminate_yn(*ep1);
+               *ep2 = expr_eliminate_yn(*ep2);
+       }
+       return tmp;
+}
+
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+       if (e1->type == type) {
+               expr_extract_eq(type, ep, &e1->left.expr, &e2);
+               expr_extract_eq(type, ep, &e1->right.expr, &e2);
+               return;
+       }
+       if (e2->type == type) {
+               expr_extract_eq(type, ep, ep1, &e2->left.expr);
+               expr_extract_eq(type, ep, ep1, &e2->right.expr);
+               return;
+       }
+       if (expr_eq(e1, e2)) {
+               *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1;
+               expr_free(e2);
+               if (type == E_AND) {
+                       e1 = expr_alloc_symbol(&symbol_yes);
+                       e2 = expr_alloc_symbol(&symbol_yes);
+               } else if (type == E_OR) {
+                       e1 = expr_alloc_symbol(&symbol_no);
+                       e2 = expr_alloc_symbol(&symbol_no);
+               }
+       }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+{
+       struct expr *e1, *e2;
+
+       if (!e) {
+               e = expr_alloc_symbol(sym);
+               if (type == E_UNEQUAL)
+                       e = expr_alloc_one(E_NOT, e);
+               return e;
+       }
+       switch (e->type) {
+       case E_AND:
+               e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+               e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+               if (sym == &symbol_yes)
+                       e = expr_alloc_two(E_AND, e1, e2);
+               if (sym == &symbol_no)
+                       e = expr_alloc_two(E_OR, e1, e2);
+               if (type == E_UNEQUAL)
+                       e = expr_alloc_one(E_NOT, e);
+               return e;
+       case E_OR:
+               e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+               e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+               if (sym == &symbol_yes)
+                       e = expr_alloc_two(E_OR, e1, e2);
+               if (sym == &symbol_no)
+                       e = expr_alloc_two(E_AND, e1, e2);
+               if (type == E_UNEQUAL)
+                       e = expr_alloc_one(E_NOT, e);
+               return e;
+       case E_NOT:
+               return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
+       case E_UNEQUAL:
+       case E_EQUAL:
+               if (type == E_EQUAL) {
+                       if (sym == &symbol_yes)
+                               return expr_copy(e);
+                       if (sym == &symbol_mod)
+                               return expr_alloc_symbol(&symbol_no);
+                       if (sym == &symbol_no)
+                               return expr_alloc_one(E_NOT, expr_copy(e));
+               } else {
+                       if (sym == &symbol_yes)
+                               return expr_alloc_one(E_NOT, expr_copy(e));
+                       if (sym == &symbol_mod)
+                               return expr_alloc_symbol(&symbol_yes);
+                       if (sym == &symbol_no)
+                               return expr_copy(e);
+               }
+               break;
+       case E_SYMBOL:
+               return expr_alloc_comp(type, e->left.sym, sym);
+       case E_CHOICE:
+       case E_RANGE:
+       case E_NONE:
+               /* panic */;
+       }
+       return NULL;
+}
+
+tristate expr_calc_value(struct expr *e)
+{
+       tristate val1, val2;
+       const char *str1, *str2;
+
+       if (!e)
+               return yes;
+
+       switch (e->type) {
+       case E_SYMBOL:
+               sym_calc_value(e->left.sym);
+               return e->left.sym->curr.tri;
+       case E_AND:
+               val1 = expr_calc_value(e->left.expr);
+               val2 = expr_calc_value(e->right.expr);
+               return E_AND(val1, val2);
+       case E_OR:
+               val1 = expr_calc_value(e->left.expr);
+               val2 = expr_calc_value(e->right.expr);
+               return E_OR(val1, val2);
+       case E_NOT:
+               val1 = expr_calc_value(e->left.expr);
+               return E_NOT(val1);
+       case E_EQUAL:
+               sym_calc_value(e->left.sym);
+               sym_calc_value(e->right.sym);
+               str1 = sym_get_string_value(e->left.sym);
+               str2 = sym_get_string_value(e->right.sym);
+               return !strcmp(str1, str2) ? yes : no;
+       case E_UNEQUAL:
+               sym_calc_value(e->left.sym);
+               sym_calc_value(e->right.sym);
+               str1 = sym_get_string_value(e->left.sym);
+               str2 = sym_get_string_value(e->right.sym);
+               return !strcmp(str1, str2) ? no : yes;
+       default:
+               printf("expr_calc_value: %d?\n", e->type);
+               return no;
+       }
+}
+
+int expr_compare_type(enum expr_type t1, enum expr_type t2)
+{
+#if 0
+       return 1;
+#else
+       if (t1 == t2)
+               return 0;
+       switch (t1) {
+       case E_EQUAL:
+       case E_UNEQUAL:
+               if (t2 == E_NOT)
+                       return 1;
+       case E_NOT:
+               if (t2 == E_AND)
+                       return 1;
+       case E_AND:
+               if (t2 == E_OR)
+                       return 1;
+       case E_OR:
+               if (t2 == E_CHOICE)
+                       return 1;
+       case E_CHOICE:
+               if (t2 == 0)
+                       return 1;
+       default:
+               return -1;
+       }
+       printf("[%dgt%d?]", t1, t2);
+       return 0;
+#endif
+}
+
+void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)
+{
+       if (!e) {
+               fn(data, "y");
+               return;
+       }
+
+       if (expr_compare_type(prevtoken, e->type) > 0)
+               fn(data, "(");
+       switch (e->type) {
+       case E_SYMBOL:
+               if (e->left.sym->name)
+                       fn(data, e->left.sym->name);
+               else
+                       fn(data, "<choice>");
+               break;
+       case E_NOT:
+               fn(data, "!");
+               expr_print(e->left.expr, fn, data, E_NOT);
+               break;
+       case E_EQUAL:
+               fn(data, e->left.sym->name);
+               fn(data, "=");
+               fn(data, e->right.sym->name);
+               break;
+       case E_UNEQUAL:
+               fn(data, e->left.sym->name);
+               fn(data, "!=");
+               fn(data, e->right.sym->name);
+               break;
+       case E_OR:
+               expr_print(e->left.expr, fn, data, E_OR);
+               fn(data, " || ");
+               expr_print(e->right.expr, fn, data, E_OR);
+               break;
+       case E_AND:
+               expr_print(e->left.expr, fn, data, E_AND);
+               fn(data, " && ");
+               expr_print(e->right.expr, fn, data, E_AND);
+               break;
+       case E_CHOICE:
+               fn(data, e->right.sym->name);
+               if (e->left.expr) {
+                       fn(data, " ^ ");
+                       expr_print(e->left.expr, fn, data, E_CHOICE);
+               }
+               break;
+       case E_RANGE:
+               fn(data, "[");
+               fn(data, e->left.sym->name);
+               fn(data, " ");
+               fn(data, e->right.sym->name);
+               fn(data, "]");
+               break;
+       default:
+         {
+               char buf[32];
+               sprintf(buf, "<unknown type %d>", e->type);
+               fn(data, buf);
+               break;
+         }
+       }
+       if (expr_compare_type(prevtoken, e->type) > 0)
+               fn(data, ")");
+}
+
+static void expr_print_file_helper(void *data, const char *str)
+{
+       fwrite(str, strlen(str), 1, data);
+}
+
+void expr_fprint(struct expr *e, FILE *out)
+{
+       expr_print(e, expr_print_file_helper, out, E_NONE);
+}
diff --git a/busybox/scripts/config/expr.h b/busybox/scripts/config/expr.h
new file mode 100644 (file)
index 0000000..cac51f6
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef EXPR_H
+#define EXPR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+struct file {
+       struct file *next;
+       struct file *parent;
+       char *name;
+       int lineno;
+       int flags;
+};
+
+#define FILE_BUSY              0x0001
+#define FILE_SCANNED           0x0002
+#define FILE_PRINTED           0x0004
+
+typedef enum tristate {
+       no, mod, yes
+} tristate;
+
+enum expr_type {
+       E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE
+};
+
+union expr_data {
+       struct expr *expr;
+       struct symbol *sym;
+};
+
+struct expr {
+       enum expr_type type;
+       union expr_data left, right;
+};
+
+#define E_OR(dep1, dep2)       (((dep1)>(dep2))?(dep1):(dep2))
+#define E_AND(dep1, dep2)      (((dep1)<(dep2))?(dep1):(dep2))
+#define E_NOT(dep)             (2-(dep))
+
+struct expr_value {
+       struct expr *expr;
+       tristate tri;
+};
+
+struct symbol_value {
+       void *val;
+       tristate tri;
+};
+
+enum symbol_type {
+       S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
+};
+
+struct symbol {
+       struct symbol *next;
+       char *name;
+       char *help;
+       enum symbol_type type;
+       struct symbol_value curr, user;
+       tristate visible;
+       int flags;
+       struct property *prop;
+       struct expr *dep, *dep2;
+       struct expr_value rev_dep;
+};
+
+#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
+
+#define SYMBOL_YES             0x0001
+#define SYMBOL_MOD             0x0002
+#define SYMBOL_NO              0x0004
+#define SYMBOL_CONST           0x0007
+#define SYMBOL_CHECK           0x0008
+#define SYMBOL_CHOICE          0x0010
+#define SYMBOL_CHOICEVAL       0x0020
+#define SYMBOL_PRINTED         0x0040
+#define SYMBOL_VALID           0x0080
+#define SYMBOL_OPTIONAL                0x0100
+#define SYMBOL_WRITE           0x0200
+#define SYMBOL_CHANGED         0x0400
+#define SYMBOL_NEW             0x0800
+#define SYMBOL_AUTO            0x1000
+#define SYMBOL_CHECKED         0x2000
+#define SYMBOL_CHECK_DONE      0x4000
+#define SYMBOL_WARNED          0x8000
+
+#define SYMBOL_MAXLENGTH       256
+#define SYMBOL_HASHSIZE                257
+#define SYMBOL_HASHMASK                0xff
+
+enum prop_type {
+       P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE
+};
+
+struct property {
+       struct property *next;
+       struct symbol *sym;
+       enum prop_type type;
+       const char *text;
+       struct expr_value visible;
+       struct expr *expr;
+       struct menu *menu;
+       struct file *file;
+       int lineno;
+};
+
+#define for_all_properties(sym, st, tok) \
+       for (st = sym->prop; st; st = st->next) \
+               if (st->type == (tok))
+#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
+#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
+#define for_all_prompts(sym, st) \
+       for (st = sym->prop; st; st = st->next) \
+               if (st->text)
+
+struct menu {
+       struct menu *next;
+       struct menu *parent;
+       struct menu *list;
+       struct symbol *sym;
+       struct property *prompt;
+       struct expr *dep;
+       unsigned int flags;
+       //char *help;
+       struct file *file;
+       int lineno;
+       void *data;
+};
+
+#define MENU_CHANGED           0x0001
+#define MENU_ROOT              0x0002
+
+#ifndef SWIG
+
+extern struct file *file_list;
+extern struct file *current_file;
+struct file *lookup_file(const char *name);
+
+extern struct symbol symbol_yes, symbol_no, symbol_mod;
+extern struct symbol *modules_sym;
+extern int cdebug;
+struct expr *expr_alloc_symbol(struct symbol *sym);
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
+struct expr *expr_copy(struct expr *org);
+void expr_free(struct expr *e);
+int expr_eq(struct expr *e1, struct expr *e2);
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
+tristate expr_calc_value(struct expr *e);
+struct expr *expr_eliminate_yn(struct expr *e);
+struct expr *expr_trans_bool(struct expr *e);
+struct expr *expr_eliminate_dups(struct expr *e);
+struct expr *expr_transform(struct expr *e);
+int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2);
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2);
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2);
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+
+void expr_fprint(struct expr *e, FILE *out);
+
+static inline int expr_is_yes(struct expr *e)
+{
+       return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
+}
+
+static inline int expr_is_no(struct expr *e)
+{
+       return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXPR_H */
diff --git a/busybox/scripts/config/inputbox.c b/busybox/scripts/config/inputbox.c
new file mode 100644 (file)
index 0000000..fa7bebc
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ *  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"
+
+unsigned 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;
+    unsigned char *instr = dialog_input_result;
+    WINDOW *dialog;
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+       waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL && strlen(title) >= width-2 ) {
+       /* truncate long title -- mec */
+       char * title2 = malloc(width-2+1);
+       memcpy( title2, title, width-2 );
+       title2[width-2] = '\0';
+       title = title2;
+    }
+
+    if (title != NULL) {
+       wattrset (dialog, title_attr);
+       mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+       waddstr (dialog, (char *)title);
+       waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    /* Draw the input field box */
+    box_width = width - 6;
+    getyx (dialog, y, x);
+    box_y = y + 2;
+    box_x = (width - box_width) / 2;
+    draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2,
+             border_attr, dialog_attr);
+
+    print_buttons(dialog, height, width, 0);
+
+    /* Set up the initial value */
+    wmove (dialog, box_y, box_x);
+    wattrset (dialog, inputbox_attr);
+
+    if (!init)
+       instr[0] = '\0';
+    else
+       strcpy (instr, init);
+
+    input_x = strlen (instr);
+
+    if (input_x >= box_width) {
+       scroll = input_x - box_width + 1;
+       input_x = box_width - 1;
+       for (i = 0; i < box_width - 1; i++)
+           waddch (dialog, instr[scroll + i]);
+    } else
+       waddstr (dialog, instr);
+
+    wmove (dialog, box_y, box_x + input_x);
+
+    wrefresh (dialog);
+
+    while (key != ESC) {
+       key = wgetch (dialog);
+
+       if (button == -1) {     /* Input box selected */
+           switch (key) {
+           case TAB:
+           case KEY_UP:
+           case KEY_DOWN:
+               break;
+           case KEY_LEFT:
+               continue;
+           case KEY_RIGHT:
+               continue;
+           case KEY_BACKSPACE:
+           case 127:
+               if (input_x || scroll) {
+                   wattrset (dialog, inputbox_attr);
+                   if (!input_x) {
+                       scroll = scroll < box_width - 1 ?
+                           0 : scroll - (box_width - 1);
+                       wmove (dialog, box_y, box_x);
+                       for (i = 0; i < box_width; i++)
+                           waddch (dialog, instr[scroll + input_x + i] ?
+                                   instr[scroll + input_x + i] : ' ');
+                       input_x = strlen (instr) - scroll;
+                   } else
+                       input_x--;
+                   instr[scroll + input_x] = '\0';
+                   mvwaddch (dialog, box_y, input_x + box_x, ' ');
+                   wmove (dialog, box_y, input_x + box_x);
+                   wrefresh (dialog);
+               }
+               continue;
+           default:
+               if (key < 0x100 && isprint (key)) {
+                   if (scroll + input_x < MAX_LEN) {
+                       wattrset (dialog, inputbox_attr);
+                       instr[scroll + input_x] = key;
+                       instr[scroll + input_x + 1] = '\0';
+                       if (input_x == box_width - 1) {
+                           scroll++;
+                           wmove (dialog, box_y, box_x);
+                           for (i = 0; i < box_width - 1; i++)
+                               waddch (dialog, instr[scroll + i]);
+                       } else {
+                           wmove (dialog, box_y, input_x++ + box_x);
+                           waddch (dialog, key);
+                       }
+                       wrefresh (dialog);
+                   } else
+                       flash ();       /* Alarm user about overflow */
+                   continue;
+               }
+           }
+       }
+       switch (key) {
+       case 'O':
+       case 'o':
+           delwin (dialog);
+           return 0;
+       case 'H':
+       case 'h':
+           delwin (dialog);
+           return 1;
+       case KEY_UP:
+       case KEY_LEFT:
+           switch (button) {
+           case -1:
+               button = 1;     /* Indicates "Cancel" button is selected */
+               print_buttons(dialog, height, width, 1);
+               break;
+           case 0:
+               button = -1;    /* Indicates input box is selected */
+               print_buttons(dialog, height, width, 0);
+               wmove (dialog, box_y, box_x + input_x);
+               wrefresh (dialog);
+               break;
+           case 1:
+               button = 0;     /* Indicates "OK" button is selected */
+               print_buttons(dialog, height, width, 0);
+               break;
+           }
+           break;
+       case TAB:
+       case KEY_DOWN:
+       case KEY_RIGHT:
+           switch (button) {
+           case -1:
+               button = 0;     /* Indicates "OK" button is selected */
+               print_buttons(dialog, height, width, 0);
+               break;
+           case 0:
+               button = 1;     /* Indicates "Cancel" button is selected */
+               print_buttons(dialog, height, width, 1);
+               break;
+           case 1:
+               button = -1;    /* Indicates input box is selected */
+               print_buttons(dialog, height, width, 0);
+               wmove (dialog, box_y, box_x + input_x);
+               wrefresh (dialog);
+               break;
+           }
+           break;
+       case ' ':
+       case '\n':
+           delwin (dialog);
+           return (button == -1 ? 0 : button);
+       case 'X':
+       case 'x':
+           key = ESC;
+       case ESC:
+           break;
+       }
+    }
+
+    delwin (dialog);
+    return -1;                 /* ESC pressed */
+}
diff --git a/busybox/scripts/config/lex.zconf.c_shipped b/busybox/scripts/config/lex.zconf.c_shipped
new file mode 100644 (file)
index 0000000..b877bb6
--- /dev/null
@@ -0,0 +1,3688 @@
+
+#line 3 "lex.zconf.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE zconfrestart(zconfin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int zconfleng;
+
+extern FILE *zconfin, *zconfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up zconftext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               *yy_cp = (yy_hold_char); \
+               YY_RESTORE_YY_MORE_OFFSET \
+               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via zconfrestart()), so that the user can continue scanning by
+        * just pointing zconfin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when zconftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+int zconfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow zconfwrap()'s to do buffer switches
+ * instead of setting up a fresh zconfin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void zconfrestart (FILE *input_file  );
+void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size  );
+void zconf_delete_buffer (YY_BUFFER_STATE b  );
+void zconf_flush_buffer (YY_BUFFER_STATE b  );
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void zconfpop_buffer_state (void );
+
+static void zconfensure_buffer_stack (void );
+static void zconf_load_buffer_state (void );
+static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len  );
+
+void *zconfalloc (yy_size_t  );
+void *zconfrealloc (void *,yy_size_t  );
+void zconffree (void *  );
+
+#define yy_new_buffer zconf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){ \
+        zconfensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){\
+        zconfensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define zconfwrap(n) 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[][38] =
+    {
+    {
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    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,   12,   12,   12,
+       12,   12,   12,   12,   12,   12,   12,   12,   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,   12,   12,   12,
+
+       12,   12,   12,   12,   12,   12,   12,   12,   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,   18,   18,   19,   20,
+       21,   22,   18,   18,   23,   24,   18,   25,   18,   26,
+       27,   18,   28,   29,   30,   18,   18,   16
+    },
+
+    {
+       11,   16,   16,   17,   16,   16,   16,   16,   16,   16,
+       16,   16,   16,   18,   16,   16,   18,   18,   19,   20,
+       21,   22,   18,   18,   23,   24,   18,   25,   18,   26,
+       27,   18,   28,   29,   30,   18,   18,   16
+
+    },
+
+    {
+       11,   31,   32,   33,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31
+    },
+
+    {
+       11,   31,   32,   33,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31
+    },
+
+    {
+       11,   34,   34,   35,   34,   36,   34,   34,   36,   34,
+       34,   34,   34,   34,   34,   37,   34,   34,   34,   34,
+
+       34,   34,   34,   34,   34,   34,   34,   34,   34,   34,
+       34,   34,   34,   34,   34,   34,   34,   34
+    },
+
+    {
+       11,   34,   34,   35,   34,   36,   34,   34,   36,   34,
+       34,   34,   34,   34,   34,   37,   34,   34,   34,   34,
+       34,   34,   34,   34,   34,   34,   34,   34,   34,   34,
+       34,   34,   34,   34,   34,   34,   34,   34
+    },
+
+    {
+       11,   38,   38,   39,   40,   41,   42,   43,   41,   44,
+       45,   46,   47,   47,   48,   49,   47,   47,   47,   47,
+       47,   47,   47,   47,   47,   50,   47,   47,   47,   51,
+       47,   47,   47,   47,   47,   47,   47,   52
+
+    },
+
+    {
+       11,   38,   38,   39,   40,   41,   42,   43,   41,   44,
+       45,   46,   47,   47,   48,   49,   47,   47,   47,   47,
+       47,   47,   47,   47,   47,   50,   47,   47,   47,   51,
+       47,   47,   47,   47,   47,   47,   47,   52
+    },
+
+    {
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -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,  -12,  -12,  -12,
+
+      -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12
+    },
+
+    {
+       11,  -13,   53,   54,  -13,  -13,   55,  -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,  -13,  -13,  -13,  -13,  -13
+    },
+
+    {
+       11,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14
+
+    },
+
+    {
+       11,   56,   56,   57,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56
+    },
+
+    {
+       11,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,
+      -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,
+      -16,  -16,  -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,  -17,  -17,  -17,
+
+      -17,  -17,  -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,   58,  -18,  -18,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -18
+    },
+
+    {
+       11,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,
+      -19,  -19,  -19,   58,  -19,  -19,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   59,
+       58,   58,   58,   58,   58,   58,   58,  -19
+
+    },
+
+    {
+       11,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,
+      -20,  -20,  -20,   58,  -20,  -20,   58,   58,   58,   58,
+       58,   58,   58,   58,   60,   58,   58,   58,   58,   61,
+       58,   58,   58,   58,   58,   58,   58,  -20
+    },
+
+    {
+       11,  -21,  -21,  -21,  -21,  -21,  -21,  -21,  -21,  -21,
+      -21,  -21,  -21,   58,  -21,  -21,   58,   58,   58,   58,
+       58,   62,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -21
+    },
+
+    {
+       11,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,
+      -22,  -22,  -22,   58,  -22,  -22,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   63,   58,
+       58,   58,   58,   58,   58,   58,   58,  -22
+    },
+
+    {
+       11,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,
+      -23,  -23,  -23,   58,  -23,  -23,   58,   58,   58,   58,
+       58,   64,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -23
+    },
+
+    {
+       11,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24,   58,  -24,  -24,   58,   58,   58,   58,
+       58,   58,   65,   58,   58,   58,   58,   58,   66,   58,
+       58,   58,   58,   58,   58,   58,   58,  -24
+
+    },
+
+    {
+       11,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,
+      -25,  -25,  -25,   58,  -25,  -25,   58,   67,   58,   58,
+       58,   68,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -25
+    },
+
+    {
+       11,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26,   58,  -26,  -26,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       69,   58,   58,   58,   58,   58,   58,  -26
+    },
+
+    {
+       11,  -27,  -27,  -27,  -27,  -27,  -27,  -27,  -27,  -27,
+      -27,  -27,  -27,   58,  -27,  -27,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   70,   58,   58,   58,   58,  -27
+    },
+
+    {
+       11,  -28,  -28,  -28,  -28,  -28,  -28,  -28,  -28,  -28,
+      -28,  -28,  -28,   58,  -28,  -28,   58,   71,   58,   58,
+       58,   72,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -28
+    },
+
+    {
+       11,  -29,  -29,  -29,  -29,  -29,  -29,  -29,  -29,  -29,
+      -29,  -29,  -29,   58,  -29,  -29,   58,   58,   58,   58,
+       58,   73,   58,   58,   58,   58,   58,   58,   58,   74,
+       58,   58,   58,   58,   75,   58,   58,  -29
+
+    },
+
+    {
+       11,  -30,  -30,  -30,  -30,  -30,  -30,  -30,  -30,  -30,
+      -30,  -30,  -30,   58,  -30,  -30,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   76,   58,   58,   58,   58,  -30
+    },
+
+    {
+       11,   77,   77,  -31,   77,   77,   77,   77,   77,   77,
+       77,   77,   77,   77,   77,   77,   77,   77,   77,   77,
+       77,   77,   77,   77,   77,   77,   77,   77,   77,   77,
+       77,   77,   77,   77,   77,   77,   77,   77
+    },
+
+    {
+       11,  -32,   78,   79,  -32,  -32,  -32,  -32,  -32,  -32,
+      -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,
+
+      -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,
+      -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32
+    },
+
+    {
+       11,   80,  -33,  -33,   80,   80,   80,   80,   80,   80,
+       80,   80,   80,   80,   80,   80,   80,   80,   80,   80,
+       80,   80,   80,   80,   80,   80,   80,   80,   80,   80,
+       80,   80,   80,   80,   80,   80,   80,   80
+    },
+
+    {
+       11,   81,   81,   82,   81,  -34,   81,   81,  -34,   81,
+       81,   81,   81,   81,   81,  -34,   81,   81,   81,   81,
+       81,   81,   81,   81,   81,   81,   81,   81,   81,   81,
+       81,   81,   81,   81,   81,   81,   81,   81
+
+    },
+
+    {
+       11,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,
+      -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,
+      -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,
+      -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35
+    },
+
+    {
+       11,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,
+      -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,
+      -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,
+      -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36
+    },
+
+    {
+       11,   83,   83,   84,   83,   83,   83,   83,   83,   83,
+       83,   83,   83,   83,   83,   83,   83,   83,   83,   83,
+
+       83,   83,   83,   83,   83,   83,   83,   83,   83,   83,
+       83,   83,   83,   83,   83,   83,   83,   83
+    },
+
+    {
+       11,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,
+      -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,
+      -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,
+      -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38
+    },
+
+    {
+       11,  -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,
+      -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,
+      -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39,
+      -39,  -39,  -39,  -39,  -39,  -39,  -39,  -39
+
+    },
+
+    {
+       11,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,   85,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40
+    },
+
+    {
+       11,  -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,
+      -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,
+      -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41,
+      -41,  -41,  -41,  -41,  -41,  -41,  -41,  -41
+    },
+
+    {
+       11,   86,   86,  -42,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86
+    },
+
+    {
+       11,  -43,  -43,  -43,  -43,  -43,  -43,   87,  -43,  -43,
+      -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -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,  -44,  -44,  -44,
+      -44,  -44,  -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,  -45,  -45,  -45,
+      -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,
+      -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45
+    },
+
+    {
+       11,  -46,  -46,  -46,  -46,  -46,  -46,  -46,  -46,  -46,
+      -46,   88,   89,   89,  -46,  -46,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -46
+    },
+
+    {
+       11,  -47,  -47,  -47,  -47,  -47,  -47,  -47,  -47,  -47,
+      -47,   89,   89,   89,  -47,  -47,   89,   89,   89,   89,
+
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -47
+    },
+
+    {
+       11,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48
+    },
+
+    {
+       11,  -49,  -49,   90,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49
+
+    },
+
+    {
+       11,  -50,  -50,  -50,  -50,  -50,  -50,  -50,  -50,  -50,
+      -50,   89,   89,   89,  -50,  -50,   89,   89,   89,   89,
+       89,   89,   91,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -50
+    },
+
+    {
+       11,  -51,  -51,  -51,  -51,  -51,  -51,  -51,  -51,  -51,
+      -51,   89,   89,   89,  -51,  -51,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,   89,   92,   89,
+       89,   89,   89,   89,   89,   89,   89,  -51
+    },
+
+    {
+       11,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+
+      -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52,   93
+    },
+
+    {
+       11,  -53,   53,   54,  -53,  -53,   55,  -53,  -53,  -53,
+      -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -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,  -54,  -54,  -54,
+      -54,  -54,  -54,  -54,  -54,  -54,  -54,  -54,  -54,  -54,
+      -54,  -54,  -54,  -54,  -54,  -54,  -54,  -54
+
+    },
+
+    {
+       11,   56,   56,   57,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56
+    },
+
+    {
+       11,   56,   56,   57,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56
+    },
+
+    {
+       11,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+
+      -57,  -57,  -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,   58,   58,   58,
+       58,   58,   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,   58,  -59,  -59,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   94,
+       58,   58,   58,   58,   58,   58,   58,  -59
+
+    },
+
+    {
+       11,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,  -60,  -60,   58,  -60,  -60,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   95,
+       58,   58,   58,   58,   58,   58,   58,  -60
+    },
+
+    {
+       11,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,
+      -61,  -61,  -61,   58,  -61,  -61,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   96,   97,   58,
+       58,   58,   58,   58,   58,   58,   58,  -61
+    },
+
+    {
+       11,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,
+      -62,  -62,  -62,   58,  -62,  -62,   58,   58,   58,   58,
+
+       58,   58,   98,   58,   58,   58,   58,   58,   58,   58,
+       99,   58,   58,   58,   58,   58,   58,  -62
+    },
+
+    {
+       11,  -63,  -63,  -63,  -63,  -63,  -63,  -63,  -63,  -63,
+      -63,  -63,  -63,   58,  -63,  -63,   58,  100,   58,   58,
+      101,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -63
+    },
+
+    {
+       11,  -64,  -64,  -64,  -64,  -64,  -64,  -64,  -64,  -64,
+      -64,  -64,  -64,   58,  -64,  -64,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  102,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  103,  -64
+
+    },
+
+    {
+       11,  -65,  -65,  -65,  -65,  -65,  -65,  -65,  -65,  -65,
+      -65,  -65,  -65,   58,  -65,  -65,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -65
+    },
+
+    {
+       11,  -66,  -66,  -66,  -66,  -66,  -66,  -66,  -66,  -66,
+      -66,  -66,  -66,   58,  -66,  -66,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  104,   58,   58,  -66
+    },
+
+    {
+       11,  -67,  -67,  -67,  -67,  -67,  -67,  -67,  -67,  -67,
+      -67,  -67,  -67,   58,  -67,  -67,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,  105,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -67
+    },
+
+    {
+       11,  -68,  -68,  -68,  -68,  -68,  -68,  -68,  -68,  -68,
+      -68,  -68,  -68,   58,  -68,  -68,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  106,   58,
+       58,   58,   58,   58,   58,   58,   58,  -68
+    },
+
+    {
+       11,  -69,  -69,  -69,  -69,  -69,  -69,  -69,  -69,  -69,
+      -69,  -69,  -69,   58,  -69,  -69,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  107,   58,   58,  -69
+
+    },
+
+    {
+       11,  -70,  -70,  -70,  -70,  -70,  -70,  -70,  -70,  -70,
+      -70,  -70,  -70,   58,  -70,  -70,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  108,
+       58,   58,   58,   58,   58,   58,   58,  -70
+    },
+
+    {
+       11,  -71,  -71,  -71,  -71,  -71,  -71,  -71,  -71,  -71,
+      -71,  -71,  -71,   58,  -71,  -71,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  109,   58,
+       58,   58,   58,   58,   58,   58,   58,  -71
+    },
+
+    {
+       11,  -72,  -72,  -72,  -72,  -72,  -72,  -72,  -72,  -72,
+      -72,  -72,  -72,   58,  -72,  -72,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,  110,   58,   58,   58,   58,   58,  -72
+    },
+
+    {
+       11,  -73,  -73,  -73,  -73,  -73,  -73,  -73,  -73,  -73,
+      -73,  -73,  -73,   58,  -73,  -73,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  111,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -73
+    },
+
+    {
+       11,  -74,  -74,  -74,  -74,  -74,  -74,  -74,  -74,  -74,
+      -74,  -74,  -74,   58,  -74,  -74,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  112,   58,  -74
+
+    },
+
+    {
+       11,  -75,  -75,  -75,  -75,  -75,  -75,  -75,  -75,  -75,
+      -75,  -75,  -75,   58,  -75,  -75,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  113,   58,   58,   58,   58,  -75
+    },
+
+    {
+       11,  -76,  -76,  -76,  -76,  -76,  -76,  -76,  -76,  -76,
+      -76,  -76,  -76,   58,  -76,  -76,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  114,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -76
+    },
+
+    {
+       11,   77,   77,  -77,   77,   77,   77,   77,   77,   77,
+       77,   77,   77,   77,   77,   77,   77,   77,   77,   77,
+
+       77,   77,   77,   77,   77,   77,   77,   77,   77,   77,
+       77,   77,   77,   77,   77,   77,   77,   77
+    },
+
+    {
+       11,  -78,   78,   79,  -78,  -78,  -78,  -78,  -78,  -78,
+      -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78,
+      -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78,
+      -78,  -78,  -78,  -78,  -78,  -78,  -78,  -78
+    },
+
+    {
+       11,   80,  -79,  -79,   80,   80,   80,   80,   80,   80,
+       80,   80,   80,   80,   80,   80,   80,   80,   80,   80,
+       80,   80,   80,   80,   80,   80,   80,   80,   80,   80,
+       80,   80,   80,   80,   80,   80,   80,   80
+
+    },
+
+    {
+       11,  -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,
+      -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,
+      -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80,
+      -80,  -80,  -80,  -80,  -80,  -80,  -80,  -80
+    },
+
+    {
+       11,   81,   81,   82,   81,  -81,   81,   81,  -81,   81,
+       81,   81,   81,   81,   81,  -81,   81,   81,   81,   81,
+       81,   81,   81,   81,   81,   81,   81,   81,   81,   81,
+       81,   81,   81,   81,   81,   81,   81,   81
+    },
+
+    {
+       11,  -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,
+      -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,
+
+      -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82,
+      -82,  -82,  -82,  -82,  -82,  -82,  -82,  -82
+    },
+
+    {
+       11,  -83,  -83,   84,  -83,  -83,  -83,  -83,  -83,  -83,
+      -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83,
+      -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83,
+      -83,  -83,  -83,  -83,  -83,  -83,  -83,  -83
+    },
+
+    {
+       11,  -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,
+      -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,
+      -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84,
+      -84,  -84,  -84,  -84,  -84,  -84,  -84,  -84
+
+    },
+
+    {
+       11,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85
+    },
+
+    {
+       11,   86,   86,  -86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86
+    },
+
+    {
+       11,  -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,
+      -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,
+
+      -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87,
+      -87,  -87,  -87,  -87,  -87,  -87,  -87,  -87
+    },
+
+    {
+       11,  -88,  -88,  -88,  -88,  -88,  -88,  -88,  -88,  -88,
+      -88,  115,   89,   89,  -88,  -88,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -88
+    },
+
+    {
+       11,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,
+      -89,   89,   89,   89,  -89,  -89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -89
+
+    },
+
+    {
+       11,  -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,
+      -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,
+      -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90,
+      -90,  -90,  -90,  -90,  -90,  -90,  -90,  -90
+    },
+
+    {
+       11,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,
+      -91,   89,   89,   89,  -91,  -91,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -91
+    },
+
+    {
+       11,  -92,  -92,  -92,  -92,  -92,  -92,  -92,  -92,  -92,
+      -92,   89,   89,   89,  -92,  -92,   89,   89,   89,   89,
+
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,  -92
+    },
+
+    {
+       11,  -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,
+      -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,
+      -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93,
+      -93,  -93,  -93,  -93,  -93,  -93,  -93,  -93
+    },
+
+    {
+       11,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,
+      -94,  -94,  -94,   58,  -94,  -94,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  116,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -94
+
+    },
+
+    {
+       11,  -95,  -95,  -95,  -95,  -95,  -95,  -95,  -95,  -95,
+      -95,  -95,  -95,   58,  -95,  -95,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  117,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -95
+    },
+
+    {
+       11,  -96,  -96,  -96,  -96,  -96,  -96,  -96,  -96,  -96,
+      -96,  -96,  -96,   58,  -96,  -96,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  118,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -96
+    },
+
+    {
+       11,  -97,  -97,  -97,  -97,  -97,  -97,  -97,  -97,  -97,
+      -97,  -97,  -97,   58,  -97,  -97,   58,   58,   58,   58,
+
+       58,   58,  119,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -97
+    },
+
+    {
+       11,  -98,  -98,  -98,  -98,  -98,  -98,  -98,  -98,  -98,
+      -98,  -98,  -98,   58,  -98,  -98,  120,  121,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -98
+    },
+
+    {
+       11,  -99,  -99,  -99,  -99,  -99,  -99,  -99,  -99,  -99,
+      -99,  -99,  -99,   58,  -99,  -99,   58,   58,   58,   58,
+       58,  122,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  -99
+
+    },
+
+    {
+       11, -100, -100, -100, -100, -100, -100, -100, -100, -100,
+     -100, -100, -100,   58, -100, -100,   58,   58,  123,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -100
+    },
+
+    {
+       11, -101, -101, -101, -101, -101, -101, -101, -101, -101,
+     -101, -101, -101,   58, -101, -101,   58,   58,   58,  124,
+       58,   58,   58,   58,   58,  125,   58,  126,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -101
+    },
+
+    {
+       11, -102, -102, -102, -102, -102, -102, -102, -102, -102,
+     -102, -102, -102,   58, -102, -102,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      127,   58,   58,   58,   58,   58,   58, -102
+    },
+
+    {
+       11, -103, -103, -103, -103, -103, -103, -103, -103, -103,
+     -103, -103, -103,   58, -103, -103,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -103
+    },
+
+    {
+       11, -104, -104, -104, -104, -104, -104, -104, -104, -104,
+     -104, -104, -104,   58, -104, -104,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -104
+
+    },
+
+    {
+       11, -105, -105, -105, -105, -105, -105, -105, -105, -105,
+     -105, -105, -105,   58, -105, -105,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  128,   58,
+       58,   58,   58,   58,   58,   58,   58, -105
+    },
+
+    {
+       11, -106, -106, -106, -106, -106, -106, -106, -106, -106,
+     -106, -106, -106,   58, -106, -106,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  129,   58, -106
+    },
+
+    {
+       11, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+     -107, -107, -107,   58, -107, -107,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,  130,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -107
+    },
+
+    {
+       11, -108, -108, -108, -108, -108, -108, -108, -108, -108,
+     -108, -108, -108,   58, -108, -108,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  131,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -108
+    },
+
+    {
+       11, -109, -109, -109, -109, -109, -109, -109, -109, -109,
+     -109, -109, -109,   58, -109, -109,   58,   58,   58,   58,
+       58,   58,   58,  132,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -109
+
+    },
+
+    {
+       11, -110, -110, -110, -110, -110, -110, -110, -110, -110,
+     -110, -110, -110,   58, -110, -110,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  133,   58, -110
+    },
+
+    {
+       11, -111, -111, -111, -111, -111, -111, -111, -111, -111,
+     -111, -111, -111,   58, -111, -111,   58,   58,   58,   58,
+       58,  134,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -111
+    },
+
+    {
+       11, -112, -112, -112, -112, -112, -112, -112, -112, -112,
+     -112, -112, -112,   58, -112, -112,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  135,   58,   58,   58,   58, -112
+    },
+
+    {
+       11, -113, -113, -113, -113, -113, -113, -113, -113, -113,
+     -113, -113, -113,   58, -113, -113,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  136,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -113
+    },
+
+    {
+       11, -114, -114, -114, -114, -114, -114, -114, -114, -114,
+     -114, -114, -114,   58, -114, -114,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  137,   58,   58,   58, -114
+
+    },
+
+    {
+       11, -115, -115, -115, -115, -115, -115, -115, -115, -115,
+     -115,   89,   89,   89, -115, -115,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89,   89,   89,   89,
+       89,   89,   89,   89,   89,   89,   89, -115
+    },
+
+    {
+       11, -116, -116, -116, -116, -116, -116, -116, -116, -116,
+     -116, -116, -116,   58, -116, -116,   58,   58,   58,   58,
+       58,  138,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -116
+    },
+
+    {
+       11, -117, -117, -117, -117, -117, -117, -117, -117, -117,
+     -117, -117, -117,   58, -117, -117,   58,   58,   58,  139,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -117
+    },
+
+    {
+       11, -118, -118, -118, -118, -118, -118, -118, -118, -118,
+     -118, -118, -118,   58, -118, -118,   58,   58,   58,   58,
+       58,  140,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -118
+    },
+
+    {
+       11, -119, -119, -119, -119, -119, -119, -119, -119, -119,
+     -119, -119, -119,   58, -119, -119,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  141,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -119
+
+    },
+
+    {
+       11, -120, -120, -120, -120, -120, -120, -120, -120, -120,
+     -120, -120, -120,   58, -120, -120,   58,   58,  142,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  143,   58,   58, -120
+    },
+
+    {
+       11, -121, -121, -121, -121, -121, -121, -121, -121, -121,
+     -121, -121, -121,   58, -121, -121,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  144,   58, -121
+    },
+
+    {
+       11, -122, -122, -122, -122, -122, -122, -122, -122, -122,
+     -122, -122, -122,   58, -122, -122,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,  145,   58,
+       58,   58,   58,   58,   58,   58,   58, -122
+    },
+
+    {
+       11, -123, -123, -123, -123, -123, -123, -123, -123, -123,
+     -123, -123, -123,   58, -123, -123,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  146,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -123
+    },
+
+    {
+       11, -124, -124, -124, -124, -124, -124, -124, -124, -124,
+     -124, -124, -124,   58, -124, -124,   58,   58,   58,   58,
+       58,   58,   58,   58,  147,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -124
+
+    },
+
+    {
+       11, -125, -125, -125, -125, -125, -125, -125, -125, -125,
+     -125, -125, -125,   58, -125, -125,   58,   58,   58,   58,
+       58,   58,  148,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -125
+    },
+
+    {
+       11, -126, -126, -126, -126, -126, -126, -126, -126, -126,
+     -126, -126, -126,   58, -126, -126,   58,   58,   58,   58,
+       58,  149,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -126
+    },
+
+    {
+       11, -127, -127, -127, -127, -127, -127, -127, -127, -127,
+     -127, -127, -127,   58, -127, -127,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -127
+    },
+
+    {
+       11, -128, -128, -128, -128, -128, -128, -128, -128, -128,
+     -128, -128, -128,   58, -128, -128,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  150,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -128
+    },
+
+    {
+       11, -129, -129, -129, -129, -129, -129, -129, -129, -129,
+     -129, -129, -129,   58, -129, -129,   58,   58,   58,  151,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -129
+
+    },
+
+    {
+       11, -130, -130, -130, -130, -130, -130, -130, -130, -130,
+     -130, -130, -130,   58, -130, -130,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  152,
+       58,   58,   58,   58,   58,   58,   58, -130
+    },
+
+    {
+       11, -131, -131, -131, -131, -131, -131, -131, -131, -131,
+     -131, -131, -131,   58, -131, -131,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      153,   58,   58,   58,   58,   58,   58, -131
+    },
+
+    {
+       11, -132, -132, -132, -132, -132, -132, -132, -132, -132,
+     -132, -132, -132,   58, -132, -132,   58,   58,   58,   58,
+
+       58,  154,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -132
+    },
+
+    {
+       11, -133, -133, -133, -133, -133, -133, -133, -133, -133,
+     -133, -133, -133,   58, -133, -133,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  155,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -133
+    },
+
+    {
+       11, -134, -134, -134, -134, -134, -134, -134, -134, -134,
+     -134, -134, -134,   58, -134, -134,   58,   58,   58,  156,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -134
+
+    },
+
+    {
+       11, -135, -135, -135, -135, -135, -135, -135, -135, -135,
+     -135, -135, -135,   58, -135, -135,   58,   58,   58,  157,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -135
+    },
+
+    {
+       11, -136, -136, -136, -136, -136, -136, -136, -136, -136,
+     -136, -136, -136,   58, -136, -136,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  158,   58,
+       58,   58,   58,   58,   58,   58,   58, -136
+    },
+
+    {
+       11, -137, -137, -137, -137, -137, -137, -137, -137, -137,
+     -137, -137, -137,   58, -137, -137,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  159,   58,   58, -137
+    },
+
+    {
+       11, -138, -138, -138, -138, -138, -138, -138, -138, -138,
+     -138, -138, -138,   58, -138, -138,   58,  160,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -138
+    },
+
+    {
+       11, -139, -139, -139, -139, -139, -139, -139, -139, -139,
+     -139, -139, -139,   58, -139, -139,   58,   58,   58,   58,
+       58,  161,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -139
+
+    },
+
+    {
+       11, -140, -140, -140, -140, -140, -140, -140, -140, -140,
+     -140, -140, -140,   58, -140, -140,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  162,   58,
+       58,   58,   58,   58,   58,   58,   58, -140
+    },
+
+    {
+       11, -141, -141, -141, -141, -141, -141, -141, -141, -141,
+     -141, -141, -141,   58, -141, -141,   58,   58,   58,   58,
+       58,   58,   58,  163,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -141
+    },
+
+    {
+       11, -142, -142, -142, -142, -142, -142, -142, -142, -142,
+     -142, -142, -142,   58, -142, -142,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  164,
+       58,   58,   58,   58,   58,   58,   58, -142
+    },
+
+    {
+       11, -143, -143, -143, -143, -143, -143, -143, -143, -143,
+     -143, -143, -143,   58, -143, -143,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  165,   58,   58,   58,   58, -143
+    },
+
+    {
+       11, -144, -144, -144, -144, -144, -144, -144, -144, -144,
+     -144, -144, -144,   58, -144, -144,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  166,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -144
+
+    },
+
+    {
+       11, -145, -145, -145, -145, -145, -145, -145, -145, -145,
+     -145, -145, -145,   58, -145, -145,   58,   58,   58,   58,
+      167,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -145
+    },
+
+    {
+       11, -146, -146, -146, -146, -146, -146, -146, -146, -146,
+     -146, -146, -146,   58, -146, -146,   58,   58,   58,   58,
+       58,  168,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -146
+    },
+
+    {
+       11, -147, -147, -147, -147, -147, -147, -147, -147, -147,
+     -147, -147, -147,   58, -147, -147,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  169,
+       58,   58,   58,   58,   58,   58,   58, -147
+    },
+
+    {
+       11, -148, -148, -148, -148, -148, -148, -148, -148, -148,
+     -148, -148, -148,   58, -148, -148,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -148
+    },
+
+    {
+       11, -149, -149, -149, -149, -149, -149, -149, -149, -149,
+     -149, -149, -149,   58, -149, -149,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  170,   58,
+       58,   58,   58,   58,   58,   58,   58, -149
+
+    },
+
+    {
+       11, -150, -150, -150, -150, -150, -150, -150, -150, -150,
+     -150, -150, -150,   58, -150, -150,   58,   58,   58,   58,
+       58,  171,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -150
+    },
+
+    {
+       11, -151, -151, -151, -151, -151, -151, -151, -151, -151,
+     -151, -151, -151,   58, -151, -151,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  172,
+       58,   58,   58,   58,   58,   58,   58, -151
+    },
+
+    {
+       11, -152, -152, -152, -152, -152, -152, -152, -152, -152,
+     -152, -152, -152,   58, -152, -152,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,  173,   58,
+       58,   58,   58,   58,   58,   58,   58, -152
+    },
+
+    {
+       11, -153, -153, -153, -153, -153, -153, -153, -153, -153,
+     -153, -153, -153,   58, -153, -153,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  174,   58,   58, -153
+    },
+
+    {
+       11, -154, -154, -154, -154, -154, -154, -154, -154, -154,
+     -154, -154, -154,   58, -154, -154,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -154
+
+    },
+
+    {
+       11, -155, -155, -155, -155, -155, -155, -155, -155, -155,
+     -155, -155, -155,   58, -155, -155,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  175,   58,   58,   58,   58, -155
+    },
+
+    {
+       11, -156, -156, -156, -156, -156, -156, -156, -156, -156,
+     -156, -156, -156,   58, -156, -156,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  176,   58,   58, -156
+    },
+
+    {
+       11, -157, -157, -157, -157, -157, -157, -157, -157, -157,
+     -157, -157, -157,   58, -157, -157,   58,   58,   58,   58,
+
+       58,  177,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -157
+    },
+
+    {
+       11, -158, -158, -158, -158, -158, -158, -158, -158, -158,
+     -158, -158, -158,   58, -158, -158,   58,   58,   58,   58,
+       58,   58,   58,  178,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -158
+    },
+
+    {
+       11, -159, -159, -159, -159, -159, -159, -159, -159, -159,
+     -159, -159, -159,   58, -159, -159,   58,  179,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -159
+
+    },
+
+    {
+       11, -160, -160, -160, -160, -160, -160, -160, -160, -160,
+     -160, -160, -160,   58, -160, -160,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  180,   58,
+       58,   58,   58,   58,   58,   58,   58, -160
+    },
+
+    {
+       11, -161, -161, -161, -161, -161, -161, -161, -161, -161,
+     -161, -161, -161,   58, -161, -161,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -161
+    },
+
+    {
+       11, -162, -162, -162, -162, -162, -162, -162, -162, -162,
+     -162, -162, -162,   58, -162, -162,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  181,   58,   58, -162
+    },
+
+    {
+       11, -163, -163, -163, -163, -163, -163, -163, -163, -163,
+     -163, -163, -163,   58, -163, -163,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -163
+    },
+
+    {
+       11, -164, -164, -164, -164, -164, -164, -164, -164, -164,
+     -164, -164, -164,   58, -164, -164,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  182,
+       58,   58,   58,   58,   58,   58,   58, -164
+
+    },
+
+    {
+       11, -165, -165, -165, -165, -165, -165, -165, -165, -165,
+     -165, -165, -165,   58, -165, -165,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  183,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -165
+    },
+
+    {
+       11, -166, -166, -166, -166, -166, -166, -166, -166, -166,
+     -166, -166, -166,   58, -166, -166,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  184,   58,   58, -166
+    },
+
+    {
+       11, -167, -167, -167, -167, -167, -167, -167, -167, -167,
+     -167, -167, -167,   58, -167, -167,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  185,   58,   58,   58, -167
+    },
+
+    {
+       11, -168, -168, -168, -168, -168, -168, -168, -168, -168,
+     -168, -168, -168,   58, -168, -168,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -168
+    },
+
+    {
+       11, -169, -169, -169, -169, -169, -169, -169, -169, -169,
+     -169, -169, -169,   58, -169, -169,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  186,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -169
+
+    },
+
+    {
+       11, -170, -170, -170, -170, -170, -170, -170, -170, -170,
+     -170, -170, -170,   58, -170, -170,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  187,   58, -170
+    },
+
+    {
+       11, -171, -171, -171, -171, -171, -171, -171, -171, -171,
+     -171, -171, -171,   58, -171, -171,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  188,   58,
+       58,   58,   58,   58,   58,   58,   58, -171
+    },
+
+    {
+       11, -172, -172, -172, -172, -172, -172, -172, -172, -172,
+     -172, -172, -172,   58, -172, -172,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,  189,   58,
+       58,   58,   58,   58,   58,   58,   58, -172
+    },
+
+    {
+       11, -173, -173, -173, -173, -173, -173, -173, -173, -173,
+     -173, -173, -173,   58, -173, -173,   58,  190,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -173
+    },
+
+    {
+       11, -174, -174, -174, -174, -174, -174, -174, -174, -174,
+     -174, -174, -174,   58, -174, -174,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -174
+
+    },
+
+    {
+       11, -175, -175, -175, -175, -175, -175, -175, -175, -175,
+     -175, -175, -175,   58, -175, -175,   58,   58,   58,   58,
+       58,  191,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -175
+    },
+
+    {
+       11, -176, -176, -176, -176, -176, -176, -176, -176, -176,
+     -176, -176, -176,   58, -176, -176,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -176
+    },
+
+    {
+       11, -177, -177, -177, -177, -177, -177, -177, -177, -177,
+     -177, -177, -177,   58, -177, -177,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -177
+    },
+
+    {
+       11, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+     -178, -178, -178,   58, -178, -178,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -178
+    },
+
+    {
+       11, -179, -179, -179, -179, -179, -179, -179, -179, -179,
+     -179, -179, -179,   58, -179, -179,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  192,   58,   58, -179
+
+    },
+
+    {
+       11, -180, -180, -180, -180, -180, -180, -180, -180, -180,
+     -180, -180, -180,   58, -180, -180,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -180
+    },
+
+    {
+       11, -181, -181, -181, -181, -181, -181, -181, -181, -181,
+     -181, -181, -181,   58, -181, -181,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -181
+    },
+
+    {
+       11, -182, -182, -182, -182, -182, -182, -182, -182, -182,
+     -182, -182, -182,   58, -182, -182,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,  193,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -182
+    },
+
+    {
+       11, -183, -183, -183, -183, -183, -183, -183, -183, -183,
+     -183, -183, -183,   58, -183, -183,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  194,   58,   58,   58, -183
+    },
+
+    {
+       11, -184, -184, -184, -184, -184, -184, -184, -184, -184,
+     -184, -184, -184,   58, -184, -184,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -184
+
+    },
+
+    {
+       11, -185, -185, -185, -185, -185, -185, -185, -185, -185,
+     -185, -185, -185,   58, -185, -185,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -185
+    },
+
+    {
+       11, -186, -186, -186, -186, -186, -186, -186, -186, -186,
+     -186, -186, -186,   58, -186, -186,   58,   58,   58,  195,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -186
+    },
+
+    {
+       11, -187, -187, -187, -187, -187, -187, -187, -187, -187,
+     -187, -187, -187,   58, -187, -187,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -187
+    },
+
+    {
+       11, -188, -188, -188, -188, -188, -188, -188, -188, -188,
+     -188, -188, -188,   58, -188, -188,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  196,   58, -188
+    },
+
+    {
+       11, -189, -189, -189, -189, -189, -189, -189, -189, -189,
+     -189, -189, -189,   58, -189, -189,   58,   58,   58,   58,
+       58,   58,  197,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -189
+
+    },
+
+    {
+       11, -190, -190, -190, -190, -190, -190, -190, -190, -190,
+     -190, -190, -190,   58, -190, -190,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  198,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -190
+    },
+
+    {
+       11, -191, -191, -191, -191, -191, -191, -191, -191, -191,
+     -191, -191, -191,   58, -191, -191,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  199,   58,   58,   58, -191
+    },
+
+    {
+       11, -192, -192, -192, -192, -192, -192, -192, -192, -192,
+     -192, -192, -192,   58, -192, -192,   58,   58,   58,   58,
+
+       58,  200,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -192
+    },
+
+    {
+       11, -193, -193, -193, -193, -193, -193, -193, -193, -193,
+     -193, -193, -193,   58, -193, -193,   58,   58,   58,   58,
+       58,  201,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -193
+    },
+
+    {
+       11, -194, -194, -194, -194, -194, -194, -194, -194, -194,
+     -194, -194, -194,   58, -194, -194,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  202,   58,   58, -194
+
+    },
+
+    {
+       11, -195, -195, -195, -195, -195, -195, -195, -195, -195,
+     -195, -195, -195,   58, -195, -195,   58,   58,   58,   58,
+       58,  203,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -195
+    },
+
+    {
+       11, -196, -196, -196, -196, -196, -196, -196, -196, -196,
+     -196, -196, -196,   58, -196, -196,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -196
+    },
+
+    {
+       11, -197, -197, -197, -197, -197, -197, -197, -197, -197,
+     -197, -197, -197,   58, -197, -197,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,  204,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -197
+    },
+
+    {
+       11, -198, -198, -198, -198, -198, -198, -198, -198, -198,
+     -198, -198, -198,   58, -198, -198,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -198
+    },
+
+    {
+       11, -199, -199, -199, -199, -199, -199, -199, -199, -199,
+     -199, -199, -199,   58, -199, -199,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -199
+
+    },
+
+    {
+       11, -200, -200, -200, -200, -200, -200, -200, -200, -200,
+     -200, -200, -200,   58, -200, -200,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -200
+    },
+
+    {
+       11, -201, -201, -201, -201, -201, -201, -201, -201, -201,
+     -201, -201, -201,   58, -201, -201,   58,  205,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -201
+    },
+
+    {
+       11, -202, -202, -202, -202, -202, -202, -202, -202, -202,
+     -202, -202, -202,   58, -202, -202,   58,  206,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -202
+    },
+
+    {
+       11, -203, -203, -203, -203, -203, -203, -203, -203, -203,
+     -203, -203, -203,   58, -203, -203,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -203
+    },
+
+    {
+       11, -204, -204, -204, -204, -204, -204, -204, -204, -204,
+     -204, -204, -204,   58, -204, -204,   58,   58,   58,   58,
+       58,   58,   58,  207,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -204
+
+    },
+
+    {
+       11, -205, -205, -205, -205, -205, -205, -205, -205, -205,
+     -205, -205, -205,   58, -205, -205,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  208,   58,
+       58,   58,   58,   58,   58,   58,   58, -205
+    },
+
+    {
+       11, -206, -206, -206, -206, -206, -206, -206, -206, -206,
+     -206, -206, -206,   58, -206, -206,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  209,   58,   58, -206
+    },
+
+    {
+       11, -207, -207, -207, -207, -207, -207, -207, -207, -207,
+     -207, -207, -207,   58, -207, -207,   58,   58,   58,   58,
+
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -207
+    },
+
+    {
+       11, -208, -208, -208, -208, -208, -208, -208, -208, -208,
+     -208, -208, -208,   58, -208, -208,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -208
+    },
+
+    {
+       11, -209, -209, -209, -209, -209, -209, -209, -209, -209,
+     -209, -209, -209,   58, -209, -209,   58,   58,   58,   58,
+       58,  210,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -209
+
+    },
+
+    {
+       11, -210, -210, -210, -210, -210, -210, -210, -210, -210,
+     -210, -210, -210,   58, -210, -210,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58, -210
+    },
+
+    } ;
+
+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 64
+#define YY_END_OF_BUFFER 65
+/* 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[211] =
+    {   0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+       65,    5,    4,    3,    2,   36,   37,   35,   35,   35,
+       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
+       63,   60,   62,   55,   59,   58,   57,   53,   48,   42,
+       47,   51,   53,   40,   41,   50,   50,   43,   53,   50,
+       50,   53,    4,    3,    2,    2,    1,   35,   35,   35,
+       35,   35,   35,   35,   16,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   35,   63,   60,   62,   61,
+       55,   54,   57,   56,   44,   51,   38,   50,   50,   52,
+       45,   46,   39,   35,   35,   35,   35,   35,   35,   35,
+
+       35,   35,   30,   29,   35,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   49,   25,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   35,   15,   35,    7,   35,
+       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   35,   35,   17,   35,   35,
+       35,   35,   35,   34,   35,   35,   35,   35,   35,   35,
+       10,   35,   13,   35,   35,   35,   35,   33,   35,   35,
+       35,   35,   35,   22,   35,   32,    9,   31,   35,   26,
+       12,   35,   35,   21,   18,   35,    8,   35,   35,   35,
+       35,   35,   27,   35,   35,    6,   35,   20,   19,   23,
+
+       35,   35,   11,   35,   35,   35,   14,   28,   35,   24
+    } ;
+
+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,   16,    1,   17,   18,   19,   20,
+
+       21,   22,   23,   24,   25,   13,   13,   26,   27,   28,
+       29,   30,   31,   32,   33,   34,   35,   13,   13,   36,
+       13,   13,    1,   37,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    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
+
+char *text;
+static char *text_ptr;
+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 struct buffer *zconf_endfile(void);
+
+void new_string(void)
+{
+       text = malloc(START_STRSIZE);
+       text_asize = START_STRSIZE;
+       text_ptr = text;
+       text_size = 0;
+       *text_ptr = 0;
+}
+
+void append_string(const char *str, int size)
+{
+       int new_size = text_size + size + 1;
+       if (new_size > text_asize) {
+               text = realloc(text, new_size);
+               text_asize = new_size;
+               text_ptr = text + text_size;
+       }
+       memcpy(text_ptr, str, size);
+       text_ptr += size;
+       text_size += size;
+       *text_ptr = 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
+
+/* 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>
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int zconfwrap (void );
+#else
+extern int zconfwrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       errno=0; \
+       while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
+       { \
+               if( errno != EINTR) \
+               { \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+                       break; \
+               } \
+               errno=0; \
+               clearerr(zconfin); \
+       }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int zconflex (void);
+
+#define YY_DECL int zconflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after zconftext and zconfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+
+       int str = 0;
+       int ts, i;
+
+       if ( (yy_init) )
+               {
+               (yy_init) = 0;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! (yy_start) )
+                       (yy_start) = 1; /* first start state */
+
+               if ( ! zconfin )
+                       zconfin = stdin;
+
+               if ( ! zconfout )
+                       zconfout = stdout;
+
+               if ( ! YY_CURRENT_BUFFER ) {
+                       zconfensure_buffer_stack ();
+                       YY_CURRENT_BUFFER_LVALUE =
+                               zconf_create_buffer(zconfin,YY_BUF_SIZE );
+               }
+
+               zconf_load_buffer_state( );
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = (yy_c_buf_p);
+
+               /* Support of zconftext. */
+               *yy_cp = (yy_hold_char);
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = (yy_start);
+yy_match:
+               while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)]  ]) > 0 )
+                       ++yy_cp;
+
+               yy_current_state = -yy_current_state;
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+
+               YY_DO_BEFORE_ACTION;
+
+do_action:     /* This label is used only to access EOF actions. */
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+       YY_BREAK
+case 2:
+YY_RULE_SETUP
+
+       YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+current_file->lineno++; return T_EOL;
+       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
+BEGIN(PARAM); return T_MAINMENU;
+       YY_BREAK
+case 7:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_MENU;
+       YY_BREAK
+case 8:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_ENDMENU;
+       YY_BREAK
+case 9:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_SOURCE;
+       YY_BREAK
+case 10:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_CHOICE;
+       YY_BREAK
+case 11:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_ENDCHOICE;
+       YY_BREAK
+case 12:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_COMMENT;
+       YY_BREAK
+case 13:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_CONFIG;
+       YY_BREAK
+case 14:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_MENUCONFIG;
+       YY_BREAK
+case 15:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_HELP;
+       YY_BREAK
+case 16:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_IF;
+       YY_BREAK
+case 17:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_ENDIF;
+       YY_BREAK
+case 18:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_DEPENDS;
+       YY_BREAK
+case 19:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_REQUIRES;
+       YY_BREAK
+case 20:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_OPTIONAL;
+       YY_BREAK
+case 21:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_DEFAULT;
+       YY_BREAK
+case 22:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_PROMPT;
+       YY_BREAK
+case 23:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_TRISTATE;
+       YY_BREAK
+case 24:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_DEF_TRISTATE;
+       YY_BREAK
+case 25:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_BOOLEAN;
+       YY_BREAK
+case 26:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_BOOLEAN;
+       YY_BREAK
+case 27:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_DEF_BOOLEAN;
+       YY_BREAK
+case 28:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_DEF_BOOLEAN;
+       YY_BREAK
+case 29:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_INT;
+       YY_BREAK
+case 30:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_HEX;
+       YY_BREAK
+case 31:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_STRING;
+       YY_BREAK
+case 32:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_SELECT;
+       YY_BREAK
+case 33:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_SELECT;
+       YY_BREAK
+case 34:
+YY_RULE_SETUP
+BEGIN(PARAM); return T_RANGE;
+       YY_BREAK
+case 35:
+YY_RULE_SETUP
+{
+               alloc_string(zconftext, zconfleng);
+               zconflval.string = text;
+               return T_WORD;
+       }
+       YY_BREAK
+case 36:
+YY_RULE_SETUP
+
+       YY_BREAK
+case 37:
+/* rule 37 can match eol */
+YY_RULE_SETUP
+current_file->lineno++; BEGIN(INITIAL);
+       YY_BREAK
+
+case 38:
+YY_RULE_SETUP
+return T_AND;
+       YY_BREAK
+case 39:
+YY_RULE_SETUP
+return T_OR;
+       YY_BREAK
+case 40:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+       YY_BREAK
+case 41:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+       YY_BREAK
+case 42:
+YY_RULE_SETUP
+return T_NOT;
+       YY_BREAK
+case 43:
+YY_RULE_SETUP
+return T_EQUAL;
+       YY_BREAK
+case 44:
+YY_RULE_SETUP
+return T_UNEQUAL;
+       YY_BREAK
+case 45:
+YY_RULE_SETUP
+return T_IF;
+       YY_BREAK
+case 46:
+YY_RULE_SETUP
+return T_ON;
+       YY_BREAK
+case 47:
+YY_RULE_SETUP
+{
+               str = zconftext[0];
+               new_string();
+               BEGIN(STRING);
+       }
+       YY_BREAK
+case 48:
+/* rule 48 can match eol */
+YY_RULE_SETUP
+BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+       YY_BREAK
+case 49:
+YY_RULE_SETUP
+/* ignore */
+       YY_BREAK
+case 50:
+YY_RULE_SETUP
+{
+               alloc_string(zconftext, zconfleng);
+               zconflval.string = text;
+               return T_WORD;
+       }
+       YY_BREAK
+case 51:
+YY_RULE_SETUP
+/* comment */
+       YY_BREAK
+case 52:
+/* rule 52 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+       YY_BREAK
+case 53:
+YY_RULE_SETUP
+
+       YY_BREAK
+case YY_STATE_EOF(PARAM):
+{
+               BEGIN(INITIAL);
+       }
+       YY_BREAK
+
+case 54:
+/* rule 54 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 55:
+YY_RULE_SETUP
+{
+               append_string(zconftext, zconfleng);
+       }
+       YY_BREAK
+case 56:
+/* rule 56 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 57:
+YY_RULE_SETUP
+{
+               append_string(zconftext + 1, zconfleng - 1);
+       }
+       YY_BREAK
+case 58:
+YY_RULE_SETUP
+{
+               if (str == zconftext[0]) {
+                       BEGIN(PARAM);
+                       zconflval.string = text;
+                       return T_WORD_QUOTE;
+               } else
+                       append_string(zconftext, 1);
+       }
+       YY_BREAK
+case 59:
+/* rule 59 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 60:
+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 61:
+/* rule 61 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 62:
+/* rule 62 can match eol */
+YY_RULE_SETUP
+{
+               current_file->lineno++;
+               append_string("\n", 1);
+       }
+       YY_BREAK
+case 63:
+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_buf) {
+               zconf_endfile();
+               return T_EOF;
+       }
+       fclose(zconfin);
+       yyterminate();
+}
+       YY_BREAK
+case 64:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+       YY_BREAK
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = (yy_hold_char);
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed zconfin at a new source and called
+                        * zconflex().  If so, then we have to assure
+                        * consistency between YY_CURRENT_BUFFER and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state(  );
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++(yy_c_buf_p);
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = (yy_c_buf_p);
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer(  ) )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               (yy_did_buffer_switch_on_eof) = 0;
+
+                               if ( zconfwrap( ) )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * zconftext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               (yy_c_buf_p) =
+                                       (yytext_ptr) + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               (yy_c_buf_p) =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+} /* end of zconflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = (yytext_ptr);
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+       else
+               {
+                       size_t num_to_read =
+                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+                       int yy_c_buf_p_offset =
+                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+                                               number_to_move - 1;
+
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+                       (yy_n_chars), num_to_read );
+
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       if ( (yy_n_chars) == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       zconfrestart(zconfin  );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       (yy_n_chars) += number_to_move;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+       return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+
+       yy_current_state = (yy_start);
+
+       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+               {
+               yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+               }
+
+       return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+       register int yy_is_jam;
+
+       yy_current_state = yy_nxt[yy_current_state][1];
+       yy_is_jam = (yy_current_state <= 0);
+
+       return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+       register char *yy_cp;
+
+    yy_cp = (yy_c_buf_p);
+
+       /* undo effects of setting up zconftext */
+       *yy_cp = (yy_hold_char);
+
+       if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+               { /* need to shift things up to make room */
+               /* +2 for EOB chars. */
+               register int number_to_move = (yy_n_chars) + 2;
+               register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+                                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+               register char *source =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+               while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       *--dest = *--source;
+
+               yy_cp += (int) (dest - source);
+               yy_bp += (int) (dest - source);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+               if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+                       YY_FATAL_ERROR( "flex scanner push-back overflow" );
+               }
+
+       *--yy_cp = (char) c;
+
+       (yytext_ptr) = yy_bp;
+       (yy_hold_char) = *yy_cp;
+       (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+       int c;
+
+       *(yy_c_buf_p) = (yy_hold_char);
+
+       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       /* This was really a NUL. */
+                       *(yy_c_buf_p) = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = (yy_c_buf_p) - (yytext_ptr);
+                       ++(yy_c_buf_p);
+
+                       switch ( yy_get_next_buffer(  ) )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       zconfrestart(zconfin );
+
+                                       /*FALLTHROUGH*/
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( zconfwrap( ) )
+                                               return EOF;
+
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       (yy_c_buf_p) = (yytext_ptr) + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
+       *(yy_c_buf_p) = '\0';   /* preserve zconftext */
+       (yy_hold_char) = *++(yy_c_buf_p);
+
+       return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void zconfrestart  (FILE * input_file )
+{
+
+       if ( ! YY_CURRENT_BUFFER ){
+        zconfensure_buffer_stack ();
+               YY_CURRENT_BUFFER_LVALUE =
+            zconf_create_buffer(zconfin,YY_BUF_SIZE );
+       }
+
+       zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
+       zconf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+    void zconf_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+
+       /* TODO. We should be able to replace this entire function body
+        * with
+        *              zconfpop_buffer_state();
+        *              zconfpush_buffer_state(new_buffer);
+     */
+       zconfensure_buffer_stack ();
+       if ( YY_CURRENT_BUFFER == new_buffer )
+               return;
+
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+       zconf_load_buffer_state( );
+
+       /* We don't actually know whether we did this switch during
+        * EOF (zconfwrap()) processing, but the only time this flag
+        * is looked at is after zconfwrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void zconf_load_buffer_state  (void)
+{
+       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+       zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+       (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE zconf_create_buffer  (FILE * file, int  size )
+{
+       YY_BUFFER_STATE b;
+
+       b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2  );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       zconf_init_buffer(b,file );
+
+       return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with zconf_create_buffer()
+ *
+ */
+    void zconf_delete_buffer (YY_BUFFER_STATE  b )
+{
+
+       if ( ! b )
+               return;
+
+       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               zconffree((void *) b->yy_ch_buf  );
+
+       zconffree((void *) b  );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a zconfrestart() or at EOF.
+ */
+    static void zconf_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+       int oerrno = errno;
+
+       zconf_flush_buffer(b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then zconf_init_buffer was _probably_
+     * called from zconfrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = 0;
+
+       errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+    void zconf_flush_buffer (YY_BUFFER_STATE  b )
+{
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == YY_CURRENT_BUFFER )
+               zconf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *
+ */
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+       if (new_buffer == NULL)
+               return;
+
+       zconfensure_buffer_stack();
+
+       /* This block is copied from zconf_switch_to_buffer. */
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       /* Only push if top exists. Otherwise, replace top. */
+       if (YY_CURRENT_BUFFER)
+               (yy_buffer_stack_top)++;
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+       /* copied from zconf_switch_to_buffer. */
+       zconf_load_buffer_state( );
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *
+ */
+void zconfpop_buffer_state (void)
+{
+       if (!YY_CURRENT_BUFFER)
+               return;
+
+       zconf_delete_buffer(YY_CURRENT_BUFFER );
+       YY_CURRENT_BUFFER_LVALUE = NULL;
+       if ((yy_buffer_stack_top) > 0)
+               --(yy_buffer_stack_top);
+
+       if (YY_CURRENT_BUFFER) {
+               zconf_load_buffer_state( );
+               (yy_did_buffer_switch_on_eof) = 1;
+       }
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void zconfensure_buffer_stack (void)
+{
+       int num_to_alloc;
+
+       if (!(yy_buffer_stack)) {
+
+               /* First allocation is just for 2 elements, since we don't know if this
+                * scanner will even need a stack. We use 2 instead of 1 to avoid an
+                * immediate realloc on the next call.
+         */
+               num_to_alloc = 1;
+               (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
+                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+
+               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+               (yy_buffer_stack_max) = num_to_alloc;
+               (yy_buffer_stack_top) = 0;
+               return;
+       }
+
+       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+               /* Increase the buffer to prepare for a possible push. */
+               int grow_size = 8 /* arbitrary grow size */;
+
+               num_to_alloc = (yy_buffer_stack_max) + grow_size;
+               (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
+                                                               ((yy_buffer_stack),
+                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+
+               /* zero only the new slots.*/
+               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+               (yy_buffer_stack_max) = num_to_alloc;
+       }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_buffer  (char * base, yy_size_t  size )
+{
+       YY_BUFFER_STATE b;
+
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       zconf_switch_to_buffer(b  );
+
+       return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to zconflex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       zconf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE zconf_scan_string (yyconst char * str )
+{
+
+       return zconf_scan_bytes(str,strlen(str) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_bytes  (yyconst char * bytes, int  len )
+{
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = len + 2;
+       buf = (char *) zconfalloc(n  );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
+
+       for ( i = 0; i < len; ++i )
+               buf[i] = bytes[i];
+
+       buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = zconf_scan_buffer(buf,n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up zconftext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               zconftext[zconfleng] = (yy_hold_char); \
+               (yy_c_buf_p) = zconftext + yyless_macro_arg; \
+               (yy_hold_char) = *(yy_c_buf_p); \
+               *(yy_c_buf_p) = '\0'; \
+               zconfleng = yyless_macro_arg; \
+               } \
+       while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int zconfget_lineno  (void)
+{
+
+    return zconflineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *zconfget_in  (void)
+{
+        return zconfin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *zconfget_out  (void)
+{
+        return zconfout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int zconfget_leng  (void)
+{
+        return zconfleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *zconfget_text  (void)
+{
+        return zconftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void zconfset_lineno (int  line_number )
+{
+
+    zconflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see zconf_switch_to_buffer
+ */
+void zconfset_in (FILE *  in_str )
+{
+        zconfin = in_str ;
+}
+
+void zconfset_out (FILE *  out_str )
+{
+        zconfout = out_str ;
+}
+
+int zconfget_debug  (void)
+{
+        return zconf_flex_debug;
+}
+
+void zconfset_debug (int  bdebug )
+{
+        zconf_flex_debug = bdebug ;
+}
+
+/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
+int zconflex_destroy  (void)
+{
+
+    /* Pop the buffer stack, destroying each element. */
+       while(YY_CURRENT_BUFFER){
+               zconf_delete_buffer(YY_CURRENT_BUFFER  );
+               YY_CURRENT_BUFFER_LVALUE = NULL;
+               zconfpop_buffer_state();
+       }
+
+       /* Destroy the stack itself. */
+       zconffree((yy_buffer_stack) );
+       (yy_buffer_stack) = NULL;
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+}
+#endif
+
+void *zconfalloc (yy_size_t  size )
+{
+       return (void *) malloc( size );
+}
+
+void *zconfrealloc  (void * ptr, yy_size_t  size )
+{
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+}
+
+void zconffree (void * ptr )
+{
+       free( (char *) ptr );   /* see zconfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+void zconf_starthelp(void)
+{
+       new_string();
+       last_ts = first_ts = 0;
+       BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+       zconflval.string = text;
+       BEGIN(INITIAL);
+}
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+       char *env, 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 struct buffer *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;
+
+       return parent;
+}
+
+int zconf_lineno(void)
+{
+       if (current_buf)
+               return current_file->lineno - 1;
+       else
+               return 0;
+}
+
+char *zconf_curname(void)
+{
+       if (current_buf)
+               return current_file->name;
+       else
+               return "<none>";
+}
+
diff --git a/busybox/scripts/config/lkc.h b/busybox/scripts/config/lkc.h
new file mode 100644 (file)
index 0000000..dd040f7
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef LKC_H
+#define LKC_H
+
+#include "expr.h"
+
+#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"
+
+int zconfparse(void);
+void zconfdump(FILE *out);
+
+extern int zconfdebug;
+void zconf_starthelp(void);
+FILE *zconf_fopen(const char *name);
+void zconf_initscan(const char *name);
+void zconf_nextfile(const char *name);
+int zconf_lineno(void);
+char *zconf_curname(void);
+
+/* confdata.c */
+extern const char conf_def_filename[];
+extern char conf_filename[];
+
+char *conf_get_default_confname(void);
+
+/* kconfig_load.c */
+void kconfig_load(void);
+
+/* menu.c */
+void menu_init(void);
+void 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);
+void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+void menu_finalize(struct menu *parent);
+void menu_set_type(int type);
+struct file *file_lookup(const char *name);
+int file_write_dep(const char *name);
+
+extern struct menu *current_entry;
+extern struct menu *current_menu;
+
+/* symbol.c */
+void sym_init(void);
+void sym_clear_all_valid(void);
+void sym_set_changed(struct symbol *sym);
+struct symbol *sym_check_deps(struct symbol *sym);
+struct property *prop_alloc(enum prop_type type, struct symbol *sym);
+struct symbol *prop_get_symbol(struct property *prop);
+
+static inline tristate sym_get_tristate_value(struct symbol *sym)
+{
+       return sym->curr.tri;
+}
+
+
+static inline struct symbol *sym_get_choice_value(struct symbol *sym)
+{
+       return (struct symbol *)sym->curr.val;
+}
+
+static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
+{
+       return sym_set_tristate_value(chval, yes);
+}
+
+static inline bool sym_is_choice(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_CHOICE ? true : false;
+}
+
+static inline bool sym_is_choice_value(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_CHOICEVAL ? true : false;
+}
+
+static inline bool sym_is_optional(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_OPTIONAL ? true : false;
+}
+
+static inline bool sym_has_value(struct symbol *sym)
+{
+       return sym->flags & SYMBOL_NEW ? false : true;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LKC_H */
diff --git a/busybox/scripts/config/lkc_proto.h b/busybox/scripts/config/lkc_proto.h
new file mode 100644 (file)
index 0000000..97c7917
--- /dev/null
@@ -0,0 +1,39 @@
+
+/* confdata.c */
+P(conf_parse,void,(const char *name));
+P(conf_read,int,(const char *name));
+P(conf_write,int,(const char *name));
+
+/* menu.c */
+P(rootmenu,struct menu,);
+
+P(menu_is_visible,bool,(struct menu *menu));
+P(menu_get_prompt,const char *,(struct menu *menu));
+P(menu_get_root_menu,struct menu *,(struct menu *menu));
+P(menu_get_parent_menu,struct menu *,(struct menu *menu));
+
+/* symbol.c */
+P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]);
+P(sym_change_count,int,);
+
+P(sym_lookup,struct symbol *,(const char *name, int isconst));
+P(sym_find,struct symbol *,(const char *name));
+P(sym_type_name,const char *,(enum symbol_type type));
+P(sym_calc_value,void,(struct symbol *sym));
+P(sym_get_type,enum symbol_type,(struct symbol *sym));
+P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri));
+P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri));
+P(sym_toggle_tristate_value,tristate,(struct symbol *sym));
+P(sym_string_valid,bool,(struct symbol *sym, const char *newval));
+P(sym_string_within_range,bool,(struct symbol *sym, const char *str));
+P(sym_set_string_value,bool,(struct symbol *sym, const char *newval));
+P(sym_is_changable,bool,(struct symbol *sym));
+P(sym_get_choice_prop,struct property *,(struct symbol *sym));
+P(sym_get_default_prop,struct property *,(struct symbol *sym));
+P(sym_get_string_value,const char *,(struct symbol *sym));
+
+P(prop_get_type_name,const char *,(enum prop_type type));
+
+/* expr.c */
+P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2));
+P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken));
diff --git a/busybox/scripts/config/mconf.c b/busybox/scripts/config/mconf.c
new file mode 100644 (file)
index 0000000..63b4ff7
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * 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>
+ *
+ * Directly use liblxdialog library routines.
+ * 2002-11-14 Petr Baudis <pasky@ucw.cz>
+ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/termios.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 "dialog.h"
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static char menu_backtitle[128];
+static const char menu_instructions[] =
+       "Arrow keys navigate the menu.  "
+       "<Enter> selects submenus --->.  "
+       "Highlighted letters are hotkeys.  "
+       "Pressing <Y> selectes a feature, while <N> will exclude a feature.  "
+       "Press <Esc><Esc> to exit, <?> for Help.  "
+       "Legend: [*] feature is selected  [ ] feature is excluded",
+radiolist_instructions[] =
+       "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[] =
+       "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[] =
+       "Please enter a hexadecimal value. "
+       "Use the <TAB> key to move from the input field to the buttons below it.",
+inputbox_instructions_string[] =
+       "Please enter a string value. "
+       "Use the <TAB> key to move from the input field to the buttons below it.",
+setmod_text[] =
+       "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[] =
+       "There is no help available for this option.\n",
+load_config_text[] =
+       "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"
+       "For various reasons, one may wish to keep several different BusyBox\n"
+       "configurations available on a single machine.\n"
+       "\n"
+       "If you have saved a previous configuration in a file other than the\n"
+       "BusyBox's 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[] =
+       "Enter a filename to which this configuration should be saved "
+       "as an alternate.  Leave blank to abort.",
+save_config_help[] =
+       "\n"
+       "For various reasons, one may wish to keep different BusyBox\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",
+top_menu_help[] =
+       "\n"
+       "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"
+       "\n"
+       "You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
+       "unseen options into view.\n"
+;
+
+static char filename[PATH_MAX+1] = ".config";
+static int indent = 0;
+static struct termios ios_org;
+static int rows, cols;
+struct menu *current_menu;
+static int child_count;
+static int single_menu_mode;
+
+static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */
+static int item_no;
+
+static void conf(struct menu *menu);
+static void conf_choice(struct menu *menu);
+static void conf_string(struct menu *menu);
+static void conf_load(void);
+static void conf_save(void);
+static void show_textbox(const char *title, const char *text, int r, int c);
+static void show_helptext(const char *title, const char *text);
+static void show_help(struct menu *menu);
+static void show_readme(void);
+
+static void init_wsize(void)
+{
+       struct winsize ws;
+       char *env;
+
+       if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
+               rows = 24;
+               cols = 80;
+       } else {
+               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, "Your display is too small to run Menuconfig!\n");
+               fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
+               exit(1);
+       }
+
+       rows -= 4;
+       cols -= 5;
+}
+
+static void cinit(void)
+{
+       item_no = 0;
+}
+
+static void cmake(void)
+{
+       items[item_no] = malloc(sizeof(struct dialog_list_item));
+       memset(items[item_no], 0, sizeof(struct dialog_list_item));
+       items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0;
+       items[item_no]->name = malloc(512); items[item_no]->name[0] = 0;
+       items[item_no]->namelen = 0;
+       item_no++;
+}
+
+static int cprint_name(const char *fmt, ...)
+{
+       va_list ap;
+       int res;
+
+       if (!item_no)
+               cmake();
+       va_start(ap, fmt);
+       res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
+                       512 - items[item_no - 1]->namelen, fmt, ap);
+       if (res > 0)
+               items[item_no - 1]->namelen += res;
+       va_end(ap);
+
+       return res;
+}
+
+static int cprint_tag(const char *fmt, ...)
+{
+       va_list ap;
+       int res;
+
+       if (!item_no)
+               cmake();
+       va_start(ap, fmt);
+       res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
+       va_end(ap);
+
+       return res;
+}
+
+static void cdone(void)
+{
+       int i;
+
+       for (i = 0; i < item_no; i++) {
+               free(items[i]->tag);
+               free(items[i]->name);
+               free(items[i]);
+       }
+
+       item_no = 0;
+}
+
+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++;
+                               cmake();
+                               cprint_tag("m%p", menu);
+
+                               if (single_menu_mode) {
+                                       cprint_name("%s%*c%s",
+                                               menu->data ? "-->" : "++>",
+                                               indent + 1, ' ', prompt);
+                               } else {
+                                       cprint_name("   %*c%s  --->", indent + 1, ' ', prompt);
+                               }
+
+                               if (single_menu_mode && menu->data)
+                                       goto conf_childs;
+                               return;
+                       default:
+                               if (prompt) {
+                                       child_count++;
+                                       cmake();
+                                       cprint_tag(":%p", menu);
+                                       cprint_name("---%*c%s", indent + 1, ' ', prompt);
+                               }
+                       }
+               } else
+                       doint = 0;
+               goto conf_childs;
+       }
+
+       cmake();
+       type = sym_get_type(sym);
+       if (sym_is_choice(sym)) {
+               struct symbol *def_sym = sym_get_choice_value(sym);
+               struct menu *def_menu = NULL;
+
+               child_count++;
+               for (child = menu->list; child; child = child->next) {
+                       if (menu_is_visible(child) && child->sym == def_sym)
+                               def_menu = child;
+               }
+
+               val = sym_get_tristate_value(sym);
+               if (sym_is_changable(sym)) {
+                       cprint_tag("t%p", menu);
+                       switch (type) {
+                       case S_BOOLEAN:
+                               cprint_name("[%c]", val == no ? ' ' : '*');
+                               break;
+                       case S_TRISTATE:
+                               switch (val) {
+                               case yes: ch = '*'; break;
+                               case mod: ch = 'M'; break;
+                               default:  ch = ' '; break;
+                               }
+                               cprint_name("<%c>", ch);
+                               break;
+                       }
+               } else {
+                       cprint_tag("%c%p", def_menu ? 't' : ':', menu);
+                       cprint_name("   ");
+               }
+
+               cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+               if (val == yes) {
+                       if (def_menu) {
+                               cprint_name(" (%s)", menu_get_prompt(def_menu));
+                               cprint_name("  --->");
+                               if (def_menu->list) {
+                                       indent += 2;
+                                       build_conf(def_menu);
+                                       indent -= 2;
+                               }
+                       }
+                       return;
+               }
+       } else {
+               child_count++;
+               val = sym_get_tristate_value(sym);
+               if (sym_is_choice_value(sym) && val == yes) {
+                       cprint_tag(":%p", menu);
+                       cprint_name("   ");
+               } else {
+                       switch (type) {
+                       case S_BOOLEAN:
+                               cprint_tag("t%p", menu);
+                               if (sym_is_changable(sym))
+                                       cprint_name("[%c]", val == no ? ' ' : '*');
+                               else
+                                       cprint_name("---");
+                               break;
+                       case S_TRISTATE:
+                               cprint_tag("t%p", menu);
+                               switch (val) {
+                               case yes: ch = '*'; break;
+                               case mod: ch = 'M'; break;
+                               default:  ch = ' '; break;
+                               }
+                               if (sym_is_changable(sym))
+                                       cprint_name("<%c>", ch);
+                               else
+                                       cprint_name("---");
+                               break;
+                       default:
+                               cprint_tag("s%p", menu);
+                               tmp = cprint_name("(%s)", sym_get_string_value(sym));
+                               tmp = indent - tmp + 4;
+                               if (tmp < 0)
+                                       tmp = 0;
+                               cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
+                                       (sym_has_value(sym) || !sym_is_changable(sym)) ?
+                                       "" : " (NEW)");
+                               goto conf_childs;
+                       }
+               }
+               cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
+                       (sym_has_value(sym) || !sym_is_changable(sym)) ?
+                       "" : " (NEW)");
+               if (menu->prompt->type == P_MENU) {
+                       cprint_name("  --->");
+                       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 dialog_list_item *active_item = NULL;
+       struct menu *submenu;
+       const char *prompt = menu_get_prompt(menu);
+       struct symbol *sym;
+       char active_entry[40];
+       int stat, type;
+
+       unlink("lxdialog.scrltmp");
+       active_entry[0] = 0;
+       while (1) {
+               indent = 0;
+               child_count = 0;
+               current_menu = menu;
+               cdone(); cinit();
+               build_conf(menu);
+               if (!child_count)
+                       break;
+               if (menu == &rootmenu) {
+                       cmake(); cprint_tag(":"); cprint_name("--- ");
+                       cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
+                       cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
+               }
+               dialog_clear();
+               stat = dialog_menu(prompt ? prompt : "Main Menu",
+                               menu_instructions, rows, cols, rows - 10,
+                               active_entry, item_no, items);
+               if (stat < 0)
+                       return;
+
+               if (stat == 1 || stat == 255)
+                       break;
+
+               active_item = first_sel_item(item_no, items);
+               if (!active_item)
+                       continue;
+               active_item->selected = 0;
+               strncpy(active_entry, active_item->tag, sizeof(active_entry));
+               active_entry[sizeof(active_entry)-1] = 0;
+               type = active_entry[0];
+               if (!type)
+                       continue;
+
+               sym = NULL;
+               submenu = NULL;
+               if (sscanf(active_entry + 1, "%p", &submenu) == 1)
+                       sym = submenu->sym;
+
+               switch (stat) {
+               case 0:
+                       switch (type) {
+                       case 'm':
+                               if (single_menu_mode)
+                                       submenu->data = (void *) (long) !submenu->data;
+                               else
+                                       conf(submenu);
+                               break;
+                       case 't':
+                               if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
+                                       conf_choice(submenu);
+                               else if (submenu->prompt->type == P_MENU)
+                                       conf(submenu);
+                               break;
+                       case 's':
+                               conf_string(submenu);
+                               break;
+                       case 'L':
+                               conf_load();
+                               break;
+                       case 'S':
+                               conf_save();
+                               break;
+                       }
+                       break;
+               case 2:
+                       if (sym)
+                               show_help(submenu);
+                       else
+                               show_readme();
+                       break;
+               case 3:
+                       if (type == 't') {
+                               if (sym_set_tristate_value(sym, yes))
+                                       break;
+                               if (sym_set_tristate_value(sym, mod))
+                                       show_textbox(NULL, setmod_text, 6, 74);
+                       }
+                       break;
+               case 4:
+                       if (type == 't')
+                               sym_set_tristate_value(sym, no);
+                       break;
+               case 5:
+                       if (type == 't')
+                               sym_set_tristate_value(sym, mod);
+                       break;
+               case 6:
+                       if (type == 't')
+                               sym_toggle_tristate_value(sym);
+                       else if (type == 'm')
+                               conf(submenu);
+                       break;
+               }
+       }
+}
+
+static void show_textbox(const char *title, const char *text, int r, int c)
+{
+       int fd;
+
+       fd = creat(".help.tmp", 0777);
+       write(fd, text, strlen(text));
+       close(fd);
+       while (dialog_textbox(title, ".help.tmp", r, c) < 0)
+               ;
+       unlink(".help.tmp");
+}
+
+static void show_helptext(const char *title, const char *text)
+{
+       show_textbox(title, text, rows, cols);
+}
+
+static void show_help(struct menu *menu)
+{
+       const char *help;
+       char *helptext;
+       struct symbol *sym = menu->sym;
+
+       help = sym->help;
+       if (!help)
+               help = nohelp_text;
+       if (sym->name) {
+               helptext = malloc(strlen(sym->name) + strlen(help) + 16);
+               sprintf(helptext, "%s:\n\n%s", sym->name, help);
+               show_helptext(menu_get_prompt(menu), helptext);
+               free(helptext);
+       } else
+               show_helptext(menu_get_prompt(menu), help);
+}
+
+static void show_readme(void)
+{
+       show_helptext("Help", top_menu_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) {
+               current_menu = menu;
+               cdone(); cinit();
+               for (child = menu->list; child; child = child->next) {
+                       if (!menu_is_visible(child))
+                               continue;
+                       cmake();
+                       cprint_tag("%p", child);
+                       cprint_name("%s", menu_get_prompt(child));
+                       if (child->sym == sym_get_choice_value(menu->sym))
+                               items[item_no - 1]->selected = 1; /* ON */
+                       else if (child->sym == active)
+                               items[item_no - 1]->selected = 2; /* SELECTED */
+                       else
+                               items[item_no - 1]->selected = 0; /* OFF */
+               }
+
+               switch (dialog_checklist(prompt ? prompt : "Main Menu",
+                                       radiolist_instructions, 15, 70, 6,
+                                       item_no, items, FLAG_RADIO)) {
+               case 0:
+                       if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1)
+                               break;
+                       sym_set_tristate_value(child->sym, yes);
+                       return;
+               case 1:
+                       if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) {
+                               show_help(child);
+                               active = child->sym;
+                       } else
+                               show_help(menu);
+                       break;
+               case 255:
+                       return;
+               }
+       }
+}
+
+static void conf_string(struct menu *menu)
+{
+       const char *prompt = menu_get_prompt(menu);
+
+       while (1) {
+               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!";
+                       /* panic? */;
+               }
+
+               switch (dialog_inputbox(prompt ? prompt : "Main Menu",
+                                       heading, 10, 75,
+                                       sym_get_string_value(menu->sym))) {
+               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 255:
+                       return;
+               }
+       }
+}
+
+static void conf_load(void)
+{
+       while (1) {
+               switch (dialog_inputbox(NULL, load_config_text, 11, 55,
+                                       filename)) {
+               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 255:
+                       return;
+               }
+       }
+}
+
+static void conf_save(void)
+{
+       while (1) {
+               switch (dialog_inputbox(NULL, save_config_text, 11, 55,
+                                       filename)) {
+               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 255:
+                       return;
+               }
+       }
+}
+
+static void conf_cleanup(void)
+{
+       tcsetattr(1, TCSAFLUSH, &ios_org);
+       unlink(".help.tmp");
+}
+
+static void winch_handler(int sig)
+{
+       struct winsize ws;
+
+       if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
+               rows = 24;
+               cols = 80;
+       } else {
+               rows = ws.ws_row;
+               cols = ws.ws_col;
+       }
+
+       if (rows < 19 || cols < 80) {
+               end_dialog();
+               fprintf(stderr, "Your display is too small to run Menuconfig!\n");
+               fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
+               exit(1);
+       }
+
+       rows -= 4;
+       cols -= 5;
+
+}
+
+int main(int ac, char **av)
+{
+       int stat;
+       char *mode;
+       struct symbol *sym;
+
+       conf_parse(av[1]);
+       conf_read(NULL);
+
+       sym = sym_lookup("VERSION", 0);
+       sym_calc_value(sym);
+       snprintf(menu_backtitle, 128, "BusyBox 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();
+       init_dialog();
+       signal(SIGWINCH, winch_handler);
+       conf(&rootmenu);
+       end_dialog();
+
+       /* Restart dialog to act more like when lxdialog was still separate */
+       init_dialog();
+       do {
+               stat = dialog_yesno(NULL,
+                               "Do you wish to save your new BusyBox configuration?", 5, 60);
+       } while (stat < 0);
+       end_dialog();
+
+       if (stat == 0) {
+               conf_write(NULL);
+               printf("\n\n"
+                       "*** End of BusyBox configuration.\n"
+                       "*** Check the top-level Makefile for additional configuration options.\n\n");
+       } else
+               printf("\n\nYour BusyBox configuration changes were NOT saved.\n\n");
+
+       return 0;
+}
diff --git a/busybox/scripts/config/menu.c b/busybox/scripts/config/menu.c
new file mode 100644 (file)
index 0000000..6425296
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * 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;
+struct menu *current_menu, *current_entry;
+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)
+{
+}
+
+void menu_add_menu(void)
+{
+       current_menu = current_entry;
+       last_entry_ptr = &current_entry->list;
+}
+
+void menu_end_menu(void)
+{
+       last_entry_ptr = &current_menu->next;
+       current_menu = current_menu->parent;
+}
+
+struct expr *menu_check_dep(struct expr *e)
+{
+       if (!e)
+               return e;
+
+       switch (e->type) {
+       case E_NOT:
+               e->left.expr = menu_check_dep(e->left.expr);
+               break;
+       case E_OR:
+       case E_AND:
+               e->left.expr = menu_check_dep(e->left.expr);
+               e->right.expr = menu_check_dep(e->right.expr);
+               break;
+       case E_SYMBOL:
+               /* change 'm' into 'm' && MODULES */
+               if (e->left.sym == &symbol_mod)
+                       return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
+               break;
+       default:
+               break;
+       }
+       return e;
+}
+
+void menu_add_dep(struct expr *dep)
+{
+       current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
+}
+
+void menu_set_type(int type)
+{
+       struct symbol *sym = current_entry->sym;
+
+       if (sym->type == type)
+               return;
+       if (sym->type == S_UNKNOWN) {
+               sym->type = type;
+               return;
+       }
+       menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n",
+           sym->name ? sym->name : "<choice>",
+           sym_type_name(sym->type), sym_type_name(type));
+}
+
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
+{
+       struct property *prop = prop_alloc(type, current_entry->sym);
+
+       prop->menu = current_entry;
+       prop->text = prompt;
+       prop->expr = expr;
+       prop->visible.expr = menu_check_dep(dep);
+
+       if (prompt) {
+               if (current_entry->prompt)
+                       menu_warn(current_entry, "prompt redefined\n");
+               current_entry->prompt = prop;
+       }
+
+       return prop;
+}
+
+void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
+{
+       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 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 (!sym_string_valid(sym, prop->expr->left.sym->name) ||
+                           !sym_string_valid(sym, prop->expr->right.sym->name))
+                               prop_warn(prop, "range is invalid");
+                       break;
+               default:
+                       ;
+               }
+       }
+}
+
+void menu_finalize(struct menu *parent)
+{
+       struct menu *menu, *last_menu;
+       struct symbol *sym;
+       struct property *prop;
+       struct expr *parentdep, *basedep, *dep, *dep2, **ep;
+
+       sym = parent->sym;
+       if (parent->list) {
+               if (sym && sym_is_choice(sym)) {
+                       /* find the first choice value and find out choice type */
+                       for (menu = parent->list; menu; menu = menu->next) {
+                               if (menu->sym) {
+                                       current_entry = parent;
+                                       menu_set_type(menu->sym->type);
+                                       current_entry = menu;
+                                       menu_set_type(sym->type);
+                                       break;
+                               }
+                       }
+                       parentdep = expr_alloc_symbol(sym);
+               } else if (parent->prompt)
+                       parentdep = parent->prompt->visible.expr;
+               else
+                       parentdep = parent->dep;
+
+               for (menu = parent->list; menu; menu = menu->next) {
+                       basedep = expr_transform(menu->dep);
+                       basedep = expr_alloc_and(expr_copy(parentdep), basedep);
+                       basedep = expr_eliminate_dups(basedep);
+                       menu->dep = basedep;
+                       if (menu->sym)
+                               prop = menu->sym->prop;
+                       else
+                               prop = menu->prompt;
+                       for (; prop; prop = prop->next) {
+                               if (prop->menu != menu)
+                                       continue;
+                               dep = expr_transform(prop->visible.expr);
+                               dep = expr_alloc_and(expr_copy(basedep), dep);
+                               dep = expr_eliminate_dups(dep);
+                               if (menu->sym && menu->sym->type != S_TRISTATE)
+                                       dep = expr_trans_bool(dep);
+                               prop->visible.expr = dep;
+                               if (prop->type == P_SELECT) {
+                                       struct symbol *es = prop_get_symbol(prop);
+                                       es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+                                                       expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+                               }
+                       }
+               }
+               for (menu = parent->list; menu; menu = menu->next)
+                       menu_finalize(menu);
+       } else if (sym) {
+               basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
+               basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
+               basedep = expr_eliminate_dups(expr_transform(basedep));
+               last_menu = NULL;
+               for (menu = parent->next; menu; menu = menu->next) {
+                       dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
+                       if (!expr_contains_symbol(dep, sym))
+                               break;
+                       if (expr_depends_symbol(dep, sym))
+                               goto next;
+                       dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
+                       dep = expr_eliminate_dups(expr_transform(dep));
+                       dep2 = expr_copy(basedep);
+                       expr_eliminate_eq(&dep, &dep2);
+                       expr_free(dep);
+                       if (!expr_is_yes(dep2)) {
+                               expr_free(dep2);
+                               break;
+                       }
+                       expr_free(dep2);
+               next:
+                       menu_finalize(menu);
+                       menu->parent = parent;
+                       last_menu = menu;
+               }
+               if (last_menu) {
+                       parent->list = parent->next;
+                       parent->next = last_menu->next;
+                       last_menu->next = NULL;
+               }
+       }
+       for (menu = parent->list; menu; menu = menu->next) {
+               if (sym && sym_is_choice(sym) && menu->sym) {
+                       menu->sym->flags |= SYMBOL_CHOICEVAL;
+                       if (!menu->prompt)
+                               menu_warn(menu, "choice value must have a prompt");
+                       for (prop = menu->sym->prop; prop; prop = prop->next) {
+                               if (prop->type == P_PROMPT && prop->menu != menu) {
+                                       prop_warn(prop, "choice values "
+                                           "currently only support a "
+                                           "single prompt");
+                               }
+                               if (prop->type == P_DEFAULT)
+                                       prop_warn(prop, "defaults for choice "
+                                           "values not supported");
+                       }
+                       current_entry = menu;
+                       menu_set_type(sym->type);
+                       menu_add_symbol(P_CHOICE, sym, NULL);
+                       prop = sym_get_choice_prop(sym);
+                       for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
+                               ;
+                       *ep = expr_alloc_one(E_CHOICE, NULL);
+                       (*ep)->right.sym = menu->sym;
+               }
+               if (menu->list && (!menu->prompt || !menu->prompt->text)) {
+                       for (last_menu = menu->list; ; last_menu = last_menu->next) {
+                               last_menu->parent = parent;
+                               if (!last_menu->next)
+                                       break;
+                       }
+                       last_menu->next = menu->next;
+                       menu->next = menu->list;
+                       menu->list = NULL;
+               }
+       }
+
+       if (sym && !(sym->flags & SYMBOL_WARNED)) {
+               if (sym->type == S_UNKNOWN)
+                       menu_warn(parent, "config symbol defined "
+                           "without type\n");
+
+               if (sym_is_choice(sym) && !parent->prompt)
+                       menu_warn(parent, "choice must have a prompt\n");
+
+               /* Check properties connected to this symbol */
+               sym_check_prop(sym);
+               sym->flags |= SYMBOL_WARNED;
+       }
+
+       if (sym && !sym_is_optional(sym) && parent->prompt) {
+               sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+                               expr_alloc_and(parent->prompt->visible.expr,
+                                       expr_alloc_symbol(&symbol_mod)));
+       }
+}
+
+bool menu_is_visible(struct menu *menu)
+{
+       struct menu *child;
+       struct symbol *sym;
+       tristate visible;
+
+       if (!menu->prompt)
+               return false;
+       sym = menu->sym;
+       if (sym) {
+               sym_calc_value(sym);
+               visible = menu->prompt->visible.tri;
+       } else
+               visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
+
+       if (visible != no)
+               return true;
+       if (!sym || sym_get_tristate_value(menu->sym) == no)
+               return false;
+
+       for (child = menu->list; child; child = child->next)
+               if (menu_is_visible(child))
+                       return true;
+       return false;
+}
+
+const char *menu_get_prompt(struct menu *menu)
+{
+       if (menu->prompt)
+               return menu->prompt->text;
+       else if (menu->sym)
+               return menu->sym->name;
+       return NULL;
+}
+
+struct menu *menu_get_root_menu(struct menu *menu)
+{
+       return &rootmenu;
+}
+
+struct menu *menu_get_parent_menu(struct menu *menu)
+{
+       enum prop_type type;
+
+       for (; menu != &rootmenu; menu = menu->parent) {
+               type = menu->prompt ? menu->prompt->type : 0;
+               if (type == P_MENU)
+                       break;
+       }
+       return menu;
+}
+
+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;
+}
+
+int file_write_dep(const char *name)
+{
+       struct file *file;
+       FILE *out;
+
+       if (!name)
+               name = ".config.cmd";
+       out = fopen(".config.tmp", "w");
+       if (!out)
+               return 1;
+       fprintf(out, "deps_config := \\\n");
+       for (file = file_list; file; file = file->next) {
+               if (file->next)
+                       fprintf(out, "\t%s \\\n", file->name);
+               else
+                       fprintf(out, "\t%s\n", file->name);
+       }
+       fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n");
+       fclose(out);
+       rename(".config.tmp", name);
+       return 0;
+}
+
diff --git a/busybox/scripts/config/menubox.c b/busybox/scripts/config/menubox.c
new file mode 100644 (file)
index 0000000..431f09f
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ *  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
+print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey)
+{
+    int j;
+    char menu_item[menu_width+1];
+
+    strncpy(menu_item, item, menu_width);
+    menu_item[menu_width] = 0;
+    j = first_alpha(menu_item, "YyNnMm");
+
+    /* Clear 'residue' of last item */
+    wattrset (win, menubox_attr);
+    wmove (win, choice, 0);
+#if OLD_NCURSES
+    {
+        int i;
+        for (i = 0; i < menu_width; i++)
+           waddch (win, ' ');
+    }
+#else
+    wclrtoeol(win);
+#endif
+    wattrset (win, selected ? item_selected_attr : item_attr);
+    mvwaddstr (win, choice, item_x, menu_item);
+    if (hotkey) {
+       wattrset (win, selected ? tag_key_selected_attr : tag_key_attr);
+       mvwaddch(win, choice, item_x+j, menu_item[j]);
+    }
+    if (selected) {
+       wmove (win, choice, item_x+1);
+       wrefresh (win);
+    }
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void
+print_arrows (WINDOW * win, int item_no, int scroll,
+               int y, int x, int height)
+{
+    int cur_y, cur_x;
+
+    getyx(win, cur_y, cur_x);
+
+    wmove(win, y, x);
+
+    if (scroll > 0) {
+       wattrset (win, uarrow_attr);
+       waddch (win, ACS_UARROW);
+       waddstr (win, "(-)");
+    }
+    else {
+       wattrset (win, menubox_attr);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+    }
+
+   y = y + height + 1;
+   wmove(win, y, x);
+
+   if ((height < item_no) && (scroll + height < item_no)) {
+       wattrset (win, darrow_attr);
+       waddch (win, ACS_DARROW);
+       waddstr (win, "(+)");
+    }
+    else {
+       wattrset (win, menubox_border_attr);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+       waddch (win, ACS_HLINE);
+   }
+
+   wmove(win, cur_y, cur_x);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Display a menu for choosing among a number of options
+ */
+int
+dialog_menu (const char *title, const char *prompt, int height, int width,
+               int menu_height, const char *current, int item_no,
+               struct dialog_list_item ** items)
+{
+    int i, j, x, y, box_x, box_y;
+    int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice;
+    WINDOW *dialog, *menu;
+    FILE *f;
+
+    max_choice = MIN (menu_height, item_no);
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height - 3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+       waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    wbkgdset (dialog, dialog_attr & A_COLOR);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL && strlen(title) >= width-2 ) {
+       /* truncate long title -- mec */
+       char * title2 = malloc(width-2+1);
+       memcpy( title2, title, width-2 );
+       title2[width-2] = '\0';
+       title = title2;
+    }
+
+    if (title != NULL) {
+       wattrset (dialog, title_attr);
+       mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+       waddstr (dialog, (char *)title);
+       waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    menu_width = width - 6;
+    box_y = height - menu_height - 5;
+    box_x = (width - menu_width) / 2 - 1;
+
+    /* create new window for the menu */
+    menu = subwin (dialog, menu_height, menu_width,
+               y + box_y + 1, x + box_x + 1);
+    keypad (menu, TRUE);
+
+    /* draw a box around the menu items */
+    draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+             menubox_border_attr, menubox_attr);
+
+    /*
+     * Find length of longest item in order to center menu.
+     * Set 'choice' to default item.
+     */
+    item_x = 0;
+    for (i = 0; i < item_no; i++) {
+       item_x = MAX (item_x, MIN(menu_width, strlen (items[i]->name) + 2));
+       if (strcmp(current, items[i]->tag) == 0) choice = i;
+    }
+
+    item_x = (menu_width - item_x) / 2;
+
+    /* get the scroll info from the temp file */
+    if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) {
+       if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) &&
+            (scroll+max_choice > choice) && (scroll >= 0) &&
+            (scroll+max_choice <= item_no) ) {
+           first_item = scroll;
+           choice = choice - scroll;
+           fclose(f);
+       } else {
+           scroll=0;
+           remove("lxdialog.scrltmp");
+           fclose(f);
+           f=NULL;
+       }
+    }
+    if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) {
+       if (choice >= item_no-max_choice/2)
+           scroll = first_item = item_no-max_choice;
+       else
+           scroll = first_item = choice - max_choice/2;
+       choice = choice - scroll;
+    }
+
+    /* Print the menu */
+    for (i=0; i < max_choice; i++) {
+       print_item (menu, items[first_item + i]->name, i, i == choice,
+                    (items[first_item + i]->tag[0] != ':'));
+    }
+
+    wnoutrefresh (menu);
+
+    print_arrows(dialog, item_no, scroll,
+                box_y, box_x+item_x+1, menu_height);
+
+    print_buttons (dialog, height, width, 0);
+    wmove (menu, choice, item_x+1);
+    wrefresh (menu);
+
+    while (key != ESC) {
+       key = wgetch(menu);
+
+       if (key < 256 && isalpha(key)) key = tolower(key);
+
+       if (strchr("ynm", key))
+               i = max_choice;
+       else {
+        for (i = choice+1; i < max_choice; i++) {
+               j = first_alpha(items[scroll + i]->name, "YyNnMm>");
+               if (key == tolower(items[scroll + i]->name[j]))
+                       break;
+       }
+       if (i == max_choice)
+                       for (i = 0; i < max_choice; i++) {
+                       j = first_alpha(items[scroll + i]->name, "YyNnMm>");
+                       if (key == tolower(items[scroll + i]->name[j]))
+                               break;
+               }
+       }
+
+       if (i < max_choice ||
+            key == KEY_UP || key == KEY_DOWN ||
+            key == '-' || key == '+' ||
+            key == KEY_PPAGE || key == KEY_NPAGE) {
+
+            print_item (menu, items[scroll + choice]->name, choice, FALSE,
+                       (items[scroll + choice]->tag[0] != ':'));
+
+           if (key == KEY_UP || key == '-') {
+                if (choice < 2 && scroll) {
+                   /* Scroll menu down */
+                    scrollok (menu, TRUE);
+                    wscrl (menu, -1);
+                    scrollok (menu, FALSE);
+
+                    scroll--;
+
+                    print_item (menu, items[scroll]->name, 0, FALSE,
+                               (items[scroll]->tag[0] != ':'));
+               } else
+                   choice = MAX(choice - 1, 0);
+
+           } else if (key == KEY_DOWN || key == '+')  {
+
+               print_item (menu, items[scroll + choice]->name, choice, FALSE,
+                                (items[scroll + choice]->tag[0] != ':'));
+
+                if ((choice > max_choice-3) &&
+                    (scroll + max_choice < item_no)
+                   ) {
+                   /* Scroll menu up */
+                   scrollok (menu, TRUE);
+                    scroll (menu);
+                    scrollok (menu, FALSE);
+
+                    scroll++;
+
+                    print_item (menu, items[scroll + max_choice - 1]->name,
+                               max_choice-1, FALSE,
+                               (items[scroll + max_choice - 1]->tag[0] != ':'));
+                } 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) {
+                       wscrl (menu, -1);
+                       scroll--;
+                       print_item (menu, items[scroll]->name, 0, FALSE,
+                       (items[scroll]->tag[0] != ':'));
+                    } else {
+                        if (choice > 0)
+                            choice--;
+                    }
+                }
+                scrollok (menu, FALSE);
+
+            } else if (key == KEY_NPAGE) {
+                for (i=0; (i < max_choice); i++) {
+                    if (scroll+max_choice < item_no) {
+                       scrollok (menu, TRUE);
+                       scroll(menu);
+                       scrollok (menu, FALSE);
+                       scroll++;
+                       print_item (menu, items[scroll + max_choice - 1]->name,
+                                   max_choice-1, FALSE,
+                                   (items[scroll + max_choice - 1]->tag[0] != ':'));
+                   } else {
+                       if (choice+1 < max_choice)
+                           choice++;
+                   }
+                }
+
+            } else
+                choice = i;
+
+            print_item (menu, items[scroll + choice]->name, choice, TRUE,
+                       (items[scroll + choice]->tag[0] != ':'));
+
+            print_arrows(dialog, item_no, scroll,
+                         box_y, box_x+item_x+1, menu_height);
+
+            wnoutrefresh (dialog);
+            wrefresh (menu);
+
+           continue;           /* wait for another key press */
+        }
+
+       switch (key) {
+       case KEY_LEFT:
+       case TAB:
+       case KEY_RIGHT:
+           button = ((key == KEY_LEFT ? --button : ++button) < 0)
+                       ? 2 : (button > 2 ? 0 : button);
+
+           print_buttons(dialog, height, width, button);
+           wrefresh (menu);
+           break;
+       case ' ':
+       case 's':
+       case 'y':
+       case 'n':
+       case 'm':
+           /* save scroll info */
+           if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) {
+               fprintf(f,"%d\n",scroll);
+               fclose(f);
+           }
+           delwin (dialog);
+            items[scroll + choice]->selected = 1;
+            switch (key) {
+            case 's': return 3;
+            case 'y': return 3;
+            case 'n': return 4;
+            case 'm': return 5;
+            case ' ': return 6;
+            }
+           return 0;
+       case 'h':
+       case '?':
+           button = 2;
+       case '\n':
+           delwin (dialog);
+           items[scroll + choice]->selected = 1;
+
+           remove("lxdialog.scrltmp");
+           return button;
+       case 'e':
+       case 'x':
+           key = ESC;
+       case ESC:
+           break;
+       }
+    }
+
+    delwin (dialog);
+    remove("lxdialog.scrltmp");
+    return -1;                 /* ESC pressed */
+}
diff --git a/busybox/scripts/config/msgbox.c b/busybox/scripts/config/msgbox.c
new file mode 100644 (file)
index 0000000..93692e1
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  msgbox.c -- implements the message box and info box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display a message box. Program will pause and display an "OK" button
+ * if the parameter 'pause' is non-zero.
+ */
+int
+dialog_msgbox (const char *title, const char *prompt, int height, int width,
+               int pause)
+{
+    int i, x, y, key = 0;
+    WINDOW *dialog;
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+    if (title != NULL && strlen(title) >= width-2 ) {
+       /* truncate long title -- mec */
+       char * title2 = malloc(width-2+1);
+       memcpy( title2, title, width-2 );
+       title2[width-2] = '\0';
+       title = title2;
+    }
+
+    if (title != NULL) {
+       wattrset (dialog, title_attr);
+       mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+       waddstr (dialog, (char *)title);
+       waddch (dialog, ' ');
+    }
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 2);
+
+    if (pause) {
+       wattrset (dialog, border_attr);
+       mvwaddch (dialog, height - 3, 0, ACS_LTEE);
+       for (i = 0; i < width - 2; i++)
+           waddch (dialog, ACS_HLINE);
+       wattrset (dialog, dialog_attr);
+       waddch (dialog, ACS_RTEE);
+
+       print_button (dialog, "  Ok  ",
+                     height - 2, width / 2 - 4, TRUE);
+
+       wrefresh (dialog);
+       while (key != ESC && key != '\n' && key != ' ' &&
+               key != 'O' && key != 'o' && key != 'X' && key != 'x')
+           key = wgetch (dialog);
+    } else {
+       key = '\n';
+       wrefresh (dialog);
+    }
+
+    delwin (dialog);
+    return key == ESC ? -1 : 0;
+}
diff --git a/busybox/scripts/config/symbol.c b/busybox/scripts/config/symbol.c
new file mode 100644 (file)
index 0000000..a9fae9c
--- /dev/null
@@ -0,0 +1,771 @@
+/*
+ * 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 <sys/utsname.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+       .name = "y",
+       .curr = { "y", yes },
+       .flags = SYMBOL_YES|SYMBOL_VALID,
+}, symbol_mod = {
+       .name = "m",
+       .curr = { "m", mod },
+       .flags = SYMBOL_MOD|SYMBOL_VALID,
+}, symbol_no = {
+       .name = "n",
+       .curr = { "n", no },
+       .flags = SYMBOL_NO|SYMBOL_VALID,
+}, symbol_empty = {
+       .name = "",
+       .curr = { "", no },
+       .flags = SYMBOL_VALID,
+};
+
+int sym_change_count;
+struct symbol *modules_sym;
+tristate modules_val;
+
+void sym_add_default(struct symbol *sym, const char *def)
+{
+       struct property *prop = prop_alloc(P_DEFAULT, sym);
+
+       prop->expr = expr_alloc_symbol(sym_lookup(def, 1));
+}
+
+void sym_init(void)
+{
+       struct symbol *sym;
+       char *p;
+       static bool inited = false;
+
+       if (inited)
+               return;
+       inited = true;
+
+       sym = sym_lookup("VERSION", 0);
+       sym->type = S_STRING;
+       sym->flags |= SYMBOL_AUTO;
+       p = getenv("VERSION");
+       if (p)
+               sym_add_default(sym, p);
+
+       sym = sym_lookup("TARGET_ARCH", 0);
+       sym->type = S_STRING;
+       sym->flags |= SYMBOL_AUTO;
+       p = getenv("TARGET_ARCH");
+       if (p)
+               sym_add_default(sym, p);
+
+}
+
+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 void sym_calc_visibility(struct symbol *sym)
+{
+       struct property *prop;
+       tristate tri;
+
+       /* any prompt visible? */
+       tri = no;
+       for_all_prompts(sym, prop) {
+               prop->visible.tri = expr_calc_value(prop->visible.expr);
+               tri = E_OR(tri, prop->visible.tri);
+       }
+       if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
+               tri = yes;
+       if (sym->visible != tri) {
+               sym->visible = tri;
+               sym_set_changed(sym);
+       }
+       if (sym_is_choice_value(sym))
+               return;
+       tri = no;
+       if (sym->rev_dep.expr)
+               tri = expr_calc_value(sym->rev_dep.expr);
+       if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+               tri = yes;
+       if (sym->rev_dep.tri != tri) {
+               sym->rev_dep.tri = tri;
+               sym_set_changed(sym);
+       }
+}
+
+static struct symbol *sym_calc_choice(struct symbol *sym)
+{
+       struct symbol *def_sym;
+       struct property *prop;
+       struct expr *e;
+
+       /* is the user choice visible? */
+       def_sym = sym->user.val;
+       if (def_sym) {
+               sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       return def_sym;
+       }
+
+       /* any of the defaults visible? */
+       for_all_defaults(sym, prop) {
+               prop->visible.tri = expr_calc_value(prop->visible.expr);
+               if (prop->visible.tri == no)
+                       continue;
+               def_sym = prop_get_symbol(prop);
+               sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       return def_sym;
+       }
+
+       /* just get the first visible value */
+       prop = sym_get_choice_prop(sym);
+       for (e = prop->expr; e; e = e->left.expr) {
+               def_sym = e->right.sym;
+               sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       return def_sym;
+       }
+
+       /* no choice? reset tristate value */
+       sym->curr.tri = no;
+       return NULL;
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+       struct symbol_value newval, oldval;
+       struct property *prop;
+       struct expr *e;
+
+       if (!sym)
+               return;
+
+       if (sym->flags & SYMBOL_VALID)
+               return;
+       sym->flags |= SYMBOL_VALID;
+
+       oldval = sym->curr;
+
+       switch (sym->type) {
+       case S_INT:
+       case S_HEX:
+       case S_STRING:
+               newval = symbol_empty.curr;
+               break;
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               newval = symbol_no.curr;
+               break;
+       default:
+               sym->curr.val = sym->name;
+               sym->curr.tri = no;
+               return;
+       }
+       if (!sym_is_choice_value(sym))
+               sym->flags &= ~SYMBOL_WRITE;
+
+       sym_calc_visibility(sym);
+
+       /* set default if recursively called */
+       sym->curr = newval;
+
+       switch (sym_get_type(sym)) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               if (sym_is_choice_value(sym) && sym->visible == yes) {
+                       prop = sym_get_choice_prop(sym);
+                       newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
+               } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) {
+                       sym->flags |= SYMBOL_WRITE;
+                       if (sym_has_value(sym))
+                               newval.tri = sym->user.tri;
+                       else if (!sym_is_choice(sym)) {
+                               prop = sym_get_default_prop(sym);
+                               if (prop)
+                                       newval.tri = expr_calc_value(prop->expr);
+                       }
+                       newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri);
+               } else if (!sym_is_choice(sym)) {
+                       prop = sym_get_default_prop(sym);
+                       if (prop) {
+                               sym->flags |= SYMBOL_WRITE;
+                               newval.tri = expr_calc_value(prop->expr);
+                       }
+               }
+               if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
+                       newval.tri = yes;
+               break;
+       case S_STRING:
+       case S_HEX:
+       case S_INT:
+               if (sym->visible != no) {
+                       sym->flags |= SYMBOL_WRITE;
+                       if (sym_has_value(sym)) {
+                               newval.val = sym->user.val;
+                               break;
+                       }
+               }
+               prop = sym_get_default_prop(sym);
+               if (prop) {
+                       struct symbol *ds = prop_get_symbol(prop);
+                       if (ds) {
+                               sym->flags |= SYMBOL_WRITE;
+                               sym_calc_value(ds);
+                               newval.val = ds->curr.val;
+                       }
+               }
+               break;
+       default:
+               ;
+       }
+
+       sym->curr = newval;
+       if (sym_is_choice(sym) && newval.tri == yes)
+               sym->curr.val = sym_calc_choice(sym);
+
+       if (memcmp(&oldval, &sym->curr, sizeof(oldval)))
+               sym_set_changed(sym);
+       if (modules_sym == sym)
+               modules_val = modules_sym->curr.tri;
+
+       if (sym_is_choice(sym)) {
+               int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
+               prop = sym_get_choice_prop(sym);
+               for (e = prop->expr; e; e = e->left.expr) {
+                       e->right.sym->flags |= flags;
+                       if (flags & SYMBOL_CHANGED)
+                               sym_set_changed(e->right.sym);
+               }
+       }
+}
+
+void sym_clear_all_valid(void)
+{
+       struct symbol *sym;
+       int i;
+
+       for_all_symbols(i, sym)
+               sym->flags &= ~SYMBOL_VALID;
+       sym_change_count++;
+       if (modules_sym)
+               sym_calc_value(modules_sym);
+}
+
+void sym_set_changed(struct symbol *sym)
+{
+       struct property *prop;
+
+       sym->flags |= SYMBOL_CHANGED;
+       for (prop = sym->prop; prop; prop = prop->next) {
+               if (prop->menu)
+                       prop->menu->flags |= MENU_CHANGED;
+       }
+}
+
+void sym_set_all_changed(void)
+{
+       struct symbol *sym;
+       int i;
+
+       for_all_symbols(i, sym)
+               sym_set_changed(sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+       int type = sym_get_type(sym);
+
+       if (sym->visible == no)
+               return false;
+
+       if (type != S_BOOLEAN && type != S_TRISTATE)
+               return false;
+
+       if (type == S_BOOLEAN && val == mod)
+               return false;
+       if (sym->visible <= sym->rev_dep.tri)
+               return false;
+       if (sym_is_choice_value(sym) && sym->visible == yes)
+               return val == yes;
+       return val >= sym->rev_dep.tri && val <= sym->visible;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+       tristate oldval = sym_get_tristate_value(sym);
+
+       if (oldval != val && !sym_tristate_within_range(sym, val))
+               return false;
+
+       if (sym->flags & SYMBOL_NEW) {
+               sym->flags &= ~SYMBOL_NEW;
+               sym_set_changed(sym);
+       }
+       if (sym_is_choice_value(sym) && val == yes) {
+               struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+
+               cs->user.val = sym;
+               cs->flags &= ~SYMBOL_NEW;
+       }
+
+       sym->user.tri = val;
+       if (oldval != val) {
+               sym_clear_all_valid();
+               if (sym == modules_sym)
+                       sym_set_all_changed();
+       }
+
+       return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+       tristate oldval, newval;
+
+       oldval = newval = sym_get_tristate_value(sym);
+       do {
+               switch (newval) {
+               case no:
+                       newval = mod;
+                       break;
+               case mod:
+                       newval = yes;
+                       break;
+               case yes:
+                       newval = no;
+                       break;
+               }
+               if (sym_set_tristate_value(sym, newval))
+                       break;
+       } while (oldval != newval);
+       return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+       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 >= strtol(prop->expr->left.sym->name, NULL, 10) &&
+                      val <= strtol(prop->expr->right.sym->name, NULL, 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 >= strtol(prop->expr->left.sym->name, NULL, 16) &&
+                      val <= strtol(prop->expr->right.sym->name, NULL, 16);
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               switch (str[0]) {
+               case 'y': case 'Y':
+                       return sym_tristate_within_range(sym, yes);
+               case 'm': case 'M':
+                       return sym_tristate_within_range(sym, mod);
+               case 'n': case 'N':
+                       return sym_tristate_within_range(sym, no);
+               }
+               return false;
+       default:
+               return false;
+       }
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+       const char *oldval;
+       char *val;
+       int size;
+
+       switch (sym->type) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               switch (newval[0]) {
+               case 'y': case 'Y':
+                       return sym_set_tristate_value(sym, yes);
+               case 'm': case 'M':
+                       return sym_set_tristate_value(sym, mod);
+               case 'n': case 'N':
+                       return sym_set_tristate_value(sym, no);
+               }
+               return false;
+       default:
+               ;
+       }
+
+       if (!sym_string_within_range(sym, newval))
+               return false;
+
+       if (sym->flags & SYMBOL_NEW) {
+               sym->flags &= ~SYMBOL_NEW;
+               sym_set_changed(sym);
+       }
+
+       oldval = sym->user.val;
+       size = strlen(newval) + 1;
+       if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+               size += 2;
+               sym->user.val = val = malloc(size);
+               *val++ = '0';
+               *val++ = 'x';
+       } else if (!oldval || strcmp(oldval, newval))
+               sym->user.val = val = malloc(size);
+       else
+               return true;
+
+       strcpy(val, newval);
+       free((void *)oldval);
+       sym_clear_all_valid();
+
+       return true;
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+       tristate val;
+
+       switch (sym->type) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               val = sym_get_tristate_value(sym);
+               switch (val) {
+               case no:
+                       return "n";
+               case mod:
+                       return "m";
+               case yes:
+                       return "y";
+               }
+               break;
+       default:
+               ;
+       }
+       return (const char *)sym->curr.val;
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+       return sym->visible > sym->rev_dep.tri;
+}
+
+struct symbol *sym_lookup(const char *name, int isconst)
+{
+       struct symbol *symbol;
+       const char *ptr;
+       char *new_name;
+       int hash = 0;
+
+       if (name) {
+               if (name[0] && !name[1]) {
+                       switch (name[0]) {
+                       case 'y': return &symbol_yes;
+                       case 'm': return &symbol_mod;
+                       case 'n': return &symbol_no;
+                       }
+               }
+               for (ptr = name; *ptr; ptr++)
+                       hash += *ptr;
+               hash &= 0xff;
+
+               for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+                       if (!strcmp(symbol->name, name)) {
+                               if ((isconst && symbol->flags & SYMBOL_CONST) ||
+                                   (!isconst && !(symbol->flags & SYMBOL_CONST)))
+                                       return symbol;
+                       }
+               }
+               new_name = strdup(name);
+       } else {
+               new_name = NULL;
+               hash = 256;
+       }
+
+       symbol = malloc(sizeof(*symbol));
+       memset(symbol, 0, sizeof(*symbol));
+       symbol->name = new_name;
+       symbol->type = S_UNKNOWN;
+       symbol->flags = SYMBOL_NEW;
+       if (isconst)
+               symbol->flags |= SYMBOL_CONST;
+
+       symbol->next = symbol_hash[hash];
+       symbol_hash[hash] = symbol;
+
+       return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+       struct symbol *symbol = NULL;
+       const char *ptr;
+       int hash = 0;
+
+       if (!name)
+               return NULL;
+
+       if (name[0] && !name[1]) {
+               switch (name[0]) {
+               case 'y': return &symbol_yes;
+               case 'm': return &symbol_mod;
+               case 'n': return &symbol_no;
+               }
+       }
+       for (ptr = name; *ptr; ptr++)
+               hash += *ptr;
+       hash &= 0xff;
+
+       for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+               if (!strcmp(symbol->name, name) &&
+                   !(symbol->flags & SYMBOL_CONST))
+                               break;
+       }
+
+       return symbol;
+}
+
+struct symbol *sym_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_DONE)
+               return NULL;
+       if (sym->flags & SYMBOL_CHECK) {
+               printf("Warning! Found recursive dependency: %s", sym->name);
+               return sym;
+       }
+
+       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);
+       sym->flags &= ~SYMBOL_CHECK;
+       return sym2;
+}
+
+struct property *prop_alloc(enum prop_type type, struct symbol *sym)
+{
+       struct property *prop;
+       struct property **propp;
+
+       prop = malloc(sizeof(*prop));
+       memset(prop, 0, sizeof(*prop));
+       prop->type = type;
+       prop->sym = sym;
+       prop->file = current_file;
+       prop->lineno = zconf_lineno();
+
+       /* append property to the prop list of symbol */
+       if (sym) {
+               for (propp = &sym->prop; *propp; propp = &(*propp)->next)
+                       ;
+               *propp = prop;
+       }
+
+       return prop;
+}
+
+struct symbol *prop_get_symbol(struct property *prop)
+{
+       if (prop->expr && (prop->expr->type == E_SYMBOL ||
+                          prop->expr->type == E_CHOICE))
+               return prop->expr->left.sym;
+       return NULL;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+       switch (type) {
+       case P_PROMPT:
+               return "prompt";
+       case P_COMMENT:
+               return "comment";
+       case P_MENU:
+               return "menu";
+       case P_DEFAULT:
+               return "default";
+       case P_CHOICE:
+               return "choice";
+       case P_SELECT:
+               return "select";
+       case P_RANGE:
+               return "range";
+       case P_UNKNOWN:
+               break;
+       }
+       return "unknown";
+}
diff --git a/busybox/scripts/config/textbox.c b/busybox/scripts/config/textbox.c
new file mode 100644 (file)
index 0000000..a5a460b
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ *  textbox.c -- implements the text box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void back_lines (int n);
+static void print_page (WINDOW * win, int height, int width);
+static void print_line (WINDOW * win, int row, int width);
+static char *get_line (void);
+static void print_position (WINDOW * win, int height, int width);
+
+static int hscroll, fd, file_size, bytes_read;
+static int begin_reached = 1, end_reached, page_length;
+static char *buf, *page;
+
+/*
+ * Display text from a file in a dialog box.
+ */
+int
+dialog_textbox (const char *title, const char *file, int height, int width)
+{
+    int i, x, y, cur_x, cur_y, fpos, key = 0;
+    int passed_end;
+    char search_term[MAX_LEN + 1];
+    WINDOW *dialog, *text;
+
+    search_term[0] = '\0';     /* no search term entered yet */
+
+    /* Open input file for reading */
+    if ((fd = open (file, O_RDONLY)) == -1) {
+       endwin ();
+       fprintf (stderr,
+                "\nCan't open input file in dialog_textbox().\n");
+       exit (-1);
+    }
+    /* Get file size. Actually, 'file_size' is the real file size - 1,
+       since it's only the last byte offset from the beginning */
+    if ((file_size = lseek (fd, 0, SEEK_END)) == -1) {
+       endwin ();
+       fprintf (stderr, "\nError getting file size in dialog_textbox().\n");
+       exit (-1);
+    }
+    /* Restore file pointer to beginning of file after getting file size */
+    if (lseek (fd, 0, SEEK_SET) == -1) {
+       endwin ();
+       fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n");
+       exit (-1);
+    }
+    /* Allocate space for read buffer */
+    if ((buf = malloc (BUF_SIZE + 1)) == NULL) {
+       endwin ();
+       fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n");
+       exit (-1);
+    }
+    if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+       endwin ();
+       fprintf (stderr, "\nError reading file in dialog_textbox().\n");
+       exit (-1);
+    }
+    buf[bytes_read] = '\0';    /* mark end of valid data */
+    page = buf;                        /* page is pointer to start of page to be displayed */
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    /* Create window for text region, used for scrolling text */
+    text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
+    wattrset (text, dialog_attr);
+    wbkgdset (text, dialog_attr & A_COLOR);
+
+    keypad (text, TRUE);
+
+    /* register the new window, along with its borders */
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+       waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    wbkgdset (dialog, dialog_attr & A_COLOR);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL && strlen(title) >= width-2 ) {
+       /* truncate long title -- mec */
+       char * title2 = malloc(width-2+1);
+       memcpy( title2, title, width-2 );
+       title2[width-2] = '\0';
+       title = title2;
+    }
+
+    if (title != NULL) {
+       wattrset (dialog, title_attr);
+       mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+       waddstr (dialog, (char *)title);
+       waddch (dialog, ' ');
+    }
+    print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
+    wnoutrefresh (dialog);
+    getyx (dialog, cur_y, cur_x);      /* Save cursor position */
+
+    /* Print first page of text */
+    attr_clear (text, height - 4, width - 2, dialog_attr);
+    print_page (text, height - 4, width - 2);
+    print_position (dialog, height, width);
+    wmove (dialog, cur_y, cur_x);      /* Restore cursor position */
+    wrefresh (dialog);
+
+    while ((key != ESC) && (key != '\n')) {
+       key = wgetch (dialog);
+       switch (key) {
+       case 'E':               /* Exit */
+       case 'e':
+       case 'X':
+       case 'x':
+           delwin (dialog);
+           free (buf);
+           close (fd);
+           return 0;
+       case 'g':               /* First page */
+       case KEY_HOME:
+           if (!begin_reached) {
+               begin_reached = 1;
+               /* First page not in buffer? */
+               if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+                   endwin ();
+                   fprintf (stderr,
+                     "\nError moving file pointer in dialog_textbox().\n");
+                   exit (-1);
+               }
+               if (fpos > bytes_read) {        /* Yes, we have to read it in */
+                   if (lseek (fd, 0, SEEK_SET) == -1) {
+                       endwin ();
+                       fprintf (stderr, "\nError moving file pointer in "
+                                "dialog_textbox().\n");
+                       exit (-1);
+                   }
+                   if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+                       endwin ();
+                       fprintf (stderr,
+                            "\nError reading file in dialog_textbox().\n");
+                       exit (-1);
+                   }
+                   buf[bytes_read] = '\0';
+               }
+               page = buf;
+               print_page (text, height - 4, width - 2);
+               print_position (dialog, height, width);
+               wmove (dialog, cur_y, cur_x);   /* Restore cursor position */
+               wrefresh (dialog);
+           }
+           break;
+       case 'G':               /* Last page */
+       case KEY_END:
+
+           end_reached = 1;
+           /* Last page not in buffer? */
+           if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+               endwin ();
+               fprintf (stderr,
+                     "\nError moving file pointer in dialog_textbox().\n");
+               exit (-1);
+           }
+           if (fpos < file_size) {     /* Yes, we have to read it in */
+               if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) {
+                   endwin ();
+                   fprintf (stderr,
+                     "\nError moving file pointer in dialog_textbox().\n");
+                   exit (-1);
+               }
+               if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+                   endwin ();
+                   fprintf (stderr,
+                            "\nError reading file in dialog_textbox().\n");
+                   exit (-1);
+               }
+               buf[bytes_read] = '\0';
+           }
+           page = buf + bytes_read;
+           back_lines (height - 4);
+           print_page (text, height - 4, width - 2);
+           print_position (dialog, height, width);
+           wmove (dialog, cur_y, cur_x);       /* Restore cursor position */
+           wrefresh (dialog);
+           break;
+       case 'K':               /* Previous line */
+       case 'k':
+       case KEY_UP:
+           if (!begin_reached) {
+               back_lines (page_length + 1);
+
+               /* We don't call print_page() here but use scrolling to ensure
+                  faster screen update. However, 'end_reached' and
+                  'page_length' should still be updated, and 'page' should
+                  point to start of next page. This is done by calling
+                  get_line() in the following 'for' loop. */
+               scrollok (text, TRUE);
+               wscrl (text, -1);       /* Scroll text region down one line */
+               scrollok (text, FALSE);
+               page_length = 0;
+               passed_end = 0;
+               for (i = 0; i < height - 4; i++) {
+                   if (!i) {
+                       /* print first line of page */
+                       print_line (text, 0, width - 2);
+                       wnoutrefresh (text);
+                   } else
+                       /* Called to update 'end_reached' and 'page' */
+                       get_line ();
+                   if (!passed_end)
+                       page_length++;
+                   if (end_reached && !passed_end)
+                       passed_end = 1;
+               }
+
+               print_position (dialog, height, width);
+               wmove (dialog, cur_y, cur_x);   /* Restore cursor position */
+               wrefresh (dialog);
+           }
+           break;
+       case 'B':               /* Previous page */
+       case 'b':
+       case KEY_PPAGE:
+           if (begin_reached)
+               break;
+           back_lines (page_length + height - 4);
+           print_page (text, height - 4, width - 2);
+           print_position (dialog, height, width);
+           wmove (dialog, cur_y, cur_x);
+           wrefresh (dialog);
+           break;
+       case 'J':               /* Next line */
+       case 'j':
+       case KEY_DOWN:
+           if (!end_reached) {
+               begin_reached = 0;
+               scrollok (text, TRUE);
+               scroll (text);  /* Scroll text region up one line */
+               scrollok (text, FALSE);
+               print_line (text, height - 5, width - 2);
+               wnoutrefresh (text);
+               print_position (dialog, height, width);
+               wmove (dialog, cur_y, cur_x);   /* Restore cursor position */
+               wrefresh (dialog);
+           }
+           break;
+       case KEY_NPAGE:         /* Next page */
+       case ' ':
+           if (end_reached)
+               break;
+
+           begin_reached = 0;
+           print_page (text, height - 4, width - 2);
+           print_position (dialog, height, width);
+           wmove (dialog, cur_y, cur_x);
+           wrefresh (dialog);
+           break;
+       case '0':               /* Beginning of line */
+       case 'H':               /* Scroll left */
+       case 'h':
+       case KEY_LEFT:
+           if (hscroll <= 0)
+               break;
+
+           if (key == '0')
+               hscroll = 0;
+           else
+               hscroll--;
+           /* Reprint current page to scroll horizontally */
+           back_lines (page_length);
+           print_page (text, height - 4, width - 2);
+           wmove (dialog, cur_y, cur_x);
+           wrefresh (dialog);
+           break;
+       case 'L':               /* Scroll right */
+       case 'l':
+       case KEY_RIGHT:
+           if (hscroll >= MAX_LEN)
+               break;
+           hscroll++;
+           /* Reprint current page to scroll horizontally */
+           back_lines (page_length);
+           print_page (text, height - 4, width - 2);
+           wmove (dialog, cur_y, cur_x);
+           wrefresh (dialog);
+           break;
+       case ESC:
+           break;
+       }
+    }
+
+    delwin (dialog);
+    free (buf);
+    close (fd);
+    return 1;                  /* ESC pressed */
+}
+
+/*
+ * Go back 'n' lines in text file. Called by dialog_textbox().
+ * 'page' will be updated to point to the desired line in 'buf'.
+ */
+static void
+back_lines (int n)
+{
+    int i, fpos;
+
+    begin_reached = 0;
+    /* We have to distinguish between end_reached and !end_reached
+       since at end of file, the line is not ended by a '\n'.
+       The code inside 'if' basically does a '--page' to move one
+       character backward so as to skip '\n' of the previous line */
+    if (!end_reached) {
+       /* Either beginning of buffer or beginning of file reached? */
+       if (page == buf) {
+           if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+               endwin ();
+               fprintf (stderr, "\nError moving file pointer in "
+                        "back_lines().\n");
+               exit (-1);
+           }
+           if (fpos > bytes_read) {    /* Not beginning of file yet */
+               /* We've reached beginning of buffer, but not beginning of
+                  file yet, so read previous part of file into buffer.
+                  Note that we only move backward for BUF_SIZE/2 bytes,
+                  but not BUF_SIZE bytes to avoid re-reading again in
+                  print_page() later */
+               /* Really possible to move backward BUF_SIZE/2 bytes? */
+               if (fpos < BUF_SIZE / 2 + bytes_read) {
+                   /* No, move less then */
+                   if (lseek (fd, 0, SEEK_SET) == -1) {
+                       endwin ();
+                       fprintf (stderr, "\nError moving file pointer in "
+                                "back_lines().\n");
+                       exit (-1);
+                   }
+                   page = buf + fpos - bytes_read;
+               } else {        /* Move backward BUF_SIZE/2 bytes */
+                   if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR)
+                       == -1) {
+                       endwin ();
+                       fprintf (stderr, "\nError moving file pointer "
+                                "in back_lines().\n");
+                       exit (-1);
+                   }
+                   page = buf + BUF_SIZE / 2;
+               }
+               if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+                   endwin ();
+                   fprintf (stderr, "\nError reading file in back_lines().\n");
+                   exit (-1);
+               }
+               buf[bytes_read] = '\0';
+           } else {            /* Beginning of file reached */
+               begin_reached = 1;
+               return;
+           }
+       }
+       if (*(--page) != '\n') {        /* '--page' here */
+           /* Something's wrong... */
+           endwin ();
+           fprintf (stderr, "\nInternal error in back_lines().\n");
+           exit (-1);
+       }
+    }
+    /* Go back 'n' lines */
+    for (i = 0; i < n; i++)
+       do {
+           if (page == buf) {
+               if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+                   endwin ();
+                   fprintf (stderr,
+                         "\nError moving file pointer in back_lines().\n");
+                   exit (-1);
+               }
+               if (fpos > bytes_read) {
+                   /* Really possible to move backward BUF_SIZE/2 bytes? */
+                   if (fpos < BUF_SIZE / 2 + bytes_read) {
+                       /* No, move less then */
+                       if (lseek (fd, 0, SEEK_SET) == -1) {
+                           endwin ();
+                           fprintf (stderr, "\nError moving file pointer "
+                                    "in back_lines().\n");
+                           exit (-1);
+                       }
+                       page = buf + fpos - bytes_read;
+                   } else {    /* Move backward BUF_SIZE/2 bytes */
+                       if (lseek (fd, -(BUF_SIZE / 2 + bytes_read),
+                                  SEEK_CUR) == -1) {
+                           endwin ();
+                           fprintf (stderr, "\nError moving file pointer"
+                                    " in back_lines().\n");
+                           exit (-1);
+                       }
+                       page = buf + BUF_SIZE / 2;
+                   }
+                   if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+                       endwin ();
+                       fprintf (stderr, "\nError reading file in "
+                                "back_lines().\n");
+                       exit (-1);
+                   }
+                   buf[bytes_read] = '\0';
+               } else {        /* Beginning of file reached */
+                   begin_reached = 1;
+                   return;
+               }
+           }
+       } while (*(--page) != '\n');
+    page++;
+}
+
+/*
+ * Print a new page of text. Called by dialog_textbox().
+ */
+static void
+print_page (WINDOW * win, int height, int width)
+{
+    int i, passed_end = 0;
+
+    page_length = 0;
+    for (i = 0; i < height; i++) {
+       print_line (win, i, width);
+       if (!passed_end)
+           page_length++;
+       if (end_reached && !passed_end)
+           passed_end = 1;
+    }
+    wnoutrefresh (win);
+}
+
+/*
+ * Print a new line of text. Called by dialog_textbox() and print_page().
+ */
+static void
+print_line (WINDOW * win, int row, int width)
+{
+    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, fpos;
+    static char line[MAX_LEN + 1];
+
+    end_reached = 0;
+    while (*page != '\n') {
+       if (*page == '\0') {
+           /* Either end of file or end of buffer reached */
+           if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+               endwin ();
+               fprintf (stderr, "\nError moving file pointer in "
+                        "get_line().\n");
+               exit (-1);
+           }
+           if (fpos < file_size) {     /* Not end of file yet */
+               /* We've reached end of buffer, but not end of file yet,
+                  so read next part of file into buffer */
+               if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+                   endwin ();
+                   fprintf (stderr, "\nError reading file in get_line().\n");
+                   exit (-1);
+               }
+               buf[bytes_read] = '\0';
+               page = buf;
+           } else {
+               if (!end_reached)
+                   end_reached = 1;
+               break;
+           }
+       } else if (i < MAX_LEN)
+           line[i++] = *(page++);
+       else {
+           /* Truncate lines longer than MAX_LEN characters */
+           if (i == MAX_LEN)
+               line[i++] = '\0';
+           page++;
+       }
+    }
+    if (i <= MAX_LEN)
+       line[i] = '\0';
+    if (!end_reached)
+       page++;                 /* move pass '\n' */
+
+    return line;
+}
+
+/*
+ * Print current position
+ */
+static void
+print_position (WINDOW * win, int height, int width)
+{
+    int fpos, percent;
+
+    if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+       endwin ();
+       fprintf (stderr, "\nError moving file pointer in print_position().\n");
+       exit (-1);
+    }
+    wattrset (win, position_indicator_attr);
+    wbkgdset (win, position_indicator_attr & A_COLOR);
+    percent = !file_size ?
+       100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
+    wmove (win, height - 3, width - 9);
+    wprintw (win, "(%3d%%)", percent);
+}
diff --git a/busybox/scripts/config/util.c b/busybox/scripts/config/util.c
new file mode 100644 (file)
index 0000000..0a2f827
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ *  util.c
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+
+/* use colors by default? */
+bool use_colors = 1;
+
+char *backtitle = NULL;
+
+const char *dialog_result;
+
+/*
+ * Attribute values, default is for mono display
+ */
+chtype attributes[] =
+{
+    A_NORMAL,                  /* screen_attr */
+    A_NORMAL,                  /* shadow_attr */
+    A_NORMAL,                  /* dialog_attr */
+    A_BOLD,                    /* title_attr */
+    A_NORMAL,                  /* border_attr */
+    A_REVERSE,                 /* button_active_attr */
+    A_DIM,                     /* button_inactive_attr */
+    A_REVERSE,                 /* button_key_active_attr */
+    A_BOLD,                    /* button_key_inactive_attr */
+    A_REVERSE,                 /* button_label_active_attr */
+    A_NORMAL,                  /* button_label_inactive_attr */
+    A_NORMAL,                  /* inputbox_attr */
+    A_NORMAL,                  /* inputbox_border_attr */
+    A_NORMAL,                  /* searchbox_attr */
+    A_BOLD,                    /* searchbox_title_attr */
+    A_NORMAL,                  /* searchbox_border_attr */
+    A_BOLD,                    /* position_indicator_attr */
+    A_NORMAL,                  /* menubox_attr */
+    A_NORMAL,                  /* menubox_border_attr */
+    A_NORMAL,                  /* item_attr */
+    A_REVERSE,                 /* item_selected_attr */
+    A_BOLD,                    /* tag_attr */
+    A_REVERSE,                 /* tag_selected_attr */
+    A_BOLD,                    /* tag_key_attr */
+    A_REVERSE,                 /* tag_key_selected_attr */
+    A_BOLD,                    /* check_attr */
+    A_REVERSE,                 /* check_selected_attr */
+    A_BOLD,                    /* uarrow_attr */
+    A_BOLD                     /* darrow_attr */
+};
+
+
+#include "colors.h"
+
+/*
+ * Table of color values
+ */
+int color_table[][3] =
+{
+    {SCREEN_FG, SCREEN_BG, SCREEN_HL},
+    {SHADOW_FG, SHADOW_BG, SHADOW_HL},
+    {DIALOG_FG, DIALOG_BG, DIALOG_HL},
+    {TITLE_FG, TITLE_BG, TITLE_HL},
+    {BORDER_FG, BORDER_BG, BORDER_HL},
+    {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
+    {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
+    {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
+    {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL},
+    {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL},
+    {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
+     BUTTON_LABEL_INACTIVE_HL},
+    {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
+    {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
+    {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
+    {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
+    {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
+    {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
+    {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
+    {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
+    {ITEM_FG, ITEM_BG, ITEM_HL},
+    {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
+    {TAG_FG, TAG_BG, TAG_HL},
+    {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
+    {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
+    {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
+    {CHECK_FG, CHECK_BG, CHECK_HL},
+    {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
+    {UARROW_FG, UARROW_BG, UARROW_HL},
+    {DARROW_FG, DARROW_BG, DARROW_HL},
+};                             /* color_table */
+
+/*
+ * Set window to attribute 'attr'
+ */
+void
+attr_clear (WINDOW * win, int height, int width, chtype attr)
+{
+    int i, j;
+
+    wattrset (win, attr);
+    for (i = 0; i < height; i++) {
+       wmove (win, i, 0);
+       for (j = 0; j < width; j++)
+           waddch (win, ' ');
+    }
+    touchwin (win);
+}
+
+void dialog_clear (void)
+{
+    attr_clear (stdscr, LINES, COLS, screen_attr);
+    /* Display background title if it exists ... - SLH */
+    if (backtitle != NULL) {
+        int i;
+
+        wattrset (stdscr, screen_attr);
+        mvwaddstr (stdscr, 0, 1, (char *)backtitle);
+        wmove (stdscr, 1, 1);
+        for (i = 1; i < COLS - 1; i++)
+            waddch (stdscr, ACS_HLINE);
+    }
+    wnoutrefresh (stdscr);
+}
+
+/*
+ * Do some initialization for dialog
+ */
+void
+init_dialog (void)
+{
+    initscr ();                        /* Init curses */
+    keypad (stdscr, TRUE);
+    cbreak ();
+    noecho ();
+
+
+    if (use_colors)    /* Set up colors */
+       color_setup ();
+
+
+    dialog_clear ();
+}
+
+/*
+ * Setup for color display
+ */
+void
+color_setup (void)
+{
+    int i;
+
+    if (has_colors ()) {       /* Terminal supports color? */
+       start_color ();
+
+       /* Initialize color pairs */
+       for (i = 0; i < ATTRIBUTE_COUNT; i++)
+           init_pair (i + 1, color_table[i][0], color_table[i][1]);
+
+       /* Setup color attributes */
+       for (i = 0; i < ATTRIBUTE_COUNT; i++)
+           attributes[i] = C_ATTR (color_table[i][2], i + 1);
+    }
+}
+
+/*
+ * End using dialog functions.
+ */
+void
+end_dialog (void)
+{
+    endwin ();
+}
+
+
+/*
+ * Print 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 ? button_active_attr : button_inactive_attr);
+    waddstr (win, "<");
+    temp = strspn (label, " ");
+    label += temp;
+    wattrset (win, selected ? button_label_active_attr
+             : button_label_inactive_attr);
+    for (i = 0; i < temp; i++)
+       waddch (win, ' ');
+    wattrset (win, selected ? button_key_active_attr
+             : button_key_inactive_attr);
+    waddch (win, label[0]);
+    wattrset (win, selected ? button_label_active_attr
+             : button_label_inactive_attr);
+    waddstr (win, (char *)label + 1);
+    wattrset (win, selected ? button_active_attr : button_inactive_attr);
+    waddstr (win, ">");
+    wmove (win, y, x + temp + 1);
+}
+
+/*
+ * Draw a rectangular box with line drawing characters
+ */
+void
+draw_box (WINDOW * win, int y, int x, int height, int width,
+         chtype box, chtype border)
+{
+    int i, j;
+
+    wattrset (win, 0);
+    for (i = 0; i < height; i++) {
+       wmove (win, y + i, x);
+       for (j = 0; j < width; j++)
+           if (!i && !j)
+               waddch (win, border | ACS_ULCORNER);
+           else if (i == height - 1 && !j)
+               waddch (win, border | ACS_LLCORNER);
+           else if (!i && j == width - 1)
+               waddch (win, box | ACS_URCORNER);
+           else if (i == height - 1 && j == width - 1)
+               waddch (win, box | ACS_LRCORNER);
+           else if (!i)
+               waddch (win, border | ACS_HLINE);
+           else if (i == height - 1)
+               waddch (win, box | ACS_HLINE);
+           else if (!j)
+               waddch (win, border | ACS_VLINE);
+           else if (j == width - 1)
+               waddch (win, box | ACS_VLINE);
+           else
+               waddch (win, box | ' ');
+    }
+}
+
+/*
+ * Draw shadows along the right and bottom edge to give a more 3D look
+ * to the boxes
+ */
+void
+draw_shadow (WINDOW * win, int y, int x, int height, int width)
+{
+    int i;
+
+    if (has_colors ()) {       /* Whether terminal supports color? */
+       wattrset (win, shadow_attr);
+       wmove (win, y + height, x + 2);
+       for (i = 0; i < width; i++)
+           waddch (win, winch (win) & A_CHARTEXT);
+       for (i = y + 1; i < y + height + 1; i++) {
+           wmove (win, i, x + width);
+           waddch (win, winch (win) & A_CHARTEXT);
+           waddch (win, winch (win) & A_CHARTEXT);
+       }
+       wnoutrefresh (win);
+    }
+}
+
+/*
+ *  Return the position of the first alphabetic character in a string.
+ */
+int
+first_alpha(const char *string, const char *exempt)
+{
+       int i, in_paren=0, c;
+
+       for (i = 0; i < strlen(string); i++) {
+               c = tolower(string[i]);
+
+               if (strchr("<[(", c)) ++in_paren;
+               if (strchr(">])", c) && in_paren > 0) --in_paren;
+
+               if ((! in_paren) && isalpha(c) &&
+                    strchr(exempt, c) == 0)
+                       return i;
+       }
+
+       return 0;
+}
+
+/*
+ * Get the first selected item in the dialog_list_item list.
+ */
+struct dialog_list_item *
+first_sel_item(int item_no, struct dialog_list_item ** items)
+{
+       int i;
+
+       for (i = 0; i < item_no; i++) {
+               if (items[i]->selected)
+                       return items[i];
+       }
+
+       return NULL;
+}
diff --git a/busybox/scripts/config/yesno.c b/busybox/scripts/config/yesno.c
new file mode 100644 (file)
index 0000000..11fcc25
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *  yesno.c -- implements the yes/no box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display termination buttons
+ */
+static void
+print_buttons(WINDOW *dialog, int height, int width, int selected)
+{
+    int x = width / 2 - 10;
+    int y = height - 2;
+
+    print_button (dialog, " Yes ", y, x, selected == 0);
+    print_button (dialog, "  No  ", y, x + 13, selected == 1);
+
+    wmove(dialog, y, x+1 + 13*selected );
+    wrefresh (dialog);
+}
+
+/*
+ * Display a dialog box with two buttons - Yes and No
+ */
+int
+dialog_yesno (const char *title, const char *prompt, int height, int width)
+{
+    int i, x, y, key = 0, button = 0;
+    WINDOW *dialog;
+
+    /* center dialog box on screen */
+    x = (COLS - width) / 2;
+    y = (LINES - height) / 2;
+
+    draw_shadow (stdscr, y, x, height, width);
+
+    dialog = newwin (height, width, y, x);
+    keypad (dialog, TRUE);
+
+    draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+    wattrset (dialog, border_attr);
+    mvwaddch (dialog, height-3, 0, ACS_LTEE);
+    for (i = 0; i < width - 2; i++)
+       waddch (dialog, ACS_HLINE);
+    wattrset (dialog, dialog_attr);
+    waddch (dialog, ACS_RTEE);
+
+    if (title != NULL && strlen(title) >= width-2 ) {
+       /* truncate long title -- mec */
+       char * title2 = malloc(width-2+1);
+       memcpy( title2, title, width-2 );
+       title2[width-2] = '\0';
+       title = title2;
+    }
+
+    if (title != NULL) {
+       wattrset (dialog, title_attr);
+       mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+       waddstr (dialog, (char *)title);
+       waddch (dialog, ' ');
+    }
+
+    wattrset (dialog, dialog_attr);
+    print_autowrap (dialog, prompt, width - 2, 1, 3);
+
+    print_buttons(dialog, height, width, 0);
+
+    while (key != ESC) {
+       key = wgetch (dialog);
+       switch (key) {
+       case 'Y':
+       case 'y':
+           delwin (dialog);
+           return 0;
+       case 'N':
+       case 'n':
+           delwin (dialog);
+           return 1;
+
+       case TAB:
+       case KEY_LEFT:
+       case KEY_RIGHT:
+           button = ((key == KEY_LEFT ? --button : ++button) < 0)
+                       ? 1 : (button > 1 ? 0 : button);
+
+           print_buttons(dialog, height, width, button);
+           wrefresh (dialog);
+           break;
+       case ' ':
+       case '\n':
+           delwin (dialog);
+           return button;
+       case ESC:
+           break;
+       }
+    }
+
+    delwin (dialog);
+    return -1;                 /* ESC pressed */
+}
diff --git a/busybox/scripts/config/zconf.l b/busybox/scripts/config/zconf.l
new file mode 100644 (file)
index 0000000..55517b2
--- /dev/null
@@ -0,0 +1,366 @@
+%option backup nostdinit noyywrap never-interactive full ecs
+%option 8bit backup nodefault perf-report perf-report
+%x COMMAND HELP STRING PARAM
+%{
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE  16
+
+char *text;
+static char *text_ptr;
+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 struct buffer *zconf_endfile(void);
+
+void new_string(void)
+{
+       text = malloc(START_STRSIZE);
+       text_asize = START_STRSIZE;
+       text_ptr = text;
+       text_size = 0;
+       *text_ptr = 0;
+}
+
+void append_string(const char *str, int size)
+{
+       int new_size = text_size + size + 1;
+       if (new_size > text_asize) {
+               text = realloc(text, new_size);
+               text_asize = new_size;
+               text_ptr = text + text_size;
+       }
+       memcpy(text_ptr, str, size);
+       text_ptr += size;
+       text_size += size;
+       *text_ptr = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+       text = malloc(size + 1);
+       memcpy(text, str, size);
+       text[size] = 0;
+}
+%}
+
+ws     [ \n\t]
+n      [A-Za-z0-9_]
+
+%%
+       int str = 0;
+       int ts, i;
+
+[ \t]*#.*\n    current_file->lineno++;
+[ \t]*#.*
+
+[ \t]*\n       current_file->lineno++; return T_EOL;
+
+[ \t]+ {
+       BEGIN(COMMAND);
+}
+
+.      {
+       unput(yytext[0]);
+       BEGIN(COMMAND);
+}
+
+
+<COMMAND>{
+       "mainmenu"              BEGIN(PARAM); return T_MAINMENU;
+       "menu"                  BEGIN(PARAM); return T_MENU;
+       "endmenu"               BEGIN(PARAM); return T_ENDMENU;
+       "source"                BEGIN(PARAM); return T_SOURCE;
+       "choice"                BEGIN(PARAM); return T_CHOICE;
+       "endchoice"             BEGIN(PARAM); return T_ENDCHOICE;
+       "comment"               BEGIN(PARAM); return T_COMMENT;
+       "config"                BEGIN(PARAM); return T_CONFIG;
+       "menuconfig"            BEGIN(PARAM); return T_MENUCONFIG;
+       "help"                  BEGIN(PARAM); return T_HELP;
+       "if"                    BEGIN(PARAM); return T_IF;
+       "endif"                 BEGIN(PARAM); return T_ENDIF;
+       "depends"               BEGIN(PARAM); return T_DEPENDS;
+       "requires"              BEGIN(PARAM); return T_REQUIRES;
+       "optional"              BEGIN(PARAM); return T_OPTIONAL;
+       "default"               BEGIN(PARAM); return T_DEFAULT;
+       "prompt"                BEGIN(PARAM); return T_PROMPT;
+       "tristate"              BEGIN(PARAM); return T_TRISTATE;
+       "def_tristate"          BEGIN(PARAM); return T_DEF_TRISTATE;
+       "bool"                  BEGIN(PARAM); return T_BOOLEAN;
+       "boolean"               BEGIN(PARAM); return T_BOOLEAN;
+       "def_bool"              BEGIN(PARAM); return T_DEF_BOOLEAN;
+       "def_boolean"           BEGIN(PARAM); return T_DEF_BOOLEAN;
+       "int"                   BEGIN(PARAM); return T_INT;
+       "hex"                   BEGIN(PARAM); return T_HEX;
+       "string"                BEGIN(PARAM); return T_STRING;
+       "select"                BEGIN(PARAM); return T_SELECT;
+       "enable"                BEGIN(PARAM); return T_SELECT;
+       "range"                 BEGIN(PARAM); return T_RANGE;
+       {n}+    {
+               alloc_string(yytext, yyleng);
+               zconflval.string = text;
+               return T_WORD;
+       }
+       .
+       \n      current_file->lineno++; BEGIN(INITIAL);
+}
+
+<PARAM>{
+       "&&"    return T_AND;
+       "||"    return T_OR;
+       "("     return T_OPEN_PAREN;
+       ")"     return T_CLOSE_PAREN;
+       "!"     return T_NOT;
+       "="     return T_EQUAL;
+       "!="    return T_UNEQUAL;
+       "if"    return T_IF;
+       "on"    return T_ON;
+       \"|\'   {
+               str = yytext[0];
+               new_string();
+               BEGIN(STRING);
+       }
+       \n      BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+       ---     /* ignore */
+       ({n}|[-/.])+    {
+               alloc_string(yytext, yyleng);
+               zconflval.string = text;
+               return T_WORD;
+       }
+       #.*     /* comment */
+       \\\n    current_file->lineno++;
+       .
+       <<EOF>> {
+               BEGIN(INITIAL);
+       }
+}
+
+<STRING>{
+       [^'"\\\n]+/\n   {
+               append_string(yytext, yyleng);
+               zconflval.string = text;
+               return T_WORD_QUOTE;
+       }
+       [^'"\\\n]+      {
+               append_string(yytext, yyleng);
+       }
+       \\.?/\n {
+               append_string(yytext + 1, yyleng - 1);
+               zconflval.string = text;
+               return T_WORD_QUOTE;
+       }
+       \\.?    {
+               append_string(yytext + 1, yyleng - 1);
+       }
+       \'|\"   {
+               if (str == yytext[0]) {
+                       BEGIN(PARAM);
+                       zconflval.string = text;
+                       return T_WORD_QUOTE;
+               } else
+                       append_string(yytext, 1);
+       }
+       \n      {
+               printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+               current_file->lineno++;
+               BEGIN(INITIAL);
+               return T_EOL;
+       }
+       <<EOF>> {
+               BEGIN(INITIAL);
+       }
+}
+
+<HELP>{
+       [ \t]+  {
+               ts = 0;
+               for (i = 0; i < yyleng; i++) {
+                       if (yytext[i] == '\t')
+                               ts = (ts & ~7) + 8;
+                       else
+                               ts++;
+               }
+               last_ts = ts;
+               if (first_ts) {
+                       if (ts < first_ts) {
+                               zconf_endhelp();
+                               return T_HELPTEXT;
+                       }
+                       ts -= first_ts;
+                       while (ts > 8) {
+                               append_string("        ", 8);
+                               ts -= 8;
+                       }
+                       append_string("        ", ts);
+               }
+       }
+       [ \t]*\n/[^ \t\n] {
+               current_file->lineno++;
+               zconf_endhelp();
+               return T_HELPTEXT;
+       }
+       [ \t]*\n        {
+               current_file->lineno++;
+               append_string("\n", 1);
+       }
+       [^ \t\n].* {
+               append_string(yytext, yyleng);
+               if (!first_ts)
+                       first_ts = last_ts;
+       }
+       <<EOF>> {
+               zconf_endhelp();
+               return T_HELPTEXT;
+       }
+}
+
+<<EOF>>        {
+       if (current_buf) {
+               zconf_endfile();
+               return T_EOF;
+       }
+       fclose(yyin);
+       yyterminate();
+}
+
+%%
+void zconf_starthelp(void)
+{
+       new_string();
+       last_ts = first_ts = 0;
+       BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+       zconflval.string = text;
+       BEGIN(INITIAL);
+}
+
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+       char *env, 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)
+{
+       yyin = zconf_fopen(name);
+       if (!yyin) {
+               printf("can't find file %s\n", name);
+               exit(1);
+       }
+
+       current_buf = malloc(sizeof(*current_buf));
+       memset(current_buf, 0, sizeof(*current_buf));
+
+       current_file = file_lookup(name);
+       current_file->lineno = 1;
+       current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+       struct file *file = file_lookup(name);
+       struct buffer *buf = malloc(sizeof(*buf));
+       memset(buf, 0, sizeof(*buf));
+
+       current_buf->state = YY_CURRENT_BUFFER;
+       yyin = zconf_fopen(name);
+       if (!yyin) {
+               printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+               exit(1);
+       }
+       yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+       buf->parent = current_buf;
+       current_buf = buf;
+
+       if (file->flags & FILE_BUSY) {
+               printf("recursive scan (%s)?\n", name);
+               exit(1);
+       }
+       if (file->flags & FILE_SCANNED) {
+               printf("file %s already scanned?\n", name);
+               exit(1);
+       }
+       file->flags |= FILE_BUSY;
+       file->lineno = 1;
+       file->parent = current_file;
+       current_file = file;
+}
+
+static struct buffer *zconf_endfile(void)
+{
+       struct buffer *parent;
+
+       current_file->flags |= FILE_SCANNED;
+       current_file->flags &= ~FILE_BUSY;
+       current_file = current_file->parent;
+
+       parent = current_buf->parent;
+       if (parent) {
+               fclose(yyin);
+               yy_delete_buffer(YY_CURRENT_BUFFER);
+               yy_switch_to_buffer(parent->state);
+       }
+       free(current_buf);
+       current_buf = parent;
+
+       return parent;
+}
+
+int zconf_lineno(void)
+{
+       if (current_buf)
+               return current_file->lineno - 1;
+       else
+               return 0;
+}
+
+char *zconf_curname(void)
+{
+       if (current_buf)
+               return current_file->name;
+       else
+               return "<none>";
+}
diff --git a/busybox/scripts/config/zconf.tab.c_shipped b/busybox/scripts/config/zconf.tab.c_shipped
new file mode 100644 (file)
index 0000000..a5f69a0
--- /dev/null
@@ -0,0 +1,2127 @@
+/* A Bison parser, made by GNU Bison 1.875a.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+/* If NAME_PREFIX is specified substitute the variables and functions
+   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_DEFAULT = 275,
+     T_TRISTATE = 276,
+     T_DEF_TRISTATE = 277,
+     T_BOOLEAN = 278,
+     T_DEF_BOOLEAN = 279,
+     T_STRING = 280,
+     T_INT = 281,
+     T_HEX = 282,
+     T_WORD = 283,
+     T_WORD_QUOTE = 284,
+     T_UNEQUAL = 285,
+     T_EOF = 286,
+     T_EOL = 287,
+     T_CLOSE_PAREN = 288,
+     T_OPEN_PAREN = 289,
+     T_ON = 290,
+     T_SELECT = 291,
+     T_RANGE = 292,
+     T_OR = 293,
+     T_AND = 294,
+     T_EQUAL = 295,
+     T_NOT = 296
+   };
+#endif
+#define T_MAINMENU 258
+#define T_MENU 259
+#define T_ENDMENU 260
+#define T_SOURCE 261
+#define T_CHOICE 262
+#define T_ENDCHOICE 263
+#define T_COMMENT 264
+#define T_CONFIG 265
+#define T_MENUCONFIG 266
+#define T_HELP 267
+#define T_HELPTEXT 268
+#define T_IF 269
+#define T_ENDIF 270
+#define T_DEPENDS 271
+#define T_REQUIRES 272
+#define T_OPTIONAL 273
+#define T_PROMPT 274
+#define T_DEFAULT 275
+#define T_TRISTATE 276
+#define T_DEF_TRISTATE 277
+#define T_BOOLEAN 278
+#define T_DEF_BOOLEAN 279
+#define T_STRING 280
+#define T_INT 281
+#define T_HEX 282
+#define T_WORD 283
+#define T_WORD_QUOTE 284
+#define T_UNEQUAL 285
+#define T_EOF 286
+#define T_EOL 287
+#define T_CLOSE_PAREN 288
+#define T_OPEN_PAREN 289
+#define T_ON 290
+#define T_SELECT 291
+#define T_RANGE 292
+#define T_OR 293
+#define T_AND 294
+#define T_EQUAL 295
+#define T_NOT 296
+
+
+
+
+/* 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 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 zconferror(const char *err);
+static bool zconf_endtoken(int token, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+#define YYERROR_VERBOSE
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+
+typedef union YYSTYPE {
+       int token;
+       char *string;
+       struct symbol *symbol;
+       struct expr *expr;
+       struct menu *menu;
+} YYSTYPE;
+/* Line 191 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.  */
+
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+
+/* Line 214 of yacc.c.  */
+
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# if YYSTACK_USE_ALLOCA
+#  define YYSTACK_ALLOC alloca
+# else
+#  ifndef YYSTACK_USE_ALLOCA
+#   if defined (alloca) || defined (_ALLOCA_H)
+#    define YYSTACK_ALLOC alloca
+#   else
+#    ifdef __GNUC__
+#     define YYSTACK_ALLOC __builtin_alloca
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC malloc
+#  define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+        || (YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short 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) + sizeof (YYSTYPE))                                \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         register YYSIZE_T yyi;                \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)                                       \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack, Stack, yysize);                          \
+       Stack = &yyptr->Stack;                                          \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   201
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  42
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  41
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  104
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  182
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   296
+
+#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,    36,    37,    38,    39,    40,    41
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned short yyprhs[] =
+{
+       0,     0,     3,     4,     7,     9,    11,    13,    17,    19,
+      21,    23,    26,    28,    30,    32,    34,    36,    38,    42,
+      45,    49,    52,    53,    56,    59,    62,    65,    69,    74,
+      78,    83,    87,    91,    95,   100,   105,   110,   116,   119,
+     122,   124,   128,   131,   132,   135,   138,   141,   144,   149,
+     153,   157,   160,   165,   166,   169,   173,   175,   179,   182,
+     183,   186,   189,   192,   196,   199,   201,   205,   208,   209,
+     212,   215,   218,   222,   226,   228,   232,   235,   238,   241,
+     242,   245,   248,   253,   257,   261,   262,   265,   267,   269,
+     272,   275,   278,   280,   282,   283,   286,   288,   292,   296,
+     300,   303,   307,   311,   313
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      43,     0,    -1,    -1,    43,    44,    -1,    45,    -1,    55,
+      -1,    66,    -1,     3,    77,    79,    -1,     5,    -1,    15,
+      -1,     8,    -1,     1,    79,    -1,    61,    -1,    71,    -1,
+      47,    -1,    49,    -1,    69,    -1,    79,    -1,    10,    28,
+      32,    -1,    46,    50,    -1,    11,    28,    32,    -1,    48,
+      50,    -1,    -1,    50,    51,    -1,    50,    75,    -1,    50,
+      73,    -1,    50,    32,    -1,    21,    76,    32,    -1,    22,
+      81,    80,    32,    -1,    23,    76,    32,    -1,    24,    81,
+      80,    32,    -1,    26,    76,    32,    -1,    27,    76,    32,
+      -1,    25,    76,    32,    -1,    19,    77,    80,    32,    -1,
+      20,    81,    80,    32,    -1,    36,    28,    80,    32,    -1,
+      37,    82,    82,    80,    32,    -1,     7,    32,    -1,    52,
+      56,    -1,    78,    -1,    53,    58,    54,    -1,    53,    58,
+      -1,    -1,    56,    57,    -1,    56,    75,    -1,    56,    73,
+      -1,    56,    32,    -1,    19,    77,    80,    32,    -1,    21,
+      76,    32,    -1,    23,    76,    32,    -1,    18,    32,    -1,
+      20,    28,    80,    32,    -1,    -1,    58,    45,    -1,    14,
+      81,    32,    -1,    78,    -1,    59,    62,    60,    -1,    59,
+      62,    -1,    -1,    62,    45,    -1,    62,    66,    -1,    62,
+      55,    -1,     4,    77,    32,    -1,    63,    74,    -1,    78,
+      -1,    64,    67,    65,    -1,    64,    67,    -1,    -1,    67,
+      45,    -1,    67,    66,    -1,    67,    55,    -1,    67,     1,
+      32,    -1,     6,    77,    32,    -1,    68,    -1,     9,    77,
+      32,    -1,    70,    74,    -1,    12,    32,    -1,    72,    13,
+      -1,    -1,    74,    75,    -1,    74,    32,    -1,    16,    35,
+      81,    32,    -1,    16,    81,    32,    -1,    17,    81,    32,
+      -1,    -1,    77,    80,    -1,    28,    -1,    29,    -1,     5,
+      79,    -1,     8,    79,    -1,    15,    79,    -1,    32,    -1,
+      31,    -1,    -1,    14,    81,    -1,    82,    -1,    82,    40,
+      82,    -1,    82,    30,    82,    -1,    34,    81,    33,    -1,
+      41,    81,    -1,    81,    38,    81,    -1,    81,    39,    81,
+      -1,    28,    -1,    29,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned short yyrline[] =
+{
+       0,    94,    94,    95,    98,    99,   100,   101,   102,   103,
+     104,   105,   109,   110,   111,   112,   113,   114,   120,   128,
+     134,   142,   152,   154,   155,   156,   157,   160,   166,   173,
+     179,   186,   192,   198,   204,   210,   216,   222,   230,   239,
+     245,   254,   255,   261,   263,   264,   265,   266,   269,   275,
+     281,   287,   293,   299,   301,   306,   315,   324,   325,   331,
+     333,   334,   335,   340,   347,   353,   362,   363,   369,   371,
+     372,   373,   374,   377,   383,   390,   397,   404,   410,   417,
+     418,   419,   422,   427,   432,   440,   442,   447,   448,   451,
+     452,   453,   457,   457,   459,   460,   463,   464,   465,   466,
+     467,   468,   469,   472,   473
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
+  "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
+  "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
+  "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_DEFAULT", "T_TRISTATE",
+  "T_DEF_TRISTATE", "T_BOOLEAN", "T_DEF_BOOLEAN", "T_STRING", "T_INT",
+  "T_HEX", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", "T_EOF", "T_EOL",
+  "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_ON", "T_SELECT", "T_RANGE", "T_OR",
+  "T_AND", "T_EQUAL", "T_NOT", "$accept", "input", "block",
+  "common_block", "config_entry_start", "config_stmt",
+  "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+  "config_option", "choice", "choice_entry", "choice_end", "choice_stmt",
+  "choice_option_list", "choice_option", "choice_block", "if", "if_end",
+  "if_stmt", "if_block", "menu", "menu_entry", "menu_end", "menu_stmt",
+  "menu_block", "source", "source_stmt", "comment", "comment_stmt",
+  "help_start", "help", "depends_list", "depends", "prompt_stmt_opt",
+  "prompt", "end", "nl_or_eof", "if_expr", "expr", "symbol", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short 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,   291,   292,   293,   294,
+     295,   296
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    42,    43,    43,    44,    44,    44,    44,    44,    44,
+      44,    44,    45,    45,    45,    45,    45,    45,    46,    47,
+      48,    49,    50,    50,    50,    50,    50,    51,    51,    51,
+      51,    51,    51,    51,    51,    51,    51,    51,    52,    53,
+      54,    55,    55,    56,    56,    56,    56,    56,    57,    57,
+      57,    57,    57,    58,    58,    59,    60,    61,    61,    62,
+      62,    62,    62,    63,    64,    65,    66,    66,    67,    67,
+      67,    67,    67,    68,    69,    70,    71,    72,    73,    74,
+      74,    74,    75,    75,    75,    76,    76,    77,    77,    78,
+      78,    78,    79,    79,    80,    80,    81,    81,    81,    81,
+      81,    81,    81,    82,    82
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     0,     2,     1,     1,     1,     3,     1,     1,
+       1,     2,     1,     1,     1,     1,     1,     1,     3,     2,
+       3,     2,     0,     2,     2,     2,     2,     3,     4,     3,
+       4,     3,     3,     3,     4,     4,     4,     5,     2,     2,
+       1,     3,     2,     0,     2,     2,     2,     2,     4,     3,
+       3,     2,     4,     0,     2,     3,     1,     3,     2,     0,
+       2,     2,     2,     3,     2,     1,     3,     2,     0,     2,
+       2,     2,     3,     3,     1,     3,     2,     2,     2,     0,
+       2,     2,     4,     3,     3,     0,     2,     1,     1,     2,
+       2,     2,     1,     1,     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[] =
+{
+       2,     0,     1,     0,     0,     0,     8,     0,     0,    10,
+       0,     0,     0,     0,     9,    93,    92,     3,     4,    22,
+      14,    22,    15,    43,    53,     5,    59,    12,    79,    68,
+       6,    74,    16,    79,    13,    17,    11,    87,    88,     0,
+       0,     0,    38,     0,     0,     0,   103,   104,     0,     0,
+       0,    96,    19,    21,    39,    42,    58,    64,     0,    76,
+       7,    63,    73,    75,    18,    20,     0,   100,    55,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,    85,     0,
+      85,     0,    85,    85,    85,    26,     0,     0,    23,     0,
+      25,    24,     0,     0,     0,    85,    85,    47,    44,    46,
+      45,     0,     0,     0,    54,    41,    40,    60,    62,    57,
+      61,    56,    81,    80,     0,    69,    71,    66,    70,    65,
+      99,   101,   102,    98,    97,    77,     0,     0,     0,    94,
+      94,     0,    94,    94,     0,    94,     0,     0,     0,    94,
+       0,    78,    51,    94,    94,     0,     0,    89,    90,    91,
+      72,     0,    83,    84,     0,     0,     0,    27,    86,     0,
+      29,     0,    33,    31,    32,     0,    94,     0,     0,    49,
+      50,    82,    95,    34,    35,    28,    30,    36,     0,    48,
+      52,    37
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short yydefgoto[] =
+{
+      -1,     1,    17,    18,    19,    20,    21,    22,    52,    88,
+      23,    24,   105,    25,    54,    98,    55,    26,   109,    27,
+      56,    28,    29,   117,    30,    58,    31,    32,    33,    34,
+      89,    90,    57,    91,   131,   132,   106,    35,   155,    50,
+      51
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -99
+static const short yypact[] =
+{
+     -99,    48,   -99,    38,    46,    46,   -99,    46,   -29,   -99,
+      46,   -17,    -3,   -11,   -99,   -99,   -99,   -99,   -99,   -99,
+     -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,
+     -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,    38,
+      12,    15,   -99,    18,    51,    62,   -99,   -99,   -11,   -11,
+       4,   -24,   138,   138,   160,   121,   110,    -4,    81,    -4,
+     -99,   -99,   -99,   -99,   -99,   -99,   -19,   -99,   -99,   -11,
+     -11,    70,    70,    73,    32,   -11,    46,   -11,    46,   -11,
+      46,   -11,    46,    46,    46,   -99,    36,    70,   -99,    95,
+     -99,   -99,    96,    46,   106,    46,    46,   -99,   -99,   -99,
+     -99,    38,    38,    38,   -99,   -99,   -99,   -99,   -99,   -99,
+     -99,   -99,   -99,   -99,   112,   -99,   -99,   -99,   -99,   -99,
+     -99,   117,   -99,   -99,   -99,   -99,   -11,    33,    65,   131,
+       1,   119,   131,     1,   136,     1,   153,   154,   155,   131,
+      70,   -99,   -99,   131,   131,   156,   157,   -99,   -99,   -99,
+     -99,   101,   -99,   -99,   -11,   158,   159,   -99,   -99,   161,
+     -99,   162,   -99,   -99,   -99,   163,   131,   164,   165,   -99,
+     -99,   -99,    99,   -99,   -99,   -99,   -99,   -99,   166,   -99,
+     -99,   -99
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const short yypgoto[] =
+{
+     -99,   -99,   -99,   111,   -99,   -99,   -99,   -99,   178,   -99,
+     -99,   -99,   -99,    91,   -99,   -99,   -99,   -99,   -99,   -99,
+     -99,   -99,   -99,   -99,   115,   -99,   -99,   -99,   -99,   -99,
+     -99,   146,   168,    89,    27,     0,   126,    -1,   -98,   -48,
+     -63
+};
+
+/* 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 -68
+static const short yytable[] =
+{
+      66,    67,    36,    42,    39,    40,    71,    41,   123,   124,
+      43,    44,    74,    75,   120,   154,    72,    46,    47,    69,
+      70,   121,   122,    48,   140,    45,   127,   128,   112,   130,
+      49,   133,   156,   135,   158,   159,    68,   161,    60,    69,
+      70,   165,    69,    70,    61,   167,   168,    62,     2,     3,
+      63,     4,     5,     6,     7,     8,     9,    10,    11,    12,
+      46,    47,    13,    14,   139,   152,    48,   126,   178,    15,
+      16,    69,    70,    49,    37,    38,   129,   166,   151,    15,
+      16,   -67,   114,    64,   -67,     5,   101,     7,     8,   102,
+      10,    11,    12,   143,    65,    13,   103,   153,    46,    47,
+     147,   148,   149,    69,    70,   125,   172,   134,   141,   136,
+     137,   138,    15,    16,     5,   101,     7,     8,   102,    10,
+      11,    12,   145,   146,    13,   103,   101,     7,   142,   102,
+      10,    11,    12,   171,   144,    13,   103,    69,    70,    69,
+      70,    15,    16,   100,   150,   154,   113,   108,   113,   116,
+      73,   157,    15,    16,    74,    75,    70,    76,    77,    78,
+      79,    80,    81,    82,    83,    84,   104,   107,   160,   115,
+      85,   110,    73,   118,    86,    87,    74,    75,    92,    93,
+      94,    95,   111,    96,   119,   162,   163,   164,   169,   170,
+     173,   174,    97,   175,   176,   177,   179,   180,   181,    53,
+      99,    59
+};
+
+static const unsigned char yycheck[] =
+{
+      48,    49,     3,    32,     4,     5,    30,     7,    71,    72,
+      10,    28,    16,    17,    33,    14,    40,    28,    29,    38,
+      39,    69,    70,    34,    87,    28,    74,    75,    32,    77,
+      41,    79,   130,    81,   132,   133,    32,   135,    39,    38,
+      39,   139,    38,    39,    32,   143,   144,    32,     0,     1,
+      32,     3,     4,     5,     6,     7,     8,     9,    10,    11,
+      28,    29,    14,    15,    28,    32,    34,    35,   166,    31,
+      32,    38,    39,    41,    28,    29,    76,   140,   126,    31,
+      32,     0,     1,    32,     3,     4,     5,     6,     7,     8,
+       9,    10,    11,    93,    32,    14,    15,    32,    28,    29,
+     101,   102,   103,    38,    39,    32,   154,    80,    13,    82,
+      83,    84,    31,    32,     4,     5,     6,     7,     8,     9,
+      10,    11,    95,    96,    14,    15,     5,     6,    32,     8,
+       9,    10,    11,    32,    28,    14,    15,    38,    39,    38,
+      39,    31,    32,    54,    32,    14,    57,    56,    59,    58,
+      12,    32,    31,    32,    16,    17,    39,    19,    20,    21,
+      22,    23,    24,    25,    26,    27,    55,    56,    32,    58,
+      32,    56,    12,    58,    36,    37,    16,    17,    18,    19,
+      20,    21,    56,    23,    58,    32,    32,    32,    32,    32,
+      32,    32,    32,    32,    32,    32,    32,    32,    32,    21,
+      54,    33
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,    43,     0,     1,     3,     4,     5,     6,     7,     8,
+       9,    10,    11,    14,    15,    31,    32,    44,    45,    46,
+      47,    48,    49,    52,    53,    55,    59,    61,    63,    64,
+      66,    68,    69,    70,    71,    79,    79,    28,    29,    77,
+      77,    77,    32,    77,    28,    28,    28,    29,    34,    41,
+      81,    82,    50,    50,    56,    58,    62,    74,    67,    74,
+      79,    32,    32,    32,    32,    32,    81,    81,    32,    38,
+      39,    30,    40,    12,    16,    17,    19,    20,    21,    22,
+      23,    24,    25,    26,    27,    32,    36,    37,    51,    72,
+      73,    75,    18,    19,    20,    21,    23,    32,    57,    73,
+      75,     5,     8,    15,    45,    54,    78,    45,    55,    60,
+      66,    78,    32,    75,     1,    45,    55,    65,    66,    78,
+      33,    81,    81,    82,    82,    32,    35,    81,    81,    77,
+      81,    76,    77,    81,    76,    81,    76,    76,    76,    28,
+      82,    13,    32,    77,    28,    76,    76,    79,    79,    79,
+      32,    81,    32,    32,    14,    80,    80,    32,    80,    80,
+      32,    80,    32,    32,    32,    80,    82,    80,    80,    32,
+      32,    32,    81,    32,    32,    32,    32,    32,    80,    32,
+      32,    32
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrlab1
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL         goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      yytoken = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK;                                              \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;                                                 \
+    }                                                          \
+while (0)
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)         \
+  Current.first_line   = Rhs[1].first_line;      \
+  Current.first_column = Rhs[1].first_column;    \
+  Current.last_line    = Rhs[N].last_line;       \
+  Current.last_column  = Rhs[N].last_column;
+#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 YYDSYMPRINT(Args)                     \
+do {                                           \
+  if (yydebug)                                 \
+    yysymprint Args;                           \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location)           \
+do {                                                           \
+  if (yydebug)                                                 \
+    {                                                          \
+      YYFPRINTF (stderr, "%s ", Title);                                \
+      yysymprint (stderr,                                      \
+                  Token, Value);       \
+      YYFPRINTF (stderr, "\n");                                        \
+    }                                                          \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (cinluded).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short *bottom;
+    short *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylineno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylineno);
+  /* 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 YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#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
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+\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]);
+# ifdef YYPRINT
+      YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+    }
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  switch (yytype)
+    {
+
+      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 lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead 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        yyssa[YYINITDEPTH];
+  short *yyss = yyssa;
+  register short *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  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 *yyss1 = yyss;
+
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow ("parser stack overflow",
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       short *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyoverflowlab;
+       YYSTACK_RELOCATE (yyss);
+       YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long 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 lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead 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);
+      YYDSYMPRINTF ("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 lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+  /* 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:
+
+    { zconfprint("unexpected 'endmenu' statement"); ;}
+    break;
+
+  case 9:
+
+    { zconfprint("unexpected 'endif' statement"); ;}
+    break;
+
+  case 10:
+
+    { zconfprint("unexpected 'endchoice' statement"); ;}
+    break;
+
+  case 11:
+
+    { zconfprint("syntax error"); yyerrok; ;}
+    break;
+
+  case 18:
+
+    {
+       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 19:
+
+    {
+       menu_end_entry();
+       printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 20:
+
+    {
+       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 21:
+
+    {
+       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 27:
+
+    {
+       menu_set_type(S_TRISTATE);
+       printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 28:
+
+    {
+       menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr);
+       menu_set_type(S_TRISTATE);
+       printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 29:
+
+    {
+       menu_set_type(S_BOOLEAN);
+       printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 30:
+
+    {
+       menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr);
+       menu_set_type(S_BOOLEAN);
+       printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 31:
+
+    {
+       menu_set_type(S_INT);
+       printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 32:
+
+    {
+       menu_set_type(S_HEX);
+       printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 33:
+
+    {
+       menu_set_type(S_STRING);
+       printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 34:
+
+    {
+       menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr);
+       printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 35:
+
+    {
+       menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr);
+       printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 36:
+
+    {
+       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 37:
+
+    {
+       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 38:
+
+    {
+       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 39:
+
+    {
+       menu_end_entry();
+       menu_add_menu();
+;}
+    break;
+
+  case 40:
+
+    {
+       if (zconf_endtoken(yyvsp[0].token, T_CHOICE, T_ENDCHOICE)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+       }
+;}
+    break;
+
+  case 42:
+
+    {
+       printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno);
+       zconfnerrs++;
+;}
+    break;
+
+  case 48:
+
+    {
+       menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr);
+       printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 49:
+
+    {
+       menu_set_type(S_TRISTATE);
+       printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 50:
+
+    {
+       menu_set_type(S_BOOLEAN);
+       printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 51:
+
+    {
+       current_entry->sym->flags |= SYMBOL_OPTIONAL;
+       printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 52:
+
+    {
+       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());
+;}
+    break;
+
+  case 55:
+
+    {
+       printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+       menu_add_entry(NULL);
+       menu_add_dep(yyvsp[-1].expr);
+       menu_end_entry();
+       menu_add_menu();
+;}
+    break;
+
+  case 56:
+
+    {
+       if (zconf_endtoken(yyvsp[0].token, T_IF, T_ENDIF)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+       }
+;}
+    break;
+
+  case 58:
+
+    {
+       printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno);
+       zconfnerrs++;
+;}
+    break;
+
+  case 63:
+
+    {
+       menu_add_entry(NULL);
+       menu_add_prop(P_MENU, yyvsp[-1].string, NULL, NULL);
+       printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 64:
+
+    {
+       menu_end_entry();
+       menu_add_menu();
+;}
+    break;
+
+  case 65:
+
+    {
+       if (zconf_endtoken(yyvsp[0].token, T_MENU, T_ENDMENU)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+       }
+;}
+    break;
+
+  case 67:
+
+    {
+       printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno);
+       zconfnerrs++;
+;}
+    break;
+
+  case 72:
+
+    { zconfprint("invalid menu option"); yyerrok; ;}
+    break;
+
+  case 73:
+
+    {
+       yyval.string = yyvsp[-1].string;
+       printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string);
+;}
+    break;
+
+  case 74:
+
+    {
+       zconf_nextfile(yyvsp[0].string);
+;}
+    break;
+
+  case 75:
+
+    {
+       menu_add_entry(NULL);
+       menu_add_prop(P_COMMENT, yyvsp[-1].string, NULL, NULL);
+       printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 76:
+
+    {
+       menu_end_entry();
+;}
+    break;
+
+  case 77:
+
+    {
+       printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+       zconf_starthelp();
+;}
+    break;
+
+  case 78:
+
+    {
+       current_entry->sym->help = yyvsp[0].string;
+;}
+    break;
+
+  case 82:
+
+    {
+       menu_add_dep(yyvsp[-1].expr);
+       printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 83:
+
+    {
+       menu_add_dep(yyvsp[-1].expr);
+       printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 84:
+
+    {
+       menu_add_dep(yyvsp[-1].expr);
+       printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 86:
+
+    {
+       menu_add_prop(P_PROMPT, yyvsp[-1].string, NULL, yyvsp[0].expr);
+;}
+    break;
+
+  case 89:
+
+    { yyval.token = T_ENDMENU; ;}
+    break;
+
+  case 90:
+
+    { yyval.token = T_ENDCHOICE; ;}
+    break;
+
+  case 91:
+
+    { yyval.token = T_ENDIF; ;}
+    break;
+
+  case 94:
+
+    { yyval.expr = NULL; ;}
+    break;
+
+  case 95:
+
+    { yyval.expr = yyvsp[0].expr; ;}
+    break;
+
+  case 96:
+
+    { yyval.expr = expr_alloc_symbol(yyvsp[0].symbol); ;}
+    break;
+
+  case 97:
+
+    { yyval.expr = expr_alloc_comp(E_EQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;}
+    break;
+
+  case 98:
+
+    { yyval.expr = expr_alloc_comp(E_UNEQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;}
+    break;
+
+  case 99:
+
+    { yyval.expr = yyvsp[-1].expr; ;}
+    break;
+
+  case 100:
+
+    { yyval.expr = expr_alloc_one(E_NOT, yyvsp[0].expr); ;}
+    break;
+
+  case 101:
+
+    { yyval.expr = expr_alloc_two(E_OR, yyvsp[-2].expr, yyvsp[0].expr); ;}
+    break;
+
+  case 102:
+
+    { yyval.expr = expr_alloc_two(E_AND, yyvsp[-2].expr, yyvsp[0].expr); ;}
+    break;
+
+  case 103:
+
+    { yyval.symbol = sym_lookup(yyvsp[0].string, 0); free(yyvsp[0].string); ;}
+    break;
+
+  case 104:
+
+    { yyval.symbol = sym_lookup(yyvsp[0].string, 1); free(yyvsp[0].string); ;}
+    break;
+
+
+    }
+
+/* Line 999 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)
+       {
+         YYSIZE_T yysize = 0;
+         int yytype = YYTRANSLATE (yychar);
+         char *yymsg;
+         int yyx, yycount;
+
+         yycount = 0;
+         /* Start YYX at -YYN if negative to avoid negative indexes in
+            YYCHECK.  */
+         for (yyx = yyn < 0 ? -yyn : 0;
+              yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+           if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+             yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+         yysize += yystrlen ("syntax error, unexpected ") + 1;
+         yysize += yystrlen (yytname[yytype]);
+         yymsg = (char *) YYSTACK_ALLOC (yysize);
+         if (yymsg != 0)
+           {
+             char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+             yyp = yystpcpy (yyp, yytname[yytype]);
+
+             if (yycount < 5)
+               {
+                 yycount = 0;
+                 for (yyx = yyn < 0 ? -yyn : 0;
+                      yyx < (int) (sizeof (yytname) / sizeof (char *));
+                      yyx++)
+                   if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+                     {
+                       const char *yyq = ! yycount ? ", expecting " : " or ";
+                       yyp = yystpcpy (yyp, yyq);
+                       yyp = yystpcpy (yyp, yytname[yyx]);
+                       yycount++;
+                     }
+               }
+             yyerror (yymsg);
+             YYSTACK_FREE (yymsg);
+           }
+         else
+           yyerror ("syntax error; also virtual memory exhausted");
+       }
+      else
+#endif /* YYERROR_VERBOSE */
+       yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+        error, discard it.  */
+
+      /* Return failure if at end of input.  */
+      if (yychar == YYEOF)
+        {
+         /* Pop the error token.  */
+          YYPOPSTACK;
+         /* Pop the rest of the stack.  */
+         while (yyss < yyssp)
+           {
+             YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+             yydestruct (yystos[*yyssp], yyvsp);
+             YYPOPSTACK;
+           }
+         YYABORT;
+        }
+
+      YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+      yydestruct (yytoken, &yylval);
+      yychar = YYEMPTY;
+
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action.  |
+`----------------------------------------------------*/
+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;
+
+      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+      yydestruct (yystos[yystate], yyvsp);
+      yyvsp--;
+      yystate = *--yyssp;
+
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+
+
+
+void conf_parse(const char *name)
+{
+       struct symbol *sym;
+       int i;
+
+       zconf_initscan(name);
+
+       sym_init();
+       menu_init();
+       modules_sym = sym_lookup("MODULES", 0);
+       rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL);
+
+       //zconfdebug = 1;
+       zconfparse();
+       if (zconfnerrs)
+               exit(1);
+       menu_finalize(&rootmenu);
+       for_all_symbols(i, sym) {
+                if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym))
+                        printf("\n");
+               else
+                       sym->flags |= SYMBOL_CHECK_DONE;
+        }
+
+       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";
+       }
+       return "<token>";
+}
+
+static bool zconf_endtoken(int token, int starttoken, int endtoken)
+{
+       if (token != endtoken) {
+               zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken));
+               zconfnerrs++;
+               return false;
+       }
+       if (current_menu->file != current_file) {
+               zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken));
+               zconfprint("location of the '%s'", 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() + 1);
+       va_start(ap, err);
+       vfprintf(stderr, err, ap);
+       va_end(ap);
+       fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+       fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+}
+
+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 "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
+
+
diff --git a/busybox/scripts/config/zconf.tab.h_shipped b/busybox/scripts/config/zconf.tab.h_shipped
new file mode 100644 (file)
index 0000000..3b191ef
--- /dev/null
@@ -0,0 +1,125 @@
+/* A Bison parser, made from zconf.y, by GNU bison 1.75.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+#ifndef BISON_ZCONF_TAB_H
+# define BISON_ZCONF_TAB_H
+
+/* 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_HELP = 266,
+     T_HELPTEXT = 267,
+     T_IF = 268,
+     T_ENDIF = 269,
+     T_DEPENDS = 270,
+     T_REQUIRES = 271,
+     T_OPTIONAL = 272,
+     T_PROMPT = 273,
+     T_DEFAULT = 274,
+     T_TRISTATE = 275,
+     T_BOOLEAN = 276,
+     T_INT = 277,
+     T_HEX = 278,
+     T_WORD = 279,
+     T_STRING = 280,
+     T_UNEQUAL = 281,
+     T_EOF = 282,
+     T_EOL = 283,
+     T_CLOSE_PAREN = 284,
+     T_OPEN_PAREN = 285,
+     T_ON = 286,
+     T_OR = 287,
+     T_AND = 288,
+     T_EQUAL = 289,
+     T_NOT = 290
+   };
+#endif
+#define T_MAINMENU 258
+#define T_MENU 259
+#define T_ENDMENU 260
+#define T_SOURCE 261
+#define T_CHOICE 262
+#define T_ENDCHOICE 263
+#define T_COMMENT 264
+#define T_CONFIG 265
+#define T_HELP 266
+#define T_HELPTEXT 267
+#define T_IF 268
+#define T_ENDIF 269
+#define T_DEPENDS 270
+#define T_REQUIRES 271
+#define T_OPTIONAL 272
+#define T_PROMPT 273
+#define T_DEFAULT 274
+#define T_TRISTATE 275
+#define T_BOOLEAN 276
+#define T_INT 277
+#define T_HEX 278
+#define T_WORD 279
+#define T_STRING 280
+#define T_UNEQUAL 281
+#define T_EOF 282
+#define T_EOL 283
+#define T_CLOSE_PAREN 284
+#define T_OPEN_PAREN 285
+#define T_ON 286
+#define T_OR 287
+#define T_AND 288
+#define T_EQUAL 289
+#define T_NOT 290
+
+
+
+
+#ifndef YYSTYPE
+#line 33 "zconf.y"
+typedef union {
+       int token;
+       char *string;
+       struct symbol *symbol;
+       struct expr *expr;
+       struct menu *menu;
+} yystype;
+/* Line 1281 of /usr/share/bison/yacc.c.  */
+#line 118 "zconf.tab.h"
+# define YYSTYPE yystype
+#endif
+
+extern YYSTYPE zconflval;
+
+
+#endif /* not BISON_ZCONF_TAB_H */
+
diff --git a/busybox/scripts/config/zconf.y b/busybox/scripts/config/zconf.y
new file mode 100644 (file)
index 0000000..658495c
--- /dev/null
@@ -0,0 +1,687 @@
+%{
+/*
+ * 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 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 zconferror(const char *err);
+static bool zconf_endtoken(int token, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+#define YYERROR_VERBOSE
+%}
+%expect 40
+
+%union
+{
+       int token;
+       char *string;
+       struct symbol *symbol;
+       struct expr *expr;
+       struct menu *menu;
+}
+
+%token T_MAINMENU
+%token T_MENU
+%token T_ENDMENU
+%token T_SOURCE
+%token T_CHOICE
+%token T_ENDCHOICE
+%token T_COMMENT
+%token T_CONFIG
+%token T_MENUCONFIG
+%token T_HELP
+%token <string> T_HELPTEXT
+%token T_IF
+%token T_ENDIF
+%token T_DEPENDS
+%token T_REQUIRES
+%token T_OPTIONAL
+%token T_PROMPT
+%token T_DEFAULT
+%token T_TRISTATE
+%token T_DEF_TRISTATE
+%token T_BOOLEAN
+%token T_DEF_BOOLEAN
+%token T_STRING
+%token T_INT
+%token T_HEX
+%token <string> T_WORD
+%token <string> T_WORD_QUOTE
+%token T_UNEQUAL
+%token T_EOF
+%token T_EOL
+%token T_CLOSE_PAREN
+%token T_OPEN_PAREN
+%token T_ON
+%token T_SELECT
+%token T_RANGE
+
+%left T_OR
+%left T_AND
+%left T_EQUAL T_UNEQUAL
+%nonassoc T_NOT
+
+%type <string> prompt
+%type <string> source
+%type <symbol> symbol
+%type <expr> expr
+%type <expr> if_expr
+%type <token> end
+
+%{
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+%}
+%%
+input:   /* empty */
+       | input block
+;
+
+block:   common_block
+       | choice_stmt
+       | menu_stmt
+       | T_MAINMENU prompt nl_or_eof
+       | T_ENDMENU             { zconfprint("unexpected 'endmenu' statement"); }
+       | T_ENDIF               { zconfprint("unexpected 'endif' statement"); }
+       | T_ENDCHOICE           { zconfprint("unexpected 'endchoice' statement"); }
+       | error nl_or_eof       { zconfprint("syntax error"); yyerrok; }
+;
+
+common_block:
+         if_stmt
+       | comment_stmt
+       | config_stmt
+       | menuconfig_stmt
+       | source_stmt
+       | nl_or_eof
+;
+
+
+/* config/menuconfig entry */
+
+config_entry_start: T_CONFIG T_WORD T_EOL
+{
+       struct symbol *sym = sym_lookup($2, 0);
+       sym->flags |= SYMBOL_OPTIONAL;
+       menu_add_entry(sym);
+       printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+config_stmt: config_entry_start config_option_list
+{
+       menu_end_entry();
+       printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
+{
+       struct symbol *sym = sym_lookup($2, 0);
+       sym->flags |= SYMBOL_OPTIONAL;
+       menu_add_entry(sym);
+       printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+menuconfig_stmt: menuconfig_entry_start config_option_list
+{
+       if (current_entry->prompt)
+               current_entry->prompt->type = P_MENU;
+       else
+               zconfprint("warning: menuconfig statement without prompt");
+       menu_end_entry();
+       printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+config_option_list:
+         /* empty */
+       | config_option_list config_option
+       | config_option_list depends
+       | config_option_list help
+       | config_option_list T_EOL
+;
+
+config_option: T_TRISTATE prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_TRISTATE);
+       printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_DEF_TRISTATE expr if_expr T_EOL
+{
+       menu_add_expr(P_DEFAULT, $2, $3);
+       menu_set_type(S_TRISTATE);
+       printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_BOOLEAN prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_BOOLEAN);
+       printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_DEF_BOOLEAN expr if_expr T_EOL
+{
+       menu_add_expr(P_DEFAULT, $2, $3);
+       menu_set_type(S_BOOLEAN);
+       printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_INT prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_INT);
+       printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_HEX prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_HEX);
+       printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_STRING prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_STRING);
+       printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_PROMPT prompt if_expr T_EOL
+{
+       menu_add_prompt(P_PROMPT, $2, $3);
+       printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_DEFAULT expr if_expr T_EOL
+{
+       menu_add_expr(P_DEFAULT, $2, $3);
+       printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_SELECT T_WORD if_expr T_EOL
+{
+       menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
+       printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_RANGE symbol symbol if_expr T_EOL
+{
+       menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
+       printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+};
+
+/* choice entry */
+
+choice: T_CHOICE T_EOL
+{
+       struct symbol *sym = sym_lookup(NULL, 0);
+       sym->flags |= SYMBOL_CHOICE;
+       menu_add_entry(sym);
+       menu_add_expr(P_CHOICE, NULL, NULL);
+       printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+};
+
+choice_entry: choice choice_option_list
+{
+       menu_end_entry();
+       menu_add_menu();
+};
+
+choice_end: end
+{
+       if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+       }
+};
+
+choice_stmt:
+         choice_entry choice_block choice_end
+       | choice_entry choice_block
+{
+       printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno);
+       zconfnerrs++;
+};
+
+choice_option_list:
+         /* empty */
+       | choice_option_list choice_option
+       | choice_option_list depends
+       | choice_option_list help
+       | choice_option_list T_EOL
+;
+
+choice_option: T_PROMPT prompt if_expr T_EOL
+{
+       menu_add_prompt(P_PROMPT, $2, $3);
+       printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_TRISTATE prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_TRISTATE);
+       printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_BOOLEAN prompt_stmt_opt T_EOL
+{
+       menu_set_type(S_BOOLEAN);
+       printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_OPTIONAL T_EOL
+{
+       current_entry->sym->flags |= SYMBOL_OPTIONAL;
+       printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_DEFAULT T_WORD if_expr T_EOL
+{
+       menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
+       printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
+};
+
+choice_block:
+         /* empty */
+       | choice_block common_block
+;
+
+/* if entry */
+
+if: T_IF expr T_EOL
+{
+       printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+       menu_add_entry(NULL);
+       menu_add_dep($2);
+       menu_end_entry();
+       menu_add_menu();
+};
+
+if_end: end
+{
+       if (zconf_endtoken($1, T_IF, T_ENDIF)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+       }
+};
+
+if_stmt:
+         if if_block if_end
+       | if if_block
+{
+       printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno);
+       zconfnerrs++;
+};
+
+if_block:
+         /* empty */
+       | if_block common_block
+       | if_block menu_stmt
+       | if_block choice_stmt
+;
+
+/* menu entry */
+
+menu: T_MENU prompt T_EOL
+{
+       menu_add_entry(NULL);
+       menu_add_prop(P_MENU, $2, NULL, NULL);
+       printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+};
+
+menu_entry: menu depends_list
+{
+       menu_end_entry();
+       menu_add_menu();
+};
+
+menu_end: end
+{
+       if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
+               menu_end_menu();
+               printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+       }
+};
+
+menu_stmt:
+         menu_entry menu_block menu_end
+       | menu_entry menu_block
+{
+       printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno);
+       zconfnerrs++;
+};
+
+menu_block:
+         /* empty */
+       | menu_block common_block
+       | menu_block menu_stmt
+       | menu_block choice_stmt
+       | menu_block error T_EOL                { zconfprint("invalid menu option"); yyerrok; }
+;
+
+source: T_SOURCE prompt T_EOL
+{
+       $$ = $2;
+       printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+source_stmt: source
+{
+       zconf_nextfile($1);
+};
+
+/* comment entry */
+
+comment: T_COMMENT prompt T_EOL
+{
+       menu_add_entry(NULL);
+       menu_add_prop(P_COMMENT, $2, NULL, NULL);
+       printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+};
+
+comment_stmt: comment depends_list
+{
+       menu_end_entry();
+};
+
+/* help option */
+
+help_start: T_HELP T_EOL
+{
+       printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+       zconf_starthelp();
+};
+
+help: help_start T_HELPTEXT
+{
+       current_entry->sym->help = $2;
+};
+
+/* depends option */
+
+depends_list:    /* empty */
+               | depends_list depends
+               | depends_list T_EOL
+;
+
+depends: T_DEPENDS T_ON expr T_EOL
+{
+       menu_add_dep($3);
+       printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+}
+       | T_DEPENDS expr T_EOL
+{
+       menu_add_dep($2);
+       printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+}
+       | T_REQUIRES expr T_EOL
+{
+       menu_add_dep($2);
+       printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+};
+
+/* prompt statement */
+
+prompt_stmt_opt:
+         /* empty */
+       | prompt if_expr
+{
+       menu_add_prop(P_PROMPT, $1, NULL, $2);
+};
+
+prompt:          T_WORD
+       | T_WORD_QUOTE
+;
+
+end:     T_ENDMENU nl_or_eof   { $$ = T_ENDMENU; }
+       | T_ENDCHOICE nl_or_eof { $$ = T_ENDCHOICE; }
+       | T_ENDIF nl_or_eof     { $$ = T_ENDIF; }
+;
+
+nl_or_eof:
+       T_EOL | T_EOF;
+
+if_expr:  /* empty */                  { $$ = NULL; }
+       | T_IF expr                     { $$ = $2; }
+;
+
+expr:    symbol                                { $$ = expr_alloc_symbol($1); }
+       | symbol T_EQUAL symbol                 { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
+       | symbol T_UNEQUAL symbol               { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
+       | T_OPEN_PAREN expr T_CLOSE_PAREN       { $$ = $2; }
+       | T_NOT expr                            { $$ = expr_alloc_one(E_NOT, $2); }
+       | expr T_OR expr                        { $$ = expr_alloc_two(E_OR, $1, $3); }
+       | expr T_AND expr                       { $$ = expr_alloc_two(E_AND, $1, $3); }
+;
+
+symbol:          T_WORD        { $$ = sym_lookup($1, 0); free($1); }
+       | T_WORD_QUOTE  { $$ = sym_lookup($1, 1); free($1); }
+;
+
+%%
+
+void conf_parse(const char *name)
+{
+       struct symbol *sym;
+       int i;
+
+       zconf_initscan(name);
+
+       sym_init();
+       menu_init();
+       modules_sym = sym_lookup("MODULES", 0);
+       rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL);
+
+       //zconfdebug = 1;
+       zconfparse();
+       if (zconfnerrs)
+               exit(1);
+       menu_finalize(&rootmenu);
+       for_all_symbols(i, sym) {
+                if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym))
+                        printf("\n");
+               else
+                       sym->flags |= SYMBOL_CHECK_DONE;
+        }
+
+       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";
+       }
+       return "<token>";
+}
+
+static bool zconf_endtoken(int token, int starttoken, int endtoken)
+{
+       if (token != endtoken) {
+               zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken));
+               zconfnerrs++;
+               return false;
+       }
+       if (current_menu->file != current_file) {
+               zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken));
+               zconfprint("location of the '%s'", 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() + 1);
+       va_start(ap, err);
+       vfprintf(stderr, err, ap);
+       va_end(ap);
+       fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+       fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+}
+
+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 "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
diff --git a/busybox/scripts/mkdep.c b/busybox/scripts/mkdep.c
new file mode 100644 (file)
index 0000000..ae3cc74
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * Originally by Linus Torvalds.
+ * Smart CONFIG_* processing by Werner Almesberger, Michael Chastain.
+ *
+ * Usage: mkdep cflags -- file ...
+ *
+ * Read source files and output makefile dependency lines for them.
+ * I make simple dependency lines for #include <*.h> and #include "*.h".
+ * I also find instances of CONFIG_FOO and generate dependencies
+ *    like include/config/foo.h.
+ *
+ * 1 August 1999, Michael Elizabeth Chastain, <mec@shout.net>
+ * - Keith Owens reported a bug in smart config processing.  There used
+ *   to be an optimization for "#define CONFIG_FOO ... #ifdef CONFIG_FOO",
+ *   so that the file would not depend on CONFIG_FOO because the file defines
+ *   this symbol itself.  But this optimization is bogus!  Consider this code:
+ *   "#if 0 \n #define CONFIG_FOO \n #endif ... #ifdef CONFIG_FOO".  Here
+ *   the definition is inactivated, but I still used it.  It turns out this
+ *   actually happens a few times in the kernel source.  The simple way to
+ *   fix this problem is to remove this particular optimization.
+ *
+ * 2.3.99-pre1, Andrew Morton <andrewm@uow.edu.au>
+ * - Changed so that 'filename.o' depends upon 'filename.[cS]'.  This is so that
+ *   missing source files are noticed, rather than silently ignored.
+ *
+ * 2.4.2-pre3, Keith Owens <kaos@ocs.com.au>
+ * - Accept cflags followed by '--' followed by filenames.  mkdep extracts -I
+ *   options from cflags and looks in the specified directories as well as the
+ *   defaults.   Only -I is supported, no attempt is made to handle -idirafter,
+ *   -isystem, -I- etc.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+
+char depname[512];
+int hasdep;
+
+struct path_struct {
+       int len;
+       char *buffer;
+};
+struct path_struct *path_array;
+int paths;
+
+
+/* Current input file */
+static const char *g_filename;
+
+/*
+ * This records all the configuration options seen.
+ * In perl this would be a hash, but here it's a long string
+ * of values separated by newlines.  This is simple and
+ * extremely fast.
+ */
+char * str_config  = NULL;
+int    size_config = 0;
+int    len_config  = 0;
+
+static void
+do_depname(void)
+{
+       if (!hasdep) {
+               hasdep = 1;
+               if (g_filename) {
+                       /* Source file (*.[cS]) */
+                       printf("%s:", depname);
+                       printf(" %s", g_filename);
+               } else {
+                       /* header file (*.h) */
+                       printf("dep_%s +=", depname);
+               }
+       }
+}
+
+/*
+ * Grow the configuration string to a desired length.
+ * Usually the first growth is plenty.
+ */
+void grow_config(int len)
+{
+       while (len_config + len > size_config) {
+               if (size_config == 0)
+                       size_config = 2048;
+               str_config = realloc(str_config, size_config *= 2);
+               if (str_config == NULL)
+                       { perror("malloc config"); exit(1); }
+       }
+}
+
+
+
+/*
+ * Lookup a value in the configuration string.
+ */
+int is_defined_config(const char * name, int len)
+{
+       const char * pconfig;
+       const char * plast = str_config + len_config - len;
+       for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
+               if (pconfig[ -1] == '\n'
+               &&  pconfig[len] == '\n'
+               &&  !memcmp(pconfig, name, len))
+                       return 1;
+       }
+       return 0;
+}
+
+
+
+/*
+ * Add a new value to the configuration string.
+ */
+void define_config(const char * name, int len)
+{
+       grow_config(len + 1);
+
+       memcpy(str_config+len_config, name, len);
+       len_config += len;
+       str_config[len_config++] = '\n';
+}
+
+
+
+/*
+ * Clear the set of configuration strings.
+ */
+void clear_config(void)
+{
+       len_config = 0;
+       define_config("", 0);
+}
+
+
+
+/*
+ * This records all the precious .h filenames.  No need for a hash,
+ * it's a long string of values enclosed in tab and newline.
+ */
+char * str_precious  = NULL;
+int    size_precious = 0;
+int    len_precious  = 0;
+
+
+
+/*
+ * Grow the precious string to a desired length.
+ * Usually the first growth is plenty.
+ */
+void grow_precious(int len)
+{
+       while (len_precious + len > size_precious) {
+               if (size_precious == 0)
+                       size_precious = 2048;
+               str_precious = realloc(str_precious, size_precious *= 2);
+               if (str_precious == NULL)
+                       { perror("malloc"); exit(1); }
+       }
+}
+
+
+
+/*
+ * Add a new value to the precious string.
+ */
+void define_precious(const char * filename)
+{
+       int len = strlen(filename);
+       grow_precious(len + 4);
+       *(str_precious+len_precious++) = '\t';
+       memcpy(str_precious+len_precious, filename, len);
+       len_precious += len;
+       memcpy(str_precious+len_precious, " \\\n", 3);
+       len_precious += 3;
+}
+
+
+
+/*
+ * Handle an #include line.
+ */
+void handle_include(int start, const char * name, int len)
+{
+       struct path_struct *path;
+       int i;
+
+       if (len == 14 && !memcmp(name, "include/config.h", len))
+               return;
+
+       if (len >= 7 && !memcmp(name, "config/", 7))
+               define_config(name+7, len-7-2);
+
+       for (i = start, path = path_array+start; i < paths; ++i, ++path) {
+               memcpy(path->buffer+path->len, name, len);
+               path->buffer[path->len+len] = '\0';
+               if (access(path->buffer, F_OK) == 0) {
+                       do_depname();
+                       printf(" \\\n %s $(dep_%s)", path->buffer, path->buffer);
+                       return;
+               }
+       }
+
+}
+
+
+
+/*
+ * Add a path to the list of include paths.
+ */
+void add_path(const char * name)
+{
+       struct path_struct *path;
+       char resolved_path[PATH_MAX+1];
+       const char *name2;
+
+       if (strcmp(name, ".")) {
+               name2 = realpath(name, resolved_path);
+               if (!name2) {
+                       fprintf(stderr, "realpath(%s) failed, %m\n", name);
+                       exit(1);
+               }
+       }
+       else {
+               name2 = "";
+       }
+
+       path_array = realloc(path_array, (++paths)*sizeof(*path_array));
+       if (!path_array) {
+               fprintf(stderr, "cannot expand path_arry\n");
+               exit(1);
+       }
+
+       path = path_array+paths-1;
+       path->len = strlen(name2);
+       path->buffer = malloc(path->len+1+256+1);
+       if (!path->buffer) {
+               fprintf(stderr, "cannot allocate path buffer\n");
+               exit(1);
+       }
+       strcpy(path->buffer, name2);
+       if (path->len && *(path->buffer+path->len-1) != '/') {
+               *(path->buffer+path->len) = '/';
+               *(path->buffer+(++(path->len))) = '\0';
+       }
+}
+
+
+
+/*
+ * Record the use of a CONFIG_* word.
+ */
+void use_config(const char * name, int len)
+{
+       char *pc;
+       int i;
+
+       pc = path_array[paths-1].buffer + path_array[paths-1].len;
+       memcpy(pc, "config/", 7);
+       pc += 7;
+
+       for (i = 0; i < len; i++) {
+           char c = name[i];
+           if (isupper((int)c)) c = tolower((int)c);
+           if (c == '_')   c = '/';
+           pc[i] = c;
+       }
+       pc[len] = '\0';
+
+       if (is_defined_config(pc, len))
+           return;
+
+       define_config(pc, len);
+
+       do_depname();
+       printf(" \\\n   $(wildcard %s.h)", path_array[paths-1].buffer);
+}
+
+
+
+/*
+ * Macros for stunningly fast map-based character access.
+ * __buf is a register which holds the current word of the input.
+ * Thus, there is one memory access per sizeof(unsigned long) characters.
+ */
+
+#if defined(__alpha__) || defined(__i386__) || defined(__ia64__)  || defined(__x86_64__) || defined(__MIPSEL__)        \
+    || defined(__arm__)
+#define LE_MACHINE
+#endif
+
+#ifdef LE_MACHINE
+#define next_byte(x) (x >>= 8)
+#define current ((unsigned char) __buf)
+#else
+#define next_byte(x) (x <<= 8)
+#define current (__buf >> 8*(sizeof(unsigned long)-1))
+#endif
+
+#define GETNEXT { \
+       next_byte(__buf); \
+       if ((unsigned long) next % sizeof(unsigned long) == 0) { \
+               if (next >= end) \
+                       break; \
+               __buf = * (unsigned long *) next; \
+       } \
+       next++; \
+}
+
+/*
+ * State machine macros.
+ */
+#define CASE(c,label) if (current == c) goto label
+#define NOTCASE(c,label) if (current != c) goto label
+
+/*
+ * Yet another state machine speedup.
+ */
+#define MAX2(a,b) ((a)>(b)?(a):(b))
+#define MIN2(a,b) ((a)<(b)?(a):(b))
+#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e)))))
+#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e)))))
+
+
+
+/*
+ * The state machine looks for (approximately) these Perl regular expressions:
+ *
+ *    m|\/\*.*?\*\/|
+ *    m|\/\/.*|
+ *    m|'.*?'|
+ *    m|".*?"|
+ *    m|#\s*include\s*"(.*?)"|
+ *    m|#\s*include\s*<(.*?>"|
+ *    m|#\s*(?define|undef)\s*CONFIG_(\w*)|
+ *    m|(?!\w)CONFIG_|
+ *
+ * About 98% of the CPU time is spent here, and most of that is in
+ * the 'start' paragraph.  Because the current characters are
+ * in a register, the start loop usually eats 4 or 8 characters
+ * per memory read.  The MAX5 and MIN5 tests dispose of most
+ * input characters with 1 or 2 comparisons.
+ */
+void state_machine(const char * map, const char * end)
+{
+       const char * next = map;
+       const char * map_dot;
+       unsigned long __buf = 0;
+
+       for (;;) {
+start:
+       GETNEXT
+__start:
+       if (current > MAX5('/','\'','"','#','C')) goto start;
+       if (current < MIN5('/','\'','"','#','C')) goto start;
+       CASE('/',  slash);
+       CASE('\'', squote);
+       CASE('"',  dquote);
+       CASE('#',  pound);
+       CASE('C',  cee);
+       goto start;
+
+/* // */
+slash_slash:
+       GETNEXT
+       CASE('\n', start);
+       NOTCASE('\\', slash_slash);
+       GETNEXT
+       goto slash_slash;
+
+/* / */
+slash:
+       GETNEXT
+       CASE('/',  slash_slash);
+       NOTCASE('*', __start);
+slash_star_dot_star:
+       GETNEXT
+__slash_star_dot_star:
+       NOTCASE('*', slash_star_dot_star);
+       GETNEXT
+       NOTCASE('/', __slash_star_dot_star);
+       goto start;
+
+/* '.*?' */
+squote:
+       GETNEXT
+       CASE('\'', start);
+       NOTCASE('\\', squote);
+       GETNEXT
+       goto squote;
+
+/* ".*?" */
+dquote:
+       GETNEXT
+       CASE('"', start);
+       NOTCASE('\\', dquote);
+       GETNEXT
+       goto dquote;
+
+/* #\s* */
+pound:
+       GETNEXT
+       CASE(' ',  pound);
+       CASE('\t', pound);
+       CASE('i',  pound_i);
+       CASE('d',  pound_d);
+       CASE('u',  pound_u);
+       goto __start;
+
+/* #\s*i */
+pound_i:
+       GETNEXT NOTCASE('n', __start);
+       GETNEXT NOTCASE('c', __start);
+       GETNEXT NOTCASE('l', __start);
+       GETNEXT NOTCASE('u', __start);
+       GETNEXT NOTCASE('d', __start);
+       GETNEXT NOTCASE('e', __start);
+       goto pound_include;
+
+/* #\s*include\s* */
+pound_include:
+       GETNEXT
+       CASE(' ',  pound_include);
+       CASE('\t', pound_include);
+       map_dot = next;
+       CASE('"',  pound_include_dquote);
+       CASE('<',  pound_include_langle);
+       goto __start;
+
+/* #\s*include\s*"(.*)" */
+pound_include_dquote:
+       GETNEXT
+       CASE('\n', start);
+       NOTCASE('"', pound_include_dquote);
+       handle_include(0, map_dot, next - map_dot - 1);
+       goto start;
+
+/* #\s*include\s*<(.*)> */
+pound_include_langle:
+       GETNEXT
+       CASE('\n', start);
+       NOTCASE('>', pound_include_langle);
+       handle_include(1, map_dot, next - map_dot - 1);
+       goto start;
+
+/* #\s*d */
+pound_d:
+       GETNEXT NOTCASE('e', __start);
+       GETNEXT NOTCASE('f', __start);
+       GETNEXT NOTCASE('i', __start);
+       GETNEXT NOTCASE('n', __start);
+       GETNEXT NOTCASE('e', __start);
+       goto pound_define_undef;
+
+/* #\s*u */
+pound_u:
+       GETNEXT NOTCASE('n', __start);
+       GETNEXT NOTCASE('d', __start);
+       GETNEXT NOTCASE('e', __start);
+       GETNEXT NOTCASE('f', __start);
+       goto pound_define_undef;
+
+/*
+ * #\s*(define|undef)\s*CONFIG_(\w*)
+ *
+ * this does not define the word, because it could be inside another
+ * conditional (#if 0).  But I do parse the word so that this instance
+ * does not count as a use.  -- mec
+ */
+pound_define_undef:
+       GETNEXT
+       CASE(' ',  pound_define_undef);
+       CASE('\t', pound_define_undef);
+
+               NOTCASE('C', __start);
+       GETNEXT NOTCASE('O', __start);
+       GETNEXT NOTCASE('N', __start);
+       GETNEXT NOTCASE('F', __start);
+       GETNEXT NOTCASE('I', __start);
+       GETNEXT NOTCASE('G', __start);
+       GETNEXT NOTCASE('_', __start);
+
+       map_dot = next;
+pound_define_undef_CONFIG_word:
+       GETNEXT
+       if (isalnum(current) || current == '_')
+               goto pound_define_undef_CONFIG_word;
+       goto __start;
+
+/* \<CONFIG_(\w*) */
+cee:
+       if (next >= map+2 && (isalnum((int)next[-2]) || next[-2] == '_'))
+               goto start;
+       GETNEXT NOTCASE('O', __start);
+       GETNEXT NOTCASE('N', __start);
+       GETNEXT NOTCASE('F', __start);
+       GETNEXT NOTCASE('I', __start);
+       GETNEXT NOTCASE('G', __start);
+       GETNEXT NOTCASE('_', __start);
+
+       map_dot = next;
+cee_CONFIG_word:
+       GETNEXT
+       if (isalnum(current) || current == '_')
+               goto cee_CONFIG_word;
+       use_config(map_dot, next - map_dot - 1);
+       goto __start;
+    }
+}
+
+
+
+/*
+ * Generate dependencies for one file.
+ */
+void do_depend(const char * filename)
+{
+       int mapsize;
+       int pagesizem1 = getpagesize()-1;
+       int fd;
+       struct stat st;
+       char * map;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               perror(filename);
+               return;
+       }
+
+       fstat(fd, &st);
+       if (st.st_size == 0) {
+               fprintf(stderr,"%s is empty\n",filename);
+               close(fd);
+               return;
+       }
+
+       mapsize = st.st_size;
+       mapsize = (mapsize+pagesizem1) & ~pagesizem1;
+       map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
+       if ((long) map == -1) {
+               perror("mkdep: mmap");
+               close(fd);
+               return;
+       }
+       if ((unsigned long) map % sizeof(unsigned long) != 0)
+       {
+               fprintf(stderr, "do_depend: map not aligned\n");
+               exit(1);
+       }
+
+       hasdep = 0;
+       clear_config();
+       state_machine(map, map+st.st_size);
+       if (hasdep) {
+               puts("");
+       }
+
+       munmap(map, mapsize);
+       close(fd);
+}
+
+
+
+/*
+ * Generate dependencies for all files.
+ */
+int main(int argc, char **argv)
+{
+       int len;
+       const char *hpath;
+
+       hpath = getenv("TOPDIR");
+       if (!hpath) {
+               fputs("mkdep: TOPDIR not set in environment.  "
+                     "Don't bypass the top level Makefile.\n", stderr);
+               return 1;
+       }
+
+       add_path(".");          /* for #include "..." */
+
+       while (++argv, --argc > 0) {
+               if (strncmp(*argv, "-I", 2) == 0) {
+                       if (*((*argv)+2)) {
+                               add_path((*argv)+2);
+                       }
+                       else {
+                               ++argv;
+                               --argc;
+                               add_path(*argv);
+                       }
+               }
+               else if (strcmp(*argv, "--") == 0) {
+                       break;
+               }
+       }
+
+       add_path(hpath);        /* must be last entry, for config files */
+
+       while (--argc > 0) {
+               const char * filename = *++argv;
+               g_filename = 0;
+               len = strlen(filename);
+               memcpy(depname, filename, len+1);
+               if (len > 2 && filename[len-2] == '.') {
+                       if (filename[len-1] == 'c' || filename[len-1] == 'S') {
+                           depname[len-1] = 'o';
+                           g_filename = filename;
+                       }
+               }
+               do_depend(filename);
+       }
+       if (len_precious) {
+               *(str_precious+len_precious) = '\0';
+               printf(".PRECIOUS:%s\n", str_precious);
+       }
+       return 0;
+}
diff --git a/busybox/scripts/split-include.c b/busybox/scripts/split-include.c
new file mode 100644 (file)
index 0000000..624a0d6
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * split-include.c
+ *
+ * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
+ * This is a C version of syncdep.pl by Werner Almesberger.
+ *
+ * This program takes autoconf.h as input and outputs a directory full
+ * of one-line include files, merging onto the old values.
+ *
+ * Think of the configuration options as key-value pairs.  Then there
+ * are five cases:
+ *
+ *    key      old value   new value   action
+ *
+ *    KEY-1    VALUE-1     VALUE-1     leave file alone
+ *    KEY-2    VALUE-2A    VALUE-2B    write VALUE-2B into file
+ *    KEY-3    -           VALUE-3     write VALUE-3  into file
+ *    KEY-4    VALUE-4     -           write an empty file
+ *    KEY-5    (empty)     -           leave old empty file alone
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ERROR_EXIT(strExit)                                            \
+    {                                                                  \
+       const int errnoSave = errno;                                    \
+       fprintf(stderr, "%s: ", str_my_name);                           \
+       errno = errnoSave;                                              \
+       perror((strExit));                                              \
+       exit(1);                                                        \
+    }
+
+
+
+int main(int argc, const char * argv [])
+{
+    const char * str_my_name;
+    const char * str_file_autoconf;
+    const char * str_dir_config;
+
+    FILE * fp_config;
+    FILE * fp_target;
+    FILE * fp_find;
+
+    int buffer_size;
+
+    char * line;
+    char * old_line;
+    char * list_target;
+    char * ptarget;
+
+    struct stat stat_buf;
+
+    /* Check arg count. */
+    if (argc != 3)
+    {
+       fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]);
+       exit(1);
+    }
+
+    str_my_name       = argv[0];
+    str_file_autoconf = argv[1];
+    str_dir_config    = argv[2];
+
+    /* Find a buffer size. */
+    if (stat(str_file_autoconf, &stat_buf) != 0)
+       ERROR_EXIT(str_file_autoconf);
+    buffer_size = 2 * stat_buf.st_size + 4096;
+
+    /* Allocate buffers. */
+    if ( (line        = malloc(buffer_size)) == NULL
+    ||   (old_line    = malloc(buffer_size)) == NULL
+    ||   (list_target = malloc(buffer_size)) == NULL )
+       ERROR_EXIT(str_file_autoconf);
+
+    /* Open autoconfig file. */
+    if ((fp_config = fopen(str_file_autoconf, "r")) == NULL)
+       ERROR_EXIT(str_file_autoconf);
+
+    /* Make output directory if needed. */
+    if (stat(str_dir_config, &stat_buf) != 0)
+    {
+       if (mkdir(str_dir_config, 0755) != 0)
+           ERROR_EXIT(str_dir_config);
+    }
+
+    /* Change to output directory. */
+    if (chdir(str_dir_config) != 0)
+       ERROR_EXIT(str_dir_config);
+
+    /* Put initial separator into target list. */
+    ptarget = list_target;
+    *ptarget++ = '\n';
+
+    /* Read config lines. */
+    while (fgets(line, buffer_size, fp_config))
+    {
+       const char * str_config;
+       int is_same;
+       int itarget;
+
+       if (line[0] != '#')
+           continue;
+       if ((str_config = strstr(line, "CONFIG_")) == NULL)
+           continue;
+
+       /* Make the output file name. */
+       str_config += sizeof("CONFIG_") - 1;
+       for (itarget = 0; !isspace(str_config[itarget]); itarget++)
+       {
+           char c = str_config[itarget];
+           if (isupper(c)) c = tolower(c);
+           if (c == '_')   c = '/';
+           ptarget[itarget] = c;
+       }
+       ptarget[itarget++] = '.';
+       ptarget[itarget++] = 'h';
+       ptarget[itarget++] = '\0';
+
+       /* Check for existing file. */
+       is_same = 0;
+       if ((fp_target = fopen(ptarget, "r")) != NULL)
+       {
+           fgets(old_line, buffer_size, fp_target);
+           if (fclose(fp_target) != 0)
+               ERROR_EXIT(ptarget);
+           if (!strcmp(line, old_line))
+               is_same = 1;
+       }
+
+       if (!is_same)
+       {
+           /* Auto-create directories. */
+           int islash;
+           for (islash = 0; islash < itarget; islash++)
+           {
+               if (ptarget[islash] == '/')
+               {
+                   ptarget[islash] = '\0';
+                   if (stat(ptarget, &stat_buf) != 0
+                   &&  mkdir(ptarget, 0755)     != 0)
+                       ERROR_EXIT( ptarget );
+                   ptarget[islash] = '/';
+               }
+           }
+
+           /* Write the file. */
+           if ((fp_target = fopen(ptarget, "w" )) == NULL)
+               ERROR_EXIT(ptarget);
+           fputs(line, fp_target);
+           if (ferror(fp_target) || fclose(fp_target) != 0)
+               ERROR_EXIT(ptarget);
+       }
+
+       /* Update target list */
+       ptarget += itarget;
+       *(ptarget-1) = '\n';
+    }
+
+    /*
+     * Close autoconfig file.
+     * Terminate the target list.
+     */
+    if (fclose(fp_config) != 0)
+       ERROR_EXIT(str_file_autoconf);
+    *ptarget = '\0';
+
+    /*
+     * Fix up existing files which have no new value.
+     * This is Case 4 and Case 5.
+     *
+     * I re-read the tree and filter it against list_target.
+     * This is crude.  But it avoids data copies.  Also, list_target
+     * is compact and contiguous, so it easily fits into cache.
+     *
+     * Notice that list_target contains strings separated by \n,
+     * with a \n before the first string and after the last.
+     * fgets gives the incoming names a terminating \n.
+     * So by having an initial \n, strstr will find exact matches.
+     */
+
+    fp_find = popen("find * -type f -name \"*.h\" -print", "r");
+    if (fp_find == 0)
+       ERROR_EXIT( "find" );
+
+    line[0] = '\n';
+    while (fgets(line+1, buffer_size, fp_find))
+    {
+       if (strstr(list_target, line) == NULL)
+       {
+           /*
+            * This is an old file with no CONFIG_* flag in autoconf.h.
+            */
+
+           /* First strip the \n. */
+           line[strlen(line)-1] = '\0';
+
+           /* Grab size. */
+           if (stat(line+1, &stat_buf) != 0)
+               ERROR_EXIT(line);
+
+           /* If file is not empty, make it empty and give it a fresh date. */
+           if (stat_buf.st_size != 0)
+           {
+               if ((fp_target = fopen(line+1, "w")) == NULL)
+                   ERROR_EXIT(line);
+               if (fclose(fp_target) != 0)
+                   ERROR_EXIT(line);
+           }
+       }
+    }
+
+    if (pclose(fp_find) != 0)
+       ERROR_EXIT("find");
+
+    return 0;
+}
diff --git a/busybox/shell/Config.in b/busybox/shell/Config.in
new file mode 100644 (file)
index 0000000..bdba40d
--- /dev/null
@@ -0,0 +1,229 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Another Bourne-like Shell"
+
+choice
+       prompt "Choose your default shell"
+       default CONFIG_FEATURE_SH_IS_NONE
+       help
+         Choose a shell. The ash shell is the most bash compatible
+         and full featured one.
+
+config CONFIG_FEATURE_SH_IS_ASH
+       select CONFIG_ASH
+       bool "ash"
+
+config CONFIG_FEATURE_SH_IS_HUSH
+       select CONFIG_HUSH
+       bool "hush"
+
+config CONFIG_FEATURE_SH_IS_LASH
+       select CONFIG_LASH
+       bool "lash"
+
+config CONFIG_FEATURE_SH_IS_MSH
+       select CONFIG_MSH
+       bool "msh"
+
+config CONFIG_FEATURE_SH_IS_NONE
+       bool "none"
+
+endchoice
+
+config CONFIG_ASH
+       bool "ash"
+       default y
+       help
+         Tha 'ash' shell adds about 60k in the default configuration and is
+         the most complete and most pedantically correct shell included with
+         busybox.  This shell is actually a derivative of the Debian 'dash'
+         shell (by Herbert Xu), which was created by porting the 'ash' shell
+         (written by Kenneth Almquist) from NetBSD.
+
+comment "Ash Shell Options"
+       depends on CONFIG_ASH
+
+config CONFIG_ASH_JOB_CONTROL
+       bool "  Enable Job control"
+       default y
+       depends on CONFIG_ASH
+       help
+         Enable job control in the ash shell.
+
+config CONFIG_ASH_ALIAS
+       bool "  Enable alias support"
+       default y
+       depends on CONFIG_ASH
+       help
+         Enable alias support in the ash shell.
+
+config CONFIG_ASH_MATH_SUPPORT
+       bool "  Enable Posix math support"
+       default y
+       depends on CONFIG_ASH
+       help
+         Enable math support in the ash shell.
+
+config CONFIG_ASH_MATH_SUPPORT_64
+       bool "    Extend Posix math support to 64 bit"
+       default n
+       depends on CONFIG_ASH_MATH_SUPPORT
+       help
+         Enable 64-bit math support in the ash shell.  This will make
+         the shell slightly larger, but will allow computation with very
+         large numbers.
+
+config CONFIG_ASH_GETOPTS
+       bool "  Enable getopts builtin to parse positional parameters"
+       default n
+       depends on CONFIG_ASH
+       help
+         Enable getopts builtin in the ash shell.
+
+config CONFIG_ASH_CMDCMD
+       bool "  Enable cmdcmd to override shell builtins"
+       default n
+       depends on CONFIG_ASH
+       help
+         Enable support for the ash 'command' builtin, which allows
+         you to run the specified command with the specified arguments,
+         even when there is an ash builtin command with the same name.
+
+config CONFIG_ASH_MAIL
+       bool "  Check for new mail on interactive shells"
+       default y
+       depends on CONFIG_ASH
+       help
+         Enable "check for new mail" in the ash shell.
+
+config CONFIG_ASH_OPTIMIZE_FOR_SIZE
+       bool "  Optimize for size instead of speed"
+       default y
+       depends on CONFIG_ASH
+       help
+         Compile ash for reduced size at price of speed.
+
+config CONFIG_ASH_RANDOM_SUPPORT
+       bool "  Enable pseudorandom generator and variable $RANDOM"
+       default n
+       depends on CONFIG_ASH
+       help
+         Enable pseudorandom generator and dynamic variable "$RANDOM".
+         Each read of "$RANDOM" will generate a new pseudorandom value.
+         You can reset the generator by using a specified start value.
+         After "unset RANDOM" then generator will switch off and this
+         variable will no longer have special treatment.
+
+config CONFIG_HUSH
+       bool "hush"
+       default n
+       help
+         hush is a very small shell (just 18k) and it has fairly complete
+         Bourne shell grammar.  It even handles all the normal flow control
+         options such as if/then/elif/else/fi, for/in/do/done, while loops,
+         etc.
+
+         It does not handle case/esac, select, function, here documents ( <<
+         word ), arithmetic expansion, aliases, brace expansion, tilde
+         expansion, &> and >& redirection of stdout+stderr, etc.
+
+
+config CONFIG_LASH
+       bool "lash"
+       default n
+       help
+         lash is the very smallest shell (adds just 10k) and it is quite
+         usable as a command prompt, but it is not suitable for any but the
+         most trivial scripting (such as an initrd that calls insmod a few
+         times) since it does not understand any Bourne shell grammar.  It
+         does handle pipes, redirects, and job control though.  Adding in
+         command editing makes it a very nice lightweight command prompt.
+
+
+config CONFIG_MSH
+       bool "msh"
+       default n
+       help
+         The minix shell (adds just 30k) is quite complete and handles things
+         like for/do/done, case/esac and all the things you expect a Bourne
+         shell to do.  It is not always pedantically correct about Bourne
+         shell grammar (try running the shell testscript "tests/sh.testcases"
+         on it and compare vs bash) but for most things it works quite well.
+         It also uses only vfork, so it can be used on uClinux systems.
+
+comment "Bourne Shell Options"
+       depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
+
+config CONFIG_FEATURE_SH_EXTRA_QUIET
+       bool "Hide message on interactive shell startup"
+       default n
+       depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
+       help
+         Remove the busybox introduction when starting a shell.
+
+config CONFIG_FEATURE_SH_STANDALONE_SHELL
+       bool "Standalone shell"
+       default n
+       depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
+       help
+         This option causes the selected busybox shell to use busybox applets
+         in preference to executables in the PATH whenever possible.  For
+         example, entering the command 'ifconfig' into the shell would cause
+         busybox to use the ifconfig busybox applet.  Specifying the fully
+         qualified executable name, such as '/sbin/ifconfig' will still
+         execute the /sbin/ifconfig executable on the filesystem.  This option
+         is generally used when creating a staticly linked version of busybox
+         for use as a rescue shell, in the event that you screw up your system.
+
+         Note that when using this option, the shell will attempt to directly
+         run '/bin/busybox'.  If you do not have the busybox binary sitting in
+         that exact location with that exact name, this option will not work at
+         all.
+
+config CONFIG_FEATURE_COMMAND_EDITING
+       bool "command line editing"
+       default n
+       depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
+       help
+         Enable command editing in shell.
+
+config CONFIG_FEATURE_COMMAND_HISTORY
+       int "history size"
+       default 15
+       depends on CONFIG_FEATURE_COMMAND_EDITING
+       help
+         Specify command history size in shell.
+
+config CONFIG_FEATURE_COMMAND_SAVEHISTORY
+       bool "history saving"
+       default n
+       depends on CONFIG_ASH && CONFIG_FEATURE_COMMAND_EDITING
+       help
+         Enable history saving in ash shell.
+
+config CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+       bool "tab completion"
+       default n
+       depends on CONFIG_FEATURE_COMMAND_EDITING
+       help
+         Enable tab completion in shell.
+
+config CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
+       bool "username completion"
+       default n
+       depends on CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+       help
+         Enable username completion in shell.
+
+config CONFIG_FEATURE_SH_FANCY_PROMPT
+       bool "Fancy shell prompts"
+       default n
+       depends on CONFIG_FEATURE_COMMAND_EDITING
+       help
+         Setting this option allows for prompts to use things like \w and
+         \$ and also using escape codes.
+
+endmenu
diff --git a/busybox/shell/Makefile b/busybox/shell/Makefile
new file mode 100644 (file)
index 0000000..bd1dad6
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_builddir=..
+srcdir=$(top_srcdir)/shell
+SHELL_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/shell/Makefile.in b/busybox/shell/Makefile.in
new file mode 100644 (file)
index 0000000..61b2846
--- /dev/null
@@ -0,0 +1,40 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+SHELL_AR:=shell.a
+ifndef $(SHELL_DIR)
+SHELL_DIR:=$(top_builddir)/shell/
+endif
+srcdir=$(top_srcdir)/shell
+
+SHELLT-y:=
+SHELLT-$(CONFIG_ASH)                           += ash.o
+SHELLT-$(CONFIG_HUSH)                          += hush.o
+SHELLT-$(CONFIG_LASH)                          += lash.o
+SHELLT-$(CONFIG_MSH)                           += msh.o
+SHELLT-$(CONFIG_FEATURE_COMMAND_EDITING)       += cmdedit.o
+
+libraries-y+=$(SHELL_DIR)$(SHELL_AR)
+
+$(SHELL_DIR)$(SHELL_AR): $(patsubst %,$(SHELL_DIR)%, $(SHELLT-y))
+       $(AR) -ro $@ $(patsubst %,$(SHELL_DIR)%, $(SHELLT-y))
+
+$(SHELL_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/shell/ash.c b/busybox/shell/ash.c
new file mode 100644 (file)
index 0000000..d9ea2b0
--- /dev/null
@@ -0,0 +1,13586 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ash shell port for busybox
+ *
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
+ * was re-ported from NetBSD and debianized.
+ *
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original BSD copyright notice is retained at the end of this file.
+ */
+
+/*
+ * rewrite arith.y to micro stack based cryptic algorithm by
+ * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+ *
+ * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
+ * dynamic variables.
+ *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2004 to be
+ * used in busybox and size optimizations,
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
+ *
+ */
+
+
+/*
+ * The follow should be set to reflect the type of system you have:
+ *      JOBS -> 1 if you have Berkeley job control, 0 otherwise.
+ *      define SYSV if you are running under System V.
+ *      define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
+ *      define DEBUG=2 to compile in and turn on debugging.
+ *
+ * When debugging is on, debugging info will be written to ./trace and
+ * a quit signal will generate a core dump.
+ */
+
+
+
+#define IFS_BROKEN
+
+#define PROFILE 0
+
+#ifdef DEBUG
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sysexits.h>
+#include <time.h>
+#include <fnmatch.h>
+
+
+#include "busybox.h"
+#include "pwd_.h"
+
+#ifdef CONFIG_ASH_JOB_CONTROL
+#define JOBS 1
+#else
+#undef JOBS
+#endif
+
+#if JOBS
+#include <termios.h>
+#endif
+
+#include "cmdedit.h"
+
+#ifdef __GLIBC__
+/* glibc sucks */
+static int *dash_errno;
+#undef errno
+#define errno (*dash_errno)
+#endif
+
+#if defined(__uClinux__)
+#error "Do not even bother, ash will not run on uClinux"
+#endif
+
+#ifdef DEBUG
+#define _DIAGASSERT(assert_expr) assert(assert_expr)
+#else
+#define _DIAGASSERT(assert_expr)
+#endif
+
+
+#ifdef CONFIG_ASH_ALIAS
+/*      $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $       */
+
+#define ALIASINUSE      1
+#define ALIASDEAD       2
+
+struct alias {
+       struct alias *next;
+       char *name;
+       char *val;
+       int flag;
+};
+
+static struct alias *lookupalias(const char *, int);
+static int aliascmd(int, char **);
+static int unaliascmd(int, char **);
+static void rmaliases(void);
+static int unalias(const char *);
+static void printalias(const struct alias *);
+#endif
+
+/*      $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $  */
+
+
+static void    setpwd(const char *, int);
+
+/*      $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $      */
+
+
+/*
+ * Types of operations (passed to the errmsg routine).
+ */
+
+
+static const char not_found_msg[] = "%s: not found";
+
+
+#define E_OPEN  "No such file"          /* opening a file */
+#define E_CREAT "Directory nonexistent" /* creating a file */
+#define E_EXEC  not_found_msg+4         /* executing a program */
+
+/*
+ * 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 exception.  To implement nested
+ * exception handlers, the user should save the value of handler on entry
+ * to an inner scope, set handler to point to a jmploc structure for the
+ * inner scope, and restore handler on exit from the scope.
+ */
+
+struct jmploc {
+       jmp_buf loc;
+};
+
+static struct jmploc *handler;
+static int exception;
+static volatile int suppressint;
+static volatile sig_atomic_t intpending;
+
+static int exerrno;            /* Last exec error, error for EXEXEC */
+
+/* exceptions */
+#define EXINT 0         /* SIGINT received */
+#define EXERROR 1       /* a generic error */
+#define EXSHELLPROC 2   /* execute a shell procedure */
+#define EXEXEC 3        /* command execution failed */
+#define EXEXIT 4        /* exit the shell */
+#define EXSIG 5         /* trapped signal in wait(1) */
+
+
+/* do we generate EXSIG events */
+static int exsig;
+/* last pending signal */
+static volatile sig_atomic_t pendingsigs;
+
+/*
+ * These macros allow the user to suspend the handling of interrupt signals
+ * over a period of time.  This is similar to SIGHOLD to or sigblock, but
+ * much more efficient and portable.  (But hacking the kernel is so much
+ * more fun than worrying about efficiency and portability. :-))
+ */
+
+#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
+#define INTOFF \
+       ({ \
+               suppressint++; \
+               xbarrier(); \
+               0; \
+       })
+#define SAVEINT(v) ((v) = suppressint)
+#define RESTOREINT(v) \
+       ({ \
+               xbarrier(); \
+               if ((suppressint = (v)) == 0 && intpending) onint(); \
+               0; \
+       })
+#define EXSIGON() \
+       ({ \
+               exsig++; \
+               xbarrier(); \
+               if (pendingsigs) \
+                       exraise(EXSIG); \
+               0; \
+       })
+/* EXSIG is turned off by evalbltin(). */
+
+
+static void exraise(int) __attribute__((__noreturn__));
+static void onint(void) __attribute__((__noreturn__));
+
+static void error(const char *, ...) __attribute__((__noreturn__));
+static void exerror(int, const char *, ...) __attribute__((__noreturn__));
+
+static void sh_warnx(const char *, ...);
+
+#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
+static void
+inton(void) {
+       if (--suppressint == 0 && intpending) {
+               onint();
+       }
+}
+#define INTON inton()
+static void forceinton(void)
+{
+       suppressint = 0;
+       if (intpending)
+               onint();
+}
+#define FORCEINTON forceinton()
+#else
+#define INTON \
+       ({ \
+               xbarrier(); \
+               if (--suppressint == 0 && intpending) onint(); \
+               0; \
+       })
+#define FORCEINTON \
+       ({ \
+               xbarrier(); \
+               suppressint = 0; \
+               if (intpending) onint(); \
+               0; \
+       })
+#endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
+
+/*
+ * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
+ * so we use _setjmp instead.
+ */
+
+#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
+#define setjmp(jmploc)  _setjmp(jmploc)
+#define longjmp(jmploc, val)    _longjmp(jmploc, val)
+#endif
+
+/*      $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $     */
+
+struct strlist {
+       struct strlist *next;
+       char *text;
+};
+
+
+struct arglist {
+       struct strlist *list;
+       struct strlist **lastp;
+};
+
+/*
+ * expandarg() flags
+ */
+#define EXP_FULL        0x1     /* perform word splitting & file globbing */
+#define EXP_TILDE       0x2     /* do normal tilde expansion */
+#define EXP_VARTILDE    0x4     /* expand tildes in an assignment */
+#define EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
+#define EXP_CASE        0x10    /* keeps quotes around for CASE pattern */
+#define EXP_RECORD      0x20    /* need to record arguments for ifs breakup */
+#define EXP_VARTILDE2   0x40    /* expand tildes after colons only */
+#define EXP_WORD        0x80    /* expand word in parameter expansion */
+#define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */
+
+
+union node;
+static void expandarg(union node *, struct arglist *, int);
+#define rmescapes(p) _rmescapes((p), 0)
+static char *_rmescapes(char *, int);
+static int casematch(union node *, char *);
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static void expari(int);
+#endif
+
+/*      $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $       */
+
+static char *commandname;              /* currently executing command */
+static struct strlist *cmdenviron;     /* environment for builtin command */
+static int exitstatus;                 /* exit status of last command */
+static int back_exitstatus;            /* exit status of backquoted 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 */
+};
+
+/*
+ * This file was generated by the mknodes program.
+ */
+
+#define NCMD 0
+#define NPIPE 1
+#define NREDIR 2
+#define NBACKGND 3
+#define NSUBSHELL 4
+#define NAND 5
+#define NOR 6
+#define NSEMI 7
+#define NIF 8
+#define NWHILE 9
+#define NUNTIL 10
+#define NFOR 11
+#define NCASE 12
+#define NCLIST 13
+#define NDEFUN 14
+#define NARG 15
+#define NTO 16
+#define NCLOBBER 17
+#define NFROM 18
+#define NFROMTO 19
+#define NAPPEND 20
+#define NTOFD 21
+#define NFROMFD 22
+#define NHERE 23
+#define NXHERE 24
+#define NNOT 25
+
+
+
+struct ncmd {
+      int type;
+      union node *assign;
+      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 nbinary {
+      int type;
+      union node *ch1;
+      union node *ch2;
+};
+
+
+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;
+      union node *vname;
+};
+
+
+struct nhere {
+      int type;
+      union node *next;
+      int fd;
+      union node *doc;
+};
+
+
+struct nnot {
+      int type;
+      union node *com;
+};
+
+
+union node {
+      int type;
+      struct ncmd ncmd;
+      struct npipe npipe;
+      struct nredir nredir;
+      struct nbinary nbinary;
+      struct nif nif;
+      struct nfor nfor;
+      struct ncase ncase;
+      struct nclist nclist;
+      struct narg narg;
+      struct nfile nfile;
+      struct ndup ndup;
+      struct nhere nhere;
+      struct nnot nnot;
+};
+
+
+struct nodelist {
+       struct nodelist *next;
+       union node *n;
+};
+
+
+struct funcnode {
+       int count;
+       union node n;
+};
+
+
+static void freefunc(struct funcnode *);
+/*      $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $     */
+
+/* control characters in argument strings */
+#define CTL_FIRST '\201'        /* first 'special' character */
+#define CTLESC '\201'           /* escape next character */
+#define CTLVAR '\202'           /* variable defn */
+#define CTLENDVAR '\203'
+#define CTLBACKQ '\204'
+#define CTLQUOTE 01             /* ored with CTLBACKQ code if in quotes */
+/*      CTLBACKQ | CTLQUOTE == '\205' */
+#define CTLARI  '\206'          /* arithmetic expression */
+#define CTLENDARI '\207'
+#define CTLQUOTEMARK '\210'
+#define CTL_LAST '\210'         /* last 'special' character */
+
+/* variable substitution byte (follows CTLVAR) */
+#define VSTYPE  0x0f            /* type of variable substitution */
+#define VSNUL   0x10            /* colon--treat the empty string as unset */
+#define VSQUOTE 0x80            /* inside double quotes--suppress splitting */
+
+/* values of VSTYPE field */
+#define VSNORMAL        0x1             /* normal variable:  $var or ${var} */
+#define VSMINUS         0x2             /* ${var-text} */
+#define VSPLUS          0x3             /* ${var+text} */
+#define VSQUESTION      0x4             /* ${var?message} */
+#define VSASSIGN        0x5             /* ${var=text} */
+#define VSTRIMRIGHT     0x6             /* ${var%pattern} */
+#define VSTRIMRIGHTMAX  0x7             /* ${var%%pattern} */
+#define VSTRIMLEFT      0x8             /* ${var#pattern} */
+#define VSTRIMLEFTMAX   0x9             /* ${var##pattern} */
+#define VSLENGTH        0xa             /* ${#var} */
+
+/* values of checkkwd variable */
+#define CHKALIAS        0x1
+#define CHKKWD          0x2
+#define CHKNL           0x4
+
+#define IBUFSIZ (BUFSIZ + 1)
+
+/*
+ * 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.
+ */
+static int plinno = 1;                  /* input line number */
+
+/* number of characters left in input buffer */
+static int parsenleft;                  /* copy of parsefile->nleft */
+static int parselleft;                  /* copy of parsefile->lleft */
+
+/* next character in input buffer */
+static char *parsenextc;                /* copy of parsefile->nextc */
+
+struct strpush {
+       struct strpush *prev;   /* preceding string on stack */
+       char *prevstring;
+       int prevnleft;
+#ifdef CONFIG_ASH_ALIAS
+       struct alias *ap;       /* if push was associated with an alias */
+#endif
+       char *string;           /* remember the string since it may change */
+};
+
+struct parsefile {
+       struct parsefile *prev; /* preceding file on stack */
+       int linno;              /* current line */
+       int fd;                 /* file descriptor (or -1 if string) */
+       int nleft;              /* number of chars left in this line */
+       int lleft;              /* number of chars left in this buffer */
+       char *nextc;            /* next char in buffer */
+       char *buf;              /* input buffer */
+       struct strpush *strpush; /* for pushing strings at this level */
+       struct strpush basestrpush; /* so pushing one is fast */
+};
+
+static struct parsefile basepf;         /* top level input file */
+static char basebuf[IBUFSIZ];           /* buffer for top level input file */
+static struct parsefile *parsefile = &basepf;  /* current input file */
+
+
+static int tokpushback;                 /* last token pushed back */
+#define NEOF ((union node *)&tokpushback)
+static int parsebackquote;             /* nonzero if we are inside backquotes */
+static int doprompt;                   /* if set, prompt the user */
+static int needprompt;                 /* true if interactive and at start of line */
+static int lasttoken;                  /* last token read */
+static char *wordtext;                 /* text of last word returned by readtoken */
+static int checkkwd;
+static struct nodelist *backquotelist;
+static union node *redirnode;
+static struct heredoc *heredoc;
+static int quoteflag;                  /* set if (part of) last token was quoted */
+static int startlinno;                 /* line # where last token started */
+
+static union node *parsecmd(int);
+static void fixredir(union node *, const char *, int);
+static const char *const *findkwd(const char *);
+static char *endofname(const char *);
+
+/*      $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $   */
+
+typedef void *pointer;
+
+static char nullstr[1];                /* zero length string */
+static const char spcstr[] = " ";
+static const char snlfmt[] = "%s\n";
+static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+static const char illnum[] = "Illegal number: %s";
+static const char homestr[] = "HOME";
+
+#ifdef DEBUG
+#define TRACE(param)    trace param
+#define TRACEV(param)   tracev param
+#else
+#define TRACE(param)
+#define TRACEV(param)
+#endif
+
+#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+#define __builtin_expect(x, expected_value) (x)
+#endif
+
+#define xlikely(x)       __builtin_expect((x),1)
+
+
+#define TEOF 0
+#define TNL 1
+#define TREDIR 2
+#define TWORD 3
+#define TSEMI 4
+#define TBACKGND 5
+#define TAND 6
+#define TOR 7
+#define TPIPE 8
+#define TLP 9
+#define TRP 10
+#define TENDCASE 11
+#define TENDBQUOTE 12
+#define TNOT 13
+#define TCASE 14
+#define TDO 15
+#define TDONE 16
+#define TELIF 17
+#define TELSE 18
+#define TESAC 19
+#define TFI 20
+#define TFOR 21
+#define TIF 22
+#define TIN 23
+#define TTHEN 24
+#define TUNTIL 25
+#define TWHILE 26
+#define TBEGIN 27
+#define TEND 28
+
+/* first char is indicating which tokens mark the end of a list */
+static const char *const tokname_array[] = {
+       "\1end of file",
+       "\0newline",
+       "\0redirection",
+       "\0word",
+       "\0;",
+       "\0&",
+       "\0&&",
+       "\0||",
+       "\0|",
+       "\0(",
+       "\1)",
+       "\1;;",
+       "\1`",
+#define KWDOFFSET 13
+       /* the following are keywords */
+       "\0!",
+       "\0case",
+       "\1do",
+       "\1done",
+       "\1elif",
+       "\1else",
+       "\1esac",
+       "\1fi",
+       "\0for",
+       "\0if",
+       "\0in",
+       "\1then",
+       "\0until",
+       "\0while",
+       "\0{",
+       "\1}",
+};
+
+static const char *tokname(int tok)
+{
+       static char buf[16];
+
+       if (tok >= TSEMI)
+               buf[0] = '"';
+       sprintf(buf + (tok >= TSEMI), "%s%c",
+                       tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
+       return buf;
+}
+
+/*      $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $    */
+
+/*
+ * Most machines require the value returned from malloc to be aligned
+ * in some way.  The following macro will get this right on many machines.
+ */
+
+#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1)
+/*
+ * It appears that grabstackstr() will barf with such alignments
+ * because stalloc() will return a string allocated in a new stackblock.
+ */
+#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
+
+/*
+ * This file was generated by the mksyntax program.
+ */
+
+
+/* Syntax classes */
+#define CWORD 0                 /* character is nothing special */
+#define CNL 1                   /* newline character */
+#define CBACK 2                 /* a backslash character */
+#define CSQUOTE 3               /* single quote */
+#define CDQUOTE 4               /* double quote */
+#define CENDQUOTE 5             /* a terminating quote */
+#define CBQUOTE 6               /* backwards single quote */
+#define CVAR 7                  /* a dollar sign */
+#define CENDVAR 8               /* a '}' character */
+#define CLP 9                   /* a left paren in arithmetic */
+#define CRP 10                  /* a right paren in arithmetic */
+#define CENDFILE 11             /* end of file */
+#define CCTL 12                 /* like CWORD, except it must be escaped */
+#define CSPCL 13                /* these terminate a word */
+#define CIGN 14                 /* character should be ignored */
+
+#ifdef CONFIG_ASH_ALIAS
+#define SYNBASE 130
+#define PEOF -130
+#define PEOA -129
+#define PEOA_OR_PEOF PEOA
+#else
+#define SYNBASE 129
+#define PEOF -129
+#define PEOA_OR_PEOF PEOF
+#endif
+
+#define is_digit(c)     ((unsigned)((c) - '0') <= 9)
+#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+
+/*
+ * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
+ * (assuming ascii char codes, as the original implementation did)
+ */
+#define is_special(c) \
+    ( (((unsigned int)c) - 33 < 32) \
+                        && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+
+#define digit_val(c)    ((c) - '0')
+
+/*
+ * This file was generated by the mksyntax program.
+ */
+
+#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
+#define USE_SIT_FUNCTION
+#endif
+
+/* number syntax index */
+#define  BASESYNTAX  0  /* not in quotes */
+#define  DQSYNTAX    1  /* in double quotes */
+#define  SQSYNTAX    2  /* in single quotes */
+#define  ARISYNTAX   3  /* in arithmetic */
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static const char S_I_T[][4] = {
+#ifdef CONFIG_ASH_ALIAS
+       {CSPCL, CIGN, CIGN, CIGN},              /* 0, PEOA */
+#endif
+       {CSPCL, CWORD, CWORD, CWORD},           /* 1, ' ' */
+       {CNL, CNL, CNL, CNL},                   /* 2, \n */
+       {CWORD, CCTL, CCTL, CWORD},             /* 3, !*-/:=?[]~ */
+       {CDQUOTE, CENDQUOTE, CWORD, CWORD},     /* 4, '"' */
+       {CVAR, CVAR, CWORD, CVAR},              /* 5, $ */
+       {CSQUOTE, CWORD, CENDQUOTE, CWORD},     /* 6, "'" */
+       {CSPCL, CWORD, CWORD, CLP},             /* 7, ( */
+       {CSPCL, CWORD, CWORD, CRP},             /* 8, ) */
+       {CBACK, CBACK, CCTL, CBACK},            /* 9, \ */
+       {CBQUOTE, CBQUOTE, CWORD, CBQUOTE},     /* 10, ` */
+       {CENDVAR, CENDVAR, CWORD, CENDVAR},     /* 11, } */
+#ifndef USE_SIT_FUNCTION
+       {CENDFILE, CENDFILE, CENDFILE, CENDFILE},       /* 12, PEOF */
+       {CWORD, CWORD, CWORD, CWORD},           /* 13, 0-9A-Za-z */
+       {CCTL, CCTL, CCTL, CCTL}                /* 14, CTLESC ... */
+#endif
+};
+#else
+static const char S_I_T[][3] = {
+#ifdef CONFIG_ASH_ALIAS
+       {CSPCL, CIGN, CIGN},                    /* 0, PEOA */
+#endif
+       {CSPCL, CWORD, CWORD},                  /* 1, ' ' */
+       {CNL, CNL, CNL},                        /* 2, \n */
+       {CWORD, CCTL, CCTL},                    /* 3, !*-/:=?[]~ */
+       {CDQUOTE, CENDQUOTE, CWORD},            /* 4, '"' */
+       {CVAR, CVAR, CWORD},                    /* 5, $ */
+       {CSQUOTE, CWORD, CENDQUOTE},            /* 6, "'" */
+       {CSPCL, CWORD, CWORD},                  /* 7, ( */
+       {CSPCL, CWORD, CWORD},                  /* 8, ) */
+       {CBACK, CBACK, CCTL},                   /* 9, \ */
+       {CBQUOTE, CBQUOTE, CWORD},              /* 10, ` */
+       {CENDVAR, CENDVAR, CWORD},              /* 11, } */
+#ifndef USE_SIT_FUNCTION
+       {CENDFILE, CENDFILE, CENDFILE},         /* 12, PEOF */
+       {CWORD, CWORD, CWORD},                  /* 13, 0-9A-Za-z */
+       {CCTL, CCTL, CCTL}                      /* 14, CTLESC ... */
+#endif
+};
+#endif /* CONFIG_ASH_MATH_SUPPORT */
+
+#ifdef USE_SIT_FUNCTION
+
+#define U_C(c) ((unsigned char)(c))
+
+static int SIT(int c, int syntax)
+{
+       static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+#ifdef CONFIG_ASH_ALIAS
+       static const char syntax_index_table[] = {
+               1, 2, 1, 3, 4, 5, 1, 6,         /* "\t\n !\"$&'" */
+               7, 8, 3, 3, 3, 3, 1, 1,         /* "()*-/:;<" */
+               3, 1, 3, 3, 9, 3, 10, 1,        /* "=>?[\\]`|" */
+               11, 3                           /* "}~" */
+       };
+#else
+       static const char syntax_index_table[] = {
+               0, 1, 0, 2, 3, 4, 0, 5,         /* "\t\n !\"$&'" */
+               6, 7, 2, 2, 2, 2, 0, 0,         /* "()*-/:;<" */
+               2, 0, 2, 2, 8, 2, 9, 0,         /* "=>?[\\]`|" */
+               10, 2                           /* "}~" */
+       };
+#endif
+       const char *s;
+       int indx;
+
+       if (c == PEOF)          /* 2^8+2 */
+               return CENDFILE;
+#ifdef CONFIG_ASH_ALIAS
+       if (c == PEOA)          /* 2^8+1 */
+               indx = 0;
+       else
+#endif
+               if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
+                       return CCTL;
+       else {
+               s = strchr(spec_symbls, c);
+               if (s == 0 || *s == 0)
+                       return CWORD;
+               indx = syntax_index_table[(s - spec_symbls)];
+       }
+       return S_I_T[indx][syntax];
+}
+
+#else                                                   /* USE_SIT_FUNCTION */
+
+#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
+
+#ifdef CONFIG_ASH_ALIAS
+#define CSPCL_CIGN_CIGN_CIGN                           0
+#define CSPCL_CWORD_CWORD_CWORD                        1
+#define CNL_CNL_CNL_CNL                                2
+#define CWORD_CCTL_CCTL_CWORD                          3
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD                  4
+#define CVAR_CVAR_CWORD_CVAR                           5
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD                  6
+#define CSPCL_CWORD_CWORD_CLP                          7
+#define CSPCL_CWORD_CWORD_CRP                          8
+#define CBACK_CBACK_CCTL_CBACK                         9
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE                 10
+#define CENDVAR_CENDVAR_CWORD_CENDVAR                 11
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE           12
+#define CWORD_CWORD_CWORD_CWORD                       13
+#define CCTL_CCTL_CCTL_CCTL                           14
+#else
+#define CSPCL_CWORD_CWORD_CWORD                        0
+#define CNL_CNL_CNL_CNL                                1
+#define CWORD_CCTL_CCTL_CWORD                          2
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD                  3
+#define CVAR_CVAR_CWORD_CVAR                           4
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD                  5
+#define CSPCL_CWORD_CWORD_CLP                          6
+#define CSPCL_CWORD_CWORD_CRP                          7
+#define CBACK_CBACK_CCTL_CBACK                         8
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE                  9
+#define CENDVAR_CENDVAR_CWORD_CENDVAR                 10
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE           11
+#define CWORD_CWORD_CWORD_CWORD                       12
+#define CCTL_CCTL_CCTL_CCTL                           13
+#endif
+
+static const char syntax_index_table[258] = {
+       /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
+       /*   0  PEOF */      CENDFILE_CENDFILE_CENDFILE_CENDFILE,
+#ifdef CONFIG_ASH_ALIAS
+       /*   1  PEOA */      CSPCL_CIGN_CIGN_CIGN,
+#endif
+       /*   2  -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
+       /*   3  -127 CTLESC       */ CCTL_CCTL_CCTL_CCTL,
+       /*   4  -126 CTLVAR       */ CCTL_CCTL_CCTL_CCTL,
+       /*   5  -125 CTLENDVAR    */ CCTL_CCTL_CCTL_CCTL,
+       /*   6  -124 CTLBACKQ     */ CCTL_CCTL_CCTL_CCTL,
+       /*   7  -123 CTLQUOTE     */ CCTL_CCTL_CCTL_CCTL,
+       /*   8  -122 CTLARI       */ CCTL_CCTL_CCTL_CCTL,
+       /*   9  -121 CTLENDARI    */ CCTL_CCTL_CCTL_CCTL,
+       /*  10  -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
+       /*  11  -119      */ CWORD_CWORD_CWORD_CWORD,
+       /*  12  -118      */ CWORD_CWORD_CWORD_CWORD,
+       /*  13  -117      */ CWORD_CWORD_CWORD_CWORD,
+       /*  14  -116      */ CWORD_CWORD_CWORD_CWORD,
+       /*  15  -115      */ CWORD_CWORD_CWORD_CWORD,
+       /*  16  -114      */ CWORD_CWORD_CWORD_CWORD,
+       /*  17  -113      */ CWORD_CWORD_CWORD_CWORD,
+       /*  18  -112      */ CWORD_CWORD_CWORD_CWORD,
+       /*  19  -111      */ CWORD_CWORD_CWORD_CWORD,
+       /*  20  -110      */ CWORD_CWORD_CWORD_CWORD,
+       /*  21  -109      */ CWORD_CWORD_CWORD_CWORD,
+       /*  22  -108      */ CWORD_CWORD_CWORD_CWORD,
+       /*  23  -107      */ CWORD_CWORD_CWORD_CWORD,
+       /*  24  -106      */ CWORD_CWORD_CWORD_CWORD,
+       /*  25  -105      */ CWORD_CWORD_CWORD_CWORD,
+       /*  26  -104      */ CWORD_CWORD_CWORD_CWORD,
+       /*  27  -103      */ CWORD_CWORD_CWORD_CWORD,
+       /*  28  -102      */ CWORD_CWORD_CWORD_CWORD,
+       /*  29  -101      */ CWORD_CWORD_CWORD_CWORD,
+       /*  30  -100      */ CWORD_CWORD_CWORD_CWORD,
+       /*  31   -99      */ CWORD_CWORD_CWORD_CWORD,
+       /*  32   -98      */ CWORD_CWORD_CWORD_CWORD,
+       /*  33   -97      */ CWORD_CWORD_CWORD_CWORD,
+       /*  34   -96      */ CWORD_CWORD_CWORD_CWORD,
+       /*  35   -95      */ CWORD_CWORD_CWORD_CWORD,
+       /*  36   -94      */ CWORD_CWORD_CWORD_CWORD,
+       /*  37   -93      */ CWORD_CWORD_CWORD_CWORD,
+       /*  38   -92      */ CWORD_CWORD_CWORD_CWORD,
+       /*  39   -91      */ CWORD_CWORD_CWORD_CWORD,
+       /*  40   -90      */ CWORD_CWORD_CWORD_CWORD,
+       /*  41   -89      */ CWORD_CWORD_CWORD_CWORD,
+       /*  42   -88      */ CWORD_CWORD_CWORD_CWORD,
+       /*  43   -87      */ CWORD_CWORD_CWORD_CWORD,
+       /*  44   -86      */ CWORD_CWORD_CWORD_CWORD,
+       /*  45   -85      */ CWORD_CWORD_CWORD_CWORD,
+       /*  46   -84      */ CWORD_CWORD_CWORD_CWORD,
+       /*  47   -83      */ CWORD_CWORD_CWORD_CWORD,
+       /*  48   -82      */ CWORD_CWORD_CWORD_CWORD,
+       /*  49   -81      */ CWORD_CWORD_CWORD_CWORD,
+       /*  50   -80      */ CWORD_CWORD_CWORD_CWORD,
+       /*  51   -79      */ CWORD_CWORD_CWORD_CWORD,
+       /*  52   -78      */ CWORD_CWORD_CWORD_CWORD,
+       /*  53   -77      */ CWORD_CWORD_CWORD_CWORD,
+       /*  54   -76      */ CWORD_CWORD_CWORD_CWORD,
+       /*  55   -75      */ CWORD_CWORD_CWORD_CWORD,
+       /*  56   -74      */ CWORD_CWORD_CWORD_CWORD,
+       /*  57   -73      */ CWORD_CWORD_CWORD_CWORD,
+       /*  58   -72      */ CWORD_CWORD_CWORD_CWORD,
+       /*  59   -71      */ CWORD_CWORD_CWORD_CWORD,
+       /*  60   -70      */ CWORD_CWORD_CWORD_CWORD,
+       /*  61   -69      */ CWORD_CWORD_CWORD_CWORD,
+       /*  62   -68      */ CWORD_CWORD_CWORD_CWORD,
+       /*  63   -67      */ CWORD_CWORD_CWORD_CWORD,
+       /*  64   -66      */ CWORD_CWORD_CWORD_CWORD,
+       /*  65   -65      */ CWORD_CWORD_CWORD_CWORD,
+       /*  66   -64      */ CWORD_CWORD_CWORD_CWORD,
+       /*  67   -63      */ CWORD_CWORD_CWORD_CWORD,
+       /*  68   -62      */ CWORD_CWORD_CWORD_CWORD,
+       /*  69   -61      */ CWORD_CWORD_CWORD_CWORD,
+       /*  70   -60      */ CWORD_CWORD_CWORD_CWORD,
+       /*  71   -59      */ CWORD_CWORD_CWORD_CWORD,
+       /*  72   -58      */ CWORD_CWORD_CWORD_CWORD,
+       /*  73   -57      */ CWORD_CWORD_CWORD_CWORD,
+       /*  74   -56      */ CWORD_CWORD_CWORD_CWORD,
+       /*  75   -55      */ CWORD_CWORD_CWORD_CWORD,
+       /*  76   -54      */ CWORD_CWORD_CWORD_CWORD,
+       /*  77   -53      */ CWORD_CWORD_CWORD_CWORD,
+       /*  78   -52      */ CWORD_CWORD_CWORD_CWORD,
+       /*  79   -51      */ CWORD_CWORD_CWORD_CWORD,
+       /*  80   -50      */ CWORD_CWORD_CWORD_CWORD,
+       /*  81   -49      */ CWORD_CWORD_CWORD_CWORD,
+       /*  82   -48      */ CWORD_CWORD_CWORD_CWORD,
+       /*  83   -47      */ CWORD_CWORD_CWORD_CWORD,
+       /*  84   -46      */ CWORD_CWORD_CWORD_CWORD,
+       /*  85   -45      */ CWORD_CWORD_CWORD_CWORD,
+       /*  86   -44      */ CWORD_CWORD_CWORD_CWORD,
+       /*  87   -43      */ CWORD_CWORD_CWORD_CWORD,
+       /*  88   -42      */ CWORD_CWORD_CWORD_CWORD,
+       /*  89   -41      */ CWORD_CWORD_CWORD_CWORD,
+       /*  90   -40      */ CWORD_CWORD_CWORD_CWORD,
+       /*  91   -39      */ CWORD_CWORD_CWORD_CWORD,
+       /*  92   -38      */ CWORD_CWORD_CWORD_CWORD,
+       /*  93   -37      */ CWORD_CWORD_CWORD_CWORD,
+       /*  94   -36      */ CWORD_CWORD_CWORD_CWORD,
+       /*  95   -35      */ CWORD_CWORD_CWORD_CWORD,
+       /*  96   -34      */ CWORD_CWORD_CWORD_CWORD,
+       /*  97   -33      */ CWORD_CWORD_CWORD_CWORD,
+       /*  98   -32      */ CWORD_CWORD_CWORD_CWORD,
+       /*  99   -31      */ CWORD_CWORD_CWORD_CWORD,
+       /* 100   -30      */ CWORD_CWORD_CWORD_CWORD,
+       /* 101   -29      */ CWORD_CWORD_CWORD_CWORD,
+       /* 102   -28      */ CWORD_CWORD_CWORD_CWORD,
+       /* 103   -27      */ CWORD_CWORD_CWORD_CWORD,
+       /* 104   -26      */ CWORD_CWORD_CWORD_CWORD,
+       /* 105   -25      */ CWORD_CWORD_CWORD_CWORD,
+       /* 106   -24      */ CWORD_CWORD_CWORD_CWORD,
+       /* 107   -23      */ CWORD_CWORD_CWORD_CWORD,
+       /* 108   -22      */ CWORD_CWORD_CWORD_CWORD,
+       /* 109   -21      */ CWORD_CWORD_CWORD_CWORD,
+       /* 110   -20      */ CWORD_CWORD_CWORD_CWORD,
+       /* 111   -19      */ CWORD_CWORD_CWORD_CWORD,
+       /* 112   -18      */ CWORD_CWORD_CWORD_CWORD,
+       /* 113   -17      */ CWORD_CWORD_CWORD_CWORD,
+       /* 114   -16      */ CWORD_CWORD_CWORD_CWORD,
+       /* 115   -15      */ CWORD_CWORD_CWORD_CWORD,
+       /* 116   -14      */ CWORD_CWORD_CWORD_CWORD,
+       /* 117   -13      */ CWORD_CWORD_CWORD_CWORD,
+       /* 118   -12      */ CWORD_CWORD_CWORD_CWORD,
+       /* 119   -11      */ CWORD_CWORD_CWORD_CWORD,
+       /* 120   -10      */ CWORD_CWORD_CWORD_CWORD,
+       /* 121    -9      */ CWORD_CWORD_CWORD_CWORD,
+       /* 122    -8      */ CWORD_CWORD_CWORD_CWORD,
+       /* 123    -7      */ CWORD_CWORD_CWORD_CWORD,
+       /* 124    -6      */ CWORD_CWORD_CWORD_CWORD,
+       /* 125    -5      */ CWORD_CWORD_CWORD_CWORD,
+       /* 126    -4      */ CWORD_CWORD_CWORD_CWORD,
+       /* 127    -3      */ CWORD_CWORD_CWORD_CWORD,
+       /* 128    -2      */ CWORD_CWORD_CWORD_CWORD,
+       /* 129    -1      */ CWORD_CWORD_CWORD_CWORD,
+       /* 130     0      */ CWORD_CWORD_CWORD_CWORD,
+       /* 131     1      */ CWORD_CWORD_CWORD_CWORD,
+       /* 132     2      */ CWORD_CWORD_CWORD_CWORD,
+       /* 133     3      */ CWORD_CWORD_CWORD_CWORD,
+       /* 134     4      */ CWORD_CWORD_CWORD_CWORD,
+       /* 135     5      */ CWORD_CWORD_CWORD_CWORD,
+       /* 136     6      */ CWORD_CWORD_CWORD_CWORD,
+       /* 137     7      */ CWORD_CWORD_CWORD_CWORD,
+       /* 138     8      */ CWORD_CWORD_CWORD_CWORD,
+       /* 139     9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
+       /* 140    10 "\n" */ CNL_CNL_CNL_CNL,
+       /* 141    11      */ CWORD_CWORD_CWORD_CWORD,
+       /* 142    12      */ CWORD_CWORD_CWORD_CWORD,
+       /* 143    13      */ CWORD_CWORD_CWORD_CWORD,
+       /* 144    14      */ CWORD_CWORD_CWORD_CWORD,
+       /* 145    15      */ CWORD_CWORD_CWORD_CWORD,
+       /* 146    16      */ CWORD_CWORD_CWORD_CWORD,
+       /* 147    17      */ CWORD_CWORD_CWORD_CWORD,
+       /* 148    18      */ CWORD_CWORD_CWORD_CWORD,
+       /* 149    19      */ CWORD_CWORD_CWORD_CWORD,
+       /* 150    20      */ CWORD_CWORD_CWORD_CWORD,
+       /* 151    21      */ CWORD_CWORD_CWORD_CWORD,
+       /* 152    22      */ CWORD_CWORD_CWORD_CWORD,
+       /* 153    23      */ CWORD_CWORD_CWORD_CWORD,
+       /* 154    24      */ CWORD_CWORD_CWORD_CWORD,
+       /* 155    25      */ CWORD_CWORD_CWORD_CWORD,
+       /* 156    26      */ CWORD_CWORD_CWORD_CWORD,
+       /* 157    27      */ CWORD_CWORD_CWORD_CWORD,
+       /* 158    28      */ CWORD_CWORD_CWORD_CWORD,
+       /* 159    29      */ CWORD_CWORD_CWORD_CWORD,
+       /* 160    30      */ CWORD_CWORD_CWORD_CWORD,
+       /* 161    31      */ CWORD_CWORD_CWORD_CWORD,
+       /* 162    32  " " */ CSPCL_CWORD_CWORD_CWORD,
+       /* 163    33  "!" */ CWORD_CCTL_CCTL_CWORD,
+       /* 164    34  """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
+       /* 165    35  "#" */ CWORD_CWORD_CWORD_CWORD,
+       /* 166    36  "$" */ CVAR_CVAR_CWORD_CVAR,
+       /* 167    37  "%" */ CWORD_CWORD_CWORD_CWORD,
+       /* 168    38  "&" */ CSPCL_CWORD_CWORD_CWORD,
+       /* 169    39  "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
+       /* 170    40  "(" */ CSPCL_CWORD_CWORD_CLP,
+       /* 171    41  ")" */ CSPCL_CWORD_CWORD_CRP,
+       /* 172    42  "*" */ CWORD_CCTL_CCTL_CWORD,
+       /* 173    43  "+" */ CWORD_CWORD_CWORD_CWORD,
+       /* 174    44  "," */ CWORD_CWORD_CWORD_CWORD,
+       /* 175    45  "-" */ CWORD_CCTL_CCTL_CWORD,
+       /* 176    46  "." */ CWORD_CWORD_CWORD_CWORD,
+       /* 177    47  "/" */ CWORD_CCTL_CCTL_CWORD,
+       /* 178    48  "0" */ CWORD_CWORD_CWORD_CWORD,
+       /* 179    49  "1" */ CWORD_CWORD_CWORD_CWORD,
+       /* 180    50  "2" */ CWORD_CWORD_CWORD_CWORD,
+       /* 181    51  "3" */ CWORD_CWORD_CWORD_CWORD,
+       /* 182    52  "4" */ CWORD_CWORD_CWORD_CWORD,
+       /* 183    53  "5" */ CWORD_CWORD_CWORD_CWORD,
+       /* 184    54  "6" */ CWORD_CWORD_CWORD_CWORD,
+       /* 185    55  "7" */ CWORD_CWORD_CWORD_CWORD,
+       /* 186    56  "8" */ CWORD_CWORD_CWORD_CWORD,
+       /* 187    57  "9" */ CWORD_CWORD_CWORD_CWORD,
+       /* 188    58  ":" */ CWORD_CCTL_CCTL_CWORD,
+       /* 189    59  ";" */ CSPCL_CWORD_CWORD_CWORD,
+       /* 190    60  "<" */ CSPCL_CWORD_CWORD_CWORD,
+       /* 191    61  "=" */ CWORD_CCTL_CCTL_CWORD,
+       /* 192    62  ">" */ CSPCL_CWORD_CWORD_CWORD,
+       /* 193    63  "?" */ CWORD_CCTL_CCTL_CWORD,
+       /* 194    64  "@" */ CWORD_CWORD_CWORD_CWORD,
+       /* 195    65  "A" */ CWORD_CWORD_CWORD_CWORD,
+       /* 196    66  "B" */ CWORD_CWORD_CWORD_CWORD,
+       /* 197    67  "C" */ CWORD_CWORD_CWORD_CWORD,
+       /* 198    68  "D" */ CWORD_CWORD_CWORD_CWORD,
+       /* 199    69  "E" */ CWORD_CWORD_CWORD_CWORD,
+       /* 200    70  "F" */ CWORD_CWORD_CWORD_CWORD,
+       /* 201    71  "G" */ CWORD_CWORD_CWORD_CWORD,
+       /* 202    72  "H" */ CWORD_CWORD_CWORD_CWORD,
+       /* 203    73  "I" */ CWORD_CWORD_CWORD_CWORD,
+       /* 204    74  "J" */ CWORD_CWORD_CWORD_CWORD,
+       /* 205    75  "K" */ CWORD_CWORD_CWORD_CWORD,
+       /* 206    76  "L" */ CWORD_CWORD_CWORD_CWORD,
+       /* 207    77  "M" */ CWORD_CWORD_CWORD_CWORD,
+       /* 208    78  "N" */ CWORD_CWORD_CWORD_CWORD,
+       /* 209    79  "O" */ CWORD_CWORD_CWORD_CWORD,
+       /* 210    80  "P" */ CWORD_CWORD_CWORD_CWORD,
+       /* 211    81  "Q" */ CWORD_CWORD_CWORD_CWORD,
+       /* 212    82  "R" */ CWORD_CWORD_CWORD_CWORD,
+       /* 213    83  "S" */ CWORD_CWORD_CWORD_CWORD,
+       /* 214    84  "T" */ CWORD_CWORD_CWORD_CWORD,
+       /* 215    85  "U" */ CWORD_CWORD_CWORD_CWORD,
+       /* 216    86  "V" */ CWORD_CWORD_CWORD_CWORD,
+       /* 217    87  "W" */ CWORD_CWORD_CWORD_CWORD,
+       /* 218    88  "X" */ CWORD_CWORD_CWORD_CWORD,
+       /* 219    89  "Y" */ CWORD_CWORD_CWORD_CWORD,
+       /* 220    90  "Z" */ CWORD_CWORD_CWORD_CWORD,
+       /* 221    91  "[" */ CWORD_CCTL_CCTL_CWORD,
+       /* 222    92  "\" */ CBACK_CBACK_CCTL_CBACK,
+       /* 223    93  "]" */ CWORD_CCTL_CCTL_CWORD,
+       /* 224    94  "^" */ CWORD_CWORD_CWORD_CWORD,
+       /* 225    95  "_" */ CWORD_CWORD_CWORD_CWORD,
+       /* 226    96  "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
+       /* 227    97  "a" */ CWORD_CWORD_CWORD_CWORD,
+       /* 228    98  "b" */ CWORD_CWORD_CWORD_CWORD,
+       /* 229    99  "c" */ CWORD_CWORD_CWORD_CWORD,
+       /* 230   100  "d" */ CWORD_CWORD_CWORD_CWORD,
+       /* 231   101  "e" */ CWORD_CWORD_CWORD_CWORD,
+       /* 232   102  "f" */ CWORD_CWORD_CWORD_CWORD,
+       /* 233   103  "g" */ CWORD_CWORD_CWORD_CWORD,
+       /* 234   104  "h" */ CWORD_CWORD_CWORD_CWORD,
+       /* 235   105  "i" */ CWORD_CWORD_CWORD_CWORD,
+       /* 236   106  "j" */ CWORD_CWORD_CWORD_CWORD,
+       /* 237   107  "k" */ CWORD_CWORD_CWORD_CWORD,
+       /* 238   108  "l" */ CWORD_CWORD_CWORD_CWORD,
+       /* 239   109  "m" */ CWORD_CWORD_CWORD_CWORD,
+       /* 240   110  "n" */ CWORD_CWORD_CWORD_CWORD,
+       /* 241   111  "o" */ CWORD_CWORD_CWORD_CWORD,
+       /* 242   112  "p" */ CWORD_CWORD_CWORD_CWORD,
+       /* 243   113  "q" */ CWORD_CWORD_CWORD_CWORD,
+       /* 244   114  "r" */ CWORD_CWORD_CWORD_CWORD,
+       /* 245   115  "s" */ CWORD_CWORD_CWORD_CWORD,
+       /* 246   116  "t" */ CWORD_CWORD_CWORD_CWORD,
+       /* 247   117  "u" */ CWORD_CWORD_CWORD_CWORD,
+       /* 248   118  "v" */ CWORD_CWORD_CWORD_CWORD,
+       /* 249   119  "w" */ CWORD_CWORD_CWORD_CWORD,
+       /* 250   120  "x" */ CWORD_CWORD_CWORD_CWORD,
+       /* 251   121  "y" */ CWORD_CWORD_CWORD_CWORD,
+       /* 252   122  "z" */ CWORD_CWORD_CWORD_CWORD,
+       /* 253   123  "{" */ CWORD_CWORD_CWORD_CWORD,
+       /* 254   124  "|" */ CSPCL_CWORD_CWORD_CWORD,
+       /* 255   125  "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
+       /* 256   126  "~" */ CWORD_CCTL_CCTL_CWORD,
+       /* 257   127      */ CWORD_CWORD_CWORD_CWORD,
+};
+
+#endif                                                  /* USE_SIT_FUNCTION */
+
+/*      $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $      */
+
+
+#define ATABSIZE 39
+
+static int     funcblocksize;          /* size of structures in function */
+static int     funcstringsize;         /* size of strings in node */
+static pointer funcblock;              /* block to allocate function from */
+static char   *funcstring;             /* block to allocate strings from */
+
+static const short nodesize[26] = {
+      SHELL_ALIGN(sizeof (struct ncmd)),
+      SHELL_ALIGN(sizeof (struct npipe)),
+      SHELL_ALIGN(sizeof (struct nredir)),
+      SHELL_ALIGN(sizeof (struct nredir)),
+      SHELL_ALIGN(sizeof (struct nredir)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nif)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nfor)),
+      SHELL_ALIGN(sizeof (struct ncase)),
+      SHELL_ALIGN(sizeof (struct nclist)),
+      SHELL_ALIGN(sizeof (struct narg)),
+      SHELL_ALIGN(sizeof (struct narg)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct ndup)),
+      SHELL_ALIGN(sizeof (struct ndup)),
+      SHELL_ALIGN(sizeof (struct nhere)),
+      SHELL_ALIGN(sizeof (struct nhere)),
+      SHELL_ALIGN(sizeof (struct nnot)),
+};
+
+
+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 *);
+
+
+
+static void evalstring(char *);
+union node;     /* BLETCH for ansi C */
+static void evaltree(union node *, int);
+static void evalbackcmd(union node *, struct backcmd *);
+
+/* in_function returns nonzero if we are currently evaluating a function */
+#define in_function()   funcnest
+static int evalskip;                   /* set if we are skipping commands */
+static int skipcount;           /* number of levels to skip */
+static int funcnest;                   /* depth of function calls */
+
+/* reasons for skipping commands (see comment on breakcmd routine) */
+#define SKIPBREAK       1
+#define SKIPCONT        2
+#define SKIPFUNC        3
+#define SKIPFILE        4
+
+/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+#ifdef JOBS
+static int bgcmd(int, char **);
+#endif
+static int breakcmd(int, char **);
+static int cdcmd(int, char **);
+#ifdef CONFIG_ASH_CMDCMD
+static int commandcmd(int, char **);
+#endif
+static int dotcmd(int, char **);
+static int evalcmd(int, char **);
+static int execcmd(int, char **);
+static int exitcmd(int, char **);
+static int exportcmd(int, char **);
+static int falsecmd(int, char **);
+#ifdef JOBS
+static int fgcmd(int, char **);
+#endif
+#ifdef CONFIG_ASH_GETOPTS
+static int getoptscmd(int, char **);
+#endif
+static int hashcmd(int, char **);
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+static int helpcmd(int argc, char **argv);
+#endif
+#ifdef JOBS
+static int jobscmd(int, char **);
+#endif
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static int letcmd(int, char **);
+#endif
+static int localcmd(int, char **);
+static int pwdcmd(int, char **);
+static int readcmd(int, char **);
+static int returncmd(int, char **);
+static int setcmd(int, char **);
+static int shiftcmd(int, char **);
+static int timescmd(int, char **);
+static int trapcmd(int, char **);
+static int truecmd(int, char **);
+static int typecmd(int, char **);
+static int umaskcmd(int, char **);
+static int unsetcmd(int, char **);
+static int waitcmd(int, char **);
+static int ulimitcmd(int, char **);
+#ifdef JOBS
+static int killcmd(int, char **);
+#endif
+
+/*      $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */
+
+#ifdef CONFIG_ASH_MAIL
+static void chkmail(void);
+static void changemail(const char *);
+#endif
+
+/*      $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $    */
+
+/* values of cmdtype */
+#define CMDUNKNOWN      -1      /* no entry in table for command */
+#define CMDNORMAL       0       /* command is an executable program */
+#define CMDFUNCTION     1       /* command is a shell function */
+#define CMDBUILTIN      2       /* command is a shell builtin */
+
+struct builtincmd {
+       const char *name;
+       int (*builtin)(int, char **);
+       /* unsigned flags; */
+};
+
+#ifdef CONFIG_ASH_CMDCMD
+# ifdef JOBS
+#  ifdef CONFIG_ASH_ALIAS
+#    define COMMANDCMD (builtincmd + 7)
+#    define EXECCMD (builtincmd + 10)
+#  else
+#    define COMMANDCMD (builtincmd + 6)
+#    define EXECCMD (builtincmd + 9)
+#  endif
+# else /* ! JOBS */
+#  ifdef CONFIG_ASH_ALIAS
+#    define COMMANDCMD (builtincmd + 6)
+#    define EXECCMD (builtincmd + 9)
+#  else
+#    define COMMANDCMD (builtincmd + 5)
+#    define EXECCMD (builtincmd + 8)
+#  endif
+# endif /* JOBS */
+#else   /* ! CONFIG_ASH_CMDCMD */
+# ifdef JOBS
+#  ifdef CONFIG_ASH_ALIAS
+#    define EXECCMD (builtincmd + 9)
+#  else
+#    define EXECCMD (builtincmd + 8)
+#  endif
+# else /* ! JOBS */
+#  ifdef CONFIG_ASH_ALIAS
+#    define EXECCMD (builtincmd + 8)
+#  else
+#    define EXECCMD (builtincmd + 7)
+#  endif
+# endif /* JOBS */
+#endif /* CONFIG_ASH_CMDCMD */
+
+#define BUILTIN_NOSPEC  "0"
+#define BUILTIN_SPECIAL "1"
+#define BUILTIN_REGULAR "2"
+#define BUILTIN_SPEC_REG "3"
+#define BUILTIN_ASSIGN  "4"
+#define BUILTIN_SPEC_ASSG  "5"
+#define BUILTIN_REG_ASSG   "6"
+#define BUILTIN_SPEC_REG_ASSG   "7"
+
+#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
+#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
+
+static const struct builtincmd builtincmd[] = {
+       { BUILTIN_SPEC_REG      ".", dotcmd },
+       { BUILTIN_SPEC_REG      ":", truecmd },
+#ifdef CONFIG_ASH_ALIAS
+       { BUILTIN_REG_ASSG      "alias", aliascmd },
+#endif
+#ifdef JOBS
+       { BUILTIN_REGULAR       "bg", bgcmd },
+#endif
+       { BUILTIN_SPEC_REG      "break", breakcmd },
+       { BUILTIN_REGULAR       "cd", cdcmd },
+       { BUILTIN_NOSPEC        "chdir", cdcmd },
+#ifdef CONFIG_ASH_CMDCMD
+       { BUILTIN_REGULAR       "command", commandcmd },
+#endif
+       { BUILTIN_SPEC_REG      "continue", breakcmd },
+       { BUILTIN_SPEC_REG      "eval", evalcmd },
+       { BUILTIN_SPEC_REG      "exec", execcmd },
+       { BUILTIN_SPEC_REG      "exit", exitcmd },
+       { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
+       { BUILTIN_REGULAR       "false", falsecmd },
+#ifdef JOBS
+       { BUILTIN_REGULAR       "fg", fgcmd },
+#endif
+#ifdef CONFIG_ASH_GETOPTS
+       { BUILTIN_REGULAR       "getopts", getoptscmd },
+#endif
+       { BUILTIN_NOSPEC        "hash", hashcmd },
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+       { BUILTIN_NOSPEC        "help", helpcmd },
+#endif
+#ifdef JOBS
+       { BUILTIN_REGULAR       "jobs", jobscmd },
+       { BUILTIN_REGULAR       "kill", killcmd },
+#endif
+#ifdef CONFIG_ASH_MATH_SUPPORT
+       { BUILTIN_NOSPEC        "let", letcmd },
+#endif
+       { BUILTIN_ASSIGN        "local", localcmd },
+       { BUILTIN_NOSPEC        "pwd", pwdcmd },
+       { BUILTIN_REGULAR       "read", readcmd },
+       { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
+       { BUILTIN_SPEC_REG      "return", returncmd },
+       { BUILTIN_SPEC_REG      "set", setcmd },
+       { BUILTIN_SPEC_REG      "shift", shiftcmd },
+       { BUILTIN_SPEC_REG      "times", timescmd },
+       { BUILTIN_SPEC_REG      "trap", trapcmd },
+       { BUILTIN_REGULAR       "true", truecmd },
+       { BUILTIN_NOSPEC        "type", typecmd },
+       { BUILTIN_NOSPEC        "ulimit", ulimitcmd },
+       { BUILTIN_REGULAR       "umask", umaskcmd },
+#ifdef CONFIG_ASH_ALIAS
+       { BUILTIN_REGULAR       "unalias", unaliascmd },
+#endif
+       { BUILTIN_SPEC_REG      "unset", unsetcmd },
+       { BUILTIN_REGULAR       "wait", waitcmd },
+};
+
+#define NUMBUILTINS  (sizeof (builtincmd) / sizeof (struct builtincmd) )
+
+
+
+struct cmdentry {
+       int cmdtype;
+       union param {
+               int index;
+               const struct builtincmd *cmd;
+               struct funcnode *func;
+       } u;
+};
+
+
+/* action to find_command() */
+#define DO_ERR          0x01    /* prints errors */
+#define DO_ABS          0x02    /* checks absolute paths */
+#define DO_NOFUNC       0x04    /* don't return shell functions, for command */
+#define DO_ALTPATH      0x08    /* using alternate path */
+#define DO_ALTBLTIN     0x20    /* %builtin in alt. path */
+
+static const char *pathopt;     /* set by padvance */
+
+static void shellexec(char **, const char *, int)
+    __attribute__((__noreturn__));
+static char *padvance(const char **, const char *);
+static void find_command(char *, struct cmdentry *, int, const char *);
+static struct builtincmd *find_builtin(const char *);
+static void hashcd(void);
+static void changepath(const char *);
+static void defun(char *, union node *);
+static void unsetfunc(const char *);
+
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#else
+typedef long arith_t;
+#endif
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+static unsigned long rseed;
+static void change_random(const char *);
+# ifndef DYNAMIC_VAR
+#  define DYNAMIC_VAR
+# endif
+#endif
+
+/*      $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */
+
+static void reset(void);
+
+/*      $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $     */
+
+/*
+ * Shell variables.
+ */
+
+/* flags */
+#define VEXPORT         0x01    /* variable is exported */
+#define VREADONLY       0x02    /* variable cannot be modified */
+#define VSTRFIXED       0x04    /* variable struct is statically allocated */
+#define VTEXTFIXED      0x08    /* text is statically allocated */
+#define VSTACK          0x10    /* text is allocated on the stack */
+#define VUNSET          0x20    /* the variable is not set */
+#define VNOFUNC         0x40    /* don't call the callback function */
+#define VNOSET          0x80    /* do not set variable - just readonly test */
+#define VNOSAVE         0x100   /* when text is on the heap before setvareq */
+#ifdef DYNAMIC_VAR
+# define VDYNAMIC        0x200   /* dynamic variable */
+# else
+# define VDYNAMIC        0
+#endif
+
+struct var {
+       struct var *next;               /* next entry in hash list */
+       int flags;                      /* flags are defined above */
+       const char *text;               /* name=value */
+       void (*func)(const char *);     /* function to be called when  */
+                                       /* the variable gets set/unset */
+};
+
+struct localvar {
+       struct localvar *next;          /* next local variable in list */
+       struct var *vp;                 /* the variable that was made local */
+       int flags;                      /* saved flags */
+       const char *text;               /* saved text */
+};
+
+
+static struct localvar *localvars;
+
+/*
+ * Shell variables.
+ */
+
+#ifdef CONFIG_ASH_GETOPTS
+static void getoptsreset(const char *);
+#endif
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#include <locale.h>
+static void change_lc_all(const char *value);
+static void change_lc_ctype(const char *value);
+#endif
+
+
+#define VTABSIZE 39
+
+static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
+#ifdef IFS_BROKEN
+static const char defifsvar[] = "IFS= \t\n";
+#define defifs (defifsvar + 4)
+#else
+static const char defifs[] = " \t\n";
+#endif
+
+
+static struct var varinit[] = {
+#ifdef IFS_BROKEN
+       { 0,    VSTRFIXED|VTEXTFIXED,           defifsvar,      0 },
+#else
+       { 0,    VSTRFIXED|VTEXTFIXED|VUNSET,    "IFS\0",        0 },
+#endif
+
+#ifdef CONFIG_ASH_MAIL
+       { 0,    VSTRFIXED|VTEXTFIXED|VUNSET,    "MAIL\0",       changemail },
+       { 0,    VSTRFIXED|VTEXTFIXED|VUNSET,    "MAILPATH\0",   changemail },
+#endif
+
+       { 0,    VSTRFIXED|VTEXTFIXED,           defpathvar,     changepath },
+       { 0,    VSTRFIXED|VTEXTFIXED,           "PS1=$ ",       0          },
+       { 0,    VSTRFIXED|VTEXTFIXED,           "PS2=> ",       0          },
+       { 0,    VSTRFIXED|VTEXTFIXED,           "PS4=+ ",       0          },
+#ifdef CONFIG_ASH_GETOPTS
+       { 0,    VSTRFIXED|VTEXTFIXED,           "OPTIND=1",     getoptsreset },
+#endif
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+       {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+#endif
+#ifdef CONFIG_LOCALE_SUPPORT
+       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
+#endif
+#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
+#endif
+};
+
+#define vifs varinit[0]
+#ifdef CONFIG_ASH_MAIL
+#define vmail (&vifs)[1]
+#define vmpath (&vmail)[1]
+#else
+#define vmpath vifs
+#endif
+#define vpath (&vmpath)[1]
+#define vps1 (&vpath)[1]
+#define vps2 (&vps1)[1]
+#define vps4 (&vps2)[1]
+#define voptind (&vps4)[1]
+#ifdef CONFIG_ASH_GETOPTS
+#define vrandom (&voptind)[1]
+#else
+#define vrandom (&vps4)[1]
+#endif
+#define defpath (defpathvar + 5)
+
+/*
+ * 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 ifsset()        ((vifs.flags & VUNSET) == 0)
+#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)
+#define ps4val()        (vps4.text + 4)
+#define optindval()     (voptind.text + 7)
+
+#define mpathset()      ((vmpath.flags & VUNSET) == 0)
+
+static void setvar(const char *, const char *, int);
+static void setvareq(char *, int);
+static void listsetvar(struct strlist *, int);
+static char *lookupvar(const char *);
+static char *bltinlookup(const char *);
+static char **listvars(int, int, char ***);
+#define environment() listvars(VEXPORT, VUNSET, 0)
+static int showvars(const char *, int, int);
+static void poplocalvars(void);
+static int unsetvar(const char *);
+#ifdef CONFIG_ASH_GETOPTS
+static int setvarsafe(const char *, const char *, int);
+#endif
+static int varcmp(const char *, const char *);
+static struct var **hashvar(const char *);
+
+
+static inline int varequal(const char *a, const char *b) {
+       return !varcmp(a, b);
+}
+
+
+static int loopnest;            /* current loop nesting level */
+
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+
+
+struct redirtab {
+       struct redirtab *next;
+       int renamed[10];
+       int nullredirs;
+};
+
+static struct redirtab *redirlist;
+static int nullredirs;
+
+extern char **environ;
+
+/*      $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $     */
+
+
+static void outstr(const char *, FILE *);
+static void outcslow(int, FILE *);
+static void flushall(void);
+static void flusherr(void);
+static int  out1fmt(const char *, ...)
+    __attribute__((__format__(__printf__,1,2)));
+static int fmtstr(char *, size_t, const char *, ...)
+    __attribute__((__format__(__printf__,3,4)));
+
+static int preverrout_fd;   /* save fd2 before print debug if xflag is set. */
+
+
+static void out1str(const char *p)
+{
+       outstr(p, stdout);
+}
+
+static void out2str(const char *p)
+{
+       outstr(p, stderr);
+       flusherr();
+}
+
+/*
+ * Initialization code.
+ */
+
+/*
+ * This routine initializes the builtin variables.
+ */
+
+static inline void
+initvar(void)
+{
+       struct var *vp;
+       struct var *end;
+       struct var **vpp;
+
+       /*
+        * PS1 depends on uid
+        */
+#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+       vps1.text = "PS1=\\w \\$ ";
+#else
+       if (!geteuid())
+               vps1.text = "PS1=# ";
+#endif
+       vp = varinit;
+       end = vp + sizeof(varinit) / sizeof(varinit[0]);
+       do {
+               vpp = hashvar(vp->text);
+               vp->next = *vpp;
+               *vpp = vp;
+       } while (++vp < end);
+}
+
+static inline void
+init(void)
+{
+
+      /* from input.c: */
+      {
+             basepf.nextc = basepf.buf = basebuf;
+      }
+
+      /* from trap.c: */
+      {
+             signal(SIGCHLD, SIG_DFL);
+      }
+
+      /* from var.c: */
+      {
+             char **envp;
+             char ppid[32];
+
+             initvar();
+             for (envp = environ ; *envp ; envp++) {
+                     if (strchr(*envp, '=')) {
+                             setvareq(*envp, VEXPORT|VTEXTFIXED);
+                     }
+             }
+
+             snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
+             setvar("PPID", ppid, 0);
+             setpwd(0, 0);
+      }
+}
+
+/* PEOF (the end of file marker) */
+
+/*
+ * The input line number.  Input.c just defines this variable, and saves
+ * and restores it when files are pushed and popped.  The user of this
+ * package must set its value.
+ */
+
+static int pgetc(void);
+static int pgetc2(void);
+static int preadbuffer(void);
+static void pungetc(void);
+static void pushstring(char *, void *);
+static void popstring(void);
+static void setinputfile(const char *, int);
+static void setinputfd(int, int);
+static void setinputstring(char *);
+static void popfile(void);
+static void popallfiles(void);
+static void closescript(void);
+
+
+/*      $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl 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
+
+/* mode flags for showjob(s) */
+#define SHOW_PGID       0x01    /* only show pgid - for jobs -p */
+#define SHOW_PID        0x04    /* include process pid */
+#define SHOW_CHANGED    0x08    /* only jobs whose state has changed */
+
+
+/*
+ * A job structure contains information about a job.  A job is either a
+ * single process or a set of processes contained in a pipeline.  In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+
+struct procstat {
+       pid_t   pid;            /* process id */
+       int     status;         /* last process status from wait() */
+       char    *cmd;           /* text of command being run */
+};
+
+struct job {
+       struct procstat ps0;    /* status of process */
+       struct procstat *ps;    /* status or processes when more than one */
+#if JOBS
+       int stopstatus;         /* status of a stopped job */
+#endif
+       uint32_t
+               nprocs: 16,     /* number of processes */
+               state: 8,
+#define JOBRUNNING      0       /* at least one proc running */
+#define JOBSTOPPED      1       /* all procs are stopped */
+#define JOBDONE         2       /* all procs are completed */
+#if JOBS
+               sigint: 1,      /* job was killed by SIGINT */
+               jobctl: 1,      /* job running under job control */
+#endif
+               waited: 1,      /* true if this entry has been waited for */
+               used: 1,        /* true if this entry is in used */
+               changed: 1;     /* true if status has changed */
+       struct job *prev_job;   /* previous job */
+};
+
+static pid_t backgndpid;        /* pid of last background process */
+static int job_warning;         /* user was warned about stopped jobs */
+#if JOBS
+static int jobctl;              /* true if doing job control */
+#endif
+
+static struct job *makejob(union node *, int);
+static int forkshell(struct job *, union node *, int);
+static int waitforjob(struct job *);
+static int stoppedjobs(void);
+
+#if ! JOBS
+#define setjobctl(on)   /* do nothing */
+#else
+static void setjobctl(int);
+static void showjobs(FILE *, int);
+#endif
+
+/*      $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $        */
+
+
+/* pid of main shell */
+static int rootpid;
+/* true if we aren't a child of the main shell */
+static int rootshell;
+
+static void readcmdfile(char *);
+static void cmdloop(int);
+
+/*      $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $        */
+
+
+struct stackmark {
+       struct stack_block *stackp;
+       char *stacknxt;
+       size_t stacknleft;
+       struct stackmark *marknext;
+};
+
+/* minimum size of a block */
+#define MINSIZE SHELL_ALIGN(504)
+
+struct stack_block {
+       struct stack_block *prev;
+       char space[MINSIZE];
+};
+
+static struct stack_block stackbase;
+static struct stack_block *stackp = &stackbase;
+static struct stackmark *markp;
+static char *stacknxt = stackbase.space;
+static size_t stacknleft = MINSIZE;
+static char *sstrend = stackbase.space + MINSIZE;
+static int herefd = -1;
+
+
+static pointer ckmalloc(size_t);
+static pointer ckrealloc(pointer, size_t);
+static char *savestr(const char *);
+static pointer stalloc(size_t);
+static void stunalloc(pointer);
+static void setstackmark(struct stackmark *);
+static void popstackmark(struct stackmark *);
+static void growstackblock(void);
+static void *growstackstr(void);
+static char *makestrspace(size_t, char *);
+static char *stnputs(const char *, size_t, char *);
+static char *stputs(const char *, char *);
+
+
+static inline char *_STPUTC(char c, char *p) {
+       if (p == sstrend)
+               p = growstackstr();
+       *p++ = c;
+       return p;
+}
+
+#define stackblock() ((void *)stacknxt)
+#define stackblocksize() stacknleft
+#define STARTSTACKSTR(p) ((p) = stackblock())
+#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
+#define CHECKSTRSPACE(n, p) \
+       ({ \
+               char *q = (p); \
+               size_t l = (n); \
+               size_t m = sstrend - q; \
+               if (l > m) \
+                       (p) = makestrspace(l, q); \
+               0; \
+       })
+#define USTPUTC(c, p)   (*p++ = (c))
+#define STACKSTRNUL(p)  ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STUNPUTC(p)     (--p)
+#define STTOPC(p)       p[-1]
+#define STADJUST(amount, p)     (p += (amount))
+
+#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
+#define ungrabstackstr(s, p) stunalloc((s))
+#define stackstrend() ((void *)sstrend)
+
+#define ckfree(p)       free((pointer)(p))
+
+/*      $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $   */
+
+
+#define DOLATSTRLEN 4
+
+static char *prefix(const char *, const char *);
+static int number(const char *);
+static int is_number(const char *);
+static char *single_quote(const char *);
+static char *sstrdup(const char *);
+
+#define equal(s1, s2)   (strcmp(s1, s2) == 0)
+#define scopy(s1, s2)   ((void)strcpy(s2, s1))
+
+/*      $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
+
+struct shparam {
+       int nparam;             /* # of positional parameters (without $0) */
+       unsigned char malloc;   /* if parameter list dynamically allocated */
+       char **p;               /* parameter list */
+#ifdef CONFIG_ASH_GETOPTS
+       int optind;             /* next parameter to be processed by getopts */
+       int optoff;             /* used by getopts */
+#endif
+};
+
+
+#define eflag optlist[0]
+#define fflag optlist[1]
+#define Iflag optlist[2]
+#define iflag optlist[3]
+#define mflag optlist[4]
+#define nflag optlist[5]
+#define sflag optlist[6]
+#define xflag optlist[7]
+#define vflag optlist[8]
+#define Cflag optlist[9]
+#define aflag optlist[10]
+#define bflag optlist[11]
+#define uflag optlist[12]
+#define qflag optlist[13]
+
+#ifdef DEBUG
+#define nolog optlist[14]
+#define debug optlist[15]
+#define NOPTS   16
+#else
+#define NOPTS   14
+#endif
+
+/*      $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
+
+
+static const char *const optletters_optnames[NOPTS] = {
+       "e"   "errexit",
+       "f"   "noglob",
+       "I"   "ignoreeof",
+       "i"   "interactive",
+       "m"   "monitor",
+       "n"   "noexec",
+       "s"   "stdin",
+       "x"   "xtrace",
+       "v"   "verbose",
+       "C"   "noclobber",
+       "a"   "allexport",
+       "b"   "notify",
+       "u"   "nounset",
+       "q"   "quietprofile",
+#ifdef DEBUG
+       "\0"  "nolog",
+       "\0"  "debug",
+#endif
+};
+
+#define optletters(n) optletters_optnames[(n)][0]
+#define optnames(n) (&optletters_optnames[(n)][1])
+
+
+static char optlist[NOPTS];
+
+
+static char *arg0;                     /* value of $0 */
+static struct shparam shellparam;      /* $@ current positional parameters */
+static char **argptr;                  /* argument list for builtin commands */
+static char *optionarg;                /* set by nextopt (like getopt) */
+static char *optptr;                   /* used by nextopt */
+
+static char *minusc;                   /* argument to -c option */
+
+
+static void procargs(int, char **);
+static void optschanged(void);
+static void setparam(char **);
+static void freeparam(volatile struct shparam *);
+static int shiftcmd(int, char **);
+static int setcmd(int, char **);
+static int nextopt(const char *);
+
+/*      $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $      */
+
+/* flags passed to redirect */
+#define REDIR_PUSH 01           /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03       /* set preverrout */
+
+union node;
+static void redirect(union node *, int);
+static void popredir(int);
+static void clearredir(int);
+static int copyfd(int, int);
+static int redirectsafe(union node *, int);
+
+/*      $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $     */
+
+
+#ifdef DEBUG
+static void showtree(union node *);
+static void trace(const char *, ...);
+static void tracev(const char *, va_list);
+static void trargs(char **);
+static void trputc(int);
+static void trputs(const char *);
+static void opentrace(void);
+#endif
+
+/*      $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $       */
+
+
+/* trap handler commands */
+static char *trap[NSIG];
+/* current value of signal */
+static char sigmode[NSIG - 1];
+/* indicates specified signal received */
+static char gotsig[NSIG - 1];
+
+static void clear_traps(void);
+static void setsignal(int);
+static void ignoresig(int);
+static void onsig(int);
+static void dotrap(void);
+static void setinteractive(int);
+static void exitshell(void) __attribute__((__noreturn__));
+static int decode_signal(const char *, int);
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ */
+
+static void
+reset(void)
+{
+      /* from eval.c: */
+      {
+             evalskip = 0;
+             loopnest = 0;
+             funcnest = 0;
+      }
+
+      /* from input.c: */
+      {
+             parselleft = parsenleft = 0;      /* clear input buffer */
+             popallfiles();
+      }
+
+      /* from parser.c: */
+      {
+             tokpushback = 0;
+             checkkwd = 0;
+      }
+
+      /* from redir.c: */
+      {
+             clearredir(0);
+      }
+
+}
+
+#ifdef CONFIG_ASH_ALIAS
+static struct alias *atab[ATABSIZE];
+
+static void setalias(const char *, const char *);
+static struct alias *freealias(struct alias *);
+static struct alias **__lookupalias(const char *);
+
+static void
+setalias(const char *name, const char *val)
+{
+       struct alias *ap, **app;
+
+       app = __lookupalias(name);
+       ap = *app;
+       INTOFF;
+       if (ap) {
+               if (!(ap->flag & ALIASINUSE)) {
+                       ckfree(ap->val);
+               }
+               ap->val = savestr(val);
+               ap->flag &= ~ALIASDEAD;
+       } else {
+               /* not found */
+               ap = ckmalloc(sizeof (struct alias));
+               ap->name = savestr(name);
+               ap->val = savestr(val);
+               ap->flag = 0;
+               ap->next = 0;
+               *app = ap;
+       }
+       INTON;
+}
+
+static int
+unalias(const char *name)
+{
+       struct alias **app;
+
+       app = __lookupalias(name);
+
+       if (*app) {
+               INTOFF;
+               *app = freealias(*app);
+               INTON;
+               return (0);
+       }
+
+       return (1);
+}
+
+static void
+rmaliases(void)
+{
+       struct alias *ap, **app;
+       int i;
+
+       INTOFF;
+       for (i = 0; i < ATABSIZE; i++) {
+               app = &atab[i];
+               for (ap = *app; ap; ap = *app) {
+                       *app = freealias(*app);
+                       if (ap == *app) {
+                               app = &ap->next;
+                       }
+               }
+       }
+       INTON;
+}
+
+static struct alias *
+lookupalias(const char *name, int check)
+{
+       struct alias *ap = *__lookupalias(name);
+
+       if (check && ap && (ap->flag & ALIASINUSE))
+               return (NULL);
+       return (ap);
+}
+
+/*
+ * TODO - sort output
+ */
+static 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) {
+                               printalias(ap);
+                       }
+               return (0);
+       }
+       while ((n = *++argv) != NULL) {
+               if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
+                       if ((ap = *__lookupalias(n)) == NULL) {
+                               fprintf(stderr, "%s: %s not found\n", "alias", n);
+                               ret = 1;
+                       } else
+                               printalias(ap);
+               } else {
+                       *v++ = '\0';
+                       setalias(n, v);
+               }
+       }
+
+       return (ret);
+}
+
+static int
+unaliascmd(int argc, char **argv)
+{
+       int i;
+
+       while ((i = nextopt("a")) != '\0') {
+               if (i == 'a') {
+                       rmaliases();
+                       return (0);
+               }
+       }
+       for (i = 0; *argptr; argptr++) {
+               if (unalias(*argptr)) {
+                       fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
+                       i = 1;
+               }
+       }
+
+       return (i);
+}
+
+static struct alias *
+freealias(struct alias *ap) {
+       struct alias *next;
+
+       if (ap->flag & ALIASINUSE) {
+               ap->flag |= ALIASDEAD;
+               return ap;
+       }
+
+       next = ap->next;
+       ckfree(ap->name);
+       ckfree(ap->val);
+       ckfree(ap);
+       return next;
+}
+
+static void
+printalias(const struct alias *ap) {
+       out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
+}
+
+static struct alias **
+__lookupalias(const char *name) {
+       unsigned int hashval;
+       struct alias **app;
+       const char *p;
+       unsigned int ch;
+
+       p = name;
+
+       ch = (unsigned char)*p;
+       hashval = ch << 4;
+       while (ch) {
+               hashval += ch;
+               ch = (unsigned char)*++p;
+       }
+       app = &atab[hashval % ATABSIZE];
+
+       for (; *app; app = &(*app)->next) {
+               if (equal(name, (*app)->name)) {
+                       break;
+               }
+       }
+
+       return app;
+}
+#endif /* CONFIG_ASH_ALIAS */
+
+
+/*      $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $      */
+
+/*
+ * The cd and pwd commands.
+ */
+
+#define CD_PHYSICAL 1
+#define CD_PRINT 2
+
+static int docd(const char *, int);
+static int cdopt(void);
+
+static char *curdir = nullstr;          /* current working directory */
+static char *physdir = nullstr;         /* physical working directory */
+
+static int
+cdopt(void)
+{
+       int flags = 0;
+       int i, j;
+
+       j = 'L';
+       while ((i = nextopt("LP"))) {
+               if (i != j) {
+                       flags ^= CD_PHYSICAL;
+                       j = i;
+               }
+       }
+
+       return flags;
+}
+
+static int
+cdcmd(int argc, char **argv)
+{
+       const char *dest;
+       const char *path;
+       const char *p;
+       char c;
+       struct stat statb;
+       int flags;
+
+       flags = cdopt();
+       dest = *argptr;
+       if (!dest)
+               dest = bltinlookup(homestr);
+       else if (dest[0] == '-' && dest[1] == '\0') {
+               dest = bltinlookup("OLDPWD");
+               if ( !dest ) goto out;
+               flags |= CD_PRINT;
+               goto step7;
+       }
+       if (!dest)
+               dest = nullstr;
+       if (*dest == '/')
+               goto step7;
+       if (*dest == '.') {
+               c = dest[1];
+dotdot:
+               switch (c) {
+               case '\0':
+               case '/':
+                       goto step6;
+               case '.':
+                       c = dest[2];
+                       if (c != '.')
+                               goto dotdot;
+               }
+       }
+       if (!*dest)
+               dest = ".";
+       if (!(path = bltinlookup("CDPATH"))) {
+step6:
+step7:
+               p = dest;
+               goto docd;
+       }
+       do {
+               c = *path;
+               p = padvance(&path, dest);
+               if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+                       if (c && c != ':')
+                               flags |= CD_PRINT;
+docd:
+                       if (!docd(p, flags))
+                               goto out;
+                       break;
+               }
+       } while (path);
+       error("can't cd to %s", dest);
+       /* NOTREACHED */
+out:
+       if (flags & CD_PRINT)
+               out1fmt(snlfmt, curdir);
+       return 0;
+}
+
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.
+ */
+
+static inline const char *
+updatepwd(const char *dir)
+{
+       char *new;
+       char *p;
+       char *cdcomppath;
+       const char *lim;
+
+       cdcomppath = sstrdup(dir);
+       STARTSTACKSTR(new);
+       if (*dir != '/') {
+               if (curdir == nullstr)
+                       return 0;
+               new = stputs(curdir, new);
+       }
+       new = makestrspace(strlen(dir) + 2, new);
+       lim = stackblock() + 1;
+       if (*dir != '/') {
+               if (new[-1] != '/')
+                       USTPUTC('/', new);
+               if (new > lim && *lim == '/')
+                       lim++;
+       } else {
+               USTPUTC('/', new);
+               cdcomppath++;
+               if (dir[1] == '/' && dir[2] != '/') {
+                       USTPUTC('/', new);
+                       cdcomppath++;
+                       lim++;
+               }
+       }
+       p = strtok(cdcomppath, "/");
+       while (p) {
+               switch(*p) {
+               case '.':
+                       if (p[1] == '.' && p[2] == '\0') {
+                               while (new > lim) {
+                                       STUNPUTC(new);
+                                       if (new[-1] == '/')
+                                               break;
+                               }
+                               break;
+                       } else if (p[1] == '\0')
+                               break;
+                       /* fall through */
+               default:
+                       new = stputs(p, new);
+                       USTPUTC('/', new);
+               }
+               p = strtok(0, "/");
+       }
+       if (new > lim)
+               STUNPUTC(new);
+       *new = 0;
+       return stackblock();
+}
+
+/*
+ * Actually do the chdir.  We also call hashcd to let the routines in exec.c
+ * know that the current directory has changed.
+ */
+
+static int
+docd(const char *dest, int flags)
+{
+       const char *dir = 0;
+       int err;
+
+       TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+       INTOFF;
+       if (!(flags & CD_PHYSICAL)) {
+               dir = updatepwd(dest);
+               if (dir)
+                       dest = dir;
+       }
+       err = chdir(dest);
+       if (err)
+               goto out;
+       setpwd(dir, 1);
+       hashcd();
+out:
+       INTON;
+       return err;
+}
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+static inline char *
+getpwd(void)
+{
+       char *dir = getcwd(0, 0);
+       return dir ? dir : nullstr;
+}
+
+static int
+pwdcmd(int argc, char **argv)
+{
+       int flags;
+       const char *dir = curdir;
+
+       flags = cdopt();
+       if (flags) {
+               if (physdir == nullstr)
+                       setpwd(dir, 0);
+               dir = physdir;
+       }
+       out1fmt(snlfmt, dir);
+       return 0;
+}
+
+static void
+setpwd(const char *val, int setold)
+{
+       char *oldcur, *dir;
+
+       oldcur = dir = curdir;
+
+       if (setold) {
+               setvar("OLDPWD", oldcur, VEXPORT);
+       }
+       INTOFF;
+       if (physdir != nullstr) {
+               if (physdir != oldcur)
+                       free(physdir);
+               physdir = nullstr;
+       }
+       if (oldcur == val || !val) {
+               char *s = getpwd();
+               physdir = s;
+               if (!val)
+                       dir = s;
+       } else
+               dir = savestr(val);
+       if (oldcur != dir && oldcur != nullstr) {
+               free(oldcur);
+       }
+       curdir = dir;
+       INTON;
+       setvar("PWD", dir, VEXPORT);
+}
+
+/*      $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $   */
+
+/*
+ * Errors and exceptions.
+ */
+
+/*
+ * Code to handle exceptions in C.
+ */
+
+
+
+static void exverror(int, const char *, va_list)
+    __attribute__((__noreturn__));
+
+/*
+ * 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".
+ */
+
+static void
+exraise(int e)
+{
+#ifdef DEBUG
+       if (handler == NULL)
+               abort();
+#endif
+       INTOFF;
+
+       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 test for iflag is just
+ * defensive programming.)
+ */
+
+static void
+onint(void) {
+       int i;
+
+       intpending = 0;
+       sigsetmask(0);
+       i = EXSIG;
+       if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
+               if (!(rootshell && iflag)) {
+                       signal(SIGINT, SIG_DFL);
+                       raise(SIGINT);
+               }
+               i = EXINT;
+       }
+       exraise(i);
+       /* NOTREACHED */
+}
+
+static void
+exvwarning(const char *msg, va_list ap)
+{
+       FILE *errs;
+       const char *name;
+       const char *fmt;
+
+       errs = stderr;
+       name = arg0;
+       fmt = "%s: ";
+       if (commandname) {
+               name = commandname;
+               fmt = "%s: %d: ";
+       }
+       fprintf(errs, fmt, name, startlinno);
+       vfprintf(errs, msg, ap);
+       outcslow('\n', errs);
+}
+
+/*
+ * Exverror is called to raise the error exception.  If the second argument
+ * is not NULL then error prints an error message using printf style
+ * formatting.  It then raises the error exception.
+ */
+static void
+exverror(int cond, const char *msg, va_list ap)
+{
+#ifdef DEBUG
+       if (msg) {
+               TRACE(("exverror(%d, \"", cond));
+               TRACEV((msg, ap));
+               TRACE(("\") pid=%d\n", getpid()));
+       } else
+               TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
+       if (msg)
+#endif
+               exvwarning(msg, ap);
+
+       flushall();
+       exraise(cond);
+       /* NOTREACHED */
+}
+
+
+static void
+error(const char *msg, ...)
+{
+       va_list ap;
+
+       va_start(ap, msg);
+       exverror(EXERROR, msg, ap);
+       /* NOTREACHED */
+       va_end(ap);
+}
+
+
+static void
+exerror(int cond, const char *msg, ...)
+{
+       va_list ap;
+
+       va_start(ap, msg);
+       exverror(cond, msg, ap);
+       /* NOTREACHED */
+       va_end(ap);
+}
+
+/*
+ * error/warning routines for external builtins
+ */
+
+static void
+sh_warnx(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       exvwarning(fmt, ap);
+       va_end(ap);
+}
+
+
+/*
+ * Return a string describing an error.  The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+
+static const char *
+errmsg(int e, const char *em)
+{
+       if(e == ENOENT || e == ENOTDIR) {
+
+               return em;
+       }
+       return strerror(e);
+}
+
+
+/*      $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $  */
+
+/*
+ * Evaluate a command.
+ */
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01              /* exit after evaluating tree */
+#define EV_TESTED 02            /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04           /* command executing within back quotes */
+
+
+static void evalloop(union node *, int);
+static void evalfor(union node *, int);
+static void evalcase(union node *, int);
+static void evalsubshell(union node *, int);
+static void expredir(union node *);
+static void evalpipe(union node *, int);
+static void evalcommand(union node *, int);
+static int evalbltin(const struct builtincmd *, int, char **);
+static int evalfun(struct funcnode *, int, char **, int);
+static void prehash(union node *);
+static int bltincmd(int, char **);
+
+
+static const struct builtincmd bltin = {
+       "\0\0", bltincmd
+};
+
+
+/*
+ * Called to reset things after an exception.
+ */
+
+/*
+ * The eval command.
+ */
+
+static 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 (;;) {
+                               concat = stputs(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.
+ */
+
+static void
+evalstring(char *s)
+{
+       union node *n;
+       struct stackmark smark;
+
+       setstackmark(&smark);
+       setinputstring(s);
+
+       while ((n = parsecmd(0)) != NEOF) {
+               evaltree(n, 0);
+               popstackmark(&smark);
+               if (evalskip)
+                       break;
+       }
+       popfile();
+       popstackmark(&smark);
+}
+
+
+
+/*
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
+ */
+
+static void
+evaltree(union node *n, int flags)
+{
+       int checkexit = 0;
+       void (*evalfn)(union node *, int);
+       unsigned isor;
+       int status;
+       if (n == NULL) {
+               TRACE(("evaltree(NULL) called\n"));
+               goto out;
+       }
+       TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
+           getpid(), n, n->type, flags));
+       switch (n->type) {
+       default:
+#ifdef DEBUG
+               out1fmt("Node type = %d\n", n->type);
+               fflush(stdout);
+               break;
+#endif
+       case NNOT:
+               evaltree(n->nnot.com, EV_TESTED);
+               status = !exitstatus;
+               goto setstatus;
+       case NREDIR:
+               expredir(n->nredir.redirect);
+               status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
+               if (!status) {
+                       evaltree(n->nredir.n, flags & EV_TESTED);
+                       status = exitstatus;
+               }
+               popredir(0);
+               goto setstatus;
+       case NCMD:
+               evalfn = evalcommand;
+checkexit:
+               if (eflag && !(flags & EV_TESTED))
+                       checkexit = ~0;
+               goto calleval;
+       case NFOR:
+               evalfn = evalfor;
+               goto calleval;
+       case NWHILE:
+       case NUNTIL:
+               evalfn = evalloop;
+               goto calleval;
+       case NSUBSHELL:
+       case NBACKGND:
+               evalfn = evalsubshell;
+               goto calleval;
+       case NPIPE:
+               evalfn = evalpipe;
+               goto checkexit;
+       case NCASE:
+               evalfn = evalcase;
+               goto calleval;
+       case NAND:
+       case NOR:
+       case NSEMI:
+#if NAND + 1 != NOR
+#error NAND + 1 != NOR
+#endif
+#if NOR + 1 != NSEMI
+#error NOR + 1 != NSEMI
+#endif
+               isor = n->type - NAND;
+               evaltree(
+                       n->nbinary.ch1,
+                       (flags | ((isor >> 1) - 1)) & EV_TESTED
+               );
+               if (!exitstatus == isor)
+                       break;
+               if (!evalskip) {
+                       n = n->nbinary.ch2;
+evaln:
+                       evalfn = evaltree;
+calleval:
+                       evalfn(n, flags);
+                       break;
+               }
+               break;
+       case NIF:
+               evaltree(n->nif.test, EV_TESTED);
+               if (evalskip)
+                       break;
+               if (exitstatus == 0) {
+                       n = n->nif.ifpart;
+                       goto evaln;
+               } else if (n->nif.elsepart) {
+                       n = n->nif.elsepart;
+                       goto evaln;
+               }
+               goto success;
+       case NDEFUN:
+               defun(n->narg.text, n->narg.next);
+success:
+               status = 0;
+setstatus:
+               exitstatus = status;
+               break;
+       }
+out:
+       if (pendingsigs)
+               dotrap();
+       if (flags & EV_EXIT || checkexit & exitstatus)
+               exraise(EXEXIT);
+}
+
+
+#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
+static
+#endif
+void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
+
+
+static void
+evalloop(union node *n, int flags)
+{
+       int status;
+
+       loopnest++;
+       status = 0;
+       flags &= EV_TESTED;
+       for (;;) {
+               int i;
+
+               evaltree(n->nbinary.ch1, EV_TESTED);
+               if (evalskip) {
+skipping:         if (evalskip == SKIPCONT && --skipcount <= 0) {
+                               evalskip = 0;
+                               continue;
+                       }
+                       if (evalskip == SKIPBREAK && --skipcount <= 0)
+                               evalskip = 0;
+                       break;
+               }
+               i = exitstatus;
+               if (n->type != NWHILE)
+                       i = !i;
+               if (i != 0)
+                       break;
+               evaltree(n->nbinary.ch2, flags);
+               status = exitstatus;
+               if (evalskip)
+                       goto skipping;
+       }
+       loopnest--;
+       exitstatus = status;
+}
+
+
+
+static void
+evalfor(union node *n, int flags)
+{
+       struct arglist arglist;
+       union node *argp;
+       struct strlist *sp;
+       struct stackmark smark;
+
+       setstackmark(&smark);
+       arglist.lastp = &arglist.list;
+       for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
+               expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
+               /* XXX */
+               if (evalskip)
+                       goto out;
+       }
+       *arglist.lastp = NULL;
+
+       exitstatus = 0;
+       loopnest++;
+       flags &= EV_TESTED;
+       for (sp = arglist.list ; sp ; sp = sp->next) {
+               setvar(n->nfor.var, sp->text, 0);
+               evaltree(n->nfor.body, flags);
+               if (evalskip) {
+                       if (evalskip == SKIPCONT && --skipcount <= 0) {
+                               evalskip = 0;
+                               continue;
+                       }
+                       if (evalskip == SKIPBREAK && --skipcount <= 0)
+                               evalskip = 0;
+                       break;
+               }
+       }
+       loopnest--;
+out:
+       popstackmark(&smark);
+}
+
+
+
+static void
+evalcase(union node *n, int flags)
+{
+       union node *cp;
+       union node *patp;
+       struct arglist arglist;
+       struct stackmark smark;
+
+       setstackmark(&smark);
+       arglist.lastp = &arglist.list;
+       expandarg(n->ncase.expr, &arglist, EXP_TILDE);
+       exitstatus = 0;
+       for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
+               for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
+                       if (casematch(patp, arglist.list->text)) {
+                               if (evalskip == 0) {
+                                       evaltree(cp->nclist.body, flags);
+                               }
+                               goto out;
+                       }
+               }
+       }
+out:
+       popstackmark(&smark);
+}
+
+
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+
+static void
+evalsubshell(union node *n, int flags)
+{
+       struct job *jp;
+       int backgnd = (n->type == NBACKGND);
+       int status;
+
+       expredir(n->nredir.redirect);
+       if (!backgnd && flags & EV_EXIT && !trap[0])
+               goto nofork;
+       INTOFF;
+       jp = makejob(n, 1);
+       if (forkshell(jp, n, backgnd) == 0) {
+               INTON;
+               flags |= EV_EXIT;
+               if (backgnd)
+                       flags &=~ EV_TESTED;
+nofork:
+               redirect(n->nredir.redirect, 0);
+               evaltreenr(n->nredir.n, flags);
+               /* never returns */
+       }
+       status = 0;
+       if (! backgnd)
+               status = waitforjob(jp);
+       exitstatus = status;
+       INTON;
+}
+
+
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+
+static void
+expredir(union node *n)
+{
+       union node *redir;
+
+       for (redir = n ; redir ; redir = redir->nfile.next) {
+               struct arglist fn;
+               fn.lastp = &fn.list;
+               switch (redir->type) {
+               case NFROMTO:
+               case NFROM:
+               case NTO:
+               case NCLOBBER:
+               case NAPPEND:
+                       expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+                       redir->nfile.expfname = fn.list->text;
+                       break;
+               case NFROMFD:
+               case NTOFD:
+                       if (redir->ndup.vname) {
+                               expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+                               fixredir(redir, fn.list->text, 1);
+                       }
+                       break;
+               }
+       }
+}
+
+
+
+/*
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+
+static void
+evalpipe(union node *n, int flags)
+{
+       struct job *jp;
+       struct nodelist *lp;
+       int pipelen;
+       int prevfd;
+       int pip[2];
+
+       TRACE(("evalpipe(0x%lx) called\n", (long)n));
+       pipelen = 0;
+       for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
+               pipelen++;
+       flags |= EV_EXIT;
+       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);
+                               error("Pipe call failed");
+                       }
+               }
+               if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+                       INTON;
+                       if (pip[1] >= 0) {
+                               close(pip[0]);
+                       }
+                       if (prevfd > 0) {
+                               dup2(prevfd, 0);
+                               close(prevfd);
+                       }
+                       if (pip[1] > 1) {
+                               dup2(pip[1], 1);
+                               close(pip[1]);
+                       }
+                       evaltreenr(lp->n, flags);
+                       /* never returns */
+               }
+               if (prevfd >= 0)
+                       close(prevfd);
+               prevfd = pip[0];
+               close(pip[1]);
+       }
+       if (n->npipe.backgnd == 0) {
+               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.
+ */
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+       int saveherefd;
+
+       result->fd = -1;
+       result->buf = NULL;
+       result->nleft = 0;
+       result->jp = NULL;
+       if (n == NULL) {
+               goto out;
+       }
+
+       saveherefd = herefd;
+       herefd = -1;
+
+       {
+               int pip[2];
+               struct job *jp;
+
+               if (pipe(pip) < 0)
+                       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]);
+                       }
+                       eflag = 0;
+                       evaltreenr(n, EV_EXIT);
+                       /* NOTREACHED */
+               }
+               close(pip[1]);
+               result->fd = pip[0];
+               result->jp = jp;
+       }
+       herefd = saveherefd;
+out:
+       TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+               result->fd, result->buf, result->nleft, result->jp));
+}
+
+#ifdef CONFIG_ASH_CMDCMD
+static inline char **
+parse_command_args(char **argv, const char **path)
+{
+       char *cp, c;
+
+       for (;;) {
+               cp = *++argv;
+               if (!cp)
+                       return 0;
+               if (*cp++ != '-')
+                       break;
+               if (!(c = *cp++))
+                       break;
+               if (c == '-' && !*cp) {
+                       argv++;
+                       break;
+               }
+               do {
+                       switch (c) {
+                       case 'p':
+                               *path = defpath;
+                               break;
+                       default:
+                               /* run 'typecmd' for other options */
+                               return 0;
+                       }
+               } while ((c = *cp++));
+       }
+       return argv;
+}
+#endif
+
+
+
+/*
+ * Execute a simple command.
+ */
+
+static void
+evalcommand(union node *cmd, int flags)
+{
+       struct stackmark smark;
+       union node *argp;
+       struct arglist arglist;
+       struct arglist varlist;
+       char **argv;
+       int argc;
+       const struct strlist *sp;
+       struct cmdentry cmdentry;
+       struct job *jp;
+       char *lastarg;
+       const char *path;
+       int spclbltin;
+       int cmd_is_exec;
+       int status;
+       char **nargv;
+
+       /* First expand the arguments. */
+       TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
+       setstackmark(&smark);
+       back_exitstatus = 0;
+
+       cmdentry.cmdtype = CMDBUILTIN;
+       cmdentry.u.cmd = &bltin;
+       varlist.lastp = &varlist.list;
+       *varlist.lastp = NULL;
+       arglist.lastp = &arglist.list;
+       *arglist.lastp = NULL;
+
+       argc = 0;
+       for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
+               struct strlist **spp;
+
+               spp = arglist.lastp;
+               expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+               for (sp = *spp; sp; sp = sp->next)
+                       argc++;
+       }
+
+       argv = nargv = stalloc(sizeof (char *) * (argc + 1));
+       for (sp = arglist.list ; sp ; sp = sp->next) {
+               TRACE(("evalcommand arg: %s\n", sp->text));
+               *nargv++ = sp->text;
+       }
+       *nargv = NULL;
+
+       lastarg = NULL;
+       if (iflag && funcnest == 0 && argc > 0)
+               lastarg = nargv[-1];
+
+       preverrout_fd = 2;
+       expredir(cmd->ncmd.redirect);
+       status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
+
+       path = vpath.text;
+       for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
+               struct strlist **spp;
+               char *p;
+
+               spp = varlist.lastp;
+               expandarg(argp, &varlist, EXP_VARTILDE);
+
+               /*
+                * Modify the command lookup path, if a PATH= assignment
+                * is present
+                */
+               p = (*spp)->text;
+               if (varequal(p, path))
+                       path = p;
+       }
+
+       /* Print the command if xflag is set. */
+       if (xflag) {
+               int n;
+               const char *p = " %s";
+
+               p++;
+               dprintf(preverrout_fd, p, ps4val());
+
+               sp = varlist.list;
+               for(n = 0; n < 2; n++) {
+                       while (sp) {
+                               dprintf(preverrout_fd, p, sp->text);
+                               sp = sp->next;
+                               if(*p == '%') {
+                                       p--;
+                               }
+                       }
+                       sp = arglist.list;
+               }
+               bb_full_write(preverrout_fd, "\n", 1);
+       }
+
+       cmd_is_exec = 0;
+       spclbltin = -1;
+
+       /* Now locate the command. */
+       if (argc) {
+               const char *oldpath;
+               int cmd_flag = DO_ERR;
+
+               path += 5;
+               oldpath = path;
+               for (;;) {
+                       find_command(argv[0], &cmdentry, cmd_flag, path);
+                       if (cmdentry.cmdtype == CMDUNKNOWN) {
+                               status = 127;
+                               flusherr();
+                               goto bail;
+                       }
+
+                       /* implement bltin and command here */
+                       if (cmdentry.cmdtype != CMDBUILTIN)
+                               break;
+                       if (spclbltin < 0)
+                               spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
+                       if (cmdentry.u.cmd == EXECCMD)
+                               cmd_is_exec++;
+#ifdef CONFIG_ASH_CMDCMD
+                       if (cmdentry.u.cmd == COMMANDCMD) {
+
+                               path = oldpath;
+                               nargv = parse_command_args(argv, &path);
+                               if (!nargv)
+                                       break;
+                               argc -= nargv - argv;
+                               argv = nargv;
+                               cmd_flag |= DO_NOFUNC;
+                       } else
+#endif
+                               break;
+               }
+       }
+
+       if (status) {
+               /* We have a redirection error. */
+               if (spclbltin > 0)
+                       exraise(EXERROR);
+bail:
+               exitstatus = status;
+               goto out;
+       }
+
+       /* Execute the command. */
+       switch (cmdentry.cmdtype) {
+       default:
+               /* Fork off a child process if necessary. */
+               if (!(flags & EV_EXIT) || trap[0]) {
+                       INTOFF;
+                       jp = makejob(cmd, 1);
+                       if (forkshell(jp, cmd, FORK_FG) != 0) {
+                               exitstatus = waitforjob(jp);
+                               INTON;
+                               break;
+                       }
+                       FORCEINTON;
+               }
+               listsetvar(varlist.list, VEXPORT|VSTACK);
+               shellexec(argv, path, cmdentry.u.index);
+               /* NOTREACHED */
+
+       case CMDBUILTIN:
+               cmdenviron = varlist.list;
+               if (cmdenviron) {
+                       struct strlist *list = cmdenviron;
+                       int i = VNOSET;
+                       if (spclbltin > 0 || argc == 0) {
+                               i = 0;
+                               if (cmd_is_exec && argc > 1)
+                                       i = VEXPORT;
+                       }
+                       listsetvar(list, i);
+               }
+               if (evalbltin(cmdentry.u.cmd, argc, argv)) {
+                       int exit_status;
+                       int i, j;
+
+                       i = exception;
+                       if (i == EXEXIT)
+                               goto raise;
+
+                       exit_status = 2;
+                       j = 0;
+                       if (i == EXINT)
+                               j = SIGINT;
+                       if (i == EXSIG)
+                               j = pendingsigs;
+                       if (j)
+                               exit_status = j + 128;
+                       exitstatus = exit_status;
+
+                       if (i == EXINT || spclbltin > 0) {
+raise:
+                               longjmp(handler->loc, 1);
+                       }
+                       FORCEINTON;
+               }
+               break;
+
+       case CMDFUNCTION:
+               listsetvar(varlist.list, 0);
+               if (evalfun(cmdentry.u.func, argc, argv, flags))
+                       goto raise;
+               break;
+       }
+
+out:
+       popredir(cmd_is_exec);
+       if (lastarg)
+               /* dsl: I think this is intended to be used to support
+                * '_' in 'vi' command mode during line editing...
+                * However I implemented that within libedit itself.
+                */
+               setvar("_", lastarg, 0);
+       popstackmark(&smark);
+}
+
+static int
+evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
+       char *volatile savecmdname;
+       struct jmploc *volatile savehandler;
+       struct jmploc jmploc;
+       int i;
+
+       savecmdname = commandname;
+       if ((i = setjmp(jmploc.loc)))
+               goto cmddone;
+       savehandler = handler;
+       handler = &jmploc;
+       commandname = argv[0];
+       argptr = argv + 1;
+       optptr = NULL;                  /* initialize nextopt */
+       exitstatus = (*cmd->builtin)(argc, argv);
+       flushall();
+cmddone:
+       exitstatus |= ferror(stdout);
+       commandname = savecmdname;
+       exsig = 0;
+       handler = savehandler;
+
+       return i;
+}
+
+static int
+evalfun(struct funcnode *func, int argc, char **argv, int flags)
+{
+       volatile struct shparam saveparam;
+       struct localvar *volatile savelocalvars;
+       struct jmploc *volatile savehandler;
+       struct jmploc jmploc;
+       int e;
+
+       saveparam = shellparam;
+       savelocalvars = localvars;
+       if ((e = setjmp(jmploc.loc))) {
+               goto funcdone;
+       }
+       INTOFF;
+       savehandler = handler;
+       handler = &jmploc;
+       localvars = NULL;
+       shellparam.malloc = 0;
+       func->count++;
+       INTON;
+       shellparam.nparam = argc - 1;
+       shellparam.p = argv + 1;
+#ifdef CONFIG_ASH_GETOPTS
+       shellparam.optind = 1;
+       shellparam.optoff = -1;
+#endif
+       funcnest++;
+       evaltree(&func->n, flags & EV_TESTED);
+       funcnest--;
+funcdone:
+       INTOFF;
+       freefunc(func);
+       poplocalvars();
+       localvars = savelocalvars;
+       freeparam(&shellparam);
+       shellparam = saveparam;
+       handler = savehandler;
+       INTON;
+       if (evalskip == SKIPFUNC) {
+               evalskip = 0;
+               skipcount = 0;
+       }
+       return e;
+}
+
+
+static inline int
+goodname(const char *p)
+{
+       return !*endofname(p);
+}
+
+/*
+ * 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)
+               if (goodname(n->ncmd.args->narg.text))
+                       find_command(n->ncmd.args->narg.text, &entry, 0,
+                                    pathval());
+}
+
+
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given.
+ */
+
+static int
+bltincmd(int argc, char **argv)
+{
+       /*
+        * Preserve exitstatus of a previous possible redirection
+        * as POSIX mandates
+        */
+       return back_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.
+ */
+
+static int
+breakcmd(int argc, char **argv)
+{
+       int n = argc > 1 ? number(argv[1]) : 1;
+
+       if (n <= 0)
+               error(illnum, argv[1]);
+       if (n > loopnest)
+               n = loopnest;
+       if (n > 0) {
+               evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+               skipcount = n;
+       }
+       return 0;
+}
+
+
+/*
+ * The return command.
+ */
+
+static int
+returncmd(int argc, char **argv)
+{
+       int ret = argc > 1 ? number(argv[1]) : exitstatus;
+
+       if (funcnest) {
+               evalskip = SKIPFUNC;
+               skipcount = 1;
+               return ret;
+       }
+       else {
+               /* Do what ksh does; skip the rest of the file */
+               evalskip = SKIPFILE;
+               skipcount = 1;
+               return ret;
+       }
+}
+
+
+static int
+falsecmd(int argc, char **argv)
+{
+       return 1;
+}
+
+
+static int
+truecmd(int argc, char **argv)
+{
+       return 0;
+}
+
+
+static int
+execcmd(int argc, char **argv)
+{
+       if (argc > 1) {
+               iflag = 0;              /* exit on error */
+               mflag = 0;
+               optschanged();
+               shellexec(argv + 1, pathval(), 0);
+       }
+       return 0;
+}
+
+
+/*      $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $    */
+
+/*
+ * 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.
+ */
+
+#define CMDTABLESIZE 31         /* should be prime */
+#define ARB 1                   /* actual size determined at run time */
+
+
+
+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 */
+};
+
+
+static struct tblentry *cmdtable[CMDTABLESIZE];
+static int builtinloc = -1;             /* index in path of %builtin, or -1 */
+
+
+static void tryexec(char *, char **, char **);
+static void clearcmdentry(int);
+static struct tblentry *cmdlookup(const char *, int);
+static void delete_cmd_entry(void);
+
+
+/*
+ * Exec a program.  Never returns.  If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+
+static void
+shellexec(char **argv, const char *path, int idx)
+{
+       char *cmdname;
+       int e;
+       char **envp;
+
+       clearredir(1);
+       envp = environment();
+       if (strchr(argv[0], '/') != NULL
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+               || find_applet_by_name(argv[0])
+#endif
+                                               ) {
+               tryexec(argv[0], argv, envp);
+               e = errno;
+       } else {
+               e = ENOENT;
+               while ((cmdname = padvance(&path, argv[0])) != NULL) {
+                       if (--idx < 0 && pathopt == NULL) {
+                               tryexec(cmdname, argv, envp);
+                               if (errno != ENOENT && errno != ENOTDIR)
+                                       e = errno;
+                       }
+                       stunalloc(cmdname);
+               }
+       }
+
+       /* Map to POSIX errors */
+       switch (e) {
+       case EACCES:
+               exerrno = 126;
+               break;
+       case ENOENT:
+               exerrno = 127;
+               break;
+       default:
+               exerrno = 2;
+               break;
+       }
+       TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
+               argv[0], e, suppressint ));
+       exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
+       /* NOTREACHED */
+}
+
+
+static void
+tryexec(char *cmd, char **argv, char **envp)
+{
+       int repeated = 0;
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       int flg_bb = 0;
+       char *name = cmd;
+
+       if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
+               flg_bb = 1;
+       }
+       if(flg_bb) {
+               char **ap;
+               char **new;
+
+               *argv = name;
+               if(strcmp(name, "busybox")) {
+                       for (ap = argv; *ap; ap++);
+                       ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
+                       *ap++ = cmd = "/bin/busybox";
+                       while ((*ap++ = *argv++));
+                       argv = new;
+                       repeated++;
+               } else {
+                       cmd = "/bin/busybox";
+               }
+       }
+#endif
+
+repeat:
+#ifdef SYSV
+       do {
+               execve(cmd, argv, envp);
+       } while (errno == EINTR);
+#else
+       execve(cmd, argv, envp);
+#endif
+       if (repeated++) {
+               ckfree(argv);
+       } else if (errno == ENOEXEC) {
+               char **ap;
+               char **new;
+
+               for (ap = argv; *ap; ap++)
+                       ;
+               ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
+               ap[1] = cmd;
+               *ap = cmd = (char *)DEFAULT_SHELL;
+               ap += 2;
+               argv++;
+               while ((*ap++ = *argv++))
+                       ;
+               argv = new;
+               goto repeat;
+       }
+}
+
+
+
+/*
+ * 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.
+ */
+
+static char *
+padvance(const char **path, const char *name)
+{
+       const char *p;
+       char *q;
+       const char *start;
+       size_t len;
+
+       if (*path == NULL)
+               return NULL;
+       start = *path;
+       for (p = start ; *p && *p != ':' && *p != '%' ; p++);
+       len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
+       while (stackblocksize() < len)
+               growstackblock();
+       q = stackblock();
+       if (p != start) {
+               memcpy(q, start, p - start);
+               q += p - start;
+               *q++ = '/';
+       }
+       strcpy(q, name);
+       pathopt = NULL;
+       if (*p == '%') {
+               pathopt = ++p;
+               while (*p && *p != ':')  p++;
+       }
+       if (*p == ':')
+               *path = p + 1;
+       else
+               *path = NULL;
+       return stalloc(len);
+}
+
+
+/*** Command hashing code ***/
+
+static void
+printentry(struct tblentry *cmdp)
+{
+       int idx;
+       const char *path;
+       char *name;
+
+       idx = cmdp->param.index;
+       path = pathval();
+       do {
+               name = padvance(&path, cmdp->cmdname);
+               stunalloc(name);
+       } while (--idx >= 0);
+       out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
+}
+
+
+static int
+hashcmd(int argc, char **argv)
+{
+       struct tblentry **pp;
+       struct tblentry *cmdp;
+       int c;
+       struct cmdentry entry;
+       char *name;
+
+       while ((c = nextopt("r")) != '\0') {
+               clearcmdentry(0);
+               return 0;
+       }
+       if (*argptr == NULL) {
+               for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+                       for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+                               if (cmdp->cmdtype == CMDNORMAL)
+                                       printentry(cmdp);
+                       }
+               }
+               return 0;
+       }
+       c = 0;
+       while ((name = *argptr) != NULL) {
+               if ((cmdp = cmdlookup(name, 0)) != NULL
+                && (cmdp->cmdtype == CMDNORMAL
+                    || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+                       delete_cmd_entry();
+               find_command(name, &entry, DO_ERR, pathval());
+               if (entry.cmdtype == CMDUNKNOWN)
+                       c = 1;
+               argptr++;
+       }
+       return c;
+}
+
+
+/*
+ * Resolve a command name.  If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+
+static void
+find_command(char *name, struct cmdentry *entry, int act, const char *path)
+{
+       struct tblentry *cmdp;
+       int idx;
+       int prev;
+       char *fullname;
+       struct stat statb;
+       int e;
+       int updatetbl;
+       struct builtincmd *bcmd;
+
+       /* If name contains a slash, don't use PATH or hash table */
+       if (strchr(name, '/') != NULL) {
+               entry->u.index = -1;
+               if (act & DO_ABS) {
+                       while (stat(name, &statb) < 0) {
+#ifdef SYSV
+                               if (errno == EINTR)
+                                       continue;
+#endif
+                               entry->cmdtype = CMDUNKNOWN;
+                               return;
+                       }
+               }
+               entry->cmdtype = CMDNORMAL;
+               return;
+       }
+
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       if (find_applet_by_name(name)) {
+               entry->cmdtype = CMDNORMAL;
+               entry->u.index = -1;
+               return;
+       }
+#endif
+
+       updatetbl = (path == pathval());
+       if (!updatetbl) {
+               act |= DO_ALTPATH;
+               if (strstr(path, "%builtin") != NULL)
+                       act |= DO_ALTBLTIN;
+       }
+
+       /* If name is in the table, check answer will be ok */
+       if ((cmdp = cmdlookup(name, 0)) != NULL) {
+               int bit;
+
+               switch (cmdp->cmdtype) {
+               default:
+#if DEBUG
+                       abort();
+#endif
+               case CMDNORMAL:
+                       bit = DO_ALTPATH;
+                       break;
+               case CMDFUNCTION:
+                       bit = DO_NOFUNC;
+                       break;
+               case CMDBUILTIN:
+                       bit = DO_ALTBLTIN;
+                       break;
+               }
+               if (act & bit) {
+                       updatetbl = 0;
+                       cmdp = NULL;
+               } else if (cmdp->rehash == 0)
+                       /* if not invalidated by cd, we're done */
+                       goto success;
+       }
+
+       /* If %builtin not in path, check for builtin next */
+       bcmd = find_builtin(name);
+       if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
+               act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
+       )))
+               goto builtin_success;
+
+       /* We have to search path. */
+       prev = -1;              /* where to start */
+       if (cmdp && cmdp->rehash) {     /* doing a rehash */
+               if (cmdp->cmdtype == CMDBUILTIN)
+                       prev = builtinloc;
+               else
+                       prev = cmdp->param.index;
+       }
+
+       e = ENOENT;
+       idx = -1;
+loop:
+       while ((fullname = padvance(&path, name)) != NULL) {
+               stunalloc(fullname);
+               idx++;
+               if (pathopt) {
+                       if (prefix(pathopt, "builtin")) {
+                               if (bcmd)
+                                       goto builtin_success;
+                               continue;
+                       } else if (!(act & DO_NOFUNC) &&
+                                  prefix(pathopt, "func")) {
+                               /* handled below */
+                       } else {
+                               /* ignore unimplemented options */
+                               continue;
+                       }
+               }
+               /* if rehash, don't redo absolute path names */
+               if (fullname[0] == '/' && idx <= prev) {
+                       if (idx < prev)
+                               continue;
+                       TRACE(("searchexec \"%s\": no change\n", name));
+                       goto success;
+               }
+               while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+                       if (errno == EINTR)
+                               continue;
+#endif
+                       if (errno != ENOENT && errno != ENOTDIR)
+                               e = errno;
+                       goto loop;
+               }
+               e = EACCES;     /* if we fail, this will be the error */
+               if (!S_ISREG(statb.st_mode))
+                       continue;
+               if (pathopt) {          /* this is a %func directory */
+                       stalloc(strlen(fullname) + 1);
+                       readcmdfile(fullname);
+                       if ((cmdp = cmdlookup(name, 0)) == NULL ||
+                           cmdp->cmdtype != CMDFUNCTION)
+                               error("%s not defined in %s", name, fullname);
+                       stunalloc(fullname);
+                       goto success;
+               }
+               TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+               if (!updatetbl) {
+                       entry->cmdtype = CMDNORMAL;
+                       entry->u.index = idx;
+                       return;
+               }
+               INTOFF;
+               cmdp = cmdlookup(name, 1);
+               cmdp->cmdtype = CMDNORMAL;
+               cmdp->param.index = idx;
+               INTON;
+               goto success;
+       }
+
+       /* We failed.  If there was an entry for this command, delete it */
+       if (cmdp && updatetbl)
+               delete_cmd_entry();
+       if (act & DO_ERR)
+               sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
+       entry->cmdtype = CMDUNKNOWN;
+       return;
+
+builtin_success:
+       if (!updatetbl) {
+               entry->cmdtype = CMDBUILTIN;
+               entry->u.cmd = bcmd;
+               return;
+       }
+       INTOFF;
+       cmdp = cmdlookup(name, 1);
+       cmdp->cmdtype = CMDBUILTIN;
+       cmdp->param.cmd = bcmd;
+       INTON;
+success:
+       cmdp->rehash = 0;
+       entry->cmdtype = cmdp->cmdtype;
+       entry->u = cmdp->param;
+}
+
+
+/*
+ * Wrapper around strcmp for qsort/bsearch/...
+ */
+static int pstrcmp(const void *a, const void *b)
+{
+       return strcmp((const char *) a, (*(const char *const *) b) + 1);
+}
+
+/*
+ * Search the table of builtin commands.
+ */
+
+static struct builtincmd *
+find_builtin(const char *name)
+{
+       struct builtincmd *bp;
+
+       bp = bsearch(
+               name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
+               pstrcmp
+       );
+       return bp;
+}
+
+
+
+/*
+ * Called when a cd is done.  Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+
+static void
+hashcd(void)
+{
+       struct tblentry **pp;
+       struct tblentry *cmdp;
+
+       for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+               for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+                       if (cmdp->cmdtype == CMDNORMAL || (
+                               cmdp->cmdtype == CMDBUILTIN &&
+                               !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
+                               builtinloc > 0
+                       ))
+                               cmdp->rehash = 1;
+               }
+       }
+}
+
+
+
+/*
+ * Fix command hash table when PATH changed.
+ * Called before PATH is changed.  The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.
+ * Called with interrupts off.
+ */
+
+static void
+changepath(const char *newval)
+{
+       const char *old, *new;
+       int idx;
+       int firstchange;
+       int idx_bltin;
+
+       old = pathval();
+       new = newval;
+       firstchange = 9999;     /* assume no change */
+       idx = 0;
+       idx_bltin = -1;
+       for (;;) {
+               if (*old != *new) {
+                       firstchange = idx;
+                       if ((*old == '\0' && *new == ':')
+                        || (*old == ':' && *new == '\0'))
+                               firstchange++;
+                       old = new;      /* ignore subsequent differences */
+               }
+               if (*new == '\0')
+                       break;
+               if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
+                       idx_bltin = idx;
+               if (*new == ':') {
+                       idx++;
+               }
+               new++, old++;
+       }
+       if (builtinloc < 0 && idx_bltin >= 0)
+               builtinloc = idx_bltin;             /* zap builtins */
+       if (builtinloc >= 0 && idx_bltin < 0)
+               firstchange = 0;
+       clearcmdentry(firstchange);
+       builtinloc = idx_bltin;
+}
+
+
+/*
+ * 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;
+}
+
+
+
+/*
+ * Locate a command in the command hash table.  If "add" is nonzero,
+ * add the command to the table if it is not already present.  The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ *
+ * Interrupts must be off if called with add != 0.
+ */
+
+static struct tblentry **lastcmdentry;
+
+
+static struct tblentry *
+cmdlookup(const char *name, int add)
+{
+       unsigned int hashval;
+       const char *p;
+       struct tblentry *cmdp;
+       struct tblentry **pp;
+
+       p = name;
+       hashval = (unsigned char)*p << 4;
+       while (*p)
+               hashval += (unsigned char)*p++;
+       hashval &= 0x7FFF;
+       pp = &cmdtable[hashval % CMDTABLESIZE];
+       for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+               if (equal(cmdp->cmdname, name))
+                       break;
+               pp = &cmdp->next;
+       }
+       if (add && cmdp == NULL) {
+               cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
+                                       + strlen(name) + 1);
+               cmdp->next = NULL;
+               cmdp->cmdtype = CMDUNKNOWN;
+               strcpy(cmdp->cmdname, name);
+       }
+       lastcmdentry = pp;
+       return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+
+static void
+delete_cmd_entry(void)
+{
+       struct tblentry *cmdp;
+
+       INTOFF;
+       cmdp = *lastcmdentry;
+       *lastcmdentry = cmdp->next;
+       if (cmdp->cmdtype == CMDFUNCTION)
+               freefunc(cmdp->param.func);
+       ckfree(cmdp);
+       INTON;
+}
+
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name - except special builtins.
+ */
+
+static inline void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+       struct tblentry *cmdp;
+
+       cmdp = cmdlookup(name, 1);
+       if (cmdp->cmdtype == CMDFUNCTION) {
+               freefunc(cmdp->param.func);
+       }
+       cmdp->cmdtype = entry->cmdtype;
+       cmdp->param = entry->u;
+       cmdp->rehash = 0;
+}
+
+/*
+ * Make a copy of a parse tree.
+ */
+
+static inline struct funcnode *
+copyfunc(union node *n)
+{
+       struct funcnode *f;
+       size_t blocksize;
+
+       funcblocksize = offsetof(struct funcnode, n);
+       funcstringsize = 0;
+       calcsize(n);
+       blocksize = funcblocksize;
+       f = ckmalloc(blocksize + funcstringsize);
+       funcblock = (char *) f + offsetof(struct funcnode, n);
+       funcstring = (char *) f + blocksize;
+       copynode(n);
+       f->count = 0;
+       return f;
+}
+
+/*
+ * Define a shell function.
+ */
+
+static void
+defun(char *name, union node *func)
+{
+       struct cmdentry entry;
+
+       INTOFF;
+       entry.cmdtype = CMDFUNCTION;
+       entry.u.func = copyfunc(func);
+       addcmdentry(name, &entry);
+       INTON;
+}
+
+
+/*
+ * Delete a function if it exists.
+ */
+
+static void
+unsetfunc(const char *name)
+{
+       struct tblentry *cmdp;
+
+       if ((cmdp = cmdlookup(name, 0)) != NULL &&
+           cmdp->cmdtype == CMDFUNCTION)
+               delete_cmd_entry();
+}
+
+/*
+ * Locate and print what a word is...
+ */
+
+
+#ifdef CONFIG_ASH_CMDCMD
+static int
+describe_command(char *command, int describe_command_verbose)
+#else
+#define describe_command_verbose 1
+static int
+describe_command(char *command)
+#endif
+{
+       struct cmdentry entry;
+       struct tblentry *cmdp;
+#ifdef CONFIG_ASH_ALIAS
+       const struct alias *ap;
+#endif
+       const char *path = pathval();
+
+       if (describe_command_verbose) {
+               out1str(command);
+       }
+
+       /* First look at the keywords */
+       if (findkwd(command)) {
+               out1str(describe_command_verbose ? " is a shell keyword" : command);
+               goto out;
+       }
+
+#ifdef CONFIG_ASH_ALIAS
+       /* Then look at the aliases */
+       if ((ap = lookupalias(command, 0)) != NULL) {
+               if (describe_command_verbose) {
+                       out1fmt(" is an alias for %s", ap->val);
+               } else {
+                       out1str("alias ");
+                       printalias(ap);
+                       return 0;
+               }
+               goto out;
+       }
+#endif
+       /* Then check if it is a tracked alias */
+       if ((cmdp = cmdlookup(command, 0)) != NULL) {
+               entry.cmdtype = cmdp->cmdtype;
+               entry.u = cmdp->param;
+       } else {
+               /* Finally use brute force */
+               find_command(command, &entry, DO_ABS, path);
+       }
+
+       switch (entry.cmdtype) {
+       case CMDNORMAL: {
+               int j = entry.u.index;
+               char *p;
+               if (j == -1) {
+                       p = command;
+               } else {
+                       do {
+                               p = padvance(&path, command);
+                               stunalloc(p);
+                       } while (--j >= 0);
+               }
+               if (describe_command_verbose) {
+                       out1fmt(" is%s %s",
+                               (cmdp ? " a tracked alias for" : nullstr), p
+                       );
+               } else {
+                       out1str(p);
+               }
+               break;
+       }
+
+       case CMDFUNCTION:
+               if (describe_command_verbose) {
+                       out1str(" is a shell function");
+               } else {
+                       out1str(command);
+               }
+               break;
+
+       case CMDBUILTIN:
+               if (describe_command_verbose) {
+                       out1fmt(" is a %sshell builtin",
+                               IS_BUILTIN_SPECIAL(entry.u.cmd) ?
+                                       "special " : nullstr
+                       );
+               } else {
+                       out1str(command);
+               }
+               break;
+
+       default:
+               if (describe_command_verbose) {
+                       out1str(": not found\n");
+               }
+               return 127;
+       }
+
+out:
+       outstr("\n", stdout);
+       return 0;
+}
+
+static int
+typecmd(int argc, char **argv)
+{
+       int i;
+       int err = 0;
+
+       for (i = 1; i < argc; i++) {
+#ifdef CONFIG_ASH_CMDCMD
+               err |= describe_command(argv[i], 1);
+#else
+               err |= describe_command(argv[i]);
+#endif
+       }
+       return err;
+}
+
+#ifdef CONFIG_ASH_CMDCMD
+static int
+commandcmd(int argc, char **argv)
+{
+       int c;
+       int default_path = 0;
+       int verify_only = 0;
+       int verbose_verify_only = 0;
+
+       while ((c = nextopt("pvV")) != '\0')
+               switch (c) {
+               default:
+#ifdef DEBUG
+                       fprintf(stderr,
+"command: nextopt returned character code 0%o\n", c);
+                       return EX_SOFTWARE;
+#endif
+               case 'p':
+                       default_path = 1;
+                       break;
+               case 'v':
+                       verify_only = 1;
+                       break;
+               case 'V':
+                       verbose_verify_only = 1;
+                       break;
+               }
+
+       if (default_path + verify_only + verbose_verify_only > 1 ||
+           !*argptr) {
+                       fprintf(stderr,
+                               "command [-p] command [arg ...]\n"
+                               "command {-v|-V} command\n");
+                       return EX_USAGE;
+       }
+
+       if (verify_only || verbose_verify_only) {
+               return describe_command(*argptr, verbose_verify_only);
+       }
+
+       return 0;
+}
+#endif
+
+/*      $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $     */
+
+/*
+ * Routines to expand arguments to commands.  We have to deal with
+ * backquotes, shell variables, and file metacharacters.
+ */
+
+/*
+ * _rmescape() flags
+ */
+#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
+#define RMESCAPE_GLOB   0x2     /* Add backslashes for glob */
+#define RMESCAPE_QUOTED 0x4     /* Remove CTLESC unless in quotes */
+#define RMESCAPE_GROW   0x8     /* Grow strings instead of stalloc */
+#define RMESCAPE_HEAP   0x10    /* Malloc strings instead of stalloc */
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+
+struct ifsregion {
+       struct ifsregion *next; /* next region in list */
+       int begoff;             /* offset of start of region */
+       int endoff;             /* offset of end of region */
+       int nulonly;            /* search for nul bytes only */
+};
+
+/* output of current string */
+static char *expdest;
+/* list of back quote expressions */
+static struct nodelist *argbackq;
+/* first struct in list of ifs regions */
+static struct ifsregion ifsfirst;
+/* last struct in list */
+static struct ifsregion *ifslastp;
+/* holds expanded arg list */
+static struct arglist exparg;
+
+static void argstr(char *, int);
+static char *exptilde(char *, char *, int);
+static void expbackq(union node *, int, int);
+static const char *subevalvar(char *, char *, int, int, int, int, int);
+static char *evalvar(char *, int);
+static void strtodest(const char *, int, int);
+static void memtodest(const char *p, size_t len, int syntax, int quotes);
+static ssize_t varvalue(char *, int, int);
+static void recordregion(int, int, int);
+static void removerecordregions(int);
+static void ifsbreakup(char *, struct arglist *);
+static void ifsfree(void);
+static void expandmeta(struct strlist *, int);
+static int patmatch(char *, const char *);
+
+static int cvtnum(arith_t);
+static size_t esclen(const char *, const char *);
+static char *scanleft(char *, char *, char *, char *, int, int);
+static char *scanright(char *, char *, char *, char *, int, int);
+static void varunset(const char *, const char *, const char *, int)
+       __attribute__((__noreturn__));
+
+
+#define pmatch(a, b) !fnmatch((a), (b), 0)
+/*
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
+ *
+ * Returns an stalloced string.
+ */
+
+static inline char *
+preglob(const char *pattern, int quoted, int flag) {
+       flag |= RMESCAPE_GLOB;
+       if (quoted) {
+               flag |= RMESCAPE_QUOTED;
+       }
+       return _rmescapes((char *)pattern, flag);
+}
+
+
+static size_t
+esclen(const char *start, const char *p) {
+       size_t esc = 0;
+
+       while (p > start && *--p == CTLESC) {
+               esc++;
+       }
+       return esc;
+}
+
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+
+static inline void
+expandhere(union node *arg, int fd)
+{
+       herefd = fd;
+       expandarg(arg, (struct arglist *)NULL, 0);
+       bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
+}
+
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
+ * perform splitting and file name expansion.  When arglist is NULL, perform
+ * here document expansion.
+ */
+
+void
+expandarg(union node *arg, struct arglist *arglist, int flag)
+{
+       struct strlist *sp;
+       char *p;
+
+       argbackq = arg->narg.backquote;
+       STARTSTACKSTR(expdest);
+       ifsfirst.next = NULL;
+       ifslastp = NULL;
+       argstr(arg->narg.text, flag);
+       if (arglist == NULL) {
+               return;                 /* here document expanded */
+       }
+       STPUTC('\0', expdest);
+       p = grabstackstr(expdest);
+       exparg.lastp = &exparg.list;
+       /*
+        * TODO - EXP_REDIR
+        */
+       if (flag & EXP_FULL) {
+               ifsbreakup(p, &exparg);
+               *exparg.lastp = NULL;
+               exparg.lastp = &exparg.list;
+               expandmeta(exparg.list, flag);
+       } else {
+               if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+                       rmescapes(p);
+               sp = (struct strlist *)stalloc(sizeof (struct strlist));
+               sp->text = p;
+               *exparg.lastp = sp;
+               exparg.lastp = &sp->next;
+       }
+       if (ifsfirst.next)
+               ifsfree();
+       *exparg.lastp = NULL;
+       if (exparg.list) {
+               *arglist->lastp = exparg.list;
+               arglist->lastp = exparg.lastp;
+       }
+}
+
+
+/*
+ * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
+ * characters to allow for further processing.  Otherwise treat
+ * $@ like $* since no splitting will be performed.
+ */
+
+static void
+argstr(char *p, int flag)
+{
+       static const char spclchars[] = {
+               '=',
+               ':',
+               CTLQUOTEMARK,
+               CTLENDVAR,
+               CTLESC,
+               CTLVAR,
+               CTLBACKQ,
+               CTLBACKQ | CTLQUOTE,
+#ifdef CONFIG_ASH_MATH_SUPPORT
+               CTLENDARI,
+#endif
+               0
+       };
+       const char *reject = spclchars;
+       int c;
+       int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
+       int breakall = flag & EXP_WORD;
+       int inquotes;
+       size_t length;
+       int startloc;
+
+       if (!(flag & EXP_VARTILDE)) {
+               reject += 2;
+       } else if (flag & EXP_VARTILDE2) {
+               reject++;
+       }
+       inquotes = 0;
+       length = 0;
+       if (flag & EXP_TILDE) {
+               char *q;
+
+               flag &= ~EXP_TILDE;
+tilde:
+               q = p;
+               if (*q == CTLESC && (flag & EXP_QWORD))
+                       q++;
+               if (*q == '~')
+                       p = exptilde(p, q, flag);
+       }
+start:
+       startloc = expdest - (char *)stackblock();
+       for (;;) {
+               length += strcspn(p + length, reject);
+               c = p[length];
+               if (c && (!(c & 0x80)
+#ifdef CONFIG_ASH_MATH_SUPPORT
+                                       || c == CTLENDARI
+#endif
+                  )) {
+                       /* c == '=' || c == ':' || c == CTLENDARI */
+                       length++;
+               }
+               if (length > 0) {
+                       int newloc;
+                       expdest = stnputs(p, length, expdest);
+                       newloc = expdest - (char *)stackblock();
+                       if (breakall && !inquotes && newloc > startloc) {
+                               recordregion(startloc, newloc, 0);
+                       }
+                       startloc = newloc;
+               }
+               p += length + 1;
+               length = 0;
+
+               switch (c) {
+               case '\0':
+                       goto breakloop;
+               case '=':
+                       if (flag & EXP_VARTILDE2) {
+                               p--;
+                               continue;
+                       }
+                       flag |= EXP_VARTILDE2;
+                       reject++;
+                       /* fall through */
+               case ':':
+                       /*
+                        * sort of a hack - expand tildes in variable
+                        * assignments (after the first '=' and after ':'s).
+                        */
+                       if (*--p == '~') {
+                               goto tilde;
+                       }
+                       continue;
+               }
+
+               switch (c) {
+               case CTLENDVAR: /* ??? */
+                       goto breakloop;
+               case CTLQUOTEMARK:
+                       /* "$@" syntax adherence hack */
+                       if (
+                               !inquotes &&
+                               !memcmp(p, dolatstr, DOLATSTRLEN) &&
+                               (p[4] == CTLQUOTEMARK || (
+                                       p[4] == CTLENDVAR &&
+                                       p[5] == CTLQUOTEMARK
+                               ))
+                       ) {
+                               p = evalvar(p + 1, flag) + 1;
+                               goto start;
+                       }
+                       inquotes = !inquotes;
+addquote:
+                       if (quotes) {
+                               p--;
+                               length++;
+                               startloc++;
+                       }
+                       break;
+               case CTLESC:
+                       startloc++;
+                       length++;
+                       goto addquote;
+               case CTLVAR:
+                       p = evalvar(p, flag);
+                       goto start;
+               case CTLBACKQ:
+                       c = 0;
+               case CTLBACKQ|CTLQUOTE:
+                       expbackq(argbackq->n, c, quotes);
+                       argbackq = argbackq->next;
+                       goto start;
+#ifdef CONFIG_ASH_MATH_SUPPORT
+               case CTLENDARI:
+                       p--;
+                       expari(quotes);
+                       goto start;
+#endif
+               }
+       }
+breakloop:
+       ;
+}
+
+static char *
+exptilde(char *startp, char *p, int flag)
+{
+       char c;
+       char *name;
+       struct passwd *pw;
+       const char *home;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+       int startloc;
+
+       name = p + 1;
+
+       while ((c = *++p) != '\0') {
+               switch(c) {
+               case CTLESC:
+                       return (startp);
+               case CTLQUOTEMARK:
+                       return (startp);
+               case ':':
+                       if (flag & EXP_VARTILDE)
+                               goto done;
+                       break;
+               case '/':
+               case CTLENDVAR:
+                       goto done;
+               }
+       }
+done:
+       *p = '\0';
+       if (*name == '\0') {
+               if ((home = lookupvar(homestr)) == NULL)
+                       goto lose;
+       } else {
+               if ((pw = getpwnam(name)) == NULL)
+                       goto lose;
+               home = pw->pw_dir;
+       }
+       if (*home == '\0')
+               goto lose;
+       *p = c;
+       startloc = expdest - (char *)stackblock();
+       strtodest(home, SQSYNTAX, quotes);
+       recordregion(startloc, expdest - (char *)stackblock(), 0);
+       return (p);
+lose:
+       *p = c;
+       return (startp);
+}
+
+
+static void
+removerecordregions(int endoff)
+{
+       if (ifslastp == NULL)
+               return;
+
+       if (ifsfirst.endoff > endoff) {
+               while (ifsfirst.next != NULL) {
+                       struct ifsregion *ifsp;
+                       INTOFF;
+                       ifsp = ifsfirst.next->next;
+                       ckfree(ifsfirst.next);
+                       ifsfirst.next = ifsp;
+                       INTON;
+               }
+               if (ifsfirst.begoff > endoff)
+                       ifslastp = NULL;
+               else {
+                       ifslastp = &ifsfirst;
+                       ifsfirst.endoff = endoff;
+               }
+               return;
+       }
+
+       ifslastp = &ifsfirst;
+       while (ifslastp->next && ifslastp->next->begoff < endoff)
+               ifslastp=ifslastp->next;
+       while (ifslastp->next != NULL) {
+               struct ifsregion *ifsp;
+               INTOFF;
+               ifsp = ifslastp->next->next;
+               ckfree(ifslastp->next);
+               ifslastp->next = ifsp;
+               INTON;
+       }
+       if (ifslastp->endoff > endoff)
+               ifslastp->endoff = endoff;
+}
+
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+/*
+ * Expand arithmetic expression.  Backup to start of expression,
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+void
+expari(int quotes)
+{
+       char *p, *start;
+       int begoff;
+       int flag;
+       int len;
+
+       /*      ifsfree(); */
+
+       /*
+        * This routine is slightly over-complicated for
+        * efficiency.  Next we scan backwards looking for the
+        * start of arithmetic.
+        */
+       start = stackblock();
+       p = expdest - 1;
+       *p = '\0';
+       p--;
+       do {
+               int esc;
+
+               while (*p != CTLARI) {
+                       p--;
+#ifdef DEBUG
+                       if (p < start) {
+                               error("missing CTLARI (shouldn't happen)");
+                       }
+#endif
+               }
+
+               esc = esclen(start, p);
+               if (!(esc % 2)) {
+                       break;
+               }
+
+               p -= esc + 1;
+       } while (1);
+
+       begoff = p - start;
+
+       removerecordregions(begoff);
+
+       flag = p[1];
+
+       expdest = p;
+
+       if (quotes)
+               rmescapes(p + 2);
+
+       len = cvtnum(dash_arith(p + 2));
+
+       if (flag != '"')
+               recordregion(begoff, begoff + len, 0);
+}
+#endif
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+
+static void
+expbackq(union node *cmd, int quoted, int quotes)
+{
+       struct backcmd in;
+       int i;
+       char buf[128];
+       char *p;
+       char *dest;
+       int startloc;
+       int syntax = quoted? DQSYNTAX : BASESYNTAX;
+       struct stackmark smark;
+
+       INTOFF;
+       setstackmark(&smark);
+       dest = expdest;
+       startloc = dest - (char *)stackblock();
+       grabstackstr(dest);
+       evalbackcmd(cmd, (struct backcmd *) &in);
+       popstackmark(&smark);
+
+       p = in.buf;
+       i = in.nleft;
+       if (i == 0)
+               goto read;
+       for (;;) {
+               memtodest(p, i, syntax, quotes);
+read:
+               if (in.fd < 0)
+                       break;
+               i = safe_read(in.fd, buf, sizeof buf);
+               TRACE(("expbackq: read returns %d\n", i));
+               if (i <= 0)
+                       break;
+               p = buf;
+       }
+
+       if (in.buf)
+               ckfree(in.buf);
+       if (in.fd >= 0) {
+               close(in.fd);
+               back_exitstatus = waitforjob(in.jp);
+       }
+       INTON;
+
+       /* Eat all trailing newlines */
+       dest = expdest;
+       for (; dest > (char *)stackblock() && dest[-1] == '\n';)
+               STUNPUTC(dest);
+       expdest = dest;
+
+       if (quoted == 0)
+               recordregion(startloc, dest - (char *)stackblock(), 0);
+       TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+               (dest - (char *)stackblock()) - startloc,
+               (dest - (char *)stackblock()) - startloc,
+               stackblock() + startloc));
+}
+
+
+static char *
+scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+       int zero)
+{
+       char *loc;
+       char *loc2;
+       char c;
+
+       loc = startp;
+       loc2 = rmesc;
+       do {
+               int match;
+               const char *s = loc2;
+               c = *loc2;
+               if (zero) {
+                       *loc2 = '\0';
+                       s = rmesc;
+               }
+               match = pmatch(str, s);
+               *loc2 = c;
+               if (match)
+                       return loc;
+               if (quotes && *loc == CTLESC)
+                       loc++;
+               loc++;
+               loc2++;
+       } while (c);
+       return 0;
+}
+
+
+static char *
+scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+       int zero)
+{
+       int esc = 0;
+       char *loc;
+       char *loc2;
+
+       for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
+               int match;
+               char c = *loc2;
+               const char *s = loc2;
+               if (zero) {
+                       *loc2 = '\0';
+                       s = rmesc;
+               }
+               match = pmatch(str, s);
+               *loc2 = c;
+               if (match)
+                       return loc;
+               loc--;
+               if (quotes) {
+                       if (--esc < 0) {
+                               esc = esclen(startp, loc);
+                       }
+                       if (esc % 2) {
+                               esc--;
+                               loc--;
+                       }
+               }
+       }
+       return 0;
+}
+
+static const char *
+subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
+{
+       char *startp;
+       char *loc;
+       int saveherefd = herefd;
+       struct nodelist *saveargbackq = argbackq;
+       int amount;
+       char *rmesc, *rmescend;
+       int zero;
+       char *(*scan)(char *, char *, char *, char *, int , int);
+
+       herefd = -1;
+       argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
+       STPUTC('\0', expdest);
+       herefd = saveherefd;
+       argbackq = saveargbackq;
+       startp = stackblock() + startloc;
+
+       switch (subtype) {
+       case VSASSIGN:
+               setvar(str, startp, 0);
+               amount = startp - expdest;
+               STADJUST(amount, expdest);
+               return startp;
+
+       case VSQUESTION:
+               varunset(p, str, startp, varflags);
+               /* NOTREACHED */
+       }
+
+       subtype -= VSTRIMRIGHT;
+#ifdef DEBUG
+       if (subtype < 0 || subtype > 3)
+               abort();
+#endif
+
+       rmesc = startp;
+       rmescend = stackblock() + strloc;
+       if (quotes) {
+               rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
+               if (rmesc != startp) {
+                       rmescend = expdest;
+                       startp = stackblock() + startloc;
+               }
+       }
+       rmescend--;
+       str = stackblock() + strloc;
+       preglob(str, varflags & VSQUOTE, 0);
+
+       /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
+       zero = subtype >> 1;
+       /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
+       scan = (subtype & 1) ^ zero ? scanleft : scanright;
+
+       loc = scan(startp, rmesc, rmescend, str, quotes, zero);
+       if (loc) {
+               if (zero) {
+                       memmove(startp, loc, str - loc);
+                       loc = startp + (str - loc) - 1;
+               }
+               *loc = '\0';
+               amount = loc - expdest;
+               STADJUST(amount, expdest);
+       }
+       return loc;
+}
+
+
+/*
+ * 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;
+       int patloc;
+       int c;
+       int startloc;
+       ssize_t varlen;
+       int easy;
+       int quotes;
+       int quoted;
+
+       quotes = flag & (EXP_FULL | EXP_CASE);
+       varflags = *p++;
+       subtype = varflags & VSTYPE;
+       quoted = varflags & VSQUOTE;
+       var = p;
+       easy = (!quoted || (*var == '@' && shellparam.nparam));
+       startloc = expdest - (char *)stackblock();
+       p = strchr(p, '=') + 1;
+
+again:
+       varlen = varvalue(var, varflags, flag);
+       if (varflags & VSNUL)
+               varlen--;
+
+       if (subtype == VSPLUS) {
+               varlen = -1 - varlen;
+               goto vsplus;
+       }
+
+       if (subtype == VSMINUS) {
+vsplus:
+               if (varlen < 0) {
+                       argstr(
+                               p, flag | EXP_TILDE |
+                                       (quoted ?  EXP_QWORD : EXP_WORD)
+                       );
+                       goto end;
+               }
+               if (easy)
+                       goto record;
+               goto end;
+       }
+
+       if (subtype == VSASSIGN || subtype == VSQUESTION) {
+               if (varlen < 0) {
+                       if (subevalvar(p, var, 0, subtype, startloc,
+                                      varflags, 0)) {
+                               varflags &= ~VSNUL;
+                               /*
+                                * Remove any recorded regions beyond
+                                * start of variable
+                                */
+                               removerecordregions(startloc);
+                               goto again;
+                       }
+                       goto end;
+               }
+               if (easy)
+                       goto record;
+               goto end;
+       }
+
+       if (varlen < 0 && uflag)
+               varunset(p, var, 0, 0);
+
+       if (subtype == VSLENGTH) {
+               cvtnum(varlen > 0 ? varlen : 0);
+               goto record;
+       }
+
+       if (subtype == VSNORMAL) {
+               if (!easy)
+                       goto end;
+record:
+               recordregion(startloc, expdest - (char *)stackblock(), quoted);
+               goto end;
+       }
+
+#ifdef DEBUG
+       switch (subtype) {
+       case VSTRIMLEFT:
+       case VSTRIMLEFTMAX:
+       case VSTRIMRIGHT:
+       case VSTRIMRIGHTMAX:
+               break;
+       default:
+               abort();
+       }
+#endif
+
+       if (varlen >= 0) {
+               /*
+                * Terminate the string and start recording the pattern
+                * right after it
+                */
+               STPUTC('\0', expdest);
+               patloc = expdest - (char *)stackblock();
+               if (subevalvar(p, NULL, patloc, subtype,
+                              startloc, varflags, quotes) == 0) {
+                       int amount = expdest - (
+                               (char *)stackblock() + patloc - 1
+                       );
+                       STADJUST(-amount, expdest);
+               }
+               /* Remove any recorded regions beyond start of variable */
+               removerecordregions(startloc);
+               goto record;
+       }
+
+end:
+       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 (varlen >= 0)
+                                       argbackq = argbackq->next;
+                       } else if (c == CTLVAR) {
+                               if ((*p++ & VSTYPE) != VSNORMAL)
+                                       nesting++;
+                       } else if (c == CTLENDVAR) {
+                               if (--nesting == 0)
+                                       break;
+                       }
+               }
+       }
+       return p;
+}
+
+
+/*
+ * Put a string on the stack.
+ */
+
+static void
+memtodest(const char *p, size_t len, int syntax, int quotes) {
+       char *q = expdest;
+
+       q = makestrspace(len * 2, q);
+
+       while (len--) {
+               int c = *p++;
+               if (!c)
+                       continue;
+               if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
+                       USTPUTC(CTLESC, q);
+               USTPUTC(c, q);
+       }
+
+       expdest = q;
+}
+
+
+static void
+strtodest(const char *p, int syntax, int quotes)
+{
+       memtodest(p, strlen(p), syntax, quotes);
+}
+
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ */
+
+static ssize_t
+varvalue(char *name, int varflags, int flags)
+{
+       int num;
+       char *p;
+       int i;
+       int sep = 0;
+       int sepq = 0;
+       ssize_t len = 0;
+       char **ap;
+       int syntax;
+       int quoted = varflags & VSQUOTE;
+       int subtype = varflags & VSTYPE;
+       int quotes = flags & (EXP_FULL | EXP_CASE);
+
+       if (quoted && (flags & EXP_FULL))
+               sep = 1 << CHAR_BIT;
+
+       syntax = quoted ? DQSYNTAX : BASESYNTAX;
+       switch (*name) {
+       case '$':
+               num = rootpid;
+               goto numvar;
+       case '?':
+               num = exitstatus;
+               goto numvar;
+       case '#':
+               num = shellparam.nparam;
+               goto numvar;
+       case '!':
+               num = backgndpid;
+               if (num == 0)
+                       return -1;
+numvar:
+               len = cvtnum(num);
+               break;
+       case '-':
+               p = makestrspace(NOPTS, expdest);
+               for (i = NOPTS - 1; i >= 0; i--) {
+                       if (optlist[i]) {
+                               USTPUTC(optletters(i), p);
+                               len++;
+                       }
+               }
+               expdest = p;
+               break;
+       case '@':
+               if (sep)
+                       goto param;
+               /* fall through */
+       case '*':
+               sep = ifsset() ? ifsval()[0] : ' ';
+               if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
+                       sepq = 1;
+param:
+               if (!(ap = shellparam.p))
+                       return -1;
+               while ((p = *ap++)) {
+                       size_t partlen;
+
+                       partlen = strlen(p);
+
+                       len += partlen;
+                       if (len > partlen && sep) {
+                               char *q;
+
+                               len++;
+                               if (subtype == VSPLUS || subtype == VSLENGTH) {
+                                       continue;
+                               }
+                               q = expdest;
+                               if (sepq)
+                                       STPUTC(CTLESC, q);
+                               STPUTC(sep, q);
+                               expdest = q;
+                       }
+
+                       if (!(subtype == VSPLUS || subtype == VSLENGTH))
+                               memtodest(p, partlen, syntax, quotes);
+               }
+               return len;
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+               num = atoi(name);
+               if (num < 0 || num > shellparam.nparam)
+                       return -1;
+               p = num ? shellparam.p[num - 1] : arg0;
+               goto value;
+       default:
+               p = lookupvar(name);
+value:
+               if (!p)
+                       return -1;
+
+               len = strlen(p);
+               if (!(subtype == VSPLUS || subtype == VSLENGTH))
+                       memtodest(p, len, syntax, quotes);
+               return len;
+       }
+
+       if (subtype == VSPLUS || subtype == VSLENGTH)
+               STADJUST(-len, expdest);
+       return len;
+}
+
+
+/*
+ * Record the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+
+static void
+recordregion(int start, int end, int nulonly)
+{
+       struct ifsregion *ifsp;
+
+       if (ifslastp == NULL) {
+               ifsp = &ifsfirst;
+       } else {
+               INTOFF;
+               ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
+               ifsp->next = NULL;
+               ifslastp->next = ifsp;
+               INTON;
+       }
+       ifslastp = ifsp;
+       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;
+       char *p;
+       char *q;
+       const char *ifs, *realifs;
+       int ifsspc;
+       int nulonly;
+
+
+       start = string;
+       if (ifslastp != NULL) {
+               ifsspc = 0;
+               nulonly = 0;
+               realifs = ifsset() ? ifsval() : defifs;
+               ifsp = &ifsfirst;
+               do {
+                       p = string + ifsp->begoff;
+                       nulonly = ifsp->nulonly;
+                       ifs = nulonly ? nullstr : realifs;
+                       ifsspc = 0;
+                       while (p < string + ifsp->endoff) {
+                               q = p;
+                               if (*p == CTLESC)
+                                       p++;
+                               if (strchr(ifs, *p)) {
+                                       if (!nulonly)
+                                               ifsspc = (strchr(defifs, *p) != NULL);
+                                       /* Ignore IFS whitespace at start */
+                                       if (q == start && ifsspc) {
+                                               p++;
+                                               start = p;
+                                               continue;
+                                       }
+                                       *q = '\0';
+                                       sp = (struct strlist *)stalloc(sizeof *sp);
+                                       sp->text = start;
+                                       *arglist->lastp = sp;
+                                       arglist->lastp = &sp->next;
+                                       p++;
+                                       if (!nulonly) {
+                                               for (;;) {
+                                                       if (p >= string + ifsp->endoff) {
+                                                               break;
+                                                       }
+                                                       q = p;
+                                                       if (*p == CTLESC)
+                                                               p++;
+                                                       if (strchr(ifs, *p) == NULL ) {
+                                                               p = q;
+                                                               break;
+                                                       } else if (strchr(defifs, *p) == NULL) {
+                                                               if (ifsspc) {
+                                                                       p++;
+                                                                       ifsspc = 0;
+                                                               } else {
+                                                                       p = q;
+                                                                       break;
+                                                               }
+                                                       } else
+                                                               p++;
+                                               }
+                                       }
+                                       start = p;
+                               } else
+                                       p++;
+                       }
+               } while ((ifsp = ifsp->next) != NULL);
+               if (nulonly)
+                       goto add;
+       }
+
+       if (!*start)
+               return;
+
+add:
+       sp = (struct strlist *)stalloc(sizeof *sp);
+       sp->text = start;
+       *arglist->lastp = sp;
+       arglist->lastp = &sp->next;
+}
+
+static void
+ifsfree(void)
+{
+       struct ifsregion *p;
+
+       INTOFF;
+       p = ifsfirst.next;
+       do {
+               struct ifsregion *ifsp;
+               ifsp = p->next;
+               ckfree(p);
+               p = ifsp;
+       } while (p);
+       ifslastp = NULL;
+       ifsfirst.next = NULL;
+       INTON;
+}
+
+static void expmeta(char *, char *);
+static struct strlist *expsort(struct strlist *);
+static struct strlist *msort(struct strlist *, int);
+
+static char *expdir;
+
+
+static void
+expandmeta(struct strlist *str, int flag)
+{
+       static const char metachars[] = {
+               '*', '?', '[', 0
+       };
+       /* TODO - EXP_REDIR */
+
+       while (str) {
+               struct strlist **savelastp;
+               struct strlist *sp;
+               char *p;
+
+               if (fflag)
+                       goto nometa;
+               if (!strpbrk(str->text, metachars))
+                       goto nometa;
+               savelastp = exparg.lastp;
+
+               INTOFF;
+               p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
+               {
+                       int i = strlen(str->text);
+                       expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+               }
+
+               expmeta(expdir, p);
+               ckfree(expdir);
+               if (p != str->text)
+                       ckfree(p);
+               INTON;
+               if (exparg.lastp == savelastp) {
+                       /*
+                        * no matches
+                        */
+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;
+       }
+}
+
+/*
+ * Add a file name to the list.
+ */
+
+static void
+addfname(const char *name)
+{
+       struct strlist *sp;
+
+       sp = (struct strlist *)stalloc(sizeof *sp);
+       sp->text = sstrdup(name);
+       *exparg.lastp = sp;
+       exparg.lastp = &sp->next;
+}
+
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+
+static void
+expmeta(char *enddir, char *name)
+{
+       char *p;
+       const char *cp;
+       char *start;
+       char *endname;
+       int metaflag;
+       struct stat statb;
+       DIR *dirp;
+       struct dirent *dp;
+       int atend;
+       int matchdot;
+
+       metaflag = 0;
+       start = name;
+       for (p = name; *p; p++) {
+               if (*p == '*' || *p == '?')
+                       metaflag = 1;
+               else if (*p == '[') {
+                       char *q = p + 1;
+                       if (*q == '!')
+                               q++;
+                       for (;;) {
+                               if (*q == '\\')
+                                       q++;
+                               if (*q == '/' || *q == '\0')
+                                       break;
+                               if (*++q == ']') {
+                                       metaflag = 1;
+                                       break;
+                               }
+                       }
+               } else if (*p == '\\')
+                       p++;
+               else if (*p == '/') {
+                       if (metaflag)
+                               goto out;
+                       start = p + 1;
+               }
+       }
+out:
+       if (metaflag == 0) {    /* we've reached the end of the file name */
+               if (enddir != expdir)
+                       metaflag++;
+               p = name;
+               do {
+                       if (*p == '\\')
+                               p++;
+                       *enddir++ = *p;
+               } while (*p++);
+               if (metaflag == 0 || lstat(expdir, &statb) >= 0)
+                       addfname(expdir);
+               return;
+       }
+       endname = p;
+       if (name < start) {
+               p = name;
+               do {
+                       if (*p == '\\')
+                               p++;
+                       *enddir++ = *p++;
+               } while (p < start);
+       }
+       if (enddir == expdir) {
+               cp = ".";
+       } else if (enddir == expdir + 1 && *expdir == '/') {
+               cp = "/";
+       } else {
+               cp = expdir;
+               enddir[-1] = '\0';
+       }
+       if ((dirp = opendir(cp)) == NULL)
+               return;
+       if (enddir != expdir)
+               enddir[-1] = '/';
+       if (*endname == 0) {
+               atend = 1;
+       } else {
+               atend = 0;
+               *endname++ = '\0';
+       }
+       matchdot = 0;
+       p = start;
+       if (*p == '\\')
+               p++;
+       if (*p == '.')
+               matchdot++;
+       while (! intpending && (dp = readdir(dirp)) != NULL) {
+               if (dp->d_name[0] == '.' && ! matchdot)
+                       continue;
+               if (pmatch(start, dp->d_name)) {
+                       if (atend) {
+                               scopy(dp->d_name, enddir);
+                               addfname(expdir);
+                       } else {
+                               for (p = enddir, cp = dp->d_name;
+                                    (*p++ = *cp++) != '\0';)
+                                       continue;
+                               p[-1] = '/';
+                               expmeta(p, endname);
+                       }
+               }
+       }
+       closedir(dirp);
+       if (! atend)
+               endname[-1] = '/';
+}
+
+/*
+ * 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 (;;) {
+#ifdef CONFIG_LOCALE_SUPPORT
+               if (strcoll(p->text, q->text) < 0)
+#else
+               if (strcmp(p->text, q->text) < 0)
+#endif
+                                               {
+                       *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.
+ */
+
+static inline int
+patmatch(char *pattern, const char *string)
+{
+       return pmatch(preglob(pattern, 0, 0), string);
+}
+
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+
+static char *
+_rmescapes(char *str, int flag)
+{
+       char *p, *q, *r;
+       static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
+       unsigned inquotes;
+       int notescaped;
+       int globbing;
+
+       p = strpbrk(str, qchars);
+       if (!p) {
+               return str;
+       }
+       q = p;
+       r = str;
+       if (flag & RMESCAPE_ALLOC) {
+               size_t len = p - str;
+               size_t fulllen = len + strlen(p) + 1;
+
+               if (flag & RMESCAPE_GROW) {
+                       r = makestrspace(fulllen, expdest);
+               } else if (flag & RMESCAPE_HEAP) {
+                       r = ckmalloc(fulllen);
+               } else {
+                       r = stalloc(fulllen);
+               }
+               q = r;
+               if (len > 0) {
+                       q = mempcpy(q, str, len);
+               }
+       }
+       inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
+       globbing = flag & RMESCAPE_GLOB;
+       notescaped = globbing;
+       while (*p) {
+               if (*p == CTLQUOTEMARK) {
+                       inquotes = ~inquotes;
+                       p++;
+                       notescaped = globbing;
+                       continue;
+               }
+               if (*p == '\\') {
+                       /* naked back slash */
+                       notescaped = 0;
+                       goto copy;
+               }
+               if (*p == CTLESC) {
+                       p++;
+                       if (notescaped && inquotes && *p != '/') {
+                               *q++ = '\\';
+                       }
+               }
+               notescaped = globbing;
+copy:
+               *q++ = *p++;
+       }
+       *q = '\0';
+       if (flag & RMESCAPE_GROW) {
+               expdest = r;
+               STADJUST(q - r + 1, expdest);
+       }
+       return r;
+}
+
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+
+int
+casematch(union node *pattern, char *val)
+{
+       struct stackmark smark;
+       int result;
+
+       setstackmark(&smark);
+       argbackq = pattern->narg.backquote;
+       STARTSTACKSTR(expdest);
+       ifslastp = NULL;
+       argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+       STACKSTRNUL(expdest);
+       result = patmatch(stackblock(), val);
+       popstackmark(&smark);
+       return result;
+}
+
+/*
+ * Our own itoa().
+ */
+
+static int
+cvtnum(arith_t num)
+{
+       int len;
+
+       expdest = makestrspace(32, expdest);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+       len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
+       len = fmtstr(expdest, 32, "%ld", num);
+#endif
+       STADJUST(len, expdest);
+       return len;
+}
+
+static void
+varunset(const char *end, const char *var, const char *umsg, int varflags)
+{
+       const char *msg;
+       const char *tail;
+
+       tail = nullstr;
+       msg = "parameter not set";
+       if (umsg) {
+               if (*end == CTLENDVAR) {
+                       if (varflags & VSNUL)
+                               tail = " or null";
+               } else
+                       msg = umsg;
+       }
+       error("%.*s: %s%s", end - var - 1, var, msg, tail);
+}
+
+
+/*      $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $      */
+
+/*
+ * This implements the input routines used by the parser.
+ */
+
+#define EOF_NLEFT -99           /* value of parsenleft when EOF pushed back */
+#define IBUFSIZ (BUFSIZ + 1)
+
+static void pushfile(void);
+
+/*
+ * Read a character from the script, returning PEOF on end of file.
+ * Nul characters in the input are silently discarded.
+ */
+
+#define pgetc_as_macro()   (--parsenleft >= 0? *parsenextc++ : preadbuffer())
+
+#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
+#define pgetc_macro() pgetc()
+static int
+pgetc(void)
+{
+       return pgetc_as_macro();
+}
+#else
+#define pgetc_macro()   pgetc_as_macro()
+static int
+pgetc(void)
+{
+       return pgetc_macro();
+}
+#endif
+
+
+/*
+ * Same as pgetc(), but ignores PEOA.
+ */
+#ifdef CONFIG_ASH_ALIAS
+static int pgetc2(void)
+{
+       int c;
+
+       do {
+               c = pgetc_macro();
+       } while (c == PEOA);
+       return c;
+}
+#else
+static inline int pgetc2(void)
+{
+       return pgetc_macro();
+}
+#endif
+
+/*
+ * Read a line from the script.
+ */
+
+static inline char *
+pfgets(char *line, int len)
+{
+       char *p = line;
+       int nleft = len;
+       int c;
+
+       while (--nleft > 0) {
+               c = pgetc2();
+               if (c == PEOF) {
+                       if (p == line)
+                               return NULL;
+                       break;
+               }
+               *p++ = c;
+               if (c == '\n')
+                       break;
+       }
+       *p = '\0';
+       return line;
+}
+
+
+
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+static const char *cmdedit_prompt;
+static inline void putprompt(const char *s)
+{
+       cmdedit_prompt = s;
+}
+#else
+static inline void putprompt(const char *s)
+{
+       out2str(s);
+}
+#endif
+
+static inline int
+preadfd(void)
+{
+       int nr;
+       char *buf =  parsefile->buf;
+       parsenextc = buf;
+
+retry:
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+       if (!iflag || parsefile->fd)
+               nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+       else {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+               cmdedit_path_lookup = pathval();
+#endif
+               nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
+               if(nr == 0) {
+                       /* Ctrl+C presend */
+                       if(trap[SIGINT]) {
+                               buf[0] = '\n';
+                               buf[1] = 0;
+                               raise(SIGINT);
+                               return 1;
+                       }
+                       goto retry;
+               }
+               if(nr < 0 && errno == 0) {
+                       /* Ctrl+D presend */
+                       nr = 0;
+               }
+       }
+#else
+       nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+#endif
+
+       if (nr < 0) {
+               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;
+                               }
+                       }
+               }
+       }
+       return nr;
+}
+
+/*
+ * Refill the input buffer and return the next input character:
+ *
+ * 1) If a string was pushed back on the input, pop it;
+ * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
+ *    from a string so we can't refill the buffer, return EOF.
+ * 3) If the is more stuff in this buffer, use it else call read to fill it.
+ * 4) Process input up to the next newline, deleting nul characters.
+ */
+
+int
+preadbuffer(void)
+{
+       char *p, *q;
+       int more;
+       char savec;
+
+       while (parsefile->strpush) {
+#ifdef CONFIG_ASH_ALIAS
+               if (parsenleft == -1 && parsefile->strpush->ap &&
+                       parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
+                       return PEOA;
+               }
+#endif
+               popstring();
+               if (--parsenleft >= 0)
+                       return (*parsenextc++);
+       }
+       if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+               return PEOF;
+       flushall();
+
+again:
+       if (parselleft <= 0) {
+               if ((parselleft = preadfd()) <= 0) {
+                       parselleft = parsenleft = EOF_NLEFT;
+                       return PEOF;
+               }
+       }
+
+       q = p = parsenextc;
+
+       /* delete nul characters */
+       for (more = 1; more;) {
+               switch (*p) {
+               case '\0':
+                       p++;    /* Skip nul */
+                       goto check;
+
+               case '\n':
+                       parsenleft = q - parsenextc;
+                       more = 0; /* Stop processing here */
+                       break;
+
+               }
+
+               *q++ = *p++;
+check:
+               if (--parselleft <= 0 && more) {
+                       parsenleft = q - parsenextc - 1;
+                       if (parsenleft < 0)
+                               goto again;
+                       more = 0;
+               }
+       }
+
+       savec = *q;
+       *q = '\0';
+
+       if (vflag) {
+               out2str(parsenextc);
+       }
+
+       *q = savec;
+
+       return *parsenextc++;
+}
+
+/*
+ * Undo the last call to pgetc.  Only one character may be pushed back.
+ * PEOF may be pushed back.
+ */
+
+void
+pungetc(void)
+{
+       parsenleft++;
+       parsenextc--;
+}
+
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+void
+pushstring(char *s, void *ap)
+{
+       struct strpush *sp;
+       size_t len;
+
+       len = strlen(s);
+       INTOFF;
+/*dprintf("*** 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;
+#ifdef CONFIG_ASH_ALIAS
+       sp->ap = (struct alias *)ap;
+       if (ap) {
+               ((struct alias *)ap)->flag |= ALIASINUSE;
+               sp->string = s;
+       }
+#endif
+       parsenextc = s;
+       parsenleft = len;
+       INTON;
+}
+
+void
+popstring(void)
+{
+       struct strpush *sp = parsefile->strpush;
+
+       INTOFF;
+#ifdef CONFIG_ASH_ALIAS
+       if (sp->ap) {
+               if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+                       checkkwd |= CHKALIAS;
+               }
+               if (sp->string != sp->ap->val) {
+                       ckfree(sp->string);
+               }
+               sp->ap->flag &= ~ALIASINUSE;
+               if (sp->ap->flag & ALIASDEAD) {
+                       unalias(sp->ap->name);
+               }
+       }
+#endif
+       parsenextc = sp->prevstring;
+       parsenleft = sp->prevnleft;
+/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+       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(const char *fname, int push)
+{
+       int fd;
+       int fd2;
+
+       INTOFF;
+       if ((fd = open(fname, O_RDONLY)) < 0)
+               error("Can't open %s", fname);
+       if (fd < 10) {
+               fd2 = copyfd(fd, 10);
+               close(fd);
+               if (fd2 < 0)
+                       error("Out of file descriptors");
+               fd = fd2;
+       }
+       setinputfd(fd, push);
+       INTON;
+}
+
+
+/*
+ * Like setinputfile, but takes an open file descriptor.  Call this with
+ * interrupts off.
+ */
+
+static void
+setinputfd(int fd, int push)
+{
+       (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+       if (push) {
+               pushfile();
+               parsefile->buf = 0;
+       }
+       parsefile->fd = fd;
+       if (parsefile->buf == NULL)
+               parsefile->buf = ckmalloc(IBUFSIZ);
+       parselleft = parsenleft = 0;
+       plinno = 1;
+}
+
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+
+static void
+setinputstring(char *string)
+{
+       INTOFF;
+       pushfile();
+       parsenextc = string;
+       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(void)
+{
+       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;
+}
+
+
+static void
+popfile(void)
+{
+       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.
+ */
+
+static void
+popallfiles(void)
+{
+       while (parsefile != &basepf)
+               popfile();
+}
+
+
+/*
+ * Close the file(s) that the shell is reading commands from.  Called
+ * after a fork is done.
+ */
+
+static void
+closescript(void)
+{
+       popallfiles();
+       if (parsefile->fd > 0) {
+               close(parsefile->fd);
+               parsefile->fd = 0;
+       }
+}
+
+/*      $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $    */
+
+/* mode flags for set_curjob */
+#define CUR_DELETE 2
+#define CUR_RUNNING 1
+#define CUR_STOPPED 0
+
+/* mode flags for dowait */
+#define DOWAIT_NORMAL 0
+#define DOWAIT_BLOCK 1
+
+/* array of jobs */
+static struct job *jobtab;
+/* size of array */
+static unsigned njobs;
+#if JOBS
+/* pgrp of shell on invocation */
+static int initialpgrp;
+static int ttyfd = -1;
+#endif
+/* current job */
+static struct job *curjob;
+/* number of presumed living untracked jobs */
+static int jobless;
+
+static void set_curjob(struct job *, unsigned);
+#if JOBS
+static int restartjob(struct job *, int);
+static void xtcsetpgrp(int, pid_t);
+static char *commandtext(union node *);
+static void cmdlist(union node *, int);
+static void cmdtxt(union node *);
+static void cmdputs(const char *);
+static void showpipe(struct job *, FILE *);
+#endif
+static int sprint_status(char *, int, int);
+static void freejob(struct job *);
+static struct job *getjob(const char *, int);
+static struct job *growjobtab(void);
+static void forkchild(struct job *, union node *, int);
+static void forkparent(struct job *, union node *, int, pid_t);
+static int dowait(int, struct job *);
+static int getstatus(struct job *);
+
+static void
+set_curjob(struct job *jp, unsigned mode)
+{
+       struct job *jp1;
+       struct job **jpp, **curp;
+
+       /* first remove from list */
+       jpp = curp = &curjob;
+       do {
+               jp1 = *jpp;
+               if (jp1 == jp)
+                       break;
+               jpp = &jp1->prev_job;
+       } while (1);
+       *jpp = jp1->prev_job;
+
+       /* Then re-insert in correct position */
+       jpp = curp;
+       switch (mode) {
+       default:
+#ifdef DEBUG
+               abort();
+#endif
+       case CUR_DELETE:
+               /* job being deleted */
+               break;
+       case CUR_RUNNING:
+               /* newly created job or backgrounded job,
+                  put after all stopped jobs. */
+               do {
+                       jp1 = *jpp;
+#ifdef JOBS
+                       if (!jp1 || jp1->state != JOBSTOPPED)
+#endif
+                               break;
+                       jpp = &jp1->prev_job;
+               } while (1);
+               /* FALLTHROUGH */
+#ifdef JOBS
+       case CUR_STOPPED:
+#endif
+               /* newly stopped job - becomes curjob */
+               jp->prev_job = *jpp;
+               *jpp = jp;
+               break;
+       }
+}
+
+#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.
+ *
+ * Called with interrupts off.
+ */
+
+void
+setjobctl(int on)
+{
+       int fd;
+       int pgrp;
+
+       if (on == jobctl || rootshell == 0)
+               return;
+       if (on) {
+               int ofd;
+               ofd = fd = open(_PATH_TTY, O_RDWR);
+               if (fd < 0) {
+                       fd += 3;
+                       while (!isatty(fd) && --fd >= 0)
+                               ;
+               }
+               fd = fcntl(fd, F_DUPFD, 10);
+               close(ofd);
+               if (fd < 0)
+                       goto out;
+               fcntl(fd, F_SETFD, FD_CLOEXEC);
+               do { /* while we are in the background */
+                       if ((pgrp = tcgetpgrp(fd)) < 0) {
+out:
+                               sh_warnx("can't access tty; job control turned off");
+                               mflag = on = 0;
+                               goto close;
+                       }
+                       if (pgrp == getpgrp())
+                               break;
+                       killpg(0, SIGTTIN);
+               } while (1);
+               initialpgrp = pgrp;
+
+               setsignal(SIGTSTP);
+               setsignal(SIGTTOU);
+               setsignal(SIGTTIN);
+               pgrp = rootpid;
+               setpgid(0, pgrp);
+               xtcsetpgrp(fd, pgrp);
+       } else {
+               /* turning job control off */
+               fd = ttyfd;
+               pgrp = initialpgrp;
+               xtcsetpgrp(fd, pgrp);
+               setpgid(0, pgrp);
+               setsignal(SIGTSTP);
+               setsignal(SIGTTOU);
+               setsignal(SIGTTIN);
+close:
+               close(fd);
+               fd = -1;
+       }
+       ttyfd = fd;
+       jobctl = on;
+}
+
+static int
+killcmd(int argc, char **argv)
+{
+       int signo = -1;
+       int list = 0;
+       int i;
+       pid_t pid;
+       struct job *jp;
+
+       if (argc <= 1) {
+usage:
+               error(
+"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
+"kill -l [exitstatus]"
+               );
+       }
+
+       if (**++argv == '-') {
+               signo = decode_signal(*argv + 1, 1);
+               if (signo < 0) {
+                       int c;
+
+                       while ((c = nextopt("ls:")) != '\0')
+                               switch (c) {
+                               default:
+#ifdef DEBUG
+                                       abort();
+#endif
+                               case 'l':
+                                       list = 1;
+                                       break;
+                               case 's':
+                                       signo = decode_signal(optionarg, 1);
+                                       if (signo < 0) {
+                                               error(
+                                                       "invalid signal number or name: %s",
+                                                       optionarg
+                                               );
+                                       }
+                                       break;
+                               }
+                       argv = argptr;
+               } else
+                       argv++;
+       }
+
+       if (!list && signo < 0)
+               signo = SIGTERM;
+
+       if ((signo < 0 || !*argv) ^ list) {
+               goto usage;
+       }
+
+       if (list) {
+               const char *name;
+
+               if (!*argv) {
+                       for (i = 1; i < NSIG; i++) {
+                               name = u_signal_names(0, &i, 1);
+                               if (name)
+                                       out1fmt(snlfmt, name);
+                       }
+                       return 0;
+               }
+               name = u_signal_names(*argptr, &signo, -1);
+               if (name)
+                       out1fmt(snlfmt, name);
+               else
+                       error("invalid signal number or exit status: %s", *argptr);
+               return 0;
+       }
+
+       i = 0;
+       do {
+               if (**argv == '%') {
+                       jp = getjob(*argv, 0);
+                       pid = -jp->ps[0].pid;
+               } else
+                       pid = number(*argv);
+               if (kill(pid, signo) != 0) {
+                       sh_warnx("%m\n");
+                       i = 1;
+               }
+       } while (*++argv);
+
+       return i;
+}
+#endif /* JOBS */
+
+#if defined(JOBS) || defined(DEBUG)
+static int
+jobno(const struct job *jp)
+{
+       return jp - jobtab + 1;
+}
+#endif
+
+#ifdef JOBS
+static int
+fgcmd(int argc, char **argv)
+{
+       struct job *jp;
+       FILE *out;
+       int mode;
+       int retval;
+
+       mode = (**argv == 'f') ? FORK_FG : FORK_BG;
+       nextopt(nullstr);
+       argv = argptr;
+       out = stdout;
+       do {
+               jp = getjob(*argv, 1);
+               if (mode == FORK_BG) {
+                       set_curjob(jp, CUR_RUNNING);
+                       fprintf(out, "[%d] ", jobno(jp));
+               }
+               outstr(jp->ps->cmd, out);
+               showpipe(jp, out);
+               retval = restartjob(jp, mode);
+       } while (*argv && *++argv);
+       return retval;
+}
+
+static int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));
+
+
+static int
+restartjob(struct job *jp, int mode)
+{
+       struct procstat *ps;
+       int i;
+       int status;
+       pid_t pgid;
+
+       INTOFF;
+       if (jp->state == JOBDONE)
+               goto out;
+       jp->state = JOBRUNNING;
+       pgid = jp->ps->pid;
+       if (mode == FORK_FG)
+               xtcsetpgrp(ttyfd, pgid);
+       killpg(pgid, SIGCONT);
+       ps = jp->ps;
+       i = jp->nprocs;
+       do {
+               if (WIFSTOPPED(ps->status)) {
+                       ps->status = -1;
+               }
+       } while (ps++, --i);
+out:
+       status = (mode == FORK_FG) ? waitforjob(jp) : 0;
+       INTON;
+       return status;
+}
+#endif
+
+static int
+sprint_status(char *s, int status, int sigonly)
+{
+       int col;
+       int st;
+
+       col = 0;
+       if (!WIFEXITED(status)) {
+#if JOBS
+               if (WIFSTOPPED(status))
+                       st = WSTOPSIG(status);
+               else
+#endif
+                       st = WTERMSIG(status);
+               if (sigonly) {
+                       if (st == SIGINT || st == SIGPIPE)
+                               goto out;
+#if JOBS
+                       if (WIFSTOPPED(status))
+                               goto out;
+#endif
+               }
+               st &= 0x7f;
+               col = fmtstr(s, 32, strsignal(st));
+               if (WCOREDUMP(status)) {
+                       col += fmtstr(s + col, 16, " (core dumped)");
+               }
+       } else if (!sigonly) {
+               st = WEXITSTATUS(status);
+               if (st)
+                       col = fmtstr(s, 16, "Done(%d)", st);
+               else
+                       col = fmtstr(s, 16, "Done");
+       }
+
+out:
+       return col;
+}
+
+#if JOBS
+static void
+showjob(FILE *out, struct job *jp, int mode)
+{
+       struct procstat *ps;
+       struct procstat *psend;
+       int col;
+       int indent;
+       char s[80];
+
+       ps = jp->ps;
+
+       if (mode & SHOW_PGID) {
+               /* just output process (group) id of pipeline */
+               fprintf(out, "%d\n", ps->pid);
+               return;
+       }
+
+       col = fmtstr(s, 16, "[%d]   ", jobno(jp));
+       indent = col;
+
+       if (jp == curjob)
+               s[col - 2] = '+';
+       else if (curjob && jp == curjob->prev_job)
+               s[col - 2] = '-';
+
+       if (mode & SHOW_PID)
+               col += fmtstr(s + col, 16, "%d ", ps->pid);
+
+       psend = ps + jp->nprocs;
+
+       if (jp->state == JOBRUNNING) {
+               scopy("Running", s + col);
+               col += strlen("Running");
+       } else {
+               int status = psend[-1].status;
+#if JOBS
+               if (jp->state == JOBSTOPPED)
+                       status = jp->stopstatus;
+#endif
+               col += sprint_status(s + col, status, 0);
+       }
+
+       goto start;
+
+       do {
+               /* for each process */
+               col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
+
+start:
+               fprintf(out, "%s%*c%s",
+                       s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
+               );
+               if (!(mode & SHOW_PID)) {
+                       showpipe(jp, out);
+                       break;
+               }
+               if (++ps == psend) {
+                       outcslow('\n', out);
+                       break;
+               }
+       } while (1);
+
+       jp->changed = 0;
+
+       if (jp->state == JOBDONE) {
+               TRACE(("showjob: freeing job %d\n", jobno(jp)));
+               freejob(jp);
+       }
+}
+
+
+static int
+jobscmd(int argc, char **argv)
+{
+       int mode, m;
+       FILE *out;
+
+       mode = 0;
+       while ((m = nextopt("lp")))
+               if (m == 'l')
+                       mode = SHOW_PID;
+               else
+                       mode = SHOW_PGID;
+
+       out = stdout;
+       argv = argptr;
+       if (*argv)
+               do
+                       showjob(out, getjob(*argv,0), mode);
+               while (*++argv);
+       else
+               showjobs(out, mode);
+
+       return 0;
+}
+
+
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+
+static void
+showjobs(FILE *out, int mode)
+{
+       struct job *jp;
+
+       TRACE(("showjobs(%x) called\n", mode));
+
+       /* If not even one one job changed, there is nothing to do */
+       while (dowait(DOWAIT_NORMAL, NULL) > 0)
+               continue;
+
+       for (jp = curjob; jp; jp = jp->prev_job) {
+               if (!(mode & SHOW_CHANGED) || jp->changed)
+                       showjob(out, jp, mode);
+       }
+}
+#endif /* JOBS */
+
+/*
+ * 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;
+       set_curjob(jp, CUR_DELETE);
+       INTON;
+}
+
+
+static int
+waitcmd(int argc, char **argv)
+{
+       struct job *job;
+       int retval;
+       struct job *jp;
+
+       EXSIGON();
+
+       nextopt(nullstr);
+       retval = 0;
+
+       argv = argptr;
+       if (!*argv) {
+               /* wait for all jobs */
+               for (;;) {
+                       jp = curjob;
+                       while (1) {
+                               if (!jp) {
+                                       /* no running procs */
+                                       goto out;
+                               }
+                               if (jp->state == JOBRUNNING)
+                                       break;
+                               jp->waited = 1;
+                               jp = jp->prev_job;
+                       }
+                       dowait(DOWAIT_BLOCK, 0);
+               }
+       }
+
+       retval = 127;
+       do {
+               if (**argv != '%') {
+                       pid_t pid = number(*argv);
+                       job = curjob;
+                       goto start;
+                       do {
+                               if (job->ps[job->nprocs - 1].pid == pid)
+                                       break;
+                               job = job->prev_job;
+start:
+                               if (!job)
+                                       goto repeat;
+                       } while (1);
+               } else
+                       job = getjob(*argv, 0);
+               /* loop until process terminated or stopped */
+               while (job->state == JOBRUNNING)
+                       dowait(DOWAIT_BLOCK, 0);
+               job->waited = 1;
+               retval = getstatus(job);
+repeat:
+               ;
+       } while (*++argv);
+
+out:
+       return retval;
+}
+
+
+/*
+ * Convert a job name to a job structure.
+ */
+
+static struct job *
+getjob(const char *name, int getctl)
+{
+       struct job *jp;
+       struct job *found;
+       const char *err_msg = "No such job: %s";
+       unsigned num;
+       int c;
+       const char *p;
+       char *(*match)(const char *, const char *);
+
+       jp = curjob;
+       p = name;
+       if (!p)
+               goto currentjob;
+
+       if (*p != '%')
+               goto err;
+
+       c = *++p;
+       if (!c)
+               goto currentjob;
+
+       if (!p[1]) {
+               if (c == '+' || c == '%') {
+currentjob:
+                       err_msg = "No current job";
+                       goto check;
+               } else if (c == '-') {
+                       if (jp)
+                               jp = jp->prev_job;
+                       err_msg = "No previous job";
+check:
+                       if (!jp)
+                               goto err;
+                       goto gotit;
+               }
+       }
+
+       if (is_number(p)) {
+               num = atoi(p);
+               if (num < njobs) {
+                       jp = jobtab + num - 1;
+                       if (jp->used)
+                               goto gotit;
+                       goto err;
+               }
+       }
+
+       match = prefix;
+       if (*p == '?') {
+               match = strstr;
+               p++;
+       }
+
+       found = 0;
+       while (1) {
+               if (!jp)
+                       goto err;
+               if (match(jp->ps[0].cmd, p)) {
+                       if (found)
+                               goto err;
+                       found = jp;
+                       err_msg = "%s: ambiguous";
+               }
+               jp = jp->prev_job;
+       }
+
+gotit:
+#if JOBS
+       err_msg = "job %s not created under job control";
+       if (getctl && jp->jobctl == 0)
+               goto err;
+#endif
+       return jp;
+err:
+       error(err_msg, name);
+}
+
+
+/*
+ * Return a new job structure.
+ * Called with interrupts off.
+ */
+
+static struct job *
+makejob(union node *node, int nprocs)
+{
+       int i;
+       struct job *jp;
+
+       for (i = njobs, jp = jobtab ; ; jp++) {
+               if (--i < 0) {
+                       jp = growjobtab();
+                       break;
+               }
+               if (jp->used == 0)
+                       break;
+               if (jp->state != JOBDONE || !jp->waited)
+                       continue;
+#if JOBS
+               if (jobctl)
+                       continue;
+#endif
+               freejob(jp);
+               break;
+       }
+       memset(jp, 0, sizeof(*jp));
+#if JOBS
+       if (jobctl)
+               jp->jobctl = 1;
+#endif
+       jp->prev_job = curjob;
+       curjob = jp;
+       jp->used = 1;
+       jp->ps = &jp->ps0;
+       if (nprocs > 1) {
+               jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
+       }
+       TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+           jobno(jp)));
+       return jp;
+}
+
+static struct job *
+growjobtab(void)
+{
+       size_t len;
+       ptrdiff_t offset;
+       struct job *jp, *jq;
+
+       len = njobs * sizeof(*jp);
+       jq = jobtab;
+       jp = ckrealloc(jq, len + 4 * sizeof(*jp));
+
+       offset = (char *)jp - (char *)jq;
+       if (offset) {
+               /* Relocate pointers */
+               size_t l = len;
+
+               jq = (struct job *)((char *)jq + l);
+               while (l) {
+                       l -= sizeof(*jp);
+                       jq--;
+#define joff(p) ((struct job *)((char *)(p) + l))
+#define jmove(p) (p) = (void *)((char *)(p) + offset)
+                       if (xlikely(joff(jp)->ps == &jq->ps0))
+                               jmove(joff(jp)->ps);
+                       if (joff(jp)->prev_job)
+                               jmove(joff(jp)->prev_job);
+               }
+               if (curjob)
+                       jmove(curjob);
+#undef joff
+#undef jmove
+       }
+
+       njobs += 4;
+       jobtab = jp;
+       jp = (struct job *)((char *)jp + len);
+       jq = jp + 3;
+       do {
+               jq->used = 0;
+       } while (--jq >= jp);
+       return jp;
+}
+
+
+/*
+ * Fork off a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *      FORK_FG - Fork off a foreground process.
+ *      FORK_BG - Fork off a background process.
+ *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *                   process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ *
+ * Called with interrupts off.
+ */
+
+static inline void
+forkchild(struct job *jp, union node *n, int mode)
+{
+       int wasroot;
+
+       TRACE(("Child shell %d\n", getpid()));
+       wasroot = rootshell;
+       rootshell = 0;
+
+       closescript();
+       clear_traps();
+#if JOBS
+       /* do job control only in root shell */
+       jobctl = 0;
+       if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
+               pid_t pgrp;
+
+               if (jp->nprocs == 0)
+                       pgrp = getpid();
+               else
+                       pgrp = jp->ps[0].pid;
+               /* This can fail because we are doing it in the parent also */
+               (void)setpgid(0, pgrp);
+               if (mode == FORK_FG)
+                       xtcsetpgrp(ttyfd, pgrp);
+               setsignal(SIGTSTP);
+               setsignal(SIGTTOU);
+       } else
+#endif
+       if (mode == FORK_BG) {
+               ignoresig(SIGINT);
+               ignoresig(SIGQUIT);
+               if (jp->nprocs == 0) {
+                       close(0);
+                       if (open(_PATH_DEVNULL, O_RDONLY) != 0)
+                               error("Can't open %s", _PATH_DEVNULL);
+               }
+       }
+       if (wasroot && iflag) {
+               setsignal(SIGINT);
+               setsignal(SIGQUIT);
+               setsignal(SIGTERM);
+       }
+       for (jp = curjob; jp; jp = jp->prev_job)
+               freejob(jp);
+       jobless = 0;
+}
+
+static inline void
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
+{
+       TRACE(("In parent shell:  child = %d\n", pid));
+       if (!jp) {
+               while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
+               jobless++;
+               return;
+       }
+#if JOBS
+       if (mode != FORK_NOJOB && jp->jobctl) {
+               int pgrp;
+
+               if (jp->nprocs == 0)
+                       pgrp = pid;
+               else
+                       pgrp = jp->ps[0].pid;
+               /* This can fail because we are doing it in the child also */
+               (void)setpgid(pid, pgrp);
+       }
+#endif
+       if (mode == FORK_BG) {
+               backgndpid = pid;               /* set $! */
+               set_curjob(jp, CUR_RUNNING);
+       }
+       if (jp) {
+               struct procstat *ps = &jp->ps[jp->nprocs++];
+               ps->pid = pid;
+               ps->status = -1;
+               ps->cmd = nullstr;
+#if JOBS
+               if (jobctl && n)
+                       ps->cmd = commandtext(n);
+#endif
+       }
+}
+
+static int
+forkshell(struct job *jp, union node *n, int mode)
+{
+       int pid;
+
+       TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+       pid = fork();
+       if (pid < 0) {
+               TRACE(("Fork failed, errno=%d", errno));
+               if (jp)
+                       freejob(jp);
+               error("Cannot fork");
+       }
+       if (pid == 0)
+               forkchild(jp, n, mode);
+       else
+               forkparent(jp, n, mode, pid);
+       return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell.  This means that an infinite loop started by an 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
+ * foreground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ *
+ * Called with interrupts off.
+ */
+
+int
+waitforjob(struct job *jp)
+{
+       int st;
+
+       TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+       while (jp->state == JOBRUNNING) {
+               dowait(DOWAIT_BLOCK, jp);
+       }
+       st = getstatus(jp);
+#if JOBS
+       if (jp->jobctl) {
+               xtcsetpgrp(ttyfd, rootpid);
+               /*
+                * This is truly gross.
+                * If we're doing job control, then we did a TIOCSPGRP which
+                * caused us (the shell) to no longer be in the controlling
+                * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
+                * intuit from the subprocess exit status whether a SIGINT
+                * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
+                */
+               if (jp->sigint)
+                       raise(SIGINT);
+       }
+       if (jp->state == JOBDONE)
+#endif
+               freejob(jp);
+       return st;
+}
+
+
+/*
+ * 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.
+ */
+
+static inline int
+waitproc(int block, int *status)
+{
+       int flags = 0;
+
+#if JOBS
+       if (jobctl)
+               flags |= WUNTRACED;
+#endif
+       if (block == 0)
+               flags |= WNOHANG;
+       return wait3(status, flags, (struct rusage *)NULL);
+}
+
+/*
+ * Wait for a process to terminate.
+ */
+
+static int
+dowait(int block, struct job *job)
+{
+       int pid;
+       int status;
+       struct job *jp;
+       struct job *thisjob;
+       int state;
+
+       TRACE(("dowait(%d) called\n", block));
+       pid = waitproc(block, &status);
+       TRACE(("wait returns pid %d, status=%d\n", pid, status));
+       if (pid <= 0)
+               return pid;
+       INTOFF;
+       thisjob = NULL;
+       for (jp = curjob; jp; jp = jp->prev_job) {
+               struct procstat *sp;
+               struct procstat *spend;
+               if (jp->state == JOBDONE)
+                       continue;
+               state = JOBDONE;
+               spend = jp->ps + jp->nprocs;
+               sp = jp->ps;
+               do {
+                       if (sp->pid == pid) {
+                               TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
+                               sp->status = status;
+                               thisjob = jp;
+                       }
+                       if (sp->status == -1)
+                               state = JOBRUNNING;
+#ifdef JOBS
+                       if (state == JOBRUNNING)
+                               continue;
+                       if (WIFSTOPPED(sp->status)) {
+                               jp->stopstatus = sp->status;
+                               state = JOBSTOPPED;
+                       }
+#endif
+               } while (++sp < spend);
+               if (thisjob)
+                       goto gotjob;
+       }
+#ifdef JOBS
+       if (!WIFSTOPPED(status))
+#endif
+
+               jobless--;
+       goto out;
+
+gotjob:
+       if (state != JOBRUNNING) {
+               thisjob->changed = 1;
+
+               if (thisjob->state != state) {
+                       TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
+                       thisjob->state = state;
+#ifdef JOBS
+                       if (state == JOBSTOPPED) {
+                               set_curjob(thisjob, CUR_STOPPED);
+                       }
+#endif
+               }
+       }
+
+out:
+       INTON;
+
+       if (thisjob && thisjob == job) {
+               char s[48 + 1];
+               int len;
+
+               len = sprint_status(s, status, 1);
+               if (len) {
+                       s[len] = '\n';
+                       s[len + 1] = 0;
+                       out2str(s);
+               }
+       }
+       return pid;
+}
+
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+
+int
+stoppedjobs(void)
+{
+       struct job *jp;
+       int retval;
+
+       retval = 0;
+       if (job_warning)
+               goto out;
+       jp = curjob;
+       if (jp && jp->state == JOBSTOPPED) {
+               out2str("You have stopped jobs.\n");
+               job_warning = 2;
+               retval++;
+       }
+
+out:
+       return retval;
+}
+
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command).
+ */
+
+#if JOBS
+static char *cmdnextc;
+
+static char *
+commandtext(union node *n)
+{
+       char *name;
+
+       STARTSTACKSTR(cmdnextc);
+       cmdtxt(n);
+       name = stackblock();
+       TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
+               name, cmdnextc, cmdnextc));
+       return savestr(name);
+}
+
+static void
+cmdtxt(union node *n)
+{
+       union node *np;
+       struct nodelist *lp;
+       const char *p;
+       char s[2];
+
+       if (!n)
+               return;
+       switch (n->type) {
+       default:
+#if DEBUG
+               abort();
+#endif
+       case NPIPE:
+               lp = n->npipe.cmdlist;
+               for (;;) {
+                       cmdtxt(lp->n);
+                       lp = lp->next;
+                       if (!lp)
+                               break;
+                       cmdputs(" | ");
+               }
+               break;
+       case NSEMI:
+               p = "; ";
+               goto binop;
+       case NAND:
+               p = " && ";
+               goto binop;
+       case NOR:
+               p = " || ";
+binop:
+               cmdtxt(n->nbinary.ch1);
+               cmdputs(p);
+               n = n->nbinary.ch2;
+               goto donode;
+       case NREDIR:
+       case NBACKGND:
+               n = n->nredir.n;
+               goto donode;
+       case NNOT:
+               cmdputs("!");
+               n = n->nnot.com;
+donode:
+               cmdtxt(n);
+               break;
+       case NIF:
+               cmdputs("if ");
+               cmdtxt(n->nif.test);
+               cmdputs("; then ");
+               n = n->nif.ifpart;
+               if (n->nif.elsepart) {
+                       cmdtxt(n);
+                       cmdputs("; else ");
+                       n = n->nif.elsepart;
+               }
+               p = "; fi";
+               goto dotail;
+       case NSUBSHELL:
+               cmdputs("(");
+               n = n->nredir.n;
+               p = ")";
+               goto dotail;
+       case NWHILE:
+               p = "while ";
+               goto until;
+       case NUNTIL:
+               p = "until ";
+until:
+               cmdputs(p);
+               cmdtxt(n->nbinary.ch1);
+               n = n->nbinary.ch2;
+               p = "; done";
+dodo:
+               cmdputs("; do ");
+dotail:
+               cmdtxt(n);
+               goto dotail2;
+       case NFOR:
+               cmdputs("for ");
+               cmdputs(n->nfor.var);
+               cmdputs(" in ");
+               cmdlist(n->nfor.args, 1);
+               n = n->nfor.body;
+               p = "; done";
+               goto dodo;
+       case NDEFUN:
+               cmdputs(n->narg.text);
+               p = "() { ... }";
+               goto dotail2;
+       case NCMD:
+               cmdlist(n->ncmd.args, 1);
+               cmdlist(n->ncmd.redirect, 0);
+               break;
+       case NARG:
+               p = n->narg.text;
+dotail2:
+               cmdputs(p);
+               break;
+       case NHERE:
+       case NXHERE:
+               p = "<<...";
+               goto dotail2;
+       case NCASE:
+               cmdputs("case ");
+               cmdputs(n->ncase.expr->narg.text);
+               cmdputs(" in ");
+               for (np = n->ncase.cases; np; np = np->nclist.next) {
+                       cmdtxt(np->nclist.pattern);
+                       cmdputs(") ");
+                       cmdtxt(np->nclist.body);
+                       cmdputs(";; ");
+               }
+               p = "esac";
+               goto dotail2;
+       case NTO:
+               p = ">";
+               goto redir;
+       case NCLOBBER:
+               p = ">|";
+               goto redir;
+       case NAPPEND:
+               p = ">>";
+               goto redir;
+       case NTOFD:
+               p = ">&";
+               goto redir;
+       case NFROM:
+               p = "<";
+               goto redir;
+       case NFROMFD:
+               p = "<&";
+               goto redir;
+       case NFROMTO:
+               p = "<>";
+redir:
+               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';
+                       p = s;
+                       goto dotail2;
+               } else {
+                       n = n->nfile.fname;
+                       goto donode;
+               }
+       }
+}
+
+static void
+cmdlist(union node *np, int sep)
+{
+       for (; np; np = np->narg.next) {
+               if (!sep)
+                       cmdputs(spcstr);
+               cmdtxt(np);
+               if (sep && np->narg.next)
+                       cmdputs(spcstr);
+       }
+}
+
+static void
+cmdputs(const char *s)
+{
+       const char *p, *str;
+       char c, cc[2] = " ";
+       char *nextc;
+       int subtype = 0;
+       int quoted = 0;
+       static const char *const vstype[16] = {
+               nullstr, "}", "-", "+", "?", "=",
+               "%", "%%", "#", "##", nullstr
+       };
+
+       nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
+       p = s;
+       while ((c = *p++) != 0) {
+               str = 0;
+               switch (c) {
+               case CTLESC:
+                       c = *p++;
+                       break;
+               case CTLVAR:
+                       subtype = *p++;
+                       if ((subtype & VSTYPE) == VSLENGTH)
+                               str = "${#";
+                       else
+                               str = "${";
+                       if (!(subtype & VSQUOTE) != !(quoted & 1)) {
+                               quoted ^= 1;
+                               c = '"';
+                       } else
+                               goto dostr;
+                       break;
+               case CTLENDVAR:
+                       quoted >>= 1;
+                       subtype = 0;
+                       if (quoted & 1) {
+                               str = "\"}";
+                               goto dostr;
+                       }
+                       c = '}';
+                       break;
+               case CTLBACKQ:
+                       str = "$(...)";
+                       goto dostr;
+               case CTLBACKQ+CTLQUOTE:
+                       str = "\"$(...)\"";
+                       goto dostr;
+#ifdef CONFIG_ASH_MATH_SUPPORT
+               case CTLARI:
+                       str = "$((";
+                       goto dostr;
+               case CTLENDARI:
+                       str = "))";
+                       goto dostr;
+#endif
+               case CTLQUOTEMARK:
+                       quoted ^= 1;
+                       c = '"';
+                       break;
+               case '=':
+                       if (subtype == 0)
+                               break;
+                       str = vstype[subtype & VSTYPE];
+                       if (subtype & VSNUL)
+                               c = ':';
+                       else
+                               c = *str++;
+                       if (c != '}')
+                               quoted <<= 1;
+                       break;
+               case '\'':
+               case '\\':
+               case '"':
+               case '$':
+                       /* These can only happen inside quotes */
+                       cc[0] = c;
+                       str = cc;
+                       c = '\\';
+                       break;
+               default:
+                       break;
+               }
+               USTPUTC(c, nextc);
+               if (!str)
+                       continue;
+dostr:
+               while ((c = *str++)) {
+                       USTPUTC(c, nextc);
+               }
+       }
+       if (quoted & 1) {
+               USTPUTC('"', nextc);
+       }
+       *nextc = 0;
+       cmdnextc = nextc;
+}
+
+
+static void
+showpipe(struct job *jp, FILE *out)
+{
+       struct procstat *sp;
+       struct procstat *spend;
+
+       spend = jp->ps + jp->nprocs;
+       for (sp = jp->ps + 1; sp < spend; sp++)
+               fprintf(out, " | %s", sp->cmd);
+       outcslow('\n', out);
+       flushall();
+}
+
+static void
+xtcsetpgrp(int fd, pid_t pgrp)
+{
+       if (tcsetpgrp(fd, pgrp))
+               error("Cannot set tty process group (%m)");
+}
+#endif /* JOBS */
+
+static int
+getstatus(struct job *job) {
+       int status;
+       int retval;
+
+       status = job->ps[job->nprocs - 1].status;
+       retval = WEXITSTATUS(status);
+       if (!WIFEXITED(status)) {
+#if JOBS
+               retval = WSTOPSIG(status);
+               if (!WIFSTOPPED(status))
+#endif
+               {
+                       /* XXX: limits number of signals */
+                       retval = WTERMSIG(status);
+#if JOBS
+                       if (retval == SIGINT)
+                               job->sigint = 1;
+#endif
+               }
+               retval += 128;
+       }
+       TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
+               jobno(job), job->nprocs, status, retval));
+       return retval;
+}
+
+#ifdef CONFIG_ASH_MAIL
+/*      $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $       */
+
+/*
+ * Routines to check for mail.  (Perhaps make part of main.c?)
+ */
+
+#define MAXMBOXES 10
+
+/* times of mailboxes */
+static time_t mailtime[MAXMBOXES];
+/* Set if MAIL or MAILPATH is changed. */
+static int mail_var_path_changed;
+
+
+
+/*
+ * Print appropriate message(s) if mail has arrived.
+ * If mail_var_path_changed is set,
+ * then the value of MAIL has mail_var_path_changed,
+ * so we just update the values.
+ */
+
+static void
+chkmail(void)
+{
+       const char *mpath;
+       char *p;
+       char *q;
+       time_t *mtp;
+       struct stackmark smark;
+       struct stat statb;
+
+       setstackmark(&smark);
+       mpath = mpathset() ? mpathval() : mailval();
+       for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
+               p = padvance(&mpath, nullstr);
+               if (p == NULL)
+                       break;
+               if (*p == '\0')
+                       continue;
+               for (q = p ; *q ; q++);
+#ifdef DEBUG
+               if (q[-1] != '/')
+                       abort();
+#endif
+               q[-1] = '\0';                   /* delete trailing '/' */
+               if (stat(p, &statb) < 0) {
+                       *mtp = 0;
+                       continue;
+               }
+               if (!mail_var_path_changed && statb.st_mtime != *mtp) {
+                       fprintf(
+                               stderr, snlfmt,
+                               pathopt ? pathopt : "you have mail"
+                       );
+               }
+               *mtp = statb.st_mtime;
+       }
+       mail_var_path_changed = 0;
+       popstackmark(&smark);
+}
+
+
+static void
+changemail(const char *val)
+{
+       mail_var_path_changed++;
+}
+
+#endif /* CONFIG_ASH_MAIL */
+
+/*      $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $       */
+
+
+#if PROFILE
+static short profile_buf[16384];
+extern int etext();
+#endif
+
+static int isloginsh;
+
+static void read_profile(const char *);
+
+/*
+ * Main routine.  We initialize things, parse the arguments, execute
+ * profiles if we're a login shell, and then call cmdloop to execute
+ * commands.  The setjmp call sets up the location to jump to when an
+ * exception occurs.  When an exception occurs the variable "state"
+ * is used to figure out how far we had gotten.
+ */
+
+int
+ash_main(int argc, char **argv)
+{
+       char *shinit;
+       volatile int state;
+       struct jmploc jmploc;
+       struct stackmark smark;
+
+#ifdef __GLIBC__
+       dash_errno = __errno_location();
+#endif
+
+#if PROFILE
+       monitor(4, etext, profile_buf, sizeof profile_buf, 50);
+#endif
+       state = 0;
+       if (setjmp(jmploc.loc)) {
+               int status;
+               int e;
+
+               reset();
+
+               e = exception;
+               switch (exception) {
+               case EXEXEC:
+                       status = exerrno;
+                       break;
+
+               case EXERROR:
+                       status = 2;
+                       break;
+
+               default:
+                       status = exitstatus;
+                       break;
+               }
+               exitstatus = status;
+
+               if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
+                       exitshell();
+
+               if (e == EXINT) {
+                       outcslow('\n', stderr);
+               }
+               popstackmark(&smark);
+               FORCEINTON;                             /* enable interrupts */
+               if (state == 1)
+                       goto state1;
+               else if (state == 2)
+                       goto state2;
+               else if (state == 3)
+                       goto state3;
+               else
+                       goto state4;
+       }
+       handler = &jmploc;
+#ifdef DEBUG
+       opentrace();
+       trputs("Shell args:  ");  trargs(argv);
+#endif
+       rootpid = getpid();
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+       rseed = rootpid + ((time_t)time((time_t *)0));
+#endif
+       rootshell = 1;
+       init();
+       setstackmark(&smark);
+       procargs(argc, argv);
+#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+       if ( iflag ) {
+               const char *hp = lookupvar("HISTFILE");
+
+               if(hp == NULL ) {
+                       hp = lookupvar("HOME");
+                       if(hp != NULL) {
+                               char *defhp = concat_path_file(hp, ".ash_history");
+                               setvar("HISTFILE", defhp, 0);
+                               free(defhp);
+                       }
+               }
+       }
+#endif
+       if (argv[0] && argv[0][0] == '-')
+               isloginsh = 1;
+       if (isloginsh) {
+               state = 1;
+               read_profile("/etc/profile");
+state1:
+               state = 2;
+               read_profile(".profile");
+       }
+state2:
+       state = 3;
+       if (
+#ifndef linux
+               getuid() == geteuid() && getgid() == getegid() &&
+#endif
+               iflag
+       ) {
+               if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
+                       read_profile(shinit);
+               }
+       }
+state3:
+       state = 4;
+       if (minusc)
+               evalstring(minusc);
+
+       if (sflag || minusc == NULL) {
+#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+           if ( iflag ) {
+               const char *hp = lookupvar("HISTFILE");
+
+               if(hp != NULL )
+                       load_history ( hp );
+           }
+#endif
+state4: /* XXX ??? - why isn't this before the "if" statement */
+               cmdloop(1);
+       }
+#if PROFILE
+       monitor(0);
+#endif
+#if GPROF
+       {
+               extern void _mcleanup(void);
+               _mcleanup();
+       }
+#endif
+       exitshell();
+       /* NOTREACHED */
+}
+
+
+/*
+ * Read and execute commands.  "Top" is nonzero for the top level command
+ * loop; it turns on prompting if the shell is interactive.
+ */
+
+static void
+cmdloop(int top)
+{
+       union node *n;
+       struct stackmark smark;
+       int inter;
+       int numeof = 0;
+
+       TRACE(("cmdloop(%d) called\n", top));
+       for (;;) {
+               setstackmark(&smark);
+               if (pendingsigs)
+                       dotrap();
+#if JOBS
+               if (jobctl)
+                       showjobs(stderr, SHOW_CHANGED);
+#endif
+               inter = 0;
+               if (iflag && top) {
+                       inter++;
+#ifdef CONFIG_ASH_MAIL
+                       chkmail();
+#endif
+               }
+               n = parsecmd(inter);
+               /* showtree(n); DEBUG */
+               if (n == NEOF) {
+                       if (!top || numeof >= 50)
+                               break;
+                       if (!stoppedjobs()) {
+                               if (!Iflag)
+                                       break;
+                               out2str("\nUse \"exit\" to leave shell.\n");
+                       }
+                       numeof++;
+               } else if (n != NULL && nflag == 0) {
+                       job_warning = (job_warning == 2) ? 1 : 0;
+                       numeof = 0;
+                       evaltree(n, 0);
+               }
+               popstackmark(&smark);
+               if (evalskip) {
+                       evalskip = 0;
+                       break;
+               }
+       }
+}
+
+
+/*
+ * Read /etc/profile or .profile.  Return on error.
+ */
+
+static void
+read_profile(const char *name)
+{
+       int fd;
+       int xflag_set = 0;
+       int vflag_set = 0;
+
+       INTOFF;
+       if ((fd = open(name, O_RDONLY)) >= 0)
+               setinputfd(fd, 1);
+       INTON;
+       if (fd < 0)
+               return;
+       /* -q turns off -x and -v just when executing init files */
+       if (qflag)  {
+           if (xflag)
+                   xflag = 0, xflag_set = 1;
+           if (vflag)
+                   vflag = 0, vflag_set = 1;
+       }
+       cmdloop(0);
+       if (qflag)  {
+           if (xflag_set)
+                   xflag = 1;
+           if (vflag_set)
+                   vflag = 1;
+       }
+       popfile();
+}
+
+
+/*
+ * Read a file containing shell functions.
+ */
+
+static void
+readcmdfile(char *name)
+{
+       int fd;
+
+       INTOFF;
+       if ((fd = open(name, O_RDONLY)) >= 0)
+               setinputfd(fd, 1);
+       else
+               error("Can't open %s", name);
+       INTON;
+       cmdloop(0);
+       popfile();
+}
+
+
+/*
+ * Take commands from a file.  To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+
+static inline char *
+find_dot_file(char *name)
+{
+       char *fullname;
+       const char *path = pathval();
+       struct stat statb;
+
+       /* don't try this for absolute or relative paths */
+       if (strchr(name, '/'))
+               return name;
+
+       while ((fullname = padvance(&path, name)) != NULL) {
+               if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+                       /*
+                        * Don't bother freeing here, since it will
+                        * be freed by the caller.
+                        */
+                       return fullname;
+               }
+               stunalloc(fullname);
+       }
+
+       /* not found in the PATH */
+       error(not_found_msg, name);
+       /* NOTREACHED */
+}
+
+static int dotcmd(int argc, char **argv)
+{
+       struct strlist *sp;
+       volatile struct shparam saveparam;
+
+       exitstatus = 0;
+
+       for (sp = cmdenviron; sp; sp = sp->next)
+               setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+
+       if (argc >= 2) {        /* That's what SVR2 does */
+               char *fullname;
+               struct stackmark smark;
+
+               setstackmark(&smark);
+               fullname = find_dot_file(argv[1]);
+
+               if (argc > 2) {
+                       saveparam = shellparam;
+                       shellparam.malloc = 0;
+                       shellparam.nparam = argc - 2;
+                       shellparam.p = argv + 2;
+               };
+
+               setinputfile(fullname, 1);
+               commandname = fullname;
+               cmdloop(0);
+               popfile();
+
+               if (argc > 2) {
+                       freeparam(&shellparam);
+                       shellparam = saveparam;
+               };
+
+               popstackmark(&smark);
+       }
+       return exitstatus;
+}
+
+
+static int
+exitcmd(int argc, char **argv)
+{
+       if (stoppedjobs())
+               return 0;
+       if (argc > 1)
+               exitstatus = number(argv[1]);
+       exraise(EXEXIT);
+       /* NOTREACHED */
+}
+
+/*      $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $        */
+
+/*
+ * Same for malloc, realloc, but returns an error when out of space.
+ */
+
+static pointer
+ckrealloc(pointer p, size_t nbytes)
+{
+       p = realloc(p, nbytes);
+       if (p == NULL)
+               error(bb_msg_memory_exhausted);
+       return p;
+}
+
+static pointer
+ckmalloc(size_t nbytes)
+{
+       return ckrealloc(NULL, nbytes);
+}
+
+/*
+ * Make a copy of a string in safe storage.
+ */
+
+static char *
+savestr(const char *s)
+{
+       char *p = strdup(s);
+       if (!p)
+               error(bb_msg_memory_exhausted);
+       return p;
+}
+
+
+/*
+ * Parse trees for commands are allocated in lifo order, so we use a stack
+ * to make this more efficient, and also to avoid all sorts of exception
+ * handling code to handle interrupts in the middle of a parse.
+ *
+ * The size 504 was chosen because the Ultrix malloc handles that size
+ * well.
+ */
+
+
+static pointer
+stalloc(size_t nbytes)
+{
+       char *p;
+       size_t aligned;
+
+       aligned = SHELL_ALIGN(nbytes);
+       if (aligned > stacknleft) {
+               size_t len;
+               size_t blocksize;
+               struct stack_block *sp;
+
+               blocksize = aligned;
+               if (blocksize < MINSIZE)
+                       blocksize = MINSIZE;
+               len = sizeof(struct stack_block) - MINSIZE + blocksize;
+               if (len < blocksize)
+                       error(bb_msg_memory_exhausted);
+               INTOFF;
+               sp = ckmalloc(len);
+               sp->prev = stackp;
+               stacknxt = sp->space;
+               stacknleft = blocksize;
+               sstrend = stacknxt + blocksize;
+               stackp = sp;
+               INTON;
+       }
+       p = stacknxt;
+       stacknxt += aligned;
+       stacknleft -= aligned;
+       return p;
+}
+
+
+void
+stunalloc(pointer p)
+{
+#ifdef DEBUG
+       if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
+               write(2, "stunalloc\n", 10);
+               abort();
+       }
+#endif
+       stacknleft += stacknxt - (char *)p;
+       stacknxt = p;
+}
+
+
+void
+setstackmark(struct stackmark *mark)
+{
+       mark->stackp = stackp;
+       mark->stacknxt = stacknxt;
+       mark->stacknleft = stacknleft;
+       mark->marknext = markp;
+       markp = mark;
+}
+
+
+void
+popstackmark(struct stackmark *mark)
+{
+       struct stack_block *sp;
+
+       INTOFF;
+       markp = mark->marknext;
+       while (stackp != mark->stackp) {
+               sp = stackp;
+               stackp = sp->prev;
+               ckfree(sp);
+       }
+       stacknxt = mark->stacknxt;
+       stacknleft = mark->stacknleft;
+       sstrend = mark->stacknxt + 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(void)
+{
+       size_t newlen;
+
+       newlen = stacknleft * 2;
+       if (newlen < stacknleft)
+               error(bb_msg_memory_exhausted);
+       if (newlen < 128)
+               newlen += 128;
+
+       if (stacknxt == stackp->space && stackp != &stackbase) {
+               struct stack_block *oldstackp;
+               struct stackmark *xmark;
+               struct stack_block *sp;
+               struct stack_block *prevstackp;
+               size_t grosslen;
+
+               INTOFF;
+               oldstackp = stackp;
+               sp = stackp;
+               prevstackp = sp->prev;
+               grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
+               sp = ckrealloc((pointer)sp, grosslen);
+               sp->prev = prevstackp;
+               stackp = sp;
+               stacknxt = sp->space;
+               stacknleft = newlen;
+               sstrend = sp->space + newlen;
+
+               /*
+                * Stack marks pointing to the start of the old block
+                * must be relocated to point to the new block
+                */
+               xmark = markp;
+               while (xmark != NULL && xmark->stackp == oldstackp) {
+                       xmark->stackp = stackp;
+                       xmark->stacknxt = stacknxt;
+                       xmark->stacknleft = stacknleft;
+                       xmark = xmark->marknext;
+               }
+               INTON;
+       } else {
+               char *oldspace = stacknxt;
+               int oldlen = stacknleft;
+               char *p = stalloc(newlen);
+
+               /* free the space we just allocated */
+               stacknxt = memcpy(p, oldspace, oldlen);
+               stacknleft += newlen;
+       }
+}
+
+static inline void
+grabstackblock(size_t len)
+{
+       len = SHELL_ALIGN(len);
+       stacknxt += len;
+       stacknleft -= len;
+}
+
+/*
+ * The following routines are somewhat easier to use than the above.
+ * The user declares a variable of type STACKSTR, which may be declared
+ * to be a register.  The macro STARTSTACKSTR initializes things.  Then
+ * the user uses the macro STPUTC to add characters to the string.  In
+ * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
+ * grown as necessary.  When the user is done, she can just leave the
+ * string there and refer to it using stackblock().  Or she can allocate
+ * the space for it using grabstackstr().  If it is necessary to allow
+ * someone else to use the stack temporarily and then continue to grow
+ * the string, the user should use grabstack to allocate the space, and
+ * then call ungrabstr(p) to return to the previous mode of operation.
+ *
+ * USTPUTC is like STPUTC except that it doesn't check for overflow.
+ * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
+ * is space for at least one character.
+ */
+
+void *
+growstackstr(void)
+{
+       size_t len = stackblocksize();
+       if (herefd >= 0 && len >= 1024) {
+               bb_full_write(herefd, stackblock(), len);
+               return stackblock();
+       }
+       growstackblock();
+       return stackblock() + len;
+}
+
+/*
+ * Called from CHECKSTRSPACE.
+ */
+
+char *
+makestrspace(size_t newlen, char *p)
+{
+       size_t len = p - stacknxt;
+       size_t size = stackblocksize();
+
+       for (;;) {
+               size_t nleft;
+
+               size = stackblocksize();
+               nleft = size - len;
+               if (nleft >= newlen)
+                       break;
+               growstackblock();
+       }
+       return stackblock() + len;
+}
+
+char *
+stnputs(const char *s, size_t n, char *p)
+{
+       p = makestrspace(n, p);
+       p = mempcpy(p, s, n);
+       return p;
+}
+
+char *
+stputs(const char *s, char *p)
+{
+       return stnputs(s, strlen(s), p);
+}
+
+/*      $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $   */
+
+/*
+ * String functions.
+ *
+ *      number(s)               Convert a string of digits to an integer.
+ *      is_number(s)            Return true if s is a string of digits.
+ */
+
+/*
+ * prefix -- see if pfx is a prefix of string.
+ */
+
+char *
+prefix(const char *string, const char *pfx)
+{
+       while (*pfx) {
+               if (*pfx++ != *string++)
+                       return 0;
+       }
+       return (char *) string;
+}
+
+
+/*
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
+ */
+
+int
+number(const char *s)
+{
+
+       if (! is_number(s))
+               error(illnum, s);
+       return atoi(s);
+}
+
+
+/*
+ * Check for a valid number.  This should be elsewhere.
+ */
+
+int
+is_number(const char *p)
+{
+       do {
+               if (! is_digit(*p))
+                       return 0;
+       } while (*++p != '\0');
+       return 1;
+}
+
+
+/*
+ * Produce a possibly single quoted string suitable as input to the shell.
+ * The return string is allocated on the stack.
+ */
+
+char *
+single_quote(const char *s) {
+       char *p;
+
+       STARTSTACKSTR(p);
+
+       do {
+               char *q;
+               size_t len;
+
+               len = strchrnul(s, '\'') - s;
+
+               q = p = makestrspace(len + 3, p);
+
+               *q++ = '\'';
+               q = mempcpy(q, s, len);
+               *q++ = '\'';
+               s += len;
+
+               STADJUST(q - p, p);
+
+               len = strspn(s, "'");
+               if (!len)
+                       break;
+
+               q = p = makestrspace(len + 3, p);
+
+               *q++ = '"';
+               q = mempcpy(q, s, len);
+               *q++ = '"';
+               s += len;
+
+               STADJUST(q - p, p);
+       } while (*s);
+
+       USTPUTC(0, p);
+
+       return stackblock();
+}
+
+/*
+ * Like strdup but works with the ash stack.
+ */
+
+char *
+sstrdup(const char *p)
+{
+       size_t len = strlen(p) + 1;
+       return memcpy(stalloc(len), p, len);
+}
+
+
+static void
+calcsize(union node *n)
+{
+      if (n == NULL)
+           return;
+      funcblocksize += nodesize[n->type];
+      switch (n->type) {
+      case NCMD:
+           calcsize(n->ncmd.redirect);
+           calcsize(n->ncmd.args);
+           calcsize(n->ncmd.assign);
+           break;
+      case NPIPE:
+           sizenodelist(n->npipe.cmdlist);
+           break;
+      case NREDIR:
+      case NBACKGND:
+      case NSUBSHELL:
+           calcsize(n->nredir.redirect);
+           calcsize(n->nredir.n);
+           break;
+      case NAND:
+      case NOR:
+      case NSEMI:
+      case NWHILE:
+      case NUNTIL:
+           calcsize(n->nbinary.ch2);
+           calcsize(n->nbinary.ch1);
+           break;
+      case NIF:
+           calcsize(n->nif.elsepart);
+           calcsize(n->nif.ifpart);
+           calcsize(n->nif.test);
+           break;
+      case NFOR:
+           funcstringsize += strlen(n->nfor.var) + 1;
+           calcsize(n->nfor.body);
+           calcsize(n->nfor.args);
+           break;
+      case NCASE:
+           calcsize(n->ncase.cases);
+           calcsize(n->ncase.expr);
+           break;
+      case NCLIST:
+           calcsize(n->nclist.body);
+           calcsize(n->nclist.pattern);
+           calcsize(n->nclist.next);
+           break;
+      case NDEFUN:
+      case NARG:
+           sizenodelist(n->narg.backquote);
+           funcstringsize += strlen(n->narg.text) + 1;
+           calcsize(n->narg.next);
+           break;
+      case NTO:
+      case NCLOBBER:
+      case NFROM:
+      case NFROMTO:
+      case NAPPEND:
+           calcsize(n->nfile.fname);
+           calcsize(n->nfile.next);
+           break;
+      case NTOFD:
+      case NFROMFD:
+           calcsize(n->ndup.vname);
+           calcsize(n->ndup.next);
+           break;
+      case NHERE:
+      case NXHERE:
+           calcsize(n->nhere.doc);
+           calcsize(n->nhere.next);
+           break;
+      case NNOT:
+           calcsize(n->nnot.com);
+           break;
+      };
+}
+
+
+static void
+sizenodelist(struct nodelist *lp)
+{
+       while (lp) {
+               funcblocksize += SHELL_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 = (char *) funcblock + nodesize[n->type];
+      switch (n->type) {
+      case NCMD:
+           new->ncmd.redirect = copynode(n->ncmd.redirect);
+           new->ncmd.args = copynode(n->ncmd.args);
+           new->ncmd.assign = copynode(n->ncmd.assign);
+           break;
+      case NPIPE:
+           new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+           new->npipe.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 NAND:
+      case NOR:
+      case NSEMI:
+      case NWHILE:
+      case NUNTIL:
+           new->nbinary.ch2 = copynode(n->nbinary.ch2);
+           new->nbinary.ch1 = copynode(n->nbinary.ch1);
+           break;
+      case NIF:
+           new->nif.elsepart = copynode(n->nif.elsepart);
+           new->nif.ifpart = copynode(n->nif.ifpart);
+           new->nif.test = copynode(n->nif.test);
+           break;
+      case NFOR:
+           new->nfor.var = 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 NCLOBBER:
+      case NFROM:
+      case NFROMTO:
+      case NAPPEND:
+           new->nfile.fname = copynode(n->nfile.fname);
+           new->nfile.fd = n->nfile.fd;
+           new->nfile.next = copynode(n->nfile.next);
+           break;
+      case NTOFD:
+      case NFROMFD:
+           new->ndup.vname = copynode(n->ndup.vname);
+           new->ndup.dupfd = n->ndup.dupfd;
+           new->ndup.fd = n->ndup.fd;
+           new->ndup.next = copynode(n->ndup.next);
+           break;
+      case NHERE:
+      case NXHERE:
+           new->nhere.doc = copynode(n->nhere.doc);
+           new->nhere.fd = n->nhere.fd;
+           new->nhere.next = copynode(n->nhere.next);
+           break;
+      case NNOT:
+           new->nnot.com = copynode(n->nnot.com);
+           break;
+      };
+      new->type = n->type;
+       return new;
+}
+
+
+static struct nodelist *
+copynodelist(struct nodelist *lp)
+{
+       struct nodelist *start;
+       struct nodelist **lpp;
+
+       lpp = &start;
+       while (lp) {
+               *lpp = funcblock;
+               funcblock = (char *) funcblock +
+                   SHELL_ALIGN(sizeof(struct nodelist));
+               (*lpp)->n = copynode(lp->n);
+               lp = lp->next;
+               lpp = &(*lpp)->next;
+       }
+       *lpp = NULL;
+       return start;
+}
+
+
+static char *
+nodesavestr(char   *s)
+{
+       char   *rtn = funcstring;
+
+       funcstring = stpcpy(funcstring, s) + 1;
+       return rtn;
+}
+
+
+/*
+ * Free a parse tree.
+ */
+
+static void
+freefunc(struct funcnode *f)
+{
+       if (f && --f->count < 0)
+               ckfree(f);
+}
+
+
+static void options(int);
+static void setoption(int, int);
+
+
+/*
+ * Process the shell command line arguments.
+ */
+
+void
+procargs(int argc, char **argv)
+{
+       int i;
+       const char *xminusc;
+       char **xargv;
+
+       xargv = argv;
+       arg0 = xargv[0];
+       if (argc > 0)
+               xargv++;
+       for (i = 0; i < NOPTS; i++)
+               optlist[i] = 2;
+       argptr = xargv;
+       options(1);
+       xargv = argptr;
+       xminusc = minusc;
+       if (*xargv == NULL) {
+               if (xminusc)
+                       error("-c requires an argument");
+               sflag = 1;
+       }
+       if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+               iflag = 1;
+       if (mflag == 2)
+               mflag = iflag;
+       for (i = 0; i < NOPTS; i++)
+               if (optlist[i] == 2)
+                       optlist[i] = 0;
+#if DEBUG == 2
+       debug = 1;
+#endif
+       /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+       if (xminusc) {
+               minusc = *xargv++;
+               if (*xargv)
+                       goto setarg0;
+       } else if (!sflag) {
+               setinputfile(*xargv, 0);
+setarg0:
+               arg0 = *xargv++;
+               commandname = arg0;
+       }
+
+       shellparam.p = xargv;
+#ifdef CONFIG_ASH_GETOPTS
+       shellparam.optind = 1;
+       shellparam.optoff = -1;
+#endif
+       /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
+       while (*xargv) {
+               shellparam.nparam++;
+               xargv++;
+       }
+       optschanged();
+}
+
+
+void
+optschanged(void)
+{
+#ifdef DEBUG
+       opentrace();
+#endif
+       setinteractive(iflag);
+       setjobctl(mflag);
+}
+
+static inline void
+minus_o(char *name, int val)
+{
+       int i;
+
+       if (name == NULL) {
+               out1str("Current option settings\n");
+               for (i = 0; i < NOPTS; i++)
+                       out1fmt("%-16s%s\n", optnames(i),
+                               optlist[i] ? "on" : "off");
+       } else {
+               for (i = 0; i < NOPTS; i++)
+                       if (equal(name, optnames(i))) {
+                               optlist[i] = val;
+                               return;
+                       }
+               error("Illegal option -o %s", name);
+       }
+}
+
+/*
+ * Process shell options.  The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ */
+
+static void
+options(int cmdline)
+{
+       char *p;
+       int val;
+       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) {
+                               minusc = p;     /* command is after shell args*/
+                       } else if (c == 'o') {
+                               minus_o(*argptr, val);
+                               if (*argptr)
+                                       argptr++;
+                       } else if (cmdline && (c == '-')) {     // long options
+                               if (strcmp(p, "login") == 0)
+                                       isloginsh = 1;
+                               break;
+                       } else {
+                               setoption(c, val);
+                       }
+               }
+       }
+}
+
+
+static void
+setoption(int flag, int val)
+{
+       int i;
+
+       for (i = 0; i < NOPTS; i++)
+               if (optletters(i) == flag) {
+                       optlist[i] = val;
+                       return;
+               }
+       error("Illegal option -%c", flag);
+       /* NOTREACHED */
+}
+
+
+
+/*
+ * 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;
+#ifdef CONFIG_ASH_GETOPTS
+       shellparam.optind = 1;
+       shellparam.optoff = -1;
+#endif
+}
+
+
+/*
+ * Free the list of positional parameters.
+ */
+
+void
+freeparam(volatile 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)
+               error("can't shift that many");
+       INTOFF;
+       shellparam.nparam -= n;
+       for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
+               if (shellparam.malloc)
+                       ckfree(*ap1);
+       }
+       ap2 = shellparam.p;
+       while ((*ap2++ = *ap1++) != NULL);
+#ifdef CONFIG_ASH_GETOPTS
+       shellparam.optind = 1;
+       shellparam.optoff = -1;
+#endif
+       INTON;
+       return 0;
+}
+
+
+
+/*
+ * The set command builtin.
+ */
+
+int
+setcmd(int argc, char **argv)
+{
+       if (argc == 1)
+               return showvars(nullstr, 0, VUNSET);
+       INTOFF;
+       options(0);
+       optschanged();
+       if (*argptr != NULL) {
+               setparam(argptr);
+       }
+       INTON;
+       return 0;
+}
+
+
+#ifdef CONFIG_ASH_GETOPTS
+static void
+getoptsreset(value)
+       const char *value;
+{
+       shellparam.optind = number(value);
+       shellparam.optoff = -1;
+}
+#endif
+
+#ifdef CONFIG_LOCALE_SUPPORT
+static void change_lc_all(const char *value)
+{
+       if (value != 0 && *value != 0)
+               setlocale(LC_ALL, value);
+}
+
+static void change_lc_ctype(const char *value)
+{
+       if (value != 0 && *value != 0)
+               setlocale(LC_CTYPE, value);
+}
+
+#endif
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+/* Roughly copied from bash.. */
+static void change_random(const char *value)
+{
+       if(value == NULL) {
+               /* "get", generate */
+               char buf[16];
+
+               rseed = rseed * 1103515245 + 12345;
+               sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+               /* set without recursion */
+               setvar(vrandom.text, buf, VNOFUNC);
+               vrandom.flags &= ~VNOFUNC;
+       } else {
+               /* set/reset */
+               rseed = strtoul(value, (char **)NULL, 10);
+       }
+}
+#endif
+
+
+#ifdef CONFIG_ASH_GETOPTS
+static int
+getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
+{
+       char *p, *q;
+       char c = '?';
+       int done = 0;
+       int err = 0;
+       char s[12];
+       char **optnext;
+
+       if(*param_optind < 1)
+               return 1;
+       optnext = optfirst + *param_optind - 1;
+
+       if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
+               p = NULL;
+       else
+               p = optnext[-1] + *optoff;
+       if (p == NULL || *p == '\0') {
+               /* Current word is done, advance */
+               p = *optnext;
+               if (p == NULL || *p != '-' || *++p == '\0') {
+atend:
+                       p = NULL;
+                       done = 1;
+                       goto out;
+               }
+               optnext++;
+               if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
+                       goto atend;
+       }
+
+       c = *p++;
+       for (q = optstr; *q != c; ) {
+               if (*q == '\0') {
+                       if (optstr[0] == ':') {
+                               s[0] = c;
+                               s[1] = '\0';
+                               err |= setvarsafe("OPTARG", s, 0);
+                       } else {
+                               fprintf(stderr, "Illegal option -%c\n", c);
+                               (void) unsetvar("OPTARG");
+                       }
+                       c = '?';
+                       goto out;
+               }
+               if (*++q == ':')
+                       q++;
+       }
+
+       if (*++q == ':') {
+               if (*p == '\0' && (p = *optnext) == NULL) {
+                       if (optstr[0] == ':') {
+                               s[0] = c;
+                               s[1] = '\0';
+                               err |= setvarsafe("OPTARG", s, 0);
+                               c = ':';
+                       } else {
+                               fprintf(stderr, "No arg for -%c option\n", c);
+                               (void) unsetvar("OPTARG");
+                               c = '?';
+                       }
+                       goto out;
+               }
+
+               if (p == *optnext)
+                       optnext++;
+               err |= setvarsafe("OPTARG", p, 0);
+               p = NULL;
+       } else
+               err |= setvarsafe("OPTARG", nullstr, 0);
+
+out:
+       *optoff = p ? p - *(optnext - 1) : -1;
+       *param_optind = optnext - optfirst + 1;
+       fmtstr(s, sizeof(s), "%d", *param_optind);
+       err |= setvarsafe("OPTIND", s, VNOFUNC);
+       s[0] = c;
+       s[1] = '\0';
+       err |= setvarsafe(optvar, s, 0);
+       if (err) {
+               *param_optind = 1;
+               *optoff = -1;
+               flushall();
+               exraise(EXERROR);
+       }
+       return done;
+}
+
+/*
+ * The getopts builtin.  Shellparam.optnext points to the next argument
+ * to be processed.  Shellparam.optptr points to the next character to
+ * be processed in the current argument.  If shellparam.optnext is NULL,
+ * then it's the first time getopts has been called.
+ */
+
+int
+getoptscmd(int argc, char **argv)
+{
+       char **optbase;
+
+       if (argc < 3)
+               error("Usage: getopts optstring var [arg]");
+       else if (argc == 3) {
+               optbase = shellparam.p;
+               if (shellparam.optind > shellparam.nparam + 1) {
+                       shellparam.optind = 1;
+                       shellparam.optoff = -1;
+               }
+       }
+       else {
+               optbase = &argv[3];
+               if (shellparam.optind > argc - 2) {
+                       shellparam.optind = 1;
+                       shellparam.optoff = -1;
+               }
+       }
+
+       return getopts(argv[1], argv[2], optbase, &shellparam.optind,
+                      &shellparam.optoff);
+}
+#endif /* CONFIG_ASH_GETOPTS */
+
+/*
+ * XXX - should get rid of.  have all builtins use getopt(3).  the
+ * library getopt must have the BSD extension static variable "optreset"
+ * otherwise it can't be used within the shell safely.
+ *
+ * Standard option processing (a la getopt) for builtin routines.  The
+ * only argument that is passed to nextopt is the option string; the
+ * other arguments are unnecessary.  It return the character, or '\0' on
+ * end of input.
+ */
+
+static int
+nextopt(const char *optstring)
+{
+       char *p;
+       const char *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')
+                       error("Illegal option -%c", c);
+               if (*++q == ':')
+                       q++;
+       }
+       if (*++q == ':') {
+               if (*p == '\0' && (p = *argptr++) == NULL)
+                       error("No arg for -%c option", c);
+               optionarg = p;
+               p = NULL;
+       }
+       optptr = p;
+       return c;
+}
+
+
+/*      $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $     */
+
+void
+outstr(const char *p, FILE *file)
+{
+       INTOFF;
+       fputs(p, file);
+       INTON;
+}
+
+void
+flushall(void)
+{
+       INTOFF;
+       fflush(stdout);
+       fflush(stderr);
+       INTON;
+}
+
+void
+flusherr(void)
+{
+       INTOFF;
+       fflush(stderr);
+       INTON;
+}
+
+static void
+outcslow(int c, FILE *dest)
+{
+       INTOFF;
+       putc(c, dest);
+       fflush(dest);
+       INTON;
+}
+
+
+static int
+out1fmt(const char *fmt, ...)
+{
+       va_list ap;
+       int r;
+
+       INTOFF;
+       va_start(ap, fmt);
+       r = vprintf(fmt, ap);
+       va_end(ap);
+       INTON;
+       return r;
+}
+
+
+int
+fmtstr(char *outbuf, size_t length, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       INTOFF;
+       ret = vsnprintf(outbuf, length, fmt, ap);
+       va_end(ap);
+       INTON;
+       return ret;
+}
+
+
+
+/*      $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $     */
+
+
+/*
+ * Shell command parser.
+ */
+
+#define EOFMARKLEN 79
+
+
+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 */
+};
+
+
+
+static struct heredoc *heredoclist;    /* list of here documents to read */
+
+
+static union node *list(int);
+static union node *andor(void);
+static union node *pipeline(void);
+static union node *command(void);
+static union node *simplecmd(void);
+static union node *makename(void);
+static void parsefname(void);
+static void parseheredoc(void);
+static char peektoken(void);
+static int readtoken(void);
+static int xxreadtoken(void);
+static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
+static int noexpand(char *);
+static void synexpect(int) __attribute__((__noreturn__));
+static void synerror(const char *) __attribute__((__noreturn__));
+static void setprompt(int);
+
+
+
+static inline int
+isassignment(const char *p)
+{
+       const char *q = endofname(p);
+       if (p == q)
+               return 0;
+       return *q == '=';
+}
+
+
+/*
+ * 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;
+
+       tokpushback = 0;
+       doprompt = interact;
+       if (doprompt)
+               setprompt(doprompt);
+       needprompt = 0;
+       t = readtoken();
+       if (t == TEOF)
+               return NEOF;
+       if (t == TNL)
+               return NULL;
+       tokpushback++;
+       return list(1);
+}
+
+
+static union node *
+list(int nlflag)
+{
+       union node *n1, *n2, *n3;
+       int tok;
+
+       checkkwd = CHKNL | CHKKWD | CHKALIAS;
+       if (nlflag == 2 && peektoken())
+               return NULL;
+       n1 = NULL;
+       for (;;) {
+               n2 = andor();
+               tok = readtoken();
+               if (tok == TBACKGND) {
+                       if (n2->type == NPIPE) {
+                               n2->npipe.backgnd = 1;
+                       } else {
+                               if (n2->type != NREDIR) {
+                                       n3 = stalloc(sizeof(struct nredir));
+                                       n3->nredir.n = n2;
+                                       n3->nredir.redirect = NULL;
+                                       n2 = n3;
+                               }
+                               n2->type = NBACKGND;
+                       }
+               }
+               if (n1 == NULL) {
+                       n1 = n2;
+               }
+               else {
+                       n3 = (union node *)stalloc(sizeof (struct nbinary));
+                       n3->type = NSEMI;
+                       n3->nbinary.ch1 = n1;
+                       n3->nbinary.ch2 = n2;
+                       n1 = n3;
+               }
+               switch (tok) {
+               case TBACKGND:
+               case TSEMI:
+                       tok = readtoken();
+                       /* fall through */
+               case TNL:
+                       if (tok == TNL) {
+                               parseheredoc();
+                               if (nlflag == 1)
+                                       return n1;
+                       } else {
+                               tokpushback++;
+                       }
+                       checkkwd = CHKNL | CHKKWD | CHKALIAS;
+                       if (peektoken())
+                               return n1;
+                       break;
+               case TEOF:
+                       if (heredoclist)
+                               parseheredoc();
+                       else
+                               pungetc();              /* push back EOF on input */
+                       return n1;
+               default:
+                       if (nlflag == 1)
+                               synexpect(-1);
+                       tokpushback++;
+                       return n1;
+               }
+       }
+}
+
+
+
+static union node *
+andor(void)
+{
+       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;
+               }
+               checkkwd = CHKNL | CHKKWD | CHKALIAS;
+               n2 = pipeline();
+               n3 = (union node *)stalloc(sizeof (struct nbinary));
+               n3->type = t;
+               n3->nbinary.ch1 = n1;
+               n3->nbinary.ch2 = n2;
+               n1 = n3;
+       }
+}
+
+
+
+static union node *
+pipeline(void)
+{
+       union node *n1, *n2, *pipenode;
+       struct nodelist *lp, *prev;
+       int negate;
+
+       negate = 0;
+       TRACE(("pipeline: entered\n"));
+       if (readtoken() == TNOT) {
+               negate = !negate;
+               checkkwd = CHKKWD | CHKALIAS;
+       } else
+               tokpushback++;
+       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));
+                       checkkwd = CHKNL | CHKKWD | CHKALIAS;
+                       lp->n = command();
+                       prev->next = lp;
+               } while (readtoken() == TPIPE);
+               lp->next = NULL;
+               n1 = pipenode;
+       }
+       tokpushback++;
+       if (negate) {
+               n2 = (union node *)stalloc(sizeof (struct nnot));
+               n2->type = NNOT;
+               n2->nnot.com = n1;
+               return n2;
+       } else
+               return n1;
+}
+
+
+
+static union node *
+command(void)
+{
+       union node *n1, *n2;
+       union node *ap, **app;
+       union node *cp, **cpp;
+       union node *redir, **rpp;
+       union node **rpp2;
+       int t;
+
+       redir = NULL;
+       rpp2 = &redir;
+
+       switch (readtoken()) {
+       default:
+               synexpect(-1);
+               /* NOTREACHED */
+       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++;
+               }
+               t = TFI;
+               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);
+               t = TDONE;
+               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;
+               checkkwd = CHKKWD | CHKALIAS;
+               if (readtoken() == TIN) {
+                       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;
+                       if (lasttoken != TNL && lasttoken != TSEMI)
+                               synexpect(-1);
+               } else {
+                       n2 = (union node *)stalloc(sizeof (struct narg));
+                       n2->type = NARG;
+                       n2->narg.text = (char *)dolatstr;
+                       n2->narg.backquote = NULL;
+                       n2->narg.next = NULL;
+                       n1->nfor.args = n2;
+                       /*
+                        * Newline or semicolon here is optional (but note
+                        * that the original Bourne shell only allowed NL).
+                        */
+                       if (lasttoken != TNL && lasttoken != TSEMI)
+                               tokpushback++;
+               }
+               checkkwd = CHKNL | CHKKWD | CHKALIAS;
+               if (readtoken() != TDO)
+                       synexpect(TDO);
+               n1->nfor.body = list(0);
+               t = TDONE;
+               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;
+               do {
+                       checkkwd = CHKKWD | CHKALIAS;
+               } while (readtoken() == TNL);
+               if (lasttoken != TIN)
+                       synexpect(TIN);
+               cpp = &n1->ncase.cases;
+next_case:
+               checkkwd = CHKNL | CHKKWD;
+               t = readtoken();
+               while(t != TESAC) {
+                       if (lasttoken == TLP)
+                               readtoken();
+                       *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;
+                               readtoken();
+                       }
+                       ap->narg.next = NULL;
+                       if (lasttoken != TRP)
+                               synexpect(TRP);
+                       cp->nclist.body = list(2);
+
+                       cpp = &cp->nclist.next;
+
+                       checkkwd = CHKNL | CHKKWD;
+                       if ((t = readtoken()) != TESAC) {
+                               if (t != TENDCASE)
+                                       synexpect(TENDCASE);
+                               else
+                                       goto next_case;
+                       }
+               }
+               *cpp = NULL;
+               goto redir;
+       case TLP:
+               n1 = (union node *)stalloc(sizeof (struct nredir));
+               n1->type = NSUBSHELL;
+               n1->nredir.n = list(0);
+               n1->nredir.redirect = NULL;
+               t = TRP;
+               break;
+       case TBEGIN:
+               n1 = list(0);
+               t = TEND;
+               break;
+       case TWORD:
+       case TREDIR:
+               tokpushback++;
+               return simplecmd();
+       }
+
+       if (readtoken() != t)
+               synexpect(t);
+
+redir:
+       /* Now check for redirection which may follow command */
+       checkkwd = CHKKWD | CHKALIAS;
+       rpp = rpp2;
+       while (readtoken() == TREDIR) {
+               *rpp = n2 = redirnode;
+               rpp = &n2->nfile.next;
+               parsefname();
+       }
+       tokpushback++;
+       *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;
+       }
+
+       return n1;
+}
+
+
+static union node *
+simplecmd(void) {
+       union node *args, **app;
+       union node *n = NULL;
+       union node *vars, **vpp;
+       union node **rpp, *redir;
+       int savecheckkwd;
+
+       args = NULL;
+       app = &args;
+       vars = NULL;
+       vpp = &vars;
+       redir = NULL;
+       rpp = &redir;
+
+       savecheckkwd = CHKALIAS;
+       for (;;) {
+               checkkwd = savecheckkwd;
+               switch (readtoken()) {
+               case TWORD:
+                       n = (union node *)stalloc(sizeof (struct narg));
+                       n->type = NARG;
+                       n->narg.text = wordtext;
+                       n->narg.backquote = backquotelist;
+                       if (savecheckkwd && isassignment(wordtext)) {
+                               *vpp = n;
+                               vpp = &n->narg.next;
+                       } else {
+                               *app = n;
+                               app = &n->narg.next;
+                               savecheckkwd = 0;
+                       }
+                       break;
+               case TREDIR:
+                       *rpp = n = redirnode;
+                       rpp = &n->nfile.next;
+                       parsefname();   /* read name of redirection file */
+                       break;
+               case TLP:
+                       if (
+                               args && app == &args->narg.next &&
+                               !vars && !redir
+                       ) {
+                               struct builtincmd *bcmd;
+                               const char *name;
+
+                               /* We have a function */
+                               if (readtoken() != TRP)
+                                       synexpect(TRP);
+                               name = n->narg.text;
+                               if (
+                                       !goodname(name) || (
+                                               (bcmd = find_builtin(name)) &&
+                                               IS_BUILTIN_SPECIAL(bcmd)
+                                       )
+                               )
+                                       synerror("Bad function name");
+                               n->type = NDEFUN;
+                               checkkwd = CHKNL | CHKKWD | CHKALIAS;
+                               n->narg.next = command();
+                               return n;
+                       }
+                       /* fall through */
+               default:
+                       tokpushback++;
+                       goto out;
+               }
+       }
+out:
+       *app = NULL;
+       *vpp = NULL;
+       *rpp = NULL;
+       n = (union node *)stalloc(sizeof (struct ncmd));
+       n->type = NCMD;
+       n->ncmd.args = args;
+       n->ncmd.assign = vars;
+       n->ncmd.redirect = redir;
+       return n;
+}
+
+static union node *
+makename(void)
+{
+       union node *n;
+
+       n = (union node *)stalloc(sizeof (struct narg));
+       n->type = NARG;
+       n->narg.next = NULL;
+       n->narg.text = wordtext;
+       n->narg.backquote = backquotelist;
+       return n;
+}
+
+void fixredir(union node *n, const char *text, int err)
+{
+       TRACE(("Fix redir %s %d\n", text, err));
+       if (!err)
+               n->ndup.vname = NULL;
+
+       if (is_digit(text[0]) && text[1] == '\0')
+               n->ndup.dupfd = digit_val(text[0]);
+       else if (text[0] == '-' && text[1] == '\0')
+               n->ndup.dupfd = -1;
+       else {
+
+               if (err)
+                       synerror("Bad fd number");
+               else
+                       n->ndup.vname = makename();
+       }
+}
+
+
+static void
+parsefname(void)
+{
+       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 (! 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) {
+               fixredir(n, wordtext, 0);
+       } else {
+               n->nfile.fname = makename();
+       }
+}
+
+
+/*
+ * Input any here documents.
+ */
+
+static void
+parseheredoc(void)
+{
+       struct heredoc *here;
+       union node *n;
+
+       here = heredoclist;
+       heredoclist = 0;
+
+       while (here) {
+               if (needprompt) {
+                       setprompt(2);
+                       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;
+               here = here->next;
+       }
+}
+
+static char peektoken(void)
+{
+       int t;
+
+       t = readtoken();
+       tokpushback++;
+       return tokname_array[t][0];
+}
+
+static int
+readtoken(void)
+{
+       int t;
+#ifdef DEBUG
+       int alreadyseen = tokpushback;
+#endif
+
+#ifdef CONFIG_ASH_ALIAS
+top:
+#endif
+
+       t = xxreadtoken();
+
+       /*
+        * eat newlines
+        */
+       if (checkkwd & CHKNL) {
+               while (t == TNL) {
+                       parseheredoc();
+                       t = xxreadtoken();
+               }
+       }
+
+       if (t != TWORD || quoteflag) {
+               goto out;
+       }
+
+       /*
+        * check for keywords
+        */
+       if (checkkwd & CHKKWD) {
+               const char *const *pp;
+
+               if ((pp = findkwd(wordtext))) {
+                       lasttoken = t = pp - tokname_array;
+                       TRACE(("keyword %s recognized\n", tokname(t)));
+                       goto out;
+               }
+       }
+
+       if (checkkwd & CHKALIAS) {
+#ifdef CONFIG_ASH_ALIAS
+               struct alias *ap;
+               if ((ap = lookupalias(wordtext, 1)) != NULL) {
+                       if (*ap->val) {
+                               pushstring(ap->val, ap);
+                       }
+                       goto top;
+               }
+#endif
+       }
+out:
+       checkkwd = 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 NEW_xxreadtoken
+#ifdef NEW_xxreadtoken
+
+/* singles must be first! */
+static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
+
+static const char xxreadtoken_tokens[] = {
+       TNL, TLP, TRP,          /* only single occurrence allowed */
+       TBACKGND, TPIPE, TSEMI, /* if single occurrence */
+       TEOF,                   /* corresponds to trailing nul */
+       TAND, TOR, TENDCASE,    /* if double occurrence */
+};
+
+#define xxreadtoken_doubles \
+       (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
+#define xxreadtoken_singles \
+       (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
+
+static int xxreadtoken()
+{
+       int c;
+
+       if (tokpushback) {
+               tokpushback = 0;
+               return lasttoken;
+       }
+       if (needprompt) {
+               setprompt(2);
+               needprompt = 0;
+       }
+       startlinno = plinno;
+       for (;;) {                      /* until token or start of word found */
+               c = pgetc_macro();
+
+               if ((c != ' ') && (c != '\t')
+#ifdef CONFIG_ASH_ALIAS
+                       && (c != PEOA)
+#endif
+                       ) {
+                       if (c == '#') {
+                               while ((c = pgetc()) != '\n' && c != PEOF);
+                               pungetc();
+                       } else if (c == '\\') {
+                               if (pgetc() != '\n') {
+                                       pungetc();
+                                       goto READTOKEN1;
+                               }
+                               startlinno = ++plinno;
+                               if (doprompt)
+                                       setprompt(2);
+                       } else {
+                               const char *p
+                                       = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
+
+                               if (c != PEOF) {
+                                       if (c == '\n') {
+                                               plinno++;
+                                               needprompt = doprompt;
+                                       }
+
+                                       p = strchr(xxreadtoken_chars, c);
+                                       if (p == NULL) {
+                                         READTOKEN1:
+                                               return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
+                                       }
+
+                                       if (p - xxreadtoken_chars >= xxreadtoken_singles) {
+                                               if (pgetc() == *p) {    /* double occurrence? */
+                                                       p += xxreadtoken_doubles + 1;
+                                               } else {
+                                                       pungetc();
+                                               }
+                                       }
+                               }
+
+                               return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+                       }
+               }
+       }
+}
+
+
+#else
+#define RETURN(token)   return lasttoken = token
+
+static int
+xxreadtoken(void)
+{
+       int c;
+
+       if (tokpushback) {
+               tokpushback = 0;
+               return lasttoken;
+       }
+       if (needprompt) {
+               setprompt(2);
+               needprompt = 0;
+       }
+       startlinno = plinno;
+       for (;;) {      /* until token or start of word found */
+               c = pgetc_macro();
+               switch (c) {
+               case ' ': case '\t':
+#ifdef CONFIG_ASH_ALIAS
+               case PEOA:
+#endif
+                       continue;
+               case '#':
+                       while ((c = pgetc()) != '\n' && c != PEOF);
+                       pungetc();
+                       continue;
+               case '\\':
+                       if (pgetc() == '\n') {
+                               startlinno = ++plinno;
+                               if (doprompt)
+                                       setprompt(2);
+                               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
+}
+#endif /* NEW_xxreadtoken */
+
+
+/*
+ * 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:;}
+#define PARSEARITH()    {goto parsearith; parsearith_return:;}
+
+static int
+readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
+{
+       int c = firstc;
+       char *out;
+       int len;
+       char line[EOFMARKLEN + 1];
+       struct nodelist *bqlist;
+       int quotef;
+       int dblquote;
+       int varnest;    /* levels of variables expansion */
+       int arinest;    /* levels of arithmetic expansion */
+       int parenlevel; /* levels of parens in arithmetic */
+       int dqvarnest;  /* levels of variables expansion within double quotes */
+       int oldstyle;
+       int prevsyntax; /* syntax before arithmetic */
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &out;
+       (void) &quotef;
+       (void) &dblquote;
+       (void) &varnest;
+       (void) &arinest;
+       (void) &parenlevel;
+       (void) &dqvarnest;
+       (void) &oldstyle;
+       (void) &prevsyntax;
+       (void) &syntax;
+#endif
+
+       startlinno = plinno;
+       dblquote = 0;
+       if (syntax == DQSYNTAX)
+               dblquote = 1;
+       quotef = 0;
+       bqlist = NULL;
+       varnest = 0;
+       arinest = 0;
+       parenlevel = 0;
+       dqvarnest = 0;
+
+       STARTSTACKSTR(out);
+       loop: { /* for each line, until end of word */
+               CHECKEND();     /* set c to PEOF if at end of here document */
+               for (;;) {      /* until end of line or end of word */
+                       CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
+                       switch(SIT(c, syntax)) {
+                       case CNL:       /* '\n' */
+                               if (syntax == BASESYNTAX)
+                                       goto endword;   /* exit outer loop */
+                               USTPUTC(c, out);
+                               plinno++;
+                               if (doprompt)
+                                       setprompt(2);
+                               c = pgetc();
+                               goto loop;              /* continue outer loop */
+                       case CWORD:
+                               USTPUTC(c, out);
+                               break;
+                       case CCTL:
+                               if (eofmark == NULL || dblquote)
+                                       USTPUTC(CTLESC, out);
+                               USTPUTC(c, out);
+                               break;
+                       case CBACK:     /* backslash */
+                               c = pgetc2();
+                               if (c == PEOF) {
+                                       USTPUTC(CTLESC, out);
+                                       USTPUTC('\\', out);
+                                       pungetc();
+                               } else if (c == '\n') {
+                                       if (doprompt)
+                                               setprompt(2);
+                               } else {
+                                       if (
+                                               dblquote &&
+                                               c != '\\' && c != '`' &&
+                                               c != '$' && (
+                                                       c != '"' ||
+                                                       eofmark != NULL
+                                               )
+                                       ) {
+                                               USTPUTC(CTLESC, out);
+                                               USTPUTC('\\', out);
+                                       }
+                                       if (SIT(c, SQSYNTAX) == CCTL)
+                                               USTPUTC(CTLESC, out);
+                                       USTPUTC(c, out);
+                                       quotef++;
+                               }
+                               break;
+                       case CSQUOTE:
+                               syntax = SQSYNTAX;
+quotemark:
+                               if (eofmark == NULL) {
+                                       USTPUTC(CTLQUOTEMARK, out);
+                               }
+                               break;
+                       case CDQUOTE:
+                               syntax = DQSYNTAX;
+                               dblquote = 1;
+                               goto quotemark;
+                       case CENDQUOTE:
+                               if (eofmark != NULL && arinest == 0 &&
+                                   varnest == 0) {
+                                       USTPUTC(c, out);
+                               } else {
+                                       if (dqvarnest == 0) {
+                                               syntax = BASESYNTAX;
+                                               dblquote = 0;
+                                       }
+                                       quotef++;
+                                       goto quotemark;
+                               }
+                               break;
+                       case CVAR:      /* '$' */
+                               PARSESUB();             /* parse substitution */
+                               break;
+                       case CENDVAR:   /* '}' */
+                               if (varnest > 0) {
+                                       varnest--;
+                                       if (dqvarnest > 0) {
+                                               dqvarnest--;
+                                       }
+                                       USTPUTC(CTLENDVAR, out);
+                               } else {
+                                       USTPUTC(c, out);
+                               }
+                               break;
+#ifdef CONFIG_ASH_MATH_SUPPORT
+                       case CLP:       /* '(' in arithmetic */
+                               parenlevel++;
+                               USTPUTC(c, out);
+                               break;
+                       case CRP:       /* ')' in arithmetic */
+                               if (parenlevel > 0) {
+                                       USTPUTC(c, out);
+                                       --parenlevel;
+                               } else {
+                                       if (pgetc() == ')') {
+                                               if (--arinest == 0) {
+                                                       USTPUTC(CTLENDARI, out);
+                                                       syntax = prevsyntax;
+                                                       if (syntax == DQSYNTAX)
+                                                               dblquote = 1;
+                                                       else
+                                                               dblquote = 0;
+                                               } else
+                                                       USTPUTC(')', out);
+                                       } else {
+                                               /*
+                                                * unbalanced parens
+                                                *  (don't 2nd guess - no error)
+                                                */
+                                               pungetc();
+                                               USTPUTC(')', out);
+                                       }
+                               }
+                               break;
+#endif
+                       case CBQUOTE:   /* '`' */
+                               PARSEBACKQOLD();
+                               break;
+                       case CENDFILE:
+                               goto endword;           /* exit outer loop */
+                       case CIGN:
+                               break;
+                       default:
+                               if (varnest == 0)
+                                       goto endword;   /* exit outer loop */
+#ifdef CONFIG_ASH_ALIAS
+                               if (c != PEOA)
+#endif
+                                       USTPUTC(c, out);
+
+                       }
+                       c = pgetc_macro();
+               }
+       }
+endword:
+#ifdef CONFIG_ASH_MATH_SUPPORT
+       if (syntax == ARISYNTAX)
+               synerror("Missing '))'");
+#endif
+       if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
+               synerror("Unterminated quoted string");
+       if (varnest != 0) {
+               startlinno = plinno;
+               /* { */
+               synerror("Missing '}'");
+       }
+       USTPUTC('\0', out);
+       len = out - (char *)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) {
+#ifdef CONFIG_ASH_ALIAS
+               if (c == PEOA) {
+                       c = pgetc2();
+               }
+#endif
+               if (striptabs) {
+                       while (c == '\t') {
+                               c = pgetc2();
+                       }
+               }
+               if (c == *eofmark) {
+                       if (pfgets(line, sizeof line) != NULL) {
+                               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, 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 = NCLOBBER;
+               else if (c == '&')
+                       np->type = NTOFD;
+               else {
+                       np->type = NTO;
+                       pungetc();
+               }
+       } else {        /* c == '<' */
+               np->nfile.fd = 0;
+               switch (c = pgetc()) {
+               case '<':
+                       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();
+                       }
+                       break;
+
+               case '&':
+                       np->type = NFROMFD;
+                       break;
+
+               case '>':
+                       np->type = NFROMTO;
+                       break;
+
+               default:
+                       np->type = NFROM;
+                       pungetc();
+                       break;
+               }
+       }
+       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 <= PEOA_OR_PEOF  ||
+               (c != '(' && c != '{' && !is_name(c) && !is_special(c))
+       ) {
+               USTPUTC('$', out);
+               pungetc();
+       } else if (c == '(') {  /* $(command) or $((arith)) */
+               if (pgetc() == '(') {
+#ifdef CONFIG_ASH_MATH_SUPPORT
+                       PARSEARITH();
+#else
+                       synerror("We unsupport $((arith))");
+#endif
+               } else {
+                       pungetc();
+                       PARSEBACKQNEW();
+               }
+       } else {
+               USTPUTC(CTLVAR, out);
+               typeloc = out - (char *)stackblock();
+               USTPUTC(VSNORMAL, out);
+               subtype = VSNORMAL;
+               if (c == '{') {
+                       c = pgetc();
+                       if (c == '#') {
+                               if ((c = pgetc()) == '}')
+                                       c = '#';
+                               else
+                                       subtype = VSLENGTH;
+                       }
+                       else
+                               subtype = 0;
+               }
+               if (c > PEOA_OR_PEOF && is_name(c)) {
+                       do {
+                               STPUTC(c, out);
+                               c = pgetc();
+                       } while (c > PEOA_OR_PEOF && is_in_name(c));
+               } else if (is_digit(c)) {
+                       do {
+                               STPUTC(c, out);
+                               c = pgetc();
+                       } while (is_digit(c));
+               }
+               else if (is_special(c)) {
+                       USTPUTC(c, out);
+                       c = pgetc();
+               }
+               else
+badsub:                 synerror("Bad substitution");
+
+               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 || arinest)
+                       flags |= VSQUOTE;
+               *((char *)stackblock() + typeloc) = subtype | flags;
+               if (subtype != VSNORMAL) {
+                       varnest++;
+                       if (dblquote || arinest) {
+                               dqvarnest++;
+                       }
+               }
+       }
+       goto parsesub_return;
+}
+
+
+/*
+ * Called to parse command substitutions.  Newstyle is set if the command
+ * is enclosed inside $(...); nlpp is a pointer to the head of the linked
+ * list of commands (passed by reference), and savelen is the number of
+ * characters on the top of the stack which must be preserved.
+ */
+
+parsebackq: {
+       struct nodelist **nlpp;
+       int savepbq;
+       union node *n;
+       char *volatile str;
+       struct jmploc jmploc;
+       struct jmploc *volatile savehandler;
+       size_t savelen;
+       int saveprompt;
+#ifdef __GNUC__
+       (void) &saveprompt;
+#endif
+
+       savepbq = parsebackquote;
+       if (setjmp(jmploc.loc)) {
+               if (str)
+                       ckfree(str);
+               parsebackquote = 0;
+               handler = savehandler;
+               longjmp(handler->loc, 1);
+       }
+       INTOFF;
+       str = NULL;
+       savelen = out - (char *)stackblock();
+       if (savelen > 0) {
+               str = ckmalloc(savelen);
+               memcpy(str, stackblock(), 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.  */
+               char *pout;
+               int pc;
+               size_t psavelen;
+               char *pstr;
+
+
+               STARTSTACKSTR(pout);
+               for (;;) {
+                       if (needprompt) {
+                               setprompt(2);
+                               needprompt = 0;
+                       }
+                       switch (pc = pgetc()) {
+                       case '`':
+                               goto done;
+
+                       case '\\':
+                               if ((pc = pgetc()) == '\n') {
+                                       plinno++;
+                                       if (doprompt)
+                                               setprompt(2);
+                                       /*
+                                        * If eating a newline, avoid putting
+                                        * the newline into the new character
+                                        * stream (via the STPUTC after the
+                                        * switch).
+                                        */
+                                       continue;
+                               }
+                               if (pc != '\\' && pc != '`' && pc != '$'
+                                   && (!dblquote || pc != '"'))
+                                       STPUTC('\\', pout);
+                               if (pc > PEOA_OR_PEOF) {
+                                       break;
+                               }
+                               /* fall through */
+
+                       case PEOF:
+#ifdef CONFIG_ASH_ALIAS
+                       case PEOA:
+#endif
+                               startlinno = plinno;
+                               synerror("EOF in backquote substitution");
+
+                       case '\n':
+                               plinno++;
+                               needprompt = doprompt;
+                               break;
+
+                       default:
+                               break;
+                       }
+                       STPUTC(pc, pout);
+               }
+done:
+               STPUTC('\0', pout);
+               psavelen = pout - (char *)stackblock();
+               if (psavelen > 0) {
+                       pstr = grabstackstr(pout);
+                       setinputstring(pstr);
+               }
+       }
+       nlpp = &bqlist;
+       while (*nlpp)
+               nlpp = &(*nlpp)->next;
+       *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+       (*nlpp)->next = NULL;
+       parsebackquote = oldstyle;
+
+       if (oldstyle) {
+               saveprompt = doprompt;
+               doprompt = 0;
+       }
+
+       n = list(2);
+
+       if (oldstyle)
+               doprompt = saveprompt;
+       else {
+               if (readtoken() != TRP)
+                       synexpect(TRP);
+       }
+
+       (*nlpp)->n = n;
+       if (oldstyle) {
+               /*
+                * Start reading from old file again, ignoring any pushed back
+                * tokens left from the backquote parsing
+                */
+               popfile();
+               tokpushback = 0;
+       }
+       while (stackblocksize() <= savelen)
+               growstackblock();
+       STARTSTACKSTR(out);
+       if (str) {
+               memcpy(out, str, savelen);
+               STADJUST(savelen, out);
+               INTOFF;
+               ckfree(str);
+               str = NULL;
+               INTON;
+       }
+       parsebackquote = savepbq;
+       handler = savehandler;
+       if (arinest || dblquote)
+               USTPUTC(CTLBACKQ | CTLQUOTE, out);
+       else
+               USTPUTC(CTLBACKQ, out);
+       if (oldstyle)
+               goto parsebackq_oldreturn;
+       else
+               goto parsebackq_newreturn;
+}
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+/*
+ * Parse an arithmetic expansion (indicate start of one and set state)
+ */
+parsearith: {
+
+       if (++arinest == 1) {
+               prevsyntax = syntax;
+               syntax = ARISYNTAX;
+               USTPUTC(CTLARI, out);
+               if (dblquote)
+                       USTPUTC('"',out);
+               else
+                       USTPUTC(' ',out);
+       } else {
+               /*
+                * we collapse embedded arithmetic expansion to
+                * parenthesis, which should be equivalent
+                */
+               USTPUTC('(', out);
+       }
+       goto parsearith_return;
+}
+#endif
+
+} /* end of readtoken */
+
+
+
+/*
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
+ */
+
+static int
+noexpand(char *text)
+{
+       char *p;
+       char c;
+
+       p = text;
+       while ((c = *p++) != '\0') {
+               if (c == CTLQUOTEMARK)
+                       continue;
+               if (c == CTLESC)
+                       p++;
+               else if (SIT(c, BASESYNTAX) == CCTL)
+                       return 0;
+       }
+       return 1;
+}
+
+
+/*
+ * Return of a legal variable name (a letter or underscore followed by zero or
+ * more letters, underscores, and digits).
+ */
+
+static char *
+endofname(const char *name)
+{
+       char *p;
+
+       p = (char *) name;
+       if (! is_name(*p))
+               return p;
+       while (*++p) {
+               if (! is_in_name(*p))
+                       break;
+       }
+       return p;
+}
+
+
+/*
+ * 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];
+       int l;
+
+       l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+       if (token >= 0)
+               sprintf(msg + l, " (expecting %s)", tokname(token));
+       synerror(msg);
+       /* NOTREACHED */
+}
+
+static void
+synerror(const char *msg)
+{
+       error("Syntax error: %s", msg);
+       /* NOTREACHED */
+}
+
+
+/*
+ * called by editline -- any expansions to the prompt
+ *    should be added here.
+ */
+
+static void setprompt(int whichprompt)
+{
+       const char *prompt;
+
+       switch (whichprompt) {
+       case 1:
+               prompt = ps1val();
+               break;
+       case 2:
+               prompt = ps2val();
+               break;
+       default:                        /* 0 */
+               prompt = nullstr;
+       }
+       putprompt(prompt);
+}
+
+
+static const char *const *findkwd(const char *s)
+{
+       return bsearch(s, tokname_array + KWDOFFSET,
+                      (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
+                                  sizeof(const char *), pstrcmp);
+}
+
+/*      $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $      */
+
+/*
+ * Code for dealing with input/output redirection.
+ */
+
+#define EMPTY -2                /* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096          /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+static inline int
+noclobberopen(const char *fname)
+{
+       int r, fd;
+       struct stat finfo, finfo2;
+
+       /*
+        * If the file exists and is a regular file, return an error
+        * immediately.
+        */
+       r = stat(fname, &finfo);
+       if (r == 0 && S_ISREG(finfo.st_mode)) {
+               errno = EEXIST;
+               return -1;
+       }
+
+       /*
+        * If the file was not present (r != 0), make sure we open it
+        * exclusively so that if it is created before we open it, our open
+        * will fail.  Make sure that we do not truncate an existing file.
+        * Note that we don't turn on O_EXCL unless the stat failed -- if the
+        * file was not a regular file, we leave O_EXCL off.
+        */
+       if (r != 0)
+               return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+       fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+       /* If the open failed, return the file descriptor right away. */
+       if (fd < 0)
+               return fd;
+
+       /*
+        * OK, the open succeeded, but the file may have been changed from a
+        * non-regular file to a regular file between the stat and the open.
+        * We are assuming that the O_EXCL open handles the case where FILENAME
+        * did not exist and is symlinked to an existing file between the stat
+        * and open.
+        */
+
+       /*
+        * If we can open it and fstat the file descriptor, and neither check
+        * revealed that it was a regular file, and the file has not been
+        * replaced, return the file descriptor.
+        */
+        if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
+            finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+               return fd;
+
+       /* The file has been replaced.  badness. */
+       close(fd);
+       errno = EEXIST;
+       return -1;
+}
+
+/*
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+
+static inline int
+openhere(union node *redir)
+{
+       int pip[2];
+       size_t len = 0;
+
+       if (pipe(pip) < 0)
+               error("Pipe call failed");
+       if (redir->type == NHERE) {
+               len = strlen(redir->nhere.doc->narg.text);
+               if (len <= PIPESIZE) {
+                       bb_full_write(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)
+                       bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
+               else
+                       expandhere(redir->nhere.doc, pip[1]);
+               _exit(0);
+       }
+out:
+       close(pip[1]);
+       return pip[0];
+}
+
+static int
+openredirect(union node *redir)
+{
+       char *fname;
+       int f;
+
+       switch (redir->nfile.type) {
+       case NFROM:
+               fname = redir->nfile.expfname;
+               if ((f = open(fname, O_RDONLY)) < 0)
+                       goto eopen;
+               break;
+       case NFROMTO:
+               fname = redir->nfile.expfname;
+               if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+                       goto ecreate;
+               break;
+       case NTO:
+               /* Take care of noclobber mode. */
+               if (Cflag) {
+                       fname = redir->nfile.expfname;
+                       if ((f = noclobberopen(fname)) < 0)
+                               goto ecreate;
+                       break;
+               }
+               /* FALLTHROUGH */
+       case NCLOBBER:
+               fname = redir->nfile.expfname;
+               if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+                       goto ecreate;
+               break;
+       case NAPPEND:
+               fname = redir->nfile.expfname;
+               if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+                       goto ecreate;
+               break;
+       default:
+#ifdef DEBUG
+               abort();
+#endif
+               /* Fall through to eliminate warning. */
+       case NTOFD:
+       case NFROMFD:
+               f = -1;
+               break;
+       case NHERE:
+       case NXHERE:
+               f = openhere(redir);
+               break;
+       }
+
+       return f;
+ecreate:
+       error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+eopen:
+       error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+}
+
+static inline void
+dupredirect(union node *redir, int f)
+{
+       int fd = redir->nfile.fd;
+
+       if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+               if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
+                               copyfd(redir->ndup.dupfd, fd);
+               }
+               return;
+       }
+
+       if (f != fd) {
+               copyfd(f, fd);
+               close(f);
+       }
+       return;
+}
+
+/*
+ * 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;
+       int i;
+       int fd;
+       int newfd;
+       int *p;
+       nullredirs++;
+       if (!redir) {
+               return;
+       }
+       sv = NULL;
+       INTOFF;
+       if (flags & REDIR_PUSH) {
+               struct redirtab *q;
+               q = ckmalloc(sizeof (struct redirtab));
+               q->next = redirlist;
+               redirlist = q;
+               q->nullredirs = nullredirs - 1;
+               for (i = 0 ; i < 10 ; i++)
+                       q->renamed[i] = EMPTY;
+               nullredirs = 0;
+               sv = q;
+       }
+       n = redir;
+       do {
+               fd = n->nfile.fd;
+               if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
+                   n->ndup.dupfd == fd)
+                       continue; /* redirect from/to same file descriptor */
+
+               newfd = openredirect(n);
+               if (fd == newfd)
+                       continue;
+               if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
+                       i = fcntl(fd, F_DUPFD, 10);
+
+                       if (i == -1) {
+                               i = errno;
+                               if (i != EBADF) {
+                                       close(newfd);
+                                       errno = i;
+                                       error("%d: %m", fd);
+                                       /* NOTREACHED */
+                               }
+                       } else {
+                               *p = i;
+                               close(fd);
+                       }
+               } else {
+                       close(fd);
+               }
+               dupredirect(n, newfd);
+       } while ((n = n->nfile.next));
+       INTON;
+       if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+               preverrout_fd = sv->renamed[2];
+}
+
+
+/*
+ * Undo the effects of the last redirection.
+ */
+
+void
+popredir(int drop)
+{
+       struct redirtab *rp;
+       int i;
+
+       if (--nullredirs >= 0)
+               return;
+       INTOFF;
+       rp = redirlist;
+       for (i = 0 ; i < 10 ; i++) {
+               if (rp->renamed[i] != EMPTY) {
+                       if (!drop) {
+                               close(i);
+                               copyfd(rp->renamed[i], i);
+                       }
+                       close(rp->renamed[i]);
+               }
+       }
+       redirlist = rp->next;
+       nullredirs = rp->nullredirs;
+       ckfree(rp);
+       INTON;
+}
+
+/*
+ * Undo all redirections.  Called on error or interrupt.
+ */
+
+/*
+ * Discard all saved file descriptors.
+ */
+
+void
+clearredir(int drop)
+{
+       for (;;) {
+               nullredirs = 0;
+               if (!redirlist)
+                       break;
+               popredir(drop);
+       }
+}
+
+
+/*
+ * Copy a file descriptor to be >= to.  Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
+ */
+
+int
+copyfd(int from, int to)
+{
+       int newfd;
+
+       newfd = fcntl(from, F_DUPFD, to);
+       if (newfd < 0) {
+               if (errno == EMFILE)
+                       return EMPTY;
+               else
+                       error("%d: %m", from);
+       }
+       return newfd;
+}
+
+
+int
+redirectsafe(union node *redir, int flags)
+{
+       int err;
+       volatile int saveint;
+       struct jmploc *volatile savehandler = handler;
+       struct jmploc jmploc;
+
+       SAVEINT(saveint);
+       if (!(err = setjmp(jmploc.loc) * 2)) {
+               handler = &jmploc;
+               redirect(redir, flags);
+       }
+       handler = savehandler;
+       if (err && exception != EXERROR)
+               longjmp(handler->loc, 1);
+       RESTOREINT(saveint);
+       return err;
+}
+
+/*      $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $    */
+
+#ifdef DEBUG
+static void shtree(union node *, int, char *, FILE*);
+static void shcmd(union node *, FILE *);
+static void sharg(union node *, FILE *);
+static void indent(int, char *, FILE *);
+static void trstring(char *);
+
+
+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;
+       const char *s;
+
+       if (n == NULL)
+               return;
+
+       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;
+       const char *s;
+       int dftfd;
+
+       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 NCLOBBER:  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;
+                       case NFROMTO:   s = "<>"; dftfd = 0; break;
+                       default:        s = "*error*"; 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) {
+               out1fmt("<node type %d>\n", arg->type);
+               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:
+                               out1fmt("<subtype %d>", subtype);
+                       }
+                       break;
+               case CTLENDVAR:
+                    putc('}', fp);
+                    break;
+               case CTLBACKQ:
+               case CTLBACKQ|CTLQUOTE:
+                       putc('$', fp);
+                       putc('(', fp);
+                       shtree(bqlist->n, -1, NULL, fp);
+                       putc(')', fp);
+                       break;
+               default:
+                       putc(*p, fp);
+                       break;
+               }
+       }
+}
+
+
+static void
+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);
+       }
+}
+
+
+
+/*
+ * Debugging stuff.
+ */
+
+
+FILE *tracefile;
+
+
+void
+trputc(int c)
+{
+       if (debug != 1)
+               return;
+       putc(c, tracefile);
+}
+
+void
+trace(const char *fmt, ...)
+{
+       va_list va;
+
+       if (debug != 1)
+               return;
+       va_start(va, fmt);
+       (void) vfprintf(tracefile, fmt, va);
+       va_end(va);
+}
+
+void
+tracev(const char *fmt, va_list va)
+{
+       if (debug != 1)
+               return;
+       (void) vfprintf(tracefile, fmt, va);
+}
+
+
+void
+trputs(const char *s)
+{
+       if (debug != 1)
+               return;
+       fputs(s, tracefile);
+}
+
+
+static void
+trstring(char *s)
+{
+       char *p;
+       char c;
+
+       if (debug != 1)
+               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);
+}
+
+
+void
+trargs(char **ap)
+{
+       if (debug != 1)
+               return;
+       while (*ap) {
+               trstring(*ap++);
+               if (*ap)
+                       putc(' ', tracefile);
+               else
+                       putc('\n', tracefile);
+       }
+}
+
+
+void
+opentrace(void)
+{
+       char s[100];
+#ifdef O_APPEND
+       int flags;
+#endif
+
+       if (debug != 1) {
+               if (tracefile)
+                       fflush(tracefile);
+               /* leave open because libedit might be using it */
+               return;
+       }
+       scopy("./trace", s);
+       if (tracefile) {
+               if (!freopen(s, "a", tracefile)) {
+                       fprintf(stderr, "Can't re-open %s\n", s);
+                       debug = 0;
+                       return;
+               }
+       } else {
+               if ((tracefile = fopen(s, "a")) == NULL) {
+                       fprintf(stderr, "Can't open %s\n", s);
+                       debug = 0;
+                       return;
+               }
+       }
+#ifdef O_APPEND
+       if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
+               fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
+#endif
+       setlinebuf(tracefile);
+       fputs("\nTracing started.\n", tracefile);
+}
+#endif /* DEBUG */
+
+
+/*      $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $       */
+
+/*
+ * 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,
+ */
+
+#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 S_RESET 5               /* temporary - to reset a hard ignored sig */
+
+
+
+/*
+ * The trap builtin.
+ */
+
+int
+trapcmd(int argc, char **argv)
+{
+       char *action;
+       char **ap;
+       int signo;
+
+       nextopt(nullstr);
+       ap = argptr;
+       if (!*ap) {
+               for (signo = 0 ; signo < NSIG ; signo++) {
+                       if (trap[signo] != NULL) {
+                               const char *sn;
+
+                               sn = u_signal_names(0, &signo, 0);
+                               if (sn == NULL)
+                                       sn = "???";
+                               out1fmt("trap -- %s %s\n",
+                                       single_quote(trap[signo]), sn);
+                       }
+               }
+               return 0;
+       }
+       if (!ap[1])
+               action = NULL;
+       else
+               action = *ap++;
+       while (*ap) {
+               if ((signo = decode_signal(*ap, 0)) < 0)
+                       error("%s: bad trap", *ap);
+               INTOFF;
+               if (action) {
+                       if (action[0] == '-' && action[1] == '\0')
+                               action = NULL;
+                       else
+                               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(void)
+{
+       char **tp;
+
+       for (tp = trap ; tp < &trap[NSIG] ; 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.
+ */
+
+void
+setsignal(int signo)
+{
+       int action;
+       char *t, tsig;
+       struct sigaction act;
+
+       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 || minusc || sflag == 0)
+                               action = S_CATCH;
+                       break;
+               case SIGQUIT:
+#ifdef DEBUG
+                       if (debug)
+                               break;
+#endif
+                       /* FALLTHROUGH */
+               case SIGTERM:
+                       if (iflag)
+                               action = S_IGN;
+                       break;
+#if JOBS
+               case SIGTSTP:
+               case SIGTTOU:
+                       if (mflag)
+                               action = S_IGN;
+                       break;
+#endif
+               }
+       }
+
+       t = &sigmode[signo - 1];
+       tsig = *t;
+       if (tsig == 0) {
+               /*
+                * current setting unknown
+                */
+               if (sigaction(signo, 0, &act) == -1) {
+                       /*
+                        * Pretend it worked; maybe we should give a warning
+                        * here, but other shells don't. We don't alter
+                        * sigmode, so that we retry every time.
+                        */
+                       return;
+               }
+               if (act.sa_handler == SIG_IGN) {
+                       if (mflag && (signo == SIGTSTP ||
+                            signo == SIGTTIN || signo == SIGTTOU)) {
+                               tsig = S_IGN;   /* don't hard ignore these */
+                       } else
+                               tsig = S_HARD_IGN;
+               } else {
+                       tsig = S_RESET; /* force to be set */
+               }
+       }
+       if (tsig == S_HARD_IGN || tsig == action)
+               return;
+       switch (action) {
+       case S_CATCH:
+               act.sa_handler = onsig;
+               break;
+       case S_IGN:
+               act.sa_handler = SIG_IGN;
+               break;
+       default:
+               act.sa_handler = SIG_DFL;
+       }
+       *t = action;
+       act.sa_flags = 0;
+       sigfillset(&act.sa_mask);
+       sigaction(signo, &act, 0);
+}
+
+/*
+ * 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)
+{
+       gotsig[signo - 1] = 1;
+       pendingsigs = signo;
+
+       if (exsig || (signo == SIGINT && !trap[SIGINT])) {
+               if (!suppressint)
+                       onint();
+               intpending = 1;
+       }
+}
+
+
+/*
+ * Called to execute a trap.  Perhaps we should avoid entering new trap
+ * handlers while we are executing a trap handler.
+ */
+
+void
+dotrap(void)
+{
+       char *p;
+       char *q;
+       int savestatus;
+
+       savestatus = exitstatus;
+       q = gotsig;
+       while (pendingsigs = 0, xbarrier(), (p = memchr(q, 1, NSIG - 1))) {
+               *p = 0;
+               p = trap[p - q + 1];
+               if (!p)
+                       continue;
+               evalstring(p);
+               exitstatus = savestatus;
+       }
+}
+
+
+/*
+ * Controls whether the shell is interactive or not.
+ */
+
+void
+setinteractive(int on)
+{
+       static int is_interactive;
+
+       if (++on == is_interactive)
+               return;
+       is_interactive = on;
+       setsignal(SIGINT);
+       setsignal(SIGQUIT);
+       setsignal(SIGTERM);
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+               if(is_interactive > 1) {
+                       /* Looks like they want an interactive shell */
+                       static int do_banner;
+
+                               if(!do_banner) {
+                                       out1fmt(
+                       "\n\n" BB_BANNER " Built-in shell (ash)\n"
+                       "Enter 'help' for a list of built-in commands.\n\n");
+                                       do_banner++;
+                               }
+               }
+#endif
+}
+
+
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+/*** List the available builtins ***/
+
+static int helpcmd(int argc, char **argv)
+{
+       int col, i;
+
+       out1fmt("\nBuilt-in commands:\n-------------------\n");
+       for (col = 0, i = 0; i < NUMBUILTINS; i++) {
+               col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
+                                         builtincmd[i].name + 1);
+               if (col > 60) {
+                       out1fmt("\n");
+                       col = 0;
+               }
+       }
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       {
+               extern const struct BB_applet applets[];
+               extern const size_t NUM_APPLETS;
+
+               for (i = 0; i < NUM_APPLETS; i++) {
+
+                       col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
+                       if (col > 60) {
+                               out1fmt("\n");
+                               col = 0;
+                       }
+               }
+       }
+#endif
+       out1fmt("\n\n");
+       return EXIT_SUCCESS;
+}
+#endif /* CONFIG_FEATURE_SH_EXTRA_QUIET */
+
+/*
+ * Called to exit the shell.
+ */
+
+void
+exitshell(void)
+{
+       struct jmploc loc;
+       char *p;
+       int status;
+       int jmp;
+
+       jmp = setjmp(loc.loc);
+       status = exitstatus;
+       TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
+       if (jmp)
+               goto out;
+       handler = &loc;
+       if ((p = trap[0]) != NULL && *p != '\0') {
+               trap[0] = NULL;
+               evalstring(p);
+       }
+       flushall();
+       setjobctl(0);
+#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+       if (iflag && rootshell) {
+               const char *hp = lookupvar("HISTFILE");
+
+               if(hp != NULL )
+                       save_history ( hp );
+       }
+#endif
+out:
+       _exit(status);
+       /* NOTREACHED */
+}
+
+static int decode_signal(const char *string, int minsig)
+{
+       int signo;
+       const char *name = u_signal_names(string, &signo, minsig);
+
+       return name ? signo : -1;
+}
+
+/*      $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $     */
+
+static struct var *vartab[VTABSIZE];
+
+static int vpcmp(const void *, const void *);
+static struct var **findvar(struct var **, const char *);
+
+/*
+ * Initialize the variable symbol tables and import the environment
+ */
+
+
+#ifdef CONFIG_ASH_GETOPTS
+/*
+ * Safe version of setvar, returns 1 on success 0 on failure.
+ */
+
+int
+setvarsafe(const char *name, const char *val, int flags)
+{
+       int err;
+       volatile int saveint;
+       struct jmploc *volatile savehandler = handler;
+       struct jmploc jmploc;
+
+       SAVEINT(saveint);
+       if (setjmp(jmploc.loc))
+               err = 1;
+       else {
+               handler = &jmploc;
+               setvar(name, val, flags);
+               err = 0;
+       }
+       handler = savehandler;
+       RESTOREINT(saveint);
+       return err;
+}
+#endif
+
+/*
+ * Set the value of a variable.  The flags argument is ored with the
+ * flags of the variable.  If val is NULL, the variable is unset.
+ */
+
+static void
+setvar(const char *name, const char *val, int flags)
+{
+       char *p, *q;
+       size_t namelen;
+       char *nameeq;
+       size_t vallen;
+
+       q = endofname(name);
+       p = strchrnul(q, '=');
+       namelen = p - name;
+       if (!namelen || p != q)
+               error("%.*s: bad variable name", namelen, name);
+       vallen = 0;
+       if (val == NULL) {
+               flags |= VUNSET;
+       } else {
+               vallen = strlen(val);
+       }
+       INTOFF;
+       p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
+       *p++ = '\0';
+       if (vallen) {
+               p[-1] = '=';
+               p = mempcpy(p, val, vallen);
+       }
+       *p = '\0';
+       setvareq(nameeq, flags | VNOSAVE);
+       INTON;
+}
+
+
+/*
+ * Same as setvar except that the variable and value are passed in
+ * the first argument as name=value.  Since the first argument will
+ * be actually stored in the table, it should not be a string that
+ * will go away.
+ * Called with interrupts off.
+ */
+
+void
+setvareq(char *s, int flags)
+{
+       struct var *vp, **vpp;
+
+       vpp = hashvar(s);
+       flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
+       vp = *findvar(vpp, s);
+       if (vp) {
+               if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+                       const char *n;
+
+                       if (flags & VNOSAVE)
+                               free(s);
+                       n = vp->text;
+                       error("%.*s: is read only", strchrnul(n, '=') - n, n);
+               }
+
+               if (flags & VNOSET)
+                       return;
+
+               if (vp->func && (flags & VNOFUNC) == 0)
+                       (*vp->func)(strchrnul(s, '=') + 1);
+
+               if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+                       ckfree(vp->text);
+
+               flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
+       } else {
+               if (flags & VNOSET)
+                       return;
+               /* not found */
+               vp = ckmalloc(sizeof (*vp));
+               vp->next = *vpp;
+               vp->func = NULL;
+               *vpp = vp;
+       }
+       if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
+               s = savestr(s);
+       vp->text = s;
+       vp->flags = flags;
+}
+
+
+/*
+ * Process a linked list of variable assignments.
+ */
+
+static void
+listsetvar(struct strlist *list_set_var, int flags)
+{
+       struct strlist *lp = list_set_var;
+
+       if (!lp)
+               return;
+       INTOFF;
+       do {
+               setvareq(lp->text, flags);
+       } while ((lp = lp->next));
+       INTON;
+}
+
+
+/*
+ * Find the value of a variable.  Returns NULL if not set.
+ */
+
+static char *
+lookupvar(const char *name)
+{
+       struct var *v;
+
+       if ((v = *findvar(hashvar(name), name))) {
+#ifdef DYNAMIC_VAR
+       /*
+        * Dynamic variables are implemented roughly the same way they are
+        * in bash. Namely, they're "special" so long as they aren't unset.
+        * As soon as they're unset, they're no longer dynamic, and dynamic
+        * lookup will no longer happen at that point. -- PFM.
+        */
+               if((v->flags & VDYNAMIC))
+                       (*v->func)(NULL);
+#endif
+               if(!(v->flags & VUNSET))
+                       return strchrnul(v->text, '=') + 1;
+       }
+
+       return NULL;
+}
+
+
+/*
+ * Search the environment of a builtin command.
+ */
+
+static char *
+bltinlookup(const char *name)
+{
+       struct strlist *sp;
+
+       for (sp = cmdenviron ; sp ; sp = sp->next) {
+               if (varequal(sp->text, name))
+                       return strchrnul(sp->text, '=') + 1;
+       }
+       return lookupvar(name);
+}
+
+
+/*
+ * Generate a list of variables satisfying the given conditions.
+ */
+
+static char **
+listvars(int on, int off, char ***end)
+{
+       struct var **vpp;
+       struct var *vp;
+       char **ep;
+       int mask;
+
+       STARTSTACKSTR(ep);
+       vpp = vartab;
+       mask = on | off;
+       do {
+               for (vp = *vpp ; vp ; vp = vp->next)
+                       if ((vp->flags & mask) == on) {
+                               if (ep == stackstrend())
+                                       ep = growstackstr();
+                               *ep++ = (char *) vp->text;
+                       }
+       } while (++vpp < vartab + VTABSIZE);
+       if (ep == stackstrend())
+               ep = growstackstr();
+       if (end)
+               *end = ep;
+       *ep++ = NULL;
+       return grabstackstr(ep);
+}
+
+
+/*
+ * POSIX requires that 'set' (but not export or readonly) output the
+ * variables in lexicographic order - by the locale's collating order (sigh).
+ * Maybe we could keep them in an ordered balanced binary tree
+ * instead of hashed lists.
+ * For now just roll 'em through qsort for printing...
+ */
+
+static int
+showvars(const char *sep_prefix, int on, int off)
+{
+       const char *sep;
+       char **ep, **epend;
+
+       ep = listvars(on, off, &epend);
+       qsort(ep, epend - ep, sizeof(char *), vpcmp);
+
+       sep = *sep_prefix ? spcstr : sep_prefix;
+
+       for (; ep < epend; ep++) {
+               const char *p;
+               const char *q;
+
+               p = strchrnul(*ep, '=');
+               q = nullstr;
+               if (*p)
+                       q = single_quote(++p);
+
+               out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
+       }
+
+       return 0;
+}
+
+
+
+/*
+ * The export and readonly commands.
+ */
+
+static int
+exportcmd(int argc, char **argv)
+{
+       struct var *vp;
+       char *name;
+       const char *p;
+       char **aptr;
+       int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+       int notp;
+
+       notp = nextopt("p") - 'p';
+       if (notp && ((name = *(aptr = argptr)))) {
+               do {
+                       if ((p = strchr(name, '=')) != NULL) {
+                               p++;
+                       } else {
+                               if ((vp = *findvar(hashvar(name), name))) {
+                                       vp->flags |= flag;
+                                       continue;
+                               }
+                       }
+                       setvar(name, p, flag);
+               } while ((name = *++aptr) != NULL);
+       } else {
+               showvars(argv[0], flag, 0);
+       }
+       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.
+ */
+
+static inline 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') {
+               char *p;
+               p = ckmalloc(sizeof(optlist));
+               lvp->text = memcpy(p, optlist, sizeof(optlist));
+               vp = NULL;
+       } else {
+               char *eq;
+
+               vpp = hashvar(name);
+               vp = *findvar(vpp, name);
+               eq = strchr(name, '=');
+               if (vp == NULL) {
+                       if (eq)
+                               setvareq(name, VSTRFIXED);
+                       else
+                               setvar(name, NULL, VSTRFIXED);
+                       vp = *vpp;      /* the new variable */
+                       lvp->flags = VUNSET;
+               } else {
+                       lvp->text = vp->text;
+                       lvp->flags = vp->flags;
+                       vp->flags |= VSTRFIXED|VTEXTFIXED;
+                       if (eq)
+                               setvareq(name, 0);
+               }
+       }
+       lvp->vp = vp;
+       lvp->next = localvars;
+       localvars = lvp;
+       INTON;
+}
+
+/*
+ * The "local" command.
+ */
+
+static int
+localcmd(int argc, char **argv)
+{
+       char *name;
+
+       argv = argptr;
+       while ((name = *argv++) != NULL) {
+               mklocal(name);
+       }
+       return 0;
+}
+
+
+/*
+ * Called after a function returns.
+ * Interrupts must be off.
+ */
+
+static void
+poplocalvars(void)
+{
+       struct localvar *lvp;
+       struct var *vp;
+
+       while ((lvp = localvars) != NULL) {
+               localvars = lvp->next;
+               vp = lvp->vp;
+               TRACE(("poplocalvar %s", vp ? vp->text : "-"));
+               if (vp == NULL) {       /* $- saved */
+                       memcpy(optlist, lvp->text, sizeof(optlist));
+                       ckfree(lvp->text);
+                       optschanged();
+               } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+                       unsetvar(vp->text);
+               } else {
+                       if (vp->func)
+                               (*vp->func)(strchrnul(lvp->text, '=') + 1);
+                       if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+                               ckfree(vp->text);
+                       vp->flags = lvp->flags;
+                       vp->text = lvp->text;
+               }
+               ckfree(lvp);
+       }
+}
+
+
+/*
+ * 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 flag = 0;
+       int ret = 0;
+
+       while ((i = nextopt("vf")) != '\0') {
+               flag = i;
+       }
+
+       for (ap = argptr; *ap ; ap++) {
+               if (flag != 'f') {
+                       i = unsetvar(*ap);
+                       ret |= i;
+                       if (!(i & 2))
+                               continue;
+               }
+               if (flag != 'v')
+                       unsetfunc(*ap);
+       }
+       return ret & 1;
+}
+
+
+/*
+ * Unset the specified variable.
+ */
+
+int
+unsetvar(const char *s)
+{
+       struct var **vpp;
+       struct var *vp;
+       int retval;
+
+       vpp = findvar(hashvar(s), s);
+       vp = *vpp;
+       retval = 2;
+       if (vp) {
+               int flags = vp->flags;
+
+               retval = 1;
+               if (flags & VREADONLY)
+                       goto out;
+#ifdef DYNAMIC_VAR
+               vp->flags &= ~VDYNAMIC;
+#endif
+               if (flags & VUNSET)
+                       goto ok;
+               if ((flags & VSTRFIXED) == 0) {
+                       INTOFF;
+                       if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+                               ckfree(vp->text);
+                       *vpp = vp->next;
+                       ckfree(vp);
+                       INTON;
+               } else {
+                       setvar(s, 0, 0);
+                       vp->flags &= ~VEXPORT;
+               }
+ok:
+               retval = 0;
+       }
+
+out:
+       return retval;
+}
+
+
+
+/*
+ * Find the appropriate entry in the hash table from the name.
+ */
+
+static struct var **
+hashvar(const char *p)
+{
+       unsigned int hashval;
+
+       hashval = ((unsigned char) *p) << 4;
+       while (*p && *p != '=')
+               hashval += (unsigned char) *p++;
+       return &vartab[hashval % VTABSIZE];
+}
+
+
+
+/*
+ * Compares two strings up to the first = or '\0'.  The first
+ * string must be terminated by '='; the second may be terminated by
+ * either '=' or '\0'.
+ */
+
+int
+varcmp(const char *p, const char *q)
+{
+       int c, d;
+
+       while ((c = *p) == (d = *q)) {
+               if (!c || c == '=')
+                       goto out;
+               p++;
+               q++;
+       }
+       if (c == '=')
+               c = 0;
+       if (d == '=')
+               d = 0;
+out:
+       return c - d;
+}
+
+static int
+vpcmp(const void *a, const void *b)
+{
+       return varcmp(*(const char **)a, *(const char **)b);
+}
+
+static struct var **
+findvar(struct var **vpp, const char *name)
+{
+       for (; *vpp; vpp = &(*vpp)->next) {
+               if (varequal((*vpp)->text, name)) {
+                       break;
+               }
+       }
+       return vpp;
+}
+/*      $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $      */
+
+#include <sys/times.h>
+
+static const unsigned char timescmd_str[] = {
+       ' ',  offsetof(struct tms, tms_utime),
+       '\n', offsetof(struct tms, tms_stime),
+       ' ',  offsetof(struct tms, tms_cutime),
+       '\n', offsetof(struct tms, tms_cstime),
+       0
+};
+
+static int timescmd(int ac, char **av)
+{
+       long int clk_tck, s, t;
+       const unsigned char *p;
+       struct tms buf;
+
+       clk_tck = sysconf(_SC_CLK_TCK);
+       times(&buf);
+
+       p = timescmd_str;
+       do {
+               t = *(clock_t *)(((char *) &buf) + p[1]);
+               s = t / clk_tck;
+               out1fmt("%ldm%ld.%.3lds%c",
+                       s/60, s%60,
+                       ((t - s * clk_tck) * 1000) / clk_tck,
+                       p[0]);
+       } while (*(p += 2));
+
+       return 0;
+}
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static arith_t
+dash_arith(const char *s)
+{
+       arith_t result;
+       int errcode = 0;
+
+       INTOFF;
+       result = arith(s, &errcode);
+       if (errcode < 0) {
+               if (errcode == -3)
+                       error("exponent less than 0");
+               else if (errcode == -2)
+                       error("divide by zero");
+               else if (errcode == -5)
+                       error("expression recursion loop detected");
+               else
+                       synerror(s);
+       }
+       INTON;
+
+       return (result);
+}
+
+
+/*
+ *  The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
+ *  Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+ *
+ *  Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ */
+
+static int
+letcmd(int argc, char **argv)
+{
+       char **ap;
+       arith_t i;
+
+       ap = argv + 1;
+       if(!*ap)
+               error("expression expected");
+       for (ap = argv + 1; *ap; ap++) {
+               i = dash_arith(*ap);
+       }
+
+       return (!i);
+}
+#endif /* CONFIG_ASH_MATH_SUPPORT */
+
+/*      $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $  */
+
+/*
+ * Miscellaneous builtins.
+ */
+
+#undef rflag
+
+#ifdef __GLIBC__
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
+typedef enum __rlimit_resource rlim_t;
+#endif
+#endif
+
+
+/*
+ * The read builtin.  The -e option causes backslashes to escape the
+ * following character.
+ *
+ * This uses unbuffered input, which may be avoidable in some cases.
+ */
+
+static int
+readcmd(int argc, char **argv)
+{
+       char **ap;
+       int backslash;
+       char c;
+       int rflag;
+       char *prompt;
+       const char *ifs;
+       char *p;
+       int startword;
+       int status;
+       int i;
+
+       rflag = 0;
+       prompt = NULL;
+       while ((i = nextopt("p:r")) != '\0') {
+               if (i == 'p')
+                       prompt = optionarg;
+               else
+                       rflag = 1;
+       }
+       if (prompt && isatty(0)) {
+               out2str(prompt);
+       }
+       if (*(ap = argptr) == NULL)
+               error("arg count");
+       if ((ifs = bltinlookup("IFS")) == NULL)
+               ifs = defifs;
+       status = 0;
+       startword = 1;
+       backslash = 0;
+       STARTSTACKSTR(p);
+       for (;;) {
+               if (read(0, &c, 1) != 1) {
+                       status = 1;
+                       break;
+               }
+               if (c == '\0')
+                       continue;
+               if (backslash) {
+                       backslash = 0;
+                       if (c != '\n')
+                               goto put;
+                       continue;
+               }
+               if (!rflag && c == '\\') {
+                       backslash++;
+                       continue;
+               }
+               if (c == '\n')
+                       break;
+               if (startword && *ifs == ' ' && strchr(ifs, c)) {
+                       continue;
+               }
+               startword = 0;
+               if (ap[1] != NULL && strchr(ifs, c) != NULL) {
+                       STACKSTRNUL(p);
+                       setvar(*ap, stackblock(), 0);
+                       ap++;
+                       startword = 1;
+                       STARTSTACKSTR(p);
+               } else {
+put:
+                       STPUTC(c, p);
+               }
+       }
+       STACKSTRNUL(p);
+       /* Remove trailing blanks */
+       while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
+               *p = '\0';
+       setvar(*ap, stackblock(), 0);
+       while (*++ap != NULL)
+               setvar(*ap, nullstr, 0);
+       return status;
+}
+
+
+static int umaskcmd(int argc, char **argv)
+{
+       static const char permuser[3] = "ugo";
+       static const char permmode[3] = "rwx";
+       static const short int permmask[] = {
+               S_IRUSR, S_IWUSR, S_IXUSR,
+               S_IRGRP, S_IWGRP, S_IXGRP,
+               S_IROTH, S_IWOTH, S_IXOTH
+       };
+
+       char *ap;
+       mode_t mask;
+       int i;
+       int symbolic_mode = 0;
+
+       while (nextopt("S") != '\0') {
+               symbolic_mode = 1;
+       }
+
+       INTOFF;
+       mask = umask(0);
+       umask(mask);
+       INTON;
+
+       if ((ap = *argptr) == NULL) {
+               if (symbolic_mode) {
+                       char buf[18];
+                       char *p = buf;
+
+                       for (i = 0; i < 3; i++) {
+                               int j;
+
+                               *p++ = permuser[i];
+                               *p++ = '=';
+                               for (j = 0; j < 3; j++) {
+                                       if ((mask & permmask[3 * i + j]) == 0) {
+                                               *p++ = permmode[j];
+                                       }
+                               }
+                               *p++ = ',';
+                       }
+                       *--p = 0;
+                       puts(buf);
+               } else {
+                       out1fmt("%.4o\n", mask);
+               }
+       } else {
+               if (is_digit((unsigned char) *ap)) {
+                       mask = 0;
+                       do {
+                               if (*ap >= '8' || *ap < '0')
+                                       error(illnum, argv[1]);
+                               mask = (mask << 3) + (*ap - '0');
+                       } while (*++ap != '\0');
+                       umask(mask);
+               } else {
+                       mask = ~mask & 0777;
+                       if (!bb_parse_mode(ap, &mask)) {
+                               error("Illegal mode: %s", ap);
+                       }
+                       umask(~mask & 0777);
+               }
+       }
+       return 0;
+}
+
+/*
+ * ulimit builtin
+ *
+ * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
+ * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
+ * ash by J.T. Conklin.
+ *
+ * Public domain.
+ */
+
+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",                    RLIMIT_NPROC,      1, 'p' },
+#endif
+#ifdef RLIMIT_NOFILE
+       { "nofiles",                    RLIMIT_NOFILE,     1, 'n' },
+#endif
+#ifdef RLIMIT_AS
+       { "vmemory(kbytes)",            RLIMIT_AS,      1024, 'v' },
+#endif
+#ifdef RLIMIT_LOCKS
+       { "locks",                      RLIMIT_LOCKS,      1, 'w' },
+#endif
+       { (char *) 0,                   0,                 0,  '\0' }
+};
+
+enum limtype { SOFT = 0x1, HARD = 0x2 };
+
+static void printlim(enum limtype how, const struct rlimit *limit,
+                       const struct limits *l)
+{
+       rlim_t val;
+
+       val = limit->rlim_max;
+       if (how & SOFT)
+               val = limit->rlim_cur;
+
+       if (val == RLIM_INFINITY)
+               out1fmt("unlimited\n");
+       else {
+               val /= l->factor;
+               out1fmt("%lld\n", (long long) val);
+       }
+}
+
+int
+ulimitcmd(int argc, char **argv)
+{
+       int     c;
+       rlim_t val = 0;
+       enum limtype how = SOFT | HARD;
+       const struct limits     *l;
+       int             set, all = 0;
+       int             optc, what;
+       struct rlimit   limit;
+
+       what = 'f';
+       while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+                               "t"
+#endif
+#ifdef RLIMIT_FSIZE
+                               "f"
+#endif
+#ifdef RLIMIT_DATA
+                               "d"
+#endif
+#ifdef RLIMIT_STACK
+                               "s"
+#endif
+#ifdef RLIMIT_CORE
+                               "c"
+#endif
+#ifdef RLIMIT_RSS
+                               "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+                               "l"
+#endif
+#ifdef RLIMIT_NPROC
+                               "p"
+#endif
+#ifdef RLIMIT_NOFILE
+                               "n"
+#endif
+#ifdef RLIMIT_AS
+                               "v"
+#endif
+#ifdef RLIMIT_LOCKS
+                               "w"
+#endif
+                                               )) != '\0')
+               switch (optc) {
+               case 'H':
+                       how = HARD;
+                       break;
+               case 'S':
+                       how = SOFT;
+                       break;
+               case 'a':
+                       all = 1;
+                       break;
+               default:
+                       what = optc;
+               }
+
+       for (l = limits; l->option != what; l++)
+               ;
+
+       set = *argptr ? 1 : 0;
+       if (set) {
+               char *p = *argptr;
+
+               if (all || argptr[1])
+                       error("too many arguments");
+               if (strncmp(p, "unlimited\n", 9) == 0)
+                       val = RLIM_INFINITY;
+               else {
+                       val = (rlim_t) 0;
+
+                       while ((c = *p++) >= '0' && c <= '9')
+                       {
+                               val = (val * 10) + (long)(c - '0');
+                               if (val < (rlim_t) 0)
+                                       break;
+                       }
+                       if (c)
+                               error("bad number");
+                       val *= l->factor;
+               }
+       }
+       if (all) {
+               for (l = limits; l->name; l++) {
+                       getrlimit(l->cmd, &limit);
+                       out1fmt("%-20s ", l->name);
+                       printlim(how, &limit, l);
+               }
+               return 0;
+       }
+
+       getrlimit(l->cmd, &limit);
+       if (set) {
+               if (how & HARD)
+                       limit.rlim_max = val;
+               if (how & SOFT)
+                       limit.rlim_cur = val;
+               if (setrlimit(l->cmd, &limit) < 0)
+                       error("error setting limit (%m)");
+       } else {
+               printlim(how, &limit, l);
+       }
+       return 0;
+}
+
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+
+/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* This is my infix parser/evaluator. It is optimized for size, intended
+ * as a replacement for yacc-based parsers. However, it may well be faster
+ * than a comparable parser written in yacc. The supported operators are
+ * listed in #defines below. Parens, order of operations, and error handling
+ * are supported. This code is thread safe. The exact expression format should
+ * be that which POSIX specifies for shells. */
+
+/* The code uses a simple two-stack algorithm. See
+ * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
+ * for a detailed explanation of the infix-to-postfix algorithm on which
+ * this is based (this code differs in that it applies operators immediately
+ * to the stack instead of adding them to a queue to end up with an
+ * expression). */
+
+/* To use the routine, call it with an expression string and error return
+ * pointer */
+
+/*
+ * Aug 24, 2001              Manuel Novoa III
+ *
+ * Reduced the generated code size by about 30% (i386) and fixed several bugs.
+ *
+ * 1) In arith_apply():
+ *    a) Cached values of *numptr and &(numptr[-1]).
+ *    b) Removed redundant test for zero denominator.
+ *
+ * 2) In arith():
+ *    a) Eliminated redundant code for processing operator tokens by moving
+ *       to a table-based implementation.  Also folded handling of parens
+ *       into the table.
+ *    b) Combined all 3 loops which called arith_apply to reduce generated
+ *       code size at the cost of speed.
+ *
+ * 3) The following expressions were treated as valid by the original code:
+ *       1()  ,    0!  ,    1 ( *3 )   .
+ *    These bugs have been fixed by internally enclosing the expression in
+ *    parens and then checking that all binary ops and right parens are
+ *    preceded by a valid expression (NUM_TOKEN).
+ *
+ * Note: It may be desirable to replace Aaron's test for whitespace with
+ * ctype's isspace() if it is used by another busybox applet or if additional
+ * whitespace chars should be considered.  Look below the "#include"s for a
+ * precompiler test.
+ */
+
+/*
+ * Aug 26, 2001              Manuel Novoa III
+ *
+ * Return 0 for null expressions.  Pointed out by Vladimir Oleynik.
+ *
+ * Merge in Aaron's comments previously posted to the busybox list,
+ * modified slightly to take account of my changes to the code.
+ *
+ */
+
+/*
+ *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * - allow access to variable,
+ *   used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
+ * - realize assign syntax (VAR=expr, +=, *= etc)
+ * - realize exponentiation (** operator)
+ * - realize comma separated - expr, expr
+ * - realise ++expr --expr expr++ expr--
+ * - realise expr ? expr : expr (but, second expr calculate always)
+ * - allow hexadecimal and octal numbers
+ * - was restored loses XOR operator
+ * - remove one goto label, added three ;-)
+ * - protect $((num num)) as true zero expr (Manuel`s error)
+ * - always use special isspace(), see comment from bash ;-)
+ */
+
+
+#define arith_isspace(arithval) \
+       (arithval == ' ' || arithval == '\n' || arithval == '\t')
+
+
+typedef unsigned char operator;
+
+/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
+ * precedence, and 3 high bits are an ID unique across operators of that
+ * precedence. The ID portion is so that multiple operators can have the
+ * same precedence, ensuring that the leftmost one is evaluated first.
+ * Consider * and /. */
+
+#define tok_decl(prec,id) (((id)<<5)|(prec))
+#define PREC(op) ((op) & 0x1F)
+
+#define TOK_LPAREN tok_decl(0,0)
+
+#define TOK_COMMA tok_decl(1,0)
+
+#define TOK_ASSIGN tok_decl(2,0)
+#define TOK_AND_ASSIGN tok_decl(2,1)
+#define TOK_OR_ASSIGN tok_decl(2,2)
+#define TOK_XOR_ASSIGN tok_decl(2,3)
+#define TOK_PLUS_ASSIGN tok_decl(2,4)
+#define TOK_MINUS_ASSIGN tok_decl(2,5)
+#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
+#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
+
+#define TOK_MUL_ASSIGN tok_decl(3,0)
+#define TOK_DIV_ASSIGN tok_decl(3,1)
+#define TOK_REM_ASSIGN tok_decl(3,2)
+
+/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
+#define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0)
+
+/* conditional is right associativity too */
+#define TOK_CONDITIONAL tok_decl(4,0)
+#define TOK_CONDITIONAL_SEP tok_decl(4,1)
+
+#define TOK_OR tok_decl(5,0)
+
+#define TOK_AND tok_decl(6,0)
+
+#define TOK_BOR tok_decl(7,0)
+
+#define TOK_BXOR tok_decl(8,0)
+
+#define TOK_BAND tok_decl(9,0)
+
+#define TOK_EQ tok_decl(10,0)
+#define TOK_NE tok_decl(10,1)
+
+#define TOK_LT tok_decl(11,0)
+#define TOK_GT tok_decl(11,1)
+#define TOK_GE tok_decl(11,2)
+#define TOK_LE tok_decl(11,3)
+
+#define TOK_LSHIFT tok_decl(12,0)
+#define TOK_RSHIFT tok_decl(12,1)
+
+#define TOK_ADD tok_decl(13,0)
+#define TOK_SUB tok_decl(13,1)
+
+#define TOK_MUL tok_decl(14,0)
+#define TOK_DIV tok_decl(14,1)
+#define TOK_REM tok_decl(14,2)
+
+/* exponent is right associativity */
+#define TOK_EXPONENT tok_decl(15,1)
+
+/* For now unary operators. */
+#define UNARYPREC 16
+#define TOK_BNOT tok_decl(UNARYPREC,0)
+#define TOK_NOT tok_decl(UNARYPREC,1)
+
+#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
+#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
+
+#define PREC_PRE (UNARYPREC+2)
+
+#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
+#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
+
+#define PREC_POST (UNARYPREC+3)
+
+#define TOK_POST_INC tok_decl(PREC_POST, 0)
+#define TOK_POST_DEC tok_decl(PREC_POST, 1)
+
+#define SPEC_PREC (UNARYPREC+4)
+
+#define TOK_NUM tok_decl(SPEC_PREC, 0)
+#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
+
+#define NUMPTR (*numstackptr)
+
+static inline int tok_have_assign(operator op)
+{
+       operator prec = PREC(op);
+
+       convert_prec_is_assing(prec);
+       return (prec == PREC(TOK_ASSIGN) ||
+                       prec == PREC_PRE || prec == PREC_POST);
+}
+
+static inline int is_right_associativity(operator prec)
+{
+    return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) ||
+           prec == PREC(TOK_CONDITIONAL));
+}
+
+
+typedef struct ARITCH_VAR_NUM {
+       arith_t val;
+       arith_t contidional_second_val;
+       char contidional_second_val_initialized;
+       char *var;      /* if NULL then is regular number,
+                          else is variable name */
+} v_n_t;
+
+
+typedef struct CHK_VAR_RECURSIVE_LOOPED {
+       const char *var;
+       struct CHK_VAR_RECURSIVE_LOOPED *next;
+} chk_var_recursive_looped_t;
+
+static chk_var_recursive_looped_t *prev_chk_var_recursive;
+
+
+static int arith_lookup_val(v_n_t *t)
+{
+    if(t->var) {
+       const char * p = lookupvar(t->var);
+
+       if(p) {
+           int errcode;
+
+           /* recursive try as expression */
+           chk_var_recursive_looped_t *cur;
+           chk_var_recursive_looped_t cur_save;
+
+           for(cur = prev_chk_var_recursive; cur; cur = cur->next) {
+               if(strcmp(cur->var, t->var) == 0) {
+                   /* expression recursion loop detected */
+                   return -5;
+               }
+           }
+           /* save current lookuped var name */
+           cur = prev_chk_var_recursive;
+           cur_save.var = t->var;
+           cur_save.next = cur;
+           prev_chk_var_recursive = &cur_save;
+
+           t->val = arith (p, &errcode);
+           /* restore previous ptr after recursiving */
+           prev_chk_var_recursive = cur;
+           return errcode;
+       } else {
+           /* allow undefined var as 0 */
+           t->val = 0;
+       }
+    }
+    return 0;
+}
+
+/* "applying" a token means performing it on the top elements on the integer
+ * stack. For a unary operator it will only change the top element, but a
+ * binary operator will pop two arguments and push a result */
+static inline int
+arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
+{
+       v_n_t *numptr_m1;
+       arith_t numptr_val, rez;
+       int ret_arith_lookup_val;
+
+       if (NUMPTR == numstack) goto err; /* There is no operator that can work
+                                                                                without arguments */
+       numptr_m1 = NUMPTR - 1;
+
+       /* check operand is var with noninteger value */
+       ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+       if(ret_arith_lookup_val)
+               return ret_arith_lookup_val;
+
+       rez = numptr_m1->val;
+       if (op == TOK_UMINUS)
+               rez *= -1;
+       else if (op == TOK_NOT)
+               rez = !rez;
+       else if (op == TOK_BNOT)
+               rez = ~rez;
+       else if (op == TOK_POST_INC || op == TOK_PRE_INC)
+               rez++;
+       else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
+               rez--;
+       else if (op != TOK_UPLUS) {
+               /* Binary operators */
+
+           /* check and binary operators need two arguments */
+           if (numptr_m1 == numstack) goto err;
+
+           /* ... and they pop one */
+           --NUMPTR;
+           numptr_val = rez;
+           if (op == TOK_CONDITIONAL) {
+               if(! numptr_m1->contidional_second_val_initialized) {
+                   /* protect $((expr1 ? expr2)) without ": expr" */
+                   goto err;
+               }
+               rez = numptr_m1->contidional_second_val;
+           } else if(numptr_m1->contidional_second_val_initialized) {
+                   /* protect $((expr1 : expr2)) without "expr ? " */
+                   goto err;
+           }
+           numptr_m1 = NUMPTR - 1;
+           if(op != TOK_ASSIGN) {
+               /* check operand is var with noninteger value for not '=' */
+               ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+               if(ret_arith_lookup_val)
+                   return ret_arith_lookup_val;
+           }
+           if (op == TOK_CONDITIONAL) {
+                   numptr_m1->contidional_second_val = rez;
+           }
+           rez = numptr_m1->val;
+           if (op == TOK_BOR || op == TOK_OR_ASSIGN)
+                       rez |= numptr_val;
+           else if (op == TOK_OR)
+                       rez = numptr_val || rez;
+           else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
+                       rez &= numptr_val;
+           else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
+                       rez ^= numptr_val;
+           else if (op == TOK_AND)
+                       rez = rez && numptr_val;
+           else if (op == TOK_EQ)
+                       rez = (rez == numptr_val);
+           else if (op == TOK_NE)
+                       rez = (rez != numptr_val);
+           else if (op == TOK_GE)
+                       rez = (rez >= numptr_val);
+           else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
+                       rez >>= numptr_val;
+           else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
+                       rez <<= numptr_val;
+           else if (op == TOK_GT)
+                       rez = (rez > numptr_val);
+           else if (op == TOK_LT)
+                       rez = (rez < numptr_val);
+           else if (op == TOK_LE)
+                       rez = (rez <= numptr_val);
+           else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
+                       rez *= numptr_val;
+           else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
+                       rez += numptr_val;
+           else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
+                       rez -= numptr_val;
+           else if (op == TOK_ASSIGN || op == TOK_COMMA)
+                       rez = numptr_val;
+           else if (op == TOK_CONDITIONAL_SEP) {
+                       if (numptr_m1 == numstack) {
+                           /* protect $((expr : expr)) without "expr ? " */
+                           goto err;
+                       }
+                       numptr_m1->contidional_second_val_initialized = op;
+                       numptr_m1->contidional_second_val = numptr_val;
+           }
+           else if (op == TOK_CONDITIONAL) {
+                       rez = rez ?
+                             numptr_val : numptr_m1->contidional_second_val;
+           }
+           else if(op == TOK_EXPONENT) {
+                       if(numptr_val < 0)
+                               return -3;      /* exponent less than 0 */
+                       else {
+                               arith_t c = 1;
+
+                               if(numptr_val)
+                                       while(numptr_val--)
+                                               c *= rez;
+                               rez = c;
+                       }
+           }
+           else if(numptr_val==0)          /* zero divisor check */
+                       return -2;
+           else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
+                       rez /= numptr_val;
+           else if (op == TOK_REM || op == TOK_REM_ASSIGN)
+                       rez %= numptr_val;
+       }
+       if(tok_have_assign(op)) {
+               char buf[32];
+
+               if(numptr_m1->var == NULL) {
+                       /* Hmm, 1=2 ? */
+                       goto err;
+               }
+               /* save to shell variable */
+               snprintf(buf, sizeof(buf), "%lld", (long long) rez);
+               setvar(numptr_m1->var, buf, 0);
+               /* after saving, make previous value for v++ or v-- */
+               if(op == TOK_POST_INC)
+                       rez--;
+               else if(op == TOK_POST_DEC)
+                       rez++;
+       }
+       numptr_m1->val = rez;
+       /* protect geting var value, is number now */
+       numptr_m1->var = NULL;
+       return 0;
+err: return(-1);
+}
+
+/* longest must first */
+static const char op_tokens[] = {
+       '<','<','=',0, TOK_LSHIFT_ASSIGN,
+       '>','>','=',0, TOK_RSHIFT_ASSIGN,
+       '<','<',    0, TOK_LSHIFT,
+       '>','>',    0, TOK_RSHIFT,
+       '|','|',    0, TOK_OR,
+       '&','&',    0, TOK_AND,
+       '!','=',    0, TOK_NE,
+       '<','=',    0, TOK_LE,
+       '>','=',    0, TOK_GE,
+       '=','=',    0, TOK_EQ,
+       '|','=',    0, TOK_OR_ASSIGN,
+       '&','=',    0, TOK_AND_ASSIGN,
+       '*','=',    0, TOK_MUL_ASSIGN,
+       '/','=',    0, TOK_DIV_ASSIGN,
+       '%','=',    0, TOK_REM_ASSIGN,
+       '+','=',    0, TOK_PLUS_ASSIGN,
+       '-','=',    0, TOK_MINUS_ASSIGN,
+       '-','-',    0, TOK_POST_DEC,
+       '^','=',    0, TOK_XOR_ASSIGN,
+       '+','+',    0, TOK_POST_INC,
+       '*','*',    0, TOK_EXPONENT,
+       '!',        0, TOK_NOT,
+       '<',        0, TOK_LT,
+       '>',        0, TOK_GT,
+       '=',        0, TOK_ASSIGN,
+       '|',        0, TOK_BOR,
+       '&',        0, TOK_BAND,
+       '*',        0, TOK_MUL,
+       '/',        0, TOK_DIV,
+       '%',        0, TOK_REM,
+       '+',        0, TOK_ADD,
+       '-',        0, TOK_SUB,
+       '^',        0, TOK_BXOR,
+       /* uniq */
+       '~',        0, TOK_BNOT,
+       ',',        0, TOK_COMMA,
+       '?',        0, TOK_CONDITIONAL,
+       ':',        0, TOK_CONDITIONAL_SEP,
+       ')',        0, TOK_RPAREN,
+       '(',        0, TOK_LPAREN,
+       0
+};
+/* ptr to ")" */
+#define endexpression &op_tokens[sizeof(op_tokens)-7]
+
+
+static arith_t arith (const char *expr, int *perrcode)
+{
+    register char arithval; /* Current character under analysis */
+    operator lasttok, op;
+    operator prec;
+
+    const char *p = endexpression;
+    int errcode;
+
+    size_t datasizes = strlen(expr) + 2;
+
+    /* Stack of integers */
+    /* The proof that there can be no more than strlen(startbuf)/2+1 integers
+     * in any given correct or incorrect expression is left as an exercise to
+     * the reader. */
+    v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
+           *numstackptr = numstack;
+    /* Stack of operator tokens */
+    operator *stack = alloca((datasizes) * sizeof(operator)),
+           *stackptr = stack;
+
+    *stackptr++ = lasttok = TOK_LPAREN;     /* start off with a left paren */
+    *perrcode = errcode = 0;
+
+    while(1) {
+       if ((arithval = *expr) == 0) {
+               if (p == endexpression) {
+                       /* Null expression. */
+                       return 0;
+               }
+
+               /* This is only reached after all tokens have been extracted from the
+                * input stream. If there are still tokens on the operator stack, they
+                * are to be applied in order. At the end, there should be a final
+                * result on the integer stack */
+
+               if (expr != endexpression + 1) {
+                       /* If we haven't done so already, */
+                       /* append a closing right paren */
+                       expr = endexpression;
+                       /* and let the loop process it. */
+                       continue;
+               }
+               /* At this point, we're done with the expression. */
+               if (numstackptr != numstack+1) {
+                       /* ... but if there isn't, it's bad */
+                 err:
+                       return (*perrcode = -1);
+               }
+               if(numstack->var) {
+                   /* expression is $((var)) only, lookup now */
+                   errcode = arith_lookup_val(numstack);
+               }
+       ret:
+               *perrcode = errcode;
+               return numstack->val;
+       } else {
+               /* Continue processing the expression. */
+               if (arith_isspace(arithval)) {
+                       /* Skip whitespace */
+                       goto prologue;
+               }
+               if((p = endofname(expr)) != expr) {
+                       size_t var_name_size = (p-expr) + 1;  /* trailing zero */
+
+                       numstackptr->var = alloca(var_name_size);
+                       safe_strncpy(numstackptr->var, expr, var_name_size);
+                       expr = p;
+               num:
+                       numstackptr->contidional_second_val_initialized = 0;
+                       numstackptr++;
+                       lasttok = TOK_NUM;
+                       continue;
+               } else if (is_digit(arithval)) {
+                       numstackptr->var = NULL;
+                       numstackptr->val = strtoll(expr, (char **) &expr, 0);
+                       goto num;
+               }
+               for(p = op_tokens; ; p++) {
+                       const char *o;
+
+                       if(*p == 0) {
+                               /* strange operator not found */
+                               goto err;
+                       }
+                       for(o = expr; *p && *o == *p; p++)
+                               o++;
+                       if(! *p) {
+                               /* found */
+                               expr = o - 1;
+                               break;
+                       }
+                       /* skip tail uncompared token */
+                       while(*p)
+                               p++;
+                       /* skip zero delim */
+                       p++;
+               }
+               op = p[1];
+
+               /* post grammar: a++ reduce to num */
+               if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
+                   lasttok = TOK_NUM;
+
+               /* Plus and minus are binary (not unary) _only_ if the last
+                * token was as number, or a right paren (which pretends to be
+                * a number, since it evaluates to one). Think about it.
+                * It makes sense. */
+               if (lasttok != TOK_NUM) {
+                       switch(op) {
+                               case TOK_ADD:
+                                   op = TOK_UPLUS;
+                                   break;
+                               case TOK_SUB:
+                                   op = TOK_UMINUS;
+                                   break;
+                               case TOK_POST_INC:
+                                   op = TOK_PRE_INC;
+                                   break;
+                               case TOK_POST_DEC:
+                                   op = TOK_PRE_DEC;
+                                   break;
+                       }
+               }
+               /* We don't want a unary operator to cause recursive descent on the
+                * stack, because there can be many in a row and it could cause an
+                * operator to be evaluated before its argument is pushed onto the
+                * integer stack. */
+               /* But for binary operators, "apply" everything on the operator
+                * stack until we find an operator with a lesser priority than the
+                * one we have just extracted. */
+               /* Left paren is given the lowest priority so it will never be
+                * "applied" in this way.
+                * if associativity is right and priority eq, applied also skip
+                */
+               prec = PREC(op);
+               if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
+                       /* not left paren or unary */
+                       if (lasttok != TOK_NUM) {
+                               /* binary op must be preceded by a num */
+                               goto err;
+                       }
+                       while (stackptr != stack) {
+                           if (op == TOK_RPAREN) {
+                               /* The algorithm employed here is simple: while we don't
+                                * hit an open paren nor the bottom of the stack, pop
+                                * tokens and apply them */
+                               if (stackptr[-1] == TOK_LPAREN) {
+                                   --stackptr;
+                                   /* Any operator directly after a */
+                                   lasttok = TOK_NUM;
+                                   /* close paren should consider itself binary */
+                                   goto prologue;
+                               }
+                           } else {
+                               operator prev_prec = PREC(stackptr[-1]);
+
+                               convert_prec_is_assing(prec);
+                               convert_prec_is_assing(prev_prec);
+                               if (prev_prec < prec)
+                                       break;
+                               /* check right assoc */
+                               if(prev_prec == prec && is_right_associativity(prec))
+                                       break;
+                           }
+                           errcode = arith_apply(*--stackptr, numstack, &numstackptr);
+                           if(errcode) goto ret;
+                       }
+                       if (op == TOK_RPAREN) {
+                               goto err;
+                       }
+               }
+
+               /* Push this operator to the stack and remember it. */
+               *stackptr++ = lasttok = op;
+
+         prologue:
+               ++expr;
+       }
+    }
+}
+#endif /* CONFIG_ASH_MATH_SUPPORT */
+
+
+#ifdef DEBUG
+const char *bb_applet_name = "debug stuff usage";
+int main(int argc, char **argv)
+{
+       return ash_main(argc, argv);
+}
+#endif
+
+/*-
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/shell/cmdedit.c b/busybox/shell/cmdedit.c
new file mode 100644 (file)
index 0000000..56b789a
--- /dev/null
@@ -0,0 +1,1582 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Termios command line History and Editing.
+ *
+ * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
+ * Written by:   Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Used ideas:
+ *      Adam Rogoyski    <rogoyski@cs.utexas.edu>
+ *      Dave Cinege      <dcinege@psychosis.com>
+ *      Jakub Jelinek (c) 1995
+ *      Erik Andersen    <andersen@codepoet.org> (Majorly adjusted for busybox)
+ *
+ * This code is 'as is' with no warranty.
+ *
+ *
+ */
+
+/*
+   Usage and Known bugs:
+   Terminal key codes are not extensive, and more will probably
+   need to be added. This version was created on Debian GNU/Linux 2.x.
+   Delete, Backspace, Home, End, and the arrow keys were tested
+   to work in an Xterm and console. Ctrl-A also works as Home.
+   Ctrl-E also works as End.
+
+   Small bugs (simple effect):
+   - not true viewing if terminal size (x*y symbols) less
+     size (prompt + editor`s line + 2 symbols)
+   - not true viewing if length prompt less terminal width
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <limits.h>
+
+#include "busybox.h"
+
+#include "../shell/cmdedit.h"
+
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#define Isprint(c) isprint((c))
+#else
+#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
+#endif
+
+#ifdef TEST
+
+/* pretect redefined for test */
+#undef CONFIG_FEATURE_COMMAND_EDITING
+#undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+#undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
+#undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+#undef CONFIG_FEATURE_CLEAN_UP
+
+#define CONFIG_FEATURE_COMMAND_EDITING
+#define CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+#define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
+#define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+#define CONFIG_FEATURE_CLEAN_UP
+
+#endif                                                  /* TEST */
+
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+#include <dirent.h>
+#include <sys/stat.h>
+#endif
+
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+
+#if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+#define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+#endif
+
+#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+#include "pwd_.h"
+#endif                                                  /* advanced FEATURES */
+
+
+/* Maximum length of the linked list for the command line history */
+#ifndef CONFIG_FEATURE_COMMAND_HISTORY
+#define MAX_HISTORY   15
+#else
+#define MAX_HISTORY   CONFIG_FEATURE_COMMAND_HISTORY
+#endif
+
+#if MAX_HISTORY < 1
+#warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
+#else
+static char *history[MAX_HISTORY+1]; /* history + current */
+/* saved history lines */
+static int n_history;
+/* current pointer to history line */
+static int cur_history;
+#endif
+
+#include <termios.h>
+#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
+#define getTermSettings(fd,argp) tcgetattr(fd, argp);
+
+/* Current termio and the previous termio before starting sh */
+static struct termios initial_settings, new_settings;
+
+
+static
+volatile int cmdedit_termw = 80;        /* actual terminal width */
+static
+volatile int handlers_sets = 0; /* Set next bites: */
+
+enum {
+       SET_ATEXIT = 1,         /* when atexit() has been called
+                                  and get euid,uid,gid to fast compare */
+       SET_WCHG_HANDLERS = 2,  /* winchg signal handler */
+       SET_RESET_TERM = 4,     /* if the terminal needs to be reset upon exit */
+};
+
+
+static int cmdedit_x;           /* real x terminal position */
+static int cmdedit_y;           /* pseudoreal y terminal position */
+static int cmdedit_prmt_len;    /* lenght prompt without colores string */
+
+static int cursor;              /* required global for signal handler */
+static int len;                 /* --- "" - - "" - -"- --""-- --""--- */
+static char *command_ps;        /* --- "" - - "" - -"- --""-- --""--- */
+static
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+       const
+#endif
+char *cmdedit_prompt;           /* --- "" - - "" - -"- --""-- --""--- */
+
+#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+static char *user_buf = "";
+static char *home_pwd_buf = "";
+static int my_euid;
+#endif
+
+#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
+static char *hostname_buf;
+static int num_ok_lines = 1;
+#endif
+
+
+#ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+
+#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+static int my_euid;
+#endif
+
+static int my_uid;
+static int my_gid;
+
+#endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
+
+static void cmdedit_setwidth(int w, int redraw_flg);
+
+static void win_changed(int nsig)
+{
+       static sighandler_t previous_SIGWINCH_handler;  /* for reset */
+
+       /*   emulate      || signal call */
+       if (nsig == -SIGWINCH || nsig == SIGWINCH) {
+               int width = 0;
+               get_terminal_width_height(0, &width, NULL);
+               cmdedit_setwidth(width, nsig == SIGWINCH);
+       }
+       /* Unix not all standart in recall signal */
+
+       if (nsig == -SIGWINCH)          /* save previous handler   */
+               previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
+       else if (nsig == SIGWINCH)      /* signaled called handler */
+               signal(SIGWINCH, win_changed);  /* set for next call       */
+       else                                            /* nsig == 0 */
+               /* set previous handler    */
+               signal(SIGWINCH, previous_SIGWINCH_handler);    /* reset    */
+}
+
+static void cmdedit_reset_term(void)
+{
+       if ((handlers_sets & SET_RESET_TERM) != 0) {
+/* sparc and other have broken termios support: use old termio handling. */
+               setTermSettings(STDIN_FILENO, (void *) &initial_settings);
+               handlers_sets &= ~SET_RESET_TERM;
+       }
+       if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
+               /* reset SIGWINCH handler to previous (default) */
+               win_changed(0);
+               handlers_sets &= ~SET_WCHG_HANDLERS;
+       }
+       fflush(stdout);
+}
+
+
+/* special for recount position for scroll and remove terminal margin effect */
+static void cmdedit_set_out_char(int next_char)
+{
+
+       int c = (int)((unsigned char) command_ps[cursor]);
+
+       if (c == 0)
+               c = ' ';        /* destroy end char? */
+#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+       if (!Isprint(c)) {      /* Inverse put non-printable characters */
+               if (c >= 128)
+                       c -= 128;
+               if (c < ' ')
+                       c += '@';
+               if (c == 127)
+                       c = '?';
+               printf("\033[7m%c\033[0m", c);
+       } else
+#endif
+               putchar(c);
+       if (++cmdedit_x >= cmdedit_termw) {
+               /* terminal is scrolled down */
+               cmdedit_y++;
+               cmdedit_x = 0;
+
+               if (!next_char)
+                       next_char = ' ';
+               /* destroy "(auto)margin" */
+               putchar(next_char);
+               putchar('\b');
+       }
+       cursor++;
+}
+
+/* Move to end line. Bonus: rewrite line from cursor */
+static void input_end(void)
+{
+       while (cursor < len)
+               cmdedit_set_out_char(0);
+}
+
+/* Go to the next line */
+static void goto_new_line(void)
+{
+       input_end();
+       if (cmdedit_x)
+               putchar('\n');
+}
+
+
+static inline void out1str(const char *s)
+{
+       if ( s )
+               fputs(s, stdout);
+}
+
+static inline void beep(void)
+{
+       putchar('\007');
+}
+
+/* Move back one character */
+/* special for slow terminal */
+static void input_backward(int num)
+{
+       if (num > cursor)
+               num = cursor;
+       cursor -= num;          /* new cursor (in command, not terminal) */
+
+       if (cmdedit_x >= num) {         /* no to up line */
+               cmdedit_x -= num;
+               if (num < 4)
+                       while (num-- > 0)
+                               putchar('\b');
+
+               else
+                       printf("\033[%dD", num);
+       } else {
+               int count_y;
+
+               if (cmdedit_x) {
+                       putchar('\r');          /* back to first terminal pos.  */
+                       num -= cmdedit_x;       /* set previous backward        */
+               }
+               count_y = 1 + num / cmdedit_termw;
+               printf("\033[%dA", count_y);
+               cmdedit_y -= count_y;
+               /*  require  forward  after  uping   */
+               cmdedit_x = cmdedit_termw * count_y - num;
+               printf("\033[%dC", cmdedit_x);  /* set term cursor   */
+       }
+}
+
+static void put_prompt(void)
+{
+       out1str(cmdedit_prompt);
+       cmdedit_x = cmdedit_prmt_len;   /* count real x terminal position */
+       cursor = 0;
+       cmdedit_y = 0;                  /* new quasireal y */
+}
+
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+static void parse_prompt(const char *prmt_ptr)
+{
+       cmdedit_prompt = prmt_ptr;
+       cmdedit_prmt_len = strlen(prmt_ptr);
+       put_prompt();
+}
+#else
+static void parse_prompt(const char *prmt_ptr)
+{
+       int prmt_len = 0;
+       int sub_len = 0;
+       char  flg_not_length = '[';
+       char *prmt_mem_ptr = xcalloc(1, 1);
+       char *pwd_buf = xgetcwd(0);
+       char  buf2[PATH_MAX + 1];
+       char  buf[2];
+       char  c;
+       char *pbuf;
+
+       if (!pwd_buf) {
+               pwd_buf=(char *)bb_msg_unknown;
+       }
+
+       while (*prmt_ptr) {
+               pbuf    = buf;
+               pbuf[1] = 0;
+               c = *prmt_ptr++;
+               if (c == '\\') {
+                       const char *cp = prmt_ptr;
+                       int l;
+
+                       c = bb_process_escape_sequence(&prmt_ptr);
+                       if(prmt_ptr==cp) {
+                         if (*cp == 0)
+                               break;
+                         c = *prmt_ptr++;
+                         switch (c) {
+#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+                         case 'u':
+                               pbuf = user_buf;
+                               break;
+#endif
+                         case 'h':
+                               pbuf = hostname_buf;
+                               if (pbuf == 0) {
+                                       pbuf = xcalloc(256, 1);
+                                       if (gethostname(pbuf, 255) < 0) {
+                                               strcpy(pbuf, "?");
+                                       } else {
+                                               char *s = strchr(pbuf, '.');
+
+                                               if (s)
+                                                       *s = 0;
+                                       }
+                                       hostname_buf = pbuf;
+                               }
+                               break;
+                         case '$':
+                               c = my_euid == 0 ? '#' : '$';
+                               break;
+#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+                         case 'w':
+                               pbuf = pwd_buf;
+                               l = strlen(home_pwd_buf);
+                               if (home_pwd_buf[0] != 0 &&
+                                   strncmp(home_pwd_buf, pbuf, l) == 0 &&
+                                   (pbuf[l]=='/' || pbuf[l]=='\0') &&
+                                   strlen(pwd_buf+l)<PATH_MAX) {
+                                       pbuf = buf2;
+                                       *pbuf = '~';
+                                       strcpy(pbuf+1, pwd_buf+l);
+                                       }
+                               break;
+#endif
+                         case 'W':
+                               pbuf = pwd_buf;
+                               cp = strrchr(pbuf,'/');
+                               if ( (cp != NULL) && (cp != pbuf) )
+                                       pbuf += (cp-pbuf)+1;
+                               break;
+                         case '!':
+                               snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
+                               break;
+                         case 'e': case 'E':     /* \e \E = \033 */
+                               c = '\033';
+                               break;
+                         case 'x': case 'X':
+                               for (l = 0; l < 3;) {
+                                       int h;
+                                       buf2[l++] = *prmt_ptr;
+                                       buf2[l] = 0;
+                                       h = strtol(buf2, &pbuf, 16);
+                                       if (h > UCHAR_MAX || (pbuf - buf2) < l) {
+                                               l--;
+                                               break;
+                                       }
+                                       prmt_ptr++;
+                               }
+                               buf2[l] = 0;
+                               c = (char)strtol(buf2, 0, 16);
+                               if(c==0)
+                                       c = '?';
+                               pbuf = buf;
+                               break;
+                         case '[': case ']':
+                               if (c == flg_not_length) {
+                                       flg_not_length = flg_not_length == '[' ? ']' : '[';
+                                       continue;
+                               }
+                               break;
+                         }
+                       }
+               }
+               if(pbuf == buf)
+                       *pbuf = c;
+               prmt_len += strlen(pbuf);
+               prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
+               if (flg_not_length == ']')
+                       sub_len++;
+       }
+       if(pwd_buf!=(char *)bb_msg_unknown)
+               free(pwd_buf);
+       cmdedit_prompt = prmt_mem_ptr;
+       cmdedit_prmt_len = prmt_len - sub_len;
+       put_prompt();
+}
+#endif
+
+
+/* draw prompt, editor line, and clear tail */
+static void redraw(int y, int back_cursor)
+{
+       if (y > 0)                              /* up to start y */
+               printf("\033[%dA", y);
+       putchar('\r');
+       put_prompt();
+       input_end();                            /* rewrite */
+       printf("\033[J");                       /* destroy tail after cursor */
+       input_backward(back_cursor);
+}
+
+/* Delete the char in front of the cursor */
+static void input_delete(void)
+{
+       int j = cursor;
+
+       if (j == len)
+               return;
+
+       strcpy(command_ps + j, command_ps + j + 1);
+       len--;
+       input_end();                    /* rewtite new line */
+       cmdedit_set_out_char(0);        /* destroy end char */
+       input_backward(cursor - j);     /* back to old pos cursor */
+}
+
+/* Delete the char in back of the cursor */
+static void input_backspace(void)
+{
+       if (cursor > 0) {
+               input_backward(1);
+               input_delete();
+       }
+}
+
+
+/* Move forward one character */
+static void input_forward(void)
+{
+       if (cursor < len)
+               cmdedit_set_out_char(command_ps[cursor + 1]);
+}
+
+
+static void cmdedit_setwidth(int w, int redraw_flg)
+{
+       cmdedit_termw = cmdedit_prmt_len + 2;
+       if (w <= cmdedit_termw) {
+               cmdedit_termw = cmdedit_termw % w;
+       }
+       if (w > cmdedit_termw) {
+               cmdedit_termw = w;
+
+               if (redraw_flg) {
+                       /* new y for current cursor */
+                       int new_y = (cursor + cmdedit_prmt_len) / w;
+
+                       /* redraw */
+                       redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
+                       fflush(stdout);
+               }
+       }
+}
+
+static void cmdedit_init(void)
+{
+       cmdedit_prmt_len = 0;
+       if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
+               /* emulate usage handler to set handler and call yours work */
+               win_changed(-SIGWINCH);
+               handlers_sets |= SET_WCHG_HANDLERS;
+       }
+
+       if ((handlers_sets & SET_ATEXIT) == 0) {
+#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+               struct passwd *entry;
+
+               my_euid = geteuid();
+               entry = getpwuid(my_euid);
+               if (entry) {
+                       user_buf = bb_xstrdup(entry->pw_name);
+                       home_pwd_buf = bb_xstrdup(entry->pw_dir);
+               }
+#endif
+
+#ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+
+#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
+               my_euid = geteuid();
+#endif
+               my_uid = getuid();
+               my_gid = getgid();
+#endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
+               handlers_sets |= SET_ATEXIT;
+               atexit(cmdedit_reset_term);     /* be sure to do this only once */
+       }
+}
+
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+
+static int is_execute(const struct stat *st)
+{
+       if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
+               (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
+               (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
+               (st->st_mode & S_IXOTH)) return TRUE;
+       return FALSE;
+}
+
+#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
+
+static char **username_tab_completion(char *ud, int *num_matches)
+{
+       struct passwd *entry;
+       int userlen;
+       char *temp;
+
+
+       ud++;                           /* ~user/... to user/... */
+       userlen = strlen(ud);
+
+       if (num_matches == 0) {         /* "~/..." or "~user/..." */
+               char *sav_ud = ud - 1;
+               char *home = 0;
+
+               if (*ud == '/') {       /* "~/..."     */
+                       home = home_pwd_buf;
+               } else {
+                       /* "~user/..." */
+                       temp = strchr(ud, '/');
+                       *temp = 0;              /* ~user\0 */
+                       entry = getpwnam(ud);
+                       *temp = '/';            /* restore ~user/... */
+                       ud = temp;
+                       if (entry)
+                               home = entry->pw_dir;
+               }
+               if (home) {
+                       if ((userlen + strlen(home) + 1) < BUFSIZ) {
+                               char temp2[BUFSIZ];     /* argument size */
+
+                               /* /home/user/... */
+                               sprintf(temp2, "%s%s", home, ud);
+                               strcpy(sav_ud, temp2);
+                       }
+               }
+               return 0;       /* void, result save to argument :-) */
+       } else {
+               /* "~[^/]*" */
+               char **matches = (char **) NULL;
+               int nm = 0;
+
+               setpwent();
+
+               while ((entry = getpwent()) != NULL) {
+                       /* Null usernames should result in all users as possible completions. */
+                       if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
+
+                              bb_xasprintf(&temp, "~%s/", entry->pw_name);
+                               matches = xrealloc(matches, (nm + 1) * sizeof(char *));
+
+                               matches[nm++] = temp;
+                       }
+               }
+
+               endpwent();
+               (*num_matches) = nm;
+               return (matches);
+       }
+}
+#endif  /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
+
+enum {
+       FIND_EXE_ONLY = 0,
+       FIND_DIR_ONLY = 1,
+       FIND_FILE_ONLY = 2,
+};
+
+#ifdef CONFIG_ASH
+const char *cmdedit_path_lookup;
+#else
+#define cmdedit_path_lookup getenv("PATH")
+#endif
+
+static int path_parse(char ***p, int flags)
+{
+       int npth;
+       const char *tmp;
+       const char *pth;
+
+       /* if not setenv PATH variable, to search cur dir "." */
+       if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
+               /* PATH=<empty> or PATH=:<empty> */
+               *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
+               return 1;
+       }
+
+       tmp = pth;
+       npth = 0;
+
+       for (;;) {
+               npth++;                 /* count words is + 1 count ':' */
+               tmp = strchr(tmp, ':');
+               if (tmp) {
+                       if (*++tmp == 0)
+                               break;  /* :<empty> */
+               } else
+                       break;
+       }
+
+       *p = xmalloc(npth * sizeof(char *));
+
+       tmp = pth;
+       (*p)[0] = bb_xstrdup(tmp);
+       npth = 1;                       /* count words is + 1 count ':' */
+
+       for (;;) {
+               tmp = strchr(tmp, ':');
+               if (tmp) {
+                       (*p)[0][(tmp - pth)] = 0;       /* ':' -> '\0' */
+                       if (*++tmp == 0)
+                               break;                  /* :<empty> */
+               } else
+                       break;
+               (*p)[npth++] = &(*p)[0][(tmp - pth)];   /* p[next]=p[0][&'\0'+1] */
+       }
+
+       return npth;
+}
+
+static char *add_quote_for_spec_chars(char *found)
+{
+       int l = 0;
+       char *s = xmalloc((strlen(found) + 1) * 2);
+
+       while (*found) {
+               if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
+                       s[l++] = '\\';
+               s[l++] = *found++;
+       }
+       s[l] = 0;
+       return s;
+}
+
+static char **exe_n_cwd_tab_completion(char *command, int *num_matches,
+                                       int type)
+{
+
+       char **matches = 0;
+       DIR *dir;
+       struct dirent *next;
+       char dirbuf[BUFSIZ];
+       int nm = *num_matches;
+       struct stat st;
+       char *path1[1];
+       char **paths = path1;
+       int npaths;
+       int i;
+       char *found;
+       char *pfind = strrchr(command, '/');
+
+       path1[0] = ".";
+
+       if (pfind == NULL) {
+               /* no dir, if flags==EXE_ONLY - get paths, else "." */
+               npaths = path_parse(&paths, type);
+               pfind = command;
+       } else {
+               /* with dir */
+               /* save for change */
+               strcpy(dirbuf, command);
+               /* set dir only */
+               dirbuf[(pfind - command) + 1] = 0;
+#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
+               if (dirbuf[0] == '~')   /* ~/... or ~user/... */
+                       username_tab_completion(dirbuf, 0);
+#endif
+               /* "strip" dirname in command */
+               pfind++;
+
+               paths[0] = dirbuf;
+               npaths = 1;                             /* only 1 dir */
+       }
+
+       for (i = 0; i < npaths; i++) {
+
+               dir = opendir(paths[i]);
+               if (!dir)                       /* Don't print an error */
+                       continue;
+
+               while ((next = readdir(dir)) != NULL) {
+                       char *str_found = next->d_name;
+
+                       /* matched ? */
+                       if (strncmp(str_found, pfind, strlen(pfind)))
+                               continue;
+                       /* not see .name without .match */
+                       if (*str_found == '.' && *pfind == 0) {
+                               if (*paths[i] == '/' && paths[i][1] == 0
+                                       && str_found[1] == 0) str_found = "";   /* only "/" */
+                               else
+                                       continue;
+                       }
+                       found = concat_path_file(paths[i], str_found);
+                       /* hmm, remover in progress? */
+                       if (stat(found, &st) < 0)
+                               goto cont;
+                       /* find with dirs ? */
+                       if (paths[i] != dirbuf)
+                               strcpy(found, next->d_name);    /* only name */
+                       if (S_ISDIR(st.st_mode)) {
+                               /* name is directory      */
+                               str_found = found;
+                               found = concat_path_file(found, "");
+                               free(str_found);
+                               str_found = add_quote_for_spec_chars(found);
+                       } else {
+                               /* not put found file if search only dirs for cd */
+                               if (type == FIND_DIR_ONLY)
+                                       goto cont;
+                               str_found = add_quote_for_spec_chars(found);
+                               if (type == FIND_FILE_ONLY ||
+                                       (type == FIND_EXE_ONLY && is_execute(&st)))
+                                       strcat(str_found, " ");
+                       }
+                       /* Add it to the list */
+                       matches = xrealloc(matches, (nm + 1) * sizeof(char *));
+
+                       matches[nm++] = str_found;
+cont:
+                       free(found);
+               }
+               closedir(dir);
+       }
+       if (paths != path1) {
+               free(paths[0]);                 /* allocated memory only in first member */
+               free(paths);
+       }
+       *num_matches = nm;
+       return (matches);
+}
+
+static int match_compare(const void *a, const void *b)
+{
+       return strcmp(*(char **) a, *(char **) b);
+}
+
+
+
+#define QUOT    (UCHAR_MAX+1)
+
+#define collapse_pos(is, in) { \
+       memcpy(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
+       memcpy(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
+
+static int find_match(char *matchBuf, int *len_with_quotes)
+{
+       int i, j;
+       int command_mode;
+       int c, c2;
+       int int_buf[BUFSIZ + 1];
+       int pos_buf[BUFSIZ + 1];
+
+       /* set to integer dimension characters and own positions */
+       for (i = 0;; i++) {
+               int_buf[i] = (int) ((unsigned char) matchBuf[i]);
+               if (int_buf[i] == 0) {
+                       pos_buf[i] = -1;        /* indicator end line */
+                       break;
+               } else
+                       pos_buf[i] = i;
+       }
+
+       /* mask \+symbol and convert '\t' to ' ' */
+       for (i = j = 0; matchBuf[i]; i++, j++)
+               if (matchBuf[i] == '\\') {
+                       collapse_pos(j, j + 1);
+                       int_buf[j] |= QUOT;
+                       i++;
+#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+                       if (matchBuf[i] == '\t')        /* algorithm equivalent */
+                               int_buf[j] = ' ' | QUOT;
+#endif
+               }
+#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+               else if (matchBuf[i] == '\t')
+                       int_buf[j] = ' ';
+#endif
+
+       /* mask "symbols" or 'symbols' */
+       c2 = 0;
+       for (i = 0; int_buf[i]; i++) {
+               c = int_buf[i];
+               if (c == '\'' || c == '"') {
+                       if (c2 == 0)
+                               c2 = c;
+                       else {
+                               if (c == c2)
+                                       c2 = 0;
+                               else
+                                       int_buf[i] |= QUOT;
+                       }
+               } else if (c2 != 0 && c != '$')
+                       int_buf[i] |= QUOT;
+       }
+
+       /* skip commands with arguments if line have commands delimiters */
+       /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
+       for (i = 0; int_buf[i]; i++) {
+               c = int_buf[i];
+               c2 = int_buf[i + 1];
+               j = i ? int_buf[i - 1] : -1;
+               command_mode = 0;
+               if (c == ';' || c == '&' || c == '|') {
+                       command_mode = 1 + (c == c2);
+                       if (c == '&') {
+                               if (j == '>' || j == '<')
+                                       command_mode = 0;
+                       } else if (c == '|' && j == '>')
+                               command_mode = 0;
+               }
+               if (command_mode) {
+                       collapse_pos(0, i + command_mode);
+                       i = -1;                         /* hack incremet */
+               }
+       }
+       /* collapse `command...` */
+       for (i = 0; int_buf[i]; i++)
+               if (int_buf[i] == '`') {
+                       for (j = i + 1; int_buf[j]; j++)
+                               if (int_buf[j] == '`') {
+                                       collapse_pos(i, j + 1);
+                                       j = 0;
+                                       break;
+                               }
+                       if (j) {
+                               /* not found close ` - command mode, collapse all previous */
+                               collapse_pos(0, i + 1);
+                               break;
+                       } else
+                               i--;                    /* hack incremet */
+               }
+
+       /* collapse (command...(command...)...) or {command...{command...}...} */
+       c = 0;                                          /* "recursive" level */
+       c2 = 0;
+       for (i = 0; int_buf[i]; i++)
+               if (int_buf[i] == '(' || int_buf[i] == '{') {
+                       if (int_buf[i] == '(')
+                               c++;
+                       else
+                               c2++;
+                       collapse_pos(0, i + 1);
+                       i = -1;                         /* hack incremet */
+               }
+       for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
+               if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
+                       if (int_buf[i] == ')')
+                               c--;
+                       else
+                               c2--;
+                       collapse_pos(0, i + 1);
+                       i = -1;                         /* hack incremet */
+               }
+
+       /* skip first not quote space */
+       for (i = 0; int_buf[i]; i++)
+               if (int_buf[i] != ' ')
+                       break;
+       if (i)
+               collapse_pos(0, i);
+
+       /* set find mode for completion */
+       command_mode = FIND_EXE_ONLY;
+       for (i = 0; int_buf[i]; i++)
+               if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
+                       if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
+                               && matchBuf[pos_buf[0]]=='c'
+                               && matchBuf[pos_buf[1]]=='d' )
+                               command_mode = FIND_DIR_ONLY;
+                       else {
+                               command_mode = FIND_FILE_ONLY;
+                               break;
+                       }
+               }
+       /* "strlen" */
+       for (i = 0; int_buf[i]; i++);
+       /* find last word */
+       for (--i; i >= 0; i--) {
+               c = int_buf[i];
+               if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
+                       collapse_pos(0, i + 1);
+                       break;
+               }
+       }
+       /* skip first not quoted '\'' or '"' */
+       for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
+       /* collapse quote or unquote // or /~ */
+       while ((int_buf[i] & ~QUOT) == '/' &&
+                       ((int_buf[i + 1] & ~QUOT) == '/'
+                        || (int_buf[i + 1] & ~QUOT) == '~')) {
+               i++;
+       }
+
+       /* set only match and destroy quotes */
+       j = 0;
+       for (c = 0; pos_buf[i] >= 0; i++) {
+               matchBuf[c++] = matchBuf[pos_buf[i]];
+               j = pos_buf[i] + 1;
+       }
+       matchBuf[c] = 0;
+       /* old lenght matchBuf with quotes symbols */
+       *len_with_quotes = j ? j - pos_buf[0] : 0;
+
+       return command_mode;
+}
+
+/*
+   display by column original ideas from ls applet,
+   very optimize by my :)
+*/
+static void showfiles(char **matches, int nfiles)
+{
+       int ncols, row;
+       int column_width = 0;
+       int nrows = nfiles;
+
+       /* find the longest file name-  use that as the column width */
+       for (row = 0; row < nrows; row++) {
+               int l = strlen(matches[row]);
+
+               if (column_width < l)
+                       column_width = l;
+       }
+       column_width += 2;              /* min space for columns */
+       ncols = cmdedit_termw / column_width;
+
+       if (ncols > 1) {
+               nrows /= ncols;
+               if(nfiles % ncols)
+                       nrows++;        /* round up fractionals */
+               column_width = -column_width;   /* for printf("%-Ns", ...); */
+       } else {
+               ncols = 1;
+       }
+       for (row = 0; row < nrows; row++) {
+               int n = row;
+               int nc;
+
+               for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++)
+                       printf("%*s", column_width, matches[n]);
+               printf("%s\n", matches[n]);
+       }
+}
+
+
+static void input_tab(int *lastWasTab)
+{
+       /* Do TAB completion */
+       static int num_matches;
+       static char **matches;
+
+       if (lastWasTab == 0) {          /* free all memory */
+               if (matches) {
+                       while (num_matches > 0)
+                               free(matches[--num_matches]);
+                       free(matches);
+                       matches = (char **) NULL;
+               }
+               return;
+       }
+       if (! *lastWasTab) {
+
+               char *tmp;
+               int len_found;
+               char matchBuf[BUFSIZ];
+               int find_type;
+               int recalc_pos;
+
+               *lastWasTab = TRUE;             /* flop trigger */
+
+               /* Make a local copy of the string -- up
+                * to the position of the cursor */
+               tmp = strncpy(matchBuf, command_ps, cursor);
+               tmp[cursor] = 0;
+
+               find_type = find_match(matchBuf, &recalc_pos);
+
+               /* Free up any memory already allocated */
+               input_tab(0);
+
+#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
+               /* If the word starts with `~' and there is no slash in the word,
+                * then try completing this word as a username. */
+
+               if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
+                       matches = username_tab_completion(matchBuf, &num_matches);
+#endif
+               /* Try to match any executable in our path and everything
+                * in the current working directory that matches.  */
+               if (!matches)
+                       matches =
+                               exe_n_cwd_tab_completion(matchBuf,
+                                       &num_matches, find_type);
+               /* Remove duplicate found */
+               if(matches) {
+                       int i, j;
+                       /* bubble */
+                       for(i=0; i<(num_matches-1); i++)
+                               for(j=i+1; j<num_matches; j++)
+                                       if(matches[i]!=0 && matches[j]!=0 &&
+                                               strcmp(matches[i], matches[j])==0) {
+                                                       free(matches[j]);
+                                                       matches[j]=0;
+                                       }
+                       j=num_matches;
+                       num_matches = 0;
+                       for(i=0; i<j; i++)
+                               if(matches[i]) {
+                                       if(!strcmp(matches[i], "./"))
+                                               matches[i][1]=0;
+                                       else if(!strcmp(matches[i], "../"))
+                                               matches[i][2]=0;
+                                       matches[num_matches++]=matches[i];
+                               }
+               }
+               /* Did we find exactly one match? */
+               if (!matches || num_matches > 1) {
+                       char *tmp1;
+
+                       beep();
+                       if (!matches)
+                               return;         /* not found */
+                       /* sort */
+                       qsort(matches, num_matches, sizeof(char *), match_compare);
+
+                       /* find minimal match */
+                       tmp = bb_xstrdup(matches[0]);
+                       for (tmp1 = tmp; *tmp1; tmp1++)
+                               for (len_found = 1; len_found < num_matches; len_found++)
+                                       if (matches[len_found][(tmp1 - tmp)] != *tmp1) {
+                                               *tmp1 = 0;
+                                               break;
+                                       }
+                       if (*tmp == 0) {        /* have unique */
+                               free(tmp);
+                               return;
+                       }
+               } else {                        /* one match */
+                       tmp = matches[0];
+                       /* for next completion current found */
+                       *lastWasTab = FALSE;
+               }
+
+               len_found = strlen(tmp);
+               /* have space to placed match? */
+               if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
+
+                       /* before word for match   */
+                       command_ps[cursor - recalc_pos] = 0;
+                       /* save   tail line        */
+                       strcpy(matchBuf, command_ps + cursor);
+                       /* add    match            */
+                       strcat(command_ps, tmp);
+                       /* add    tail             */
+                       strcat(command_ps, matchBuf);
+                       /* back to begin word for match    */
+                       input_backward(recalc_pos);
+                       /* new pos                         */
+                       recalc_pos = cursor + len_found;
+                       /* new len                         */
+                       len = strlen(command_ps);
+                       /* write out the matched command   */
+                       redraw(cmdedit_y, len - recalc_pos);
+               }
+               if (tmp != matches[0])
+                       free(tmp);
+       } else {
+               /* Ok -- the last char was a TAB.  Since they
+                * just hit TAB again, print a list of all the
+                * available choices... */
+               if (matches && num_matches > 0) {
+                       int sav_cursor = cursor;        /* change goto_new_line() */
+
+                       /* Go to the next line */
+                       goto_new_line();
+                       showfiles(matches, num_matches);
+                       redraw(0, len - sav_cursor);
+               }
+       }
+}
+#endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
+
+#if MAX_HISTORY >= 1
+static void get_previous_history(void)
+{
+       if(command_ps[0] != 0 || history[cur_history] == 0) {
+               free(history[cur_history]);
+               history[cur_history] = bb_xstrdup(command_ps);
+       }
+       cur_history--;
+}
+
+static int get_next_history(void)
+{
+       int ch = cur_history;
+
+       if (ch < n_history) {
+               get_previous_history(); /* save the current history line */
+               return (cur_history = ch+1);
+       } else {
+               beep();
+               return 0;
+       }
+}
+
+#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+extern void load_history ( const char *fromfile )
+{
+       FILE *fp;
+       int hi;
+
+       /* cleanup old */
+
+       for(hi = n_history; hi > 0; ) {
+               hi--;
+               free ( history [hi] );
+       }
+
+       if (( fp = fopen ( fromfile, "r" ))) {
+
+               for ( hi = 0; hi < MAX_HISTORY; ) {
+                       char * hl = bb_get_chomped_line_from_file(fp);
+                       int l;
+
+                       if(!hl)
+                               break;
+                       l = strlen(hl);
+                       if(l >= BUFSIZ)
+                               hl[BUFSIZ-1] = 0;
+                       if(l == 0 || hl[0] == ' ') {
+                               free(hl);
+                               continue;
+                       }
+                       history [hi++] = hl;
+               }
+               fclose ( fp );
+       }
+       cur_history = n_history = hi;
+}
+
+extern void save_history ( const char *tofile )
+{
+       FILE *fp = fopen ( tofile, "w" );
+
+       if ( fp ) {
+               int i;
+
+               for ( i = 0; i < n_history; i++ ) {
+                       fprintf(fp, "%s\n", history [i]);
+               }
+               fclose ( fp );
+       }
+}
+#endif
+
+#endif
+
+enum {
+       ESC = 27,
+       DEL = 127,
+};
+
+
+/*
+ * This function is used to grab a character buffer
+ * from the input file descriptor and allows you to
+ * a string with full command editing (sort of like
+ * a mini readline).
+ *
+ * The following standard commands are not implemented:
+ * ESC-b -- Move back one word
+ * ESC-f -- Move forward one word
+ * ESC-d -- Delete back one word
+ * ESC-h -- Delete forward one word
+ * CTL-t -- Transpose two characters
+ *
+ * Furthermore, the "vi" command editing keys are not implemented.
+ *
+ */
+
+
+int cmdedit_read_input(char *prompt, char command[BUFSIZ])
+{
+
+       int break_out = 0;
+       int lastWasTab = FALSE;
+       unsigned char c = 0;
+
+       /* prepare before init handlers */
+       cmdedit_y = 0;  /* quasireal y, not true work if line > xt*yt */
+       len = 0;
+       command_ps = command;
+
+       getTermSettings(0, (void *) &initial_settings);
+       memcpy(&new_settings, &initial_settings, sizeof(struct termios));
+       new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
+       /* Turn off echoing and CTRL-C, so we can trap it */
+       new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
+       /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
+       new_settings.c_cc[VMIN] = 1;
+       new_settings.c_cc[VTIME] = 0;
+       /* Turn off CTRL-C, so we can trap it */
+#       ifndef _POSIX_VDISABLE
+#               define _POSIX_VDISABLE '\0'
+#       endif
+       new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
+       command[0] = 0;
+
+       setTermSettings(0, (void *) &new_settings);
+       handlers_sets |= SET_RESET_TERM;
+
+       /* Now initialize things */
+       cmdedit_init();
+       /* Print out the command prompt */
+       parse_prompt(prompt);
+
+       while (1) {
+
+               fflush(stdout);                 /* buffered out to fast */
+
+               if (safe_read(0, &c, 1) < 1)
+                       /* if we can't read input then exit */
+                       goto prepare_to_die;
+
+               switch (c) {
+               case '\n':
+               case '\r':
+                       /* Enter */
+                       goto_new_line();
+                       break_out = 1;
+                       break;
+               case 1:
+                       /* Control-a -- Beginning of line */
+                       input_backward(cursor);
+                       break;
+               case 2:
+                       /* Control-b -- Move back one character */
+                       input_backward(1);
+                       break;
+               case 3:
+                       /* Control-c -- stop gathering input */
+                       goto_new_line();
+#ifndef CONFIG_ASH
+                       command[0] = 0;
+                       len = 0;
+                       lastWasTab = FALSE;
+                       put_prompt();
+#else
+                       len = 0;
+                       break_out = -1; /* to control traps */
+#endif
+                       break;
+               case 4:
+                       /* Control-d -- Delete one character, or exit
+                        * if the len=0 and no chars to delete */
+                       if (len == 0) {
+                                       errno = 0;
+prepare_to_die:
+#if !defined(CONFIG_ASH)
+                               printf("exit");
+                               goto_new_line();
+                               /* cmdedit_reset_term() called in atexit */
+                               exit(EXIT_SUCCESS);
+#else
+                               /* to control stopped jobs */
+                               len = break_out = -1;
+                               break;
+#endif
+                       } else {
+                               input_delete();
+                       }
+                       break;
+               case 5:
+                       /* Control-e -- End of line */
+                       input_end();
+                       break;
+               case 6:
+                       /* Control-f -- Move forward one character */
+                       input_forward();
+                       break;
+               case '\b':
+               case DEL:
+                       /* Control-h and DEL */
+                       input_backspace();
+                       break;
+               case '\t':
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+                       input_tab(&lastWasTab);
+#endif
+                       break;
+               case 11:
+                       /* Control-k -- clear to end of line */
+                       *(command + cursor) = 0;
+                       len = cursor;
+                       printf("\033[J");
+                       break;
+               case 12:
+                       /* Control-l -- clear screen */
+                       printf("\033[H");
+                       redraw(0, len-cursor);
+                       break;
+#if MAX_HISTORY >= 1
+               case 14:
+                       /* Control-n -- Get next command in history */
+                       if (get_next_history())
+                               goto rewrite_line;
+                       break;
+               case 16:
+                       /* Control-p -- Get previous command from history */
+                       if (cur_history > 0) {
+                               get_previous_history();
+                               goto rewrite_line;
+                       } else {
+                               beep();
+                       }
+                       break;
+#endif
+               case 21:
+                       /* Control-U -- Clear line before cursor */
+                       if (cursor) {
+                               strcpy(command, command + cursor);
+                               redraw(cmdedit_y, len -= cursor);
+                       }
+                       break;
+               case 23:
+                       /* Control-W -- Remove the last word */
+                       while (cursor > 0 && isspace(command[cursor-1]))
+                               input_backspace();
+                       while (cursor > 0 &&!isspace(command[cursor-1]))
+                               input_backspace();
+                       break;
+               case ESC:{
+                       /* escape sequence follows */
+                       if (safe_read(0, &c, 1) < 1)
+                               goto prepare_to_die;
+                       /* different vt100 emulations */
+                       if (c == '[' || c == 'O') {
+                               if (safe_read(0, &c, 1) < 1)
+                                       goto prepare_to_die;
+                       }
+                       if (c >= '1' && c <= '9') {
+                               unsigned char dummy;
+
+                               if (safe_read(0, &dummy, 1) < 1)
+                                       goto prepare_to_die;
+                               if(dummy != '~')
+                                       c = 0;
+                       }
+                       switch (c) {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
+                       case '\t':                      /* Alt-Tab */
+
+                               input_tab(&lastWasTab);
+                               break;
+#endif
+#if MAX_HISTORY >= 1
+                       case 'A':
+                               /* Up Arrow -- Get previous command from history */
+                               if (cur_history > 0) {
+                                       get_previous_history();
+                                       goto rewrite_line;
+                               } else {
+                                       beep();
+                               }
+                               break;
+                       case 'B':
+                               /* Down Arrow -- Get next command in history */
+                               if (!get_next_history())
+                               break;
+                               /* Rewrite the line with the selected history item */
+rewrite_line:
+                               /* change command */
+                               len = strlen(strcpy(command, history[cur_history]));
+                               /* redraw and go to end line */
+                               redraw(cmdedit_y, 0);
+                               break;
+#endif
+                       case 'C':
+                               /* Right Arrow -- Move forward one character */
+                               input_forward();
+                               break;
+                       case 'D':
+                               /* Left Arrow -- Move back one character */
+                               input_backward(1);
+                               break;
+                       case '3':
+                               /* Delete */
+                               input_delete();
+                               break;
+                       case '1':
+                       case 'H':
+                               /* <Home> */
+                               input_backward(cursor);
+                               break;
+                       case '4':
+                       case 'F':
+                               /* <End> */
+                               input_end();
+                               break;
+                       default:
+                               c = 0;
+                               beep();
+                       }
+                       break;
+               }
+
+               default:        /* If it's regular input, do the normal thing */
+#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+                       /* Control-V -- Add non-printable symbol */
+                       if (c == 22) {
+                               if (safe_read(0, &c, 1) < 1)
+                                       goto prepare_to_die;
+                               if (c == 0) {
+                                       beep();
+                                       break;
+                               }
+                       } else
+#endif
+                       if (!Isprint(c))        /* Skip non-printable characters */
+                               break;
+
+                       if (len >= (BUFSIZ - 2))        /* Need to leave space for enter */
+                               break;
+
+                       len++;
+
+                       if (cursor == (len - 1)) {      /* Append if at the end of the line */
+                               *(command + cursor) = c;
+                               *(command + cursor + 1) = 0;
+                               cmdedit_set_out_char(0);
+                       } else {                        /* Insert otherwise */
+                               int sc = cursor;
+
+                               memmove(command + sc + 1, command + sc, len - sc);
+                               *(command + sc) = c;
+                               sc++;
+                               /* rewrite from cursor */
+                               input_end();
+                               /* to prev x pos + 1 */
+                               input_backward(cursor - sc);
+                       }
+
+                       break;
+               }
+               if (break_out)                  /* Enter is the command terminator, no more input. */
+                       break;
+
+               if (c != '\t')
+                       lastWasTab = FALSE;
+       }
+
+       setTermSettings(0, (void *) &initial_settings);
+       handlers_sets &= ~SET_RESET_TERM;
+
+#if MAX_HISTORY >= 1
+       /* Handle command history log */
+       /* cleanup may be saved current command line */
+       if (len> 0) {                                      /* no put empty line */
+               int i = n_history;
+
+               free(history[MAX_HISTORY]);
+               history[MAX_HISTORY] = 0;
+                       /* After max history, remove the oldest command */
+               if (i >= MAX_HISTORY) {
+                       free(history[0]);
+                       for(i = 0; i < (MAX_HISTORY-1); i++)
+                               history[i] = history[i+1];
+               }
+               history[i++] = bb_xstrdup(command);
+               cur_history = i;
+               n_history = i;
+#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+               num_ok_lines++;
+#endif
+       }
+#else  /* MAX_HISTORY < 1 */
+#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+       if (len > 0) {              /* no put empty line */
+               num_ok_lines++;
+       }
+#endif
+#endif  /* MAX_HISTORY >= 1 */
+       if (break_out > 0) {
+               command[len++] = '\n';          /* set '\n' */
+               command[len] = 0;
+       }
+#if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
+       input_tab(0);                           /* strong free */
+#endif
+#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+       free(cmdedit_prompt);
+#endif
+       cmdedit_reset_term();
+       return len;
+}
+
+
+
+#endif  /* CONFIG_FEATURE_COMMAND_EDITING */
+
+
+#ifdef TEST
+
+const char *bb_applet_name = "debug stuff usage";
+
+#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+#include <locale.h>
+#endif
+
+int main(int argc, char **argv)
+{
+       char buff[BUFSIZ];
+       char *prompt =
+#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+               "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
+\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
+\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
+#else
+               "% ";
+#endif
+
+#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
+       setlocale(LC_ALL, "");
+#endif
+       while(1) {
+               int l;
+               l = cmdedit_read_input(prompt, buff);
+               if(l > 0 && buff[l-1] == '\n') {
+                       buff[l-1] = 0;
+                       printf("*** cmdedit_read_input() returned line =%s=\n", buff);
+               } else {
+                       break;
+               }
+       }
+       printf("*** cmdedit_read_input() detect ^D\n");
+       return 0;
+}
+
+#endif  /* TEST */
diff --git a/busybox/shell/cmdedit.h b/busybox/shell/cmdedit.h
new file mode 100644 (file)
index 0000000..4c0c09d
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef CMDEDIT_H
+#define CMDEDIT_H
+
+int     cmdedit_read_input(char* promptStr, char* command);
+
+#ifdef CONFIG_ASH
+extern const char *cmdedit_path_lookup;
+#endif
+
+#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
+void    load_history ( const char *fromfile );
+void    save_history ( const char *tofile );
+#endif
+
+#endif /* CMDEDIT_H */
diff --git a/busybox/shell/hush.c b/busybox/shell/hush.c
new file mode 100644 (file)
index 0000000..49e2397
--- /dev/null
@@ -0,0 +1,2963 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sh.c -- a prototype Bourne shell grammar parser
+ *      Intended to follow the original Thompson and Ritchie
+ *      "small and simple is beautiful" philosophy, which
+ *      incidentally is a good match to today's BusyBox.
+ *
+ * Copyright (C) 2000,2001  Larry Doolittle  <larry@doolittle.boa.org>
+ *
+ * Credits:
+ *      The parser routines proper are all original material, first
+ *      written Dec 2000 and Jan 2001 by Larry Doolittle.  The
+ *      execution engine, the builtins, and much of the underlying
+ *      support has been adapted from busybox-0.49pre's lash, which is
+ *      Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *      written by Erik Andersen <andersen@codepoet.org>.  That, in turn,
+ *      is based in part on ladsh.c, by Michael K. Johnson and Erik W.
+ *      Troan, which they placed in the public domain.  I don't know
+ *      how much of the Johnson/Troan code has survived the repeated
+ *      rewrites.
+ *
+ * Other credits:
+ *      simple_itoa() was lifted from boa-0.93.15
+ *      b_addchr() derived from similar w_addchar function in glibc-2.2
+ *      setup_redirect(), redirect_opt_num(), and big chunks of main()
+ *        and many builtins derived from contributions by Erik Andersen
+ *      miscellaneous bugfixes from Matt Kraai
+ *
+ * There are two big (and related) architecture differences between
+ * this parser and the lash parser.  One is that this version is
+ * actually designed from the ground up to understand nearly all
+ * of the Bourne grammar.  The second, consequential change is that
+ * the parser and input reader have been turned inside out.  Now,
+ * the parser is in control, and asks for input as needed.  The old
+ * way had the input reader in control, and it asked for parsing to
+ * take place as needed.  The new way makes it much easier to properly
+ * handle the recursion implicit in the various substitutions, especially
+ * across continuation lines.
+ *
+ * Bash grammar not implemented: (how many of these were in original sh?)
+ *      $@ (those sure look like weird quoting rules)
+ *      $_
+ *      ! negation operator for pipes
+ *      &> and >& redirection of stdout+stderr
+ *      Brace Expansion
+ *      Tilde Expansion
+ *      fancy forms of Parameter Expansion
+ *      aliases
+ *      Arithmetic Expansion
+ *      <(list) and >(list) Process Substitution
+ *      reserved words: case, esac, select, function
+ *      Here Documents ( << word )
+ *      Functions
+ * Major bugs:
+ *      job handling woefully incomplete and buggy
+ *      reserved word execution woefully incomplete and buggy
+ * to-do:
+ *      port selected bugfixes from post-0.49 busybox lash - done?
+ *      finish implementing reserved words: for, while, until, do, done
+ *      change { and } from special chars to reserved words
+ *      builtins: break, continue, eval, return, set, trap, ulimit
+ *      test magic exec
+ *      handle children going into background
+ *      clean up recognition of null pipes
+ *      check setting of global_argc and global_argv
+ *      control-C handling, probably with longjmp
+ *      follow IFS rules more precisely, including update semantics
+ *      figure out what to do with backslash-newline
+ *      explain why we use signal instead of sigaction
+ *      propagate syntax errors, die on resource errors?
+ *      continuation lines, both explicit and implicit - done?
+ *      memory leak finding and plugging - done?
+ *      more testing, especially quoting rules and redirection
+ *      document how quoting rules not precisely followed for variable assignments
+ *      maybe change map[] to use 2-bit entries
+ *      (eventually) remove all the printf's
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <ctype.h>     /* isalpha, isdigit */
+#include <unistd.h>    /* getpid */
+#include <stdlib.h>    /* getenv, atoi */
+#include <string.h>    /* strchr */
+#include <stdio.h>     /* popen etc. */
+#include <glob.h>      /* glob, of course */
+#include <stdarg.h>    /* va_list */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>    /* should be pretty obvious */
+
+#include <sys/stat.h>  /* ulimit */
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+/* #include <dmalloc.h> */
+/* #define DEBUG_SHELL */
+
+#if 1
+#include "busybox.h"
+#include "cmdedit.h"
+#else
+#define bb_applet_name "hush"
+#include "standalone.h"
+#define hush_main main
+#undef CONFIG_FEATURE_SH_FANCY_PROMPT
+#define BB_BANNER
+#endif
+#define SPECIAL_VAR_SYMBOL 03
+#define FLAG_EXIT_FROM_LOOP 1
+#define FLAG_PARSE_SEMICOLON (1 << 1)          /* symbol ';' is special for parser */
+#define FLAG_REPARSING          (1 << 2)               /* >=2nd pass */
+
+typedef enum {
+       REDIRECT_INPUT     = 1,
+       REDIRECT_OVERWRITE = 2,
+       REDIRECT_APPEND    = 3,
+       REDIRECT_HEREIS    = 4,
+       REDIRECT_IO        = 5
+} redir_type;
+
+/* The descrip member of this structure is only used to make debugging
+ * output pretty */
+struct {int mode; int default_fd; char *descrip;} redir_table[] = {
+       { 0,                         0, "()" },
+       { O_RDONLY,                  0, "<"  },
+       { O_CREAT|O_TRUNC|O_WRONLY,  1, ">"  },
+       { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
+       { O_RDONLY,                 -1, "<<" },
+       { O_RDWR,                    1, "<>" }
+};
+
+typedef enum {
+       PIPE_SEQ = 1,
+       PIPE_AND = 2,
+       PIPE_OR  = 3,
+       PIPE_BG  = 4,
+} pipe_style;
+
+/* might eventually control execution */
+typedef enum {
+       RES_NONE  = 0,
+       RES_IF    = 1,
+       RES_THEN  = 2,
+       RES_ELIF  = 3,
+       RES_ELSE  = 4,
+       RES_FI    = 5,
+       RES_FOR   = 6,
+       RES_WHILE = 7,
+       RES_UNTIL = 8,
+       RES_DO    = 9,
+       RES_DONE  = 10,
+       RES_XXXX  = 11,
+       RES_IN    = 12,
+       RES_SNTX  = 13
+} reserved_style;
+#define FLAG_END   (1<<RES_NONE)
+#define FLAG_IF    (1<<RES_IF)
+#define FLAG_THEN  (1<<RES_THEN)
+#define FLAG_ELIF  (1<<RES_ELIF)
+#define FLAG_ELSE  (1<<RES_ELSE)
+#define FLAG_FI    (1<<RES_FI)
+#define FLAG_FOR   (1<<RES_FOR)
+#define FLAG_WHILE (1<<RES_WHILE)
+#define FLAG_UNTIL (1<<RES_UNTIL)
+#define FLAG_DO    (1<<RES_DO)
+#define FLAG_DONE  (1<<RES_DONE)
+#define FLAG_IN    (1<<RES_IN)
+#define FLAG_START (1<<RES_XXXX)
+
+/* This holds pointers to the various results of parsing */
+struct p_context {
+       struct child_prog *child;
+       struct pipe *list_head;
+       struct pipe *pipe;
+       struct redir_struct *pending_redirect;
+       reserved_style w;
+       int old_flag;                           /* for figuring out valid reserved words */
+       struct p_context *stack;
+       int type;                       /* define type of parser : ";$" common or special symbol */
+       /* How about quoting status? */
+};
+
+struct redir_struct {
+       redir_type type;                        /* type of redirection */
+       int fd;                                         /* file descriptor being redirected */
+       int dup;                                        /* -1, or file descriptor being duplicated */
+       struct redir_struct *next;      /* pointer to the next redirect in the list */
+       glob_t word;                            /* *word.gl_pathv is the filename */
+};
+
+struct child_prog {
+       pid_t pid;                                      /* 0 if exited */
+       char **argv;                            /* program name and arguments */
+       struct pipe *group;                     /* if non-NULL, first in group or subshell */
+       int subshell;                           /* flag, non-zero if group must be forked */
+       struct redir_struct *redirects; /* I/O redirections */
+       glob_t glob_result;                     /* result of parameter globbing */
+       int is_stopped;                         /* is the program currently running? */
+       struct pipe *family;            /* pointer back to the child's parent pipe */
+       int sp;                         /* number of SPECIAL_VAR_SYMBOL */
+       int type;
+};
+
+struct pipe {
+       int jobid;                                      /* job number */
+       int num_progs;                          /* total number of programs in job */
+       int running_progs;                      /* number of programs running */
+       char *text;                                     /* name of job */
+       char *cmdbuf;                           /* buffer various argv's point into */
+       pid_t pgrp;                                     /* process group ID for the job */
+       struct child_prog *progs;       /* array of commands in pipe */
+       struct pipe *next;                      /* to track background commands */
+       int stopped_progs;                      /* number of programs alive, but stopped */
+       int job_context;                        /* bitmask defining current context */
+       pipe_style followup;            /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
+       reserved_style r_mode;          /* supports if, for, while, until */
+};
+
+struct close_me {
+       int fd;
+       struct close_me *next;
+};
+
+struct variables {
+       char *name;
+       char *value;
+       int flg_export;
+       int flg_read_only;
+       struct variables *next;
+};
+
+/* globals, connect us to the outside world
+ * the first three support $?, $#, and $1 */
+char **global_argv;
+unsigned int global_argc;
+unsigned int last_return_code;
+extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
+
+/* "globals" within this file */
+static char *ifs;
+static char map[256];
+static int fake_mode;
+static int interactive;
+static struct close_me *close_me_head;
+static const char *cwd;
+static struct pipe *job_list;
+static unsigned int last_bg_pid;
+static unsigned int last_jobid;
+static unsigned int shell_terminal;
+static char *PS1;
+static char *PS2;
+struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
+struct variables *top_vars = &shell_ver;
+
+
+#define B_CHUNK (100)
+#define B_NOSPAC 1
+
+typedef struct {
+       char *data;
+       int length;
+       int maxlen;
+       int quote;
+       int nonnull;
+} o_string;
+#define NULL_O_STRING {NULL,0,0,0,0}
+/* used for initialization:
+       o_string foo = NULL_O_STRING; */
+
+/* I can almost use ordinary FILE *.  Is open_memstream() universally
+ * available?  Where is it documented? */
+struct in_str {
+       const char *p;
+       char peek_buf[2];
+       int __promptme;
+       int promptmode;
+       FILE *file;
+       int (*get) (struct in_str *);
+       int (*peek) (struct in_str *);
+};
+#define b_getch(input) ((input)->get(input))
+#define b_peek(input) ((input)->peek(input))
+
+#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
+
+struct built_in_command {
+       char *cmd;                                      /* name */
+       char *descr;                            /* description */
+       int (*function) (struct child_prog *);  /* function ptr */
+};
+
+/* belongs in busybox.h */
+static inline int max(int a, int b) {
+       return (a>b)?a:b;
+}
+
+/* This should be in utility.c */
+#ifdef DEBUG_SHELL
+static void debug_printf(const char *format, ...)
+{
+       va_list args;
+       va_start(args, format);
+       vfprintf(stderr, format, args);
+       va_end(args);
+}
+#else
+static inline void debug_printf(const char *format, ...) { }
+#endif
+#define final_printf debug_printf
+
+static void __syntax(char *file, int line) {
+       bb_error_msg("syntax error %s:%d", file, line);
+}
+#define syntax() __syntax(__FILE__, __LINE__)
+
+/* Index of subroutines: */
+/*   function prototypes for builtins */
+static int builtin_cd(struct child_prog *child);
+static int builtin_env(struct child_prog *child);
+static int builtin_eval(struct child_prog *child);
+static int builtin_exec(struct child_prog *child);
+static int builtin_exit(struct child_prog *child);
+static int builtin_export(struct child_prog *child);
+static int builtin_fg_bg(struct child_prog *child);
+static int builtin_help(struct child_prog *child);
+static int builtin_jobs(struct child_prog *child);
+static int builtin_pwd(struct child_prog *child);
+static int builtin_read(struct child_prog *child);
+static int builtin_set(struct child_prog *child);
+static int builtin_shift(struct child_prog *child);
+static int builtin_source(struct child_prog *child);
+static int builtin_umask(struct child_prog *child);
+static int builtin_unset(struct child_prog *child);
+static int builtin_not_written(struct child_prog *child);
+/*   o_string manipulation: */
+static int b_check_space(o_string *o, int len);
+static int b_addchr(o_string *o, int ch);
+static void b_reset(o_string *o);
+static int b_addqchr(o_string *o, int ch, int quote);
+static int b_adduint(o_string *o, unsigned int i);
+/*  in_str manipulations: */
+static int static_get(struct in_str *i);
+static int static_peek(struct in_str *i);
+static int file_get(struct in_str *i);
+static int file_peek(struct in_str *i);
+static void setup_file_in_str(struct in_str *i, FILE *f);
+static void setup_string_in_str(struct in_str *i, const char *s);
+/*  close_me manipulations: */
+static void mark_open(int fd);
+static void mark_closed(int fd);
+static void close_all(void);
+/*  "run" the final data structures: */
+static char *indenter(int i);
+static int free_pipe_list(struct pipe *head, int indent);
+static int free_pipe(struct pipe *pi, int indent);
+/*  really run the final data structures: */
+static int setup_redirects(struct child_prog *prog, int squirrel[]);
+static int run_list_real(struct pipe *pi);
+static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn));
+static int run_pipe_real(struct pipe *pi);
+/*   extended glob support: */
+static int globhack(const char *src, int flags, glob_t *pglob);
+static int glob_needed(const char *s);
+static int xglob(o_string *dest, int flags, glob_t *pglob);
+/*   variable assignment: */
+static int is_assignment(const char *s);
+/*   data structure manipulation: */
+static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
+static void initialize_context(struct p_context *ctx);
+static int done_word(o_string *dest, struct p_context *ctx);
+static int done_command(struct p_context *ctx);
+static int done_pipe(struct p_context *ctx, pipe_style type);
+/*   primary string parsing: */
+static int redirect_dup_num(struct in_str *input);
+static int redirect_opt_num(o_string *o);
+static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end);
+static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
+static char *lookup_param(char *src);
+static char *make_string(char **inp);
+static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
+static int parse_string(o_string *dest, struct p_context *ctx, const char *src);
+static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger);
+/*   setup: */
+static int parse_stream_outer(struct in_str *inp, int flag);
+static int parse_string_outer(const char *s, int flag);
+static int parse_file_outer(FILE *f);
+/*   job management: */
+static int checkjobs(struct pipe* fg_pipe);
+static void insert_bg_job(struct pipe *pi);
+static void remove_bg_job(struct pipe *pi);
+/*     local variable support */
+static char **make_list_in(char **inp, char *name);
+static char *insert_var_value(char *inp);
+static char *get_local_var(const char *var);
+static void  unset_local_var(const char *name);
+static int set_local_var(const char *s, int flg_export);
+
+/* Table of built-in functions.  They can be forked or not, depending on
+ * context: within pipes, they fork.  As simple commands, they do not.
+ * When used in non-forking context, they can change global variables
+ * in the parent shell process.  If forked, of course they can not.
+ * For example, 'unset foo | whatever' will parse and run, but foo will
+ * still be set at the end. */
+static struct built_in_command bltins[] = {
+       {"bg", "Resume a job in the background", builtin_fg_bg},
+       {"break", "Exit for, while or until loop", builtin_not_written},
+       {"cd", "Change working directory", builtin_cd},
+       {"continue", "Continue for, while or until loop", builtin_not_written},
+       {"env", "Print all environment variables", builtin_env},
+       {"eval", "Construct and run shell command", builtin_eval},
+       {"exec", "Exec command, replacing this shell with the exec'd process",
+               builtin_exec},
+       {"exit", "Exit from shell()", builtin_exit},
+       {"export", "Set environment variable", builtin_export},
+       {"fg", "Bring job into the foreground", builtin_fg_bg},
+       {"jobs", "Lists the active jobs", builtin_jobs},
+       {"pwd", "Print current directory", builtin_pwd},
+       {"read", "Input environment variable", builtin_read},
+       {"return", "Return from a function", builtin_not_written},
+       {"set", "Set/unset shell local variables", builtin_set},
+       {"shift", "Shift positional parameters", builtin_shift},
+       {"trap", "Trap signals", builtin_not_written},
+       {"ulimit","Controls resource limits", builtin_not_written},
+       {"umask","Sets file creation mask", builtin_umask},
+       {"unset", "Unset environment variable", builtin_unset},
+       {".", "Source-in and run commands in a file", builtin_source},
+       {"help", "List shell built-in commands", builtin_help},
+       {NULL, NULL, NULL}
+};
+
+static const char *set_cwd(void)
+{
+       if(cwd==bb_msg_unknown)
+               cwd = NULL;     /* xgetcwd(arg) called free(arg) */
+       cwd = xgetcwd((char *)cwd);
+       if (!cwd)
+               cwd = bb_msg_unknown;
+       return cwd;
+}
+
+/* built-in 'eval' handler */
+static int builtin_eval(struct child_prog *child)
+{
+       char *str = NULL;
+       int rcode = EXIT_SUCCESS;
+
+       if (child->argv[1]) {
+               str = make_string(child->argv + 1);
+               parse_string_outer(str, FLAG_EXIT_FROM_LOOP |
+                                       FLAG_PARSE_SEMICOLON);
+               free(str);
+               rcode = last_return_code;
+       }
+       return rcode;
+}
+
+/* built-in 'cd <path>' handler */
+static int builtin_cd(struct child_prog *child)
+{
+       char *newdir;
+       if (child->argv[1] == NULL)
+               newdir = getenv("HOME");
+       else
+               newdir = child->argv[1];
+       if (chdir(newdir)) {
+               printf("cd: %s: %s\n", newdir, strerror(errno));
+               return EXIT_FAILURE;
+       }
+       set_cwd();
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'env' handler */
+static int builtin_env(struct child_prog *dummy)
+{
+       char **e = environ;
+       if (e == NULL) return EXIT_FAILURE;
+       for (; *e; e++) {
+               puts(*e);
+       }
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'exec' handler */
+static int builtin_exec(struct child_prog *child)
+{
+       if (child->argv[1] == NULL)
+               return EXIT_SUCCESS;   /* Really? */
+       child->argv++;
+       pseudo_exec(child);
+       /* never returns */
+}
+
+/* built-in 'exit' handler */
+static int builtin_exit(struct child_prog *child)
+{
+       if (child->argv[1] == NULL)
+               exit(last_return_code);
+       exit (atoi(child->argv[1]));
+}
+
+/* built-in 'export VAR=value' handler */
+static int builtin_export(struct child_prog *child)
+{
+       int res = 0;
+       char *name = child->argv[1];
+
+       if (name == NULL) {
+               return (builtin_env(child));
+       }
+
+       name = strdup(name);
+
+       if(name) {
+               char *value = strchr(name, '=');
+
+               if (!value) {
+                       char *tmp;
+                       /* They are exporting something without an =VALUE */
+
+                       value = get_local_var(name);
+                       if (value) {
+                               size_t ln = strlen(name);
+
+                               tmp = realloc(name, ln+strlen(value)+2);
+                               if(tmp==NULL)
+                                       res = -1;
+                               else {
+                                       sprintf(tmp+ln, "=%s", value);
+                                       name = tmp;
+                               }
+                       } else {
+                               /* bash does not return an error when trying to export
+                                * an undefined variable.  Do likewise. */
+                               res = 1;
+                       }
+               }
+       }
+       if (res<0)
+               bb_perror_msg("export");
+       else if(res==0)
+               res = set_local_var(name, 1);
+       else
+               res = 0;
+       free(name);
+       return res;
+}
+
+/* built-in 'fg' and 'bg' handler */
+static int builtin_fg_bg(struct child_prog *child)
+{
+       int i, jobnum;
+       struct pipe *pi=NULL;
+
+       if (!interactive)
+               return EXIT_FAILURE;
+       /* If they gave us no args, assume they want the last backgrounded task */
+       if (!child->argv[1]) {
+               for (pi = job_list; pi; pi = pi->next) {
+                       if (pi->jobid == last_jobid) {
+                               break;
+                       }
+               }
+               if (!pi) {
+                       bb_error_msg("%s: no current job", child->argv[0]);
+                       return EXIT_FAILURE;
+               }
+       } else {
+               if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
+                       bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
+                       return EXIT_FAILURE;
+               }
+               for (pi = job_list; pi; pi = pi->next) {
+                       if (pi->jobid == jobnum) {
+                               break;
+                       }
+               }
+               if (!pi) {
+                       bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (*child->argv[0] == 'f') {
+               /* Put the job into the foreground.  */
+               tcsetpgrp(shell_terminal, pi->pgrp);
+       }
+
+       /* Restart the processes in the job */
+       for (i = 0; i < pi->num_progs; i++)
+               pi->progs[i].is_stopped = 0;
+
+       if ( (i=kill(- pi->pgrp, SIGCONT)) < 0) {
+               if (i == ESRCH) {
+                       remove_bg_job(pi);
+               } else {
+                       bb_perror_msg("kill (SIGCONT)");
+               }
+       }
+
+       pi->stopped_progs = 0;
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'help' handler */
+static int builtin_help(struct child_prog *dummy)
+{
+       struct built_in_command *x;
+
+       printf("\nBuilt-in commands:\n");
+       printf("-------------------\n");
+       for (x = bltins; x->cmd; x++) {
+               if (x->descr==NULL)
+                       continue;
+               printf("%s\t%s\n", x->cmd, x->descr);
+       }
+       printf("\n\n");
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'jobs' handler */
+static int builtin_jobs(struct child_prog *child)
+{
+       struct pipe *job;
+       char *status_string;
+
+       for (job = job_list; job; job = job->next) {
+               if (job->running_progs == job->stopped_progs)
+                       status_string = "Stopped";
+               else
+                       status_string = "Running";
+
+               printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
+       }
+       return EXIT_SUCCESS;
+}
+
+
+/* built-in 'pwd' handler */
+static int builtin_pwd(struct child_prog *dummy)
+{
+       puts(set_cwd());
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'read VAR' handler */
+static int builtin_read(struct child_prog *child)
+{
+       int res;
+
+       if (child->argv[1]) {
+               char string[BUFSIZ];
+               char *var = 0;
+
+               string[0] = 0;  /* In case stdin has only EOF */
+               /* read string */
+               fgets(string, sizeof(string), stdin);
+               chomp(string);
+               var = malloc(strlen(child->argv[1])+strlen(string)+2);
+               if(var) {
+                       sprintf(var, "%s=%s", child->argv[1], string);
+                       res = set_local_var(var, 0);
+               } else
+                       res = -1;
+               if (res)
+                       fprintf(stderr, "read: %m\n");
+               free(var);      /* So not move up to avoid breaking errno */
+               return res;
+       } else {
+               do res=getchar(); while(res!='\n' && res!=EOF);
+               return 0;
+       }
+}
+
+/* built-in 'set VAR=value' handler */
+static int builtin_set(struct child_prog *child)
+{
+       char *temp = child->argv[1];
+       struct variables *e;
+
+       if (temp == NULL)
+               for(e = top_vars; e; e=e->next)
+                       printf("%s=%s\n", e->name, e->value);
+       else
+               set_local_var(temp, 0);
+
+               return EXIT_SUCCESS;
+}
+
+
+/* Built-in 'shift' handler */
+static int builtin_shift(struct child_prog *child)
+{
+       int n=1;
+       if (child->argv[1]) {
+               n=atoi(child->argv[1]);
+       }
+       if (n>=0 && n<global_argc) {
+               /* XXX This probably breaks $0 */
+               global_argc -= n;
+               global_argv += n;
+               return EXIT_SUCCESS;
+       } else {
+               return EXIT_FAILURE;
+       }
+}
+
+/* Built-in '.' handler (read-in and execute commands from file) */
+static int builtin_source(struct child_prog *child)
+{
+       FILE *input;
+       int status;
+
+       if (child->argv[1] == NULL)
+               return EXIT_FAILURE;
+
+       /* XXX search through $PATH is missing */
+       input = fopen(child->argv[1], "r");
+       if (!input) {
+               bb_error_msg("Couldn't open file '%s'", child->argv[1]);
+               return EXIT_FAILURE;
+       }
+
+       /* Now run the file */
+       /* XXX argv and argc are broken; need to save old global_argv
+        * (pointer only is OK!) on this stack frame,
+        * set global_argv=child->argv+1, recurse, and restore. */
+       mark_open(fileno(input));
+       status = parse_file_outer(input);
+       mark_closed(fileno(input));
+       fclose(input);
+       return (status);
+}
+
+static int builtin_umask(struct child_prog *child)
+{
+       mode_t new_umask;
+       const char *arg = child->argv[1];
+       char *end;
+       if (arg) {
+               new_umask=strtoul(arg, &end, 8);
+               if (*end!='\0' || end == arg) {
+                       return EXIT_FAILURE;
+               }
+       } else {
+               printf("%.3o\n", (unsigned int) (new_umask=umask(0)));
+       }
+       umask(new_umask);
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'unset VAR' handler */
+static int builtin_unset(struct child_prog *child)
+{
+       /* bash returned already true */
+       unset_local_var(child->argv[1]);
+       return EXIT_SUCCESS;
+}
+
+static int builtin_not_written(struct child_prog *child)
+{
+       printf("builtin_%s not written\n",child->argv[0]);
+       return EXIT_FAILURE;
+}
+
+static int b_check_space(o_string *o, int len)
+{
+       /* It would be easy to drop a more restrictive policy
+        * in here, such as setting a maximum string length */
+       if (o->length + len > o->maxlen) {
+               char *old_data = o->data;
+               /* assert (data == NULL || o->maxlen != 0); */
+               o->maxlen += max(2*len, B_CHUNK);
+               o->data = realloc(o->data, 1 + o->maxlen);
+               if (o->data == NULL) {
+                       free(old_data);
+               }
+       }
+       return o->data == NULL;
+}
+
+static int b_addchr(o_string *o, int ch)
+{
+       debug_printf("b_addchr: %c %d %p\n", ch, o->length, o);
+       if (b_check_space(o, 1)) return B_NOSPAC;
+       o->data[o->length] = ch;
+       o->length++;
+       o->data[o->length] = '\0';
+       return 0;
+}
+
+static void b_reset(o_string *o)
+{
+       o->length = 0;
+       o->nonnull = 0;
+       if (o->data != NULL) *o->data = '\0';
+}
+
+static void b_free(o_string *o)
+{
+       b_reset(o);
+       free(o->data);
+       o->data = NULL;
+       o->maxlen = 0;
+}
+
+/* My analysis of quoting semantics tells me that state information
+ * is associated with a destination, not a source.
+ */
+static int b_addqchr(o_string *o, int ch, int quote)
+{
+       if (quote && strchr("*?[\\",ch)) {
+               int rc;
+               rc = b_addchr(o, '\\');
+               if (rc) return rc;
+       }
+       return b_addchr(o, ch);
+}
+
+/* belongs in utility.c */
+char *simple_itoa(unsigned int i)
+{
+       /* 21 digits plus null terminator, good for 64-bit or smaller ints */
+       static char local[22];
+       char *p = &local[21];
+       *p-- = '\0';
+       do {
+               *p-- = '0' + i % 10;
+               i /= 10;
+       } while (i > 0);
+       return p + 1;
+}
+
+static int b_adduint(o_string *o, unsigned int i)
+{
+       int r;
+       char *p = simple_itoa(i);
+       /* no escape checking necessary */
+       do r=b_addchr(o, *p++); while (r==0 && *p);
+       return r;
+}
+
+static int static_get(struct in_str *i)
+{
+       int ch=*i->p++;
+       if (ch=='\0') return EOF;
+       return ch;
+}
+
+static int static_peek(struct in_str *i)
+{
+       return *i->p;
+}
+
+static inline void cmdedit_set_initial_prompt(void)
+{
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+       PS1 = NULL;
+#else
+       PS1 = getenv("PS1");
+       if(PS1==0)
+               PS1 = "\\w \\$ ";
+#endif
+}
+
+static inline void setup_prompt_string(int promptmode, char **prompt_str)
+{
+       debug_printf("setup_prompt_string %d ",promptmode);
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+       /* Set up the prompt */
+       if (promptmode == 1) {
+               free(PS1);
+               PS1=xmalloc(strlen(cwd)+4);
+               sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ?  "$ ":"# ");
+               *prompt_str = PS1;
+       } else {
+               *prompt_str = PS2;
+       }
+#else
+       *prompt_str = (promptmode==1)? PS1 : PS2;
+#endif
+       debug_printf("result %s\n",*prompt_str);
+}
+
+static void get_user_input(struct in_str *i)
+{
+       char *prompt_str;
+       static char the_command[BUFSIZ];
+
+       setup_prompt_string(i->promptmode, &prompt_str);
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+       /*
+        ** enable command line editing only while a command line
+        ** is actually being read; otherwise, we'll end up bequeathing
+        ** atexit() handlers and other unwanted stuff to our
+        ** child processes (rob@sysgo.de)
+        */
+       cmdedit_read_input(prompt_str, the_command);
+#else
+       fputs(prompt_str, stdout);
+       fflush(stdout);
+       the_command[0]=fgetc(i->file);
+       the_command[1]='\0';
+#endif
+       fflush(stdout);
+       i->p = the_command;
+}
+
+/* This is the magic location that prints prompts
+ * and gets data back from the user */
+static int file_get(struct in_str *i)
+{
+       int ch;
+
+       ch = 0;
+       /* If there is data waiting, eat it up */
+       if (i->p && *i->p) {
+               ch=*i->p++;
+       } else {
+               /* need to double check i->file because we might be doing something
+                * more complicated by now, like sourcing or substituting. */
+               if (i->__promptme && interactive && i->file == stdin) {
+                       while(! i->p || (interactive && strlen(i->p)==0) ) {
+                               get_user_input(i);
+                       }
+                       i->promptmode=2;
+                       i->__promptme = 0;
+                       if (i->p && *i->p) {
+                               ch=*i->p++;
+                       }
+               } else {
+                       ch = fgetc(i->file);
+               }
+
+               debug_printf("b_getch: got a %d\n", ch);
+       }
+       if (ch == '\n') i->__promptme=1;
+       return ch;
+}
+
+/* All the callers guarantee this routine will never be
+ * used right after a newline, so prompting is not needed.
+ */
+static int file_peek(struct in_str *i)
+{
+       if (i->p && *i->p) {
+               return *i->p;
+       } else {
+               i->peek_buf[0] = fgetc(i->file);
+               i->peek_buf[1] = '\0';
+               i->p = i->peek_buf;
+               debug_printf("b_peek: got a %d\n", *i->p);
+               return *i->p;
+       }
+}
+
+static void setup_file_in_str(struct in_str *i, FILE *f)
+{
+       i->peek = file_peek;
+       i->get = file_get;
+       i->__promptme=1;
+       i->promptmode=1;
+       i->file = f;
+       i->p = NULL;
+}
+
+static void setup_string_in_str(struct in_str *i, const char *s)
+{
+       i->peek = static_peek;
+       i->get = static_get;
+       i->__promptme=1;
+       i->promptmode=1;
+       i->p = s;
+}
+
+static void mark_open(int fd)
+{
+       struct close_me *new = xmalloc(sizeof(struct close_me));
+       new->fd = fd;
+       new->next = close_me_head;
+       close_me_head = new;
+}
+
+static void mark_closed(int fd)
+{
+       struct close_me *tmp;
+       if (close_me_head == NULL || close_me_head->fd != fd)
+               bb_error_msg_and_die("corrupt close_me");
+       tmp = close_me_head;
+       close_me_head = close_me_head->next;
+       free(tmp);
+}
+
+static void close_all(void)
+{
+       struct close_me *c;
+       for (c=close_me_head; c; c=c->next) {
+               close(c->fd);
+       }
+       close_me_head = NULL;
+}
+
+/* squirrel != NULL means we squirrel away copies of stdin, stdout,
+ * and stderr if they are redirected. */
+static int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+       int openfd, mode;
+       struct redir_struct *redir;
+
+       for (redir=prog->redirects; redir; redir=redir->next) {
+               if (redir->dup == -1 && redir->word.gl_pathv == NULL) {
+                       /* something went wrong in the parse.  Pretend it didn't happen */
+                       continue;
+               }
+               if (redir->dup == -1) {
+                       mode=redir_table[redir->type].mode;
+                       openfd = open(redir->word.gl_pathv[0], mode, 0666);
+                       if (openfd < 0) {
+                       /* this could get lost if stderr has been redirected, but
+                          bash and ash both lose it as well (though zsh doesn't!) */
+                               bb_perror_msg("error opening %s", redir->word.gl_pathv[0]);
+                               return 1;
+                       }
+               } else {
+                       openfd = redir->dup;
+               }
+
+               if (openfd != redir->fd) {
+                       if (squirrel && redir->fd < 3) {
+                               squirrel[redir->fd] = dup(redir->fd);
+                       }
+                       if (openfd == -3) {
+                               close(openfd);
+                       } else {
+                               dup2(openfd, redir->fd);
+                               if (redir->dup == -1)
+                                       close (openfd);
+                       }
+               }
+       }
+       return 0;
+}
+
+static void restore_redirects(int squirrel[])
+{
+       int i, fd;
+       for (i=0; i<3; i++) {
+               fd = squirrel[i];
+               if (fd != -1) {
+                       /* No error checking.  I sure wouldn't know what
+                        * to do with an error if I found one! */
+                       dup2(fd, i);
+                       close(fd);
+               }
+       }
+}
+
+/* never returns */
+/* XXX no exit() here.  If you don't exec, use _exit instead.
+ * The at_exit handlers apparently confuse the calling process,
+ * in particular stdin handling.  Not sure why? */
+static void pseudo_exec(struct child_prog *child)
+{
+       int i, rcode;
+       char *p;
+       struct built_in_command *x;
+       if (child->argv) {
+               for (i=0; is_assignment(child->argv[i]); i++) {
+                       debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]);
+                       p = insert_var_value(child->argv[i]);
+                       putenv(strdup(p));
+                       if (p != child->argv[i]) free(p);
+               }
+               child->argv+=i;  /* XXX this hack isn't so horrible, since we are about
+                                       to exit, and therefore don't need to keep data
+                                       structures consistent for free() use. */
+               /* If a variable is assigned in a forest, and nobody listens,
+                * was it ever really set?
+                */
+               if (child->argv[0] == NULL) {
+                       _exit(EXIT_SUCCESS);
+               }
+
+               /*
+                * Check if the command matches any of the builtins.
+                * Depending on context, this might be redundant.  But it's
+                * easier to waste a few CPU cycles than it is to figure out
+                * if this is one of those cases.
+                */
+               for (x = bltins; x->cmd; x++) {
+                       if (strcmp(child->argv[0], x->cmd) == 0 ) {
+                               debug_printf("builtin exec %s\n", child->argv[0]);
+                               rcode = x->function(child);
+                               fflush(stdout);
+                               _exit(rcode);
+                       }
+               }
+
+               /* Check if the command matches any busybox internal commands
+                * ("applets") here.
+                * FIXME: This feature is not 100% safe, since
+                * BusyBox is not fully reentrant, so we have no guarantee the things
+                * from the .bss are still zeroed, or that things from .data are still
+                * at their defaults.  We could exec ourself from /proc/self/exe, but I
+                * really dislike relying on /proc for things.  We could exec ourself
+                * from global_argv[0], but if we are in a chroot, we may not be able
+                * to find ourself... */
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+               {
+                       int argc_l;
+                       char** argv_l=child->argv;
+                       char *name = child->argv[0];
+
+                       /* Count argc for use in a second... */
+                       for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
+                       optind = 1;
+                       debug_printf("running applet %s\n", name);
+                       run_applet_by_name(name, argc_l, child->argv);
+               }
+#endif
+               debug_printf("exec of %s\n",child->argv[0]);
+               execvp(child->argv[0],child->argv);
+               bb_perror_msg("couldn't exec: %s",child->argv[0]);
+               _exit(1);
+       } else if (child->group) {
+               debug_printf("runtime nesting to group\n");
+               interactive=0;    /* crucial!!!! */
+               rcode = run_list_real(child->group);
+               /* OK to leak memory by not calling free_pipe_list,
+                * since this process is about to exit */
+               _exit(rcode);
+       } else {
+               /* Can happen.  See what bash does with ">foo" by itself. */
+               debug_printf("trying to pseudo_exec null command\n");
+               _exit(EXIT_SUCCESS);
+       }
+}
+
+static void insert_bg_job(struct pipe *pi)
+{
+       struct pipe *thejob;
+
+       /* Linear search for the ID of the job to use */
+       pi->jobid = 1;
+       for (thejob = job_list; thejob; thejob = thejob->next)
+               if (thejob->jobid >= pi->jobid)
+                       pi->jobid = thejob->jobid + 1;
+
+       /* add thejob to the list of running jobs */
+       if (!job_list) {
+               thejob = job_list = xmalloc(sizeof(*thejob));
+       } else {
+               for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */;
+               thejob->next = xmalloc(sizeof(*thejob));
+               thejob = thejob->next;
+       }
+
+       /* physically copy the struct job */
+       memcpy(thejob, pi, sizeof(struct pipe));
+       thejob->next = NULL;
+       thejob->running_progs = thejob->num_progs;
+       thejob->stopped_progs = 0;
+       thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */
+
+       //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0])
+       {
+               char *bar=thejob->text;
+               char **foo=pi->progs[0].argv;
+               while(foo && *foo) {
+                       bar += sprintf(bar, "%s ", *foo++);
+               }
+       }
+
+       /* we don't wait for background thejobs to return -- append it
+          to the list of backgrounded thejobs and leave it alone */
+       printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
+       last_bg_pid = thejob->progs[0].pid;
+       last_jobid = thejob->jobid;
+}
+
+/* remove a backgrounded job */
+static void remove_bg_job(struct pipe *pi)
+{
+       struct pipe *prev_pipe;
+
+       if (pi == job_list) {
+               job_list = pi->next;
+       } else {
+               prev_pipe = job_list;
+               while (prev_pipe->next != pi)
+                       prev_pipe = prev_pipe->next;
+               prev_pipe->next = pi->next;
+       }
+       if (job_list)
+               last_jobid = job_list->jobid;
+       else
+               last_jobid = 0;
+
+       pi->stopped_progs = 0;
+       free_pipe(pi, 0);
+       free(pi);
+}
+
+/* Checks to see if any processes have exited -- if they
+   have, figure out why and see if a job has completed */
+static int checkjobs(struct pipe* fg_pipe)
+{
+       int attributes;
+       int status;
+       int prognum = 0;
+       struct pipe *pi;
+       pid_t childpid;
+
+       attributes = WUNTRACED;
+       if (fg_pipe==NULL) {
+               attributes |= WNOHANG;
+       }
+
+       while ((childpid = waitpid(-1, &status, attributes)) > 0) {
+               if (fg_pipe) {
+                       int i, rcode = 0;
+                       for (i=0; i < fg_pipe->num_progs; i++) {
+                               if (fg_pipe->progs[i].pid == childpid) {
+                                       if (i==fg_pipe->num_progs-1)
+                                               rcode=WEXITSTATUS(status);
+                                       (fg_pipe->num_progs)--;
+                                       return(rcode);
+                               }
+                       }
+               }
+
+               for (pi = job_list; pi; pi = pi->next) {
+                       prognum = 0;
+                       while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) {
+                               prognum++;
+                       }
+                       if (prognum < pi->num_progs)
+                               break;
+               }
+
+               if(pi==NULL) {
+                       debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
+                       continue;
+               }
+
+               if (WIFEXITED(status) || WIFSIGNALED(status)) {
+                       /* child exited */
+                       pi->running_progs--;
+                       pi->progs[prognum].pid = 0;
+
+                       if (!pi->running_progs) {
+                               printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text);
+                               remove_bg_job(pi);
+                       }
+               } else {
+                       /* child stopped */
+                       pi->stopped_progs++;
+                       pi->progs[prognum].is_stopped = 1;
+
+#if 0
+                       /* Printing this stuff is a pain, since it tends to
+                        * overwrite the prompt an inconveinient moments.  So
+                        * don't do that.  */
+                       if (pi->stopped_progs == pi->num_progs) {
+                               printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text);
+                       }
+#endif
+               }
+       }
+
+       if (childpid == -1 && errno != ECHILD)
+               bb_perror_msg("waitpid");
+
+       /* move the shell to the foreground */
+       //if (interactive && tcsetpgrp(shell_terminal, getpgid(0)))
+       //      bb_perror_msg("tcsetpgrp-2");
+       return -1;
+}
+
+/* Figure out our controlling tty, checking in order stderr,
+ * stdin, and stdout.  If check_pgrp is set, also check that
+ * we belong to the foreground process group associated with
+ * that tty.  The value of shell_terminal is needed in order to call
+ * tcsetpgrp(shell_terminal, ...); */
+void controlling_tty(int check_pgrp)
+{
+       pid_t curpgrp;
+
+       if ((curpgrp = tcgetpgrp(shell_terminal = 2)) < 0
+                       && (curpgrp = tcgetpgrp(shell_terminal = 0)) < 0
+                       && (curpgrp = tcgetpgrp(shell_terminal = 1)) < 0)
+               goto shell_terminal_error;
+
+       if (check_pgrp && curpgrp != getpgid(0))
+               goto shell_terminal_error;
+
+       return;
+
+shell_terminal_error:
+               shell_terminal = -1;
+               return;
+}
+
+/* run_pipe_real() starts all the jobs, but doesn't wait for anything
+ * to finish.  See checkjobs().
+ *
+ * return code is normally -1, when the caller has to wait for children
+ * to finish to determine the exit status of the pipe.  If the pipe
+ * is a simple builtin command, however, the action is done by the
+ * time run_pipe_real returns, and the exit code is provided as the
+ * return value.
+ *
+ * The input of the pipe is always stdin, the output is always
+ * stdout.  The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
+ * because it tries to avoid running the command substitution in
+ * subshell, when that is in fact necessary.  The subshell process
+ * now has its stdout directed to the input of the appropriate pipe,
+ * so this routine is noticeably simpler.
+ */
+static int run_pipe_real(struct pipe *pi)
+{
+       int i;
+       int nextin, nextout;
+       int pipefds[2];                         /* pipefds[0] is for reading */
+       struct child_prog *child;
+       struct built_in_command *x;
+       char *p;
+
+       nextin = 0;
+       pi->pgrp = -1;
+
+       /* Check if this is a simple builtin (not part of a pipe).
+        * Builtins within pipes have to fork anyway, and are handled in
+        * pseudo_exec.  "echo foo | read bar" doesn't work on bash, either.
+        */
+       if (pi->num_progs == 1) child = & (pi->progs[0]);
+       if (pi->num_progs == 1 && child->group && child->subshell == 0) {
+               int squirrel[] = {-1, -1, -1};
+               int rcode;
+               debug_printf("non-subshell grouping\n");
+               setup_redirects(child, squirrel);
+               /* XXX could we merge code with following builtin case,
+                * by creating a pseudo builtin that calls run_list_real? */
+               rcode = run_list_real(child->group);
+               restore_redirects(squirrel);
+               return rcode;
+       } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) {
+               for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
+               if (i!=0 && child->argv[i]==NULL) {
+                       /* assignments, but no command: set the local environment */
+                       for (i=0; child->argv[i]!=NULL; i++) {
+
+                               /* Ok, this case is tricky.  We have to decide if this is a
+                                * local variable, or an already exported variable.  If it is
+                                * already exported, we have to export the new value.  If it is
+                                * not exported, we need only set this as a local variable.
+                                * This junk is all to decide whether or not to export this
+                                * variable. */
+                               int export_me=0;
+                               char *name, *value;
+                               name = bb_xstrdup(child->argv[i]);
+                               debug_printf("Local environment set: %s\n", name);
+                               value = strchr(name, '=');
+                               if (value)
+                                       *value=0;
+                               if ( get_local_var(name)) {
+                                       export_me=1;
+                               }
+                               free(name);
+                               p = insert_var_value(child->argv[i]);
+                               set_local_var(p, export_me);
+                               if (p != child->argv[i]) free(p);
+                       }
+                       return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */
+               }
+               for (i = 0; is_assignment(child->argv[i]); i++) {
+                       p = insert_var_value(child->argv[i]);
+                       putenv(strdup(p));
+                       if (p != child->argv[i]) {
+                               child->sp--;
+                               free(p);
+                       }
+               }
+               if (child->sp) {
+                       char * str = NULL;
+
+                       str = make_string((child->argv + i));
+                       parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
+                       free(str);
+                       return last_return_code;
+               }
+               for (x = bltins; x->cmd; x++) {
+                       if (strcmp(child->argv[i], x->cmd) == 0 ) {
+                               int squirrel[] = {-1, -1, -1};
+                               int rcode;
+                               if (x->function == builtin_exec && child->argv[i+1]==NULL) {
+                                       debug_printf("magic exec\n");
+                                       setup_redirects(child,NULL);
+                                       return EXIT_SUCCESS;
+                               }
+                               debug_printf("builtin inline %s\n", child->argv[0]);
+                               /* XXX setup_redirects acts on file descriptors, not FILEs.
+                                * This is perfect for work that comes after exec().
+                                * Is it really safe for inline use?  Experimentally,
+                                * things seem to work with glibc. */
+                               setup_redirects(child, squirrel);
+                               child->argv+=i;  /* XXX horrible hack */
+                               rcode = x->function(child);
+                               child->argv-=i;  /* XXX restore hack so free() can work right */
+                               restore_redirects(squirrel);
+                               return rcode;
+                       }
+               }
+       }
+
+       for (i = 0; i < pi->num_progs; i++) {
+               child = & (pi->progs[i]);
+
+               /* pipes are inserted between pairs of commands */
+               if ((i + 1) < pi->num_progs) {
+                       if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe");
+                       nextout = pipefds[1];
+               } else {
+                       nextout=1;
+                       pipefds[0] = -1;
+               }
+
+               /* XXX test for failed fork()? */
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+               if (!(child->pid = fork()))
+#else
+               if (!(child->pid = vfork()))
+#endif
+               {
+                       /* Set the handling for job control signals back to the default.  */
+                       signal(SIGINT, SIG_DFL);
+                       signal(SIGQUIT, SIG_DFL);
+                       signal(SIGTERM, SIG_DFL);
+                       signal(SIGTSTP, SIG_DFL);
+                       signal(SIGTTIN, SIG_DFL);
+                       signal(SIGTTOU, SIG_DFL);
+                       signal(SIGCHLD, SIG_DFL);
+
+                       close_all();
+
+                       if (nextin != 0) {
+                               dup2(nextin, 0);
+                               close(nextin);
+                       }
+                       if (nextout != 1) {
+                               dup2(nextout, 1);
+                               close(nextout);
+                       }
+                       if (pipefds[0]!=-1) {
+                               close(pipefds[0]);  /* opposite end of our output pipe */
+                       }
+
+                       /* Like bash, explicit redirects override pipes,
+                        * and the pipe fd is available for dup'ing. */
+                       setup_redirects(child,NULL);
+
+                       if (interactive && pi->followup!=PIPE_BG) {
+                               /* If we (the child) win the race, put ourselves in the process
+                                * group whose leader is the first process in this pipe. */
+                               if (pi->pgrp < 0) {
+                                       pi->pgrp = getpid();
+                               }
+                               if (setpgid(0, pi->pgrp) == 0) {
+                                       tcsetpgrp(2, pi->pgrp);
+                               }
+                       }
+
+                       pseudo_exec(child);
+               }
+
+
+               /* put our child in the process group whose leader is the
+                  first process in this pipe */
+               if (pi->pgrp < 0) {
+                       pi->pgrp = child->pid;
+               }
+               /* Don't check for errors.  The child may be dead already,
+                * in which case setpgid returns error code EACCES. */
+               setpgid(child->pid, pi->pgrp);
+
+               if (nextin != 0)
+                       close(nextin);
+               if (nextout != 1)
+                       close(nextout);
+
+               /* If there isn't another process, nextin is garbage
+                  but it doesn't matter */
+               nextin = pipefds[0];
+       }
+       return -1;
+}
+
+static int run_list_real(struct pipe *pi)
+{
+       char *save_name = NULL;
+       char **list = NULL;
+       char **save_list = NULL;
+       struct pipe *rpipe;
+       int flag_rep = 0;
+       int save_num_progs;
+       int rcode=0, flag_skip=1;
+       int flag_restore = 0;
+       int if_code=0, next_if_code=0;  /* need double-buffer to handle elif */
+       reserved_style rmode, skip_more_in_this_rmode=RES_XXXX;
+       /* check syntax for "for" */
+       for (rpipe = pi; rpipe; rpipe = rpipe->next) {
+               if ((rpipe->r_mode == RES_IN ||
+                   rpipe->r_mode == RES_FOR) &&
+                   (rpipe->next == NULL)) {
+                               syntax();
+                               return 1;
+               }
+               if ((rpipe->r_mode == RES_IN &&
+                       (rpipe->next->r_mode == RES_IN &&
+                       rpipe->next->progs->argv != NULL))||
+                       (rpipe->r_mode == RES_FOR &&
+                       rpipe->next->r_mode != RES_IN)) {
+                               syntax();
+                               return 1;
+               }
+       }
+       for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
+               if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL ||
+                       pi->r_mode == RES_FOR) {
+                               flag_restore = 0;
+                               if (!rpipe) {
+                                       flag_rep = 0;
+                                       rpipe = pi;
+                               }
+               }
+               rmode = pi->r_mode;
+               debug_printf("rmode=%d  if_code=%d  next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode);
+               if (rmode == skip_more_in_this_rmode && flag_skip) {
+                       if (pi->followup == PIPE_SEQ) flag_skip=0;
+                       continue;
+               }
+               flag_skip = 1;
+               skip_more_in_this_rmode = RES_XXXX;
+               if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code;
+               if (rmode == RES_THEN &&  if_code) continue;
+               if (rmode == RES_ELSE && !if_code) continue;
+               if (rmode == RES_ELIF && !if_code) break;
+               if (rmode == RES_FOR && pi->num_progs) {
+                       if (!list) {
+                               /* if no variable values after "in" we skip "for" */
+                               if (!pi->next->progs->argv) continue;
+                               /* create list of variable values */
+                               list = make_list_in(pi->next->progs->argv,
+                                       pi->progs->argv[0]);
+                               save_list = list;
+                               save_name = pi->progs->argv[0];
+                               pi->progs->argv[0] = NULL;
+                               flag_rep = 1;
+                       }
+                       if (!(*list)) {
+                               free(pi->progs->argv[0]);
+                               free(save_list);
+                               list = NULL;
+                               flag_rep = 0;
+                               pi->progs->argv[0] = save_name;
+                               pi->progs->glob_result.gl_pathv[0] =
+                                       pi->progs->argv[0];
+                               continue;
+                       } else {
+                               /* insert new value from list for variable */
+                               if (pi->progs->argv[0])
+                                       free(pi->progs->argv[0]);
+                               pi->progs->argv[0] = *list++;
+                               pi->progs->glob_result.gl_pathv[0] =
+                                       pi->progs->argv[0];
+                       }
+               }
+               if (rmode == RES_IN) continue;
+               if (rmode == RES_DO) {
+                       if (!flag_rep) continue;
+               }       
+               if ((rmode == RES_DONE)) {
+                       if (flag_rep) {
+                               flag_restore = 1;
+                       } else {
+                               rpipe = NULL;
+                       }
+               }
+               if (pi->num_progs == 0) continue;
+               save_num_progs = pi->num_progs; /* save number of programs */
+               rcode = run_pipe_real(pi);
+               debug_printf("run_pipe_real returned %d\n",rcode);
+               if (rcode!=-1) {
+                       /* We only ran a builtin: rcode was set by the return value
+                        * of run_pipe_real(), and we don't need to wait for anything. */
+               } else if (pi->followup==PIPE_BG) {
+                       /* XXX check bash's behavior with nontrivial pipes */
+                       /* XXX compute jobid */
+                       /* XXX what does bash do with attempts to background builtins? */
+                       insert_bg_job(pi);
+                       rcode = EXIT_SUCCESS;
+               } else {
+                       if (interactive) {
+                               /* move the new process group into the foreground */
+                               if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY)
+                                       bb_perror_msg("tcsetpgrp-3");
+                               rcode = checkjobs(pi);
+                               /* move the shell to the foreground */
+                               if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY)
+                                       bb_perror_msg("tcsetpgrp-4");
+                       } else {
+                               rcode = checkjobs(pi);
+                       }
+                       debug_printf("checkjobs returned %d\n",rcode);
+               }
+               last_return_code=rcode;
+               pi->num_progs = save_num_progs; /* restore number of programs */
+               if ( rmode == RES_IF || rmode == RES_ELIF )
+                       next_if_code=rcode;  /* can be overwritten a number of times */
+               if (rmode == RES_WHILE)
+                       flag_rep = !last_return_code;
+               if (rmode == RES_UNTIL)
+                       flag_rep = last_return_code;
+               if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) ||
+                    (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
+                       skip_more_in_this_rmode=rmode;
+               checkjobs(NULL);
+       }
+       return rcode;
+}
+
+/* broken, of course, but OK for testing */
+static char *indenter(int i)
+{
+       static char blanks[]="                                    ";
+       return &blanks[sizeof(blanks)-i-1];
+}
+
+/* return code is the exit status of the pipe */
+static int free_pipe(struct pipe *pi, int indent)
+{
+       char **p;
+       struct child_prog *child;
+       struct redir_struct *r, *rnext;
+       int a, i, ret_code=0;
+       char *ind = indenter(indent);
+
+       if (pi->stopped_progs > 0)
+               return ret_code;
+       final_printf("%s run pipe: (pid %d)\n",ind,getpid());
+       for (i=0; i<pi->num_progs; i++) {
+               child = &pi->progs[i];
+               final_printf("%s  command %d:\n",ind,i);
+               if (child->argv) {
+                       for (a=0,p=child->argv; *p; a++,p++) {
+                               final_printf("%s   argv[%d] = %s\n",ind,a,*p);
+                       }
+                       globfree(&child->glob_result);
+                       child->argv=NULL;
+               } else if (child->group) {
+                       final_printf("%s   begin group (subshell:%d)\n",ind, child->subshell);
+                       ret_code = free_pipe_list(child->group,indent+3);
+                       final_printf("%s   end group\n",ind);
+               } else {
+                       final_printf("%s   (nil)\n",ind);
+               }
+               for (r=child->redirects; r; r=rnext) {
+                       final_printf("%s   redirect %d%s", ind, r->fd, redir_table[r->type].descrip);
+                       if (r->dup == -1) {
+                               /* guard against the case >$FOO, where foo is unset or blank */
+                               if (r->word.gl_pathv) {
+                                       final_printf(" %s\n", *r->word.gl_pathv);
+                                       globfree(&r->word);
+                               }
+                       } else {
+                               final_printf("&%d\n", r->dup);
+                       }
+                       rnext=r->next;
+                       free(r);
+               }
+               child->redirects=NULL;
+       }
+       free(pi->progs);   /* children are an array, they get freed all at once */
+       pi->progs=NULL;
+       return ret_code;
+}
+
+static int free_pipe_list(struct pipe *head, int indent)
+{
+       int rcode=0;   /* if list has no members */
+       struct pipe *pi, *next;
+       char *ind = indenter(indent);
+       for (pi=head; pi; pi=next) {
+               final_printf("%s pipe reserved mode %d\n", ind, pi->r_mode);
+               rcode = free_pipe(pi, indent);
+               final_printf("%s pipe followup code %d\n", ind, pi->followup);
+               next=pi->next;
+               pi->next=NULL;
+               free(pi);
+       }
+       return rcode;
+}
+
+/* Select which version we will use */
+static int run_list(struct pipe *pi)
+{
+       int rcode=0;
+       if (fake_mode==0) {
+               rcode = run_list_real(pi);
+       }
+       /* free_pipe_list has the side effect of clearing memory
+        * In the long run that function can be merged with run_list_real,
+        * but doing that now would hobble the debugging effort. */
+       free_pipe_list(pi,0);
+       return rcode;
+}
+
+/* The API for glob is arguably broken.  This routine pushes a non-matching
+ * string into the output structure, removing non-backslashed backslashes.
+ * If someone can prove me wrong, by performing this function within the
+ * original glob(3) api, feel free to rewrite this routine into oblivion.
+ * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
+ * XXX broken if the last character is '\\', check that before calling.
+ */
+static int globhack(const char *src, int flags, glob_t *pglob)
+{
+       int cnt=0, pathc;
+       const char *s;
+       char *dest;
+       for (cnt=1, s=src; s && *s; s++) {
+               if (*s == '\\') s++;
+               cnt++;
+       }
+       dest = malloc(cnt);
+       if (!dest) return GLOB_NOSPACE;
+       if (!(flags & GLOB_APPEND)) {
+               pglob->gl_pathv=NULL;
+               pglob->gl_pathc=0;
+               pglob->gl_offs=0;
+               pglob->gl_offs=0;
+       }
+       pathc = ++pglob->gl_pathc;
+       pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv));
+       if (pglob->gl_pathv == NULL) return GLOB_NOSPACE;
+       pglob->gl_pathv[pathc-1]=dest;
+       pglob->gl_pathv[pathc]=NULL;
+       for (s=src; s && *s; s++, dest++) {
+               if (*s == '\\') s++;
+               *dest = *s;
+       }
+       *dest='\0';
+       return 0;
+}
+
+/* XXX broken if the last character is '\\', check that before calling */
+static int glob_needed(const char *s)
+{
+       for (; *s; s++) {
+               if (*s == '\\') s++;
+               if (strchr("*[?",*s)) return 1;
+       }
+       return 0;
+}
+
+#if 0
+static void globprint(glob_t *pglob)
+{
+       int i;
+       debug_printf("glob_t at %p:\n", pglob);
+       debug_printf("  gl_pathc=%d  gl_pathv=%p  gl_offs=%d  gl_flags=%d\n",
+               pglob->gl_pathc, pglob->gl_pathv, pglob->gl_offs, pglob->gl_flags);
+       for (i=0; i<pglob->gl_pathc; i++)
+               debug_printf("pglob->gl_pathv[%d] = %p = %s\n", i,
+                       pglob->gl_pathv[i], pglob->gl_pathv[i]);
+}
+#endif
+
+static int xglob(o_string *dest, int flags, glob_t *pglob)
+{
+       int gr;
+
+       /* short-circuit for null word */
+       /* we can code this better when the debug_printf's are gone */
+       if (dest->length == 0) {
+               if (dest->nonnull) {
+                       /* bash man page calls this an "explicit" null */
+                       gr = globhack(dest->data, flags, pglob);
+                       debug_printf("globhack returned %d\n",gr);
+               } else {
+                       return 0;
+               }
+       } else if (glob_needed(dest->data)) {
+               gr = glob(dest->data, flags, NULL, pglob);
+               debug_printf("glob returned %d\n",gr);
+               if (gr == GLOB_NOMATCH) {
+                       /* quote removal, or more accurately, backslash removal */
+                       gr = globhack(dest->data, flags, pglob);
+                       debug_printf("globhack returned %d\n",gr);
+               }
+       } else {
+               gr = globhack(dest->data, flags, pglob);
+               debug_printf("globhack returned %d\n",gr);
+       }
+       if (gr == GLOB_NOSPACE)
+               bb_error_msg_and_die("out of memory during glob");
+       if (gr != 0) { /* GLOB_ABORTED ? */
+               bb_error_msg("glob(3) error %d",gr);
+       }
+       /* globprint(glob_target); */
+       return gr;
+}
+
+/* This is used to get/check local shell variables */
+static char *get_local_var(const char *s)
+{
+       struct variables *cur;
+
+       if (!s)
+               return NULL;
+       for (cur = top_vars; cur; cur=cur->next)
+               if(strcmp(cur->name, s)==0)
+                       return cur->value;
+       return NULL;
+}
+
+/* This is used to set local shell variables
+   flg_export==0 if only local (not exporting) variable
+   flg_export==1 if "new" exporting environ
+   flg_export>1  if current startup environ (not call putenv()) */
+static int set_local_var(const char *s, int flg_export)
+{
+       char *name, *value;
+       int result=0;
+       struct variables *cur;
+
+       name=strdup(s);
+
+       /* Assume when we enter this function that we are already in
+        * NAME=VALUE format.  So the first order of business is to
+        * split 's' on the '=' into 'name' and 'value' */
+       value = strchr(name, '=');
+       if (value==0 && ++value==0) {
+               free(name);
+               return -1;
+       }
+       *value++ = 0;
+
+       for(cur = top_vars; cur; cur = cur->next) {
+               if(strcmp(cur->name, name)==0)
+                       break;
+       }
+
+       if(cur) {
+               if(strcmp(cur->value, value)==0) {
+                       if(flg_export>0 && cur->flg_export==0)
+                               cur->flg_export=flg_export;
+                       else
+                               result++;
+               } else {
+                       if(cur->flg_read_only) {
+                               bb_error_msg("%s: readonly variable", name);
+                               result = -1;
+                       } else {
+                               if(flg_export>0 || cur->flg_export>1)
+                                       cur->flg_export=1;
+                               free(cur->value);
+
+                               cur->value = strdup(value);
+                       }
+               }
+       } else {
+               cur = malloc(sizeof(struct variables));
+               if(!cur) {
+                       result = -1;
+               } else {
+                       cur->name = strdup(name);
+                       if(cur->name == 0) {
+                               free(cur);
+                               result = -1;
+                       } else {
+                               struct variables *bottom = top_vars;
+                               cur->value = strdup(value);
+                               cur->next = 0;
+                               cur->flg_export = flg_export;
+                               cur->flg_read_only = 0;
+                               while(bottom->next) bottom=bottom->next;
+                               bottom->next = cur;
+                       }
+               }
+       }
+
+       if(result==0 && cur->flg_export==1) {
+               *(value-1) = '=';
+               result = putenv(name);
+       } else {
+               free(name);
+               if(result>0)            /* equivalent to previous set */
+                       result = 0;
+       }
+       return result;
+}
+
+static void unset_local_var(const char *name)
+{
+       struct variables *cur;
+
+       if (name) {
+               for (cur = top_vars; cur; cur=cur->next) {
+                       if(strcmp(cur->name, name)==0)
+                               break;
+               }
+               if(cur!=0) {
+                       struct variables *next = top_vars;
+                       if(cur->flg_read_only) {
+                               bb_error_msg("%s: readonly variable", name);
+                               return;
+                       } else {
+                               if(cur->flg_export)
+                                       unsetenv(cur->name);
+                               free(cur->name);
+                               free(cur->value);
+                               while (next->next != cur)
+                                       next = next->next;
+                               next->next = cur->next;
+                       }
+                       free(cur);
+               }
+       }
+}
+
+static int is_assignment(const char *s)
+{
+       if (s==NULL || !isalpha(*s)) return 0;
+       ++s;
+       while(isalnum(*s) || *s=='_') ++s;
+       return *s=='=';
+}
+
+/* the src parameter allows us to peek forward to a possible &n syntax
+ * for file descriptor duplication, e.g., "2>&1".
+ * Return code is 0 normally, 1 if a syntax error is detected in src.
+ * Resource errors (in xmalloc) cause the process to exit */
+static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
+       struct in_str *input)
+{
+       struct child_prog *child=ctx->child;
+       struct redir_struct *redir = child->redirects;
+       struct redir_struct *last_redir=NULL;
+
+       /* Create a new redir_struct and drop it onto the end of the linked list */
+       while(redir) {
+               last_redir=redir;
+               redir=redir->next;
+       }
+       redir = xmalloc(sizeof(struct redir_struct));
+       redir->next=NULL;
+       redir->word.gl_pathv=NULL;
+       if (last_redir) {
+               last_redir->next=redir;
+       } else {
+               child->redirects=redir;
+       }
+
+       redir->type=style;
+       redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ;
+
+       debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip);
+
+       /* Check for a '2>&1' type redirect */
+       redir->dup = redirect_dup_num(input);
+       if (redir->dup == -2) return 1;  /* syntax error */
+       if (redir->dup != -1) {
+               /* Erik had a check here that the file descriptor in question
+                * is legit; I postpone that to "run time"
+                * A "-" representation of "close me" shows up as a -3 here */
+               debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
+       } else {
+               /* We do _not_ try to open the file that src points to,
+                * since we need to return and let src be expanded first.
+                * Set ctx->pending_redirect, so we know what to do at the
+                * end of the next parsed word.
+                */
+               ctx->pending_redirect = redir;
+       }
+       return 0;
+}
+
+struct pipe *new_pipe(void) {
+       struct pipe *pi;
+       pi = xmalloc(sizeof(struct pipe));
+       pi->num_progs = 0;
+       pi->progs = NULL;
+       pi->next = NULL;
+       pi->followup = 0;  /* invalid */
+       return pi;
+}
+
+static void initialize_context(struct p_context *ctx)
+{
+       ctx->pipe=NULL;
+       ctx->pending_redirect=NULL;
+       ctx->child=NULL;
+       ctx->list_head=new_pipe();
+       ctx->pipe=ctx->list_head;
+       ctx->w=RES_NONE;
+       ctx->stack=NULL;
+       ctx->old_flag=0;
+       done_command(ctx);   /* creates the memory for working child */
+}
+
+/* normal return is 0
+ * if a reserved word is found, and processed, return 1
+ * should handle if, then, elif, else, fi, for, while, until, do, done.
+ * case, function, and select are obnoxious, save those for later.
+ */
+int reserved_word(o_string *dest, struct p_context *ctx)
+{
+       struct reserved_combo {
+               char *literal;
+               int code;
+               long flag;
+       };
+       /* Mostly a list of accepted follow-up reserved words.
+        * FLAG_END means we are done with the sequence, and are ready
+        * to turn the compound list into a command.
+        * FLAG_START means the word must start a new compound list.
+        */
+       static struct reserved_combo reserved_list[] = {
+               { "if",    RES_IF,    FLAG_THEN | FLAG_START },
+               { "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
+               { "elif",  RES_ELIF,  FLAG_THEN },
+               { "else",  RES_ELSE,  FLAG_FI   },
+               { "fi",    RES_FI,    FLAG_END  },
+               { "for",   RES_FOR,   FLAG_IN   | FLAG_START },
+               { "while", RES_WHILE, FLAG_DO   | FLAG_START },
+               { "until", RES_UNTIL, FLAG_DO   | FLAG_START },
+               { "in",    RES_IN,    FLAG_DO   },
+               { "do",    RES_DO,    FLAG_DONE },
+               { "done",  RES_DONE,  FLAG_END  }
+       };
+       struct reserved_combo *r;
+       for (r=reserved_list;
+#define NRES sizeof(reserved_list)/sizeof(struct reserved_combo)
+               r<reserved_list+NRES; r++) {
+               if (strcmp(dest->data, r->literal) == 0) {
+                       debug_printf("found reserved word %s, code %d\n",r->literal,r->code);
+                       if (r->flag & FLAG_START) {
+                               struct p_context *new = xmalloc(sizeof(struct p_context));
+                               debug_printf("push stack\n");
+                               if (ctx->w == RES_IN || ctx->w == RES_FOR) {
+                                       syntax();
+                                       free(new);
+                                       ctx->w = RES_SNTX;
+                                       b_reset(dest);
+                                       return 1;
+                               }
+                               *new = *ctx;   /* physical copy */
+                               initialize_context(ctx);
+                               ctx->stack=new;
+                       } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) {
+                               syntax();
+                               ctx->w = RES_SNTX;
+                               b_reset(dest);
+                               return 1;
+                       }
+                       ctx->w=r->code;
+                       ctx->old_flag = r->flag;
+                       if (ctx->old_flag & FLAG_END) {
+                               struct p_context *old;
+                               debug_printf("pop stack\n");
+                               done_pipe(ctx,PIPE_SEQ);
+                               old = ctx->stack;
+                               old->child->group = ctx->list_head;
+                               old->child->subshell = 0;
+                               *ctx = *old;   /* physical copy */
+                               free(old);
+                       }
+                       b_reset (dest);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* normal return is 0.
+ * Syntax or xglob errors return 1. */
+static int done_word(o_string *dest, struct p_context *ctx)
+{
+       struct child_prog *child=ctx->child;
+       glob_t *glob_target;
+       int gr, flags = 0;
+
+       debug_printf("done_word: %s %p\n", dest->data, child);
+       if (dest->length == 0 && !dest->nonnull) {
+               debug_printf("  true null, ignored\n");
+               return 0;
+       }
+       if (ctx->pending_redirect) {
+               glob_target = &ctx->pending_redirect->word;
+       } else {
+               if (child->group) {
+                       syntax();
+                       return 1;  /* syntax error, groups and arglists don't mix */
+               }
+               if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
+                       debug_printf("checking %s for reserved-ness\n",dest->data);
+                       if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX;
+               }
+               glob_target = &child->glob_result;
+               if (child->argv) flags |= GLOB_APPEND;
+       }
+       gr = xglob(dest, flags, glob_target);
+       if (gr != 0) return 1;
+
+       b_reset(dest);
+       if (ctx->pending_redirect) {
+               ctx->pending_redirect=NULL;
+               if (glob_target->gl_pathc != 1) {
+                       bb_error_msg("ambiguous redirect");
+                       return 1;
+               }
+       } else {
+               child->argv = glob_target->gl_pathv;
+       }
+       if (ctx->w == RES_FOR) {
+               done_word(dest,ctx);
+               done_pipe(ctx,PIPE_SEQ);
+       }
+       return 0;
+}
+
+/* The only possible error here is out of memory, in which case
+ * xmalloc exits. */
+static int done_command(struct p_context *ctx)
+{
+       /* The child is really already in the pipe structure, so
+        * advance the pipe counter and make a new, null child.
+        * Only real trickiness here is that the uncommitted
+        * child structure, to which ctx->child points, is not
+        * counted in pi->num_progs. */
+       struct pipe *pi=ctx->pipe;
+       struct child_prog *prog=ctx->child;
+
+       if (prog && prog->group == NULL
+                && prog->argv == NULL
+                && prog->redirects == NULL) {
+               debug_printf("done_command: skipping null command\n");
+               return 0;
+       } else if (prog) {
+               pi->num_progs++;
+               debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs);
+       } else {
+               debug_printf("done_command: initializing\n");
+       }
+       pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
+
+       prog = pi->progs + pi->num_progs;
+       prog->redirects = NULL;
+       prog->argv = NULL;
+       prog->is_stopped = 0;
+       prog->group = NULL;
+       prog->glob_result.gl_pathv = NULL;
+       prog->family = pi;
+       prog->sp = 0;
+       ctx->child = prog;
+       prog->type = ctx->type;
+
+       /* but ctx->pipe and ctx->list_head remain unchanged */
+       return 0;
+}
+
+static int done_pipe(struct p_context *ctx, pipe_style type)
+{
+       struct pipe *new_p;
+       done_command(ctx);  /* implicit closure of previous command */
+       debug_printf("done_pipe, type %d\n", type);
+       ctx->pipe->followup = type;
+       ctx->pipe->r_mode = ctx->w;
+       new_p=new_pipe();
+       ctx->pipe->next = new_p;
+       ctx->pipe = new_p;
+       ctx->child = NULL;
+       done_command(ctx);  /* set up new pipe to accept commands */
+       return 0;
+}
+
+/* peek ahead in the in_str to find out if we have a "&n" construct,
+ * as in "2>&1", that represents duplicating a file descriptor.
+ * returns either -2 (syntax error), -1 (no &), or the number found.
+ */
+static int redirect_dup_num(struct in_str *input)
+{
+       int ch, d=0, ok=0;
+       ch = b_peek(input);
+       if (ch != '&') return -1;
+
+       b_getch(input);  /* get the & */
+       ch=b_peek(input);
+       if (ch == '-') {
+               b_getch(input);
+               return -3;  /* "-" represents "close me" */
+       }
+       while (isdigit(ch)) {
+               d = d*10+(ch-'0');
+               ok=1;
+               b_getch(input);
+               ch = b_peek(input);
+       }
+       if (ok) return d;
+
+       bb_error_msg("ambiguous redirect");
+       return -2;
+}
+
+/* If a redirect is immediately preceded by a number, that number is
+ * supposed to tell which file descriptor to redirect.  This routine
+ * looks for such preceding numbers.  In an ideal world this routine
+ * needs to handle all the following classes of redirects...
+ *     echo 2>foo     # redirects fd  2 to file "foo", nothing passed to echo
+ *     echo 49>foo    # redirects fd 49 to file "foo", nothing passed to echo
+ *     echo -2>foo    # redirects fd  1 to file "foo",    "-2" passed to echo
+ *     echo 49x>foo   # redirects fd  1 to file "foo",   "49x" passed to echo
+ * A -1 output from this program means no valid number was found, so the
+ * caller should use the appropriate default for this redirection.
+ */
+static int redirect_opt_num(o_string *o)
+{
+       int num;
+
+       if (o->length==0) return -1;
+       for(num=0; num<o->length; num++) {
+               if (!isdigit(*(o->data+num))) {
+                       return -1;
+               }
+       }
+       /* reuse num (and save an int) */
+       num=atoi(o->data);
+       b_reset(o);
+       return num;
+}
+
+FILE *generate_stream_from_list(struct pipe *head)
+{
+       FILE *pf;
+#if 1
+       int pid, channel[2];
+       if (pipe(channel)<0) bb_perror_msg_and_die("pipe");
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+       pid=fork();
+#else
+       pid=vfork();
+#endif
+       if (pid<0) {
+               bb_perror_msg_and_die("fork");
+       } else if (pid==0) {
+               close(channel[0]);
+               if (channel[1] != 1) {
+                       dup2(channel[1],1);
+                       close(channel[1]);
+               }
+#if 0
+#define SURROGATE "surrogate response"
+               write(1,SURROGATE,sizeof(SURROGATE));
+               _exit(run_list(head));
+#else
+               _exit(run_list_real(head));   /* leaks memory */
+#endif
+       }
+       debug_printf("forked child %d\n",pid);
+       close(channel[1]);
+       pf = fdopen(channel[0],"r");
+       debug_printf("pipe on FILE *%p\n",pf);
+#else
+       free_pipe_list(head,0);
+       pf=popen("echo surrogate response","r");
+       debug_printf("started fake pipe on FILE *%p\n",pf);
+#endif
+       return pf;
+}
+
+/* this version hacked for testing purposes */
+/* return code is exit status of the process that is run. */
+static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end)
+{
+       int retcode;
+       o_string result=NULL_O_STRING;
+       struct p_context inner;
+       FILE *p;
+       struct in_str pipe_str;
+       initialize_context(&inner);
+
+       /* recursion to generate command */
+       retcode = parse_stream(&result, &inner, input, subst_end);
+       if (retcode != 0) return retcode;  /* syntax error or EOF */
+       done_word(&result, &inner);
+       done_pipe(&inner, PIPE_SEQ);
+       b_free(&result);
+
+       p=generate_stream_from_list(inner.list_head);
+       if (p==NULL) return 1;
+       mark_open(fileno(p));
+       setup_file_in_str(&pipe_str, p);
+
+       /* now send results of command back into original context */
+       retcode = parse_stream(dest, ctx, &pipe_str, '\0');
+       /* XXX In case of a syntax error, should we try to kill the child?
+        * That would be tough to do right, so just read until EOF. */
+       if (retcode == 1) {
+               while (b_getch(&pipe_str)!=EOF) { /* discard */ };
+       }
+
+       debug_printf("done reading from pipe, pclose()ing\n");
+       /* This is the step that wait()s for the child.  Should be pretty
+        * safe, since we just read an EOF from its stdout.  We could try
+        * to better, by using wait(), and keeping track of background jobs
+        * at the same time.  That would be a lot of work, and contrary
+        * to the KISS philosophy of this program. */
+       mark_closed(fileno(p));
+       retcode=pclose(p);
+       free_pipe_list(inner.list_head,0);
+       debug_printf("pclosed, retcode=%d\n",retcode);
+       /* XXX this process fails to trim a single trailing newline */
+       return retcode;
+}
+
+static int parse_group(o_string *dest, struct p_context *ctx,
+       struct in_str *input, int ch)
+{
+       int rcode, endch=0;
+       struct p_context sub;
+       struct child_prog *child = ctx->child;
+       if (child->argv) {
+               syntax();
+               return 1;  /* syntax error, groups and arglists don't mix */
+       }
+       initialize_context(&sub);
+       switch(ch) {
+               case '(': endch=')'; child->subshell=1; break;
+               case '{': endch='}'; break;
+               default: syntax();   /* really logic error */
+       }
+       rcode=parse_stream(dest,&sub,input,endch);
+       done_word(dest,&sub); /* finish off the final word in the subcontext */
+       done_pipe(&sub, PIPE_SEQ);  /* and the final command there, too */
+       child->group = sub.list_head;
+       return rcode;
+       /* child remains "open", available for possible redirects */
+}
+
+/* basically useful version until someone wants to get fancier,
+ * see the bash man page under "Parameter Expansion" */
+static char *lookup_param(char *src)
+{
+       char *p=NULL;
+       if (src) {
+               p = getenv(src);
+               if (!p)
+                       p = get_local_var(src);
+       }
+       return p;
+}
+
+/* return code: 0 for OK, 1 for syntax error */
+static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
+{
+       int i, advance=0;
+       char sep[]=" ";
+       int ch = input->peek(input);  /* first character after the $ */
+       debug_printf("handle_dollar: ch=%c\n",ch);
+       if (isalpha(ch)) {
+               b_addchr(dest, SPECIAL_VAR_SYMBOL);
+               ctx->child->sp++;
+               while(ch=b_peek(input),isalnum(ch) || ch=='_') {
+                       b_getch(input);
+                       b_addchr(dest,ch);
+               }
+               b_addchr(dest, SPECIAL_VAR_SYMBOL);
+       } else if (isdigit(ch)) {
+               i = ch-'0';  /* XXX is $0 special? */
+               if (i<global_argc) {
+                       parse_string(dest, ctx, global_argv[i]); /* recursion */
+               }
+               advance = 1;
+       } else switch (ch) {
+               case '$':
+                       b_adduint(dest,getpid());
+                       advance = 1;
+                       break;
+               case '!':
+                       if (last_bg_pid > 0) b_adduint(dest, last_bg_pid);
+                       advance = 1;
+                       break;
+               case '?':
+                       b_adduint(dest,last_return_code);
+                       advance = 1;
+                       break;
+               case '#':
+                       b_adduint(dest,global_argc ? global_argc-1 : 0);
+                       advance = 1;
+                       break;
+               case '{':
+                       b_addchr(dest, SPECIAL_VAR_SYMBOL);
+                       ctx->child->sp++;
+                       b_getch(input);
+                       /* XXX maybe someone will try to escape the '}' */
+                       while(ch=b_getch(input),ch!=EOF && ch!='}') {
+                               b_addchr(dest,ch);
+                       }
+                       if (ch != '}') {
+                               syntax();
+                               return 1;
+                       }
+                       b_addchr(dest, SPECIAL_VAR_SYMBOL);
+                       break;
+               case '(':
+                       b_getch(input);
+                       process_command_subs(dest, ctx, input, ')');
+                       break;
+               case '*':
+                       sep[0]=ifs[0];
+                       for (i=1; i<global_argc; i++) {
+                               parse_string(dest, ctx, global_argv[i]);
+                               if (i+1 < global_argc) parse_string(dest, ctx, sep);
+                       }
+                       break;
+               case '@':
+               case '-':
+               case '_':
+                       /* still unhandled, but should be eventually */
+                       bb_error_msg("unhandled syntax: $%c",ch);
+                       return 1;
+                       break;
+               default:
+                       b_addqchr(dest,'$',dest->quote);
+       }
+       /* Eat the character if the flag was set.  If the compiler
+        * is smart enough, we could substitute "b_getch(input);"
+        * for all the "advance = 1;" above, and also end up with
+        * a nice size-optimized program.  Hah!  That'll be the day.
+        */
+       if (advance) b_getch(input);
+       return 0;
+}
+
+int parse_string(o_string *dest, struct p_context *ctx, const char *src)
+{
+       struct in_str foo;
+       setup_string_in_str(&foo, src);
+       return parse_stream(dest, ctx, &foo, '\0');
+}
+
+/* return code is 0 for normal exit, 1 for syntax error */
+int parse_stream(o_string *dest, struct p_context *ctx,
+       struct in_str *input, int end_trigger)
+{
+       unsigned int ch, m;
+       int redir_fd;
+       redir_type redir_style;
+       int next;
+
+       /* Only double-quote state is handled in the state variable dest->quote.
+        * A single-quote triggers a bypass of the main loop until its mate is
+        * found.  When recursing, quote state is passed in via dest->quote. */
+
+       debug_printf("parse_stream, end_trigger=%d\n",end_trigger);
+       while ((ch=b_getch(input))!=EOF) {
+               m = map[ch];
+               next = (ch == '\n') ? 0 : b_peek(input);
+               debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n",
+                       ch,ch,m,dest->quote);
+               if (m==0 || ((m==1 || m==2) && dest->quote)) {
+                       b_addqchr(dest, ch, dest->quote);
+               } else {
+                       if (m==2) {  /* unquoted IFS */
+                               if (done_word(dest, ctx)) {
+                                       return 1;
+                               }
+                               /* If we aren't performing a substitution, treat a newline as a
+                                * command separator.  */
+                               if (end_trigger != '\0' && ch=='\n')
+                                       done_pipe(ctx,PIPE_SEQ);
+                       }
+                       if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) {
+                               debug_printf("leaving parse_stream (triggered)\n");
+                               return 0;
+                       }
+#if 0
+                       if (ch=='\n') {
+                               /* Yahoo!  Time to run with it! */
+                               done_pipe(ctx,PIPE_SEQ);
+                               run_list(ctx->list_head);
+                               initialize_context(ctx);
+                       }
+#endif
+                       if (m!=2) switch (ch) {
+               case '#':
+                       if (dest->length == 0 && !dest->quote) {
+                               while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); }
+                       } else {
+                               b_addqchr(dest, ch, dest->quote);
+                       }
+                       break;
+               case '\\':
+                       if (next == EOF) {
+                               syntax();
+                               return 1;
+                       }
+                       b_addqchr(dest, '\\', dest->quote);
+                       b_addqchr(dest, b_getch(input), dest->quote);
+                       break;
+               case '$':
+                       if (handle_dollar(dest, ctx, input)!=0) return 1;
+                       break;
+               case '\'':
+                       dest->nonnull = 1;
+                       while(ch=b_getch(input),ch!=EOF && ch!='\'') {
+                               b_addchr(dest,ch);
+                       }
+                       if (ch==EOF) {
+                               syntax();
+                               return 1;
+                       }
+                       break;
+               case '"':
+                       dest->nonnull = 1;
+                       dest->quote = !dest->quote;
+                       break;
+               case '`':
+                       process_command_subs(dest, ctx, input, '`');
+                       break;
+               case '>':
+                       redir_fd = redirect_opt_num(dest);
+                       done_word(dest, ctx);
+                       redir_style=REDIRECT_OVERWRITE;
+                       if (next == '>') {
+                               redir_style=REDIRECT_APPEND;
+                               b_getch(input);
+                       } else if (next == '(') {
+                               syntax();   /* until we support >(list) Process Substitution */
+                               return 1;
+                       }
+                       setup_redirect(ctx, redir_fd, redir_style, input);
+                       break;
+               case '<':
+                       redir_fd = redirect_opt_num(dest);
+                       done_word(dest, ctx);
+                       redir_style=REDIRECT_INPUT;
+                       if (next == '<') {
+                               redir_style=REDIRECT_HEREIS;
+                               b_getch(input);
+                       } else if (next == '>') {
+                               redir_style=REDIRECT_IO;
+                               b_getch(input);
+                       } else if (next == '(') {
+                               syntax();   /* until we support <(list) Process Substitution */
+                               return 1;
+                       }
+                       setup_redirect(ctx, redir_fd, redir_style, input);
+                       break;
+               case ';':
+                       done_word(dest, ctx);
+                       done_pipe(ctx,PIPE_SEQ);
+                       break;
+               case '&':
+                       done_word(dest, ctx);
+                       if (next=='&') {
+                               b_getch(input);
+                               done_pipe(ctx,PIPE_AND);
+                       } else {
+                               done_pipe(ctx,PIPE_BG);
+                       }
+                       break;
+               case '|':
+                       done_word(dest, ctx);
+                       if (next=='|') {
+                               b_getch(input);
+                               done_pipe(ctx,PIPE_OR);
+                       } else {
+                               /* we could pick up a file descriptor choice here
+                                * with redirect_opt_num(), but bash doesn't do it.
+                                * "echo foo 2| cat" yields "foo 2". */
+                               done_command(ctx);
+                       }
+                       break;
+               case '(':
+               case '{':
+                       if (parse_group(dest, ctx, input, ch)!=0) return 1;
+                       break;
+               case ')':
+               case '}':
+                       syntax();   /* Proper use of this character caught by end_trigger */
+                       return 1;
+                       break;
+               default:
+                       syntax();   /* this is really an internal logic error */
+                       return 1;
+                       }
+               }
+       }
+       /* complain if quote?  No, maybe we just finished a command substitution
+        * that was quoted.  Example:
+        * $ echo "`cat foo` plus more"
+        * and we just got the EOF generated by the subshell that ran "cat foo"
+        * The only real complaint is if we got an EOF when end_trigger != '\0',
+        * that is, we were really supposed to get end_trigger, and never got
+        * one before the EOF.  Can't use the standard "syntax error" return code,
+        * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
+       debug_printf("leaving parse_stream (EOF)\n");
+       if (end_trigger != '\0') return -1;
+       return 0;
+}
+
+void mapset(const unsigned char *set, int code)
+{
+       const unsigned char *s;
+       for (s=set; *s; s++) map[*s] = code;
+}
+
+void update_ifs_map(void)
+{
+       /* char *ifs and char map[256] are both globals. */
+       ifs = getenv("IFS");
+       if (ifs == NULL) ifs=" \t\n";
+       /* Precompute a list of 'flow through' behavior so it can be treated
+        * quickly up front.  Computation is necessary because of IFS.
+        * Special case handling of IFS == " \t\n" is not implemented.
+        * The map[] array only really needs two bits each, and on most machines
+        * that would be faster because of the reduced L1 cache footprint.
+        */
+       memset(map,0,sizeof(map)); /* most characters flow through always */
+       mapset("\\$'\"`", 3);      /* never flow through */
+       mapset("<>;&|(){}#", 1);   /* flow through if quoted */
+       mapset(ifs, 2);            /* also flow through if quoted */
+}
+
+/* most recursion does not come through here, the exception is
+ * from builtin_source() */
+int parse_stream_outer(struct in_str *inp, int flag)
+{
+
+       struct p_context ctx;
+       o_string temp=NULL_O_STRING;
+       int rcode;
+       do {
+               ctx.type = flag;
+               initialize_context(&ctx);
+               update_ifs_map();
+               if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0);
+               inp->promptmode=1;
+               rcode = parse_stream(&temp, &ctx, inp, '\n');
+               if (rcode != 1 && ctx.old_flag != 0) {
+                       syntax();
+               }
+               if (rcode != 1 && ctx.old_flag == 0) {
+                       done_word(&temp, &ctx);
+                       done_pipe(&ctx,PIPE_SEQ);
+                       run_list(ctx.list_head);
+               } else {
+                       if (ctx.old_flag != 0) {
+                               free(ctx.stack);
+                               b_reset(&temp);
+                       }
+                       temp.nonnull = 0;
+                       temp.quote = 0;
+                       inp->p = NULL;
+                       free_pipe_list(ctx.list_head,0);
+               }
+               b_free(&temp);
+       } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP));   /* loop on syntax errors, return on EOF */
+       return 0;
+}
+
+static int parse_string_outer(const char *s, int flag)
+{
+       struct in_str input;
+       setup_string_in_str(&input, s);
+       return parse_stream_outer(&input, flag);
+}
+
+static int parse_file_outer(FILE *f)
+{
+       int rcode;
+       struct in_str input;
+       setup_file_in_str(&input, f);
+       rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
+       return rcode;
+}
+
+/* Make sure we have a controlling tty.  If we get started under a job
+ * aware app (like bash for example), make sure we are now in charge so
+ * we don't fight over who gets the foreground */
+static void setup_job_control(void)
+{
+       static pid_t shell_pgrp;
+       /* Loop until we are in the foreground.  */
+       while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ()))
+               kill (- shell_pgrp, SIGTTIN);
+
+       /* Ignore interactive and job-control signals.  */
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_IGN);
+       signal(SIGTERM, SIG_IGN);
+       signal(SIGTSTP, SIG_IGN);
+       signal(SIGTTIN, SIG_IGN);
+       signal(SIGTTOU, SIG_IGN);
+       signal(SIGCHLD, SIG_IGN);
+
+       /* Put ourselves in our own process group.  */
+       setsid();
+       shell_pgrp = getpid ();
+       setpgid (shell_pgrp, shell_pgrp);
+
+       /* Grab control of the terminal.  */
+       tcsetpgrp(shell_terminal, shell_pgrp);
+}
+
+int hush_main(int argc, char **argv)
+{
+       int opt;
+       FILE *input;
+       char **e = environ;
+
+       /* XXX what should these be while sourcing /etc/profile? */
+       global_argc = argc;
+       global_argv = argv;
+
+       /* (re?) initialize globals.  Sometimes hush_main() ends up calling
+        * hush_main(), therefore we cannot rely on the BSS to zero out this
+        * stuff.  Reset these to 0 every time. */
+       ifs = NULL;
+       /* map[] is taken care of with call to update_ifs_map() */
+       fake_mode = 0;
+       interactive = 0;
+       close_me_head = NULL;
+       last_bg_pid = 0;
+       job_list = NULL;
+       last_jobid = 0;
+
+       /* Initialize some more globals to non-zero values */
+       set_cwd();
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+       cmdedit_set_initial_prompt();
+#else
+       PS1 = NULL;
+#endif
+       PS2 = "> ";
+
+       /* initialize our shell local variables with the values
+        * currently living in the environment */
+       if (e) {
+               for (; *e; e++)
+                       set_local_var(*e, 2);   /* without call putenv() */
+       }
+
+       last_return_code=EXIT_SUCCESS;
+
+
+       if (argv[0] && argv[0][0] == '-') {
+               debug_printf("\nsourcing /etc/profile\n");
+               if ((input = fopen("/etc/profile", "r")) != NULL) {
+                       mark_open(fileno(input));
+                       parse_file_outer(input);
+                       mark_closed(fileno(input));
+                       fclose(input);
+               }
+       }
+       input=stdin;
+
+       while ((opt = getopt(argc, argv, "c:xif")) > 0) {
+               switch (opt) {
+                       case 'c':
+                               {
+                                       global_argv = argv+optind;
+                                       global_argc = argc-optind;
+                                       opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
+                                       goto final_return;
+                               }
+                               break;
+                       case 'i':
+                               interactive++;
+                               break;
+                       case 'f':
+                               fake_mode++;
+                               break;
+                       default:
+#ifndef BB_VER
+                               fprintf(stderr, "Usage: sh [FILE]...\n"
+                                               "   or: sh -c command [args]...\n\n");
+                               exit(EXIT_FAILURE);
+#else
+                               bb_show_usage();
+#endif
+               }
+       }
+       /* A shell is interactive if the `-i' flag was given, or if all of
+        * the following conditions are met:
+        *        no -c command
+        *    no arguments remaining or the -s flag given
+        *    standard input is a terminal
+        *    standard output is a terminal
+        *    Refer to Posix.2, the description of the `sh' utility. */
+       if (argv[optind]==NULL && input==stdin &&
+                       isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
+               interactive++;
+       }
+
+       debug_printf("\ninteractive=%d\n", interactive);
+       if (interactive) {
+               /* Looks like they want an interactive shell */
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+               printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n");
+               printf( "Enter 'help' for a list of built-in commands.\n\n");
+#endif
+               setup_job_control();
+       }
+
+       if (argv[optind]==NULL) {
+               opt=parse_file_outer(stdin);
+               goto final_return;
+       }
+
+       debug_printf("\nrunning script '%s'\n", argv[optind]);
+       global_argv = argv+optind;
+       global_argc = argc-optind;
+       input = bb_xfopen(argv[optind], "r");
+       opt = parse_file_outer(input);
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       fclose(input);
+       if (cwd && cwd != bb_msg_unknown)
+               free((char*)cwd);
+       {
+               struct variables *cur, *tmp;
+               for(cur = top_vars; cur; cur = tmp) {
+                       tmp = cur->next;
+                       if (!cur->flg_read_only) {
+                               free(cur->name);
+                               free(cur->value);
+                               free(cur);
+                       }
+               }
+       }
+#endif
+
+final_return:
+       return(opt?opt:last_return_code);
+}
+
+static char *insert_var_value(char *inp)
+{
+       int res_str_len = 0;
+       int len;
+       int done = 0;
+       char *p, *p1, *res_str = NULL;
+
+       while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
+               if (p != inp) {
+                       len = p - inp;
+                       res_str = xrealloc(res_str, (res_str_len + len));
+                       strncpy((res_str + res_str_len), inp, len);
+                       res_str_len += len;
+               }
+               inp = ++p;
+               p = strchr(inp, SPECIAL_VAR_SYMBOL);
+               *p = '\0';
+               if ((p1 = lookup_param(inp))) {
+                       len = res_str_len + strlen(p1);
+                       res_str = xrealloc(res_str, (1 + len));
+                       strcpy((res_str + res_str_len), p1);
+                       res_str_len = len;
+               }
+               *p = SPECIAL_VAR_SYMBOL;
+               inp = ++p;
+               done = 1;
+       }
+       if (done) {
+               res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp)));
+               strcpy((res_str + res_str_len), inp);
+               while ((p = strchr(res_str, '\n'))) {
+                       *p = ' ';
+               }
+       }
+       return (res_str == NULL) ? inp : res_str;
+}
+
+static char **make_list_in(char **inp, char *name)
+{
+       int len, i;
+       int name_len = strlen(name);
+       int n = 0;
+       char **list;
+       char *p1, *p2, *p3;
+
+       /* create list of variable values */
+       list = xmalloc(sizeof(*list));
+       for (i = 0; inp[i]; i++) {
+               p3 = insert_var_value(inp[i]);
+               p1 = p3;
+               while (*p1) {
+                       if ((*p1 == ' ')) {
+                               p1++;
+                               continue;
+                       }
+                       if ((p2 = strchr(p1, ' '))) {
+                               len = p2 - p1;
+                       } else {
+                               len = strlen(p1);
+                               p2 = p1 + len;
+                       }
+                       /* we use n + 2 in realloc for list,because we add
+                        * new element and then we will add NULL element */
+                       list = xrealloc(list, sizeof(*list) * (n + 2));
+                       list[n] = xmalloc(2 + name_len + len);
+                       strcpy(list[n], name);
+                       strcat(list[n], "=");
+                       strncat(list[n], p1, len);
+                       list[n++][name_len + len + 1] = '\0';
+                       p1 = p2;
+               }
+               if (p3 != inp[i]) free(p3);
+       }
+       list[n] = NULL;
+       return list;
+}
+
+/* Make new string for parser */
+static char * make_string(char ** inp)
+{
+       char *p;
+       char *str = NULL;
+       int n;
+       int len = 2;
+
+       for (n = 0; inp[n]; n++) {
+               p = insert_var_value(inp[n]);
+               str = xrealloc(str, (len + strlen(p)));
+               if (n) {
+                       strcat(str, " ");
+               } else {
+                       *str = '\0';
+               }
+               strcat(str, p);
+               len = strlen(str) + 3;
+               if (p != inp[n]) free(p);
+       }
+       len = strlen(str);
+       *(str + len) = '\n';
+       *(str + len + 1) = '\0';
+       return str;
+}
diff --git a/busybox/shell/lash.c b/busybox/shell/lash.c
new file mode 100644 (file)
index 0000000..f454e69
--- /dev/null
@@ -0,0 +1,1696 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lash -- the BusyBox Lame-Ass SHell
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
+ * under the following liberal license: "We have placed this source code in the
+ * public domain. Use it in any project, free or commercial."
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* This shell's parsing engine is officially at a dead-end.  Future
+ * work shell work should be done using hush, msh, or ash.  This is
+ * still a very useful, small shell -- it just don't need any more
+ * features beyond what it already has...
+ */
+
+//For debugging/development on the shell only...
+//#define DEBUG_SHELL
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <termios.h>
+#include "busybox.h"
+#include "cmdedit.h"
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#include <locale.h>
+#endif
+
+#include <glob.h>
+#define expand_t       glob_t
+
+/* Always enable for the moment... */
+#define CONFIG_LASH_PIPE_N_REDIRECTS
+#define CONFIG_LASH_JOB_CONTROL
+
+static const int MAX_READ = 128;       /* size of input buffer for `read' builtin */
+#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
+
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE,
+       REDIRECT_APPEND
+};
+#endif
+
+static const unsigned int DEFAULT_CONTEXT=0x1;
+static const unsigned int IF_TRUE_CONTEXT=0x2;
+static const unsigned int IF_FALSE_CONTEXT=0x4;
+static const unsigned int THEN_EXP_CONTEXT=0x8;
+static const unsigned int ELSE_EXP_CONTEXT=0x10;
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+struct redir_struct {
+       enum redir_type type;   /* type of redirection */
+       int fd;                                         /* file descriptor being redirected */
+       char *filename;                         /* file to redirect fd to */
+};
+#endif
+
+struct child_prog {
+       pid_t pid;                                      /* 0 if exited */
+       char **argv;                            /* program name and arguments */
+       int num_redirects;                      /* elements in redirection array */
+       int is_stopped;                         /* is the program currently running? */
+       struct job *family;                     /* pointer back to the child's parent job */
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+       struct redir_struct *redirects; /* I/O redirects */
+#endif
+};
+
+struct jobset {
+       struct job *head;                       /* head of list of running jobs */
+       struct job *fg;                         /* current foreground job */
+};
+
+struct job {
+       int jobid;                                      /* job number */
+       int num_progs;                          /* total number of programs in job */
+       int running_progs;                      /* number of programs running */
+       char *text;                                     /* name of job */
+       char *cmdbuf;                           /* buffer various argv's point into */
+       pid_t pgrp;                                     /* process group ID for the job */
+       struct child_prog *progs;       /* array of programs in job */
+       struct job *next;                       /* to track background commands */
+       int stopped_progs;                      /* number of programs alive, but stopped */
+       unsigned int job_context;       /* bitmask defining current context */
+       struct jobset *job_list;
+};
+
+struct built_in_command {
+       char *cmd;                                      /* name */
+       char *descr;                            /* description */
+       int (*function) (struct child_prog *);  /* function ptr */
+};
+
+struct close_me {
+       int fd;
+       struct close_me *next;
+};
+
+/* function prototypes for builtins */
+static int builtin_cd(struct child_prog *cmd);
+static int builtin_exec(struct child_prog *cmd);
+static int builtin_exit(struct child_prog *cmd);
+static int builtin_fg_bg(struct child_prog *cmd);
+static int builtin_help(struct child_prog *cmd);
+static int builtin_jobs(struct child_prog *dummy);
+static int builtin_pwd(struct child_prog *dummy);
+static int builtin_export(struct child_prog *cmd);
+static int builtin_source(struct child_prog *cmd);
+static int builtin_unset(struct child_prog *cmd);
+static int builtin_read(struct child_prog *cmd);
+
+
+/* function prototypes for shell stuff */
+static void mark_open(int fd);
+static void mark_closed(int fd);
+static void close_all(void);
+static void checkjobs(struct jobset *job_list);
+static void remove_job(struct jobset *j_list, struct job *job);
+static int get_command(FILE * source, char *command);
+static int parse_command(char **command_ptr, struct job *job, int *inbg);
+static int run_command(struct job *newjob, int inbg, int outpipe[2]);
+static int pseudo_exec(struct child_prog *cmd) __attribute__ ((noreturn));
+static int busy_loop(FILE * input);
+
+
+/* Table of built-in functions (these are non-forking builtins, meaning they
+ * can change global variables in the parent shell process but they will not
+ * work with pipes and redirects; 'unset foo | whatever' will not work) */
+static struct built_in_command bltins[] = {
+       {"bg", "Resume a job in the background", builtin_fg_bg},
+       {"cd", "Change working directory", builtin_cd},
+       {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
+       {"exit", "Exit from shell()", builtin_exit},
+       {"fg", "Bring job into the foreground", builtin_fg_bg},
+       {"jobs", "Lists the active jobs", builtin_jobs},
+       {"export", "Set environment variable", builtin_export},
+       {"unset", "Unset environment variable", builtin_unset},
+       {"read", "Input environment variable", builtin_read},
+       {".", "Source-in and run commands in a file", builtin_source},
+       /* to do: add ulimit */
+       {NULL, NULL, NULL}
+};
+
+/* Table of forking built-in functions (things that fork cannot change global
+ * variables in the parent process, such as the current working directory) */
+static struct built_in_command bltins_forking[] = {
+       {"pwd", "Print current directory", builtin_pwd},
+       {"help", "List shell built-in commands", builtin_help},
+       {NULL, NULL, NULL}
+};
+
+
+static int shell_context;  /* Type prompt trigger (PS1 or PS2) */
+
+
+/* Globals that are static to this file */
+static const char *cwd;
+static char *local_pending_command = NULL;
+static struct jobset job_list = { NULL, NULL };
+static int argc;
+static char **argv;
+static struct close_me *close_me_head;
+static int last_return_code;
+static int last_bg_pid;
+static unsigned int last_jobid;
+static int shell_terminal;
+static char *PS1;
+static char *PS2 = "> ";
+
+
+#ifdef DEBUG_SHELL
+static inline void debug_printf(const char *format, ...)
+{
+       va_list args;
+       va_start(args, format);
+       vfprintf(stderr, format, args);
+       va_end(args);
+}
+#else
+static inline void debug_printf(const char *format, ...) { }
+#endif
+
+/*
+       Most builtins need access to the struct child_prog that has
+       their arguments, previously coded as cmd->progs[0].  That coding
+       can exhibit a bug, if the builtin is not the first command in
+       a pipeline: "echo foo | exec sort" will attempt to exec foo.
+
+builtin   previous use      notes
+------ -----------------  ---------
+cd      cmd->progs[0]
+exec    cmd->progs[0]  squashed bug: didn't look for applets or forking builtins
+exit    cmd->progs[0]
+fg_bg   cmd->progs[0], job_list->head, job_list->fg
+help    0
+jobs    job_list->head
+pwd     0
+export  cmd->progs[0]
+source  cmd->progs[0]
+unset   cmd->progs[0]
+read    cmd->progs[0]
+
+I added "struct job *family;" to struct child_prog,
+and switched API to builtin_foo(struct child_prog *child);
+So   cmd->text        becomes  child->family->text
+     cmd->job_context  becomes  child->family->job_context
+     cmd->progs[0]    becomes  *child
+     job_list          becomes  child->family->job_list
+ */
+
+/* built-in 'cd <path>' handler */
+static int builtin_cd(struct child_prog *child)
+{
+       char *newdir;
+
+       if (child->argv[1] == NULL)
+               newdir = getenv("HOME");
+       else
+               newdir = child->argv[1];
+       if (chdir(newdir)) {
+               printf("cd: %s: %m\n", newdir);
+               return EXIT_FAILURE;
+       }
+       cwd = xgetcwd((char *)cwd);
+       if (!cwd)
+               cwd = bb_msg_unknown;
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'exec' handler */
+static int builtin_exec(struct child_prog *child)
+{
+       if (child->argv[1] == NULL)
+               return EXIT_SUCCESS;   /* Really? */
+       child->argv++;
+       close_all();
+       pseudo_exec(child);
+       /* never returns */
+}
+
+/* built-in 'exit' handler */
+static int builtin_exit(struct child_prog *child)
+{
+       if (child->argv[1] == NULL)
+               exit(EXIT_SUCCESS);
+
+       exit (atoi(child->argv[1]));
+}
+
+/* built-in 'fg' and 'bg' handler */
+static int builtin_fg_bg(struct child_prog *child)
+{
+       int i, jobnum;
+       struct job *job=NULL;
+
+       /* If they gave us no args, assume they want the last backgrounded task */
+       if (!child->argv[1]) {
+               for (job = child->family->job_list->head; job; job = job->next) {
+                       if (job->jobid == last_jobid) {
+                               break;
+                       }
+               }
+               if (!job) {
+                       bb_error_msg("%s: no current job", child->argv[0]);
+                       return EXIT_FAILURE;
+               }
+       } else {
+               if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
+                       bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
+                       return EXIT_FAILURE;
+               }
+               for (job = child->family->job_list->head; job; job = job->next) {
+                       if (job->jobid == jobnum) {
+                               break;
+                       }
+               }
+               if (!job) {
+                       bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (*child->argv[0] == 'f') {
+               /* Put the job into the foreground.  */
+               tcsetpgrp(shell_terminal, job->pgrp);
+
+               child->family->job_list->fg = job;
+       }
+
+       /* Restart the processes in the job */
+       for (i = 0; i < job->num_progs; i++)
+               job->progs[i].is_stopped = 0;
+
+       job->stopped_progs = 0;
+
+       if ( (i=kill(- job->pgrp, SIGCONT)) < 0) {
+               if (i == ESRCH) {
+                       remove_job(&job_list, job);
+               } else {
+                       bb_perror_msg("kill (SIGCONT)");
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'help' handler */
+static int builtin_help(struct child_prog *dummy)
+{
+       struct built_in_command *x;
+
+       printf("\nBuilt-in commands:\n");
+       printf("-------------------\n");
+       for (x = bltins; x->cmd; x++) {
+               if (x->descr==NULL)
+                       continue;
+               printf("%s\t%s\n", x->cmd, x->descr);
+       }
+       for (x = bltins_forking; x->cmd; x++) {
+               if (x->descr==NULL)
+                       continue;
+               printf("%s\t%s\n", x->cmd, x->descr);
+       }
+       printf("\n\n");
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'jobs' handler */
+static int builtin_jobs(struct child_prog *child)
+{
+       struct job *job;
+       char *status_string;
+
+       for (job = child->family->job_list->head; job; job = job->next) {
+               if (job->running_progs == job->stopped_progs)
+                       status_string = "Stopped";
+               else
+                       status_string = "Running";
+
+               printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
+       }
+       return EXIT_SUCCESS;
+}
+
+
+/* built-in 'pwd' handler */
+static int builtin_pwd(struct child_prog *dummy)
+{
+       cwd = xgetcwd((char *)cwd);
+       if (!cwd)
+               cwd = bb_msg_unknown;
+       puts(cwd);
+       return EXIT_SUCCESS;
+}
+
+/* built-in 'export VAR=value' handler */
+static int builtin_export(struct child_prog *child)
+{
+       int res;
+       char *v = child->argv[1];
+
+       if (v == NULL) {
+               char **e;
+               for (e = environ; *e; e++) {
+                       puts(*e);
+               }
+               return 0;
+       }
+       res = putenv(v);
+       if (res)
+               fprintf(stderr, "export: %m\n");
+#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
+       if (strncmp(v, "PS1=", 4)==0)
+               PS1 = getenv("PS1");
+#endif
+
+#ifdef CONFIG_LOCALE_SUPPORT
+       if(strncmp(v, "LC_ALL=", 7)==0)
+               setlocale(LC_ALL, getenv("LC_ALL"));
+       if(strncmp(v, "LC_CTYPE=", 9)==0)
+               setlocale(LC_CTYPE, getenv("LC_CTYPE"));
+#endif
+
+       return (res);
+}
+
+/* built-in 'read VAR' handler */
+static int builtin_read(struct child_prog *child)
+{
+       int res = 0, len, newlen;
+       char *s;
+       char string[MAX_READ];
+
+       if (child->argv[1]) {
+               /* argument (VAR) given: put "VAR=" into buffer */
+               safe_strncpy(string, child->argv[1], MAX_READ-1);
+               len = strlen(string);
+               string[len++] = '=';
+               string[len]   = '\0';
+               fgets(&string[len], sizeof(string) - len, stdin);       /* read string */
+               newlen = strlen(string);
+               if(newlen > len)
+                       string[--newlen] = '\0';        /* chomp trailing newline */
+               /*
+               ** string should now contain "VAR=<value>"
+               ** copy it (putenv() won't do that, so we must make sure
+               ** the string resides in a static buffer!)
+               */
+               res = -1;
+               if((s = strdup(string)))
+                       res = putenv(s);
+               if (res)
+                       fprintf(stderr, "read: %m\n");
+       }
+       else
+               fgets(string, sizeof(string), stdin);
+
+       return (res);
+}
+
+/* Built-in '.' handler (read-in and execute commands from file) */
+static int builtin_source(struct child_prog *child)
+{
+       FILE *input;
+       int status;
+       int fd;
+
+       if (child->argv[1] == NULL)
+               return EXIT_FAILURE;
+
+       input = fopen(child->argv[1], "r");
+       if (!input) {
+               printf( "Couldn't open file '%s'\n", child->argv[1]);
+               return EXIT_FAILURE;
+       }
+
+       fd=fileno(input);
+       mark_open(fd);
+       /* Now run the file */
+       status = busy_loop(input);
+       fclose(input);
+       mark_closed(fd);
+       return (status);
+}
+
+/* built-in 'unset VAR' handler */
+static int builtin_unset(struct child_prog *child)
+{
+       if (child->argv[1] == NULL) {
+               printf( "unset: parameter required.\n");
+               return EXIT_FAILURE;
+       }
+       unsetenv(child->argv[1]);
+       return EXIT_SUCCESS;
+}
+
+static void mark_open(int fd)
+{
+       struct close_me *new = xmalloc(sizeof(struct close_me));
+       new->fd = fd;
+       new->next = close_me_head;
+       close_me_head = new;
+}
+
+static void mark_closed(int fd)
+{
+       struct close_me *tmp;
+       if (close_me_head == NULL || close_me_head->fd != fd)
+               bb_error_msg_and_die("corrupt close_me");
+       tmp = close_me_head;
+       close_me_head = close_me_head->next;
+       free(tmp);
+}
+
+static void close_all()
+{
+       struct close_me *c, *tmp;
+       for (c=close_me_head; c; c=tmp) {
+               close(c->fd);
+               tmp=c->next;
+               free(c);
+       }
+       close_me_head = NULL;
+}
+
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+/* free up all memory from a job */
+static void free_job(struct job *cmd)
+{
+       int i;
+       struct jobset *keep;
+
+       for (i = 0; i < cmd->num_progs; i++) {
+               free(cmd->progs[i].argv);
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+               if (cmd->progs[i].redirects)
+                       free(cmd->progs[i].redirects);
+#endif
+       }
+       free(cmd->progs);
+       free(cmd->text);
+       free(cmd->cmdbuf);
+       keep = cmd->job_list;
+       memset(cmd, 0, sizeof(struct job));
+       cmd->job_list = keep;
+}
+
+/* remove a job from a jobset */
+static void remove_job(struct jobset *j_list, struct job *job)
+{
+       struct job *prevjob;
+
+       free_job(job);
+       if (job == j_list->head) {
+               j_list->head = job->next;
+       } else {
+               prevjob = j_list->head;
+               while (prevjob->next != job)
+                       prevjob = prevjob->next;
+               prevjob->next = job->next;
+       }
+
+       if (j_list->head)
+               last_jobid = j_list->head->jobid;
+       else
+               last_jobid = 0;
+
+       free(job);
+}
+
+/* Checks to see if any background processes have exited -- if they
+   have, figure out why and see if a job has completed */
+static void checkjobs(struct jobset *j_list)
+{
+       struct job *job;
+       pid_t childpid;
+       int status;
+       int prognum = 0;
+
+       while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+               for (job = j_list->head; job; job = job->next) {
+                       prognum = 0;
+                       while (prognum < job->num_progs &&
+                                  job->progs[prognum].pid != childpid) prognum++;
+                       if (prognum < job->num_progs)
+                               break;
+               }
+
+               /* This happens on backticked commands */
+               if(job==NULL)
+                       return;
+
+               if (WIFEXITED(status) || WIFSIGNALED(status)) {
+                       /* child exited */
+                       job->running_progs--;
+                       job->progs[prognum].pid = 0;
+
+                       if (!job->running_progs) {
+                               printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
+                               last_jobid=0;
+                               remove_job(j_list, job);
+                       }
+               } else {
+                       /* child stopped */
+                       job->stopped_progs++;
+                       job->progs[prognum].is_stopped = 1;
+
+#if 0
+                       /* Printing this stuff is a pain, since it tends to
+                        * overwrite the prompt an inconveinient moments.  So
+                        * don't do that.  */
+                       if (job->stopped_progs == job->num_progs) {
+                               printf(JOB_STATUS_FORMAT, job->jobid, "Stopped",
+                                          job->text);
+                       }
+#endif
+               }
+       }
+
+       if (childpid == -1 && errno != ECHILD)
+               bb_perror_msg("waitpid");
+}
+#else
+static void checkjobs(struct jobset *j_list)
+{
+}
+static void free_job(struct job *cmd)
+{
+}
+static void remove_job(struct jobset *j_list, struct job *job)
+{
+}
+#endif
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+/* squirrel != NULL means we squirrel away copies of stdin, stdout,
+ * and stderr if they are redirected. */
+static int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+       int i;
+       int openfd;
+       int mode = O_RDONLY;
+       struct redir_struct *redir = prog->redirects;
+
+       for (i = 0; i < prog->num_redirects; i++, redir++) {
+               switch (redir->type) {
+               case REDIRECT_INPUT:
+                       mode = O_RDONLY;
+                       break;
+               case REDIRECT_OVERWRITE:
+                       mode = O_WRONLY | O_CREAT | O_TRUNC;
+                       break;
+               case REDIRECT_APPEND:
+                       mode = O_WRONLY | O_CREAT | O_APPEND;
+                       break;
+               }
+
+               openfd = open(redir->filename, mode, 0666);
+               if (openfd < 0) {
+                       /* this could get lost if stderr has been redirected, but
+                          bash and ash both lose it as well (though zsh doesn't!) */
+                       bb_perror_msg("error opening %s", redir->filename);
+                       return 1;
+               }
+
+               if (openfd != redir->fd) {
+                       if (squirrel && redir->fd < 3) {
+                               squirrel[redir->fd] = dup(redir->fd);
+                               fcntl (squirrel[redir->fd], F_SETFD, FD_CLOEXEC);
+                       }
+                       dup2(openfd, redir->fd);
+                       close(openfd);
+               }
+       }
+
+       return 0;
+}
+
+static void restore_redirects(int squirrel[])
+{
+       int i, fd;
+       for (i=0; i<3; i++) {
+               fd = squirrel[i];
+               if (fd != -1) {
+                       /* No error checking.  I sure wouldn't know what
+                        * to do with an error if I found one! */
+                       dup2(fd, i);
+                       close(fd);
+               }
+       }
+}
+#else
+static inline int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+       return 0;
+}
+static inline void restore_redirects(int squirrel[])
+{
+}
+#endif
+
+static inline void cmdedit_set_initial_prompt(void)
+{
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+       PS1 = NULL;
+#else
+       PS1 = getenv("PS1");
+       if(PS1==0)
+               PS1 = "\\w \\$ ";
+#endif
+}
+
+static inline void setup_prompt_string(char **prompt_str)
+{
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+       /* Set up the prompt */
+       if (shell_context == 0) {
+               free(PS1);
+               PS1=xmalloc(strlen(cwd)+4);
+               sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ?  "$ ":"# ");
+               *prompt_str = PS1;
+       } else {
+               *prompt_str = PS2;
+       }
+#else
+       *prompt_str = (shell_context==0)? PS1 : PS2;
+#endif
+}
+
+static int get_command(FILE * source, char *command)
+{
+       char *prompt_str;
+
+       if (source == NULL) {
+               if (local_pending_command) {
+                       /* a command specified (-c option): return it & mark it done */
+                       strcpy(command, local_pending_command);
+                       free(local_pending_command);
+                       local_pending_command = NULL;
+                       return 0;
+               }
+               return 1;
+       }
+
+       if (source == stdin) {
+               setup_prompt_string(&prompt_str);
+
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+               /*
+               ** enable command line editing only while a command line
+               ** is actually being read; otherwise, we'll end up bequeathing
+               ** atexit() handlers and other unwanted stuff to our
+               ** child processes (rob@sysgo.de)
+               */
+               cmdedit_read_input(prompt_str, command);
+               return 0;
+#else
+               fputs(prompt_str, stdout);
+#endif
+       }
+
+       if (!fgets(command, BUFSIZ - 2, source)) {
+               if (source == stdin)
+                       printf("\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+static char* itoa(register int i)
+{
+       static char a[7]; /* Max 7 ints */
+       register char *b = a + sizeof(a) - 1;
+       int   sign = (i < 0);
+
+       if (sign)
+               i = -i;
+       *b = 0;
+       do
+       {
+               *--b = '0' + (i % 10);
+               i /= 10;
+       }
+       while (i);
+       if (sign)
+               *--b = '-';
+       return b;
+}
+
+char * strsep_space( char *string, int * ix)
+{
+       char *token, *begin;
+
+       begin = string;
+
+       /* Short circuit the trivial case */
+       if ( !string || ! string[*ix])
+               return NULL;
+
+       /* Find the end of the token. */
+       while( string && string[*ix] && !isspace(string[*ix]) ) {
+               (*ix)++;
+       }
+
+       /* Find the end of any whitespace trailing behind
+        * the token and let that be part of the token */
+       while( string && string[*ix] && isspace(string[*ix]) ) {
+               (*ix)++;
+       }
+
+       if (! string && *ix==0) {
+               /* Nothing useful was found */
+               return NULL;
+       }
+
+       token = xmalloc(*ix+1);
+       token[*ix] = '\0';
+       strncpy(token, string,  *ix);
+
+       return token;
+}
+
+static int expand_arguments(char *command)
+{
+       int total_length=0, length, i, retval, ix = 0;
+       expand_t expand_result;
+       char *tmpcmd, *cmd, *cmd_copy;
+       char *src, *dst, *var;
+       const char *out_of_space = "out of space during expansion";
+       int flags = GLOB_NOCHECK
+#ifdef GLOB_BRACE
+               | GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+               | GLOB_TILDE
+#endif
+               ;
+
+       /* get rid of the terminating \n */
+       chomp(command);
+
+       /* Fix up escape sequences to be the Real Thing(tm) */
+       while( command && command[ix]) {
+               if (command[ix] == '\\') {
+                       const char *tmp = command+ix+1;
+                       command[ix] = bb_process_escape_sequence(  &tmp );
+                       memmove(command+ix + 1, tmp, strlen(tmp)+1);
+               }
+               ix++;
+       }
+       /* Use glob and then fixup environment variables and such */
+
+       /* It turns out that glob is very stupid.  We have to feed it one word at a
+        * time since it can't cope with a full string.  Here we convert command
+        * (char*) into cmd (char**, one word per string) */
+
+       /* We need a clean copy, so strsep can mess up the copy while
+        * we write stuff into the original (in a minute) */
+       cmd = cmd_copy = bb_xstrdup(command);
+       *command = '\0';
+       for (ix = 0, tmpcmd = cmd;
+                       (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
+               if (*tmpcmd == '\0')
+                       break;
+               /* we need to trim() the result for glob! */
+               trim(tmpcmd);
+               retval = glob(tmpcmd, flags, NULL, &expand_result);
+               free(tmpcmd); /* Free mem allocated by strsep_space */
+               if (retval == GLOB_NOSPACE) {
+                       /* Mem may have been allocated... */
+                       globfree (&expand_result);
+                       bb_error_msg(out_of_space);
+                       return FALSE;
+               } else if (retval != 0) {
+                       /* Some other error.  GLOB_NOMATCH shouldn't
+                        * happen because of the GLOB_NOCHECK flag in
+                        * the glob call. */
+                       bb_error_msg("syntax error");
+                       return FALSE;
+               } else {
+                       /* Convert from char** (one word per string) to a simple char*,
+                        * but don't overflow command which is BUFSIZ in length */
+                       for (i=0; i < expand_result.gl_pathc; i++) {
+                               length=strlen(expand_result.gl_pathv[i]);
+                               if (total_length+length+1 >= BUFSIZ) {
+                                       bb_error_msg(out_of_space);
+                                       return FALSE;
+                               }
+                               strcat(command+total_length, " ");
+                               total_length+=1;
+                               strcat(command+total_length, expand_result.gl_pathv[i]);
+                               total_length+=length;
+                       }
+                       globfree (&expand_result);
+               }
+       }
+       free(cmd_copy);
+       trim(command);
+
+       /* Now do the shell variable substitutions which
+        * wordexp can't do for us, namely $? and $! */
+       src = command;
+       while((dst = strchr(src,'$')) != NULL){
+               var = NULL;
+               switch(*(dst+1)) {
+                       case '?':
+                               var = itoa(last_return_code);
+                               break;
+                       case '!':
+                               if (last_bg_pid==-1)
+                                       *(var)='\0';
+                               else
+                                       var = itoa(last_bg_pid);
+                               break;
+                               /* Everything else like $$, $#, $[0-9], etc. should all be
+                                * expanded by wordexp(), so we can in theory skip that stuff
+                                * here, but just to be on the safe side (i.e., since uClibc
+                                * wordexp doesn't do this stuff yet), lets leave it in for
+                                * now. */
+                       case '$':
+                               var = itoa(getpid());
+                               break;
+                       case '#':
+                               var = itoa(argc-1);
+                               break;
+                       case '0':case '1':case '2':case '3':case '4':
+                       case '5':case '6':case '7':case '8':case '9':
+                               {
+                                       int ixx=*(dst+1)-48+1;
+                                       if (ixx >= argc) {
+                                               var='\0';
+                                       } else {
+                                               var = argv[ixx];
+                                       }
+                               }
+                               break;
+
+               }
+               if (var) {
+                       /* a single character construction was found, and
+                        * already handled in the case statement */
+                       src=dst+2;
+               } else {
+                       /* Looks like an environment variable */
+                       char delim_hold;
+                       int num_skip_chars=0;
+                       int dstlen = strlen(dst);
+                       /* Is this a ${foo} type variable? */
+                       if (dstlen >=2 && *(dst+1) == '{') {
+                               src=strchr(dst+1, '}');
+                               num_skip_chars=1;
+                       } else {
+                               src=dst+1;
+                               while(isalnum(*src) || *src=='_') src++;
+                       }
+                       if (src == NULL) {
+                               src = dst+dstlen;
+                       }
+                       delim_hold=*src;
+                       *src='\0';  /* temporary */
+                       var = getenv(dst + 1 + num_skip_chars);
+                       *src=delim_hold;
+                       src += num_skip_chars;
+               }
+               if (var == NULL) {
+                       /* Seems we got an un-expandable variable.  So delete it. */
+                       var = "";
+               }
+               {
+                       int subst_len = strlen(var);
+                       int trail_len = strlen(src);
+                       if (dst+subst_len+trail_len >= command+BUFSIZ) {
+                               bb_error_msg(out_of_space);
+                               return FALSE;
+                       }
+                       /* Move stuff to the end of the string to accommodate
+                        * filling the created gap with the new stuff */
+                       memmove(dst+subst_len, src, trail_len+1);
+                       /* Now copy in the new stuff */
+                       memcpy(dst, var, subst_len);
+                       src = dst+subst_len;
+               }
+       }
+
+       return TRUE;
+}
+
+/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
+   line). If a valid command is found, command_ptr is set to point to
+   the beginning of the next command (if the original command had more
+   then one job associated with it) or NULL if no more commands are
+   present. */
+static int parse_command(char **command_ptr, struct job *job, int *inbg)
+{
+       char *command;
+       char *return_command = NULL;
+       char *src, *buf;
+       int argc_l = 0;
+       int done = 0;
+       int argv_alloced;
+       int saw_quote = 0;
+       char quote = '\0';
+       int count;
+       struct child_prog *prog;
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+       int i;
+       char *chptr;
+#endif
+
+       /* skip leading white space */
+       while (**command_ptr && isspace(**command_ptr))
+               (*command_ptr)++;
+
+       /* this handles empty lines or leading '#' characters */
+       if (!**command_ptr || (**command_ptr == '#')) {
+               job->num_progs=0;
+               return 0;
+       }
+
+       *inbg = 0;
+       job->num_progs = 1;
+       job->progs = xmalloc(sizeof(*job->progs));
+
+       /* We set the argv elements to point inside of this string. The
+          memory is freed by free_job(). Allocate twice the original
+          length in case we need to quote every single character.
+
+          Getting clean memory relieves us of the task of NULL
+          terminating things and makes the rest of this look a bit
+          cleaner (though it is, admittedly, a tad less efficient) */
+       job->cmdbuf = command = xcalloc(2*strlen(*command_ptr) + 1, sizeof(char));
+       job->text = NULL;
+
+       prog = job->progs;
+       prog->num_redirects = 0;
+       prog->is_stopped = 0;
+       prog->family = job;
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+       prog->redirects = NULL;
+#endif
+
+       argv_alloced = 5;
+       prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
+       prog->argv[0] = job->cmdbuf;
+
+       buf = command;
+       src = *command_ptr;
+       while (*src && !done) {
+               if (quote == *src) {
+                       quote = '\0';
+               } else if (quote) {
+                       if (*src == '\\') {
+                               src++;
+                               if (!*src) {
+                                       bb_error_msg("character expected after \\");
+                                       free_job(job);
+                                       return 1;
+                               }
+
+                               /* in shell, "\'" should yield \' */
+                               if (*src != quote) {
+                                       *buf++ = '\\';
+                                       *buf++ = '\\';
+                               }
+                       } else if (*src == '*' || *src == '?' || *src == '[' ||
+                                          *src == ']') *buf++ = '\\';
+                       *buf++ = *src;
+               } else if (isspace(*src)) {
+                       if (*prog->argv[argc_l] || saw_quote) {
+                               buf++, argc_l++;
+                               /* +1 here leaves room for the NULL which ends argv */
+                               if ((argc_l + 1) == argv_alloced) {
+                                       argv_alloced += 5;
+                                       prog->argv = xrealloc(prog->argv,
+                                                                                 sizeof(*prog->argv) *
+                                                                                 argv_alloced);
+                               }
+                               prog->argv[argc_l] = buf;
+                               saw_quote = 0;
+                       }
+               } else
+                       switch (*src) {
+                       case '"':
+                       case '\'':
+                               quote = *src;
+                               saw_quote = 1;
+                               break;
+
+                       case '#':                       /* comment */
+                               if (*(src-1)== '$')
+                                       *buf++ = *src;
+                               else
+                                       done = 1;
+                               break;
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+                       case '>':                       /* redirects */
+                       case '<':
+                               i = prog->num_redirects++;
+                               prog->redirects = xrealloc(prog->redirects,
+                                                                                         sizeof(*prog->redirects) *
+                                                                                         (i + 1));
+
+                               prog->redirects[i].fd = -1;
+                               if (buf != prog->argv[argc_l]) {
+                                       /* the stuff before this character may be the file number
+                                          being redirected */
+                                       prog->redirects[i].fd =
+                                               strtol(prog->argv[argc_l], &chptr, 10);
+
+                                       if (*chptr && *prog->argv[argc_l]) {
+                                               buf++, argc_l++;
+                                               prog->argv[argc_l] = buf;
+                                       }
+                               }
+
+                               if (prog->redirects[i].fd == -1) {
+                                       if (*src == '>')
+                                               prog->redirects[i].fd = 1;
+                                       else
+                                               prog->redirects[i].fd = 0;
+                               }
+
+                               if (*src++ == '>') {
+                                       if (*src == '>')
+                                               prog->redirects[i].type =
+                                                       REDIRECT_APPEND, src++;
+                                       else
+                                               prog->redirects[i].type = REDIRECT_OVERWRITE;
+                               } else {
+                                       prog->redirects[i].type = REDIRECT_INPUT;
+                               }
+
+                               /* This isn't POSIX sh compliant. Oh well. */
+                               chptr = src;
+                               while (isspace(*chptr))
+                                       chptr++;
+
+                               if (!*chptr) {
+                                       bb_error_msg("file name expected after %c", *(src-1));
+                                       free_job(job);
+                                       job->num_progs=0;
+                                       return 1;
+                               }
+
+                               prog->redirects[i].filename = buf;
+                               while (*chptr && !isspace(*chptr))
+                                       *buf++ = *chptr++;
+
+                               src = chptr - 1;        /* we src++ later */
+                               prog->argv[argc_l] = ++buf;
+                               break;
+
+                       case '|':                       /* pipe */
+                               /* finish this command */
+                               if (*prog->argv[argc_l] || saw_quote)
+                                       argc_l++;
+                               if (!argc_l) {
+                                       bb_error_msg("empty command in pipe");
+                                       free_job(job);
+                                       job->num_progs=0;
+                                       return 1;
+                               }
+                               prog->argv[argc_l] = NULL;
+
+                               /* and start the next */
+                               job->num_progs++;
+                               job->progs = xrealloc(job->progs,
+                                                                         sizeof(*job->progs) * job->num_progs);
+                               prog = job->progs + (job->num_progs - 1);
+                               prog->num_redirects = 0;
+                               prog->redirects = NULL;
+                               prog->is_stopped = 0;
+                               prog->family = job;
+                               argc_l = 0;
+
+                               argv_alloced = 5;
+                               prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
+                               prog->argv[0] = ++buf;
+
+                               src++;
+                               while (*src && isspace(*src))
+                                       src++;
+
+                               if (!*src) {
+                                       bb_error_msg("empty command in pipe");
+                                       free_job(job);
+                                       job->num_progs=0;
+                                       return 1;
+                               }
+                               src--;                  /* we'll ++ it at the end of the loop */
+
+                               break;
+#endif
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+                       case '&':                       /* background */
+                               *inbg = 1;
+#endif
+                       case ';':                       /* multiple commands */
+                               done = 1;
+                               return_command = *command_ptr + (src - *command_ptr) + 1;
+                               break;
+
+                       case '\\':
+                               src++;
+                               if (!*src) {
+                                       bb_error_msg("character expected after \\");
+                                       free_job(job);
+                                       return 1;
+                               }
+                               if (*src == '*' || *src == '[' || *src == ']'
+                                       || *src == '?') *buf++ = '\\';
+                               /* fallthrough */
+                       default:
+                               *buf++ = *src;
+                       }
+
+               src++;
+       }
+
+       if (*prog->argv[argc_l] || saw_quote) {
+               argc_l++;
+       }
+       if (!argc_l) {
+               free_job(job);
+               return 0;
+       }
+       prog->argv[argc_l] = NULL;
+
+       if (!return_command) {
+               job->text = xmalloc(strlen(*command_ptr) + 1);
+               strcpy(job->text, *command_ptr);
+       } else {
+               /* This leaves any trailing spaces, which is a bit sloppy */
+               count = return_command - *command_ptr;
+               job->text = xmalloc(count + 1);
+               strncpy(job->text, *command_ptr, count);
+               job->text[count] = '\0';
+       }
+
+       *command_ptr = return_command;
+
+       return 0;
+}
+
+/* Run the child_prog, no matter what kind of command it uses.
+ */
+static int pseudo_exec(struct child_prog *child)
+{
+       struct built_in_command *x;
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       char *name;
+#endif
+
+       /* Check if the command matches any of the non-forking builtins.
+        * Depending on context, this might be redundant.  But it's
+        * easier to waste a few CPU cycles than it is to figure out
+        * if this is one of those cases.
+        */
+       for (x = bltins; x->cmd; x++) {
+               if (strcmp(child->argv[0], x->cmd) == 0 ) {
+                       _exit(x->function(child));
+               }
+       }
+
+       /* Check if the command matches any of the forking builtins. */
+       for (x = bltins_forking; x->cmd; x++) {
+               if (strcmp(child->argv[0], x->cmd) == 0) {
+                       bb_applet_name=x->cmd;
+                       _exit (x->function(child));
+               }
+       }
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       /* Check if the command matches any busybox internal
+        * commands ("applets") here.  Following discussions from
+        * November 2000 on busybox@busybox.net, don't use
+        * bb_get_last_path_component().  This way explicit (with
+        * slashes) filenames will never be interpreted as an
+        * applet, just like with builtins.  This way the user can
+        * override an applet with an explicit filename reference.
+        * The only downside to this change is that an explicit
+        * /bin/foo invocation will fork and exec /bin/foo, even if
+        * /bin/foo is a symlink to busybox.
+        */
+       name = child->argv[0];
+
+       {
+           char** argv_l=child->argv;
+           int argc_l;
+           for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
+           optind = 1;
+           run_applet_by_name(name, argc_l, child->argv);
+       }
+#endif
+
+       execvp(child->argv[0], child->argv);
+
+       /* Do not use bb_perror_msg_and_die() here, since we must not
+        * call exit() but should call _exit() instead */
+       fprintf(stderr, "%s: %m\n", child->argv[0]);
+       _exit(EXIT_FAILURE);
+}
+
+static void insert_job(struct job *newjob, int inbg)
+{
+       struct job *thejob;
+       struct jobset *j_list=newjob->job_list;
+
+       /* find the ID for thejob to use */
+       newjob->jobid = 1;
+       for (thejob = j_list->head; thejob; thejob = thejob->next)
+               if (thejob->jobid >= newjob->jobid)
+                       newjob->jobid = thejob->jobid + 1;
+
+       /* add thejob to the list of running jobs */
+       if (!j_list->head) {
+               thejob = j_list->head = xmalloc(sizeof(*thejob));
+       } else {
+               for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */;
+               thejob->next = xmalloc(sizeof(*thejob));
+               thejob = thejob->next;
+       }
+
+       *thejob = *newjob;   /* physically copy the struct job */
+       thejob->next = NULL;
+       thejob->running_progs = thejob->num_progs;
+       thejob->stopped_progs = 0;
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+       if (inbg) {
+               /* we don't wait for background thejobs to return -- append it
+                  to the list of backgrounded thejobs and leave it alone */
+               printf("[%d] %d\n", thejob->jobid,
+                          newjob->progs[newjob->num_progs - 1].pid);
+               last_jobid = newjob->jobid;
+               last_bg_pid=newjob->progs[newjob->num_progs - 1].pid;
+       } else {
+               newjob->job_list->fg = thejob;
+
+               /* move the new process group into the foreground */
+               /* suppress messages when run from /linuxrc mag@sysgo.de */
+               if (tcsetpgrp(shell_terminal, newjob->pgrp) && errno != ENOTTY)
+                       bb_perror_msg("tcsetpgrp");
+       }
+#endif
+}
+
+static int run_command(struct job *newjob, int inbg, int outpipe[2])
+{
+       /* struct job *thejob; */
+       int i;
+       int nextin, nextout;
+       int pipefds[2];                         /* pipefd[0] is for reading */
+       struct built_in_command *x;
+       struct child_prog *child;
+
+       nextin = 0, nextout = 1;
+       for (i = 0; i < newjob->num_progs; i++) {
+               child = & (newjob->progs[i]);
+
+               if ((i + 1) < newjob->num_progs) {
+                       if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe");
+                       nextout = pipefds[1];
+               } else {
+                       if (outpipe[1]!=-1) {
+                               nextout = outpipe[1];
+                       } else {
+                               nextout = 1;
+                       }
+               }
+
+
+               /* Check if the command matches any non-forking builtins,
+                * but only if this is a simple command.
+                * Non-forking builtins within pipes have to fork anyway,
+                * and are handled in pseudo_exec.  "echo foo | read bar"
+                * is doomed to failure, and doesn't work on bash, either.
+                */
+               if (newjob->num_progs == 1) {
+                       for (x = bltins; x->cmd; x++) {
+                               if (strcmp(child->argv[0], x->cmd) == 0 ) {
+                                       int rcode;
+                                       int squirrel[] = {-1, -1, -1};
+                                       setup_redirects(child, squirrel);
+                                       rcode = x->function(child);
+                                       restore_redirects(squirrel);
+                                       return rcode;
+                               }
+                       }
+               }
+
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+               if (!(child->pid = fork()))
+#else
+               if (!(child->pid = vfork()))
+#endif
+               {
+                       /* Set the handling for job control signals back to the default.  */
+                       signal(SIGINT, SIG_DFL);
+                       signal(SIGQUIT, SIG_DFL);
+                       signal(SIGTSTP, SIG_DFL);
+                       signal(SIGTTIN, SIG_DFL);
+                       signal(SIGTTOU, SIG_DFL);
+                       signal(SIGCHLD, SIG_DFL);
+
+                       close_all();
+
+                       if (outpipe[1]!=-1) {
+                               close(outpipe[0]);
+                       }
+                       if (nextin != 0) {
+                               dup2(nextin, 0);
+                               close(nextin);
+                       }
+
+                       if (nextout != 1) {
+                               dup2(nextout, 1);
+                               dup2(nextout, 2);  /* Really? */
+                               close(nextout);
+                               close(pipefds[0]);
+                       }
+
+                       /* explicit redirects override pipes */
+                       setup_redirects(child,NULL);
+
+                       pseudo_exec(child);
+               }
+               if (outpipe[1]!=-1) {
+                       close(outpipe[1]);
+               }
+
+               /* put our child in the process group whose leader is the
+                  first process in this pipe */
+               setpgid(child->pid, newjob->progs[0].pid);
+               if (nextin != 0)
+                       close(nextin);
+               if (nextout != 1)
+                       close(nextout);
+
+               /* If there isn't another process, nextin is garbage
+                  but it doesn't matter */
+               nextin = pipefds[0];
+       }
+
+       newjob->pgrp = newjob->progs[0].pid;
+
+       insert_job(newjob, inbg);
+
+       return 0;
+}
+
+static int busy_loop(FILE * input)
+{
+       char *command;
+       char *next_command = NULL;
+       struct job newjob;
+       int i;
+       int inbg;
+       int status;
+#ifdef CONFIG_LASH_JOB_CONTROL
+       pid_t  parent_pgrp;
+       /* save current owner of TTY so we can restore it on exit */
+       parent_pgrp = tcgetpgrp(shell_terminal);
+#endif
+       newjob.job_list = &job_list;
+       newjob.job_context = DEFAULT_CONTEXT;
+
+       command = (char *) xcalloc(BUFSIZ, sizeof(char));
+
+       while (1) {
+               if (!job_list.fg) {
+                       /* no job is in the foreground */
+
+                       /* see if any background processes have exited */
+                       checkjobs(&job_list);
+
+                       if (!next_command) {
+                               if (get_command(input, command))
+                                       break;
+                               next_command = command;
+                       }
+
+                       if (! expand_arguments(next_command)) {
+                               free(command);
+                               command = (char *) xcalloc(BUFSIZ, sizeof(char));
+                               next_command = NULL;
+                               continue;
+                       }
+
+                       if (!parse_command(&next_command, &newjob, &inbg) &&
+                               newjob.num_progs) {
+                               int pipefds[2] = {-1,-1};
+                               debug_printf( "job=%p fed to run_command by busy_loop()'\n",
+                                               &newjob);
+                               run_command(&newjob, inbg, pipefds);
+                       }
+                       else {
+                               free(command);
+                               command = (char *) xcalloc(BUFSIZ, sizeof(char));
+                               next_command = NULL;
+                       }
+               } else {
+                       /* a job is running in the foreground; wait for it */
+                       i = 0;
+                       while (!job_list.fg->progs[i].pid ||
+                                  job_list.fg->progs[i].is_stopped == 1) i++;
+
+                       if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED)<0) {
+                               if (errno != ECHILD) {
+                                       bb_perror_msg_and_die("waitpid(%d)",job_list.fg->progs[i].pid);
+                               }
+                       }
+
+                       if (WIFEXITED(status) || WIFSIGNALED(status)) {
+                               /* the child exited */
+                               job_list.fg->running_progs--;
+                               job_list.fg->progs[i].pid = 0;
+
+                               last_return_code=WEXITSTATUS(status);
+
+                               if (!job_list.fg->running_progs) {
+                                       /* child exited */
+                                       remove_job(&job_list, job_list.fg);
+                                       job_list.fg = NULL;
+                               }
+                       }
+#ifdef CONFIG_LASH_JOB_CONTROL
+                       else {
+                               /* the child was stopped */
+                               job_list.fg->stopped_progs++;
+                               job_list.fg->progs[i].is_stopped = 1;
+
+                               if (job_list.fg->stopped_progs == job_list.fg->running_progs) {
+                                       printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid,
+                                                  "Stopped", job_list.fg->text);
+                                       job_list.fg = NULL;
+                               }
+                       }
+
+                       if (!job_list.fg) {
+                               /* move the shell to the foreground */
+                               /* suppress messages when run from /linuxrc mag@sysgo.de */
+                               if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY)
+                                       bb_perror_msg("tcsetpgrp");
+                       }
+#endif
+               }
+       }
+       free(command);
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+       /* return controlling TTY back to parent process group before exiting */
+       if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY)
+               bb_perror_msg("tcsetpgrp");
+#endif
+
+       /* return exit status if called with "-c" */
+       if (input == NULL && WIFEXITED(status))
+               return WEXITSTATUS(status);
+
+       return 0;
+}
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+void free_memory(void)
+{
+       if (cwd && cwd!=bb_msg_unknown) {
+               free((char*)cwd);
+       }
+       if (local_pending_command)
+               free(local_pending_command);
+
+       if (job_list.fg && !job_list.fg->running_progs) {
+               remove_job(&job_list, job_list.fg);
+       }
+}
+#endif
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+/* Make sure we have a controlling tty.  If we get started under a job
+ * aware app (like bash for example), make sure we are now in charge so
+ * we don't fight over who gets the foreground */
+static void setup_job_control(void)
+{
+       int status;
+       pid_t shell_pgrp;
+
+       /* Loop until we are in the foreground.  */
+       while ((status = tcgetpgrp (shell_terminal)) >= 0) {
+               if (status == (shell_pgrp = getpgrp ())) {
+                       break;
+               }
+               kill (- shell_pgrp, SIGTTIN);
+       }
+
+       /* Ignore interactive and job-control signals.  */
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_IGN);
+       signal(SIGTSTP, SIG_IGN);
+       signal(SIGTTIN, SIG_IGN);
+       signal(SIGTTOU, SIG_IGN);
+       signal(SIGCHLD, SIG_IGN);
+
+       /* Put ourselves in our own process group.  */
+       setsid();
+       shell_pgrp = getpid ();
+       setpgid (shell_pgrp, shell_pgrp);
+
+       /* Grab control of the terminal.  */
+       tcsetpgrp(shell_terminal, shell_pgrp);
+}
+#else
+static inline void setup_job_control(void)
+{
+}
+#endif
+
+int lash_main(int argc_l, char **argv_l)
+{
+       int opt, interactive=FALSE;
+       FILE *input = stdin;
+       argc = argc_l;
+       argv = argv_l;
+
+       /* These variables need re-initializing when recursing */
+       last_jobid = 0;
+       local_pending_command = NULL;
+       close_me_head = NULL;
+       job_list.head = NULL;
+       job_list.fg = NULL;
+       last_return_code=1;
+
+       if (argv[0] && argv[0][0] == '-') {
+               FILE *prof_input;
+               prof_input = fopen("/etc/profile", "r");
+               if (prof_input) {
+                       int tmp_fd = fileno(prof_input);
+                       mark_open(tmp_fd);
+                       /* Now run the file */
+                       busy_loop(prof_input);
+                       fclose(prof_input);
+                       mark_closed(tmp_fd);
+               }
+       }
+
+       while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
+               switch (opt) {
+                       case 'c':
+                               input = NULL;
+                               if (local_pending_command != 0)
+                                       bb_error_msg_and_die("multiple -c arguments");
+                               local_pending_command = bb_xstrdup(argv[optind]);
+                               optind++;
+                               argv = argv+optind;
+                               break;
+                       case 'i':
+                               interactive = TRUE;
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+       /* A shell is interactive if the `-i' flag was given, or if all of
+        * the following conditions are met:
+        *        no -c command
+        *    no arguments remaining or the -s flag given
+        *    standard input is a terminal
+        *    standard output is a terminal
+        *    Refer to Posix.2, the description of the `sh' utility. */
+       if (argv[optind]==NULL && input==stdin &&
+                       isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
+               interactive=TRUE;
+       }
+       setup_job_control();
+       if (interactive==TRUE) {
+               //printf( "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
+               /* Looks like they want an interactive shell */
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+               printf( "\n\n" BB_BANNER " Built-in shell (lash)\n");
+               printf( "Enter 'help' for a list of built-in commands.\n\n");
+#endif
+       } else if (local_pending_command==NULL) {
+               //printf( "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
+               input = bb_xfopen(argv[optind], "r");
+               mark_open(fileno(input));  /* be lazy, never mark this closed */
+       }
+
+       /* initialize the cwd -- this is never freed...*/
+       cwd = xgetcwd(0);
+       if (!cwd)
+               cwd = bb_msg_unknown;
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       atexit(free_memory);
+#endif
+
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+       cmdedit_set_initial_prompt();
+#else
+       PS1 = NULL;
+#endif
+
+       return (busy_loop(input));
+}
diff --git a/busybox/shell/msh.c b/busybox/shell/msh.c
new file mode 100644 (file)
index 0000000..2fb0df7
--- /dev/null
@@ -0,0 +1,5487 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Minix shell port for busybox
+ *
+ * This version of the Minix shell was adapted for use in busybox
+ * by Erik Andersen <andersen@codepoet.org>
+ *
+ * - backtick expansion did not work properly
+ *   Jonas Holmberg <jonas.holmberg@axis.com>
+ *   Robert Schwebel <r.schwebel@pengutronix.de>
+ *   Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "cmdedit.h"
+#include "busybox.h"
+
+
+/* Conditional use of "register" keyword */
+#define REGISTER register
+
+
+/*#define MSHDEBUG 1*/
+
+#ifdef MSHDEBUG
+int mshdbg = 0;
+
+#define DBGPRINTF(x)   if(mshdbg>0)printf x
+#define DBGPRINTF0(x)  if(mshdbg>0)printf x
+#define DBGPRINTF1(x)  if(mshdbg>1)printf x
+#define DBGPRINTF2(x)  if(mshdbg>2)printf x
+#define DBGPRINTF3(x)  if(mshdbg>3)printf x
+#define DBGPRINTF4(x)  if(mshdbg>4)printf x
+#define DBGPRINTF5(x)  if(mshdbg>5)printf x
+#define DBGPRINTF6(x)  if(mshdbg>6)printf x
+#define DBGPRINTF7(x)  if(mshdbg>7)printf x
+#define DBGPRINTF8(x)  if(mshdbg>8)printf x
+#define DBGPRINTF9(x)  if(mshdbg>9)printf x
+
+int mshdbg_rc = 0;
+
+#define RCPRINTF(x)            if(mshdbg_rc)printf x
+
+#else
+
+#define DBGPRINTF(x)
+#define DBGPRINTF0(x)
+#define DBGPRINTF1(x)
+#define DBGPRINTF2(x)
+#define DBGPRINTF3(x)
+#define DBGPRINTF4(x)
+#define DBGPRINTF5(x)
+#define DBGPRINTF6(x)
+#define DBGPRINTF7(x)
+#define DBGPRINTF8(x)
+#define DBGPRINTF9(x)
+
+#define RCPRINTF(x)
+
+#endif                                                 /* MSHDEBUG */
+
+
+/* -------- sh.h -------- */
+/*
+ * shell
+ */
+
+#define        LINELIM   2100
+#define        NPUSH     8                             /* limit to input nesting */
+
+#undef NOFILE
+#define        NOFILE    20                    /* Number of open files */
+#define        NUFILE    10                    /* Number of user-accessible files */
+#define        FDBASE    10                    /* First file usable by Shell */
+
+/*
+ * values returned by wait
+ */
+#define        WAITSIG(s)  ((s)&0177)
+#define        WAITVAL(s)  (((s)>>8)&0377)
+#define        WAITCORE(s) (((s)&0200)!=0)
+
+/*
+ * library and system definitions
+ */
+typedef void xint;                             /* base type of jmp_buf, for not broken compilers */
+
+/*
+ * shell components
+ */
+
+#define        QUOTE   0200
+
+#define        NOBLOCK ((struct op *)NULL)
+#define        NOWORD  ((char *)NULL)
+#define        NOWORDS ((char **)NULL)
+#define        NOPIPE  ((int *)NULL)
+
+/*
+ * Description of a command or an operation on commands.
+ * Might eventually use a union.
+ */
+struct op {
+       int type;                                       /* operation type, see below */
+       char **words;                           /* arguments to a command */
+       struct ioword **ioact;          /* IO actions (eg, < > >>) */
+       struct op *left;
+       struct op *right;
+       char *str;                                      /* identifier for case and for */
+};
+
+#define        TCOM    1                               /* command */
+#define        TPAREN  2                               /* (c-list) */
+#define        TPIPE   3                               /* a | b */
+#define        TLIST   4                               /* a [&;] b */
+#define        TOR             5                               /* || */
+#define        TAND    6                               /* && */
+#define        TFOR    7
+#define        TDO             8
+#define        TCASE   9
+#define        TIF             10
+#define        TWHILE  11
+#define        TUNTIL  12
+#define        TELIF   13
+#define        TPAT    14                              /* pattern in case */
+#define        TBRACE  15                              /* {c-list} */
+#define        TASYNC  16                              /* c & */
+/* Added to support "." file expansion */
+#define        TDOT    17
+
+/* Strings for names to make debug easier */
+char *T_CMD_NAMES[] = {
+       "PLACEHOLDER",
+       "TCOM",
+       "TPAREN",
+       "TPIPE",
+       "TLIST",
+       "TOR",
+       "TAND",
+       "TFOR",
+       "TDO",
+       "TCASE",
+       "TIF",
+       "TWHILE",
+       "TUNTIL",
+       "TELIF",
+       "TPAT",
+       "TBRACE",
+       "TASYNC",
+       "TDOT",
+};
+
+
+/*
+ * actions determining the environment of a process
+ */
+#define        BIT(i)  (1<<(i))
+#define        FEXEC   BIT(0)                  /* execute without forking */
+
+#if 0                                                  /* Original value */
+#define AREASIZE       (65000)
+#else
+#define AREASIZE       (90000)
+#endif
+
+/*
+ * flags to control evaluation of words
+ */
+#define        DOSUB    1                              /* interpret $, `, and quotes */
+#define        DOBLANK  2                              /* perform blank interpretation */
+#define        DOGLOB   4                              /* interpret [?* */
+#define        DOKEY    8                              /* move words with `=' to 2nd arg. list */
+#define        DOTRIM   16                             /* trim resulting string */
+
+#define        DOALL   (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
+
+
+/* PROTOTYPES */
+static int newfile(char *s);
+static char *findeq(char *cp);
+static char *cclass(char *p, int sub);
+static void initarea(void);
+extern int msh_main(int argc, char **argv);
+
+
+struct brkcon {
+       jmp_buf brkpt;
+       struct brkcon *nextlev;
+};
+
+
+/*
+ * redirection
+ */
+struct ioword {
+       short io_unit;                          /* unit affected */
+       short io_flag;                          /* action (below) */
+       char *io_name;                          /* file name */
+};
+
+#define        IOREAD   1                              /* < */
+#define        IOHERE   2                              /* << (here file) */
+#define        IOWRITE  4                              /* > */
+#define        IOCAT    8                              /* >> */
+#define        IOXHERE  16                             /* ${}, ` in << */
+#define        IODUP    32                             /* >&digit */
+#define        IOCLOSE  64                             /* >&- */
+
+#define        IODEFAULT (-1)                  /* token for default IO unit */
+
+
+
+/*
+ * parsing & execution environment
+ */
+static struct env {
+       char *linep;
+       struct io *iobase;
+       struct io *iop;
+       xint *errpt;                            /* void * */
+       int iofd;
+       struct env *oenv;
+} e;
+
+/*
+ * flags:
+ * -e: quit on error
+ * -k: look for name=value everywhere on command line
+ * -n: no execution
+ * -t: exit after reading and executing one command
+ * -v: echo as read
+ * -x: trace
+ * -u: unset variables net diagnostic
+ */
+static char *flag;
+
+static char *null;                             /* null value for variable */
+static int intr;                               /* interrupt pending */
+
+static char *trap[_NSIG + 1];
+static char ourtrap[_NSIG + 1];
+static int trapset;                            /* trap pending */
+
+static int heedint;                            /* heed interrupt signals */
+
+static int yynerrs;                            /* yacc */
+
+static char line[LINELIM];
+static char *elinep;
+
+
+/*
+ * other functions
+ */
+static int (*inbuilt(char *s)) (struct op *);
+
+static char *rexecve(char *c, char **v, char **envp);
+static char *space(int n);
+static char *strsave(char *s, int a);
+static char *evalstr(char *cp, int f);
+static char *putn(int n);
+static char *itoa(int n);
+static char *unquote(char *as);
+static struct var *lookup(char *n);
+static int rlookup(char *n);
+static struct wdblock *glob(char *cp, struct wdblock *wb);
+static int my_getc(int ec);
+static int subgetc(int ec, int quoted);
+static char **makenv(int all, struct wdblock *wb);
+static char **eval(char **ap, int f);
+static int setstatus(int s);
+static int waitfor(int lastpid, int canintr);
+
+static void onintr(int s);             /* SIGINT handler */
+
+static int newenv(int f);
+static void quitenv(void);
+static void err(char *s);
+static int anys(char *s1, char *s2);
+static int any(int c, char *s);
+static void next(int f);
+static void setdash(void);
+static void onecommand(void);
+static void runtrap(int i);
+static int gmatch(char *s, char *p);
+
+
+/*
+ * error handling
+ */
+static void leave(void);               /* abort shell (or fail in subshell) */
+static void fail(void);                        /* fail but return to process next command */
+static void warn(char *s);
+static void sig(int i);                        /* default signal handler */
+
+
+
+/* -------- area stuff -------- */
+
+#define        REGSIZE   sizeof(struct region)
+#define GROWBY   (256)
+/* #define     SHRINKBY   (64) */
+#undef SHRINKBY
+#define FREE     (32767)
+#define BUSY     (0)
+#define        ALIGN     (sizeof(int)-1)
+
+
+struct region {
+       struct region *next;
+       int area;
+};
+
+
+
+/* -------- grammar stuff -------- */
+typedef union {
+       char *cp;
+       char **wp;
+       int i;
+       struct op *o;
+} YYSTYPE;
+
+#define        WORD    256
+#define        LOGAND  257
+#define        LOGOR   258
+#define        BREAK   259
+#define        IF              260
+#define        THEN    261
+#define        ELSE    262
+#define        ELIF    263
+#define        FI              264
+#define        CASE    265
+#define        ESAC    266
+#define        FOR             267
+#define        WHILE   268
+#define        UNTIL   269
+#define        DO              270
+#define        DONE    271
+#define        IN              272
+/* Added for "." file expansion */
+#define        DOT             273
+
+#define        YYERRCODE 300
+
+/* flags to yylex */
+#define        CONTIN  01                              /* skip new lines to complete command */
+
+#define        SYNTAXERR       zzerr()
+
+static struct op *pipeline(int cf);
+static struct op *andor(void);
+static struct op *c_list(void);
+static int synio(int cf);
+static void musthave(int c, int cf);
+static struct op *simple(void);
+static struct op *nested(int type, int mark);
+static struct op *command(int cf);
+static struct op *dogroup(int onlydone);
+static struct op *thenpart(void);
+static struct op *elsepart(void);
+static struct op *caselist(void);
+static struct op *casepart(void);
+static char **pattern(void);
+static char **wordlist(void);
+static struct op *list(struct op *t1, struct op *t2);
+static struct op *block(int type, struct op *t1, struct op *t2, char **wp);
+static struct op *newtp(void);
+static struct op *namelist(struct op *t);
+static char **copyw(void);
+static void word(char *cp);
+static struct ioword **copyio(void);
+static struct ioword *io(int u, int f, char *cp);
+static void zzerr(void);
+static void yyerror(char *s);
+static int yylex(int cf);
+static int collect(int c, int c1);
+static int dual(int c);
+static void diag(int ec);
+static char *tree(unsigned size);
+
+/* -------- var.h -------- */
+
+struct var {
+       char *value;
+       char *name;
+       struct var *next;
+       char status;
+};
+
+#define        COPYV   1                               /* flag to setval, suggesting copy */
+#define        RONLY   01                              /* variable is read-only */
+#define        EXPORT  02                              /* variable is to be exported */
+#define        GETCELL 04                              /* name & value space was got with getcell */
+
+static int yyparse(void);
+static struct var *lookup(char *n);
+static void setval(struct var *vp, char *val);
+static void nameval(struct var *vp, char *val, char *name);
+static void export(struct var *vp);
+static void ronly(struct var *vp);
+static int isassign(char *s);
+static int checkname(char *cp);
+static int assign(char *s, int cf);
+static void putvlist(int f, int out);
+static int eqname(char *n1, char *n2);
+
+static int execute(struct op *t, int *pin, int *pout, int act);
+
+
+/* -------- io.h -------- */
+/* io buffer */
+struct iobuf {
+       unsigned id;                            /* buffer id */
+       char buf[512];                          /* buffer */
+       char *bufp;                                     /* pointer into buffer */
+       char *ebufp;                            /* pointer to end of buffer */
+};
+
+/* possible arguments to an IO function */
+struct ioarg {
+       char *aword;
+       char **awordlist;
+       int afile;                                      /* file descriptor */
+       unsigned afid;                          /* buffer id */
+       long afpos;                                     /* file position */
+       struct iobuf *afbuf;            /* buffer for this file */
+};
+
+//static struct ioarg ioargstack[NPUSH];
+#define AFID_NOBUF     (~0)
+#define AFID_ID                0
+
+/* an input generator's state */
+struct io {
+       int (*iofn) (struct ioarg *, struct io *);
+       struct ioarg *argp;
+       int peekc;
+       char prev;                                      /* previous character read by readc() */
+       char nlcount;                           /* for `'s */
+       char xchar;                                     /* for `'s */
+       char task;                                      /* reason for pushed IO */
+};
+
+//static    struct  io  iostack[NPUSH];
+#define        XOTHER  0                               /* none of the below */
+#define        XDOLL   1                               /* expanding ${} */
+#define        XGRAVE  2                               /* expanding `'s */
+#define        XIO     3                                       /* file IO */
+
+/* in substitution */
+#define        INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL)
+
+
+/*
+ * input generators for IO structure
+ */
+static int nlchar(struct ioarg *ap);
+static int strchar(struct ioarg *ap);
+static int qstrchar(struct ioarg *ap);
+static int filechar(struct ioarg *ap);
+static int herechar(struct ioarg *ap);
+static int linechar(struct ioarg *ap);
+static int gravechar(struct ioarg *ap, struct io *iop);
+static int qgravechar(struct ioarg *ap, struct io *iop);
+static int dolchar(struct ioarg *ap);
+static int wdchar(struct ioarg *ap);
+static void scraphere(void);
+static void freehere(int area);
+static void gethere(void);
+static void markhere(char *s, struct ioword *iop);
+static int herein(char *hname, int xdoll);
+static int run(struct ioarg *argp, int (*f) (struct ioarg *));
+
+
+/*
+ * IO functions
+ */
+static int eofc(void);
+static int readc(void);
+static void unget(int c);
+static void ioecho(int c);
+static void prs(char *s);
+static void prn(unsigned u);
+static void closef(int i);
+static void closeall(void);
+
+
+/*
+ * IO control
+ */
+static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
+static int remap(int fd);
+static int openpipe(int *pv);
+static void closepipe(int *pv);
+static struct io *setbase(struct io *ip);
+
+#define        PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(&temparg,(gen)))
+#define        RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen)))
+
+/* -------- word.h -------- */
+
+#define        NSTART  16                              /* default number of words to allow for initially */
+
+struct wdblock {
+       short w_bsize;
+       short w_nword;
+       /* bounds are arbitrary */
+       char *w_words[1];
+};
+
+static struct wdblock *addword(char *wd, struct wdblock *wb);
+static struct wdblock *newword(int nw);
+static char **getwords(struct wdblock *wb);
+
+/* -------- area.h -------- */
+
+/*
+ * storage allocation
+ */
+static char *getcell(unsigned nbytes);
+static void garbage(void);
+static void setarea(char *cp, int a);
+static int getarea(char *cp);
+static void freearea(int a);
+static void freecell(char *cp);
+static int areanum;                            /* current allocation area */
+
+#define        NEW(type)   (type *)getcell(sizeof(type))
+#define        DELETE(obj)     freecell((char *)obj)
+
+
+/* -------- misc stuff -------- */
+
+static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp);
+static int iosetup(struct ioword *iop, int pipein, int pipeout);
+static void echo(char **wp);
+static struct op **find1case(struct op *t, char *w);
+static struct op *findcase(struct op *t, char *w);
+static void brkset(struct brkcon *bc);
+static int dolabel(struct op *t);
+static int dohelp(struct op *t);
+static int dochdir(struct op *t);
+static int doshift(struct op *t);
+static int dologin(struct op *t);
+static int doumask(struct op *t);
+static int doexec(struct op *t);
+static int dodot(struct op *t);
+static int dowait(struct op *t);
+static int doread(struct op *t);
+static int doeval(struct op *t);
+static int dotrap(struct op *t);
+static int getsig(char *s);
+static void setsig(int n, sighandler_t f);
+static int getn(char *as);
+static int dobreak(struct op *t);
+static int docontinue(struct op *t);
+static int brkcontin(char *cp, int val);
+static int doexit(struct op *t);
+static int doexport(struct op *t);
+static int doreadonly(struct op *t);
+static void rdexp(char **wp, void (*f) (struct var *), int key);
+static void badid(char *s);
+static int doset(struct op *t);
+static void varput(char *s, int out);
+static int dotimes(struct op *t);
+static int expand(char *cp, struct wdblock **wbp, int f);
+static char *blank(int f);
+static int dollar(int quoted);
+static int grave(int quoted);
+static void globname(char *we, char *pp);
+static char *generate(char *start1, char *end1, char *middle, char *end);
+static int anyspcl(struct wdblock *wb);
+static int xstrcmp(char *p1, char *p2);
+static void glob0(char *a0, unsigned int a1, int a2,
+                                 int (*a3) (char *, char *));
+static void glob1(char *base, char *lim);
+static void glob2(char *i, char *j);
+static void glob3(char *i, char *j, char *k);
+static void readhere(char **name, char *s, int ec);
+static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
+static int xxchar(struct ioarg *ap);
+
+struct here {
+       char *h_tag;
+       int h_dosub;
+       struct ioword *h_iop;
+       struct here *h_next;
+};
+
+static char *signame[] = {
+       "Signal 0",
+       "Hangup",
+       (char *) NULL,                          /* interrupt */
+       "Quit",
+       "Illegal instruction",
+       "Trace/BPT trap",
+       "Abort",
+       "Bus error",
+       "Floating Point Exception",
+       "Killed",
+       "SIGUSR1",
+       "SIGSEGV",
+       "SIGUSR2",
+       (char *) NULL,                          /* broken pipe */
+       "Alarm clock",
+       "Terminated",
+};
+
+#define        NSIGNAL (sizeof(signame)/sizeof(signame[0]))
+
+struct res {
+       char *r_name;
+       int r_val;
+};
+static struct res restab[] = {
+       {"for", FOR},
+       {"case", CASE},
+       {"esac", ESAC},
+       {"while", WHILE},
+       {"do", DO},
+       {"done", DONE},
+       {"if", IF},
+       {"in", IN},
+       {"then", THEN},
+       {"else", ELSE},
+       {"elif", ELIF},
+       {"until", UNTIL},
+       {"fi", FI},
+       {";;", BREAK},
+       {"||", LOGOR},
+       {"&&", LOGAND},
+       {"{", '{'},
+       {"}", '}'},
+       {".", DOT},
+       {0, 0},
+};
+
+
+struct builtincmd {
+       const char *name;
+       int (*builtinfunc) (struct op * t);
+};
+static const struct builtincmd builtincmds[] = {
+       {".", dodot},
+       {":", dolabel},
+       {"break", dobreak},
+       {"cd", dochdir},
+       {"continue", docontinue},
+       {"eval", doeval},
+       {"exec", doexec},
+       {"exit", doexit},
+       {"export", doexport},
+       {"help", dohelp},
+       {"login", dologin},
+       {"newgrp", dologin},
+       {"read", doread},
+       {"readonly", doreadonly},
+       {"set", doset},
+       {"shift", doshift},
+       {"times", dotimes},
+       {"trap", dotrap},
+       {"umask", doumask},
+       {"wait", dowait},
+       {0, 0}
+};
+
+struct op *scantree(struct op *);
+static struct op *dowholefile(int, int);
+
+/* Globals */
+extern char **environ;                 /* environment pointer */
+
+static char **dolv;
+static int dolc;
+static int exstat;
+static char gflg;
+static int interactive = 0;            /* Is this an interactive shell */
+static int execflg;
+static int multiline;                  /* \n changed to ; */
+static struct op *outtree;             /* result from parser */
+static xint *failpt;
+static xint *errpt;
+static struct brkcon *brklist;
+static int isbreak;
+static struct wdblock *wdlist;
+static struct wdblock *iolist;
+static char *trap[_NSIG + 1];
+static char ourtrap[_NSIG + 1];
+static int trapset;                            /* trap pending */
+static int yynerrs;                            /* yacc */
+static char line[LINELIM];
+
+#ifdef MSHDEBUG
+static struct var *mshdbg_var;
+#endif
+static struct var *vlist;              /* dictionary */
+static struct var *homedir;            /* home directory */
+static struct var *prompt;             /* main prompt */
+static struct var *cprompt;            /* continuation prompt */
+static struct var *path;               /* search path for commands */
+static struct var *shell;              /* shell to interpret command files */
+static struct var *ifs;                        /* field separators */
+
+static int areanum;                            /* current allocation area */
+static int intr;
+static int inparse;
+static char flags['z' - 'a' + 1];
+static char *flag = flags - 'a';
+static char *null = "";
+static int heedint = 1;
+static void (*qflag) (int) = SIG_IGN;
+static int startl;
+static int peeksym;
+static int nlseen;
+static int iounit = IODEFAULT;
+static YYSTYPE yylval;
+static char *elinep = line + sizeof(line) - 5;
+
+static struct ioarg temparg = { 0, 0, 0, AFID_NOBUF, 0 };      /* temporary for PUSHIO */
+static struct ioarg ioargstack[NPUSH];
+static struct io iostack[NPUSH];
+static struct iobuf sharedbuf = { AFID_NOBUF };
+static struct iobuf mainbuf = { AFID_NOBUF };
+static unsigned bufid = AFID_ID;       /* buffer id counter */
+
+static struct here *inhere;            /* list of hear docs while parsing */
+static struct here *acthere;   /* list of active here documents */
+static struct region *areabot; /* bottom of area */
+static struct region *areatop; /* top of area */
+static struct region *areanxt; /* starting point of scan */
+static void *brktop;
+static void *brkaddr;
+
+static struct env e = {
+       line,                                           /* linep:  char ptr */
+       iostack,                                        /* iobase:  struct io ptr */
+       iostack - 1,                            /* iop:  struct io ptr */
+       (xint *) NULL,                          /* errpt:  void ptr for errors? */
+       FDBASE,                                         /* iofd:  file desc  */
+       (struct env *) NULL                     /* oenv:  struct env ptr */
+};
+
+#ifdef MSHDEBUG
+void print_t(struct op *t)
+{
+       DBGPRINTF(("T: t=0x%x, type %s, words=0x%x, IOword=0x%x\n", t,
+                          T_CMD_NAMES[t->type], t->words, t->ioact));
+
+       if (t->words) {
+               DBGPRINTF(("T: W1: %s", t->words[0]));
+       }
+
+       return;
+}
+
+void print_tree(struct op *head)
+{
+       if (head == NULL) {
+               DBGPRINTF(("PRINT_TREE: no tree\n"));
+               return;
+       }
+
+       DBGPRINTF(("NODE: 0x%x,  left 0x%x, right 0x%x\n", head, head->left,
+                          head->right));
+
+       if (head->left)
+               print_tree(head->left);
+
+       if (head->right)
+               print_tree(head->right);
+
+       return;
+}
+#endif                                                 /* MSHDEBUG */
+
+
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+static char *current_prompt;
+#endif
+
+/* -------- sh.c -------- */
+/*
+ * shell
+ */
+
+
+extern int msh_main(int argc, char **argv)
+{
+       REGISTER int f;
+       REGISTER char *s;
+       int cflag;
+       char *name, **ap;
+       int (*iof) (struct ioarg *);
+
+       DBGPRINTF(("MSH_MAIN: argc %d, environ 0x%x\n", argc, environ));
+
+       initarea();
+       if ((ap = environ) != NULL) {
+               while (*ap)
+                       assign(*ap++, !COPYV);
+               for (ap = environ; *ap;)
+                       export(lookup(*ap++));
+       }
+       closeall();
+       areanum = 1;
+
+       shell = lookup("SHELL");
+       if (shell->value == null)
+               setval(shell, (char *)DEFAULT_SHELL);
+       export(shell);
+
+       homedir = lookup("HOME");
+       if (homedir->value == null)
+               setval(homedir, "/");
+       export(homedir);
+
+       setval(lookup("$"), putn(getpid()));
+
+       path = lookup("PATH");
+       if (path->value == null) {
+               if (geteuid() == 0)
+                       setval(path, "/sbin:/bin:/usr/sbin:/usr/bin");
+               else
+                       setval(path, "/bin:/usr/bin");
+       }
+       export(path);
+
+       ifs = lookup("IFS");
+       if (ifs->value == null)
+               setval(ifs, " \t\n");
+
+#ifdef MSHDEBUG
+       mshdbg_var = lookup("MSHDEBUG");
+       if (mshdbg_var->value == null)
+               setval(mshdbg_var, "0");
+#endif
+
+
+       prompt = lookup("PS1");
+#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
+       if (prompt->value == null)
+#endif
+               setval(prompt, "$ ");
+       if (geteuid() == 0) {
+               setval(prompt, "# ");
+               prompt->status &= ~EXPORT;
+       }
+       cprompt = lookup("PS2");
+#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
+       if (cprompt->value == null)
+#endif
+               setval(cprompt, "> ");
+
+       iof = filechar;
+       cflag = 0;
+       name = *argv++;
+       if (--argc >= 1) {
+               if (argv[0][0] == '-' && argv[0][1] != '\0') {
+                       for (s = argv[0] + 1; *s; s++)
+                               switch (*s) {
+                               case 'c':
+                                       prompt->status &= ~EXPORT;
+                                       cprompt->status &= ~EXPORT;
+                                       setval(prompt, "");
+                                       setval(cprompt, "");
+                                       cflag = 1;
+                                       if (--argc > 0)
+                                               PUSHIO(aword, *++argv, iof = nlchar);
+                                       break;
+
+                               case 'q':
+                                       qflag = SIG_DFL;
+                                       break;
+
+                               case 's':
+                                       /* standard input */
+                                       break;
+
+                               case 't':
+                                       prompt->status &= ~EXPORT;
+                                       setval(prompt, "");
+                                       iof = linechar;
+                                       break;
+
+                               case 'i':
+                                       interactive++;
+                               default:
+                                       if (*s >= 'a' && *s <= 'z')
+                                               flag[(int) *s]++;
+                               }
+               } else {
+                       argv--;
+                       argc++;
+               }
+
+               if (iof == filechar && --argc > 0) {
+                       setval(prompt, "");
+                       setval(cprompt, "");
+                       prompt->status &= ~EXPORT;
+                       cprompt->status &= ~EXPORT;
+
+/* Shell is non-interactive, activate printf-based debug */
+#ifdef MSHDEBUG
+                       mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0');
+                       if (mshdbg < 0)
+                               mshdbg = 0;
+#endif
+                       DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
+
+                       if (newfile(name = *++argv))
+                               exit(1);                /* Exit on error */
+               }
+       }
+
+       setdash();
+
+       /* This won't be true if PUSHIO has been called, say from newfile() above */
+       if (e.iop < iostack) {
+               PUSHIO(afile, 0, iof);
+               if (isatty(0) && isatty(1) && !cflag) {
+                       interactive++;
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+#ifdef MSHDEBUG
+                       printf("\n\n" BB_BANNER " Built-in shell (msh with debug)\n");
+#else
+                       printf("\n\n" BB_BANNER " Built-in shell (msh)\n");
+#endif
+                       printf("Enter 'help' for a list of built-in commands.\n\n");
+#endif
+               }
+       }
+
+       signal(SIGQUIT, qflag);
+       if (name && name[0] == '-') {
+               interactive++;
+               if ((f = open(".profile", 0)) >= 0)
+                       next(remap(f));
+               if ((f = open("/etc/profile", 0)) >= 0)
+                       next(remap(f));
+       }
+       if (interactive)
+               signal(SIGTERM, sig);
+
+       if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+               signal(SIGINT, onintr);
+       dolv = argv;
+       dolc = argc;
+       dolv[0] = name;
+       if (dolc > 1) {
+               for (ap = ++argv; --argc > 0;) {
+                       if (assign(*ap = *argv++, !COPYV)) {
+                               dolc--;                 /* keyword */
+                       } else {
+                               ap++;
+                       }
+               }
+       }
+       setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
+
+       DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop 0x%x, iostack 0x%x\n", interactive, e.iop, iostack));
+
+       for (;;) {
+               if (interactive && e.iop <= iostack) {
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+                       current_prompt = prompt->value;
+#else
+                       prs(prompt->value);
+#endif
+               }
+               onecommand();
+               /* Ensure that getenv("PATH") stays current */
+               setenv("PATH", path->value, 1);
+       }
+
+       DBGPRINTF(("MSH_MAIN: returning.\n"));
+}
+
+static void setdash()
+{
+       REGISTER char *cp;
+       REGISTER int c;
+       char m['z' - 'a' + 1];
+
+       cp = m;
+       for (c = 'a'; c <= 'z'; c++)
+               if (flag[(int) c])
+                       *cp++ = c;
+       *cp = 0;
+       setval(lookup("-"), m);
+}
+
+static int newfile(s)
+REGISTER char *s;
+{
+       REGISTER int f;
+
+       DBGPRINTF7(("NEWFILE: opening %s\n", s));
+
+       if (strcmp(s, "-") != 0) {
+               DBGPRINTF(("NEWFILE: s is %s\n", s));
+               f = open(s, 0);
+               if (f < 0) {
+                       prs(s);
+                       err(": cannot open");
+                       return (1);
+               }
+       } else
+               f = 0;
+
+       next(remap(f));
+       return (0);
+}
+
+
+struct op *scantree(head)
+struct op *head;
+{
+       struct op *dotnode;
+
+       if (head == NULL)
+               return (NULL);
+
+       if (head->left != NULL) {
+               dotnode = scantree(head->left);
+               if (dotnode)
+                       return (dotnode);
+       }
+
+       if (head->right != NULL) {
+               dotnode = scantree(head->right);
+               if (dotnode)
+                       return (dotnode);
+       }
+
+       if (head->words == NULL)
+               return (NULL);
+
+       DBGPRINTF5(("SCANTREE: checking node 0x%x\n", head));
+
+       if ((head->type != TDOT) && (strcmp(".", head->words[0]) == 0)) {
+               DBGPRINTF5(("SCANTREE: dot found in node 0x%x\n", head));
+               return (head);
+       }
+
+       return (NULL);
+}
+
+
+static void onecommand()
+{
+       REGISTER int i;
+       jmp_buf m1;
+
+       DBGPRINTF(("ONECOMMAND: enter, outtree=0x%x\n", outtree));
+
+       while (e.oenv)
+               quitenv();
+
+       areanum = 1;
+       freehere(areanum);
+       freearea(areanum);
+       garbage();
+       wdlist = 0;
+       iolist = 0;
+       e.errpt = 0;
+       e.linep = line;
+       yynerrs = 0;
+       multiline = 0;
+       inparse = 1;
+       intr = 0;
+       execflg = 0;
+
+       setjmp(failpt = m1);            /* Bruce Evans' fix */
+       if (setjmp(failpt = m1) || yyparse() || intr) {
+
+               DBGPRINTF(("ONECOMMAND: this is not good.\n"));
+
+               while (e.oenv)
+                       quitenv();
+               scraphere();
+               if (!interactive && intr)
+                       leave();
+               inparse = 0;
+               intr = 0;
+               return;
+       }
+
+       inparse = 0;
+       brklist = 0;
+       intr = 0;
+       execflg = 0;
+
+       if (!flag['n']) {
+               DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=0x%x\n",
+                                  outtree));
+               execute(outtree, NOPIPE, NOPIPE, 0);
+       }
+
+       if (!interactive && intr) {
+               execflg = 0;
+               leave();
+       }
+
+       if ((i = trapset) != 0) {
+               trapset = 0;
+               runtrap(i);
+       }
+}
+
+static void fail()
+{
+       longjmp(failpt, 1);
+       /* NOTREACHED */
+}
+
+static void leave()
+{
+       DBGPRINTF(("LEAVE: leave called!\n"));
+
+       if (execflg)
+               fail();
+       scraphere();
+       freehere(1);
+       runtrap(0);
+       _exit(exstat);
+       /* NOTREACHED */
+}
+
+static void warn(s)
+REGISTER char *s;
+{
+       if (*s) {
+               prs(s);
+               exstat = -1;
+       }
+       prs("\n");
+       if (flag['e'])
+               leave();
+}
+
+static void err(s)
+char *s;
+{
+       warn(s);
+       if (flag['n'])
+               return;
+       if (!interactive)
+               leave();
+       if (e.errpt)
+               longjmp(e.errpt, 1);
+       closeall();
+       e.iop = e.iobase = iostack;
+}
+
+static int newenv(f)
+int f;
+{
+       REGISTER struct env *ep;
+
+       DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f));
+
+       if (f) {
+               quitenv();
+               return (1);
+       }
+
+       ep = (struct env *) space(sizeof(*ep));
+       if (ep == NULL) {
+               while (e.oenv)
+                       quitenv();
+               fail();
+       }
+       *ep = e;
+       e.oenv = ep;
+       e.errpt = errpt;
+
+       return (0);
+}
+
+static void quitenv()
+{
+       REGISTER struct env *ep;
+       REGISTER int fd;
+
+       DBGPRINTF(("QUITENV: e.oenv=0x%x\n", e.oenv));
+
+       if ((ep = e.oenv) != NULL) {
+               fd = e.iofd;
+               e = *ep;
+               /* should close `'d files */
+               DELETE(ep);
+               while (--fd >= e.iofd)
+                       close(fd);
+       }
+}
+
+/*
+ * Is any character from s1 in s2?
+ */
+static int anys(s1, s2)
+REGISTER char *s1, *s2;
+{
+       while (*s1)
+               if (any(*s1++, s2))
+                       return (1);
+       return (0);
+}
+
+/*
+ * Is character c in s?
+ */
+static int any(c, s)
+REGISTER int c;
+REGISTER char *s;
+{
+       while (*s)
+               if (*s++ == c)
+                       return (1);
+       return (0);
+}
+
+static char *putn(n)
+REGISTER int n;
+{
+       return (itoa(n));
+}
+
+static char *itoa(n)
+REGISTER int n;
+{
+       static char s[20];
+
+       snprintf(s, sizeof(s), "%u", n);
+       return (s);
+}
+
+
+static void next(int f)
+{
+       PUSHIO(afile, f, filechar);
+}
+
+static void onintr(s)
+int s;                                                 /* ANSI C requires a parameter */
+{
+       signal(SIGINT, onintr);
+       intr = 1;
+       if (interactive) {
+               if (inparse) {
+                       prs("\n");
+                       fail();
+               }
+       } else if (heedint) {
+               execflg = 0;
+               leave();
+       }
+}
+
+static char *space(n)
+int n;
+{
+       REGISTER char *cp;
+
+       if ((cp = getcell(n)) == 0)
+               err("out of string space");
+       return (cp);
+}
+
+static char *strsave(s, a)
+REGISTER char *s;
+int a;
+{
+       REGISTER char *cp, *xp;
+
+       if ((cp = space(strlen(s) + 1)) != NULL) {
+               setarea((char *) cp, a);
+               for (xp = cp; (*xp++ = *s++) != '\0';);
+               return (cp);
+       }
+       return ("");
+}
+
+/*
+ * trap handling
+ */
+static void sig(i)
+REGISTER int i;
+{
+       trapset = i;
+       signal(i, sig);
+}
+
+static void runtrap(i)
+int i;
+{
+       char *trapstr;
+
+       if ((trapstr = trap[i]) == NULL)
+               return;
+
+       if (i == 0)
+               trap[i] = 0;
+
+       RUN(aword, trapstr, nlchar);
+}
+
+/* -------- var.c -------- */
+
+/*
+ * Find the given name in the dictionary
+ * and return its value.  If the name was
+ * not previously there, enter it now and
+ * return a null value.
+ */
+static struct var *lookup(n)
+REGISTER char *n;
+{
+       REGISTER struct var *vp;
+       REGISTER char *cp;
+       REGISTER int c;
+       static struct var dummy;
+
+       if (isdigit(*n)) {
+               dummy.name = n;
+               for (c = 0; isdigit(*n) && c < 1000; n++)
+                       c = c * 10 + *n - '0';
+               dummy.status = RONLY;
+               dummy.value = c <= dolc ? dolv[c] : null;
+               return (&dummy);
+       }
+       for (vp = vlist; vp; vp = vp->next)
+               if (eqname(vp->name, n))
+                       return (vp);
+       cp = findeq(n);
+       vp = (struct var *) space(sizeof(*vp));
+       if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) {
+               dummy.name = dummy.value = "";
+               return (&dummy);
+       }
+       for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++);
+       if (*cp == 0)
+               *cp = '=';
+       *++cp = 0;
+       setarea((char *) vp, 0);
+       setarea((char *) vp->name, 0);
+       vp->value = null;
+       vp->next = vlist;
+       vp->status = GETCELL;
+       vlist = vp;
+       return (vp);
+}
+
+/*
+ * give variable at `vp' the value `val'.
+ */
+static void setval(vp, val)
+struct var *vp;
+char *val;
+{
+       nameval(vp, val, (char *) NULL);
+}
+
+/*
+ * if name is not NULL, it must be
+ * a prefix of the space `val',
+ * and end with `='.
+ * this is all so that exporting
+ * values is reasonably painless.
+ */
+static void nameval(vp, val, name)
+REGISTER struct var *vp;
+char *val, *name;
+{
+       REGISTER char *cp, *xp;
+       char *nv;
+       int fl;
+
+       if (vp->status & RONLY) {
+               for (xp = vp->name; *xp && *xp != '=';)
+                       putc(*xp++, stderr);
+               err(" is read-only");
+               return;
+       }
+       fl = 0;
+       if (name == NULL) {
+               xp = space(strlen(vp->name) + strlen(val) + 2);
+               if (xp == 0)
+                       return;
+               /* make string:  name=value */
+               setarea((char *) xp, 0);
+               name = xp;
+               for (cp = vp->name; (*xp = *cp++) && *xp != '='; xp++);
+               if (*xp++ == 0)
+                       xp[-1] = '=';
+               nv = xp;
+               for (cp = val; (*xp++ = *cp++) != '\0';);
+               val = nv;
+               fl = GETCELL;
+       }
+       if (vp->status & GETCELL)
+               freecell(vp->name);             /* form new string `name=value' */
+       vp->name = name;
+       vp->value = val;
+       vp->status |= fl;
+}
+
+static void export(vp)
+struct var *vp;
+{
+       vp->status |= EXPORT;
+}
+
+static void ronly(vp)
+struct var *vp;
+{
+       if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
+               vp->status |= RONLY;
+}
+
+static int isassign(s)
+REGISTER char *s;
+{
+       DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
+
+       if (!isalpha((int) *s) && *s != '_')
+               return (0);
+       for (; *s != '='; s++)
+               if (*s == 0 || (!isalnum(*s) && *s != '_'))
+                       return (0);
+
+       return (1);
+}
+
+static int assign(s, cf)
+REGISTER char *s;
+int cf;
+{
+       REGISTER char *cp;
+       struct var *vp;
+
+       DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
+
+       if (!isalpha(*s) && *s != '_')
+               return (0);
+       for (cp = s; *cp != '='; cp++)
+               if (*cp == 0 || (!isalnum(*cp) && *cp != '_'))
+                       return (0);
+       vp = lookup(s);
+       nameval(vp, ++cp, cf == COPYV ? (char *) NULL : s);
+       if (cf != COPYV)
+               vp->status &= ~GETCELL;
+       return (1);
+}
+
+static int checkname(cp)
+REGISTER char *cp;
+{
+       DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
+
+       if (!isalpha(*cp++) && *(cp - 1) != '_')
+               return (0);
+       while (*cp)
+               if (!isalnum(*cp++) && *(cp - 1) != '_')
+                       return (0);
+       return (1);
+}
+
+static void putvlist(f, out)
+REGISTER int f, out;
+{
+       REGISTER struct var *vp;
+
+       for (vp = vlist; vp; vp = vp->next)
+               if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
+                       if (vp->status & EXPORT)
+                               write(out, "export ", 7);
+                       if (vp->status & RONLY)
+                               write(out, "readonly ", 9);
+                       write(out, vp->name, (int) (findeq(vp->name) - vp->name));
+                       write(out, "\n", 1);
+               }
+}
+
+static int eqname(n1, n2)
+REGISTER char *n1, *n2;
+{
+       for (; *n1 != '=' && *n1 != 0; n1++)
+               if (*n2++ != *n1)
+                       return (0);
+       return (*n2 == 0 || *n2 == '=');
+}
+
+static char *findeq(cp)
+REGISTER char *cp;
+{
+       while (*cp != '\0' && *cp != '=')
+               cp++;
+       return (cp);
+}
+
+/* -------- gmatch.c -------- */
+/*
+ * int gmatch(string, pattern)
+ * char *string, *pattern;
+ *
+ * Match a pattern as in sh(1).
+ */
+
+#define        CMASK   0377
+#define        QUOTE   0200
+#define        QMASK   (CMASK&~QUOTE)
+#define        NOT     '!'                                     /* might use ^ */
+
+static int gmatch(s, p)
+REGISTER char *s, *p;
+{
+       REGISTER int sc, pc;
+
+       if (s == NULL || p == NULL)
+               return (0);
+       while ((pc = *p++ & CMASK) != '\0') {
+               sc = *s++ & QMASK;
+               switch (pc) {
+               case '[':
+                       if ((p = cclass(p, sc)) == NULL)
+                               return (0);
+                       break;
+
+               case '?':
+                       if (sc == 0)
+                               return (0);
+                       break;
+
+               case '*':
+                       s--;
+                       do {
+                               if (*p == '\0' || gmatch(s, p))
+                                       return (1);
+                       } while (*s++ != '\0');
+                       return (0);
+
+               default:
+                       if (sc != (pc & ~QUOTE))
+                               return (0);
+               }
+       }
+       return (*s == 0);
+}
+
+static char *cclass(p, sub)
+REGISTER char *p;
+REGISTER int sub;
+{
+       REGISTER int c, d, not, found;
+
+       if ((not = *p == NOT) != 0)
+               p++;
+       found = not;
+       do {
+               if (*p == '\0')
+                       return ((char *) NULL);
+               c = *p & CMASK;
+               if (p[1] == '-' && p[2] != ']') {
+                       d = p[2] & CMASK;
+                       p++;
+               } else
+                       d = c;
+               if (c == sub || (c <= sub && sub <= d))
+                       found = !not;
+       } while (*++p != ']');
+       return (found ? p + 1 : (char *) NULL);
+}
+
+
+/* -------- area.c -------- */
+
+/*
+ * All memory between (char *)areabot and (char *)(areatop+1) is
+ * exclusively administered by the area management routines.
+ * It is assumed that sbrk() and brk() manipulate the high end.
+ */
+
+#define sbrk(X) ({ void * __q = (void *)-1; if (brkaddr + (int)(X) < brktop) { __q = brkaddr; brkaddr+=(int)(X); } __q;})
+
+static void initarea()
+{
+       brkaddr = malloc(AREASIZE);
+       brktop = brkaddr + AREASIZE;
+
+       while ((int) sbrk(0) & ALIGN)
+               sbrk(1);
+       areabot = (struct region *) sbrk(REGSIZE);
+
+       areabot->next = areabot;
+       areabot->area = BUSY;
+       areatop = areabot;
+       areanxt = areabot;
+}
+
+char *getcell(nbytes)
+unsigned nbytes;
+{
+       REGISTER int nregio;
+       REGISTER struct region *p, *q;
+       REGISTER int i;
+
+       if (nbytes == 0) {
+               puts("getcell(0)");
+               abort();
+       }
+       /* silly and defeats the algorithm */
+       /*
+        * round upwards and add administration area
+        */
+       nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
+       for (p = areanxt;;) {
+               if (p->area > areanum) {
+                       /*
+                        * merge free cells
+                        */
+                       while ((q = p->next)->area > areanum && q != areanxt)
+                               p->next = q->next;
+                       /*
+                        * exit loop if cell big enough
+                        */
+                       if (q >= p + nregio)
+                               goto found;
+               }
+               p = p->next;
+               if (p == areanxt)
+                       break;
+       }
+       i = nregio >= GROWBY ? nregio : GROWBY;
+       p = (struct region *) sbrk(i * REGSIZE);
+       if (p == (struct region *) -1)
+               return ((char *) NULL);
+       p--;
+       if (p != areatop) {
+               puts("not contig");
+               abort();                                /* allocated areas are contiguous */
+       }
+       q = p + i;
+       p->next = q;
+       p->area = FREE;
+       q->next = areabot;
+       q->area = BUSY;
+       areatop = q;
+  found:
+       /*
+        * we found a FREE area big enough, pointed to by 'p', and up to 'q'
+        */
+       areanxt = p + nregio;
+       if (areanxt < q) {
+               /*
+                * split into requested area and rest
+                */
+               if (areanxt + 1 > q) {
+                       puts("OOM");
+                       abort();                        /* insufficient space left for admin */
+               }
+               areanxt->next = q;
+               areanxt->area = FREE;
+               p->next = areanxt;
+       }
+       p->area = areanum;
+       return ((char *) (p + 1));
+}
+
+static void freecell(cp)
+char *cp;
+{
+       REGISTER struct region *p;
+
+       if ((p = (struct region *) cp) != NULL) {
+               p--;
+               if (p < areanxt)
+                       areanxt = p;
+               p->area = FREE;
+       }
+}
+
+static void freearea(a)
+REGISTER int a;
+{
+       REGISTER struct region *p, *top;
+
+       top = areatop;
+       for (p = areabot; p != top; p = p->next)
+               if (p->area >= a)
+                       p->area = FREE;
+}
+
+static void setarea(cp, a)
+char *cp;
+int a;
+{
+       REGISTER struct region *p;
+
+       if ((p = (struct region *) cp) != NULL)
+               (p - 1)->area = a;
+}
+
+int getarea(cp)
+char *cp;
+{
+       return ((struct region *) cp - 1)->area;
+}
+
+static void garbage()
+{
+       REGISTER struct region *p, *q, *top;
+
+       top = areatop;
+       for (p = areabot; p != top; p = p->next) {
+               if (p->area > areanum) {
+                       while ((q = p->next)->area > areanum)
+                               p->next = q->next;
+                       areanxt = p;
+               }
+       }
+#ifdef SHRINKBY
+       if (areatop >= q + SHRINKBY && q->area > areanum) {
+               brk((char *) (q + 1));
+               q->next = areabot;
+               q->area = BUSY;
+               areatop = q;
+       }
+#endif
+}
+
+/* -------- csyn.c -------- */
+/*
+ * shell: syntax (C version)
+ */
+
+int yyparse()
+{
+       DBGPRINTF7(("YYPARSE: enter...\n"));
+
+       startl = 1;
+       peeksym = 0;
+       yynerrs = 0;
+       outtree = c_list();
+       musthave('\n', 0);
+       return (yynerrs != 0);
+}
+
+static struct op *pipeline(cf)
+int cf;
+{
+       REGISTER struct op *t, *p;
+       REGISTER int c;
+
+       DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf));
+
+       t = command(cf);
+
+       DBGPRINTF9(("PIPELINE: t=0x%x\n", t));
+
+       if (t != NULL) {
+               while ((c = yylex(0)) == '|') {
+                       if ((p = command(CONTIN)) == NULL) {
+                               DBGPRINTF8(("PIPELINE: error!\n"));
+                               SYNTAXERR;
+                       }
+
+                       if (t->type != TPAREN && t->type != TCOM) {
+                               /* shell statement */
+                               t = block(TPAREN, t, NOBLOCK, NOWORDS);
+                       }
+
+                       t = block(TPIPE, t, p, NOWORDS);
+               }
+               peeksym = c;
+       }
+
+       DBGPRINTF7(("PIPELINE: returning t=0x%x\n", t));
+       return (t);
+}
+
+static struct op *andor()
+{
+       REGISTER struct op *t, *p;
+       REGISTER int c;
+
+       DBGPRINTF7(("ANDOR: enter...\n"));
+
+       t = pipeline(0);
+
+       DBGPRINTF9(("ANDOR: t=0x%x\n", t));
+
+       if (t != NULL) {
+               while ((c = yylex(0)) == LOGAND || c == LOGOR) {
+                       if ((p = pipeline(CONTIN)) == NULL) {
+                               DBGPRINTF8(("ANDOR: error!\n"));
+                               SYNTAXERR;
+                       }
+
+                       t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS);
+               }                                               /* WHILE */
+
+               peeksym = c;
+       }
+
+       DBGPRINTF7(("ANDOR: returning t=0x%x\n", t));
+       return (t);
+}
+
+static struct op *c_list()
+{
+       REGISTER struct op *t, *p;
+       REGISTER int c;
+
+       DBGPRINTF7(("C_LIST: enter...\n"));
+
+       t = andor();
+
+       if (t != NULL) {
+               if ((peeksym = yylex(0)) == '&')
+                       t = block(TASYNC, t, NOBLOCK, NOWORDS);
+
+               while ((c = yylex(0)) == ';' || c == '&'
+                          || (multiline && c == '\n')) {
+
+                       if ((p = andor()) == NULL)
+                               return (t);
+
+                       if ((peeksym = yylex(0)) == '&')
+                               p = block(TASYNC, p, NOBLOCK, NOWORDS);
+
+                       t = list(t, p);
+               }                                               /* WHILE */
+
+               peeksym = c;
+       }
+       /* IF */
+       DBGPRINTF7(("C_LIST: returning t=0x%x\n", t));
+       return (t);
+}
+
+static int synio(cf)
+int cf;
+{
+       REGISTER struct ioword *iop;
+       REGISTER int i;
+       REGISTER int c;
+
+       DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf));
+
+       if ((c = yylex(cf)) != '<' && c != '>') {
+               peeksym = c;
+               return (0);
+       }
+
+       i = yylval.i;
+       musthave(WORD, 0);
+       iop = io(iounit, i, yylval.cp);
+       iounit = IODEFAULT;
+
+       if (i & IOHERE)
+               markhere(yylval.cp, iop);
+
+       DBGPRINTF7(("SYNIO: returning 1\n"));
+       return (1);
+}
+
+static void musthave(c, cf)
+int c, cf;
+{
+       if ((peeksym = yylex(cf)) != c) {
+               DBGPRINTF7(("MUSTHAVE: error!\n"));
+               SYNTAXERR;
+       }
+
+       peeksym = 0;
+}
+
+static struct op *simple()
+{
+       REGISTER struct op *t;
+
+       t = NULL;
+       for (;;) {
+               switch (peeksym = yylex(0)) {
+               case '<':
+               case '>':
+                       (void) synio(0);
+                       break;
+
+               case WORD:
+                       if (t == NULL) {
+                               t = newtp();
+                               t->type = TCOM;
+                       }
+                       peeksym = 0;
+                       word(yylval.cp);
+                       break;
+
+               default:
+                       return (t);
+               }
+       }
+}
+
+static struct op *nested(type, mark)
+int type, mark;
+{
+       REGISTER struct op *t;
+
+       DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark));
+
+       multiline++;
+       t = c_list();
+       musthave(mark, 0);
+       multiline--;
+       return (block(type, t, NOBLOCK, NOWORDS));
+}
+
+static struct op *command(cf)
+int cf;
+{
+       REGISTER struct op *t;
+       struct wdblock *iosave;
+       REGISTER int c;
+
+       DBGPRINTF(("COMMAND: enter, cf=%d\n", cf));
+
+       iosave = iolist;
+       iolist = NULL;
+
+       if (multiline)
+               cf |= CONTIN;
+
+       while (synio(cf))
+               cf = 0;
+
+       c = yylex(cf);
+
+       switch (c) {
+       default:
+               peeksym = c;
+               if ((t = simple()) == NULL) {
+                       if (iolist == NULL)
+                               return ((struct op *) NULL);
+                       t = newtp();
+                       t->type = TCOM;
+               }
+               break;
+
+       case '(':
+               t = nested(TPAREN, ')');
+               break;
+
+       case '{':
+               t = nested(TBRACE, '}');
+               break;
+
+       case FOR:
+               t = newtp();
+               t->type = TFOR;
+               musthave(WORD, 0);
+               startl = 1;
+               t->str = yylval.cp;
+               multiline++;
+               t->words = wordlist();
+               if ((c = yylex(0)) != '\n' && c != ';')
+                       peeksym = c;
+               t->left = dogroup(0);
+               multiline--;
+               break;
+
+       case WHILE:
+       case UNTIL:
+               multiline++;
+               t = newtp();
+               t->type = c == WHILE ? TWHILE : TUNTIL;
+               t->left = c_list();
+               t->right = dogroup(1);
+               t->words = NULL;
+               multiline--;
+               break;
+
+       case CASE:
+               t = newtp();
+               t->type = TCASE;
+               musthave(WORD, 0);
+               t->str = yylval.cp;
+               startl++;
+               multiline++;
+               musthave(IN, CONTIN);
+               startl++;
+
+               t->left = caselist();
+
+               musthave(ESAC, 0);
+               multiline--;
+               break;
+
+       case IF:
+               multiline++;
+               t = newtp();
+               t->type = TIF;
+               t->left = c_list();
+               t->right = thenpart();
+               musthave(FI, 0);
+               multiline--;
+               break;
+
+       case DOT:
+               t = newtp();
+               t->type = TDOT;
+
+               musthave(WORD, 0);              /* gets name of file */
+               DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp));
+
+               word(yylval.cp);                /* add word to wdlist */
+               word(NOWORD);                   /* terminate  wdlist */
+               t->words = copyw();             /* dup wdlist */
+               break;
+
+       }
+
+       while (synio(0));
+
+       t = namelist(t);
+       iolist = iosave;
+
+       DBGPRINTF(("COMMAND: returning 0x%x\n", t));
+
+       return (t);
+}
+
+static struct op *dowholefile(type, mark)
+int type;
+int mark;
+{
+       REGISTER struct op *t;
+
+       DBGPRINTF(("DOWHOLEFILE: enter, type=%d, mark=%d\n", type, mark));
+
+       multiline++;
+       t = c_list();
+       multiline--;
+       t = block(type, t, NOBLOCK, NOWORDS);
+       DBGPRINTF(("DOWHOLEFILE: return t=0x%x\n", t));
+       return (t);
+}
+
+static struct op *dogroup(onlydone)
+int onlydone;
+{
+       REGISTER int c;
+       REGISTER struct op *mylist;
+
+       c = yylex(CONTIN);
+       if (c == DONE && onlydone)
+               return ((struct op *) NULL);
+       if (c != DO)
+               SYNTAXERR;
+       mylist = c_list();
+       musthave(DONE, 0);
+       return (mylist);
+}
+
+static struct op *thenpart()
+{
+       REGISTER int c;
+       REGISTER struct op *t;
+
+       if ((c = yylex(0)) != THEN) {
+               peeksym = c;
+               return ((struct op *) NULL);
+       }
+       t = newtp();
+       t->type = 0;
+       t->left = c_list();
+       if (t->left == NULL)
+               SYNTAXERR;
+       t->right = elsepart();
+       return (t);
+}
+
+static struct op *elsepart()
+{
+       REGISTER int c;
+       REGISTER struct op *t;
+
+       switch (c = yylex(0)) {
+       case ELSE:
+               if ((t = c_list()) == NULL)
+                       SYNTAXERR;
+               return (t);
+
+       case ELIF:
+               t = newtp();
+               t->type = TELIF;
+               t->left = c_list();
+               t->right = thenpart();
+               return (t);
+
+       default:
+               peeksym = c;
+               return ((struct op *) NULL);
+       }
+}
+
+static struct op *caselist()
+{
+       REGISTER struct op *t;
+
+       t = NULL;
+       while ((peeksym = yylex(CONTIN)) != ESAC) {
+               DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym));
+               t = list(t, casepart());
+       }
+
+       DBGPRINTF(("CASELIST, returning t=0x%x\n", t));
+       return (t);
+}
+
+static struct op *casepart()
+{
+       REGISTER struct op *t;
+
+       DBGPRINTF7(("CASEPART: enter...\n"));
+
+       t = newtp();
+       t->type = TPAT;
+       t->words = pattern();
+       musthave(')', 0);
+       t->left = c_list();
+       if ((peeksym = yylex(CONTIN)) != ESAC)
+               musthave(BREAK, CONTIN);
+
+       DBGPRINTF7(("CASEPART: made newtp(TPAT, t=0x%x)\n", t));
+
+       return (t);
+}
+
+static char **pattern()
+{
+       REGISTER int c, cf;
+
+       cf = CONTIN;
+       do {
+               musthave(WORD, cf);
+               word(yylval.cp);
+               cf = 0;
+       } while ((c = yylex(0)) == '|');
+       peeksym = c;
+       word(NOWORD);
+
+       return (copyw());
+}
+
+static char **wordlist()
+{
+       REGISTER int c;
+
+       if ((c = yylex(0)) != IN) {
+               peeksym = c;
+               return ((char **) NULL);
+       }
+       startl = 0;
+       while ((c = yylex(0)) == WORD)
+               word(yylval.cp);
+       word(NOWORD);
+       peeksym = c;
+       return (copyw());
+}
+
+/*
+ * supporting functions
+ */
+static struct op *list(t1, t2)
+REGISTER struct op *t1, *t2;
+{
+       DBGPRINTF7(("LIST: enter, t1=0x%x, t2=0x%x\n", t1, t2));
+
+       if (t1 == NULL)
+               return (t2);
+       if (t2 == NULL)
+               return (t1);
+
+       return (block(TLIST, t1, t2, NOWORDS));
+}
+
+static struct op *block(type, t1, t2, wp)
+int type;
+struct op *t1, *t2;
+char **wp;
+{
+       REGISTER struct op *t;
+
+       DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type]));
+
+       t = newtp();
+       t->type = type;
+       t->left = t1;
+       t->right = t2;
+       t->words = wp;
+
+       DBGPRINTF7(("BLOCK: inserted 0x%x between 0x%x and 0x%x\n", t, t1,
+                               t2));
+
+       return (t);
+}
+
+/* See if given string is a shell multiline (FOR, IF, etc) */
+static int rlookup(n)
+REGISTER char *n;
+{
+       REGISTER struct res *rp;
+
+       DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n));
+
+       for (rp = restab; rp->r_name; rp++)
+               if (strcmp(rp->r_name, n) == 0) {
+                       DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val));
+                       return (rp->r_val);     /* Return numeric code for shell multiline */
+               }
+
+       DBGPRINTF7(("RLOOKUP: NO match, returning 0\n"));
+       return (0);                                     /* Not a shell multiline */
+}
+
+static struct op *newtp()
+{
+       REGISTER struct op *t;
+
+       t = (struct op *) tree(sizeof(*t));
+       t->type = 0;
+       t->words = NULL;
+       t->ioact = NULL;
+       t->left = NULL;
+       t->right = NULL;
+       t->str = NULL;
+
+       DBGPRINTF3(("NEWTP: allocated 0x%x\n", t));
+
+       return (t);
+}
+
+static struct op *namelist(t)
+REGISTER struct op *t;
+{
+
+       DBGPRINTF7(("NAMELIST: enter, t=0x%x, type %s, iolist=0x%x\n", t,
+                               T_CMD_NAMES[t->type], iolist));
+
+       if (iolist) {
+               iolist = addword((char *) NULL, iolist);
+               t->ioact = copyio();
+       } else
+               t->ioact = NULL;
+
+       if (t->type != TCOM) {
+               if (t->type != TPAREN && t->ioact != NULL) {
+                       t = block(TPAREN, t, NOBLOCK, NOWORDS);
+                       t->ioact = t->left->ioact;
+                       t->left->ioact = NULL;
+               }
+               return (t);
+       }
+
+       word(NOWORD);
+       t->words = copyw();
+
+
+       return (t);
+}
+
+static char **copyw()
+{
+       REGISTER char **wd;
+
+       wd = getwords(wdlist);
+       wdlist = 0;
+       return (wd);
+}
+
+static void word(cp)
+char *cp;
+{
+       wdlist = addword(cp, wdlist);
+}
+
+static struct ioword **copyio()
+{
+       REGISTER struct ioword **iop;
+
+       iop = (struct ioword **) getwords(iolist);
+       iolist = 0;
+       return (iop);
+}
+
+static struct ioword *io(u, f, cp)
+int u;
+int f;
+char *cp;
+{
+       REGISTER struct ioword *iop;
+
+       iop = (struct ioword *) tree(sizeof(*iop));
+       iop->io_unit = u;
+       iop->io_flag = f;
+       iop->io_name = cp;
+       iolist = addword((char *) iop, iolist);
+       return (iop);
+}
+
+static void zzerr()
+{
+       yyerror("syntax error");
+}
+
+static void yyerror(s)
+char *s;
+{
+       yynerrs++;
+       if (interactive && e.iop <= iostack) {
+               multiline = 0;
+               while (eofc() == 0 && yylex(0) != '\n');
+       }
+       err(s);
+       fail();
+}
+
+static int yylex(cf)
+int cf;
+{
+       REGISTER int c, c1;
+       int atstart;
+
+       if ((c = peeksym) > 0) {
+               peeksym = 0;
+               if (c == '\n')
+                       startl = 1;
+               return (c);
+       }
+
+
+       nlseen = 0;
+       atstart = startl;
+       startl = 0;
+       yylval.i = 0;
+       e.linep = line;
+
+/* MALAMO */
+       line[LINELIM - 1] = '\0';
+
+  loop:
+       while ((c = my_getc(0)) == ' ' || c == '\t')    /* Skip whitespace */
+               ;
+
+       switch (c) {
+       default:
+               if (any(c, "0123456789")) {
+                       unget(c1 = my_getc(0));
+                       if (c1 == '<' || c1 == '>') {
+                               iounit = c - '0';
+                               goto loop;
+                       }
+                       *e.linep++ = c;
+                       c = c1;
+               }
+               break;
+
+       case '#':                                       /* Comment, skip to next newline or End-of-string */
+               while ((c = my_getc(0)) != 0 && c != '\n');
+               unget(c);
+               goto loop;
+
+       case 0:
+               DBGPRINTF5(("YYLEX: return 0, c=%d\n", c));
+               return (c);
+
+       case '$':
+               DBGPRINTF9(("YYLEX: found $\n"));
+               *e.linep++ = c;
+               if ((c = my_getc(0)) == '{') {
+                       if ((c = collect(c, '}')) != '\0')
+                               return (c);
+                       goto pack;
+               }
+               break;
+
+       case '`':
+       case '\'':
+       case '"':
+               if ((c = collect(c, c)) != '\0')
+                       return (c);
+               goto pack;
+
+       case '|':
+       case '&':
+       case ';':
+               startl = 1;
+               /* If more chars process them, else return NULL char */
+               if ((c1 = dual(c)) != '\0')
+                       return (c1);
+               else
+                       return (c);
+
+       case '^':
+               startl = 1;
+               return ('|');
+       case '>':
+       case '<':
+               diag(c);
+               return (c);
+
+       case '\n':
+               nlseen++;
+               gethere();
+               startl = 1;
+               if (multiline || cf & CONTIN) {
+                       if (interactive && e.iop <= iostack) {
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+                               current_prompt = cprompt->value;
+#else
+                               prs(cprompt->value);
+#endif
+                       }
+                       if (cf & CONTIN)
+                               goto loop;
+               }
+               return (c);
+
+       case '(':
+       case ')':
+               startl = 1;
+               return (c);
+       }
+
+       unget(c);
+
+  pack:
+       while ((c = my_getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) {
+               if (e.linep >= elinep)
+                       err("word too long");
+               else
+                       *e.linep++ = c;
+       };
+
+       unget(c);
+
+       if (any(c, "\"'`$"))
+               goto loop;
+
+       *e.linep++ = '\0';
+
+       if (atstart && (c = rlookup(line)) != 0) {
+               startl = 1;
+               return (c);
+       }
+
+       yylval.cp = strsave(line, areanum);
+       return (WORD);
+}
+
+
+static int collect(c, c1)
+REGISTER int c, c1;
+{
+       char s[2];
+
+       DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1));
+
+       *e.linep++ = c;
+       while ((c = my_getc(c1)) != c1) {
+               if (c == 0) {
+                       unget(c);
+                       s[0] = c1;
+                       s[1] = 0;
+                       prs("no closing ");
+                       yyerror(s);
+                       return (YYERRCODE);
+               }
+               if (interactive && c == '\n' && e.iop <= iostack) {
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+                       current_prompt = cprompt->value;
+#else
+                       prs(cprompt->value);
+#endif
+               }
+               *e.linep++ = c;
+       }
+
+       *e.linep++ = c;
+
+       DBGPRINTF8(("COLLECT: return 0, line is %s\n", line));
+
+       return (0);
+}
+
+/* "multiline commands" helper func */
+/* see if next 2 chars form a shell multiline */
+static int dual(c)
+REGISTER int c;
+{
+       char s[3];
+       REGISTER char *cp = s;
+
+       DBGPRINTF8(("DUAL: enter, c=%d\n", c));
+
+       *cp++ = c;                                      /* c is the given "peek" char */
+       *cp++ = my_getc(0);                     /* get next char of input */
+       *cp = 0;                                        /* add EOS marker */
+
+       c = rlookup(s);                         /* see if 2 chars form a shell multiline */
+       if (c == 0)
+               unget(*--cp);                   /* String is not a shell multiline, put peek char back */
+
+       return (c);                                     /* String is multiline, return numeric multiline (restab) code */
+}
+
+static void diag(ec)
+REGISTER int ec;
+{
+       REGISTER int c;
+
+       DBGPRINTF8(("DIAG: enter, ec=%d\n", ec));
+
+       c = my_getc(0);
+       if (c == '>' || c == '<') {
+               if (c != ec)
+                       zzerr();
+               yylval.i = ec == '>' ? IOWRITE | IOCAT : IOHERE;
+               c = my_getc(0);
+       } else
+               yylval.i = ec == '>' ? IOWRITE : IOREAD;
+       if (c != '&' || yylval.i == IOHERE)
+               unget(c);
+       else
+               yylval.i |= IODUP;
+}
+
+static char *tree(size)
+unsigned size;
+{
+       REGISTER char *t;
+
+       if ((t = getcell(size)) == NULL) {
+               DBGPRINTF2(("TREE: getcell(%d) failed!\n", size));
+               prs("command line too complicated\n");
+               fail();
+               /* NOTREACHED */
+       }
+       return (t);
+}
+
+/* VARARGS1 */
+/* ARGSUSED */
+
+/* -------- exec.c -------- */
+
+/*
+ * execute tree
+ */
+
+
+static int execute(t, pin, pout, act)
+REGISTER struct op *t;
+int *pin, *pout;
+int act;
+{
+       REGISTER struct op *t1;
+       volatile int i, rv, a;
+       char *cp, **wp, **wp2;
+       struct var *vp;
+       struct op *outtree_save;
+       struct brkcon bc;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &wp;
+#endif
+
+       if (t == NULL) {
+               DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n"));
+               return (0);
+       }
+
+       DBGPRINTF(("EXECUTE: t=0x%x, t->type=%d (%s), t->words is %s\n", t,
+                          t->type, T_CMD_NAMES[t->type],
+                          ((t->words == NULL) ? "NULL" : t->words[0])));
+
+       rv = 0;
+       a = areanum++;
+       wp = (wp2 = t->words) != NULL
+               ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY)
+               : NULL;
+
+/* Hard to know how many words there are, be careful of garbage pointer values */
+/* They are likely to cause "PCI bus fault" errors */
+#if 0
+       DBGPRINTF(("EXECUTE: t->left=0x%x, t->right=0x%x, t->words[1] is %s\n",
+                          t->left, t->right,
+                          ((t->words[1] == NULL) ? "NULL" : t->words[1])));
+       DBGPRINTF7(("EXECUTE: t->words[2] is %s, t->words[3] is %s\n",
+                               ((t->words[2] == NULL) ? "NULL" : t->words[2]),
+                               ((t->words[3] == NULL) ? "NULL" : t->words[3])));
+#endif
+
+
+       switch (t->type) {
+       case TDOT:
+               DBGPRINTF3(("EXECUTE: TDOT\n"));
+
+               outtree_save = outtree;
+
+               newfile(evalstr(t->words[0], DOALL));
+
+               t->left = dowholefile(TLIST, 0);
+               t->right = NULL;
+
+               outtree = outtree_save;
+
+               if (t->left)
+                       rv = execute(t->left, pin, pout, 0);
+               if (t->right)
+                       rv = execute(t->right, pin, pout, 0);
+               break;
+
+       case TPAREN:
+               rv = execute(t->left, pin, pout, 0);
+               break;
+
+       case TCOM:
+               {
+                       rv = forkexec(t, pin, pout, act, wp);
+               }
+               break;
+
+       case TPIPE:
+               {
+                       int pv[2];
+
+                       if ((rv = openpipe(pv)) < 0)
+                               break;
+                       pv[0] = remap(pv[0]);
+                       pv[1] = remap(pv[1]);
+                       (void) execute(t->left, pin, pv, 0);
+                       rv = execute(t->right, pv, pout, 0);
+               }
+               break;
+
+       case TLIST:
+               (void) execute(t->left, pin, pout, 0);
+               rv = execute(t->right, pin, pout, 0);
+               break;
+
+       case TASYNC:
+               {
+                       int hinteractive = interactive;
+
+                       DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n"));
+
+                       i = vfork();
+                       if (i != 0) {
+                               interactive = hinteractive;
+                               if (i != -1) {
+                                       setval(lookup("!"), putn(i));
+                                       if (pin != NULL)
+                                               closepipe(pin);
+                                       if (interactive) {
+                                               prs(putn(i));
+                                               prs("\n");
+                                       }
+                               } else
+                                       rv = -1;
+                               setstatus(rv);
+                       } else {
+                               signal(SIGINT, SIG_IGN);
+                               signal(SIGQUIT, SIG_IGN);
+                               if (interactive)
+                                       signal(SIGTERM, SIG_DFL);
+                               interactive = 0;
+                               if (pin == NULL) {
+                                       close(0);
+                                       open("/dev/null", 0);
+                               }
+                               _exit(execute(t->left, pin, pout, FEXEC));
+                       }
+               }
+               break;
+
+       case TOR:
+       case TAND:
+               rv = execute(t->left, pin, pout, 0);
+               if ((t1 = t->right) != NULL && (rv == 0) == (t->type == TAND))
+                       rv = execute(t1, pin, pout, 0);
+               break;
+
+       case TFOR:
+               if (wp == NULL) {
+                       wp = dolv + 1;
+                       if ((i = dolc) < 0)
+                               i = 0;
+               } else {
+                       i = -1;
+                       while (*wp++ != NULL);
+               }
+               vp = lookup(t->str);
+               while (setjmp(bc.brkpt))
+                       if (isbreak)
+                               goto broken;
+               brkset(&bc);
+               for (t1 = t->left; i-- && *wp != NULL;) {
+                       setval(vp, *wp++);
+                       rv = execute(t1, pin, pout, 0);
+               }
+               brklist = brklist->nextlev;
+               break;
+
+       case TWHILE:
+       case TUNTIL:
+               while (setjmp(bc.brkpt))
+                       if (isbreak)
+                               goto broken;
+               brkset(&bc);
+               t1 = t->left;
+               while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
+                       rv = execute(t->right, pin, pout, 0);
+               brklist = brklist->nextlev;
+               break;
+
+       case TIF:
+       case TELIF:
+               if (t->right != NULL) {
+                       rv = !execute(t->left, pin, pout, 0) ?
+                               execute(t->right->left, pin, pout, 0) :
+                               execute(t->right->right, pin, pout, 0);
+               }
+               break;
+
+       case TCASE:
+               if ((cp = evalstr(t->str, DOSUB | DOTRIM)) == 0)
+                       cp = "";
+
+               DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n",
+                                       ((t->str == NULL) ? "NULL" : t->str),
+                                       ((cp == NULL) ? "NULL" : cp)));
+
+               if ((t1 = findcase(t->left, cp)) != NULL) {
+                       DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=0x%x, t1=0x%x)...\n", t, t1));
+                       rv = execute(t1, pin, pout, 0);
+                       DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=0x%x, t1=0x%x)...\n", t, t1));
+               }
+               break;
+
+       case TBRACE:
+/*
+               if (iopp = t->ioact)
+                       while (*iopp)
+                               if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
+                                       rv = -1;
+                                       break;
+                               }
+*/
+               if (rv >= 0 && (t1 = t->left))
+                       rv = execute(t1, pin, pout, 0);
+               break;
+
+       };
+
+  broken:
+       t->words = wp2;
+       isbreak = 0;
+       freehere(areanum);
+       freearea(areanum);
+       areanum = a;
+       if (interactive && intr) {
+               closeall();
+               fail();
+       }
+
+       if ((i = trapset) != 0) {
+               trapset = 0;
+               runtrap(i);
+       }
+
+       DBGPRINTF(("EXECUTE: returning from t=0x%x, rv=%d\n", t, rv));
+       return (rv);
+}
+
+static int
+forkexec(REGISTER struct op *t, int *pin, int *pout, int act, char **wp)
+{
+       pid_t newpid;
+       int i, rv;
+       int (*shcom) (struct op *) = NULL;
+       REGISTER int f;
+       char *cp = NULL;
+       struct ioword **iopp;
+       int resetsig;
+       char **owp;
+       int forked = 0;
+
+       int *hpin = pin;
+       int *hpout = pout;
+       char *hwp;
+       int hinteractive;
+       int hintr;
+       struct brkcon *hbrklist;
+       int hexecflg;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &pin;
+       (void) &pout;
+       (void) &wp;
+       (void) &shcom;
+       (void) &cp;
+       (void) &resetsig;
+       (void) &owp;
+#endif
+
+       DBGPRINTF(("FORKEXEC: t=0x%x, pin 0x%x, pout 0x%x, act %d\n", t, pin,
+                          pout, act));
+       DBGPRINTF7(("FORKEXEC: t->words is %s\n",
+                               ((t->words == NULL) ? "NULL" : t->words[0])));
+
+/* Hard to know how many words there are, be careful of garbage pointer values */
+/* They are likely to cause "PCI bus fault" errors */
+#if 0
+       DBGPRINTF7(("FORKEXEC: t->words is %s, t->words[1] is %s\n",
+                               ((t->words == NULL) ? "NULL" : t->words[0]),
+                               ((t->words == NULL) ? "NULL" : t->words[1])));
+       DBGPRINTF7(("FORKEXEC: wp is %s, wp[1] is %s\n",
+                               ((wp == NULL) ? "NULL" : wp[0]),
+                               ((wp[1] == NULL) ? "NULL" : wp[1])));
+       DBGPRINTF7(("FORKEXEC: wp2 is %s, wp[3] is %s\n",
+                               ((wp[2] == NULL) ? "NULL" : wp[2]),
+                               ((wp[3] == NULL) ? "NULL" : wp[3])));
+#endif
+
+
+       owp = wp;
+       resetsig = 0;
+       rv = -1;                                        /* system-detected error */
+       if (t->type == TCOM) {
+               while ((cp = *wp++) != NULL);
+               cp = *wp;
+
+               /* strip all initial assignments */
+               /* not correct wrt PATH=yyy command  etc */
+               if (flag['x']) {
+                       DBGPRINTF9(("FORKEXEC: echo'ing, cp=0x%x, wp=0x%x, owp=0x%x\n",
+                                               cp, wp, owp));
+                       echo(cp ? wp : owp);
+               }
+#if 0
+               DBGPRINTF9(("FORKEXEC: t->words is %s, t->words[1] is %s\n",
+                                       ((t->words == NULL) ? "NULL" : t->words[0]),
+                                       ((t->words == NULL) ? "NULL" : t->words[1])));
+               DBGPRINTF9(("FORKEXEC: wp is %s, wp[1] is %s\n",
+                                       ((wp == NULL) ? "NULL" : wp[0]),
+                                       ((wp == NULL) ? "NULL" : wp[1])));
+#endif
+
+               if (cp == NULL && t->ioact == NULL) {
+                       while ((cp = *owp++) != NULL && assign(cp, COPYV));
+                       DBGPRINTF(("FORKEXEC: returning setstatus()\n"));
+                       return (setstatus(0));
+               } else if (cp != NULL) {
+                       shcom = inbuilt(cp);
+               }
+       }
+
+       t->words = wp;
+       f = act;
+
+#if 0
+       DBGPRINTF3(("FORKEXEC: t->words is %s, t->words[1] is %s\n",
+                               ((t->words == NULL) ? "NULL" : t->words[0]),
+                               ((t->words == NULL) ? "NULL" : t->words[1])));
+#endif
+       DBGPRINTF(("FORKEXEC: shcom 0x%x, f&FEXEC 0x%x, owp 0x%x\n", shcom,
+                          f & FEXEC, owp));
+
+       if (shcom == NULL && (f & FEXEC) == 0) {
+               /* Save values in case the child process alters them */
+               hpin = pin;
+               hpout = pout;
+               hwp = *wp;
+               hinteractive = interactive;
+               hintr = intr;
+               hbrklist = brklist;
+               hexecflg = execflg;
+
+               DBGPRINTF3(("FORKEXEC: calling vfork()...\n"));
+
+               newpid = vfork();
+
+               if (newpid == -1) {
+                       DBGPRINTF(("FORKEXEC: ERROR, unable to vfork()!\n"));
+                       return (-1);
+               }
+
+
+               if (newpid > 0) {               /* Parent */
+
+                       /* Restore values */
+                       pin = hpin;
+                       pout = hpout;
+                       *wp = hwp;
+                       interactive = hinteractive;
+                       intr = hintr;
+                       brklist = hbrklist;
+                       execflg = hexecflg;
+
+/* moved up
+                       if (i == -1)
+                               return(rv);
+*/
+
+                       if (pin != NULL)
+                               closepipe(pin);
+
+                       return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0);
+               }
+
+               /* Must be the child process, pid should be 0 */
+               DBGPRINTF(("FORKEXEC: child process, shcom=0x%x\n", shcom));
+
+               if (interactive) {
+                       signal(SIGINT, SIG_IGN);
+                       signal(SIGQUIT, SIG_IGN);
+                       resetsig = 1;
+               }
+               interactive = 0;
+               intr = 0;
+               forked = 1;
+               brklist = 0;
+               execflg = 0;
+       }
+
+
+       if (owp != NULL)
+               while ((cp = *owp++) != NULL && assign(cp, COPYV))
+                       if (shcom == NULL)
+                               export(lookup(cp));
+
+#ifdef COMPIPE
+       if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
+               err("piping to/from shell builtins not yet done");
+               if (forked)
+                       _exit(-1);
+               return (-1);
+       }
+#endif
+
+       if (pin != NULL) {
+               dup2(pin[0], 0);
+               closepipe(pin);
+       }
+       if (pout != NULL) {
+               dup2(pout[1], 1);
+               closepipe(pout);
+       }
+
+       if ((iopp = t->ioact) != NULL) {
+               if (shcom != NULL && shcom != doexec) {
+                       prs(cp);
+                       err(": cannot redirect shell command");
+                       if (forked)
+                               _exit(-1);
+                       return (-1);
+               }
+               while (*iopp)
+                       if (iosetup(*iopp++, pin != NULL, pout != NULL)) {
+                               if (forked)
+                                       _exit(rv);
+                               return (rv);
+                       }
+       }
+
+       if (shcom) {
+               i = setstatus((*shcom) (t));
+               if (forked)
+                       _exit(i);
+               DBGPRINTF(("FORKEXEC: returning i=%d\n", i));
+               return (i);
+       }
+
+       /* should use FIOCEXCL */
+       for (i = FDBASE; i < NOFILE; i++)
+               close(i);
+       if (resetsig) {
+               signal(SIGINT, SIG_DFL);
+               signal(SIGQUIT, SIG_DFL);
+       }
+
+       if (t->type == TPAREN)
+               _exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
+       if (wp[0] == NULL)
+               _exit(0);
+
+       cp = rexecve(wp[0], wp, makenv(0, NULL));
+       prs(wp[0]);
+       prs(": ");
+       err(cp);
+       if (!execflg)
+               trap[0] = NULL;
+
+       DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", newpid));
+
+       leave();
+       /* NOTREACHED */
+       _exit(1);
+}
+
+/*
+ * 0< 1> are ignored as required
+ * within pipelines.
+ */
+static int iosetup(iop, pipein, pipeout)
+REGISTER struct ioword *iop;
+int pipein, pipeout;
+{
+       REGISTER int u = -1;
+       char *cp = NULL, *msg;
+
+       DBGPRINTF(("IOSETUP: iop 0x%x, pipein 0x%x, pipeout 0x%x\n", iop,
+                          pipein, pipeout));
+
+       if (iop->io_unit == IODEFAULT)  /* take default */
+               iop->io_unit = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1;
+
+       if (pipein && iop->io_unit == 0)
+               return (0);
+
+       if (pipeout && iop->io_unit == 1)
+               return (0);
+
+       msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create";
+       if ((iop->io_flag & IOHERE) == 0) {
+               cp = iop->io_name;
+               if ((cp = evalstr(cp, DOSUB | DOTRIM)) == NULL)
+                       return (1);
+       }
+
+       if (iop->io_flag & IODUP) {
+               if (cp[1] || (!isdigit(*cp) && *cp != '-')) {
+                       prs(cp);
+                       err(": illegal >& argument");
+                       return (1);
+               }
+               if (*cp == '-')
+                       iop->io_flag = IOCLOSE;
+               iop->io_flag &= ~(IOREAD | IOWRITE);
+       }
+       switch (iop->io_flag) {
+       case IOREAD:
+               u = open(cp, 0);
+               break;
+
+       case IOHERE:
+       case IOHERE | IOXHERE:
+               u = herein(iop->io_name, iop->io_flag & IOXHERE);
+               cp = "here file";
+               break;
+
+       case IOWRITE | IOCAT:
+               if ((u = open(cp, 1)) >= 0) {
+                       lseek(u, (long) 0, 2);
+                       break;
+               }
+       case IOWRITE:
+               u = creat(cp, 0666);
+               break;
+
+       case IODUP:
+               u = dup2(*cp - '0', iop->io_unit);
+               break;
+
+       case IOCLOSE:
+               close(iop->io_unit);
+               return (0);
+       }
+       if (u < 0) {
+               prs(cp);
+               prs(": cannot ");
+               warn(msg);
+               return (1);
+       } else {
+               if (u != iop->io_unit) {
+                       dup2(u, iop->io_unit);
+                       close(u);
+               }
+       }
+       return (0);
+}
+
+static void echo(wp)
+REGISTER char **wp;
+{
+       REGISTER int i;
+
+       prs("+");
+       for (i = 0; wp[i]; i++) {
+               if (i)
+                       prs(" ");
+               prs(wp[i]);
+       }
+       prs("\n");
+}
+
+static struct op **find1case(t, w)
+struct op *t;
+char *w;
+{
+       REGISTER struct op *t1;
+       struct op **tp;
+       REGISTER char **wp, *cp;
+
+
+       if (t == NULL) {
+               DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n"));
+               return ((struct op **) NULL);
+       }
+
+       DBGPRINTF3(("FIND1CASE: enter, t->type=%d (%s)\n", t->type,
+                               T_CMD_NAMES[t->type]));
+
+       if (t->type == TLIST) {
+               if ((tp = find1case(t->left, w)) != NULL) {
+                       DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=0x%x\n", tp));
+                       return (tp);
+               }
+               t1 = t->right;                  /* TPAT */
+       } else
+               t1 = t;
+
+       for (wp = t1->words; *wp;)
+               if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) {
+                       DBGPRINTF3(("FIND1CASE: returning &t1->left= 0x%x.\n",
+                                               &t1->left));
+                       return (&t1->left);
+               }
+
+       DBGPRINTF(("FIND1CASE: returning NULL\n"));
+       return ((struct op **) NULL);
+}
+
+static struct op *findcase(t, w)
+struct op *t;
+char *w;
+{
+       REGISTER struct op **tp;
+
+       return ((tp = find1case(t, w)) != NULL ? *tp : (struct op *) NULL);
+}
+
+/*
+ * Enter a new loop level (marked for break/continue).
+ */
+static void brkset(bc)
+struct brkcon *bc;
+{
+       bc->nextlev = brklist;
+       brklist = bc;
+}
+
+/*
+ * Wait for the last process created.
+ * Print a message for each process found
+ * that was killed by a signal.
+ * Ignore interrupt signals while waiting
+ * unless `canintr' is true.
+ */
+static int waitfor(lastpid, canintr)
+REGISTER int lastpid;
+int canintr;
+{
+       REGISTER int pid, rv;
+       int s;
+       int oheedint = heedint;
+
+       heedint = 0;
+       rv = 0;
+       do {
+               pid = wait(&s);
+               if (pid == -1) {
+                       if (errno != EINTR || canintr)
+                               break;
+               } else {
+                       if ((rv = WAITSIG(s)) != 0) {
+                               if (rv < NSIGNAL) {
+                                       if (signame[rv] != NULL) {
+                                               if (pid != lastpid) {
+                                                       prn(pid);
+                                                       prs(": ");
+                                               }
+                                               prs(signame[rv]);
+                                       }
+                               } else {
+                                       if (pid != lastpid) {
+                                               prn(pid);
+                                               prs(": ");
+                                       }
+                                       prs("Signal ");
+                                       prn(rv);
+                                       prs(" ");
+                               }
+                               if (WAITCORE(s))
+                                       prs(" - core dumped");
+                               if (rv >= NSIGNAL || signame[rv])
+                                       prs("\n");
+                               rv = -1;
+                       } else
+                               rv = WAITVAL(s);
+               }
+       } while (pid != lastpid);
+       heedint = oheedint;
+       if (intr) {
+               if (interactive) {
+                       if (canintr)
+                               intr = 0;
+               } else {
+                       if (exstat == 0)
+                               exstat = rv;
+                       onintr(0);
+               }
+       }
+       return (rv);
+}
+
+static int setstatus(s)
+REGISTER int s;
+{
+       exstat = s;
+       setval(lookup("?"), putn(s));
+       return (s);
+}
+
+/*
+ * PATH-searching interface to execve.
+ * If getenv("PATH") were kept up-to-date,
+ * execvp might be used.
+ */
+static char *rexecve(c, v, envp)
+char *c, **v, **envp;
+{
+       REGISTER int i;
+       REGISTER char *sp, *tp;
+       int eacces = 0, asis = 0;
+
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       char *name = c;
+
+       optind = 1;
+       if (find_applet_by_name(name)) {
+               /* We have to exec here since we vforked.  Running
+                * run_applet_by_name() won't work and bad things
+                * will happen. */
+               execve("/proc/self/exe", v, envp);
+               execve("busybox", v, envp);
+       }
+#endif
+
+       DBGPRINTF(("REXECVE: c=0x%x, v=0x%x, envp=0x%x\n", c, v, envp));
+
+       sp = any('/', c) ? "" : path->value;
+       asis = *sp == '\0';
+       while (asis || *sp != '\0') {
+               asis = 0;
+               tp = e.linep;
+               for (; *sp != '\0'; tp++)
+                       if ((*tp = *sp++) == ':') {
+                               asis = *sp == '\0';
+                               break;
+                       }
+               if (tp != e.linep)
+                       *tp++ = '/';
+               for (i = 0; (*tp++ = c[i++]) != '\0';);
+
+               DBGPRINTF3(("REXECVE: e.linep is %s\n", e.linep));
+
+               execve(e.linep, v, envp);
+
+               switch (errno) {
+               case ENOEXEC:
+                       *v = e.linep;
+                       tp = *--v;
+                       *v = e.linep;
+                       execve(DEFAULT_SHELL, v, envp);
+                       *v = tp;
+                       return ("no Shell");
+
+               case ENOMEM:
+                       return ((char *) bb_msg_memory_exhausted);
+
+               case E2BIG:
+                       return ("argument list too long");
+
+               case EACCES:
+                       eacces++;
+                       break;
+               }
+       }
+       return (errno == ENOENT ? "not found" : "cannot execute");
+}
+
+/*
+ * Run the command produced by generator `f'
+ * applied to stream `arg'.
+ */
+static int run(struct ioarg *argp, int (*f) (struct ioarg *))
+{
+       struct op *otree;
+       struct wdblock *swdlist;
+       struct wdblock *siolist;
+       jmp_buf ev, rt;
+       xint *ofail;
+       int rv;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &rv;
+#endif
+
+       DBGPRINTF(("RUN: enter, areanum %d, outtree 0x%x, failpt 0x%x\n",
+                          areanum, outtree, failpt));
+
+       areanum++;
+       swdlist = wdlist;
+       siolist = iolist;
+       otree = outtree;
+       ofail = failpt;
+       rv = -1;
+
+       if (newenv(setjmp(errpt = ev)) == 0) {
+               wdlist = 0;
+               iolist = 0;
+               pushio(argp, f);
+               e.iobase = e.iop;
+               yynerrs = 0;
+               if (setjmp(failpt = rt) == 0 && yyparse() == 0)
+                       rv = execute(outtree, NOPIPE, NOPIPE, 0);
+               quitenv();
+       } else {
+               DBGPRINTF(("RUN: error from newenv()!\n"));
+       }
+
+       wdlist = swdlist;
+       iolist = siolist;
+       failpt = ofail;
+       outtree = otree;
+       freearea(areanum--);
+
+       return (rv);
+}
+
+/* -------- do.c -------- */
+
+/*
+ * built-in commands: doX
+ */
+
+static int dohelp(struct op *t)
+{
+       int col;
+       const struct builtincmd *x;
+
+       printf("\nBuilt-in commands:\n");
+       printf("-------------------\n");
+
+       for (col = 0, x = builtincmds; x->builtinfunc != NULL; x++) {
+               if (!x->name)
+                       continue;
+               col += printf("%s%s", ((col == 0) ? "\t" : " "), x->name);
+               if (col > 60) {
+                       printf("\n");
+                       col = 0;
+               }
+       }
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+       {
+               int i;
+               const struct BB_applet *applet;
+               extern const struct BB_applet applets[];
+               extern const size_t NUM_APPLETS;
+
+               for (i = 0, applet = applets; i < NUM_APPLETS; applet++, i++) {
+                       if (!applet->name)
+                               continue;
+
+                       col += printf("%s%s", ((col == 0) ? "\t" : " "), applet->name);
+                       if (col > 60) {
+                               printf("\n");
+                               col = 0;
+                       }
+               }
+       }
+#endif
+       printf("\n\n");
+       return EXIT_SUCCESS;
+}
+
+
+
+static int dolabel(struct op *t)
+{
+       return (0);
+}
+
+static int dochdir(t)
+REGISTER struct op *t;
+{
+       REGISTER char *cp, *er;
+
+       if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL)
+               er = ": no home directory";
+       else if (chdir(cp) < 0)
+               er = ": bad directory";
+       else
+               return (0);
+       prs(cp != NULL ? cp : "cd");
+       err(er);
+       return (1);
+}
+
+static int doshift(t)
+REGISTER struct op *t;
+{
+       REGISTER int n;
+
+       n = t->words[1] ? getn(t->words[1]) : 1;
+       if (dolc < n) {
+               err("nothing to shift");
+               return (1);
+       }
+       dolv[n] = dolv[0];
+       dolv += n;
+       dolc -= n;
+       setval(lookup("#"), putn(dolc));
+       return (0);
+}
+
+/*
+ * execute login and newgrp directly
+ */
+static int dologin(t)
+struct op *t;
+{
+       REGISTER char *cp;
+
+       if (interactive) {
+               signal(SIGINT, SIG_DFL);
+               signal(SIGQUIT, SIG_DFL);
+       }
+       cp = rexecve(t->words[0], t->words, makenv(0, NULL));
+       prs(t->words[0]);
+       prs(": ");
+       err(cp);
+       return (1);
+}
+
+static int doumask(t)
+REGISTER struct op *t;
+{
+       REGISTER int i, n;
+       REGISTER char *cp;
+
+       if ((cp = t->words[1]) == NULL) {
+               i = umask(0);
+               umask(i);
+               for (n = 3 * 4; (n -= 3) >= 0;)
+                       putc('0' + ((i >> n) & 07), stderr);
+               putc('\n', stderr);
+       } else {
+               for (n = 0; *cp >= '0' && *cp <= '9'; cp++)
+                       n = n * 8 + (*cp - '0');
+               umask(n);
+       }
+       return (0);
+}
+
+static int doexec(t)
+REGISTER struct op *t;
+{
+       REGISTER int i;
+       jmp_buf ex;
+       xint *ofail;
+
+       t->ioact = NULL;
+       for (i = 0; (t->words[i] = t->words[i + 1]) != NULL; i++);
+       if (i == 0)
+               return (1);
+       execflg = 1;
+       ofail = failpt;
+       if (setjmp(failpt = ex) == 0)
+               execute(t, NOPIPE, NOPIPE, FEXEC);
+       failpt = ofail;
+       execflg = 0;
+       return (1);
+}
+
+static int dodot(t)
+struct op *t;
+{
+       REGISTER int i;
+       REGISTER char *sp, *tp;
+       char *cp;
+       int maltmp;
+
+       DBGPRINTF(("DODOT: enter, t=0x%x, tleft 0x%x, tright 0x%x, e.linep is %s\n", t, t->left, t->right, ((e.linep == NULL) ? "NULL" : e.linep)));
+
+       if ((cp = t->words[1]) == NULL) {
+               DBGPRINTF(("DODOT: bad args, ret 0\n"));
+               return (0);
+       } else {
+               DBGPRINTF(("DODOT: cp is %s\n", cp));
+       }
+
+       sp = any('/', cp) ? ":" : path->value;
+
+       DBGPRINTF(("DODOT: sp is %s,  e.linep is %s\n",
+                          ((sp == NULL) ? "NULL" : sp),
+                          ((e.linep == NULL) ? "NULL" : e.linep)));
+
+       while (*sp) {
+               tp = e.linep;
+               while (*sp && (*tp = *sp++) != ':')
+                       tp++;
+               if (tp != e.linep)
+                       *tp++ = '/';
+
+               for (i = 0; (*tp++ = cp[i++]) != '\0';);
+
+               /* Original code */
+               if ((i = open(e.linep, 0)) >= 0) {
+                       exstat = 0;
+                       maltmp = remap(i);
+                       DBGPRINTF(("DODOT: remap=%d, exstat=%d, e.iofd %d, i %d, e.linep is %s\n", maltmp, exstat, e.iofd, i, e.linep));
+
+                       next(maltmp);           /* Basically a PUSHIO */
+
+                       DBGPRINTF(("DODOT: returning exstat=%d\n", exstat));
+
+                       return (exstat);
+               }
+
+       }                                                       /* While */
+
+       prs(cp);
+       err(": not found");
+
+       return (-1);
+}
+
+static int dowait(t)
+struct op *t;
+{
+       REGISTER int i;
+       REGISTER char *cp;
+
+       if ((cp = t->words[1]) != NULL) {
+               i = getn(cp);
+               if (i == 0)
+                       return (0);
+       } else
+               i = -1;
+       setstatus(waitfor(i, 1));
+       return (0);
+}
+
+static int doread(t)
+struct op *t;
+{
+       REGISTER char *cp, **wp;
+       REGISTER int nb = 0;
+       REGISTER int nl = 0;
+
+       if (t->words[1] == NULL) {
+               err("Usage: read name ...");
+               return (1);
+       }
+       for (wp = t->words + 1; *wp; wp++) {
+               for (cp = e.linep; !nl && cp < elinep - 1; cp++)
+                       if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
+                               (nl = (*cp == '\n')) || (wp[1] && any(*cp, ifs->value)))
+                               break;
+               *cp = 0;
+               if (nb <= 0)
+                       break;
+               setval(lookup(*wp), e.linep);
+       }
+       return (nb <= 0);
+}
+
+static int doeval(t)
+REGISTER struct op *t;
+{
+       return (RUN(awordlist, t->words + 1, wdchar));
+}
+
+static int dotrap(t)
+REGISTER struct op *t;
+{
+       REGISTER int n, i;
+       REGISTER int resetsig;
+
+       if (t->words[1] == NULL) {
+               for (i = 0; i <= _NSIG; i++)
+                       if (trap[i]) {
+                               prn(i);
+                               prs(": ");
+                               prs(trap[i]);
+                               prs("\n");
+                       }
+               return (0);
+       }
+       resetsig = isdigit(*t->words[1]);
+       for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) {
+               n = getsig(t->words[i]);
+               freecell(trap[n]);
+               trap[n] = 0;
+               if (!resetsig) {
+                       if (*t->words[1] != '\0') {
+                               trap[n] = strsave(t->words[1], 0);
+                               setsig(n, sig);
+                       } else
+                               setsig(n, SIG_IGN);
+               } else {
+                       if (interactive)
+                               if (n == SIGINT)
+                                       setsig(n, onintr);
+                               else
+                                       setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
+                       else
+                               setsig(n, SIG_DFL);
+               }
+       }
+       return (0);
+}
+
+static int getsig(s)
+char *s;
+{
+       REGISTER int n;
+
+       if ((n = getn(s)) < 0 || n > _NSIG) {
+               err("trap: bad signal number");
+               n = 0;
+       }
+       return (n);
+}
+
+static void setsig(REGISTER int n, sighandler_t f)
+{
+       if (n == 0)
+               return;
+       if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
+               ourtrap[n] = 1;
+               signal(n, f);
+       }
+}
+
+static int getn(as)
+char *as;
+{
+       REGISTER char *s;
+       REGISTER int n, m;
+
+       s = as;
+       m = 1;
+       if (*s == '-') {
+               m = -1;
+               s++;
+       }
+       for (n = 0; isdigit(*s); s++)
+               n = (n * 10) + (*s - '0');
+       if (*s) {
+               prs(as);
+               err(": bad number");
+       }
+       return (n * m);
+}
+
+static int dobreak(t)
+struct op *t;
+{
+       return (brkcontin(t->words[1], 1));
+}
+
+static int docontinue(t)
+struct op *t;
+{
+       return (brkcontin(t->words[1], 0));
+}
+
+static int brkcontin(cp, val)
+REGISTER char *cp;
+int val;
+{
+       REGISTER struct brkcon *bc;
+       REGISTER int nl;
+
+       nl = cp == NULL ? 1 : getn(cp);
+       if (nl <= 0)
+               nl = 999;
+       do {
+               if ((bc = brklist) == NULL)
+                       break;
+               brklist = bc->nextlev;
+       } while (--nl);
+       if (nl) {
+               err("bad break/continue level");
+               return (1);
+       }
+       isbreak = val;
+       longjmp(bc->brkpt, 1);
+       /* NOTREACHED */
+}
+
+static int doexit(t)
+struct op *t;
+{
+       REGISTER char *cp;
+
+       execflg = 0;
+       if ((cp = t->words[1]) != NULL)
+               setstatus(getn(cp));
+
+       DBGPRINTF(("DOEXIT: calling leave(), t=0x%x\n", t));
+
+       leave();
+       /* NOTREACHED */
+       return (0);
+}
+
+static int doexport(t)
+struct op *t;
+{
+       rdexp(t->words + 1, export, EXPORT);
+       return (0);
+}
+
+static int doreadonly(t)
+struct op *t;
+{
+       rdexp(t->words + 1, ronly, RONLY);
+       return (0);
+}
+
+static void rdexp(char **wp, void (*f) (struct var *), int key)
+{
+       DBGPRINTF6(("RDEXP: enter, wp=0x%x, func=0x%x, key=%d\n", wp, f, key));
+       DBGPRINTF6(("RDEXP: *wp=%s\n", *wp));
+
+       if (*wp != NULL) {
+               for (; *wp != NULL; wp++) {
+                       if (isassign(*wp)) {
+                               char *cp;
+
+                               assign(*wp, COPYV);
+                               for (cp = *wp; *cp != '='; cp++);
+                               *cp = '\0';
+                       }
+                       if (checkname(*wp))
+                               (*f) (lookup(*wp));
+                       else
+                               badid(*wp);
+               }
+       } else
+               putvlist(key, 1);
+}
+
+static void badid(s)
+REGISTER char *s;
+{
+       prs(s);
+       err(": bad identifier");
+}
+
+static int doset(t)
+REGISTER struct op *t;
+{
+       REGISTER struct var *vp;
+       REGISTER char *cp;
+       REGISTER int n;
+
+       if ((cp = t->words[1]) == NULL) {
+               for (vp = vlist; vp; vp = vp->next)
+                       varput(vp->name, 1);
+               return (0);
+       }
+       if (*cp == '-') {
+               /* bad: t->words++; */
+               for (n = 0; (t->words[n] = t->words[n + 1]) != NULL; n++);
+               if (*++cp == 0)
+                       flag['x'] = flag['v'] = 0;
+               else
+                       for (; *cp; cp++)
+                               switch (*cp) {
+                               case 'e':
+                                       if (!interactive)
+                                               flag['e']++;
+                                       break;
+
+                               default:
+                                       if (*cp >= 'a' && *cp <= 'z')
+                                               flag[(int) *cp]++;
+                                       break;
+                               }
+               setdash();
+       }
+       if (t->words[1]) {
+               t->words[0] = dolv[0];
+               for (n = 1; t->words[n]; n++)
+                       setarea((char *) t->words[n], 0);
+               dolc = n - 1;
+               dolv = t->words;
+               setval(lookup("#"), putn(dolc));
+               setarea((char *) (dolv - 1), 0);
+       }
+       return (0);
+}
+
+static void varput(s, out)
+REGISTER char *s;
+int out;
+{
+       if (isalnum(*s) || *s == '_') {
+               write(out, s, strlen(s));
+               write(out, "\n", 1);
+       }
+}
+
+
+/*
+ * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
+ * This file contains code for the times builtin.
+ */
+static int dotimes(struct op *t)
+{
+       struct tms buf;
+       long int clk_tck = sysconf(_SC_CLK_TCK);
+
+       times(&buf);
+       printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
+                  (int) (buf.tms_utime / clk_tck / 60),
+                  ((double) buf.tms_utime) / clk_tck,
+                  (int) (buf.tms_stime / clk_tck / 60),
+                  ((double) buf.tms_stime) / clk_tck,
+                  (int) (buf.tms_cutime / clk_tck / 60),
+                  ((double) buf.tms_cutime) / clk_tck,
+                  (int) (buf.tms_cstime / clk_tck / 60),
+                  ((double) buf.tms_cstime) / clk_tck);
+       return 0;
+}
+
+
+static int (*inbuilt(char *s)) (struct op *) {
+       const struct builtincmd *bp;
+
+       for (bp = builtincmds; bp->name != NULL; bp++)
+               if (strcmp(bp->name, s) == 0)
+                       return (bp->builtinfunc);
+
+       return (NULL);
+}
+
+/* -------- eval.c -------- */
+
+/*
+ * ${}
+ * `command`
+ * blank interpretation
+ * quoting
+ * glob
+ */
+
+static char **eval(char **ap, int f)
+{
+       struct wdblock *wb;
+       char **wp;
+       char **wf;
+       jmp_buf ev;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &wp;
+       (void) &ap;
+#endif
+
+       DBGPRINTF4(("EVAL: enter, f=%d\n", f));
+
+       wp = NULL;
+       wb = NULL;
+       wf = NULL;
+       if (newenv(setjmp(errpt = ev)) == 0) {
+               while (*ap && isassign(*ap))
+                       expand(*ap++, &wb, f & ~DOGLOB);
+               if (flag['k']) {
+                       for (wf = ap; *wf; wf++) {
+                               if (isassign(*wf))
+                                       expand(*wf, &wb, f & ~DOGLOB);
+                       }
+               }
+               for (wb = addword((char *) 0, wb); *ap; ap++) {
+                       if (!flag['k'] || !isassign(*ap))
+                               expand(*ap, &wb, f & ~DOKEY);
+               }
+               wb = addword((char *) 0, wb);
+               wp = getwords(wb);
+               quitenv();
+       } else
+               gflg = 1;
+
+       return (gflg ? (char **) NULL : wp);
+}
+
+/*
+ * Make the exported environment from the exported
+ * names in the dictionary. Keyword assignments
+ * will already have been done.
+ */
+static char **makenv(int all, struct wdblock *wb)
+{
+       REGISTER struct var *vp;
+
+       DBGPRINTF5(("MAKENV: enter, all=%d\n", all));
+
+       for (vp = vlist; vp; vp = vp->next)
+               if (all || vp->status & EXPORT)
+                       wb = addword(vp->name, wb);
+       wb = addword((char *) 0, wb);
+       return (getwords(wb));
+}
+
+static char *evalstr(cp, f)
+REGISTER char *cp;
+int f;
+{
+       struct wdblock *wb;
+
+       DBGPRINTF6(("EVALSTR: enter, cp=0x%x, f=%d\n", cp, f));
+
+       wb = NULL;
+       if (expand(cp, &wb, f)) {
+               if (wb == NULL || wb->w_nword == 0
+                       || (cp = wb->w_words[0]) == NULL)
+                       cp = "";
+               DELETE(wb);
+       } else
+               cp = NULL;
+       return (cp);
+}
+
+static int expand(char *cp, REGISTER struct wdblock **wbp, int f)
+{
+       jmp_buf ev;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &cp;
+#endif
+
+       DBGPRINTF3(("EXPAND: enter, f=%d\n", f));
+
+       gflg = 0;
+
+       if (cp == NULL)
+               return (0);
+
+       if (!anys("$`'\"", cp) &&
+               !anys(ifs->value, cp) && ((f & DOGLOB) == 0 || !anys("[*?", cp))) {
+               cp = strsave(cp, areanum);
+               if (f & DOTRIM)
+                       unquote(cp);
+               *wbp = addword(cp, *wbp);
+               return (1);
+       }
+       if (newenv(setjmp(errpt = ev)) == 0) {
+               PUSHIO(aword, cp, strchar);
+               e.iobase = e.iop;
+               while ((cp = blank(f)) && gflg == 0) {
+                       e.linep = cp;
+                       cp = strsave(cp, areanum);
+                       if ((f & DOGLOB) == 0) {
+                               if (f & DOTRIM)
+                                       unquote(cp);
+                               *wbp = addword(cp, *wbp);
+                       } else
+                               *wbp = glob(cp, *wbp);
+               }
+               quitenv();
+       } else
+               gflg = 1;
+       return (gflg == 0);
+}
+
+/*
+ * Blank interpretation and quoting
+ */
+static char *blank(f)
+int f;
+{
+       REGISTER int c, c1;
+       REGISTER char *sp;
+       int scanequals, foundequals;
+
+       DBGPRINTF3(("BLANK: enter, f=%d\n", f));
+
+       sp = e.linep;
+       scanequals = f & DOKEY;
+       foundequals = 0;
+
+  loop:
+       switch (c = subgetc('"', foundequals)) {
+       case 0:
+               if (sp == e.linep)
+                       return (0);
+               *e.linep++ = 0;
+               return (sp);
+
+       default:
+               if (f & DOBLANK && any(c, ifs->value))
+                       goto loop;
+               break;
+
+       case '"':
+       case '\'':
+               scanequals = 0;
+               if (INSUB())
+                       break;
+               for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
+                       if (c == 0)
+                               break;
+                       if (c == '\'' || !any(c, "$`\""))
+                               c |= QUOTE;
+                       *e.linep++ = c;
+               }
+               c = 0;
+       }
+       unget(c);
+       if (!isalpha(c) && c != '_')
+               scanequals = 0;
+       for (;;) {
+               c = subgetc('"', foundequals);
+               if (c == 0 ||
+                       f & (DOBLANK && any(c, ifs->value)) ||
+                       (!INSUB() && any(c, "\"'"))) {
+                       scanequals = 0;
+                       unget(c);
+                       if (any(c, "\"'"))
+                               goto loop;
+                       break;
+               }
+               if (scanequals) {
+                       if (c == '=') {
+                               foundequals = 1;
+                               scanequals = 0;
+                       } else if (!isalnum(c) && c != '_')
+                               scanequals = 0;
+               }
+               *e.linep++ = c;
+       }
+       *e.linep++ = 0;
+       return (sp);
+}
+
+/*
+ * Get characters, substituting for ` and $
+ */
+static int subgetc(ec, quoted)
+REGISTER char ec;
+int quoted;
+{
+       REGISTER char c;
+
+       DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted));
+
+  again:
+       c = my_getc(ec);
+       if (!INSUB() && ec != '\'') {
+               if (c == '`') {
+                       if (grave(quoted) == 0)
+                               return (0);
+                       e.iop->task = XGRAVE;
+                       goto again;
+               }
+               if (c == '$' && (c = dollar(quoted)) == 0) {
+                       e.iop->task = XDOLL;
+                       goto again;
+               }
+       }
+       return (c);
+}
+
+/*
+ * Prepare to generate the string returned by ${} substitution.
+ */
+static int dollar(quoted)
+int quoted;
+{
+       int otask;
+       struct io *oiop;
+       char *dolp;
+       REGISTER char *s, c, *cp = NULL;
+       struct var *vp;
+
+       DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted));
+
+       c = readc();
+       s = e.linep;
+       if (c != '{') {
+               *e.linep++ = c;
+               if (isalpha(c) || c == '_') {
+                       while ((c = readc()) != 0 && (isalnum(c) || c == '_'))
+                               if (e.linep < elinep)
+                                       *e.linep++ = c;
+                       unget(c);
+               }
+               c = 0;
+       } else {
+               oiop = e.iop;
+               otask = e.iop->task;
+
+               e.iop->task = XOTHER;
+               while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n')
+                       if (e.linep < elinep)
+                               *e.linep++ = c;
+               if (oiop == e.iop)
+                       e.iop->task = otask;
+               if (c != '}') {
+                       err("unclosed ${");
+                       gflg++;
+                       return (c);
+               }
+       }
+       if (e.linep >= elinep) {
+               err("string in ${} too long");
+               gflg++;
+               e.linep -= 10;
+       }
+       *e.linep = 0;
+       if (*s)
+               for (cp = s + 1; *cp; cp++)
+                       if (any(*cp, "=-+?")) {
+                               c = *cp;
+                               *cp++ = 0;
+                               break;
+                       }
+       if (s[1] == 0 && (*s == '*' || *s == '@')) {
+               if (dolc > 1) {
+                       /* currently this does not distinguish $* and $@ */
+                       /* should check dollar */
+                       e.linep = s;
+                       PUSHIO(awordlist, dolv + 1, dolchar);
+                       return (0);
+               } else {                                /* trap the nasty ${=} */
+                       s[0] = '1';
+                       s[1] = 0;
+               }
+       }
+       vp = lookup(s);
+       if ((dolp = vp->value) == null) {
+               switch (c) {
+               case '=':
+                       if (isdigit(*s)) {
+                               err("cannot use ${...=...} with $n");
+                               gflg++;
+                               break;
+                       }
+                       setval(vp, cp);
+                       dolp = vp->value;
+                       break;
+
+               case '-':
+                       dolp = strsave(cp, areanum);
+                       break;
+
+               case '?':
+                       if (*cp == 0) {
+                               prs("missing value for ");
+                               err(s);
+                       } else
+                               err(cp);
+                       gflg++;
+                       break;
+               }
+       } else if (c == '+')
+               dolp = strsave(cp, areanum);
+       if (flag['u'] && dolp == null) {
+               prs("unset variable: ");
+               err(s);
+               gflg++;
+       }
+       e.linep = s;
+       PUSHIO(aword, dolp, quoted ? qstrchar : strchar);
+       return (0);
+}
+
+/*
+ * Run the command in `...` and read its output.
+ */
+
+static int grave(quoted)
+int quoted;
+{
+       char *cp;
+       REGISTER int i;
+       int j;
+       int pf[2];
+       static char child_cmd[LINELIM];
+       char *src;
+       char *dest;
+       int count;
+       int ignore;
+       int ignore_once;
+       char *argument_list[4];
+       struct wdblock *wb = NULL;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &cp;
+#endif
+
+       for (cp = e.iop->argp->aword; *cp != '`'; cp++)
+               if (*cp == 0) {
+                       err("no closing `");
+                       return (0);
+               }
+
+       /* string copy with dollar expansion */
+       src = e.iop->argp->aword;
+       dest = child_cmd;
+       count = 0;
+       ignore = 0;
+       ignore_once = 0;
+       while ((*src != '`') && (count < LINELIM)) {
+               if (*src == '\'')
+                       ignore = !ignore;
+               if (*src == '\\')
+                       ignore_once = 1;
+               if (*src == '$' && !ignore && !ignore_once) {
+                       struct var *vp;
+                       char var_name[LINELIM];
+                       char alt_value[LINELIM];
+                       int var_index = 0;
+                       int alt_index = 0;
+                       char operator = 0;
+                       int braces = 0;
+                       char *value;
+
+                       src++;
+                       if (*src == '{') {
+                               braces = 1;
+                               src++;
+                       }
+
+                       var_name[var_index++] = *src++;
+                       while (isalnum(*src))
+                               var_name[var_index++] = *src++;
+                       var_name[var_index] = 0;
+
+                       if (braces) {
+                               switch (*src) {
+                               case '}':
+                                       break;
+                               case '-':
+                               case '=':
+                               case '+':
+                               case '?':
+                                       operator = * src;
+                                       break;
+                               default:
+                                       err("unclosed ${\n");
+                                       return (0);
+                               }
+                               if (operator) {
+                                       src++;
+                                       while (*src && (*src != '}')) {
+                                               alt_value[alt_index++] = *src++;
+                                       }
+                                       alt_value[alt_index] = 0;
+                                       if (*src != '}') {
+                                               err("unclosed ${\n");
+                                               return (0);
+                                       }
+                               }
+                               src++;
+                       }
+
+                       if (isalpha(*var_name)) {
+                               /* let subshell handle it instead */
+
+                               char *namep = var_name;
+
+                               *dest++ = '$';
+                               if (braces)
+                                       *dest++ = '{';
+                               while (*namep)
+                                       *dest++ = *namep++;
+                               if (operator) {
+                                       char *altp = alt_value;
+                                       *dest++ = operator;
+                                       while (*altp)
+                                               *dest++ = *altp++;
+                               }
+                               if (braces)
+                                       *dest++ = '}';
+
+                               wb = addword(lookup(var_name)->name, wb);
+                       } else {
+                               /* expand */
+
+                               vp = lookup(var_name);
+                               if (vp->value != null)
+                                       value = (operator == '+') ?
+                                               alt_value : vp->value;
+                               else if (operator == '?') {
+                                       err(alt_value);
+                                       return (0);
+                               } else if (alt_index && (operator != '+')) {
+                                       value = alt_value;
+                                       if (operator == '=')
+                                               setval(vp, value);
+                               } else
+                                       continue;
+
+                               while (*value && (count < LINELIM)) {
+                                       *dest++ = *value++;
+                                       count++;
+                               }
+                       }
+               } else {
+                       *dest++ = *src++;
+                       count++;
+                       ignore_once = 0;
+               }
+       }
+       *dest = '\0';
+
+       if (openpipe(pf) < 0)
+               return (0);
+
+       while ((i = vfork()) == -1 && errno == EAGAIN);
+
+       DBGPRINTF3(("GRAVE: i is %d\n", io));
+
+       if (i < 0) {
+               closepipe(pf);
+               err((char *) bb_msg_memory_exhausted);
+               return (0);
+       }
+       if (i != 0) {
+               waitpid(i, NULL, 0);
+               e.iop->argp->aword = ++cp;
+               close(pf[1]);
+               PUSHIO(afile, remap(pf[0]),
+                          (int (*)(struct ioarg *)) ((quoted) ? qgravechar :
+                                                                                 gravechar));
+               return (1);
+       }
+       /* allow trapped signals */
+       /* XXX - Maybe this signal stuff should go as well? */
+       for (j = 0; j <= _NSIG; j++)
+               if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN)
+                       signal(j, SIG_DFL);
+
+       dup2(pf[1], 1);
+       closepipe(pf);
+
+       argument_list[0] = (char *) DEFAULT_SHELL;
+       argument_list[1] = "-c";
+       argument_list[2] = child_cmd;
+       argument_list[3] = 0;
+
+       cp = rexecve(argument_list[0], argument_list, makenv(1, wb));
+       prs(argument_list[0]);
+       prs(": ");
+       err(cp);
+       _exit(1);
+}
+
+
+static char *unquote(as)
+REGISTER char *as;
+{
+       REGISTER char *s;
+
+       if ((s = as) != NULL)
+               while (*s)
+                       *s++ &= ~QUOTE;
+       return (as);
+}
+
+/* -------- glob.c -------- */
+
+/*
+ * glob
+ */
+
+#define        scopy(x) strsave((x), areanum)
+#define        BLKSIZ  512
+#define        NDENT   ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent))
+
+static struct wdblock *cl, *nl;
+static char spcl[] = "[?*";
+
+static struct wdblock *glob(cp, wb)
+char *cp;
+struct wdblock *wb;
+{
+       REGISTER int i;
+       REGISTER char *pp;
+
+       if (cp == 0)
+               return (wb);
+       i = 0;
+       for (pp = cp; *pp; pp++)
+               if (any(*pp, spcl))
+                       i++;
+               else if (!any(*pp & ~QUOTE, spcl))
+                       *pp &= ~QUOTE;
+       if (i != 0) {
+               for (cl = addword(scopy(cp), (struct wdblock *) 0); anyspcl(cl);
+                        cl = nl) {
+                       nl = newword(cl->w_nword * 2);
+                       for (i = 0; i < cl->w_nword; i++) {     /* for each argument */
+                               for (pp = cl->w_words[i]; *pp; pp++)
+                                       if (any(*pp, spcl)) {
+                                               globname(cl->w_words[i], pp);
+                                               break;
+                                       }
+                               if (*pp == '\0')
+                                       nl = addword(scopy(cl->w_words[i]), nl);
+                       }
+                       for (i = 0; i < cl->w_nword; i++)
+                               DELETE(cl->w_words[i]);
+                       DELETE(cl);
+               }
+               for (i = 0; i < cl->w_nword; i++)
+                       unquote(cl->w_words[i]);
+               glob0((char *) cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
+               if (cl->w_nword) {
+                       for (i = 0; i < cl->w_nword; i++)
+                               wb = addword(cl->w_words[i], wb);
+                       DELETE(cl);
+                       return (wb);
+               }
+       }
+       wb = addword(unquote(cp), wb);
+       return (wb);
+}
+
+static void globname(we, pp)
+char *we;
+REGISTER char *pp;
+{
+       REGISTER char *np, *cp;
+       char *name, *gp, *dp;
+       int k;
+       DIR *dirp;
+       struct dirent *de;
+       char dname[NAME_MAX + 1];
+       struct stat dbuf;
+
+       for (np = we; np != pp; pp--)
+               if (pp[-1] == '/')
+                       break;
+       for (dp = cp = space((int) (pp - np) + 3); np < pp;)
+               *cp++ = *np++;
+       *cp++ = '.';
+       *cp = '\0';
+       for (gp = cp = space(strlen(pp) + 1); *np && *np != '/';)
+               *cp++ = *np++;
+       *cp = '\0';
+       dirp = opendir(dp);
+       if (dirp == 0) {
+               DELETE(dp);
+               DELETE(gp);
+               return;
+       }
+       dname[NAME_MAX] = '\0';
+       while ((de = readdir(dirp)) != NULL) {
+               /* XXX Hmmm... What this could be? (abial) */
+               /*
+                  if (ent[j].d_ino == 0)
+                  continue;
+                */
+               strncpy(dname, de->d_name, NAME_MAX);
+               if (dname[0] == '.')
+                       if (*gp != '.')
+                               continue;
+               for (k = 0; k < NAME_MAX; k++)
+                       if (any(dname[k], spcl))
+                               dname[k] |= QUOTE;
+               if (gmatch(dname, gp)) {
+                       name = generate(we, pp, dname, np);
+                       if (*np && !anys(np, spcl)) {
+                               if (stat(name, &dbuf)) {
+                                       DELETE(name);
+                                       continue;
+                               }
+                       }
+                       nl = addword(name, nl);
+               }
+       }
+       closedir(dirp);
+       DELETE(dp);
+       DELETE(gp);
+}
+
+/*
+ * generate a pathname as below.
+ * start..end1 / middle end
+ * the slashes come for free
+ */
+static char *generate(start1, end1, middle, end)
+char *start1;
+REGISTER char *end1;
+char *middle, *end;
+{
+       char *p;
+       REGISTER char *op, *xp;
+
+       p = op =
+               space((int) (end1 - start1) + strlen(middle) + strlen(end) + 2);
+       for (xp = start1; xp != end1;)
+               *op++ = *xp++;
+       for (xp = middle; (*op++ = *xp++) != '\0';);
+       op--;
+       for (xp = end; (*op++ = *xp++) != '\0';);
+       return (p);
+}
+
+static int anyspcl(wb)
+REGISTER struct wdblock *wb;
+{
+       REGISTER int i;
+       REGISTER char **wd;
+
+       wd = wb->w_words;
+       for (i = 0; i < wb->w_nword; i++)
+               if (anys(spcl, *wd++))
+                       return (1);
+       return (0);
+}
+
+static int xstrcmp(p1, p2)
+char *p1, *p2;
+{
+       return (strcmp(*(char **) p1, *(char **) p2));
+}
+
+/* -------- word.c -------- */
+
+static struct wdblock *newword(nw)
+REGISTER int nw;
+{
+       REGISTER struct wdblock *wb;
+
+       wb = (struct wdblock *) space(sizeof(*wb) + nw * sizeof(char *));
+       wb->w_bsize = nw;
+       wb->w_nword = 0;
+       return (wb);
+}
+
+static struct wdblock *addword(wd, wb)
+char *wd;
+REGISTER struct wdblock *wb;
+{
+       REGISTER struct wdblock *wb2;
+       REGISTER int nw;
+
+       if (wb == NULL)
+               wb = newword(NSTART);
+       if ((nw = wb->w_nword) >= wb->w_bsize) {
+               wb2 = newword(nw * 2);
+               memcpy((char *) wb2->w_words, (char *) wb->w_words,
+                          nw * sizeof(char *));
+               wb2->w_nword = nw;
+               DELETE(wb);
+               wb = wb2;
+       }
+       wb->w_words[wb->w_nword++] = wd;
+       return (wb);
+}
+
+static
+char **getwords(wb)
+REGISTER struct wdblock *wb;
+{
+       REGISTER char **wd;
+       REGISTER int nb;
+
+       if (wb == NULL)
+               return ((char **) NULL);
+       if (wb->w_nword == 0) {
+               DELETE(wb);
+               return ((char **) NULL);
+       }
+       wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
+       memcpy((char *) wd, (char *) wb->w_words, nb);
+       DELETE(wb);                                     /* perhaps should done by caller */
+       return (wd);
+}
+
+int (*func) (char *, char *);
+int globv;
+
+static void glob0(a0, a1, a2, a3)
+char *a0;
+unsigned a1;
+int a2;
+int (*a3) (char *, char *);
+{
+       func = a3;
+       globv = a2;
+       glob1(a0, a0 + a1 * a2);
+}
+
+static void glob1(base, lim)
+char *base, *lim;
+{
+       REGISTER char *i, *j;
+       int v2;
+       char *lptr, *hptr;
+       int c;
+       unsigned n;
+
+
+       v2 = globv;
+
+  top:
+       if ((n = (int) (lim - base)) <= v2)
+               return;
+       n = v2 * (n / (2 * v2));
+       hptr = lptr = base + n;
+       i = base;
+       j = lim - v2;
+       for (;;) {
+               if (i < lptr) {
+                       if ((c = (*func) (i, lptr)) == 0) {
+                               glob2(i, lptr -= v2);
+                               continue;
+                       }
+                       if (c < 0) {
+                               i += v2;
+                               continue;
+                       }
+               }
+
+         begin:
+               if (j > hptr) {
+                       if ((c = (*func) (hptr, j)) == 0) {
+                               glob2(hptr += v2, j);
+                               goto begin;
+                       }
+                       if (c > 0) {
+                               if (i == lptr) {
+                                       glob3(i, hptr += v2, j);
+                                       i = lptr += v2;
+                                       goto begin;
+                               }
+                               glob2(i, j);
+                               j -= v2;
+                               i += v2;
+                               continue;
+                       }
+                       j -= v2;
+                       goto begin;
+               }
+
+
+               if (i == lptr) {
+                       if (lptr - base >= lim - hptr) {
+                               glob1(hptr + v2, lim);
+                               lim = lptr;
+                       } else {
+                               glob1(base, lptr);
+                               base = hptr + v2;
+                       }
+                       goto top;
+               }
+
+
+               glob3(j, lptr -= v2, i);
+               j = hptr -= v2;
+       }
+}
+
+static void glob2(i, j)
+char *i, *j;
+{
+       REGISTER char *index1, *index2, c;
+       int m;
+
+       m = globv;
+       index1 = i;
+       index2 = j;
+       do {
+               c = *index1;
+               *index1++ = *index2;
+               *index2++ = c;
+       } while (--m);
+}
+
+static void glob3(i, j, k)
+char *i, *j, *k;
+{
+       REGISTER char *index1, *index2, *index3;
+       int c;
+       int m;
+
+       m = globv;
+       index1 = i;
+       index2 = j;
+       index3 = k;
+       do {
+               c = *index1;
+               *index1++ = *index3;
+               *index3++ = *index2;
+               *index2++ = c;
+       } while (--m);
+}
+
+/* -------- io.c -------- */
+
+/*
+ * shell IO
+ */
+
+static int my_getc(int ec)
+{
+       REGISTER int c;
+
+       if (e.linep > elinep) {
+               while ((c = readc()) != '\n' && c);
+               err("input line too long");
+               gflg++;
+               return (c);
+       }
+       c = readc();
+       if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE)) {
+               if (c == '\\') {
+                       c = readc();
+                       if (c == '\n' && ec != '\"')
+                               return (my_getc(ec));
+                       c |= QUOTE;
+               }
+       }
+       return (c);
+}
+
+static void unget(c)
+int c;
+{
+       if (e.iop >= e.iobase)
+               e.iop->peekc = c;
+}
+
+static int eofc()
+{
+       return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0);
+}
+
+static int readc()
+{
+       REGISTER int c;
+
+       RCPRINTF(("READC: e.iop 0x%x, e.iobase 0x%x\n", e.iop, e.iobase));
+
+       for (; e.iop >= e.iobase; e.iop--) {
+               RCPRINTF(("READC: e.iop 0x%x, peekc 0x%x\n", e.iop, e.iop->peekc));
+               if ((c = e.iop->peekc) != '\0') {
+                       e.iop->peekc = 0;
+                       return (c);
+               } else {
+                       if (e.iop->prev != 0) {
+                               if ((c = (*e.iop->iofn) (e.iop->argp, e.iop)) != '\0') {
+                                       if (c == -1) {
+                                               e.iop++;
+                                               continue;
+                                       }
+                                       if (e.iop == iostack)
+                                               ioecho(c);
+                                       return (e.iop->prev = c);
+                               } else if (e.iop->task == XIO && e.iop->prev != '\n') {
+                                       e.iop->prev = 0;
+                                       if (e.iop == iostack)
+                                               ioecho('\n');
+                                       return '\n';
+                               }
+                       }
+                       if (e.iop->task == XIO) {
+                               if (multiline) {
+                                       return e.iop->prev = 0;
+                               }
+                               if (interactive && e.iop == iostack + 1) {
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+                                       current_prompt = prompt->value;
+#else
+                                       prs(prompt->value);
+#endif
+                               }
+                       }
+               }
+
+       }                                                       /* FOR */
+
+       if (e.iop >= iostack) {
+               RCPRINTF(("READC: return 0, e.iop 0x%x\n", e.iop));
+               return (0);
+       }
+
+       DBGPRINTF(("READC: leave()...\n"));
+       leave();
+
+       /* NOTREACHED */
+       return (0);
+}
+
+static void ioecho(c)
+char c;
+{
+       if (flag['v'])
+               write(2, &c, sizeof c);
+}
+
+
+static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *))
+{
+       DBGPRINTF(("PUSHIO: argp 0x%x, argp->afid 0x%x, e.iop 0x%x\n", argp,
+                          argp->afid, e.iop));
+
+       /* Set env ptr for io source to next array spot and check for array overflow */
+       if (++e.iop >= &iostack[NPUSH]) {
+               e.iop--;
+               err("Shell input nested too deeply");
+               gflg++;
+               return;
+       }
+
+       /* We did not overflow the NPUSH array spots so setup data structs */
+
+       e.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn;        /* Store data source func ptr */
+
+       if (argp->afid != AFID_NOBUF)
+               e.iop->argp = argp;
+       else {
+
+               e.iop->argp = ioargstack + (e.iop - iostack);   /* MAL - index into stack */
+               *e.iop->argp = *argp;   /* copy data from temp area into stack spot */
+
+               /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */
+
+               if (e.iop == &iostack[0])
+                       e.iop->argp->afbuf = &mainbuf;
+               else
+                       e.iop->argp->afbuf = &sharedbuf;
+
+               /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */
+               /* This line appears to be active when running scripts from command line */
+               if ((isatty(e.iop->argp->afile) == 0)
+                       && (e.iop == &iostack[0]
+                               || lseek(e.iop->argp->afile, 0L, 1) != -1)) {
+                       if (++bufid == AFID_NOBUF)      /* counter rollover check, AFID_NOBUF = 11111111  */
+                               bufid = AFID_ID;        /* AFID_ID = 0 */
+
+                       e.iop->argp->afid = bufid;      /* assign buffer id */
+               }
+
+               DBGPRINTF(("PUSHIO: iostack 0x%x,  e.iop 0x%x, afbuf 0x%x\n",
+                                  iostack, e.iop, e.iop->argp->afbuf));
+               DBGPRINTF(("PUSHIO: mbuf 0x%x, sbuf 0x%x, bid %d, e.iop 0x%x\n",
+                                  &mainbuf, &sharedbuf, bufid, e.iop));
+
+       }
+
+       e.iop->prev = ~'\n';
+       e.iop->peekc = 0;
+       e.iop->xchar = 0;
+       e.iop->nlcount = 0;
+
+       if (fn == filechar || fn == linechar)
+               e.iop->task = XIO;
+       else if (fn == (int (*)(struct ioarg *)) gravechar
+                        || fn == (int (*)(struct ioarg *)) qgravechar)
+               e.iop->task = XGRAVE;
+       else
+               e.iop->task = XOTHER;
+
+       return;
+}
+
+static struct io *setbase(ip)
+struct io *ip;
+{
+       REGISTER struct io *xp;
+
+       xp = e.iobase;
+       e.iobase = ip;
+       return (xp);
+}
+
+/*
+ * Input generating functions
+ */
+
+/*
+ * Produce the characters of a string, then a newline, then EOF.
+ */
+static int nlchar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER int c;
+
+       if (ap->aword == NULL)
+               return (0);
+       if ((c = *ap->aword++) == 0) {
+               ap->aword = NULL;
+               return ('\n');
+       }
+       return (c);
+}
+
+/*
+ * Given a list of words, produce the characters
+ * in them, with a space after each word.
+ */
+static int wdchar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER char c;
+       REGISTER char **wl;
+
+       if ((wl = ap->awordlist) == NULL)
+               return (0);
+       if (*wl != NULL) {
+               if ((c = *(*wl)++) != 0)
+                       return (c & 0177);
+               ap->awordlist++;
+               return (' ');
+       }
+       ap->awordlist = NULL;
+       return ('\n');
+}
+
+/*
+ * Return the characters of a list of words,
+ * producing a space between them.
+ */
+static int dolchar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER char *wp;
+
+       if ((wp = *ap->awordlist++) != NULL) {
+               PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar);
+               return (-1);
+       }
+       return (0);
+}
+
+static int xxchar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER int c;
+
+       if (ap->aword == NULL)
+               return (0);
+       if ((c = *ap->aword++) == '\0') {
+               ap->aword = NULL;
+               return (' ');
+       }
+       return (c);
+}
+
+/*
+ * Produce the characters from a single word (string).
+ */
+static int strchar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER int c;
+
+       if (ap->aword == NULL || (c = *ap->aword++) == 0)
+               return (0);
+       return (c);
+}
+
+/*
+ * Produce quoted characters from a single word (string).
+ */
+static int qstrchar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER int c;
+
+       if (ap->aword == NULL || (c = *ap->aword++) == 0)
+               return (0);
+       return (c | QUOTE);
+}
+
+/*
+ * Return the characters from a file.
+ */
+static int filechar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER int i;
+       char c;
+       struct iobuf *bp = ap->afbuf;
+
+       if (ap->afid != AFID_NOBUF) {
+               if ((i = ap->afid != bp->id) || bp->bufp == bp->ebufp) {
+
+                       if (i)
+                               lseek(ap->afile, ap->afpos, 0);
+
+                       i = safe_read(ap->afile, bp->buf, sizeof(bp->buf));
+
+                       if (i <= 0) {
+                               closef(ap->afile);
+                               return 0;
+                       }
+
+                       bp->id = ap->afid;
+                       bp->ebufp = (bp->bufp = bp->buf) + i;
+               }
+
+               ap->afpos++;
+               return *bp->bufp++ & 0177;
+       }
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+       if (interactive && isatty(ap->afile)) {
+               static char mycommand[BUFSIZ];
+               static int position = 0, size = 0;
+
+               while (size == 0 || position >= size) {
+                       cmdedit_read_input(current_prompt, mycommand);
+                       size = strlen(mycommand);
+                       position = 0;
+               }
+               c = mycommand[position];
+               position++;
+               return (c);
+       } else
+#endif
+
+       {
+               i = safe_read(ap->afile, &c, sizeof(c));
+               return (i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0));
+       }
+}
+
+/*
+ * Return the characters from a here temp file.
+ */
+static int herechar(ap)
+REGISTER struct ioarg *ap;
+{
+       char c;
+
+
+       if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
+               close(ap->afile);
+               c = 0;
+       }
+       return (c);
+
+}
+
+/*
+ * Return the characters produced by a process (`...`).
+ * Quote them if required, and remove any trailing newline characters.
+ */
+static int gravechar(ap, iop)
+struct ioarg *ap;
+struct io *iop;
+{
+       REGISTER int c;
+
+       if ((c = qgravechar(ap, iop) & ~QUOTE) == '\n')
+               c = ' ';
+       return (c);
+}
+
+static int qgravechar(ap, iop)
+REGISTER struct ioarg *ap;
+struct io *iop;
+{
+       REGISTER int c;
+
+       DBGPRINTF3(("QGRAVECHAR: enter, ap=0x%x, iop=0x%x\n", ap, iop));
+
+       if (iop->xchar) {
+               if (iop->nlcount) {
+                       iop->nlcount--;
+                       return ('\n' | QUOTE);
+               }
+               c = iop->xchar;
+               iop->xchar = 0;
+       } else if ((c = filechar(ap)) == '\n') {
+               iop->nlcount = 1;
+               while ((c = filechar(ap)) == '\n')
+                       iop->nlcount++;
+               iop->xchar = c;
+               if (c == 0)
+                       return (c);
+               iop->nlcount--;
+               c = '\n';
+       }
+       return (c != 0 ? c | QUOTE : 0);
+}
+
+/*
+ * Return a single command (usually the first line) from a file.
+ */
+static int linechar(ap)
+REGISTER struct ioarg *ap;
+{
+       REGISTER int c;
+
+       if ((c = filechar(ap)) == '\n') {
+               if (!multiline) {
+                       closef(ap->afile);
+                       ap->afile = -1;         /* illegal value */
+               }
+       }
+       return (c);
+}
+
+static void prs(s)
+REGISTER char *s;
+{
+       if (*s)
+               write(2, s, strlen(s));
+}
+
+static void prn(u)
+unsigned u;
+{
+       prs(itoa(u));
+}
+
+static void closef(i)
+REGISTER int i;
+{
+       if (i > 2)
+               close(i);
+}
+
+static void closeall()
+{
+       REGISTER int u;
+
+       for (u = NUFILE; u < NOFILE;)
+               close(u++);
+}
+
+
+/*
+ * remap fd into Shell's fd space
+ */
+static int remap(fd)
+REGISTER int fd;
+{
+       REGISTER int i;
+       int map[NOFILE];
+       int newfd;
+
+
+       DBGPRINTF(("REMAP: fd=%d, e.iofd=%d\n", fd, e.iofd));
+
+       if (fd < e.iofd) {
+               for (i = 0; i < NOFILE; i++)
+                       map[i] = 0;
+
+               do {
+                       map[fd] = 1;
+                       newfd = dup(fd);
+                       fd = newfd;
+               } while (fd >= 0 && fd < e.iofd);
+
+               for (i = 0; i < NOFILE; i++)
+                       if (map[i])
+                               close(i);
+
+               if (fd < 0)
+                       err("too many files open in shell");
+       }
+
+       return (fd);
+}
+
+static int openpipe(pv)
+REGISTER int *pv;
+{
+       REGISTER int i;
+
+       if ((i = pipe(pv)) < 0)
+               err("can't create pipe - try again");
+       return (i);
+}
+
+static void closepipe(pv)
+REGISTER int *pv;
+{
+       if (pv != NULL) {
+               close(*pv++);
+               close(*pv);
+       }
+}
+
+/* -------- here.c -------- */
+
+/*
+ * here documents
+ */
+
+static void markhere(s, iop)
+REGISTER char *s;
+struct ioword *iop;
+{
+       REGISTER struct here *h, *lh;
+
+       DBGPRINTF7(("MARKHERE: enter, s=0x%x\n", s));
+
+       h = (struct here *) space(sizeof(struct here));
+       if (h == 0)
+               return;
+
+       h->h_tag = evalstr(s, DOSUB);
+       if (h->h_tag == 0)
+               return;
+
+       h->h_iop = iop;
+       iop->io_name = 0;
+       h->h_next = NULL;
+       if (inhere == 0)
+               inhere = h;
+       else
+               for (lh = inhere; lh != NULL; lh = lh->h_next)
+                       if (lh->h_next == 0) {
+                               lh->h_next = h;
+                               break;
+                       }
+       iop->io_flag |= IOHERE | IOXHERE;
+       for (s = h->h_tag; *s; s++)
+               if (*s & QUOTE) {
+                       iop->io_flag &= ~IOXHERE;
+                       *s &= ~QUOTE;
+               }
+       h->h_dosub = iop->io_flag & IOXHERE;
+}
+
+static void gethere()
+{
+       REGISTER struct here *h, *hp;
+
+       DBGPRINTF7(("GETHERE: enter...\n"));
+
+       /* Scan here files first leaving inhere list in place */
+       for (hp = h = inhere; h != NULL; hp = h, h = h->h_next)
+               readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\'');
+
+       /* Make inhere list active - keep list intact for scraphere */
+       if (hp != NULL) {
+               hp->h_next = acthere;
+               acthere = inhere;
+               inhere = NULL;
+       }
+}
+
+static void readhere(name, s, ec)
+char **name;
+REGISTER char *s;
+int ec;
+{
+       int tf;
+       char tname[30] = ".msh_XXXXXX";
+       REGISTER int c;
+       jmp_buf ev;
+       char myline[LINELIM + 1];
+       char *thenext;
+
+       DBGPRINTF7(("READHERE: enter, name=0x%x, s=0x%x\n", name, s));
+
+       tf = mkstemp(tname);
+       if (tf < 0)
+               return;
+
+       *name = strsave(tname, areanum);
+       if (newenv(setjmp(errpt = ev)) != 0)
+               unlink(tname);
+       else {
+               pushio(e.iop->argp, (int (*)(struct ioarg *)) e.iop->iofn);
+               e.iobase = e.iop;
+               for (;;) {
+                       if (interactive && e.iop <= iostack) {
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+                               current_prompt = cprompt->value;
+#else
+                               prs(cprompt->value);
+#endif
+                       }
+                       thenext = myline;
+                       while ((c = my_getc(ec)) != '\n' && c) {
+                               if (ec == '\'')
+                                       c &= ~QUOTE;
+                               if (thenext >= &myline[LINELIM]) {
+                                       c = 0;
+                                       break;
+                               }
+                               *thenext++ = c;
+                       }
+                       *thenext = 0;
+                       if (strcmp(s, myline) == 0 || c == 0)
+                               break;
+                       *thenext++ = '\n';
+                       write(tf, myline, (int) (thenext - myline));
+               }
+               if (c == 0) {
+                       prs("here document `");
+                       prs(s);
+                       err("' unclosed");
+               }
+               quitenv();
+       }
+       close(tf);
+}
+
+/*
+ * open here temp file.
+ * if unquoted here, expand here temp file into second temp file.
+ */
+static int herein(hname, xdoll)
+char *hname;
+int xdoll;
+{
+       REGISTER int hf;
+       int tf;
+
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &tf;
+#endif
+       if (hname == NULL)
+               return (-1);
+
+       DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll));
+
+       hf = open(hname, 0);
+       if (hf < 0)
+               return (-1);
+
+       if (xdoll) {
+               char c;
+               char tname[30] = ".msh_XXXXXX";
+               jmp_buf ev;
+
+               tf = mkstemp(tname);
+               if (tf < 0)
+                       return (-1);
+               if (newenv(setjmp(errpt = ev)) == 0) {
+                       PUSHIO(afile, hf, herechar);
+                       setbase(e.iop);
+                       while ((c = subgetc(0, 0)) != 0) {
+                               c &= ~QUOTE;
+                               write(tf, &c, sizeof c);
+                       }
+                       quitenv();
+               } else
+                       unlink(tname);
+               close(tf);
+               tf = open(tname, 0);
+               unlink(tname);
+               return (tf);
+       } else
+               return (hf);
+}
+
+static void scraphere()
+{
+       REGISTER struct here *h;
+
+       DBGPRINTF7(("SCRAPHERE: enter...\n"));
+
+       for (h = inhere; h != NULL; h = h->h_next) {
+               if (h->h_iop && h->h_iop->io_name)
+                       unlink(h->h_iop->io_name);
+       }
+       inhere = NULL;
+}
+
+/* unlink here temp files before a freearea(area) */
+static void freehere(area)
+int area;
+{
+       REGISTER struct here *h, *hl;
+
+       DBGPRINTF6(("FREEHERE: enter, area=%d\n", area));
+
+       hl = NULL;
+       for (h = acthere; h != NULL; h = h->h_next)
+               if (getarea((char *) h) >= area) {
+                       if (h->h_iop->io_name != NULL)
+                               unlink(h->h_iop->io_name);
+                       if (hl == NULL)
+                               acthere = h->h_next;
+                       else
+                               hl->h_next = h->h_next;
+               } else
+                       hl = h;
+}
+
+
+
+/*
+ * Copyright (c) 1987,1997, Prentice Hall
+ * All rights reserved.
+ *
+ * Redistribution and use of the MINIX operating system 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 Prentice Hall nor the names of the software
+ * authors or 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, AUTHORS, 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 PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
diff --git a/busybox/sysdeps/linux/Config.in b/busybox/sysdeps/linux/Config.in
new file mode 100644 (file)
index 0000000..744a84d
--- /dev/null
@@ -0,0 +1,294 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+mainmenu "BusyBox Configuration"
+
+config HAVE_DOT_CONFIG
+       bool
+       default y
+
+menu "General Configuration"
+
+choice
+       prompt "Buffer allocation policy"
+       default CONFIG_FEATURE_BUFFERS_USE_MALLOC
+       help
+         There are 3 ways BusyBox can handle buffer allocations:
+         - Use malloc. This costs code size for the call to xmalloc.
+         - Put them on stack. For some very small machines with limited stack
+           space, this can be deadly.  For most folks, this works just fine.
+         - Put them in BSS. This works beautifully for computers with a real
+           MMU (and OS support), but wastes runtime RAM for uCLinux. This
+           behavior was the only one available for BusyBox versions 0.48 and
+           earlier.
+
+config CONFIG_FEATURE_BUFFERS_USE_MALLOC
+       bool "Allocate with Malloc"
+
+config CONFIG_FEATURE_BUFFERS_GO_ON_STACK
+       bool "Allocate on the Stack"
+
+config CONFIG_FEATURE_BUFFERS_GO_IN_BSS
+       bool "Allocate in the .bss section"
+
+endchoice
+
+config CONFIG_FEATURE_VERBOSE_USAGE
+       bool "Show verbose applet usage messages"
+       default n
+       help
+         All BusyBox applets will show more verbose help messages when
+         busybox is invoked with --help.  This will add a lot of text to the
+         busybox binary.  In the default configuration, this will add about
+         13k, but it can add much more depending on your configuration.
+
+config CONFIG_FEATURE_INSTALLER
+       bool "Support --install [-s] to install applet links at runtime"
+       default n
+       help
+         Enable 'busybox --install [-s]' support.  This will allow you to use
+         busybox at runtime to create hard links or symlinks for all the
+         applets that are compiled into busybox.  This feature requires the
+         /proc filesystem.
+
+config CONFIG_LOCALE_SUPPORT
+       bool "Enable locale support (system needs locale for this to work)"
+       default n
+       help
+         Enable this if your system has locale support and you would like
+         busybox to support locale settings.
+
+config CONFIG_FEATURE_DEVFS
+       bool "Support for devfs"
+       default n
+       help
+         Enable if you want BusyBox to work with devfs.
+
+config CONFIG_FEATURE_DEVPTS
+       bool "Use the devpts filesystem for Unix98 PTYs"
+       default y if CONFIG_FEATURE_DEVFS
+       help
+         Enable if you want BusyBox to use Unix98 PTY support. If enabled,
+         busybox will use /dev/ptmx for the master side of the pseudoterminal
+         and /dev/pts/<number> for the slave side.  Otherwise, BSD style
+         /dev/ttyp<number> will be used. To use this option, you should have
+         devpts or devfs mounted.
+
+config CONFIG_FEATURE_CLEAN_UP
+       bool "Clean up all memory before exiting (usually not needed)"
+       default n
+       help
+         As a size optimization, busybox by default does not cleanup memory
+         that is dynamically allocated or close files before exiting. This
+         saves space and is usually not needed since the OS will clean up for
+         us.  Don't enable this unless you have a really good reason to clean
+         things up manually.
+
+config CONFIG_FEATURE_SUID
+       bool "Support for SUID/SGID handling"
+       default n
+       help
+         Support SUID and SGID binaries.
+
+config CONFIG_FEATURE_SUID_CONFIG
+       bool "Runtime SUID/SGID configuration via /etc/busybox.conf"
+       default y if CONFIG_FEATURE_SUID
+       depends on CONFIG_FEATURE_SUID
+       help
+         Allow the SUID / SGID state of an applet to be determined runtime by
+         checking /etc/busybox.conf.  The format of this file is as follows:
+
+         <applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>)
+       
+         An example might help:
+
+         [SUID]
+         su = ssx root.0 # applet su can be run by anyone and runs with euid=0/egid=0
+         su = ssx        # exactly the same
+
+         mount = sx- root.disk # applet mount can be run by root and members of group disk
+                               # and runs with euid=0
+
+         cp = --- # disable applet cp for everyone
+
+         Robert 'sandman' Griebl has more information here:
+         <url: http://www.softforge.de/bb/suid.html >.
+
+config CONFIG_FEATURE_SUID_CONFIG_QUIET
+       bool "Suppress warning message if /etc/busybox.conf is not readable"
+       default n
+       depends on CONFIG_FEATURE_SUID_CONFIG
+       help
+         /etc/busybox.conf should be readable by the user needing the SUID, check
+         this option to avoid users to be notified about missing permissions.
+
+config CONFIG_SELINUX
+       bool "Support NSA Security Enhanced Linux"
+       default n
+       help
+         Enable support for SE Linux in applets ls, ps, and id.  Also provide
+         the option of compiling in SE Linux applets.
+
+         If you do not have a complete SE Linux Full Userland installed, this
+         stuff will not compile.  Go visit
+               http://www.nsa.gov/selinux/index.html
+         to download the necessary stuff to allow busybox to compile with this
+         option enabled.
+
+         Most people will leave this set to 'N'.
+
+endmenu
+
+menu 'Build Options'
+
+config CONFIG_STATIC
+       bool "Build BusyBox as a static binary (no shared libs)"
+       default n
+       help
+         If you want to build a static BusyBox binary, which does not
+         use or require any shared libraries, then enable this option.
+         This can cause BusyBox to be considerably larger, so you should
+         leave this option false unless you have a good reason (i.e.
+         your target platform does not support shared libraries, or
+         you are building an initrd which doesn't need anything but
+         BusyBox, etc).
+
+         Most people will leave this set to 'N'.
+
+config CONFIG_LFS
+       bool "Build with Large File Support (for accessing files > 2 GB)"
+       default n
+       select FDISK_SUPPORT_LARGE_DISKS
+       help
+         If you want to build BusyBox with large file support, then enable
+         this option.  This will have no effect if your kernel or your C
+         library lacks large file support for large files.  Some of the
+         programs that can benefit from large file support include dd, gzip,
+         cp, mount, tar, and many others.  If you want to access files larger
+         than 2 Gigabytes, enable this option.  Otherwise, leave it set to 'N'.
+
+config USING_CROSS_COMPILER
+       bool "Do you want to build BusyBox with a Cross Compiler?"
+       default n
+       help
+         Do you want to build BusyBox with a Cross Compiler?  If so,
+         then enable this option.  Otherwise leave it set to 'N'.
+
+config CROSS_COMPILER_PREFIX
+       string "Cross Compiler prefix"
+       default "/usr/i386-linux-uclibc/bin/i386-uclibc-"
+       depends on USING_CROSS_COMPILER
+       help
+         If you want to build BusyBox with a cross compiler, then you
+         will need to set this to the cross-compiler prefix.  For example,
+         if my cross-compiler is /usr/i386-linux-uclibc/bin/i386-uclibc-gcc
+         then I would enter '/usr/i386-linux-uclibc/bin/i386-uclibc-' here,
+         which will ensure the correct compiler is used.
+
+config EXTRA_CFLAGS_OPTIONS
+       string "Any extra CFLAGS options for the compiler?"
+       default ""
+       help
+         Do you want to pass any extra CFLAGS options to the compiler as
+         you build BusyBox? If so, this is the option for you...  For example,
+         if you want to add some simple compiler switches (like -march=i686),
+         or check for warnings using -Werror, just those options here.
+
+endmenu
+
+menu 'Installation Options'
+
+config CONFIG_INSTALL_NO_USR
+       bool "Don't use /usr"
+       default n
+       help
+         Disable use of /usr. Don't activate this option if you don't know
+         that you really want this behaviour.
+
+config PREFIX
+       string "BusyBox installation prefix"
+       default "./_install"
+       help
+         Define your directory to install BusyBox files/subdirs in.
+
+
+
+endmenu
+
+source archival/Config.in
+source coreutils/Config.in
+source console-tools/Config.in
+source debianutils/Config.in
+source editors/Config.in
+source findutils/Config.in
+source init/Config.in
+source loginutils/Config.in
+source miscutils/Config.in
+source modutils/Config.in
+source networking/Config.in
+source procps/Config.in
+source shell/Config.in
+source sysklogd/Config.in
+source util-linux/Config.in
+
+menu 'Debugging Options'
+
+config CONFIG_DEBUG
+       bool "Build BusyBox with Debugging symbols"
+       default n
+       help
+         Say Y here if you wish to compile BusyBox with debugging symbols.
+         This will allow you to use a debugger to examine BusyBox internals
+         while applets are running.  This increases the size of the binary
+         considerably and should only be used when doing development.
+         If you are doing development and want to debug BusyBox, answer Y.
+
+         Most people should answer N.
+
+choice
+       prompt "Additional debugging library"
+       default CONFIG_NO_DEBUG_LIB
+       depends on CONFIG_DEBUG
+       help
+         Using an additional debugging library will make BusyBox become
+         considerable larger and will cause it to run more slowly.  You
+         should always leave this option disabled for production use.
+
+         dmalloc support:
+         ----------------
+         This enables compiling with dmalloc ( http://dmalloc.com/ )
+         which is an excellent public domain mem leak and malloc problem
+         detector.  To enable dmalloc, before running busybox you will
+         want to properly set your environment, for example:
+           export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile
+         The 'debug=' value is generated using the following command
+           dmalloc -p log-stats -p log-non-free -p log-bad-space -p log-elapsed-time \
+              -p check-fence -p check-heap -p check-lists -p check-blank \
+              -p check-funcs -p realloc-copy -p allow-free-null
+
+         Electric-fence support:
+         -----------------------
+         This enables compiling with Electric-fence support.  Electric
+         fence is another very useful malloc debugging library which uses
+         your computer's virtual memory hardware to detect illegal memory
+         accesses.  This support will make BusyBox be considerable larger
+         and run slower, so you should leave this option disabled unless
+         you are hunting a hard to find memory problem.
+
+
+config CONFIG_NO_DEBUG_LIB
+       bool "None"
+
+config CONFIG_DMALLOC
+       bool "Dmalloc"
+
+config CONFIG_EFENCE
+       bool "Electric-fence"
+
+endchoice
+
+
+endmenu
+
diff --git a/busybox/sysdeps/linux/defconfig b/busybox/sysdeps/linux/defconfig
new file mode 100644 (file)
index 0000000..fd2b118
--- /dev/null
@@ -0,0 +1,421 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_FEATURE_VERBOSE_USAGE=y
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_LOCALE_SUPPORT is not set
+# CONFIG_FEATURE_DEVFS is not set
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+# CONFIG_LFS is not set
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+# CONFIG_AR is not set
+CONFIG_BUNZIP2=y
+# CONFIG_CPIO is not set
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GUNZIP=y
+# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
+CONFIG_GZIP=y
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_BZIP2=y
+# CONFIG_FEATURE_TAR_FROM is not set
+CONFIG_FEATURE_TAR_GZIP=y
+# CONFIG_FEATURE_TAR_COMPRESS is not set
+CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
+# CONFIG_UNCOMPRESS is not set
+CONFIG_UNZIP=y
+
+#
+# Common options for cpio and tar
+#
+# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+# CONFIG_CAL is not set
+CONFIG_CAT=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+CONFIG_CMP=y
+CONFIG_CP=y
+CONFIG_CUT=y
+CONFIG_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+CONFIG_DD=y
+CONFIG_DF=y
+CONFIG_DIRNAME=y
+# CONFIG_DOS2UNIX is not set
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_EXPR=y
+CONFIG_FALSE=y
+# CONFIG_FOLD is not set
+CONFIG_HEAD=y
+# CONFIG_FEATURE_FANCY_HEAD is not set
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+CONFIG_INSTALL=y
+# CONFIG_LENGTH is not set
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+# CONFIG_MD5SUM is not set
+CONFIG_MKDIR=y
+# CONFIG_MKFIFO is not set
+CONFIG_MKNOD=y
+CONFIG_MV=y
+# CONFIG_OD is not set
+# CONFIG_PRINTF is not set
+CONFIG_PWD=y
+# CONFIG_REALPATH is not set
+CONFIG_RM=y
+CONFIG_RMDIR=y
+# CONFIG_SEQ is not set
+# CONFIG_SHA1SUM is not set
+CONFIG_SLEEP=y
+# CONFIG_FEATURE_FANCY_SLEEP is not set
+CONFIG_SORT=y
+# CONFIG_STTY is not set
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TEST=y
+
+#
+# test (forced enabled for use with shell)
+#
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+# CONFIG_WATCH is not set
+CONFIG_WC=y
+# CONFIG_WHO is not set
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls and more
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+# CONFIG_DUMPKMAP is not set
+# CONFIG_LOADFONT is not set
+# CONFIG_LOADKMAP is not set
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+# CONFIG_SETKEYCODES is not set
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+CONFIG_READLINK=y
+# CONFIG_RUN_PARTS is not set
+# CONFIG_START_STOP_DAEMON is not set
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+# CONFIG_AWK is not set
+# CONFIG_PATCH is not set
+CONFIG_SED=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+# CONFIG_FEATURE_FIND_NEWER is not set
+# CONFIG_FEATURE_FIND_INUM is not set
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_INITRD=y
+# CONFIG_FEATURE_INIT_COREDUMPS is not set
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_HALT=y
+CONFIG_POWEROFF=y
+CONFIG_REBOOT=y
+# CONFIG_MESG is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_USE_BB_PWD_GRP is not set
+# CONFIG_ADDGROUP is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DELUSER is not set
+# CONFIG_GETTY is not set
+# CONFIG_LOGIN is not set
+# CONFIG_PASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+# CONFIG_CROND is not set
+# CONFIG_CRONTAB is not set
+# CONFIG_DC is not set
+# CONFIG_DEVFSD is not set
+# CONFIG_LAST is not set
+# CONFIG_HDPARM is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_MT is not set
+# CONFIG_RX is not set
+CONFIG_STRINGS=y
+CONFIG_TIME=y
+# CONFIG_WATCHDOG is not set
+
+#
+# Linux Module Utilities
+#
+# CONFIG_INSMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_RMMOD is not set
+
+#
+# Networking Utilities
+#
+# CONFIG_FEATURE_IPV6 is not set
+# CONFIG_ARPING is not set
+# CONFIG_FTPGET is not set
+# CONFIG_FTPPUT is not set
+CONFIG_HOSTNAME=y
+# CONFIG_HTTPD is not set
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+# CONFIG_FEATURE_IFCONFIG_HW is not set
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+# CONFIG_IFUPDOWN is not set
+# CONFIG_INETD is not set
+# CONFIG_IP is not set
+# CONFIG_IPCALC is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_NAMEIF is not set
+# CONFIG_NC is not set
+# CONFIG_NETSTAT is not set
+# CONFIG_NSLOOKUP is not set
+CONFIG_PING=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_ROUTE=y
+# CONFIG_TELNET is not set
+# CONFIG_TELNETD is not set
+# CONFIG_TFTP is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+# CONFIG_FEATURE_WGET_IP6_LITERAL is not set
+
+#
+# udhcp Server/Client
+#
+# CONFIG_UDHCPD is not set
+# CONFIG_UDHCPC is not set
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_PIDOF=y
+CONFIG_PS=y
+# CONFIG_RENICE is not set
+# CONFIG_TOP is not set
+CONFIG_UPTIME=y
+# CONFIG_SYSCTL is not set
+
+#
+# Another Bourne-like Shell
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_ALIAS=y
+CONFIG_ASH_MATH_SUPPORT=y
+CONFIG_ASH_MATH_SUPPORT_64=y
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
+# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
+CONFIG_FEATURE_COMMAND_EDITING=y
+CONFIG_FEATURE_COMMAND_HISTORY=15
+CONFIG_FEATURE_COMMAND_SAVEHISTORY=y
+CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
+# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_SH_FANCY_PROMPT=y
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+CONFIG_FEATURE_ROTATE_LOGFILE=y
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+# CONFIG_FBSET is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+# CONFIG_FDISK is not set
+# CONFIG_FREERAMDISK is not set
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_GETOPT is not set
+CONFIG_HEXDUMP=y
+# CONFIG_HWCLOCK is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_MKSWAP is not set
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_PIVOT_ROOT=y
+# CONFIG_RDATE is not set
+CONFIG_SWAPONOFF=y
+CONFIG_MOUNT=y
+# CONFIG_NFSMOUNT is not set
+CONFIG_UMOUNT=y
+# CONFIG_FEATURE_MOUNT_FORCE is not set
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/sysklogd/Config.in b/busybox/sysklogd/Config.in
new file mode 100644 (file)
index 0000000..f77d79e
--- /dev/null
@@ -0,0 +1,109 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "System Logging Utilities"
+
+config CONFIG_SYSLOGD
+       bool "syslogd"
+       default n
+       help
+         The syslogd utility is used to record logs of all the
+         significant events that occur on a system.  Every
+         message that is logged records the date and time of the
+         event, and will generally also record the name of the
+         application that generated the message.  When used in
+         conjunction with klogd, messages from the Linux kernel
+         can also be recorded.  This is terribly useful,
+         especially for finding what happened when something goes
+         wrong.  And something almost always will go wrong if
+         you wait long enough....
+
+config CONFIG_FEATURE_ROTATE_LOGFILE
+       bool "  Rotate message files"
+       default n
+       depends on CONFIG_SYSLOGD
+       help
+         This enables syslogd to rotate the message files
+         on his own. No need to use an external rotatescript.
+
+config CONFIG_FEATURE_REMOTE_LOG
+       bool "  Remote Log support"
+       default n
+       depends on CONFIG_SYSLOGD
+       help
+         When you enable this feature, the syslogd utility can
+         be used to send system log messages to another system
+         connected via a network.  This allows the remote
+         machine to log all the system messages, which can be
+         terribly useful for reducing the number of serial
+         cables you use.  It can also be a very good security
+         measure to prevent system logs from being tampered with
+         by an intruder.
+
+config CONFIG_FEATURE_IPC_SYSLOG
+       bool "  Circular Buffer support"
+       default n
+       depends on CONFIG_SYSLOGD
+       help
+         When you enable this feature, the syslogd utility will
+         use a circular buffer to record system log messages.
+         When the buffer is filled it will continue to overwrite
+         the oldest messages.  This can be very useful for
+         systems with little or no permanent storage, since
+         otherwise system logs can eventually fill up your
+         entire filesystem, which may cause your system to
+         break badly.
+
+config CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
+       int "    Circular buffer size in Kbytes (minimum 4KB)"
+       default 16
+       depends on CONFIG_FEATURE_IPC_SYSLOG
+       help
+         This option sets the size of the circular buffer
+         used to record system log messages.
+
+config CONFIG_LOGREAD
+       bool "  logread"
+       default y
+       depends on CONFIG_FEATURE_IPC_SYSLOG
+       help
+         If you enabled Circular Buffer support, you almost
+         certainly want to enable this feature as well.  This
+         utility will allow you to read the messages that are
+         stored in the syslogd circular buffer.
+
+config CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
+       bool "    logread double buffering"
+       default n
+       depends on CONFIG_LOGREAD
+       help
+         'logread' ouput to slow serial terminals can have
+         side effects on syslog because of the semaphore.
+         This option make logread to double buffer copy
+         from circular buffer, minimizing semaphore
+         contention at some minor memory expense.
+
+config CONFIG_KLOGD
+       bool "klogd"
+       default n
+       depends on CONFIG_SYSLOGD
+       help
+         klogd is a utility which intercepts and logs all
+         messages from the Linux kernel and sends the messages
+         out to the 'syslogd' utility so they can be logged.  If
+         you wish to record the messages produced by the kernel,
+         you should enable this option.
+
+config CONFIG_LOGGER
+       bool "logger"
+       default n
+       help
+           The logger utility allows you to send arbitrary text
+           messages to the system log (i.e. the 'syslogd' utility) so
+           they can be logged.  This is generally used to help locate
+           problems that occur within programs and scripts.
+
+endmenu
+
diff --git a/busybox/sysklogd/Makefile b/busybox/sysklogd/Makefile
new file mode 100644 (file)
index 0000000..78b0c00
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_buildddir=..
+srcdir=$(top_srcdir)/sysklogd
+SYSKLOGD_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/sysklogd/Makefile.in b/busybox/sysklogd/Makefile.in
new file mode 100644 (file)
index 0000000..99a5f82
--- /dev/null
@@ -0,0 +1,39 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+SYSKLOGD_AR:=sysklogd.a
+ifndef $(SYSKLOGD_DIR)
+SYSKLOGD_DIR:=$(top_builddir)/sysklogd/
+endif
+srcdir=$(top_srcdir)/sysklogd
+
+SYSKLOGD-:=
+SYSKLOGD-$(CONFIG_KLOGD)               += klogd.o
+SYSKLOGD-$(CONFIG_LOGGER)              += logger.o
+SYSKLOGD-$(CONFIG_LOGREAD)             += logread.o
+SYSKLOGD-$(CONFIG_SYSLOGD)             += syslogd.o
+
+libraries-y+=$(SYSKLOGD_DIR)$(SYSKLOGD_AR)
+
+$(SYSKLOGD_DIR)$(SYSKLOGD_AR): $(patsubst %,$(SYSKLOGD_DIR)%, $(SYSKLOGD-y))
+       $(AR) -ro $@ $(patsubst %,$(SYSKLOGD_DIR)%, $(SYSKLOGD-y))
+
+$(SYSKLOGD_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
diff --git a/busybox/sysklogd/klogd.c b/busybox/sysklogd/klogd.c
new file mode 100644 (file)
index 0000000..c908b59
--- /dev/null
@@ -0,0 +1,165 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini klogd implementation for busybox
+ *
+ * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
+ * Changes: Made this a standalone busybox module which uses standalone
+ *                                     syslog() client interface.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
+ *
+ * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>            /* for our signal() handlers */
+#include <string.h>            /* strncpy() */
+#include <errno.h>             /* errno and friends */
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/syslog.h>
+#include <sys/klog.h>
+
+#include "busybox.h"
+
+static void klogd_signal(int sig)
+{
+       klogctl(7, NULL, 0);
+       klogctl(0, 0, 0);
+       /* logMessage(0, "Kernel log daemon exiting."); */
+       syslog(LOG_NOTICE, "Kernel log daemon exiting.");
+       exit(EXIT_SUCCESS);
+}
+
+static void doKlogd(const int console_log_level) __attribute__ ((noreturn));
+static void doKlogd(const int console_log_level)
+{
+       int priority = LOG_INFO;
+       char log_buffer[4096];
+       int i, n, lastc;
+       char *start;
+
+       openlog("kernel", 0, LOG_KERN);
+
+       /* Set up sig handlers */
+       signal(SIGINT, klogd_signal);
+       signal(SIGKILL, klogd_signal);
+       signal(SIGTERM, klogd_signal);
+       signal(SIGHUP, SIG_IGN);
+
+       /* "Open the log. Currently a NOP." */
+       klogctl(1, NULL, 0);
+
+       /* Set level of kernel console messaging.. */
+       if (console_log_level != -1)
+               klogctl(8, NULL, console_log_level);
+
+       syslog(LOG_NOTICE, "klogd started: " BB_BANNER);
+
+       while (1) {
+               /* Use kernel syscalls */
+               memset(log_buffer, '\0', sizeof(log_buffer));
+               n = klogctl(2, log_buffer, sizeof(log_buffer));
+               if (n < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       syslog(LOG_ERR, "klogd: Error return from sys_sycall: %d - %m.\n", errno);
+                       exit(EXIT_FAILURE);
+               }
+
+               /* klogctl buffer parsing modelled after code in dmesg.c */
+               start = &log_buffer[0];
+               lastc = '\0';
+               for (i = 0; i < n; i++) {
+                       if (lastc == '\0' && log_buffer[i] == '<') {
+                               priority = 0;
+                               i++;
+                               while (isdigit(log_buffer[i])) {
+                                       priority = priority * 10 + (log_buffer[i] - '0');
+                                       i++;
+                               }
+                               if (log_buffer[i] == '>')
+                                       i++;
+                               start = &log_buffer[i];
+                       }
+                       if (log_buffer[i] == '\n') {
+                               log_buffer[i] = '\0';   /* zero terminate this message */
+                               syslog(priority, "%s", start);
+                               start = &log_buffer[i + 1];
+                               priority = LOG_INFO;
+                       }
+                       lastc = log_buffer[i];
+               }
+       }
+}
+
+extern int klogd_main(int argc, char **argv)
+{
+       /* no options, no getopt */
+       int opt;
+       int doFork = TRUE;
+       unsigned char console_log_level = -1;
+
+       /* do normal option parsing */
+       while ((opt = getopt(argc, argv, "c:n")) > 0) {
+               switch (opt) {
+               case 'c':
+                       if ((optarg == NULL) || (optarg[1] != '\0')) {
+                               bb_show_usage();
+                       }
+                       /* Valid levels are between 1 and 8 */
+                       console_log_level = *optarg - '1';
+                       if (console_log_level > 7) {
+                               bb_show_usage();
+                       }
+                       console_log_level++;
+
+                       break;
+               case 'n':
+                       doFork = FALSE;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+       if (doFork) {
+#if defined(__uClinux__)
+               vfork_daemon_rexec(0, 1, argc, argv, "-n");
+#else /* __uClinux__ */
+               if (daemon(0, 1) < 0)
+                       bb_perror_msg_and_die("daemon");
+#endif /* __uClinux__ */
+       }
+       doKlogd(console_log_level);
+
+       return EXIT_SUCCESS;
+}
+
+/*
+Local Variables
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/sysklogd/logger.c b/busybox/sysklogd/logger.c
new file mode 100644 (file)
index 0000000..1615531
--- /dev/null
@@ -0,0 +1,204 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini logger implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "busybox.h"
+#if !defined CONFIG_SYSLOGD
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+#else
+#include <sys/syslog.h>
+#  ifndef __dietlibc__
+       /* We have to do this since the header file defines static
+        * structures.  Argh.... bad libc, bad, bad...
+        */
+       typedef struct _code {
+               char *c_name;
+               int c_val;
+       } CODE;
+       extern CODE prioritynames[];
+       extern CODE facilitynames[];
+#  endif
+#endif
+
+/* Decode a symbolic name to a numeric value
+ * this function is based on code
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+static int decode(char *name, CODE * codetab)
+{
+       CODE *c;
+
+       if (isdigit(*name))
+               return (atoi(name));
+       for (c = codetab; c->c_name; c++) {
+               if (!strcasecmp(name, c->c_name)) {
+                       return (c->c_val);
+               }
+       }
+
+       return (-1);
+}
+
+/* Decode a symbolic name to a numeric value
+ * this function is based on code
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+static int pencode(char *s)
+{
+       char *save;
+       int lev, fac = LOG_USER;
+
+       for (save = s; *s && *s != '.'; ++s);
+       if (*s) {
+               *s = '\0';
+               fac = decode(save, facilitynames);
+               if (fac < 0)
+                       bb_error_msg_and_die("unknown facility name: %s", save);
+               *s++ = '.';
+       } else {
+               s = save;
+       }
+       lev = decode(s, prioritynames);
+       if (lev < 0)
+               bb_error_msg_and_die("unknown priority name: %s", save);
+       return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+
+extern int logger_main(int argc, char **argv)
+{
+       int pri = LOG_USER | LOG_NOTICE;
+       int option = 0;
+       int c, i, opt;
+       char buf[1024], name[128];
+
+       /* Fill out the name string early (may be overwritten later) */
+       my_getpwuid(name, geteuid(), sizeof(name));
+
+       /* Parse any options */
+       while ((opt = getopt(argc, argv, "p:st:")) > 0) {
+               switch (opt) {
+                       case 's':
+                               option |= LOG_PERROR;
+                               break;
+                       case 'p':
+                               pri = pencode(optarg);
+                               break;
+                       case 't':
+                               safe_strncpy(name, optarg, sizeof(name));
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       openlog(name, option, (pri | LOG_FACMASK));
+       if (optind == argc) {
+               do {
+                       /* read from stdin */
+                       i = 0;
+                       while ((c = getc(stdin)) != EOF && c != '\n' &&
+                                       i < (sizeof(buf)-1)) {
+                               buf[i++] = c;
+                       }
+                       if (i > 0) {
+                               buf[i++] = '\0';
+                               syslog(pri, "%s", buf);
+                       }
+               } while (c != EOF);
+       } else {
+               char *message = NULL;
+               int len = argc - optind; /* for the space between the args
+                                           and  '\0' */
+               opt = len;
+               argv += optind;
+               for (i = 0; i < opt; i++) {
+                       len += strlen(*argv);
+                       message = xrealloc(message, len);
+                       if(!i)
+                               message[0] = 0;
+                        else
+                       strcat(message, " ");
+                       strcat(message, *argv);
+                       argv++;
+               }
+               syslog(pri, "%s", message);
+       }
+
+       closelog();
+       return EXIT_SUCCESS;
+}
+
+
+/*-
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This is the original license statement for the decode and pencode functions.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+
diff --git a/busybox/sysklogd/logread.c b/busybox/sysklogd/logread.c
new file mode 100644 (file)
index 0000000..70d1db6
--- /dev/null
@@ -0,0 +1,186 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * circular buffer syslog implementation for busybox
+ *
+ * Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/types.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include "busybox.h"
+
+static const long KEY_ID = 0x414e4547; /*"GENA"*/
+
+static struct shbuf_ds {
+       int size;               // size of data written
+       int head;               // start of message list
+       int tail;               // end of message list
+       char data[1];           // data/messages
+} *buf = NULL;                 // shared memory pointer
+
+
+// Semaphore operation structures
+static struct sembuf SMrup[1] = {{0, -1, IPC_NOWAIT | SEM_UNDO}}; // set SMrup
+static struct sembuf SMrdn[2] = {{1, 0}, {0, +1, SEM_UNDO}}; // set SMrdn
+
+static int     log_shmid = -1; // ipc shared memory id
+static int     log_semid = -1; // ipc semaphore id
+static jmp_buf jmp_env;
+
+static void error_exit(const char *str);
+static void interrupted(int sig);
+
+/*
+ * sem_up - up()'s a semaphore.
+ */
+static inline void sem_up(int semid)
+{
+       if ( semop(semid, SMrup, 1) == -1 )
+               error_exit("semop[SMrup]");
+}
+
+/*
+ * sem_down - down()'s a semaphore
+ */
+static inline void sem_down(int semid)
+{
+       if ( semop(semid, SMrdn, 2) == -1 )
+               error_exit("semop[SMrdn]");
+}
+
+extern int logread_main(int argc, char **argv)
+{
+       int i;
+       int follow=0;
+
+       if (argc == 2 && strcmp(argv[1],"-f")==0) {
+               follow = 1;
+       } else {
+               /* no options, no getopt */
+               if (argc > 1)
+                       bb_show_usage();
+       }
+
+       // handle intrrupt signal
+       if (setjmp(jmp_env)) goto output_end;
+
+       // attempt to redefine ^C signal
+       signal(SIGINT, interrupted);
+
+       if ( (log_shmid = shmget(KEY_ID, 0, 0)) == -1)
+               error_exit("Can't find circular buffer");
+
+       // Attach shared memory to our char*
+       if ( (buf = shmat(log_shmid, NULL, SHM_RDONLY)) == NULL)
+               error_exit("Can't get access to circular buffer from syslogd");
+
+       if ( (log_semid = semget(KEY_ID, 0, 0)) == -1)
+               error_exit("Can't get access to semaphone(s) for circular buffer from syslogd");
+
+       // Suppose atomic memory move
+       i = follow ? buf->tail : buf->head;
+
+       do {
+#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
+               char *buf_data;
+               int log_len,j;
+#endif
+
+               sem_down(log_semid);
+
+               //printf("head: %i tail: %i size: %i\n",buf->head,buf->tail,buf->size);
+               if (buf->head == buf->tail || i==buf->tail) {
+                       if (follow) {
+                               sem_up(log_semid);
+                               sleep(1);       /* TODO: replace me with a sleep_on */
+                               continue;
+                       } else {
+                               printf("<empty syslog>\n");
+                       }
+               }
+
+               // Read Memory
+#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
+               log_len = buf->tail - i;
+               if (log_len < 0)
+                       log_len += buf->size;
+               buf_data = (char *)malloc(log_len);
+               if (!buf_data)
+                       error_exit("malloc failed");
+
+               if (buf->tail < i) {
+                       memcpy(buf_data, buf->data+i, buf->size-i);
+                       memcpy(buf_data+buf->size-i, buf->data, buf->tail);
+               } else {
+                       memcpy(buf_data, buf->data+i, buf->tail-i);
+               }
+               i = buf->tail;
+
+#else
+               while ( i != buf->tail) {
+                       printf("%s", buf->data+i);
+                       i+= strlen(buf->data+i) + 1;
+                       if (i >= buf->size )
+                               i=0;
+               }
+#endif
+               // release the lock on the log chain
+               sem_up(log_semid);
+
+#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
+               for (j=0; j < log_len; j+=strlen(buf_data+j)+1) {
+                       printf("%s", buf_data+j);
+                       if (follow)
+                               fflush(stdout);
+               }
+               free(buf_data);
+#endif
+       } while (follow);
+
+output_end:
+       if (log_shmid != -1)
+               shmdt(buf);
+
+       return EXIT_SUCCESS;
+}
+
+static void interrupted(int sig){
+       signal(SIGINT, SIG_IGN);
+       longjmp(jmp_env, 1);
+}
+
+static void error_exit(const char *str){
+       perror(str);
+       //release all acquired resources
+       if (log_shmid != -1)
+               shmdt(buf);
+
+       exit(1);
+}
diff --git a/busybox/sysklogd/syslogd.c b/busybox/sysklogd/syslogd.c
new file mode 100644 (file)
index 0000000..8c6c44e
--- /dev/null
@@ -0,0 +1,712 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini syslogd implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
+ *
+ * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/param.h>
+
+#include "busybox.h"
+
+/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+/* Path for the file where all log messages are written */
+#define __LOG_FILE "/var/log/messages"
+
+/* Path to the unix socket */
+static char lfile[MAXPATHLEN];
+
+static const char *logFilePath = __LOG_FILE;
+
+#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
+/* max size of message file before being rotated */
+static int logFileSize = 200 * 1024;
+
+/* number of rotated message files */
+static int logFileRotate = 1;
+#endif
+
+/* interval between marks in seconds */
+static int MarkInterval = 20 * 60;
+
+/* localhost's name */
+static char LocalHostName[64];
+
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+#include <netinet/in.h>
+/* udp socket for logging to remote host */
+static int remotefd = -1;
+static struct sockaddr_in remoteaddr;
+
+/* where do we log? */
+static char *RemoteHost;
+
+/* what port to log to? */
+static int RemotePort = 514;
+
+/* To remote log or not to remote log, that is the question. */
+static int doRemoteLog = FALSE;
+static int local_logging = FALSE;
+#endif
+
+/* Make loging output smaller. */
+static bool small = false;
+
+
+#define MAXLINE         1024   /* maximum line length */
+
+
+/* circular buffer variables/structures */
+#ifdef CONFIG_FEATURE_IPC_SYSLOG
+
+#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
+#error Sorry, you must set the syslogd buffer size to at least 4KB.
+#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
+#endif
+
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+
+/* our shared key */
+static const long KEY_ID = 0x414e4547; /*"GENA" */
+
+// Semaphore operation structures
+static struct shbuf_ds {
+       int size;                       // size of data written
+       int head;                       // start of message list
+       int tail;                       // end of message list
+       char data[1];           // data/messages
+} *buf = NULL;                 // shared memory pointer
+
+static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} };       // set SMwup
+static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} };   // set SMwdn
+
+static int shmid = -1; // ipc shared memory id
+static int s_semid = -1;       // ipc semaphore id
+static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024);  // default shm size
+static int circular_logging = FALSE;
+
+/*
+ * sem_up - up()'s a semaphore.
+ */
+static inline void sem_up(int semid)
+{
+       if (semop(semid, SMwup, 1) == -1) {
+               bb_perror_msg_and_die("semop[SMwup]");
+       }
+}
+
+/*
+ * sem_down - down()'s a semaphore
+ */
+static inline void sem_down(int semid)
+{
+       if (semop(semid, SMwdn, 3) == -1) {
+               bb_perror_msg_and_die("semop[SMwdn]");
+       }
+}
+
+
+void ipcsyslog_cleanup(void)
+{
+       printf("Exiting Syslogd!\n");
+       if (shmid != -1) {
+               shmdt(buf);
+       }
+
+       if (shmid != -1) {
+               shmctl(shmid, IPC_RMID, NULL);
+       }
+       if (s_semid != -1) {
+               semctl(s_semid, 0, IPC_RMID, 0);
+       }
+}
+
+void ipcsyslog_init(void)
+{
+       if (buf == NULL) {
+               if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
+                       bb_perror_msg_and_die("shmget");
+               }
+
+               if ((buf = shmat(shmid, NULL, 0)) == NULL) {
+                       bb_perror_msg_and_die("shmat");
+               }
+
+               buf->size = shm_size - sizeof(*buf);
+               buf->head = buf->tail = 0;
+
+               // we'll trust the OS to set initial semval to 0 (let's hope)
+               if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
+                       if (errno == EEXIST) {
+                               if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
+                                       bb_perror_msg_and_die("semget");
+                               }
+                       } else {
+                               bb_perror_msg_and_die("semget");
+                       }
+               }
+       } else {
+               printf("Buffer already allocated just grab the semaphore?");
+       }
+}
+
+/* write message to buffer */
+void circ_message(const char *msg)
+{
+       int l = strlen(msg) + 1;        /* count the whole message w/ '\0' included */
+
+       sem_down(s_semid);
+
+       /*
+        * Circular Buffer Algorithm:
+        * --------------------------
+        *
+        * Start-off w/ empty buffer of specific size SHM_SIZ
+        * Start filling it up w/ messages. I use '\0' as separator to break up messages.
+        * This is also very handy since we can do printf on message.
+        *
+        * Once the buffer is full we need to get rid of the first message in buffer and
+        * insert the new message. (Note: if the message being added is >1 message then
+        * we will need to "remove" >1 old message from the buffer). The way this is done
+        * is the following:
+        *      When we reach the end of the buffer we set a mark and start from the beginning.
+        *      Now what about the beginning and end of the buffer? Well we have the "head"
+        *      index/pointer which is the starting point for the messages and we have "tail"
+        *      index/pointer which is the ending point for the messages. When we "display" the
+        *      messages we start from the beginning and continue until we reach "tail". If we
+        *      reach end of buffer, then we just start from the beginning (offset 0). "head" and
+        *      "tail" are actually offsets from the beginning of the buffer.
+        *
+        * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
+        *       a threasafe way of handling shared memory operations.
+        */
+       if ((buf->tail + l) < buf->size) {
+               /* before we append the message we need to check the HEAD so that we won't
+                  overwrite any of the message that we still need and adjust HEAD to point
+                  to the next message! */
+               if (buf->tail < buf->head) {
+                       if ((buf->tail + l) >= buf->head) {
+                               /* we need to move the HEAD to point to the next message
+                                * Theoretically we have enough room to add the whole message to the
+                                * buffer, because of the first outer IF statement, so we don't have
+                                * to worry about overflows here!
+                                */
+                               int k = buf->tail + l - buf->head;      /* we need to know how many bytes
+                                                                                                          we are overwriting to make
+                                                                                                          enough room */
+                               char *c =
+                                       memchr(buf->data + buf->head + k, '\0',
+                                                  buf->size - (buf->head + k));
+                               if (c != NULL) {        /* do a sanity check just in case! */
+                                       buf->head = c - buf->data + 1;  /* we need to convert pointer to
+                                                                                                          offset + skip the '\0' since
+                                                                                                          we need to point to the beginning
+                                                                                                          of the next message */
+                                       /* Note: HEAD is only used to "retrieve" messages, it's not used
+                                          when writing messages into our buffer */
+                               } else {        /* show an error message to know we messed up? */
+                                       printf("Weird! Can't find the terminator token??? \n");
+                                       buf->head = 0;
+                               }
+                       }
+               }
+
+               /* in other cases no overflows have been done yet, so we don't care! */
+               /* we should be ok to append the message now */
+               strncpy(buf->data + buf->tail, msg, l); /* append our message */
+               buf->tail += l; /* count full message w/ '\0' terminating char */
+       } else {
+               /* we need to break up the message and "circle" it around */
+               char *c;
+               int k = buf->tail + l - buf->size;      /* count # of bytes we don't fit */
+
+               /* We need to move HEAD! This is always the case since we are going
+                * to "circle" the message.
+                */
+               c = memchr(buf->data + k, '\0', buf->size - k);
+
+               if (c != NULL) {        /* if we don't have '\0'??? weird!!! */
+                       /* move head pointer */
+                       buf->head = c - buf->data + 1;
+
+                       /* now write the first part of the message */
+                       strncpy(buf->data + buf->tail, msg, l - k - 1);
+
+                       /* ALWAYS terminate end of buffer w/ '\0' */
+                       buf->data[buf->size - 1] = '\0';
+
+                       /* now write out the rest of the string to the beginning of the buffer */
+                       strcpy(buf->data, &msg[l - k - 1]);
+
+                       /* we need to place the TAIL at the end of the message */
+                       buf->tail = k + 1;
+               } else {
+                       printf
+                               ("Weird! Can't find the terminator token from the beginning??? \n");
+                       buf->head = buf->tail = 0;      /* reset buffer, since it's probably corrupted */
+               }
+
+       }
+       sem_up(s_semid);
+}
+#endif                                                 /* CONFIG_FEATURE_IPC_SYSLOG */
+
+/* Note: There is also a function called "message()" in init.c */
+/* Print a message to the log file. */
+static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
+static void message(char *fmt, ...)
+{
+       int fd;
+       struct flock fl;
+       va_list arguments;
+
+       fl.l_whence = SEEK_SET;
+       fl.l_start = 0;
+       fl.l_len = 1;
+
+#ifdef CONFIG_FEATURE_IPC_SYSLOG
+       if ((circular_logging == TRUE) && (buf != NULL)) {
+               char b[1024];
+
+               va_start(arguments, fmt);
+               vsnprintf(b, sizeof(b) - 1, fmt, arguments);
+               va_end(arguments);
+               circ_message(b);
+
+       } else
+#endif
+       if ((fd =
+                        device_open(logFilePath,
+                                                        O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
+                                                        O_NONBLOCK)) >= 0) {
+               fl.l_type = F_WRLCK;
+               fcntl(fd, F_SETLKW, &fl);
+#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
+               if ( logFileSize > 0 ) {
+                       struct stat statf;
+                       int r = fstat(fd, &statf);
+                       if( !r && (statf.st_mode & S_IFREG)
+                               && (lseek(fd,0,SEEK_END) > logFileSize) ) {
+                               if(logFileRotate > 0) {
+                                       int i;
+                                       char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
+                                       for(i=logFileRotate-1;i>0;i--) {
+                                               sprintf(oldFile, "%s.%d", logFilePath, i-1);
+                                               sprintf(newFile, "%s.%d", logFilePath, i);
+                                               rename(oldFile, newFile);
+                                       }
+                                       sprintf(newFile, "%s.%d", logFilePath, 0);
+                                       fl.l_type = F_UNLCK;
+                                       fcntl (fd, F_SETLKW, &fl);
+                                       close(fd);
+                                       rename(logFilePath, newFile);
+                                       fd = device_open (logFilePath,
+                                                  O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
+                                                  O_NONBLOCK);
+                                       fl.l_type = F_WRLCK;
+                                       fcntl (fd, F_SETLKW, &fl);
+                               } else {
+                                       ftruncate( fd, 0 );
+                               }
+                       }
+               }
+#endif
+               va_start(arguments, fmt);
+               vdprintf(fd, fmt, arguments);
+               va_end(arguments);
+               fl.l_type = F_UNLCK;
+               fcntl(fd, F_SETLKW, &fl);
+               close(fd);
+       } else {
+               /* Always send console messages to /dev/console so people will see them. */
+               if ((fd =
+                        device_open(_PATH_CONSOLE,
+                                                O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
+                       va_start(arguments, fmt);
+                       vdprintf(fd, fmt, arguments);
+                       va_end(arguments);
+                       close(fd);
+               } else {
+                       fprintf(stderr, "Bummer, can't print: ");
+                       va_start(arguments, fmt);
+                       vfprintf(stderr, fmt, arguments);
+                       fflush(stderr);
+                       va_end(arguments);
+               }
+       }
+}
+
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+static void init_RemoteLog(void)
+{
+       memset(&remoteaddr, 0, sizeof(remoteaddr));
+       remotefd = socket(AF_INET, SOCK_DGRAM, 0);
+
+       if (remotefd < 0) {
+               bb_error_msg("cannot create socket");
+       }
+
+       remoteaddr.sin_family = AF_INET;
+       remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
+       remoteaddr.sin_port = htons(RemotePort);
+}
+#endif
+
+static void logMessage(int pri, char *msg)
+{
+       time_t now;
+       char *timestamp;
+       static char res[20] = "";
+#ifdef CONFIG_FEATURE_REMOTE_LOG       
+       static char line[MAXLINE + 1];
+#endif
+       CODE *c_pri, *c_fac;
+
+       if (pri != 0) {
+               for (c_fac = facilitynames;
+                        c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
+               for (c_pri = prioritynames;
+                        c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
+               if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
+                       snprintf(res, sizeof(res), "<%d>", pri);
+               } else {
+                       snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
+               }
+       }
+
+       if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
+               msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
+               time(&now);
+               timestamp = ctime(&now) + 4;
+               timestamp[15] = '\0';
+       } else {
+               timestamp = msg;
+               timestamp[15] = '\0';
+               msg += 16;
+       }
+
+       /* todo: supress duplicates */
+
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+       if (doRemoteLog == TRUE) {
+               /* trying connect the socket */
+               if (-1 == remotefd) {
+                       init_RemoteLog();
+               }
+
+               /* if we have a valid socket, send the message */
+               if (-1 != remotefd) {
+                       now = 1;
+                       snprintf(line, sizeof(line), "<%d> %s", pri, msg);
+
+               retry:
+                       /* send message to remote logger */
+                       if(( -1 == sendto(remotefd, line, strlen(line), 0,
+                                                       (struct sockaddr *) &remoteaddr,
+                                                       sizeof(remoteaddr))) && (errno == EINTR)) {
+                               /* sleep now seconds and retry (with now * 2) */
+                               sleep(now);
+                               now *= 2;
+                               goto retry;
+                       }
+               }
+       }
+
+       if (local_logging == TRUE)
+#endif
+       {
+               /* now spew out the message to wherever it is supposed to go */
+               if (small)
+                       message("%s %s\n", timestamp, msg);
+               else
+                       message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
+       }
+}
+
+static void quit_signal(int sig)
+{
+       logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
+       unlink(lfile);
+#ifdef CONFIG_FEATURE_IPC_SYSLOG
+       ipcsyslog_cleanup();
+#endif
+
+       exit(TRUE);
+}
+
+static void domark(int sig)
+{
+       if (MarkInterval > 0) {
+               logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
+               alarm(MarkInterval);
+       }
+}
+
+/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
+ * enabled, we otherwise get a "storage size isn't constant error. */
+static int serveConnection(char *tmpbuf, int n_read)
+{
+       char *p = tmpbuf;
+
+       while (p < tmpbuf + n_read) {
+
+               int pri = (LOG_USER | LOG_NOTICE);
+               int num_lt = 0;
+               char line[MAXLINE + 1];
+               unsigned char c;
+               char *q = line;
+
+               while ((c = *p) && q < &line[sizeof(line) - 1]) {
+                       if (c == '<' && num_lt == 0) {
+                               /* Parse the magic priority number. */
+                               num_lt++;
+                               pri = 0;
+                               while (isdigit(*(++p))) {
+                                       pri = 10 * pri + (*p - '0');
+                               }
+                               if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
+                                       pri = (LOG_USER | LOG_NOTICE);
+                               }
+                       } else if (c == '\n') {
+                               *q++ = ' ';
+                       } else if (iscntrl(c) && (c < 0177)) {
+                               *q++ = '^';
+                               *q++ = c ^ 0100;
+                       } else {
+                               *q++ = c;
+                       }
+                       p++;
+               }
+               *q = '\0';
+               p++;
+               /* Now log it */
+               logMessage(pri, line);
+       }
+       return n_read;
+}
+
+static void doSyslogd(void) __attribute__ ((noreturn));
+static void doSyslogd(void)
+{
+       struct sockaddr_un sunx;
+       socklen_t addrLength;
+
+       int sock_fd;
+       fd_set fds;
+
+       /* Set up signal handlers. */
+       signal(SIGINT, quit_signal);
+       signal(SIGTERM, quit_signal);
+       signal(SIGQUIT, quit_signal);
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGCHLD, SIG_IGN);
+#ifdef SIGCLD
+       signal(SIGCLD, SIG_IGN);
+#endif
+       signal(SIGALRM, domark);
+       alarm(MarkInterval);
+
+       /* Create the syslog file so realpath() can work. */
+       if (realpath(_PATH_LOG, lfile) != NULL) {
+               unlink(lfile);
+       }
+
+       memset(&sunx, 0, sizeof(sunx));
+       sunx.sun_family = AF_UNIX;
+       strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
+       if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+               bb_perror_msg_and_die("Couldn't get file descriptor for socket "
+                                                  _PATH_LOG);
+       }
+
+       addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
+       if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
+               bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
+       }
+
+       if (chmod(lfile, 0666) < 0) {
+               bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
+       }
+#ifdef CONFIG_FEATURE_IPC_SYSLOG
+       if (circular_logging == TRUE) {
+               ipcsyslog_init();
+       }
+#endif
+
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+       if (doRemoteLog == TRUE) {
+               init_RemoteLog();
+       }
+#endif
+
+       logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
+
+       for (;;) {
+
+               FD_ZERO(&fds);
+               FD_SET(sock_fd, &fds);
+
+               if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
+                       if (errno == EINTR) {
+                               /* alarm may have happened. */
+                               continue;
+                       }
+                       bb_perror_msg_and_die("select error");
+               }
+
+               if (FD_ISSET(sock_fd, &fds)) {
+                       int i;
+
+                       RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
+
+                       memset(tmpbuf, '\0', MAXLINE + 1);
+                       if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
+                               serveConnection(tmpbuf, i);
+                       } else {
+                               bb_perror_msg_and_die("UNIX socket error");
+                       }
+                       RELEASE_CONFIG_BUFFER(tmpbuf);
+               }                               /* FD_ISSET() */
+       }                                       /* for main loop */
+}
+
+extern int syslogd_main(int argc, char **argv)
+{
+       int opt;
+
+       int doFork = TRUE;
+
+       char *p;
+
+       /* do normal option parsing */
+       while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
+               switch (opt) {
+               case 'm':
+                       MarkInterval = atoi(optarg) * 60;
+                       break;
+               case 'n':
+                       doFork = FALSE;
+                       break;
+               case 'O':
+                       logFilePath = optarg;
+                       break;
+#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
+               case 's':
+                       logFileSize = atoi(optarg) * 1024;
+                       break;
+               case 'b':
+                       logFileRotate = atoi(optarg);
+                       if( logFileRotate > 99 ) logFileRotate = 99;
+                       break;
+#endif
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+               case 'R':
+                       RemoteHost = bb_xstrdup(optarg);
+                       if ((p = strchr(RemoteHost, ':'))) {
+                               RemotePort = atoi(p + 1);
+                               *p = '\0';
+                       }
+                       doRemoteLog = TRUE;
+                       break;
+               case 'L':
+                       local_logging = TRUE;
+                       break;
+#endif
+#ifdef CONFIG_FEATURE_IPC_SYSLOG
+               case 'C':
+                       if (optarg) {
+                               int buf_size = atoi(optarg);
+                               if (buf_size >= 4) {
+                                       shm_size = buf_size * 1024;
+                               }
+                       }
+                       circular_logging = TRUE;
+                       break;
+#endif
+               case 'S':
+                       small = true;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+#ifdef CONFIG_FEATURE_REMOTE_LOG
+       /* If they have not specified remote logging, then log locally */
+       if (doRemoteLog == FALSE)
+               local_logging = TRUE;
+#endif
+
+
+       /* Store away localhost's name before the fork */
+       gethostname(LocalHostName, sizeof(LocalHostName));
+       if ((p = strchr(LocalHostName, '.'))) {
+               *p = '\0';
+       }
+
+       umask(0);
+
+       if (doFork == TRUE) {
+#if defined(__uClinux__)
+               vfork_daemon_rexec(0, 1, argc, argv, "-n");
+#else /* __uClinux__ */
+               if(daemon(0, 1) < 0)
+                       bb_perror_msg_and_die("daemon");
+#endif /* __uClinux__ */
+       }
+       doSyslogd();
+
+       return EXIT_SUCCESS;
+}
+
+/*
+Local Variables
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/testsuite/README b/busybox/testsuite/README
new file mode 100644 (file)
index 0000000..40439bf
--- /dev/null
@@ -0,0 +1,31 @@
+To run the test suite, change to this directory and run "./runtest".  It will
+run all of the test cases, and list those with unexpected outcomes.  Adding the
+-v option will cause it to show expected outcomes as well.  To only run the test
+cases for particular applets, specify them as parameters to runtest.
+
+The test cases for an applet reside in the subdirectory of the applet name.  The
+name of the test case should be the assertion that is tested.  The test case
+should be a shell fragment that returns successfully if the test case passes,
+and unsuccessfully otherwise.
+
+If the test case relies on a certain feature, it should include the string
+"FEATURE: " followed by the name of the feature in a comment.  If it is always
+expected to fail, it should include the string "XFAIL" in a comment.
+
+For the entire testsuite, the copyright is as follows:
+
+Copyright (C) 2001, 2002  Matt Kraai
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
diff --git a/busybox/testsuite/TODO b/busybox/testsuite/TODO
new file mode 100644 (file)
index 0000000..ced5715
--- /dev/null
@@ -0,0 +1,18 @@
+This testsuite is quite obviously a work in progress.  As such,
+there are a number of good extensions.  If you are looking for
+something to do, feel free to tackle one or more of the following:
+
+Buildroot support
+       Erik has put together a handy package for constructing and
+       testing busybox called buildroot.  Integrating this into
+       the testsuite would allow for greater test coverage (e.g.,
+       init, mount, and other privileged applications).
+
+libbb unit testing
+       Being able to test the functions of libbb individually may
+       help to prevent regressions.
+
+Standardization
+       This testsuite is totally bastardized.  It would be better
+       to use an existing test framework, such as dejagnu, greg,
+       or a XUnit clone (shunit?).
diff --git a/busybox/testsuite/basename/basename-does-not-remove-identical-extension b/busybox/testsuite/basename/basename-does-not-remove-identical-extension
new file mode 100644 (file)
index 0000000..4448fde
--- /dev/null
@@ -0,0 +1 @@
+test xfoo = x`busybox basename foo foo`
diff --git a/busybox/testsuite/basename/basename-works b/busybox/testsuite/basename/basename-works
new file mode 100644 (file)
index 0000000..38907d4
--- /dev/null
@@ -0,0 +1,2 @@
+test x$(basename $(pwd)) = x$(busybox basename $(pwd))
+
diff --git a/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input b/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input
new file mode 100644 (file)
index 0000000..e212a12
--- /dev/null
@@ -0,0 +1,2 @@
+echo foo | bzip2 | busybox bunzip2 > output
+echo foo | cmp - output
diff --git a/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file b/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file
new file mode 100644 (file)
index 0000000..f1d1550
--- /dev/null
@@ -0,0 +1,3 @@
+echo foo | bzip2 >foo.bz2
+busybox bunzip2 foo.bz2
+test ! -f foo.bz2
diff --git a/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file b/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file
new file mode 100644 (file)
index 0000000..7d4016e
--- /dev/null
@@ -0,0 +1,3 @@
+echo foo | bzip2 >foo.bz2
+busybox bzcat foo.bz2
+test -f foo.bz2
diff --git a/busybox/testsuite/cat/cat-prints-a-file b/busybox/testsuite/cat/cat-prints-a-file
new file mode 100644 (file)
index 0000000..e3f35a8
--- /dev/null
@@ -0,0 +1,3 @@
+echo I WANT > foo
+busybox cat foo >bar
+cmp foo bar
diff --git a/busybox/testsuite/cat/cat-prints-a-file-and-standard-input b/busybox/testsuite/cat/cat-prints-a-file-and-standard-input
new file mode 100644 (file)
index 0000000..bc92318
--- /dev/null
@@ -0,0 +1,7 @@
+echo I WANT > foo
+echo SOMETHING | busybox cat foo - >bar
+cat >baz <<EOF
+I WANT
+SOMETHING
+EOF
+cmp bar baz
diff --git a/busybox/testsuite/cmp/cmp-detects-difference b/busybox/testsuite/cmp/cmp-detects-difference
new file mode 100644 (file)
index 0000000..b9bb628
--- /dev/null
@@ -0,0 +1,9 @@
+echo foo >foo
+echo bar >bar
+set +e
+busybox cmp -s foo bar
+if [ $? != 0 ] ; then
+       exit 0;
+fi
+
+exit 1;
diff --git a/busybox/testsuite/cp/cp-a-files-to-dir b/busybox/testsuite/cp/cp-a-files-to-dir
new file mode 100644 (file)
index 0000000..39f8f81
--- /dev/null
@@ -0,0 +1,14 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+mkdir there
+busybox cp -a file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! dir1/file3 -ot there/dir1/file3
+test ! dir1/file3 -nt there/dir1/file3
diff --git a/busybox/testsuite/cp/cp-a-preserves-links b/busybox/testsuite/cp/cp-a-preserves-links
new file mode 100644 (file)
index 0000000..0c0cd96
--- /dev/null
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox cp -a bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/busybox/testsuite/cp/cp-copies-empty-file b/busybox/testsuite/cp/cp-copies-empty-file
new file mode 100644 (file)
index 0000000..ad25aa1
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+busybox cp foo bar
+cmp foo bar
diff --git a/busybox/testsuite/cp/cp-copies-large-file b/busybox/testsuite/cp/cp-copies-large-file
new file mode 100644 (file)
index 0000000..c2225c6
--- /dev/null
@@ -0,0 +1,3 @@
+dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
+busybox cp foo bar
+cmp foo bar
diff --git a/busybox/testsuite/cp/cp-copies-small-file b/busybox/testsuite/cp/cp-copies-small-file
new file mode 100644 (file)
index 0000000..d52a887
--- /dev/null
@@ -0,0 +1,3 @@
+echo I WANT > foo
+busybox cp foo bar
+cmp foo bar
diff --git a/busybox/testsuite/cp/cp-d-files-to-dir b/busybox/testsuite/cp/cp-d-files-to-dir
new file mode 100644 (file)
index 0000000..9571a56
--- /dev/null
@@ -0,0 +1,11 @@
+echo file number one > file1
+echo file number two > file2
+touch file3
+ln -s file2 link1
+mkdir there
+busybox cp -d file1 file2 file3 link1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
diff --git a/busybox/testsuite/cp/cp-dir-create-dir b/busybox/testsuite/cp/cp-dir-create-dir
new file mode 100644 (file)
index 0000000..2c89af6
--- /dev/null
@@ -0,0 +1,4 @@
+mkdir bar 
+touch bar/baz
+busybox cp -R bar foo 
+test -f foo/baz 
diff --git a/busybox/testsuite/cp/cp-dir-existing-dir b/busybox/testsuite/cp/cp-dir-existing-dir
new file mode 100644 (file)
index 0000000..5ba3f8e
--- /dev/null
@@ -0,0 +1,5 @@
+mkdir bar 
+touch bar/baz
+mkdir foo
+busybox cp -R bar foo 
+test -f foo/bar/baz 
diff --git a/busybox/testsuite/cp/cp-does-not-copy-unreadable-file b/busybox/testsuite/cp/cp-does-not-copy-unreadable-file
new file mode 100644 (file)
index 0000000..ce11bfa
--- /dev/null
@@ -0,0 +1,6 @@
+touch foo
+chmod a-r foo
+set +e
+busybox cp foo bar
+set -e
+test ! -f bar
diff --git a/busybox/testsuite/cp/cp-files-to-dir b/busybox/testsuite/cp/cp-files-to-dir
new file mode 100644 (file)
index 0000000..fdb8191
--- /dev/null
@@ -0,0 +1,11 @@
+echo file number one > file1
+echo file number two > file2
+touch file3
+ln -s file2 link1
+mkdir there
+busybox cp file1 file2 file3 link1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/file3
+test -f there/link1
+cmp there/file2 there/link1
diff --git a/busybox/testsuite/cp/cp-follows-links b/busybox/testsuite/cp/cp-follows-links
new file mode 100644 (file)
index 0000000..2d9f05e
--- /dev/null
@@ -0,0 +1,4 @@
+touch foo
+ln -s foo bar
+busybox cp bar baz
+test -f baz
diff --git a/busybox/testsuite/cp/cp-preserves-hard-links b/busybox/testsuite/cp/cp-preserves-hard-links
new file mode 100644 (file)
index 0000000..4de7b85
--- /dev/null
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
+touch foo
+ln foo bar
+mkdir baz
+busybox cp -d foo bar baz
+test baz/foo -ef baz/bar
diff --git a/busybox/testsuite/cp/cp-preserves-links b/busybox/testsuite/cp/cp-preserves-links
new file mode 100644 (file)
index 0000000..301dc5f
--- /dev/null
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox cp -d bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/busybox/testsuite/cp/cp-preserves-source-file b/busybox/testsuite/cp/cp-preserves-source-file
new file mode 100644 (file)
index 0000000..f0f5065
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+busybox cp foo bar
+test -f foo
diff --git a/busybox/testsuite/cut/cut-cuts-a-character b/busybox/testsuite/cut/cut-cuts-a-character
new file mode 100644 (file)
index 0000000..d6c5efa
--- /dev/null
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 3) = c
diff --git a/busybox/testsuite/cut/cut-cuts-a-closed-range b/busybox/testsuite/cut/cut-cuts-a-closed-range
new file mode 100644 (file)
index 0000000..9680b76
--- /dev/null
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 1-2) = ab
diff --git a/busybox/testsuite/cut/cut-cuts-a-field b/busybox/testsuite/cut/cut-cuts-a-field
new file mode 100644 (file)
index 0000000..4c7f440
--- /dev/null
@@ -0,0 +1 @@
+test $(echo -e "f1\tf2\tf3" | busybox cut -f 2) = f2
diff --git a/busybox/testsuite/cut/cut-cuts-an-open-range b/busybox/testsuite/cut/cut-cuts-an-open-range
new file mode 100644 (file)
index 0000000..1fbf277
--- /dev/null
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c -3) = abc
diff --git a/busybox/testsuite/cut/cut-cuts-an-unclosed-range b/busybox/testsuite/cut/cut-cuts-an-unclosed-range
new file mode 100644 (file)
index 0000000..a2b0cdb
--- /dev/null
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 3-) = cd
diff --git a/busybox/testsuite/date/date-R-works b/busybox/testsuite/date/date-R-works
new file mode 100644 (file)
index 0000000..ec3a067
--- /dev/null
@@ -0,0 +1,2 @@
+test x"`date -R`" = x"`busybox date -R`"
+
diff --git a/busybox/testsuite/date/date-format-works b/busybox/testsuite/date/date-format-works
new file mode 100644 (file)
index 0000000..f28d06c
--- /dev/null
@@ -0,0 +1 @@
+test x"`date +%d/%m/%y`" = x"`busybox date +%d/%m/%y`"
diff --git a/busybox/testsuite/date/date-u-works b/busybox/testsuite/date/date-u-works
new file mode 100644 (file)
index 0000000..7d9902a
--- /dev/null
@@ -0,0 +1,2 @@
+test x"`date -u`" = x"`busybox date -u`"
+
diff --git a/busybox/testsuite/date/date-works b/busybox/testsuite/date/date-works
new file mode 100644 (file)
index 0000000..2f6dd1e
--- /dev/null
@@ -0,0 +1,2 @@
+test x"`date`" = x"`busybox date`"
+
diff --git a/busybox/testsuite/dd/dd-accepts-if b/busybox/testsuite/dd/dd-accepts-if
new file mode 100644 (file)
index 0000000..03d1af8
--- /dev/null
@@ -0,0 +1,2 @@
+echo I WANT >foo
+test "$(busybox dd if=foo 2>/dev/null)" = "I WANT"
diff --git a/busybox/testsuite/dd/dd-accepts-of b/busybox/testsuite/dd/dd-accepts-of
new file mode 100644 (file)
index 0000000..84405e6
--- /dev/null
@@ -0,0 +1,2 @@
+echo I WANT | busybox dd of=foo 2>/dev/null
+echo I WANT | cmp foo -
diff --git a/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output b/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output
new file mode 100644 (file)
index 0000000..d890eb0
--- /dev/null
@@ -0,0 +1 @@
+test "$(echo I WANT | busybox dd 2>/dev/null)" = "I WANT"
diff --git a/busybox/testsuite/dd/dd-prints-count-to-standard-error b/busybox/testsuite/dd/dd-prints-count-to-standard-error
new file mode 100644 (file)
index 0000000..2187dc0
--- /dev/null
@@ -0,0 +1,2 @@
+echo I WANT | busybox dd of=foo >/dev/null 2>bar
+grep -q records bar
diff --git a/busybox/testsuite/dirname/dirname-handles-absolute-path b/busybox/testsuite/dirname/dirname-handles-absolute-path
new file mode 100644 (file)
index 0000000..ca1a51b
--- /dev/null
@@ -0,0 +1 @@
+test $(busybox dirname /foo/bar/baz) = /foo/bar
diff --git a/busybox/testsuite/dirname/dirname-handles-empty-path b/busybox/testsuite/dirname/dirname-handles-empty-path
new file mode 100644 (file)
index 0000000..04134a5
--- /dev/null
@@ -0,0 +1 @@
+test $(busybox dirname '') = .
diff --git a/busybox/testsuite/dirname/dirname-handles-multiple-slashes b/busybox/testsuite/dirname/dirname-handles-multiple-slashes
new file mode 100644 (file)
index 0000000..286f253
--- /dev/null
@@ -0,0 +1 @@
+test $(busybox dirname foo/bar///baz) = foo/bar
diff --git a/busybox/testsuite/dirname/dirname-handles-relative-path b/busybox/testsuite/dirname/dirname-handles-relative-path
new file mode 100644 (file)
index 0000000..ffe4ab4
--- /dev/null
@@ -0,0 +1 @@
+test $(busybox dirname foo/bar/baz) = foo/bar
diff --git a/busybox/testsuite/dirname/dirname-handles-root b/busybox/testsuite/dirname/dirname-handles-root
new file mode 100644 (file)
index 0000000..6bd62b8
--- /dev/null
@@ -0,0 +1 @@
+test $(busybox dirname /) = /
diff --git a/busybox/testsuite/dirname/dirname-handles-single-component b/busybox/testsuite/dirname/dirname-handles-single-component
new file mode 100644 (file)
index 0000000..24f9ae1
--- /dev/null
@@ -0,0 +1 @@
+test $(busybox dirname foo) = .
diff --git a/busybox/testsuite/dirname/dirname-works b/busybox/testsuite/dirname/dirname-works
new file mode 100644 (file)
index 0000000..f339c8f
--- /dev/null
@@ -0,0 +1,2 @@
+test x$(dirname $(pwd)) = x$(busybox dirname $(pwd))
+
diff --git a/busybox/testsuite/du/du-h-works b/busybox/testsuite/du/du-h-works
new file mode 100644 (file)
index 0000000..82041ab
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -h "$d" > logfile.gnu
+busybox du -h "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/du/du-k-works b/busybox/testsuite/du/du-k-works
new file mode 100644 (file)
index 0000000..177a1a2
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -k "$d" > logfile.gnu
+busybox du -k "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/du/du-l-works b/busybox/testsuite/du/du-l-works
new file mode 100644 (file)
index 0000000..61e9140
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -l "$d" > logfile.gnu
+busybox du -l "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/du/du-m-works b/busybox/testsuite/du/du-m-works
new file mode 100644 (file)
index 0000000..bc97073
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -m "$d" > logfile.gnu
+busybox du -m "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/du/du-s-works b/busybox/testsuite/du/du-s-works
new file mode 100644 (file)
index 0000000..f0b3bf0
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -s "$d" > logfile.gnu
+busybox du -s "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/du/du-works b/busybox/testsuite/du/du-works
new file mode 100644 (file)
index 0000000..47949c6
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du "$d" > logfile.gnu
+busybox du "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/echo/echo-does-not-print-newline b/busybox/testsuite/echo/echo-does-not-print-newline
new file mode 100644 (file)
index 0000000..2ed03ca
--- /dev/null
@@ -0,0 +1 @@
+test `busybox echo -n word | wc -c` -eq 4
diff --git a/busybox/testsuite/echo/echo-prints-argument b/busybox/testsuite/echo/echo-prints-argument
new file mode 100644 (file)
index 0000000..479dac8
--- /dev/null
@@ -0,0 +1 @@
+test xfubar = x`busybox echo fubar`
diff --git a/busybox/testsuite/echo/echo-prints-arguments b/busybox/testsuite/echo/echo-prints-arguments
new file mode 100644 (file)
index 0000000..4e4e3b4
--- /dev/null
@@ -0,0 +1 @@
+test "`busybox echo foo bar`" = "foo bar"
diff --git a/busybox/testsuite/echo/echo-prints-newline b/busybox/testsuite/echo/echo-prints-newline
new file mode 100644 (file)
index 0000000..838671e
--- /dev/null
@@ -0,0 +1 @@
+test `busybox echo word | wc -c` -eq 5
diff --git a/busybox/testsuite/expr/expr-works b/busybox/testsuite/expr/expr-works
new file mode 100644 (file)
index 0000000..af49ac4
--- /dev/null
@@ -0,0 +1,59 @@
+# busybox expr
+busybox expr 1 \| 1
+busybox expr 1 \| 0
+busybox expr 0 \| 1
+busybox expr 1 \& 1
+busybox expr 0 \< 1
+busybox expr 1 \> 0
+busybox expr 0 \<= 1
+busybox expr 1 \<= 1
+busybox expr 1 \>= 0
+busybox expr 1 \>= 1
+busybox expr 1 + 2
+busybox expr 2 - 1
+busybox expr 2 \* 3
+busybox expr 12 / 2
+busybox expr 12 % 5
+
+
+set +e
+busybox expr 0 \| 0
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 1 \& 0
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 0 \& 1
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 0 \& 0
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 1 \< 0
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 0 \> 1
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 1 \<= 0
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
+busybox expr 0 \>= 1
+if [ $? != 1 ] ; then
+       exit 1;
+fi;
+
diff --git a/busybox/testsuite/false/false-is-silent b/busybox/testsuite/false/false-is-silent
new file mode 100644 (file)
index 0000000..8a9aa0c
--- /dev/null
@@ -0,0 +1 @@
+busybox false 2>&1 | cmp - /dev/null
diff --git a/busybox/testsuite/false/false-returns-failure b/busybox/testsuite/false/false-returns-failure
new file mode 100644 (file)
index 0000000..1a061f2
--- /dev/null
@@ -0,0 +1 @@
+! busybox false
diff --git a/busybox/testsuite/find/find-supports-minus-xdev b/busybox/testsuite/find/find-supports-minus-xdev
new file mode 100644 (file)
index 0000000..4c559a1
--- /dev/null
@@ -0,0 +1 @@
+busybox find . -xdev >/dev/null 2>&1
diff --git a/busybox/testsuite/grep/egrep-is-not-case-insensitive b/busybox/testsuite/grep/egrep-is-not-case-insensitive
new file mode 100644 (file)
index 0000000..8816073
--- /dev/null
@@ -0,0 +1,2 @@
+# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS
+test x`echo foo | busybox egrep FOO` = x
diff --git a/busybox/testsuite/grep/egrep-supports-extended-regexps b/busybox/testsuite/grep/egrep-supports-extended-regexps
new file mode 100644 (file)
index 0000000..6ef8b91
--- /dev/null
@@ -0,0 +1,2 @@
+# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS
+echo foo | busybox egrep fo+
diff --git a/busybox/testsuite/grep/grep-handles-binary-files b/busybox/testsuite/grep/grep-handles-binary-files
new file mode 100644 (file)
index 0000000..edb2042
--- /dev/null
@@ -0,0 +1 @@
+echo -e '\0foo' | busybox grep foo
diff --git a/busybox/testsuite/grep/grep-handles-multiple-regexps b/busybox/testsuite/grep/grep-handles-multiple-regexps
new file mode 100644 (file)
index 0000000..5c1b8de
--- /dev/null
@@ -0,0 +1 @@
+echo foo | busybox grep -e foo -e bar
diff --git a/busybox/testsuite/grep/grep-is-also-egrep b/busybox/testsuite/grep/grep-is-also-egrep
new file mode 100644 (file)
index 0000000..2e6977c
--- /dev/null
@@ -0,0 +1,2 @@
+# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS
+echo foo | busybox egrep foo
diff --git a/busybox/testsuite/grep/grep-matches-NUL b/busybox/testsuite/grep/grep-matches-NUL
new file mode 100644 (file)
index 0000000..082bd87
--- /dev/null
@@ -0,0 +1,8 @@
+set +e
+echo -e '\0' | busybox grep .
+if [ $? != 0 ] ; then
+       exit 0;
+fi
+
+exit 1;
+
diff --git a/busybox/testsuite/gunzip/gunzip-reads-from-standard-input b/busybox/testsuite/gunzip/gunzip-reads-from-standard-input
new file mode 100644 (file)
index 0000000..7c498c0
--- /dev/null
@@ -0,0 +1,2 @@
+echo foo | gzip | busybox gunzip > output
+echo foo | cmp - output
diff --git a/busybox/testsuite/gzip/gzip-accepts-multiple-files b/busybox/testsuite/gzip/gzip-accepts-multiple-files
new file mode 100644 (file)
index 0000000..8f0d9c8
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo bar
+busybox gzip foo bar
+test -f foo.gz -a -f bar.gz
diff --git a/busybox/testsuite/gzip/gzip-accepts-single-minus b/busybox/testsuite/gzip/gzip-accepts-single-minus
new file mode 100644 (file)
index 0000000..8b51fdf
--- /dev/null
@@ -0,0 +1 @@
+echo foo | busybox gzip - >/dev/null
diff --git a/busybox/testsuite/gzip/gzip-removes-original-file b/busybox/testsuite/gzip/gzip-removes-original-file
new file mode 100644 (file)
index 0000000..b9cb995
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+busybox gzip foo
+test ! -f foo
diff --git a/busybox/testsuite/head/head-n-works b/busybox/testsuite/head/head-n-works
new file mode 100644 (file)
index 0000000..db43255
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+head -n 2 "$d/README" > logfile.gnu
+busybox head -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/head/head-works b/busybox/testsuite/head/head-works
new file mode 100644 (file)
index 0000000..56ad3e3
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+head "$d/README" > logfile.gnu
+busybox head "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/hostid/hostid-works b/busybox/testsuite/hostid/hostid-works
new file mode 100644 (file)
index 0000000..e85698e
--- /dev/null
@@ -0,0 +1,2 @@
+test x$(hostid) = x$(busybox hostid)
+
diff --git a/busybox/testsuite/hostname/hostname-d-works b/busybox/testsuite/hostname/hostname-d-works
new file mode 100644 (file)
index 0000000..a9aeb92
--- /dev/null
@@ -0,0 +1,2 @@
+test x$(hostname -d) = x$(busybox hostname -d)
+
diff --git a/busybox/testsuite/hostname/hostname-i-works b/busybox/testsuite/hostname/hostname-i-works
new file mode 100644 (file)
index 0000000..68a3e67
--- /dev/null
@@ -0,0 +1,2 @@
+test x$(hostname -i) = x$(busybox hostname -i)
+
diff --git a/busybox/testsuite/hostname/hostname-s-works b/busybox/testsuite/hostname/hostname-s-works
new file mode 100644 (file)
index 0000000..172b944
--- /dev/null
@@ -0,0 +1 @@
+test x$(hostname -s) = x$(busybox hostname -s)
diff --git a/busybox/testsuite/hostname/hostname-works b/busybox/testsuite/hostname/hostname-works
new file mode 100644 (file)
index 0000000..f51a406
--- /dev/null
@@ -0,0 +1 @@
+test x$(hostname) = x$(busybox hostname)
diff --git a/busybox/testsuite/id/id-g-works b/busybox/testsuite/id/id-g-works
new file mode 100644 (file)
index 0000000..671fc53
--- /dev/null
@@ -0,0 +1 @@
+test x$(id -g) = x$(busybox id -g)
diff --git a/busybox/testsuite/id/id-u-works b/busybox/testsuite/id/id-u-works
new file mode 100644 (file)
index 0000000..2358cb0
--- /dev/null
@@ -0,0 +1 @@
+test x$(id -u) = x$(busybox id -u)
diff --git a/busybox/testsuite/id/id-un-works b/busybox/testsuite/id/id-un-works
new file mode 100644 (file)
index 0000000..db390e7
--- /dev/null
@@ -0,0 +1 @@
+test x$(id -un) = x$(busybox id -un)
diff --git a/busybox/testsuite/id/id-ur-works b/busybox/testsuite/id/id-ur-works
new file mode 100644 (file)
index 0000000..6b0fcb0
--- /dev/null
@@ -0,0 +1 @@
+test x$(id -ur) = x$(busybox id -ur)
diff --git a/busybox/testsuite/ln/ln-creates-hard-links b/busybox/testsuite/ln/ln-creates-hard-links
new file mode 100644 (file)
index 0000000..2f6e23f
--- /dev/null
@@ -0,0 +1,4 @@
+echo file number one > file1
+busybox ln file1 link1
+test -f file1
+test -f link1
diff --git a/busybox/testsuite/ln/ln-creates-soft-links b/busybox/testsuite/ln/ln-creates-soft-links
new file mode 100644 (file)
index 0000000..e875e4c
--- /dev/null
@@ -0,0 +1,4 @@
+echo file number one > file1
+busybox ln -s file1 link1
+test -L link1
+test xfile1 = x`readlink link1`
diff --git a/busybox/testsuite/ln/ln-force-creates-hard-links b/busybox/testsuite/ln/ln-force-creates-hard-links
new file mode 100644 (file)
index 0000000..c96b7d6
--- /dev/null
@@ -0,0 +1,5 @@
+echo file number one > file1
+echo file number two > link1
+busybox ln -f file1 link1
+test -f file1
+test -f link1
diff --git a/busybox/testsuite/ln/ln-force-creates-soft-links b/busybox/testsuite/ln/ln-force-creates-soft-links
new file mode 100644 (file)
index 0000000..cab8d1d
--- /dev/null
@@ -0,0 +1,5 @@
+echo file number one > file1
+echo file number two > link1
+busybox ln -f -s file1 link1
+test -L link1
+test xfile1 = x`readlink link1`
diff --git a/busybox/testsuite/ln/ln-preserves-hard-links b/busybox/testsuite/ln/ln-preserves-hard-links
new file mode 100644 (file)
index 0000000..47fb989
--- /dev/null
@@ -0,0 +1,8 @@
+echo file number one > file1
+echo file number two > link1
+set +e
+busybox ln file1 link1
+if [ $? != 0 ] ; then
+       exit 0;
+fi
+exit 1;
diff --git a/busybox/testsuite/ln/ln-preserves-soft-links b/busybox/testsuite/ln/ln-preserves-soft-links
new file mode 100644 (file)
index 0000000..a8123ec
--- /dev/null
@@ -0,0 +1,9 @@
+echo file number one > file1
+echo file number two > link1
+set +e
+busybox ln -s file1 link1
+if [ $? != 0 ] ; then
+       exit 0;
+fi
+exit 1;
+
diff --git a/busybox/testsuite/ls/ls-1-works b/busybox/testsuite/ls/ls-1-works
new file mode 100644 (file)
index 0000000..8ad484f
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+ls -1 "$d" > logfile.gnu
+busybox ls -1 "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/ls/ls-h-works b/busybox/testsuite/ls/ls-h-works
new file mode 100644 (file)
index 0000000..7331262
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+ls -h "$d" > logfile.gnu
+busybox ls -h "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/ls/ls-l-works b/busybox/testsuite/ls/ls-l-works
new file mode 100644 (file)
index 0000000..ae5141d
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+ls -l "$d" > logfile.gnu
+busybox ls -l "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/ls/ls-s-works b/busybox/testsuite/ls/ls-s-works
new file mode 100644 (file)
index 0000000..d82f328
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+ls -1s "$d" > logfile.gnu
+busybox ls -1s "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file b/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file
new file mode 100644 (file)
index 0000000..8566a23
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+md5sum foo > bar
+busybox md5sum -c bar
diff --git a/busybox/testsuite/mkdir/mkdir-makes-a-directory b/busybox/testsuite/mkdir/mkdir-makes-a-directory
new file mode 100644 (file)
index 0000000..6ca5c4d
--- /dev/null
@@ -0,0 +1,2 @@
+busybox mkdir foo
+test -d foo
diff --git a/busybox/testsuite/mkdir/mkdir-makes-parent-directories b/busybox/testsuite/mkdir/mkdir-makes-parent-directories
new file mode 100644 (file)
index 0000000..992facb
--- /dev/null
@@ -0,0 +1,2 @@
+busybox mkdir -p foo/bar
+test -d foo -a -d foo/bar
diff --git a/busybox/testsuite/msh/msh-supports-underscores-in-variable-names b/busybox/testsuite/msh/msh-supports-underscores-in-variable-names
new file mode 100644 (file)
index 0000000..9c7834b
--- /dev/null
@@ -0,0 +1 @@
+test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo
diff --git a/busybox/testsuite/mv/mv-files-to-dir b/busybox/testsuite/mv/mv-files-to-dir
new file mode 100644 (file)
index 0000000..c8eaba8
--- /dev/null
@@ -0,0 +1,16 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+mkdir there
+busybox mv file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test -f there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! -e file1
+test ! -e file2
+test ! -e link1
+test ! -e dir1/file3
diff --git a/busybox/testsuite/mv/mv-follows-links b/busybox/testsuite/mv/mv-follows-links
new file mode 100644 (file)
index 0000000..1fb355b
--- /dev/null
@@ -0,0 +1,4 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -f baz
diff --git a/busybox/testsuite/mv/mv-moves-empty-file b/busybox/testsuite/mv/mv-moves-empty-file
new file mode 100644 (file)
index 0000000..48afca4
--- /dev/null
@@ -0,0 +1,4 @@
+touch foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox/testsuite/mv/mv-moves-file b/busybox/testsuite/mv/mv-moves-file
new file mode 100644 (file)
index 0000000..edb4c37
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+busybox mv foo bar
+test ! -f foo -a -f bar
diff --git a/busybox/testsuite/mv/mv-moves-hardlinks b/busybox/testsuite/mv/mv-moves-hardlinks
new file mode 100644 (file)
index 0000000..eaa8215
--- /dev/null
@@ -0,0 +1,4 @@
+touch foo
+ln foo bar
+busybox mv bar baz
+test ! -f bar -a -f baz
diff --git a/busybox/testsuite/mv/mv-moves-large-file b/busybox/testsuite/mv/mv-moves-large-file
new file mode 100644 (file)
index 0000000..77d088f
--- /dev/null
@@ -0,0 +1,4 @@
+dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox/testsuite/mv/mv-moves-small-file b/busybox/testsuite/mv/mv-moves-small-file
new file mode 100644 (file)
index 0000000..065c7f1
--- /dev/null
@@ -0,0 +1,4 @@
+echo I WANT > foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox/testsuite/mv/mv-moves-symlinks b/busybox/testsuite/mv/mv-moves-symlinks
new file mode 100644 (file)
index 0000000..c413af0
--- /dev/null
@@ -0,0 +1,6 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -f foo
+test ! -e bar
+test -L baz
diff --git a/busybox/testsuite/mv/mv-moves-unreadable-files b/busybox/testsuite/mv/mv-moves-unreadable-files
new file mode 100644 (file)
index 0000000..bc9c313
--- /dev/null
@@ -0,0 +1,5 @@
+touch foo
+chmod a-r foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox/testsuite/mv/mv-preserves-hard-links b/busybox/testsuite/mv/mv-preserves-hard-links
new file mode 100644 (file)
index 0000000..b3ba3aa
--- /dev/null
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
+touch foo
+ln foo bar
+mkdir baz
+busybox mv foo bar baz
+test baz/foo -ef baz/bar
diff --git a/busybox/testsuite/mv/mv-preserves-links b/busybox/testsuite/mv/mv-preserves-links
new file mode 100644 (file)
index 0000000..ea565d2
--- /dev/null
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir b/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir
new file mode 100644 (file)
index 0000000..7c572c4
--- /dev/null
@@ -0,0 +1,23 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+mkdir there
+busybox mv file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test -f there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! -e file1
+test ! -e file2
+test ! -e link1
+test ! -e dir1/file3
+set +e
+busybox mv there there/dir1
+if [ $? != 0 ] ; then
+       exit 0;
+fi
+
+exit 1;
diff --git a/busybox/testsuite/mv/mv-removes-source-file b/busybox/testsuite/mv/mv-removes-source-file
new file mode 100644 (file)
index 0000000..48afca4
--- /dev/null
@@ -0,0 +1,4 @@
+touch foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox/testsuite/pwd/pwd-prints-working-directory b/busybox/testsuite/pwd/pwd-prints-working-directory
new file mode 100644 (file)
index 0000000..8575347
--- /dev/null
@@ -0,0 +1 @@
+test $(pwd) = $(busybox pwd)
diff --git a/busybox/testsuite/rm/rm-removes-file b/busybox/testsuite/rm/rm-removes-file
new file mode 100644 (file)
index 0000000..46571a9
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+busybox rm foo
+test ! -f foo
diff --git a/busybox/testsuite/rmdir/rmdir-removes-parent-directories b/busybox/testsuite/rmdir/rmdir-removes-parent-directories
new file mode 100644 (file)
index 0000000..222f5de
--- /dev/null
@@ -0,0 +1,3 @@
+mkdir -p foo/bar
+busybox rmdir -p foo/bar
+test ! -d foo
diff --git a/busybox/testsuite/runtest b/busybox/testsuite/runtest
new file mode 100755 (executable)
index 0000000..6ba334b
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+[ -n "$srcdir" ] || srcdir=$(pwd)
+[ -n "$bindir" ] || bindir=$(dirname $(pwd))
+PATH=$bindir:$PATH
+
+run_applet_testcase ()
+{
+       local applet=$1
+       local testcase=$2
+
+       local status=0
+       local RES=
+
+       local uc_applet=$(echo $applet | tr a-z A-Z)
+       local testname=$(basename $testcase)
+
+       if grep -q "^# CONFIG_${uc_applet} is not set$" $bindir/.config; then
+               echo UNTESTED: $testname
+               return 0
+       fi
+
+       if grep -q "^# FEATURE: " $testcase; then
+               local feature=`sed -ne 's/^# FEATURE: //p' $testcase`
+
+               if grep -q "^# ${feature} is not set$" $bindir/.config; then
+                       echo UNTESTED: $testname
+                       return 0
+               fi
+       fi
+
+       rm -rf tmp
+       mkdir -p tmp
+       pushd tmp >/dev/null
+
+       d=$srcdir sh -x -e $testcase >.logfile.txt 2>&1
+
+       if [ $? != 0 ] ; then
+               echo FAIL: $testname
+               if [ "$verbose" = 1 ]; then
+                       cat .logfile.txt
+                       #exit 1;
+               fi;
+               status=$?
+       else
+               echo PASS: $testname
+               rm -f .logfile.txt
+               status=$?
+       fi
+
+       popd >/dev/null
+       rm -rf tmp
+
+       return $status
+}
+
+run_applet_tests ()
+{
+       local applet=$1
+
+       local status=0
+
+       for testcase in $srcdir/$applet/*; do
+               if [ "$testcase" = "$srcdir/$applet/CVS" ]; then
+                       continue
+               fi
+
+               if run_applet_testcase $applet $testcase; then
+                       :
+               else
+                       status=1
+               fi
+       done
+
+       return $status
+}
+
+
+status=0
+
+if [ x"$1" = x"-v" ]; then
+       verbose=1
+       shift
+fi
+
+if [ $# -ne 0 ]; then
+       applets="$@"
+else
+       applets=$(ls $srcdir)
+fi
+
+for applet in $applets; do
+       if [ "$applet" != CVS -a -d "$srcdir/$applet" ]; then
+               if run_applet_tests $applet; then
+                       :
+               else
+                       status=1
+               fi
+       fi
+done
+
+exit $status
diff --git a/busybox/testsuite/sed/sed-accepts-blanks-before-command b/busybox/testsuite/sed/sed-accepts-blanks-before-command
new file mode 100644 (file)
index 0000000..9597c2f
--- /dev/null
@@ -0,0 +1 @@
+busybox sed -e '1 d' </dev/null
diff --git a/busybox/testsuite/sed/sed-aic-commands b/busybox/testsuite/sed/sed-aic-commands
new file mode 100644 (file)
index 0000000..b41c14a
--- /dev/null
@@ -0,0 +1,134 @@
+cat - >input <<EOF
+2i\\
+before 2
+5c\\
+Change 5
+10a\\
+After 10
+22i\\
+before 22\\
+Continued
+25c\\
+Change 25\\
+Continued
+20a\\
+After 20\\
+Continued
+         32i\\
+before 32\\
+Continued 1\\
+Continued 2\\
+Continued 3
+         35c\\
+Change 35\\
+Continued 1\\
+Continued 2\\
+Continued 3
+       30a\\
+After 30\\
+Continued 1\\
+Continued 2\\
+Continued 3
+EOF
+busybox sed -f input >output <<EOF
+     1 y
+     2 y
+     3 y
+     4 y
+     5 y
+     6 y
+     7 y
+     8 y
+     9 y
+    10 y
+    11 y
+    12 y
+    13 y
+    14 y
+    15 y
+    16 y
+    17 y
+    18 y
+    19 y
+    20 y
+    21 y
+    22 y
+    23 y
+    24 y
+    25 y
+    26 y
+    27 y
+    28 y
+    29 y
+    30 y
+    31 y
+    32 y
+    33 y
+    34 y
+    35 y
+    36 y
+    37 y
+    38 y
+    39 y
+    40 y
+EOF
+cmp -s output - <<EOF
+     1 y
+before 2
+     2 y
+     3 y
+     4 y
+Change 5
+     6 y
+     7 y
+     8 y
+     9 y
+    10 y
+After 10
+    11 y
+    12 y
+    13 y
+    14 y
+    15 y
+    16 y
+    17 y
+    18 y
+    19 y
+    20 y
+After 20
+Continued
+    21 y
+before 22
+Continued
+    22 y
+    23 y
+    24 y
+Change 25
+Continued
+    26 y
+    27 y
+    28 y
+    29 y
+    30 y
+After 30
+Continued 1
+Continued 2
+Continued 3
+    31 y
+before 32
+Continued 1
+Continued 2
+Continued 3
+    32 y
+    33 y
+    34 y
+Change 35
+Continued 1
+Continued 2
+Continued 3
+    36 y
+    37 y
+    38 y
+    39 y
+    40 y
+EOF
diff --git a/busybox/testsuite/sed/sed-append-hold-space-to-pattern-space b/busybox/testsuite/sed/sed-append-hold-space-to-pattern-space
new file mode 100644 (file)
index 0000000..6dda80f
--- /dev/null
@@ -0,0 +1,13 @@
+busybox sed 'G'>output <<EOF
+a
+b
+c
+EOF
+cmp -s output - <<EOF
+a
+
+b
+
+c
+
+EOF
diff --git a/busybox/testsuite/sed/sed-append-next-line b/busybox/testsuite/sed/sed-append-next-line
new file mode 100644 (file)
index 0000000..0621a31
--- /dev/null
@@ -0,0 +1,19 @@
+# This will fail if CONFIG_FEATURE_SED_GNU_COMPATABILITY is defined
+busybox sed 'N;p'>output <<EOF
+a
+b
+c
+EOF
+
+set +e
+cmp -s output - <<EOF
+a
+b
+a
+b
+c
+EOF
+if [ $? != 0 ] ; then
+       exit 0;
+fi
+exit 1;
diff --git a/busybox/testsuite/sed/sed-branch b/busybox/testsuite/sed/sed-branch
new file mode 100644 (file)
index 0000000..4167569
--- /dev/null
@@ -0,0 +1 @@
+test "$(echo foo | busybox sed 'b one;p;: one')" = foo
diff --git a/busybox/testsuite/sed/sed-branch-conditional b/busybox/testsuite/sed/sed-branch-conditional
new file mode 100644 (file)
index 0000000..47d0a5f
--- /dev/null
@@ -0,0 +1,15 @@
+busybox sed 's/a/1/;t one;p;: one;p'>output <<EOF
+a
+b
+c
+EOF
+cmp -s output - <<EOF
+1
+1
+b
+b
+b
+c
+c
+c
+EOF
diff --git a/busybox/testsuite/sed/sed-branch-conditional2 b/busybox/testsuite/sed/sed-branch-conditional2
new file mode 100644 (file)
index 0000000..f4b11f0
--- /dev/null
@@ -0,0 +1,11 @@
+#XFAIL
+busybox sed 's/a/b/;:loop;t loop'>output <<EOF
+a
+b
+c
+EOF
+cmp -s output - <<EOF
+b
+b
+c
+EOF
diff --git a/busybox/testsuite/sed/sed-branch-no-label b/busybox/testsuite/sed/sed-branch-no-label
new file mode 100644 (file)
index 0000000..446c1bc
--- /dev/null
@@ -0,0 +1 @@
+test "$(echo foo | busybox sed 'b;p')" = foo
diff --git a/busybox/testsuite/sed/sed-chains-substs b/busybox/testsuite/sed/sed-chains-substs
new file mode 100644 (file)
index 0000000..266936a
--- /dev/null
@@ -0,0 +1 @@
+test "$(echo foo | busybox sed -e s/foo/bar/ -e s/bar/baz/)" = baz
diff --git a/busybox/testsuite/sed/sed-chains-substs2 b/busybox/testsuite/sed/sed-chains-substs2
new file mode 100644 (file)
index 0000000..90568f6
--- /dev/null
@@ -0,0 +1 @@
+test x"$(echo foo | busybox -n sed -e s/foo/bar/ -e s/foo/baz/)" = x
diff --git a/busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line b/busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line
new file mode 100644 (file)
index 0000000..6f106e1
--- /dev/null
@@ -0,0 +1,2 @@
+echo foo | busybox sed -e /foo/d -e s/foo/bar/ >foo
+cmp foo /dev/null
diff --git a/busybox/testsuite/sed/sed-handles-embedded-slashes b/busybox/testsuite/sed/sed-handles-embedded-slashes
new file mode 100644 (file)
index 0000000..cc28761
--- /dev/null
@@ -0,0 +1 @@
+test "$(echo fu/bar | busybox sed -e 's/[/]//')" = fubar
diff --git a/busybox/testsuite/sed/sed-handles-empty-lines b/busybox/testsuite/sed/sed-handles-empty-lines
new file mode 100644 (file)
index 0000000..2bb8f04
--- /dev/null
@@ -0,0 +1 @@
+test `echo | busybox sed -e 's/$/@/'` = @
diff --git a/busybox/testsuite/sed/sed-handles-unsatisfied-backrefs b/busybox/testsuite/sed/sed-handles-unsatisfied-backrefs
new file mode 100644 (file)
index 0000000..61bff88
--- /dev/null
@@ -0,0 +1,6 @@
+busybox sed -e 's/.*root=/\1/' >output <<EOF
+BOOT_IMAGE=vmlinuz root=/dev/hda5 initrd=init1
+EOF
+cmp -s output - <<EOF
+/dev/hda5 initrd=init1
+EOF
diff --git a/busybox/testsuite/sed/sed-next-line b/busybox/testsuite/sed/sed-next-line
new file mode 100644 (file)
index 0000000..38fe20c
--- /dev/null
@@ -0,0 +1,12 @@
+busybox sed 'n;p'>output <<EOF
+a
+b
+c
+EOF
+cmp -s output - <<EOF
+a
+b
+b
+c
+c
+EOF
diff --git a/busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs b/busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs
new file mode 100644 (file)
index 0000000..ba8955d
--- /dev/null
@@ -0,0 +1,4 @@
+busybox sed -e s/1/2/g -e s/3/4/g >output <<EOF
+1
+EOF
+echo 2 | cmp -s output -
diff --git a/busybox/testsuite/sed/sed-recurses-properly b/busybox/testsuite/sed/sed-recurses-properly
new file mode 100644 (file)
index 0000000..a02667b
--- /dev/null
@@ -0,0 +1 @@
+test "`echo '12345' | busybox sed -e 's/[[:space:]]*/,/g')` = ',1,2,3,4,5,'"
diff --git a/busybox/testsuite/sed/sed-regex-match-newline b/busybox/testsuite/sed/sed-regex-match-newline
new file mode 100644 (file)
index 0000000..1057e17
--- /dev/null
@@ -0,0 +1,10 @@
+# FEATURE: CONFIG_FEATURE_SED_EMBEDED_NEWLINE
+busybox sed -n 'N;/a\nb/p'>output <<EOF
+a
+b
+c
+EOF
+cmp -s output - <<EOF
+a
+b
+EOF
diff --git a/busybox/testsuite/sed/sed-splits-edit-commands-on-command-line b/busybox/testsuite/sed/sed-splits-edit-commands-on-command-line
new file mode 100644 (file)
index 0000000..6421fa5
--- /dev/null
@@ -0,0 +1,9 @@
+echo 2 | busybox sed -e 'i\
+1
+a\
+3' > output
+cmp output - <<EOF
+1
+2
+3
+EOF
diff --git a/busybox/testsuite/sed/sed-subst-subprint b/busybox/testsuite/sed/sed-subst-subprint
new file mode 100644 (file)
index 0000000..24f8bad
--- /dev/null
@@ -0,0 +1,9 @@
+busybox sed 's/foo/bar/p'>output <<EOF
+foo
+bar
+EOF
+cmp -s output - <<EOF
+bar
+bar
+bar
+EOF
diff --git a/busybox/testsuite/sed/sed-write-to-stdout b/busybox/testsuite/sed/sed-write-to-stdout
new file mode 100644 (file)
index 0000000..95b4d72
--- /dev/null
@@ -0,0 +1,10 @@
+busybox sed -n 'N;P;p'>output <<EOF
+a
+b
+c
+EOF
+cmp -s output - <<EOF
+a
+a
+b
+EOF
diff --git a/busybox/testsuite/sort/sort-n-works b/busybox/testsuite/sort/sort-n-works
new file mode 100644 (file)
index 0000000..878108d
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+sort -n "$d/README" > logfile.gnu
+busybox sort -n "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/sort/sort-r-works b/busybox/testsuite/sort/sort-r-works
new file mode 100644 (file)
index 0000000..6ee0ceb
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+sort -r "$d/README" > logfile.gnu
+busybox sort -r "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/sort/sort-works b/busybox/testsuite/sort/sort-works
new file mode 100644 (file)
index 0000000..14a115a
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+sort "$d/README" > logfile.gnu
+busybox sort "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/strings/strings-works-like-GNU b/busybox/testsuite/strings/strings-works-like-GNU
new file mode 100644 (file)
index 0000000..2d64710
--- /dev/null
@@ -0,0 +1,9 @@
+rm -f foo bar
+strings -af ../../busybox > foo
+busybox strings -af ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+       set -e
+       diff -q foo bar
+fi
diff --git a/busybox/testsuite/tail/tail-n-works b/busybox/testsuite/tail/tail-n-works
new file mode 100644 (file)
index 0000000..27a905f
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+tail -n 2 "$d/README" > logfile.gnu
+busybox tail -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/tail/tail-works b/busybox/testsuite/tail/tail-works
new file mode 100644 (file)
index 0000000..27a905f
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+tail -n 2 "$d/README" > logfile.gnu
+busybox tail -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/tar/tar-archives-multiple-files b/busybox/testsuite/tar/tar-archives-multiple-files
new file mode 100644 (file)
index 0000000..245d9e9
--- /dev/null
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo bar
+busybox tar cf foo.tar foo bar
+rm foo bar
+tar xf foo.tar
+test -f foo -a -f bar
diff --git a/busybox/testsuite/tar/tar-complains-about-missing-file b/busybox/testsuite/tar/tar-complains-about-missing-file
new file mode 100644 (file)
index 0000000..26e8cbb
--- /dev/null
@@ -0,0 +1,3 @@
+touch foo
+tar cf foo.tar foo
+! busybox tar xf foo.tar bar
diff --git a/busybox/testsuite/tar/tar-demands-at-least-one-ctx b/busybox/testsuite/tar/tar-demands-at-least-one-ctx
new file mode 100644 (file)
index 0000000..85e7f60
--- /dev/null
@@ -0,0 +1 @@
+! busybox tar v
diff --git a/busybox/testsuite/tar/tar-demands-at-most-one-ctx b/busybox/testsuite/tar/tar-demands-at-most-one-ctx
new file mode 100644 (file)
index 0000000..130d0e7
--- /dev/null
@@ -0,0 +1 @@
+! busybox tar tx
diff --git a/busybox/testsuite/tar/tar-extracts-file b/busybox/testsuite/tar/tar-extracts-file
new file mode 100644 (file)
index 0000000..ca72f24
--- /dev/null
@@ -0,0 +1,5 @@
+touch foo
+tar cf foo.tar foo
+rm foo
+busybox tar xf foo.tar
+test -f foo
diff --git a/busybox/testsuite/tar/tar-extracts-from-standard-input b/busybox/testsuite/tar/tar-extracts-from-standard-input
new file mode 100644 (file)
index 0000000..a30e9f0
--- /dev/null
@@ -0,0 +1,5 @@
+touch foo
+tar cf foo.tar foo
+rm foo
+cat foo.tar | busybox tar x
+test -f foo
diff --git a/busybox/testsuite/tar/tar-extracts-multiple-files b/busybox/testsuite/tar/tar-extracts-multiple-files
new file mode 100644 (file)
index 0000000..8ae8cdd
--- /dev/null
@@ -0,0 +1,6 @@
+touch foo bar
+busybox tar cf foo.tar foo bar
+rm foo bar
+busybox tar -xf foo.tar
+test -f foo
+test -f bar
diff --git a/busybox/testsuite/tar/tar-extracts-to-standard-output b/busybox/testsuite/tar/tar-extracts-to-standard-output
new file mode 100644 (file)
index 0000000..ca48e36
--- /dev/null
@@ -0,0 +1,3 @@
+echo foo > foo
+tar cf foo.tar foo
+cat foo.tar | busybox tar Ox | cmp foo -
diff --git a/busybox/testsuite/tar/tar-handles-cz-options b/busybox/testsuite/tar/tar-handles-cz-options
new file mode 100644 (file)
index 0000000..5b55e46
--- /dev/null
@@ -0,0 +1,5 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+# FEATURE: CONFIG_FEATURE_TAR_GZIP
+touch foo
+busybox tar czf foo.tar.gz foo
+gzip -d foo.tar.gz
diff --git a/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list b/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list
new file mode 100644 (file)
index 0000000..5033642
--- /dev/null
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo
+tar cf foo.tar foo
+echo foo >foo.exclude
+busybox tar xf foo.tar -X foo.exclude
diff --git a/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists b/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists
new file mode 100644 (file)
index 0000000..2de0f0e
--- /dev/null
@@ -0,0 +1,8 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo bar baz
+tar cf foo.tar foo bar baz
+echo foo >foo.exclude
+rm foo bar baz
+busybox tar xf foo.tar foo bar -X foo.exclude
+test ! -f foo -a -f bar -a ! -f baz
diff --git a/busybox/testsuite/tar/tar-handles-multiple-X-options b/busybox/testsuite/tar/tar-handles-multiple-X-options
new file mode 100644 (file)
index 0000000..155b27e
--- /dev/null
@@ -0,0 +1,10 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo
+touch bar
+tar cf foo.tar foo bar
+echo foo > foo.exclude
+echo bar > bar.exclude
+rm foo bar
+busybox tar xf foo.tar -X foo.exclude -X bar.exclude
+test ! -f foo -a ! -f bar
diff --git a/busybox/testsuite/tar/tar-handles-nested-exclude b/busybox/testsuite/tar/tar-handles-nested-exclude
new file mode 100644 (file)
index 0000000..39013a1
--- /dev/null
@@ -0,0 +1,9 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+mkdir foo
+touch foo/bar
+tar cf foo.tar foo
+rm -rf foo
+echo foo/bar >foobar.exclude
+busybox tar xf foo.tar foo -X foobar.exclude
+test -d foo -a ! -f foo/bar
diff --git a/busybox/testsuite/tee/tee-appends-input b/busybox/testsuite/tee/tee-appends-input
new file mode 100644 (file)
index 0000000..cff20bf
--- /dev/null
@@ -0,0 +1,5 @@
+echo i\'m a little teapot >foo
+cp foo bar
+echo i\'m a little teapot >>foo
+echo i\'m a little teapot | busybox tee -a bar >/dev/null
+cmp foo bar
diff --git a/busybox/testsuite/tee/tee-tees-input b/busybox/testsuite/tee/tee-tees-input
new file mode 100644 (file)
index 0000000..26e2173
--- /dev/null
@@ -0,0 +1,3 @@
+echo i\'m a little teapot >foo
+echo i\'m a little teapot | busybox tee bar >baz
+cmp foo bar && cmp foo baz
diff --git a/busybox/testsuite/touch/touch-creates-file b/busybox/testsuite/touch/touch-creates-file
new file mode 100644 (file)
index 0000000..4b49354
--- /dev/null
@@ -0,0 +1,2 @@
+busybox touch foo
+test -f foo
diff --git a/busybox/testsuite/touch/touch-does-not-create-file b/busybox/testsuite/touch/touch-does-not-create-file
new file mode 100644 (file)
index 0000000..8852592
--- /dev/null
@@ -0,0 +1,2 @@
+busybox touch -c foo
+test ! -f foo
diff --git a/busybox/testsuite/touch/touch-touches-files-after-non-existent-file b/busybox/testsuite/touch/touch-touches-files-after-non-existent-file
new file mode 100644 (file)
index 0000000..a869ec2
--- /dev/null
@@ -0,0 +1,3 @@
+touch -t 198001010000 bar
+busybox touch -c foo bar
+test x"`find bar -mtime -1`" = xbar
diff --git a/busybox/testsuite/tr/tr-d-works b/busybox/testsuite/tr/tr-d-works
new file mode 100644 (file)
index 0000000..d939e8b
--- /dev/null
@@ -0,0 +1,4 @@
+echo testing | tr -d aeiou > logfile.gnu
+echo testing | busybox tr -d aeiou > logfile.bb
+
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/tr/tr-non-gnu b/busybox/testsuite/tr/tr-non-gnu
new file mode 100644 (file)
index 0000000..ffa6951
--- /dev/null
@@ -0,0 +1 @@
+echo fdhrnzvfu bffvsentr | busybox tr '[a-z]' '[n-z][a-m]'
diff --git a/busybox/testsuite/tr/tr-works b/busybox/testsuite/tr/tr-works
new file mode 100644 (file)
index 0000000..8753a3f
--- /dev/null
@@ -0,0 +1,9 @@
+echo "cbaab" | tr abc zyx > logfile.gnu
+echo "TESTING A B C" | tr [A-Z] [a-z] >> logfile.gnu
+echo abc[] | tr a[b AXB >> logfile.gnu
+
+echo "cbaab" | busybox tr abc zyx > logfile.bb
+echo "TESTING A B C" | busybox tr [A-Z] [a-z] >> logfile.bb
+echo abc[] | busybox tr a[b AXB >> logfile.bb
+
+cmp logfile.gnu logfile.bb
diff --git a/busybox/testsuite/true/true-is-silent b/busybox/testsuite/true/true-is-silent
new file mode 100644 (file)
index 0000000..1d1bdb2
--- /dev/null
@@ -0,0 +1 @@
+busybox true 2>&1 | cmp - /dev/null
diff --git a/busybox/testsuite/true/true-returns-success b/busybox/testsuite/true/true-returns-success
new file mode 100644 (file)
index 0000000..cdf2d55
--- /dev/null
@@ -0,0 +1 @@
+busybox true
diff --git a/busybox/testsuite/uptime/uptime-works b/busybox/testsuite/uptime/uptime-works
new file mode 100644 (file)
index 0000000..80e5787
--- /dev/null
@@ -0,0 +1,2 @@
+busybox uptime
+
diff --git a/busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly b/busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly
new file mode 100644 (file)
index 0000000..1a48a66
--- /dev/null
@@ -0,0 +1,4 @@
+saved_umask=$(umask)
+umask 0
+busybox uuencode foo </dev/null | head -n 1 | grep -q 666
+umask $saved_umask
diff --git a/busybox/testsuite/wc/wc-counts-all b/busybox/testsuite/wc/wc-counts-all
new file mode 100644 (file)
index 0000000..5e2cb6e
--- /dev/null
@@ -0,0 +1 @@
+test "`echo i\'m a little teapot | busybox wc`" = '      1       4      20'
diff --git a/busybox/testsuite/wc/wc-counts-characters b/busybox/testsuite/wc/wc-counts-characters
new file mode 100644 (file)
index 0000000..7558646
--- /dev/null
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -c` -eq 20
diff --git a/busybox/testsuite/wc/wc-counts-lines b/busybox/testsuite/wc/wc-counts-lines
new file mode 100644 (file)
index 0000000..5be6ed0
--- /dev/null
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -l` -eq 1
diff --git a/busybox/testsuite/wc/wc-counts-words b/busybox/testsuite/wc/wc-counts-words
new file mode 100644 (file)
index 0000000..331650e
--- /dev/null
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -w` -eq 4
diff --git a/busybox/testsuite/wc/wc-prints-longest-line-length b/busybox/testsuite/wc/wc-prints-longest-line-length
new file mode 100644 (file)
index 0000000..78831fc
--- /dev/null
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -L` -eq 19
diff --git a/busybox/testsuite/wget/wget--O-overrides--P b/busybox/testsuite/wget/wget--O-overrides--P
new file mode 100644 (file)
index 0000000..fdb5d47
--- /dev/null
@@ -0,0 +1,3 @@
+mkdir foo
+busybox wget -q -O index.html -P foo http://www.google.com/
+test -s index.html
diff --git a/busybox/testsuite/wget/wget-handles-empty-path b/busybox/testsuite/wget/wget-handles-empty-path
new file mode 100644 (file)
index 0000000..5b59183
--- /dev/null
@@ -0,0 +1 @@
+busybox wget http://www.google.com
diff --git a/busybox/testsuite/wget/wget-retrieves-google-index b/busybox/testsuite/wget/wget-retrieves-google-index
new file mode 100644 (file)
index 0000000..7be9a80
--- /dev/null
@@ -0,0 +1,2 @@
+busybox wget -q -O foo http://www.google.com/
+test -s foo
diff --git a/busybox/testsuite/wget/wget-supports--P b/busybox/testsuite/wget/wget-supports--P
new file mode 100644 (file)
index 0000000..9b4d095
--- /dev/null
@@ -0,0 +1,3 @@
+mkdir foo
+busybox wget -q -P foo http://www.google.com/
+test -s foo/index.html
diff --git a/busybox/testsuite/which/which-uses-default-path b/busybox/testsuite/which/which-uses-default-path
new file mode 100644 (file)
index 0000000..63ceb9f
--- /dev/null
@@ -0,0 +1,4 @@
+BUSYBOX=$(type -p busybox)
+SAVED_PATH=$PATH
+unset PATH
+$BUSYBOX which ls
diff --git a/busybox/testsuite/xargs/xargs-works b/busybox/testsuite/xargs/xargs-works
new file mode 100644 (file)
index 0000000..c95869e
--- /dev/null
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+find "$d" -name \*works -type f | xargs md5sum > logfile.gnu
+find "$d" -name \*works -type f | busybox xargs md5sum > logfile.bb
+diff -u logfile.gnu logfile.bb
diff --git a/busybox/util-linux/Config.in b/busybox/util-linux/Config.in
new file mode 100644 (file)
index 0000000..24d5487
--- /dev/null
@@ -0,0 +1,357 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux System Utilities"
+
+
+config CONFIG_DMESG
+       bool "dmesg"
+       default n
+       help
+         dmesg is used to examine or control the kernel ring buffer.  When the
+         Linux kernel prints messages to the system log, they are stored in
+         the kernel ring buffer.  You can use dmesg to print the kernel's ring
+         buffer, clear the kernel ring buffer, change the size of the kernel
+         ring buffer, and change the priority level at which kernel messages
+         are also logged to the system console.  Enable this option if you
+         wish to enable the 'dmesg' utility.
+
+config CONFIG_FBSET
+       bool "fbset"
+       default n
+       help
+         fbset is used to show or change the settings of a Linux frame buffer
+         device.  The frame buffer device provides a simple and unique
+         interface to access a graphics display.  Enable this option
+         if you wish to enable the 'fbset' utility.
+
+
+config CONFIG_FEATURE_FBSET_FANCY
+       bool "  Turn on extra fbset options"
+       default n
+       depends on CONFIG_FBSET
+       help
+         This option enables extended fbset options, allowing one to set the
+         framebuffer size, color depth, etc.  interface to access a graphics
+         display.  Enable this option if you wish to enable extended fbset
+         options.
+
+config CONFIG_FEATURE_FBSET_READMODE
+       bool "  Turn on fbset readmode support"
+       default n
+       depends on CONFIG_FBSET
+       help
+         This option allows fbset to read the video mode database stored by
+         default as /etc/fb.modes, which can be used to set frame buffer
+         device to pre-defined video modes.
+
+config CONFIG_FDFLUSH
+       bool "fdflush"
+       default n
+       help
+         fdflush is only needed when changing media on slightly-broken
+         removable media drives.  It is used to make Linux believe that a
+         hardware disk-change switch has been actuated, which causes Linux to
+         forget anything it has cached from the previous media.  If you have
+         such a slightly-broken drive, you will need to run fdflush every time
+         you change a disk.  Most people have working hardware and can safely
+         leave this disabled.
+
+config CONFIG_FDFORMAT
+       bool "fdformat"
+       default n
+       help
+         fdformat is used to low-level format a floppy disk.
+
+config CONFIG_FDISK
+       bool "fdisk"
+       default n
+       help
+         The fdisk utility is used to divide hard disks into one or more
+         logical disks, which are generally called partitions.  This utility
+         can be used to list and edit the set of partitions or BSD style
+         'disk slices' that are defined on a hard drive.
+
+config FDISK_SUPPORT_LARGE_DISKS
+       bool "  support over 4GB disks"
+       default y
+       depends on CONFIG_FDISK
+       help
+         Enable this option to support large disks > 4GB.
+
+config CONFIG_FEATURE_FDISK_WRITABLE
+       bool "  Write support"
+       default y
+       depends on CONFIG_FDISK
+       help
+         Enabling this option allows you to create or change a partition table
+         and write those changes out to disk.  If you leave this option
+         disabled, you will only be able to view the partition table.
+
+config CONFIG_FEATURE_AIX_LABEL
+       bool "  Support AIX disklabels"
+       default n
+       depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
+       help
+         Enabling this option allows you to create or change AIX disklabels.
+         Most people can safely leave this option disabled.
+
+config CONFIG_FEATURE_SGI_LABEL
+       bool "  Support SGI disklabels"
+       default n
+       depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
+       help
+         Enabling this option allows you to create or change SGI disklabels.
+         Most people can safely leave this option disabled.
+
+config CONFIG_FEATURE_SUN_LABEL
+       bool "  Support SUN disklabels"
+       default n
+       depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
+       help
+         Enabling this option allows you to create or change SUN disklabels.
+         Most people can safely leave this option disabled.
+
+config CONFIG_FEATURE_OSF_LABEL
+       bool "  Support BSD disklabels"
+       default n
+       depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
+       help
+         Enabling this option allows you to create or change BSD disklabels
+         and define and edit BSD disk slices.
+
+config CONFIG_FEATURE_FDISK_ADVANCED
+       bool "  Support expert mode"
+       default n
+       depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
+       help
+         Enabling this option allows you to do terribly unsafe things like
+         define arbitrary drive geometry, move the beginning of data in a
+         partition, and similarly evil things.  Unless you have a very good
+         reason you would be wise to leave this disabled.
+
+config CONFIG_FREERAMDISK
+       bool "freeramdisk"
+       default n
+       help
+         Linux allows you to create ramdisks.  This utility allows you to
+         delete them and completely free all memory that was used for the
+         ramdisk.  For example, if you boot Linux into a ramdisk and later
+         pivot_root, you may want to free the memory that is allocated to the
+         ramdisk.  If you have no use for freeing memory from a ramdisk, leave
+         this disabled.
+
+config CONFIG_FSCK_MINIX
+       bool "fsck_minix"
+       default n
+       help
+         The minix filesystem is a nice, small, compact, read-write filesystem
+         with little overhead.  It is not a journaling filesystem however and
+         can experience corruption if it is not properly unmounted or if the
+         power goes off in the middle of a write.  This utility allows you to
+         check for and attempt to repair any corruption that occurs to a minix
+         filesystem.
+
+config CONFIG_MKFS_MINIX
+       bool "mkfs_minix"
+       default n
+       help
+         The minix filesystem is a nice, small, compact, read-write filesystem
+         with little overhead.  If you wish to be able to create minix filesystems
+         this utility will do the job for you.
+
+comment "Minix filesystem support"
+    depends on CONFIG_FSCK_MINIX || CONFIG_MKFS_MINIX
+
+config CONFIG_FEATURE_MINIX2
+       bool "  Support Minix fs v2 (fsck_minix/mkfs_minix)"
+       default y
+       depends on CONFIG_FSCK_MINIX || CONFIG_MKFS_MINIX
+       help
+         If you wish to be able to create version 2 minix filesystems, enable this.
+         If you enabled 'mkfs_minix' then you almost certainly want to be using the
+         version 2 filesystem support.
+
+config CONFIG_GETOPT
+       bool "getopt"
+       default n
+       help
+         The getopt utility is used to break up (parse) options in command
+         lines to make it easy to write complex shell scripts that also check
+         for legal (and illegal) options.  If you want to write horribly
+         complex shell scripts, or use some horribly complex shell script
+         written by others, this utility may be for you.  Most people will
+         wisely leave this disabled.
+
+config CONFIG_HEXDUMP
+       bool "hexdump"
+       default n
+       help
+         The hexdump utility is used to display binary data in a readable
+         way that is comparable to the output from most hex editors.
+
+config CONFIG_HWCLOCK
+       bool "hwclock"
+       default n
+       help
+         The hwclock utility is used to read and set the hardware clock
+         on a system.  This is primarily used to set the current time on
+         shutdown in the hardware clock, so the hardware will keep the
+         correct time when Linux is _not_ running.
+
+config CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
+       bool "  Support long options (--hctosys,...)"
+       default n
+       depends on CONFIG_HWCLOCK
+       help
+         By default, the hwclock utility only uses short options.  If you
+         are overly fond of its long options, such as --hctosys, --utc, etc)
+         then enable this option.
+
+config CONFIG_LOSETUP
+       bool "losetup"
+       default n
+       help
+         losetup is used to associate or detach a loop device with a regular
+         file or block device, and to query the status of a loop device.  This
+         version does not currently support enabling data encryption.
+
+config CONFIG_MKSWAP
+       bool "mkswap"
+       default n
+       help
+         The mkswap utility is used to configure a file or disk partition as
+         Linux swap space.  This allows Linux to use the entire file or
+         partition as if it were additional RAM, which can greatly increase
+         the capability of low-memory machines.  This additional memory is
+         much slower than real RAM, but can be very helpful at preventing your
+         applications being killed by the Linux out of memory (OOM) killer.
+         Once you have created swap space using 'mkswap' you need to enable
+         the swap space using the 'swapon' utility.
+
+config CONFIG_MORE
+       bool "more"
+       default n
+       help
+         more is a simple utility which allows you to read text one screen
+         sized page at a time.  If you want to read text that is larger than
+         the screen, and you are using anything faster than a 300 baud modem,
+         you will probably find this utility very helpful.  If you don't have
+         any need to reading text files, you can leave this disabled.
+
+config CONFIG_FEATURE_USE_TERMIOS
+       bool "  Use termios to manipulate the screen"
+       default y
+       depends on CONFIG_MORE
+       help
+         This option allows utilities such as 'more' and 'top' to determine
+         the size of the screen.  If you leave this disabled, your utilities
+         that display things on the screen will be especially primitive and
+         will be unable to determine the current screen size, and will be
+         unable to move the cursor.
+
+config CONFIG_PIVOT_ROOT
+       bool "pivot_root"
+       default n
+       help
+         The pivot_root utility swaps the mount points for the root filesystem
+         with some other mounted filesystem.  This allows you to do all sorts
+         of wild and crazy things with your Linux system and is far more
+         powerful than 'chroot'.
+
+config CONFIG_RDATE
+       bool "rdate"
+       default n
+       help
+         The rdate utility allows you to synchronize the date and time of your
+         system clock with the date and time of a remote networked system using
+         the RFC868 protocol, which is built into the inetd daemon on most
+         systems.
+
+config CONFIG_SWAPONOFF
+       bool "swaponoff"
+       default n
+       help
+         This option enables both the 'swapon' and the 'swapoff' utilities.
+         Once you have created some swap space using 'mkswap', you also need
+         to enable your swap space with the 'swapon' utility.  The 'swapoff'
+         utility is used, typically at system shutdown, to disable any swap
+         space.  If you are not using any swap space, you can leave this
+         option disabled.
+
+config CONFIG_MOUNT
+       bool "mount"
+       default n
+       help
+         All files and filesystems in Unix are arranged into one big directory
+         tree.  The 'mount' utility is used to graft a filesystem onto a
+         particular part of the tree.  A filesystem can either live on a block
+         device, or it can be accessible over the network, as is the case with
+         NFS filesystems.  Most people using BusyBox will also want to enable
+         the 'mount' utility.
+
+config CONFIG_NFSMOUNT
+       bool "  Support mounting NFS file systems"
+       default n
+       depends on CONFIG_MOUNT
+       help
+        Enable mounting of NFS file systems.
+
+config CONFIG_UMOUNT
+       bool "umount"
+       default n
+       help
+         When you want to remove a mounted filesystem from its current mount point,
+         for example when you are shutting down the system, the 'umount' utility is
+         the tool to use.  If you enabled the 'mount' utility, you almost certainly
+         also want to enable 'umount'.
+
+config CONFIG_FEATURE_MOUNT_FORCE
+       bool "  Support forced filesystem unmounting"
+       default n
+       depends on CONFIG_UMOUNT
+       help
+         This allows you to _force_ a filesystem to be umounted.  This is generally
+         only useful when you want to get rid of an unreachable NFS system.
+
+comment "Common options for mount/umount"
+       depends on CONFIG_MOUNT || CONFIG_UMOUNT
+
+config CONFIG_FEATURE_MOUNT_LOOP
+       bool "  Support for loop devices"
+       default n
+       depends on CONFIG_MOUNT || CONFIG_UMOUNT
+       help
+         Enabling this feature allows mount to use the '-o' loop options,
+         which lets you loop mount files.  Mount will automagically setup and
+         free the necessary loop devices so you do not need to mess with the
+         'losetup' utility unless you really want to.  This is really
+         only useful if you plan to loop mount files.
+
+config CONFIG_FEATURE_MTAB_SUPPORT
+       bool "  Support for a real /etc/mtab (instead of /proc/mounts)"
+       default n
+       depends on CONFIG_MOUNT || CONFIG_UMOUNT
+       help
+         If your root filesystem is writable and you wish to have the 'mount'
+         utility create an mtab file listing the filesystems which have been
+         mounted then you should enable this option.  Most people that use
+         BusyBox have a read-only root filesystem, so they will leave this
+         option disabled and BusyBox will use the /proc/mounts file.
+
+config CONFIG_FEATURE_MTAB_FILENAME
+       string "  mtab file location"
+       default "/etc/mtab"
+       depends on CONFIG_FEATURE_MTAB_SUPPORT
+       help
+         Some people have a read only root filesystem, but they also wish to
+         have the 'mount' utility create an mtab file listing the filesystems
+         which have been mounted.  This option allows you to specify an alternative
+         location for the mtab file, such as /var/mtab, or /tmp/mtab.  The default
+         value is /etc/mtab, which is where this file is located on most desktop
+         Linux systems.
+
+endmenu
+
diff --git a/busybox/util-linux/Makefile b/busybox/util-linux/Makefile
new file mode 100644 (file)
index 0000000..4401fd1
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+top_srcdir=..
+top_buildddir=..
+srcdir=$(top_srcdir)/util-linux
+UTILLINUX_DIR:=./
+include $(top_builddir)/Rules.mak
+include $(top_builddir)/.config
+include Makefile.in
+all: $(libraries-y)
+-include $(top_builddir)/.depend
+
+clean:
+       rm -f *.o *.a $(AR_TARGET)
+
diff --git a/busybox/util-linux/Makefile.in b/busybox/util-linux/Makefile.in
new file mode 100644 (file)
index 0000000..0172b35
--- /dev/null
@@ -0,0 +1,66 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+UTILLINUX_AR:=util-linux.a
+ifndef $(UTILLINUX_DIR)
+UTILLINUX_DIR:=$(top_builddir)/util-linux/
+endif
+srcdir=$(top_srcdir)/util-linux
+
+UTILLINUX-:=
+UTILLINUX-$(CONFIG_DMESG)              +=dmesg.o
+UTILLINUX-$(CONFIG_FBSET)              +=fbset.o
+UTILLINUX-$(CONFIG_FDFLUSH)            +=fdflush.o
+UTILLINUX-$(CONFIG_FDFORMAT)           +=fdformat.o
+UTILLINUX-$(CONFIG_FDISK)              +=fdisk.o
+UTILLINUX-$(CONFIG_FREERAMDISK)                +=freeramdisk.o
+UTILLINUX-$(CONFIG_FSCK_MINIX)         +=fsck_minix.o
+UTILLINUX-$(CONFIG_GETOPT)             +=getopt.o
+UTILLINUX-$(CONFIG_HEXDUMP)            +=hexdump.o
+UTILLINUX-$(CONFIG_HWCLOCK)            +=hwclock.o
+UTILLINUX-$(CONFIG_LOSETUP)            +=losetup.o
+UTILLINUX-$(CONFIG_MKFS_MINIX)         +=mkfs_minix.o
+UTILLINUX-$(CONFIG_MKSWAP)             +=mkswap.o
+UTILLINUX-$(CONFIG_MORE)               +=more.o
+UTILLINUX-$(CONFIG_MOUNT)              +=mount.o
+UTILLINUX-$(CONFIG_NFSMOUNT)           +=nfsmount.o
+UTILLINUX-$(CONFIG_PIVOT_ROOT)         +=pivot_root.o
+UTILLINUX-$(CONFIG_RDATE)              +=rdate.o
+UTILLINUX-$(CONFIG_SWAPONOFF)          +=swaponoff.o
+UTILLINUX-$(CONFIG_UMOUNT)             +=umount.o
+
+libraries-y+=$(UTILLINUX_DIR)$(UTILLINUX_AR)
+
+$(UTILLINUX_DIR)$(UTILLINUX_AR): $(patsubst %,$(UTILLINUX_DIR)%, $(UTILLINUX-y))
+       $(AR) -ro $@ $(patsubst %,$(UTILLINUX_DIR)%, $(UTILLINUX-y))
+
+$(UTILLINUX_DIR)%.o: $(srcdir)/%.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+
+ifneq ($(strip $(CONFIG_LFS)),y)
+ifeq ($(strip $(FDISK_SUPPORT_LARGE_DISKS)),y)
+
+$(UTILLINUX_DIR)fdisk.o: $(srcdir)/fdisk.c
+       $(CC) $(CFLAGS) \
+               -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \
+               $(EXTRA_CFLAGS) -c -o $@ $<
+
+endif
+endif
+
diff --git a/busybox/util-linux/dmesg.c b/busybox/util-linux/dmesg.c
new file mode 100644 (file)
index 0000000..2ca8827
--- /dev/null
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/* dmesg.c -- Print out the contents of the kernel ring buffer
+ * Created: Sat Oct  9 16:19:47 1993
+ * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu
+ * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu)
+ * This program comes with ABSOLUTELY NO WARRANTY.
+ * Modifications by Rick Sladkey (jrs@world.std.com)
+ * Larger buffersize 3 June 1998 by Nicolai Langfeldt, based on a patch
+ * by Peeter Joot.  This was also suggested by John Hudson.
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ * from util-linux -- adapted for busybox by
+ * Erik Andersen <andersen@codepoet.org>. I ripped out Native Language
+ * Support, replaced getopt, added some gotos for redundant stuff.
+ *
+ * Audited and cleaned up on 7 March 2003 to reduce size of
+ * check error handling by Erik Andersen <andersen@codepoet.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/klog.h>
+
+#include "busybox.h"
+
+int dmesg_main(int argc, char **argv)
+{
+       char *buf
+#ifdef CONFIG_FEATURE_CLEAN_UP
+               = NULL
+#endif
+               ;
+       int bufsize = 8196;
+       int i, n;
+       int level = 0;
+       int lastc;
+       int cmd = 3;
+
+       while ((i = getopt(argc, argv, "cn:s:")) > 0) {
+               switch (i) {
+                       case 'c':
+                               cmd = 4;
+                               break;
+                       case 'n':
+                               cmd = 8;
+                               level = bb_xgetlarg(optarg, 10, 0, 10);
+                               break;
+                       case 's':
+                               /* I think a 512k max kernel ring buffer is big enough for
+                                * anybody, as the default is 16k...  Could be wrong though.
+                                * If so I'm sure I'll hear about it by the enraged masses*/
+                               bufsize = bb_xgetlarg(optarg, 10, 4096, 512*1024);
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       if (optind < argc) {
+               bb_show_usage();
+       }
+
+       if (cmd == 8) {
+               if (klogctl(cmd, NULL, level) < 0)
+                       goto die_the_death;
+               goto all_done;
+       }
+
+       buf = xmalloc(bufsize);
+       if ((n = klogctl(cmd, buf, bufsize)) < 0)
+               goto die_the_death;
+
+       lastc = '\n';
+       for (i = 0; i < n; i++) {
+               if (lastc == '\n' && buf[i] == '<') {
+                       i++;
+                       while (buf[i] >= '0' && buf[i] <= '9')
+                               i++;
+                       if (buf[i] == '>')
+                               i++;
+               }
+               lastc = buf[i];
+               putchar(lastc);
+       }
+       if (lastc != '\n')
+               putchar('\n');
+all_done:
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       if (buf) {
+               free(buf);
+       }
+#endif
+       return EXIT_SUCCESS;
+die_the_death:
+       bb_perror_nomsg_and_die();
+}
diff --git a/busybox/util-linux/fbset.c b/busybox/util-linux/fbset.c
new file mode 100644 (file)
index 0000000..83bf309
--- /dev/null
@@ -0,0 +1,427 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fbset implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This is a from-scratch implementation of fbset; but the de facto fbset
+ * implementation was a good reference. fbset (original) is released under
+ * the GPL, and is (c) 1995-1999 by:
+ *     Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+#define DEFAULTFBDEV  FB_0
+#define DEFAULTFBMODE "/etc/fb.modes"
+
+static const int OPT_CHANGE   = (1 << 0);
+static const int OPT_INFO     = (1 << 1);
+static const int OPT_READMODE = (1 << 2);
+
+enum {
+       CMD_FB = 1,
+       CMD_DB = 2,
+       CMD_GEOMETRY = 3,
+       CMD_TIMING = 4,
+       CMD_ACCEL = 5,
+       CMD_HSYNC = 6,
+       CMD_VSYNC = 7,
+       CMD_LACED = 8,
+       CMD_DOUBLE = 9,
+/*     CMD_XCOMPAT =     10, */
+       CMD_ALL = 11,
+       CMD_INFO = 12,
+       CMD_CHANGE = 13,
+
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+       CMD_XRES = 100,
+       CMD_YRES = 101,
+       CMD_VXRES = 102,
+       CMD_VYRES = 103,
+       CMD_DEPTH = 104,
+       CMD_MATCH = 105,
+       CMD_PIXCLOCK = 106,
+       CMD_LEFT = 107,
+       CMD_RIGHT = 108,
+       CMD_UPPER = 109,
+       CMD_LOWER = 110,
+       CMD_HSLEN = 111,
+       CMD_VSLEN = 112,
+       CMD_CSYNC = 113,
+       CMD_GSYNC = 114,
+       CMD_EXTSYNC = 115,
+       CMD_BCAST = 116,
+       CMD_RGBA = 117,
+       CMD_STEP = 118,
+       CMD_MOVE = 119,
+#endif
+};
+
+static unsigned int g_options = 0;
+
+/* Stuff stolen from the kernel's fb.h */
+static const int FBIOGET_VSCREENINFO = 0x4600;
+static const int FBIOPUT_VSCREENINFO = 0x4601;
+struct fb_bitfield {
+       uint32_t offset;                        /* beginning of bitfield        */
+       uint32_t length;                        /* length of bitfield           */
+       uint32_t msb_right;             /* != 0 : Most significant bit is */
+                                       /* right */
+};
+struct fb_var_screeninfo {
+       uint32_t xres;                  /* visible resolution           */
+       uint32_t yres;
+       uint32_t xres_virtual;          /* virtual resolution           */
+       uint32_t yres_virtual;
+       uint32_t xoffset;                       /* offset from virtual to visible */
+       uint32_t yoffset;                       /* resolution                   */
+
+       uint32_t bits_per_pixel;                /* guess what                   */
+       uint32_t grayscale;             /* != 0 Graylevels instead of colors */
+
+       struct fb_bitfield red;         /* bitfield in fb mem if true color, */
+       struct fb_bitfield green;       /* else only length is significant */
+       struct fb_bitfield blue;
+       struct fb_bitfield transp;      /* transparency                 */
+
+       uint32_t nonstd;                        /* != 0 Non standard pixel format */
+
+       uint32_t activate;                      /* see FB_ACTIVATE_*            */
+
+       uint32_t height;                        /* height of picture in mm    */
+       uint32_t width;                 /* width of picture in mm     */
+
+       uint32_t accel_flags;           /* acceleration flags (hints)   */
+
+       /* Timing: All values in pixclocks, except pixclock (of course) */
+       uint32_t pixclock;                      /* pixel clock in ps (pico seconds) */
+       uint32_t left_margin;           /* time from sync to picture    */
+       uint32_t right_margin;          /* time from picture to sync    */
+       uint32_t upper_margin;          /* time from sync to picture    */
+       uint32_t lower_margin;
+       uint32_t hsync_len;             /* length of horizontal sync    */
+       uint32_t vsync_len;             /* length of vertical sync      */
+       uint32_t sync;                  /* see FB_SYNC_*                */
+       uint32_t vmode;                 /* see FB_VMODE_*               */
+       uint32_t reserved[6];           /* Reserved for future compatibility */
+};
+
+
+const static struct cmdoptions_t {
+       const char *name;
+       const unsigned char param_count;
+       const unsigned char code;
+} g_cmdoptions[] = {
+       {
+       "-fb", 1, CMD_FB}, {
+       "-db", 1, CMD_DB}, {
+       "-a", 0, CMD_ALL}, {
+       "-i", 0, CMD_INFO}, {
+       "-g", 5, CMD_GEOMETRY}, {
+       "-t", 7, CMD_TIMING}, {
+       "-accel", 1, CMD_ACCEL}, {
+       "-hsync", 1, CMD_HSYNC}, {
+       "-vsync", 1, CMD_VSYNC}, {
+       "-laced", 1, CMD_LACED}, {
+       "-double", 1, CMD_DOUBLE}, {
+       "-n", 0, CMD_CHANGE}, {
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+       "-all", 0, CMD_ALL}, {
+       "-xres", 1, CMD_XRES}, {
+       "-yres", 1, CMD_YRES}, {
+       "-vxres", 1, CMD_VXRES}, {
+       "-vyres", 1, CMD_VYRES}, {
+       "-depth", 1, CMD_DEPTH}, {
+       "-match", 0, CMD_MATCH}, {
+       "-geometry", 5, CMD_GEOMETRY}, {
+       "-pixclock", 1, CMD_PIXCLOCK}, {
+       "-left", 1, CMD_LEFT}, {
+       "-right", 1, CMD_RIGHT}, {
+       "-upper", 1, CMD_UPPER}, {
+       "-lower", 1, CMD_LOWER}, {
+       "-hslen", 1, CMD_HSLEN}, {
+       "-vslen", 1, CMD_VSLEN}, {
+       "-timings", 7, CMD_TIMING}, {
+       "-csync", 1, CMD_CSYNC}, {
+       "-gsync", 1, CMD_GSYNC}, {
+       "-extsync", 1, CMD_EXTSYNC}, {
+       "-bcast", 1, CMD_BCAST}, {
+       "-rgba", 1, CMD_RGBA}, {
+       "-step", 1, CMD_STEP}, {
+       "-move", 1, CMD_MOVE}, {
+#endif
+       0, 0, 0}
+};
+
+#ifdef CONFIG_FEATURE_FBSET_READMODE
+/* taken from linux/fb.h */
+static const int FB_VMODE_INTERLACED = 1;      /* interlaced   */
+static const int FB_VMODE_DOUBLE = 2;  /* double scan */
+static const int FB_SYNC_HOR_HIGH_ACT = 1;     /* horizontal sync high active  */
+static const int FB_SYNC_VERT_HIGH_ACT = 2;    /* vertical sync high active    */
+static const int FB_SYNC_EXT = 4;      /* external sync                */
+static const int FB_SYNC_COMP_HIGH_ACT = 8;    /* composite sync high active   */
+#endif
+static int readmode(struct fb_var_screeninfo *base, const char *fn,
+                                       const char *mode)
+{
+#ifdef CONFIG_FEATURE_FBSET_READMODE
+       FILE *f;
+       char buf[256];
+       char *p = buf;
+
+       f = bb_xfopen(fn, "r");
+       while (!feof(f)) {
+               fgets(buf, sizeof(buf), f);
+               if ((p = strstr(buf, "mode ")) || (p = strstr(buf, "mode\t"))) {
+                       p += 5;
+                       if ((p = strstr(buf, mode))) {
+                               p += strlen(mode);
+                               if (!isspace(*p) && (*p != 0) && (*p != '"')
+                                       && (*p != '\r') && (*p != '\n'))
+                                       continue;       /* almost, but not quite */
+                               while (!feof(f)) {
+                                       fgets(buf, sizeof(buf), f);
+
+                    if ((p = strstr(buf, "geometry "))) {
+                        p += 9;
+
+                        sscanf(p, "%d %d %d %d %d",
+                                &(base->xres), &(base->yres),
+                                &(base->xres_virtual), &(base->yres_virtual),
+                                &(base->bits_per_pixel));
+                    } else if ((p = strstr(buf, "timings "))) {
+                        p += 8;
+    
+                        sscanf(p, "%d %d %d %d %d %d %d",
+                                &(base->pixclock),
+                                &(base->left_margin), &(base->right_margin),
+                                &(base->upper_margin), &(base->lower_margin),
+                                &(base->hsync_len), &(base->vsync_len));
+                    } else if ((p = strstr(buf, "laced "))) {
+                        p += 6;
+
+                        if (strstr(buf, "false")) {
+                            base->vmode &= ~FB_VMODE_INTERLACED;
+                        } else {
+                            base->vmode |= FB_VMODE_INTERLACED;
+                        }
+                    } else if ((p = strstr(buf, "double "))) {
+                        p += 7;
+
+                        if (strstr(buf, "false")) {
+                            base->vmode &= ~FB_VMODE_DOUBLE;
+                        } else {
+                            base->vmode |= FB_VMODE_DOUBLE;
+                        }
+                    } else if ((p = strstr(buf, "vsync "))) {
+                        p += 6;
+
+                        if (strstr(buf, "low")) {
+                            base->sync &= ~FB_SYNC_VERT_HIGH_ACT;
+                        } else {
+                            base->sync |= FB_SYNC_VERT_HIGH_ACT;
+                        }
+                    } else if ((p = strstr(buf, "hsync "))) {
+                        p += 6;
+
+                        if (strstr(buf, "low")) {
+                            base->sync &= ~FB_SYNC_HOR_HIGH_ACT;
+                        } else {
+                            base->sync |= FB_SYNC_HOR_HIGH_ACT;
+                        }
+                    } else if ((p = strstr(buf, "csync "))) {
+                        p += 6;
+
+                        if (strstr(buf, "low")) {
+                            base->sync &= ~FB_SYNC_COMP_HIGH_ACT;
+                        } else {
+                            base->sync |= FB_SYNC_COMP_HIGH_ACT;
+                        }
+                    } else if ((p = strstr(buf, "extsync "))) {
+                        p += 8;
+
+                        if (strstr(buf, "false")) {
+                            base->sync &= ~FB_SYNC_EXT;
+                        } else {
+                            base->sync |= FB_SYNC_EXT;
+                        }
+                    }
+
+                                       if (strstr(buf, "endmode"))
+                                               return 1;
+                               }
+                       }
+               }
+       }
+#else
+       bb_error_msg( "mode reading not compiled in");
+#endif
+       return 0;
+}
+
+static inline void setmode(struct fb_var_screeninfo *base,
+                                       struct fb_var_screeninfo *set)
+{
+       if ((int) set->xres > 0)
+               base->xres = set->xres;
+       if ((int) set->yres > 0)
+               base->yres = set->yres;
+       if ((int) set->xres_virtual > 0)
+               base->xres_virtual = set->xres_virtual;
+       if ((int) set->yres_virtual > 0)
+               base->yres_virtual = set->yres_virtual;
+       if ((int) set->bits_per_pixel > 0)
+               base->bits_per_pixel = set->bits_per_pixel;
+}
+
+static inline void showmode(struct fb_var_screeninfo *v)
+{
+       double drate = 0, hrate = 0, vrate = 0;
+
+       if (v->pixclock) {
+               drate = 1e12 / v->pixclock;
+               hrate =
+                       drate / (v->left_margin + v->xres + v->right_margin +
+                                        v->hsync_len);
+               vrate =
+                       hrate / (v->upper_margin + v->yres + v->lower_margin +
+                                        v->vsync_len);
+       }
+       printf("\nmode \"%ux%u-%u\"\n"
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+       "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n"
+#endif
+       "\tgeometry %u %u %u %u %u\n\ttimings %u %u %u %u %u %u %u\n\taccel %s\n\trgba %u/%u,%u/%u,%u/%u,%u/%u\nendmode\n\n",
+                  v->xres, v->yres, (int) (vrate + 0.5),
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+                  drate / 1e6, hrate / 1e3, vrate,
+#endif
+                  v->xres, v->yres, v->xres_virtual, v->yres_virtual,
+                  v->bits_per_pixel, v->pixclock, v->left_margin,
+                  v->right_margin, v->upper_margin, v->lower_margin, v->hsync_len,
+                  v->vsync_len, (v->accel_flags > 0 ? "true" : "false"), v->red.length,
+                  v->red.offset, v->green.length, v->green.offset, v->blue.length,
+                  v->blue.offset, v->transp.length, v->transp.offset);
+}
+
+#ifdef STANDALONE
+int main(int argc, char **argv)
+#else
+extern int fbset_main(int argc, char **argv)
+#endif
+{
+       struct fb_var_screeninfo var, varset;
+       int fh, i;
+       char *fbdev = DEFAULTFBDEV;
+       char *modefile = DEFAULTFBMODE;
+       char *thisarg, *mode = NULL;
+
+       memset(&varset, 0xFF, sizeof(varset));
+
+       /* parse cmd args.... why do they have to make things so difficult? */
+       argv++;
+       argc--;
+       for (; argc > 0 && (thisarg = *argv); argc--, argv++) {
+               for (i = 0; g_cmdoptions[i].name; i++) {
+                       if (!strcmp(thisarg, g_cmdoptions[i].name)) {
+                               if (argc - 1 < g_cmdoptions[i].param_count)
+                                       bb_show_usage();
+                               switch (g_cmdoptions[i].code) {
+                               case CMD_FB:
+                                       fbdev = argv[1];
+                                       break;
+                               case CMD_DB:
+                                       modefile = argv[1];
+                                       break;
+                               case CMD_GEOMETRY:
+                                       varset.xres = strtoul(argv[1], 0, 0);
+                                       varset.yres = strtoul(argv[2], 0, 0);
+                                       varset.xres_virtual = strtoul(argv[3], 0, 0);
+                                       varset.yres_virtual = strtoul(argv[4], 0, 0);
+                                       varset.bits_per_pixel = strtoul(argv[5], 0, 0);
+                                       break;
+                               case CMD_TIMING:
+                                       varset.pixclock = strtoul(argv[1], 0, 0);
+                                       varset.left_margin = strtoul(argv[2], 0, 0);
+                                       varset.right_margin = strtoul(argv[3], 0, 0);
+                                       varset.upper_margin = strtoul(argv[4], 0, 0);
+                                       varset.lower_margin = strtoul(argv[5], 0, 0);
+                                       varset.hsync_len = strtoul(argv[6], 0, 0);
+                                       varset.vsync_len = strtoul(argv[7], 0, 0);
+                                       break;
+                case CMD_CHANGE:
+                    g_options |= OPT_CHANGE;
+                    break;
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+                               case CMD_XRES:
+                                       varset.xres = strtoul(argv[1], 0, 0);
+                                       break;
+                               case CMD_YRES:
+                                       varset.yres = strtoul(argv[1], 0, 0);
+                                       break;
+                          case CMD_DEPTH:
+                                       varset.bits_per_pixel = strtoul(argv[1], 0, 0);
+                                       break;
+#endif
+                               }
+                               argc -= g_cmdoptions[i].param_count;
+                               argv += g_cmdoptions[i].param_count;
+                               break;
+                       }
+               }
+               if (!g_cmdoptions[i].name) {
+                       if (argc == 1) {
+                               mode = *argv;
+                               g_options |= OPT_READMODE;
+                       } else {
+                               bb_show_usage();
+                       }
+               }
+       }
+
+       if ((fh = open(fbdev, O_RDONLY)) < 0)
+               bb_perror_msg_and_die("fbset(open)");
+       if (ioctl(fh, FBIOGET_VSCREENINFO, &var))
+               bb_perror_msg_and_die("fbset(ioctl)");
+       if (g_options & OPT_READMODE) {
+               if (!readmode(&var, modefile, mode)) {
+                       bb_error_msg("Unknown video mode `%s'", mode);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       setmode(&var, &varset);
+       if (g_options & OPT_CHANGE)
+               if (ioctl(fh, FBIOPUT_VSCREENINFO, &var))
+                       bb_perror_msg_and_die("fbset(ioctl)");
+       showmode(&var);
+       /* Don't close the file, as exiting will take care of that */
+       /* close(fh); */
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/util-linux/fdflush.c b/busybox/util-linux/fdflush.c
new file mode 100644 (file)
index 0000000..c3fcf33
--- /dev/null
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fdflush implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@perens.com>.
+ * Copyright (C) 2003 by Erik Andersen <andersen@codeoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+/* From <linux/fd.h> */
+#define FDFLUSH  _IO(2,0x4b)
+
+extern int fdflush_main(int argc, char **argv)
+{
+       int fd, result;
+
+       if (argc <= 1)
+               bb_show_usage();
+
+       fd = bb_xopen(argv[1], 0);
+
+       result = ioctl(fd, FDFLUSH, 0);
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       close(fd);
+#endif
+       if (result) {
+               bb_perror_nomsg_and_die();
+       }
+
+       /* Don't bother closing.  Exit does
+        * that, so we can save a few bytes */
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/util-linux/fdformat.c b/busybox/util-linux/fdformat.c
new file mode 100644 (file)
index 0000000..bd45275
--- /dev/null
@@ -0,0 +1,161 @@
+/* fdformat.c  -  Low-level formats a floppy disk - Werner Almesberger */
+
+/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - more i18n/nls translatable strings marked
+ *
+ * 5 July 2003 -- modified for Busybox by Erik Andersen
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+
+/* Stuff extracted from linux/fd.h */
+struct floppy_struct {
+       unsigned int    size,           /* nr of sectors total */
+                       sect,           /* sectors per track */
+                       head,           /* nr of heads */
+                       track,          /* nr of tracks */
+                       stretch;        /* !=0 means double track steps */
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
+       unsigned char   gap,            /* gap1 size */
+
+                       rate,           /* data rate. |= 0x40 for perpendicular */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+                            512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
+
+                       spec1,          /* stepping rate, head unload time */
+                       fmt_gap;        /* gap2 size */
+       const char      * name; /* used only for predefined formats */
+};
+struct format_descr {
+       unsigned int device,head,track;
+};
+#define FDFMTBEG _IO(2,0x47)
+#define        FDFMTTRK _IOW(2,0x48, struct format_descr)
+#define FDFMTEND _IO(2,0x49)
+#define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
+#define FD_FILL_BYTE 0xF6 /* format fill byte. */
+
+static void print_and_flush(const char * __restrict format, ...)
+{
+       va_list arg;
+
+       va_start(arg, format);
+       bb_vfprintf(stdout, format, arg);
+       va_end(arg);
+       bb_xfflush_stdout();
+}
+
+static void bb_xioctl(int fd, int request, void *argp, const char *string)
+{
+       if (ioctl (fd, request, argp) < 0) {
+               bb_perror_msg_and_die(string);
+       }
+}
+
+int fdformat_main(int argc,char **argv)
+{
+       int fd, n, cyl, read_bytes, verify;
+       unsigned char *data;
+       struct stat st;
+       struct floppy_struct param;
+       struct format_descr descr;
+
+       if (argc < 2) {
+               bb_show_usage();
+       }
+       verify = !bb_getopt_ulflags(argc, argv, "n");
+       argv += optind;
+
+       /* R_OK is needed for verifying */
+       if (stat(*argv,&st) < 0 || access(*argv,W_OK | R_OK ) < 0) {
+               bb_perror_msg_and_die(*argv);
+       }
+       if (!S_ISBLK(st.st_mode)) {
+               bb_error_msg_and_die("%s: not a block device",*argv);
+               /* do not test major - perhaps this was an USB floppy */
+       }
+
+
+       /* O_RDWR for formatting and verifying */
+       fd = bb_xopen(*argv,O_RDWR );
+
+       bb_xioctl(fd, FDGETPRM, &param, "FDGETPRM");/*original message was: "Could not determine current format type" */
+
+       print_and_flush("%s-sided, %d tracks, %d sec/track. Total capacity %d kB.\n",
+               (param.head == 2) ? "Double" : "Single",
+               param.track, param.sect, param.size >> 1);
+
+       /* FORMAT */
+       print_and_flush("Formatting ... ", NULL);
+       bb_xioctl(fd, FDFMTBEG,NULL,"FDFMTBEG");
+
+       /* n == track */
+       for (n = 0; n < param.track; n++)
+       {
+           descr.head = 0;
+           descr.track = n;
+           bb_xioctl(fd, FDFMTTRK,&descr,"FDFMTTRK");
+           print_and_flush("%3d\b\b\b", n);
+           if (param.head == 2) {
+               descr.head = 1;
+               bb_xioctl(fd, FDFMTTRK,&descr,"FDFMTTRK");
+           }
+       }
+
+       bb_xioctl(fd,FDFMTEND,NULL,"FDFMTEND");
+       print_and_flush("done\n", NULL);
+
+       /* VERIFY */
+       if(verify) {
+               /* n == cyl_size */
+               n = param.sect*param.head*512;
+
+               data = xmalloc(n);
+               print_and_flush("Verifying ... ", NULL);
+               for (cyl = 0; cyl < param.track; cyl++) {
+                       print_and_flush("%3d\b\b\b", cyl);
+                       if((read_bytes = safe_read(fd,data,n))!= n ) {
+                               if(read_bytes < 0) {
+                                       bb_perror_msg("Read: ");
+                       }
+                               bb_error_msg_and_die("Problem reading cylinder %d, expected %d, read %d", cyl, n, read_bytes);
+                       }
+                       /* Check backwards so we don't need a counter */
+                       while(--read_bytes>=0) {
+                               if( data[read_bytes] != FD_FILL_BYTE) {
+                                        print_and_flush("bad data in cyl %d\nContinuing ... ",cyl);
+                               }
+                       }
+               }
+               /* There is no point in freeing blocks at the end of a program, because
+               all of the program's space is given back to the system when the process
+               terminates.*/
+#ifdef CONFIG_FEATURE_CLEAN_UP
+               free(data);
+#endif
+               print_and_flush("done\n", NULL);
+       }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       close(fd);
+#endif
+       /* Don't bother closing.  Exit does
+        * that, so we can save a few bytes */
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/util-linux/fdisk.c b/busybox/util-linux/fdisk.c
new file mode 100644 (file)
index 0000000..8580bec
--- /dev/null
@@ -0,0 +1,5880 @@
+/* fdisk.c -- Partition table manipulator for Linux.
+ *
+ * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ *
+ * This program is free software.  You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation: either version 1 or
+ * (at your option) any later version.
+ *
+ * Vladimir Oleynik <dzo@simtreas.ru> 2001,2002 Busybox port
+ */
+
+#define UTIL_LINUX_VERSION "2.12"
+
+#define PROC_PARTITIONS "/proc/partitions"
+
+#include <features.h>
+#include <sys/types.h>
+#include <sys/stat.h>           /* stat */
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <assert.h>             /* assert */
+#include <getopt.h>
+#include <endian.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>     /* major */
+
+#include <stdint.h>        /* for uint32_t, uint16_t, uint8_t, int16_t, etc */
+
+/* Copied from linux/major.h */
+#define FLOPPY_MAJOR    2
+
+#include <sys/utsname.h>
+
+#include "busybox.h"
+
+#define MAKE_VERSION(p,q,r)     (65536*(p) + 256*(q) + (r))
+
+#define DKTYPENAMES
+
+#define _(Text) Text
+
+#define BLKRRPART  _IO(0x12,95)    /* re-read partition table */
+#define BLKGETSIZE _IO(0x12,96)    /* return device size */
+#define BLKFLSBUF  _IO(0x12,97)    /* flush buffer cache */
+#define BLKSSZGET  _IO(0x12,104)   /* get block device sector size */
+
+/* Avoid conflicts with the 2.6 kernel headers, which define
+ * _IOR rather differently */
+#undef _IOR
+#define _IOR(type,nr,size)      _IOC(_IOC_READ,(type),(nr),sizeof(size))
+#define BLKGETSIZE64 _IOR(0x12,114,uint64_t)
+
+/*
+   fdisk.h
+*/
+
+#define DEFAULT_SECTOR_SIZE     512
+#define MAX_SECTOR_SIZE 2048
+#define SECTOR_SIZE     512     /* still used in BSD code */
+#define MAXIMUM_PARTS   60
+
+#define ACTIVE_FLAG     0x80
+
+#define EXTENDED        0x05
+#define WIN98_EXTENDED  0x0f
+#define LINUX_PARTITION 0x81
+#define LINUX_SWAP      0x82
+#define LINUX_NATIVE    0x83
+#define LINUX_EXTENDED  0x85
+#define LINUX_LVM       0x8e
+#define LINUX_RAID      0xfd
+
+#define SUNOS_SWAP 3
+#define WHOLE_DISK 5
+
+#define IS_EXTENDED(i) \
+       ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+
+#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+#define cround(n)       (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
+#define scround(x)      (((x)+units_per_sector-1)/units_per_sector)
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+#endif
+
+
+/* including <linux/hdreg.h> also fails */
+struct hd_geometry {
+      unsigned char heads;
+      unsigned char sectors;
+      unsigned short cylinders;
+      unsigned long start;
+};
+
+#define HDIO_GETGEO             0x0301  /* get device geometry */
+
+
+struct systypes {
+       const unsigned char *name;
+};
+
+static uint    sector_size = DEFAULT_SECTOR_SIZE,
+       user_set_sector_size,
+       sector_offset = 1;
+
+/*
+ * Raw disk label. For DOS-type partition tables the MBR,
+ * with descriptions of the primary partitions.
+ */
+static char MBRbuffer[MAX_SECTOR_SIZE];
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+static int     sun_label;                  /* looking at sun disklabel */
+#else
+#define sun_label 0
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+static int     sgi_label;                  /* looking at sgi disklabel */
+#else
+#define sgi_label 0
+#endif
+#ifdef CONFIG_FEATURE_AIX_LABEL
+static int     aix_label;                  /* looking at aix disklabel */
+#else
+#define aix_label 0
+#endif
+#ifdef CONFIG_FEATURE_OSF_LABEL
+static int     osf_label;                  /* looking at OSF/1 disklabel */
+static int     possibly_osf_label;
+#else
+#define osf_label 0
+#endif
+
+#define dos_label (!sun_label && !sgi_label && !aix_label && !osf_label)
+
+static uint heads, sectors, cylinders;
+static void update_units(void);
+
+
+/*
+ * return partition name - uses static storage unless buf is supplied
+ */
+static const char *
+partname(const char *dev, int pno, int lth) {
+       static char buffer[80];
+       const char *p;
+       int w, wp;
+       int bufsiz;
+       char *bufp;
+
+       bufp = buffer;
+       bufsiz = sizeof(buffer);
+
+       w = strlen(dev);
+       p = "";
+
+       if (isdigit(dev[w-1]))
+               p = "p";
+
+       /* devfs kludge - note: fdisk partition names are not supposed
+          to equal kernel names, so there is no reason to do this */
+       if (strcmp (dev + w - 4, "disc") == 0) {
+               w -= 4;
+               p = "part";
+       }
+
+       wp = strlen(p);
+
+       if (lth) {
+               snprintf(bufp, bufsiz, "%*.*s%s%-2u",
+                        lth-wp-2, w, dev, p, pno);
+       } else {
+               snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
+       }
+       return bufp;
+}
+
+struct partition {
+       unsigned char boot_ind;         /* 0x80 - active */
+       unsigned char head;             /* starting head */
+       unsigned char sector;           /* starting sector */
+       unsigned char cyl;              /* starting cylinder */
+       unsigned char sys_ind;          /* What partition type */
+       unsigned char end_head;         /* end head */
+       unsigned char end_sector;       /* end sector */
+       unsigned char end_cyl;          /* end cylinder */
+       unsigned char start4[4];        /* starting sector counting from 0 */
+       unsigned char size4[4];         /* nr of sectors in partition */
+} __attribute__((__packed__));
+
+enum failure {
+       ioctl_error, unable_to_open, unable_to_read, unable_to_seek,
+       unable_to_write
+};
+
+enum action {fdisk, require, try_only, create_empty_dos, create_empty_sun};
+
+static const char *disk_device;
+static int fd;                  /* the disk */
+static int partitions = 4;      /* maximum partition + 1 */
+static uint display_in_cyl_units = 1;
+static uint units_per_sector = 1;
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static char *line_ptr;
+static void change_units(void);
+static void reread_partition_table(int leave);
+static void delete_partition(int i);
+static int  get_partition(int warn, int max);
+static void list_types(const struct systypes *sys);
+static uint read_int(uint low, uint dflt, uint high, uint base, char *mesg);
+#endif
+static const char *partition_type(unsigned char type);
+static void fdisk_fatal(enum failure why) __attribute__ ((noreturn));
+static void get_geometry(void);
+static int get_boot(enum action what);
+
+#define PLURAL   0
+#define SINGULAR 1
+
+#define hex_val(c)      ({ \
+                               char _c = (c); \
+                               isdigit(_c) ? _c - '0' : \
+                               tolower(_c) + 10 - 'a'; \
+                       })
+
+
+#define LINE_LENGTH     800
+#define pt_offset(b, n) ((struct partition *)((b) + 0x1be + \
+                               (n) * sizeof(struct partition)))
+#define sector(s)       ((s) & 0x3f)
+#define cylinder(s, c)  ((c) | (((s) & 0xc0) << 2))
+
+#define hsc2sector(h,s,c) (sector(s) - 1 + sectors * \
+                               ((h) + heads * cylinder(s,c)))
+#define set_hsc(h,s,c,sector) { \
+                               s = sector % sectors + 1;       \
+                               sector /= sectors;      \
+                               h = sector % heads;     \
+                               sector /= heads;        \
+                               c = sector & 0xff;      \
+                               s |= (sector >> 2) & 0xc0;      \
+                       }
+
+
+static int32_t get_start_sect(const struct partition *p);
+static int32_t get_nr_sects(const struct partition *p);
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer (MBRbuffer)
+ * and have NULL ext_pointer.
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+static struct pte {
+       struct partition *part_table;   /* points into sectorbuffer */
+       struct partition *ext_pointer;  /* points into sectorbuffer */
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       char changed;           /* boolean */
+#endif
+       off_t offset;            /* disk sector number */
+       char *sectorbuffer;     /* disk sector contents */
+} ptes[MAXIMUM_PARTS];
+
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+set_all_unchanged(void) {
+       int i;
+
+       for (i = 0; i < MAXIMUM_PARTS; i++)
+               ptes[i].changed = 0;
+}
+
+static void
+set_changed(int i) {
+       ptes[i].changed = 1;
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_OSF_LABEL)
+static struct partition *
+get_part_table(int i) {
+       return ptes[i].part_table;
+}
+#endif
+
+static const char *
+str_units(int n) {      /* n==1: use singular */
+       if (n == 1)
+               return display_in_cyl_units ? _("cylinder") : _("sector");
+       else
+               return display_in_cyl_units ? _("cylinders") : _("sectors");
+}
+
+static int
+valid_part_table_flag(const unsigned char *b) {
+       return (b[510] == 0x55 && b[511] == 0xaa);
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static char  line_buffer[LINE_LENGTH];
+
+/* read line; return 0 or first char */
+static int
+read_line(void)
+{
+       static int got_eof = 0;
+
+       fflush (stdout);         /* requested by niles@scyld.com */
+       line_ptr = line_buffer;
+       if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
+               if (feof(stdin))
+                       got_eof++;      /* user typed ^D ? */
+               if (got_eof >= 3) {
+                       fprintf(stderr, _("\ngot EOF thrice - exiting..\n"));
+                       exit(1);
+               }
+               return 0;
+       }
+       while (*line_ptr && !isgraph(*line_ptr))
+               line_ptr++;
+       return *line_ptr;
+}
+
+static char
+read_char(const char *mesg)
+{
+       do {
+               fputs(mesg, stdout);
+       } while (!read_line());
+       return *line_ptr;
+}
+
+static char
+read_chars(const char *mesg)
+{
+       fputs(mesg, stdout);
+       if (!read_line()) {
+               *line_ptr = '\n';
+               line_ptr[1] = 0;
+       }
+       return *line_ptr;
+}
+
+static int
+read_hex(const struct systypes *sys)
+{
+       int hex;
+
+       while (1)
+       {
+          read_char(_("Hex code (type L to list codes): "));
+          if (*line_ptr == 'l' || *line_ptr == 'L')
+              list_types(sys);
+          else if (isxdigit (*line_ptr))
+          {
+             hex = 0;
+             do
+                hex = hex << 4 | hex_val(*line_ptr++);
+             while (isxdigit(*line_ptr));
+             return hex;
+          }
+       }
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+#ifdef CONFIG_FEATURE_AIX_LABEL
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *      This file may be redistributed under
+ *      the terms of the GNU Public License.
+ */
+
+typedef struct {
+       unsigned int   magic;        /* expect AIX_LABEL_MAGIC */
+       unsigned int   fillbytes1[124];
+       unsigned int   physical_volume_id;
+       unsigned int   fillbytes2[124];
+} aix_partition;
+
+#define AIX_LABEL_MAGIC         0xc9c2d4c1
+#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9
+#define AIX_INFO_MAGIC          0x00072959
+#define AIX_INFO_MAGIC_SWAPPED  0x59290700
+
+#define aixlabel ((aix_partition *)MBRbuffer)
+
+
+/*
+  Changes:
+  * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+  *     Internationalization
+  *
+  * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
+  *      Some fixes
+*/
+
+static  int     aix_other_endian;
+static  short   aix_volumes=1;
+
+/*
+ * only dealing with free blocks here
+ */
+
+static void
+aix_info( void ) {
+    puts(
+       _("\n\tThere is a valid AIX label on this disk.\n"
+       "\tUnfortunately Linux cannot handle these\n"
+       "\tdisks at the moment.  Nevertheless some\n"
+       "\tadvice:\n"
+       "\t1. fdisk will destroy its contents on write.\n"
+       "\t2. Be sure that this disk is NOT a still vital\n"
+       "\t   part of a volume group. (Otherwise you may\n"
+       "\t   erase the other disks as well, if unmirrored.)\n"
+       "\t3. Before deleting this physical volume be sure\n"
+       "\t   to remove the disk logically from your AIX\n"
+       "\t   machine.  (Otherwise you become an AIXpert).")
+    );
+}
+
+static void
+aix_nolabel( void )
+{
+    aixlabel->magic = 0;
+    aix_label = 0;
+    partitions = 4;
+    memset( MBRbuffer, 0, sizeof(MBRbuffer) );  /* avoid fdisk cores */
+    return;
+}
+
+static int
+check_aix_label( void )
+{
+    if (aixlabel->magic != AIX_LABEL_MAGIC &&
+       aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) {
+       aix_label = 0;
+       aix_other_endian = 0;
+       return 0;
+    }
+    aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
+    update_units();
+    aix_label = 1;
+    partitions= 1016;
+    aix_volumes = 15;
+    aix_info();
+    aix_nolabel();              /* %% */
+    aix_label = 1;              /* %% */
+    return 1;
+}
+#endif  /* AIX_LABEL */
+
+#ifdef CONFIG_FEATURE_OSF_LABEL
+/*
+ * Copyright (c) 1987, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgment:
+ *      This product includes software developed by the University of
+ *      California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef BSD_DISKMAGIC
+#define BSD_DISKMAGIC     ((uint32_t) 0x82564557)
+#endif
+
+#ifndef BSD_MAXPARTITIONS
+#define BSD_MAXPARTITIONS 16
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (i386) || defined (__sparc__) || defined (__arm__) || defined (__mips__) || defined (__s390__) || defined (__sh__) || defined(__x86_64__)
+#define BSD_LABELSECTOR   1
+#define BSD_LABELOFFSET   0
+#elif defined (__alpha__) || defined (__powerpc__) || defined (__ia64__) || defined (__hppa__)
+#define BSD_LABELSECTOR   0
+#define BSD_LABELOFFSET   64
+#elif defined (__s390__) || defined (__s390x__)
+#define BSD_LABELSECTOR   1
+#define BSD_LABELOFFSET   0
+#else
+#error unknown architecture
+#endif
+
+#define BSD_BBSIZE        8192          /* size of boot area, with label */
+#define BSD_SBSIZE        8192          /* max size of fs superblock */
+
+struct xbsd_disklabel {
+       uint32_t   d_magic;                /* the magic number */
+       int16_t   d_type;                 /* drive type */
+       int16_t   d_subtype;              /* controller/d_type specific */
+       char    d_typename[16];         /* type name, e.g. "eagle" */
+       char    d_packname[16];                 /* pack identifier */
+                       /* disk geometry: */
+       uint32_t   d_secsize;              /* # of bytes per sector */
+       uint32_t   d_nsectors;             /* # of data sectors per track */
+       uint32_t   d_ntracks;              /* # of tracks per cylinder */
+       uint32_t   d_ncylinders;           /* # of data cylinders per unit */
+       uint32_t   d_secpercyl;            /* # of data sectors per cylinder */
+       uint32_t   d_secperunit;           /* # of data sectors per unit */
+       /*
+        * Spares (bad sector replacements) below
+        * are not counted in d_nsectors or d_secpercyl.
+        * Spare sectors are assumed to be physical sectors
+        * which occupy space at the end of each track and/or cylinder.
+        */
+       uint16_t   d_sparespertrack;       /* # of spare sectors per track */
+       uint16_t   d_sparespercyl;         /* # of spare sectors per cylinder */
+       /*
+        * Alternate cylinders include maintenance, replacement,
+        * configuration description areas, etc.
+        */
+       uint32_t   d_acylinders;           /* # of alt. cylinders per unit */
+
+                       /* hardware characteristics: */
+       /*
+        * d_interleave, d_trackskew and d_cylskew describe perturbations
+        * in the media format used to compensate for a slow controller.
+        * Interleave is physical sector interleave, set up by the formatter
+        * or controller when formatting.  When interleaving is in use,
+        * logically adjacent sectors are not physically contiguous,
+        * but instead are separated by some number of sectors.
+        * It is specified as the ratio of physical sectors traversed
+        * per logical sector.  Thus an interleave of 1:1 implies contiguous
+        * layout, while 2:1 implies that logical sector 0 is separated
+        * by one sector from logical sector 1.
+        * d_trackskew is the offset of sector 0 on track N
+        * relative to sector 0 on track N-1 on the same cylinder.
+        * Finally, d_cylskew is the offset of sector 0 on cylinder N
+        * relative to sector 0 on cylinder N-1.
+        */
+       uint16_t   d_rpm;                  /* rotational speed */
+       uint16_t   d_interleave;           /* hardware sector interleave */
+       uint16_t   d_trackskew;            /* sector 0 skew, per track */
+       uint16_t   d_cylskew;              /* sector 0 skew, per cylinder */
+       uint32_t   d_headswitch;           /* head switch time, usec */
+       uint32_t   d_trkseek;              /* track-to-track seek, usec */
+       uint32_t   d_flags;                /* generic flags */
+#define NDDATA 5
+       uint32_t   d_drivedata[NDDATA];    /* drive-type specific information */
+#define NSPARE 5
+       uint32_t   d_spare[NSPARE];        /* reserved for future use */
+       uint32_t   d_magic2;               /* the magic number (again) */
+       uint16_t   d_checksum;             /* xor of data incl. partitions */
+                       /* filesystem and partition information: */
+       uint16_t   d_npartitions;          /* number of partitions in following */
+       uint32_t   d_bbsize;               /* size of boot area at sn0, bytes */
+       uint32_t   d_sbsize;               /* max size of fs superblock, bytes */
+       struct xbsd_partition    {      /* the partition table */
+               uint32_t   p_size;         /* number of sectors in partition */
+               uint32_t   p_offset;       /* starting sector */
+               uint32_t   p_fsize;        /* filesystem basic fragment size */
+               uint8_t    p_fstype;       /* filesystem type, see below */
+               uint8_t    p_frag;         /* filesystem fragments per block */
+               uint16_t   p_cpg;          /* filesystem cylinders per group */
+       } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+/* d_type values: */
+#define BSD_DTYPE_SMD           1               /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP          2               /* MSCP */
+#define BSD_DTYPE_DEC           3               /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI          4               /* SCSI */
+#define BSD_DTYPE_ESDI          5               /* ESDI interface */
+#define BSD_DTYPE_ST506         6               /* ST506 etc. */
+#define BSD_DTYPE_HPIB          7               /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL          8               /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY        10              /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART    0x8             /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s)   ((s) & 3)       /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY     0x10            /* drive params in label */
+
+#ifdef DKTYPENAMES
+static const char * const xbsd_dktypenames[] = {
+       "unknown",
+       "SMD",
+       "MSCP",
+       "old DEC",
+       "SCSI",
+       "ESDI",
+       "ST506",
+       "HP-IB",
+       "HP-FL",
+       "type 9",
+       "floppy",
+       0
+};
+#define BSD_DKMAXTYPES  (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1)
+#endif
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED   0               /* unused */
+#define BSD_FS_SWAP     1               /* swap */
+#define BSD_FS_V6       2               /* Sixth Edition */
+#define BSD_FS_V7       3               /* Seventh Edition */
+#define BSD_FS_SYSV     4               /* System V */
+#define BSD_FS_V71K     5               /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8       6               /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS   7               /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS   9               /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER    10              /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS     11              /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660  12              /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS    BSD_FS_ISO9660
+#define BSD_FS_BOOT     13              /* partition contains bootstrap */
+#define BSD_FS_ADOS     14              /* AmigaDOS fast file system */
+#define BSD_FS_HFS      15              /* Macintosh HFS */
+#define BSD_FS_ADVFS    16              /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2     8               /* ext2 file system */
+#else
+#define BSD_FS_MSDOS    8               /* MS-DOS file system */
+#endif
+
+#ifdef  DKTYPENAMES
+static const struct systypes xbsd_fstypes[] = {
+/* BSD_FS_UNUSED  */   {"\x00" "unused"},
+/* BSD_FS_SWAP    */   {"\x01" "swap"},
+/* BSD_FS_V6      */   {"\x02" "Version 6"},
+/* BSD_FS_V7      */   {"\x03" "Version 7"},
+/* BSD_FS_SYSV    */   {"\x04" "System V"},
+/* BSD_FS_V71K    */   {"\x05" "4.1BSD"},
+/* BSD_FS_V8      */   {"\x06" "Eighth Edition"},
+/* BSD_FS_BSDFFS  */   {"\x07" "4.2BSD"},
+#ifdef __alpha__
+/* BSD_FS_EXT2    */   {"\x08" "ext2"},
+#else
+/* BSD_FS_MSDOS   */   {"\x08" "MS-DOS"},
+#endif
+/* BSD_FS_BSDLFS  */   {"\x09" "4.4LFS"},
+/* BSD_FS_OTHER   */   {"\x0a" "unknown"},
+/* BSD_FS_HPFS    */   {"\x0b" "HPFS"},
+/* BSD_FS_ISO9660 */   {"\x0c" "ISO-9660"},
+/* BSD_FS_BOOT    */   {"\x0d" "boot"},
+/* BSD_FS_ADOS    */   {"\x0e" "ADOS"},
+/* BSD_FS_HFS     */   {"\x0f" "HFS"},
+/* BSD_FS_ADVFS   */   {"\x10" "AdvFS"},
+                      { NULL }
+};
+#define BSD_FSMAXTYPES (SIZE(xbsd_fstypes)-1)
+
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01            /* removable media */
+#define BSD_D_ECC       0x02            /* supports ECC */
+#define BSD_D_BADSECT   0x04            /* supports bad sector forw. */
+#define BSD_D_RAMDISK   0x08            /* disk emulator */
+#define BSD_D_CHAIN     0x10            /* can do back-back transfers */
+#define BSD_D_DOSPART   0x20            /* within MSDOS partition */
+
+#endif /* OSF_LABEL */
+
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *      This file may be modified and redistributed under
+ *      the terms of the GNU Public License.
+ */
+
+struct device_parameter { /* 48 bytes */
+       unsigned char  skew;
+       unsigned char  gap1;
+       unsigned char  gap2;
+       unsigned char  sparecyl;
+       unsigned short pcylcount;
+       unsigned short head_vol0;
+       unsigned short ntrks;   /* tracks in cyl 0 or vol 0 */
+       unsigned char  cmd_tag_queue_depth;
+       unsigned char  unused0;
+       unsigned short unused1;
+       unsigned short nsect;   /* sectors/tracks in cyl 0 or vol 0 */
+       unsigned short bytes;
+       unsigned short ilfact;
+       unsigned int   flags;           /* controller flags */
+       unsigned int   datarate;
+       unsigned int   retries_on_error;
+       unsigned int   ms_per_word;
+       unsigned short xylogics_gap1;
+       unsigned short xylogics_syncdelay;
+       unsigned short xylogics_readdelay;
+       unsigned short xylogics_gap2;
+       unsigned short xylogics_readgate;
+       unsigned short xylogics_writecont;
+};
+
+#define SGI_VOLHDR      0x00
+/* 1 and 2 were used for drive types no longer supported by SGI */
+#define SGI_SWAP        0x03
+/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */
+#define SGI_VOLUME      0x06
+#define SGI_EFS         0x07
+#define SGI_LVOL        0x08
+#define SGI_RLVOL       0x09
+#define SGI_XFS         0x0a
+#define SGI_XFSLOG      0x0b
+#define SGI_XLV         0x0c
+#define SGI_XVM         0x0d
+#define ENTIRE_DISK     SGI_VOLUME
+/*
+ * controller flags
+ */
+#define SECTOR_SLIP     0x01
+#define SECTOR_FWD      0x02
+#define TRACK_FWD       0x04
+#define TRACK_MULTIVOL  0x08
+#define IGNORE_ERRORS   0x10
+#define RESEEK          0x20
+#define ENABLE_CMDTAGQ  0x40
+
+typedef struct {
+       unsigned int   magic;            /* expect SGI_LABEL_MAGIC */
+       unsigned short boot_part;        /* active boot partition */
+       unsigned short swap_part;        /* active swap partition */
+       unsigned char  boot_file[16];    /* name of the bootfile */
+       struct device_parameter devparam;       /*  1 * 48 bytes */
+       struct volume_directory {               /* 15 * 16 bytes */
+               unsigned char vol_file_name[8]; /* a character array */
+               unsigned int  vol_file_start;   /* number of logical block */
+               unsigned int  vol_file_size;    /* number of bytes */
+       } directory[15];
+       struct sgi_partition {                  /* 16 * 12 bytes */
+               unsigned int num_sectors;       /* number of blocks */
+               unsigned int start_sector;      /* must be cylinder aligned */
+               unsigned int id;
+       } partitions[16];
+       unsigned int   csum;
+       unsigned int   fillbytes;
+} sgi_partition;
+
+typedef struct {
+       unsigned int   magic;           /* looks like a magic number */
+       unsigned int   a2;
+       unsigned int   a3;
+       unsigned int   a4;
+       unsigned int   b1;
+       unsigned short b2;
+       unsigned short b3;
+       unsigned int   c[16];
+       unsigned short d[3];
+       unsigned char  scsi_string[50];
+       unsigned char  serial[137];
+       unsigned short check1816;
+       unsigned char  installer[225];
+} sgiinfo;
+
+#define SGI_LABEL_MAGIC         0x0be5a941
+#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b
+#define SGI_INFO_MAGIC          0x00072959
+#define SGI_INFO_MAGIC_SWAPPED  0x59290700
+#define SGI_SSWAP16(x) (sgi_other_endian ? __swap16(x) \
+                                : (uint16_t)(x))
+#define SGI_SSWAP32(x) (sgi_other_endian ? __swap32(x) \
+                                : (uint32_t)(x))
+
+#define sgilabel ((sgi_partition *)MBRbuffer)
+#define sgiparam (sgilabel->devparam)
+
+typedef struct {
+       unsigned char info[128];   /* Informative text string */
+       unsigned char spare0[14];
+       struct sun_info {
+               unsigned char spare1;
+               unsigned char id;
+               unsigned char spare2;
+               unsigned char flags;
+       } infos[8];
+       unsigned char spare1[246]; /* Boot information etc. */
+       unsigned short rspeed;     /* Disk rotational speed */
+       unsigned short pcylcount;  /* Physical cylinder count */
+       unsigned short sparecyl;   /* extra sects per cylinder */
+       unsigned char spare2[4];   /* More magic... */
+       unsigned short ilfact;     /* Interleave factor */
+       unsigned short ncyl;       /* Data cylinder count */
+       unsigned short nacyl;      /* Alt. cylinder count */
+       unsigned short ntrks;      /* Tracks per cylinder */
+       unsigned short nsect;      /* Sectors per track */
+       unsigned char spare3[4];   /* Even more magic... */
+       struct sun_partition {
+               uint32_t start_cylinder;
+               uint32_t num_sectors;
+       } partitions[8];
+       unsigned short magic;      /* Magic number */
+       unsigned short csum;       /* Label xor'd checksum */
+} sun_partition;
+
+
+#define SUN_LABEL_MAGIC          0xDABE
+#define SUN_LABEL_MAGIC_SWAPPED  0xBEDA
+#define sunlabel ((sun_partition *)MBRbuffer)
+#define SUN_SSWAP16(x) (sun_other_endian ? __swap16(x) \
+                                : (uint16_t)(x))
+#define SUN_SSWAP32(x) (sun_other_endian ? __swap32(x) \
+                                : (uint32_t)(x))
+
+
+#ifdef CONFIG_FEATURE_OSF_LABEL
+/*
+   Changes:
+   19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
+
+   20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
+   support for OSF/1 disklabels on Alpha.
+   Also fixed unaligned accesses in alpha_bootblock_checksum()
+*/
+
+#define FREEBSD_PARTITION       0xa5
+#define NETBSD_PARTITION        0xa9
+
+static void xbsd_delete_part (void);
+static void xbsd_new_part (void);
+static void xbsd_write_disklabel (void);
+static int xbsd_create_disklabel (void);
+static void xbsd_edit_disklabel (void);
+static void xbsd_write_bootstrap (void);
+static void xbsd_change_fstype (void);
+static int xbsd_get_part_index (int max);
+static int xbsd_check_new_partition (int *i);
+static void xbsd_list_types (void);
+static u_short xbsd_dkcksum (struct xbsd_disklabel *lp);
+static int xbsd_initlabel  (struct partition *p, struct xbsd_disklabel *d,
+                           int pindex);
+static int xbsd_readlabel  (struct partition *p, struct xbsd_disklabel *d);
+static int xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d);
+
+#if defined (__alpha__)
+static void alpha_bootblock_checksum (char *boot);
+#endif
+
+#if !defined (__alpha__)
+static int xbsd_translate_fstype (int linux_type);
+static void xbsd_link_part (void);
+static struct partition *xbsd_part;
+static int xbsd_part_index;
+#endif
+
+#if defined (__alpha__)
+/* We access this through a uint64_t * when checksumming */
+static char disklabelbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
+#else
+static char disklabelbuffer[BSD_BBSIZE];
+#endif
+
+static struct xbsd_disklabel xbsd_dlabel;
+
+#define bsd_cround(n) \
+       (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
+
+/*
+ * Test whether the whole disk has BSD disk label magic.
+ *
+ * Note: often reformatting with DOS-type label leaves the BSD magic,
+ * so this does not mean that there is a BSD disk label.
+ */
+static int
+check_osf_label(void) {
+       if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0)
+               return 0;
+       return 1;
+}
+
+static void xbsd_print_disklabel(int);
+
+static int
+btrydev (const char * dev) {
+       if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0)
+               return -1;
+       printf(_("\nBSD label for device: %s\n"), dev);
+       xbsd_print_disklabel (0);
+       return 0;
+}
+
+static void
+bmenu (void) {
+  puts (_("Command action"));
+  puts (_("\td\tdelete a BSD partition"));
+  puts (_("\te\tedit drive data"));
+  puts (_("\ti\tinstall bootstrap"));
+  puts (_("\tl\tlist known filesystem types"));
+  puts (_("\tm\tprint this menu"));
+  puts (_("\tn\tadd a new BSD partition"));
+  puts (_("\tp\tprint BSD partition table"));
+  puts (_("\tq\tquit without saving changes"));
+  puts (_("\tr\treturn to main menu"));
+  puts (_("\ts\tshow complete disklabel"));
+  puts (_("\tt\tchange a partition's filesystem id"));
+  puts (_("\tu\tchange units (cylinders/sectors)"));
+  puts (_("\tw\twrite disklabel to disk"));
+#if !defined (__alpha__)
+  puts (_("\tx\tlink BSD partition to non-BSD partition"));
+#endif
+}
+
+#if !defined (__alpha__)
+static int
+hidden(int type) {
+       return type ^ 0x10;
+}
+
+static int
+is_bsd_partition_type(int type) {
+       return (type == FREEBSD_PARTITION ||
+               type == hidden(FREEBSD_PARTITION) ||
+               type == NETBSD_PARTITION ||
+               type == hidden(NETBSD_PARTITION));
+}
+#endif
+
+static void
+bselect (void) {
+#if !defined (__alpha__)
+  int t, ss;
+  struct partition *p;
+
+  for (t=0; t<4; t++) {
+    p = get_part_table(t);
+    if (p && is_bsd_partition_type(p->sys_ind)) {
+      xbsd_part = p;
+      xbsd_part_index = t;
+      ss = get_start_sect(xbsd_part);
+      if (ss == 0) {
+       fprintf (stderr, _("Partition %s has invalid starting sector 0.\n"),
+                partname(disk_device, t+1, 0));
+       return;
+      }
+      printf (_("Reading disklabel of %s at sector %d.\n"),
+             partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR);
+      if (xbsd_readlabel (xbsd_part, &xbsd_dlabel) == 0)
+       if (xbsd_create_disklabel () == 0)
+         return;
+      break;
+    }
+  }
+
+  if (t == 4) {
+    printf (_("There is no *BSD partition on %s.\n"), disk_device);
+    return;
+  }
+
+#elif defined (__alpha__)
+
+  if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0)
+    if (xbsd_create_disklabel () == 0)
+      exit ( EXIT_SUCCESS );
+
+#endif
+
+  while (1) {
+    putchar ('\n');
+    switch (tolower (read_char (_("BSD disklabel command (m for help): ")))) {
+      case 'd':
+       xbsd_delete_part ();
+       break;
+      case 'e':
+       xbsd_edit_disklabel ();
+       break;
+      case 'i':
+       xbsd_write_bootstrap ();
+       break;
+      case 'l':
+       xbsd_list_types ();
+       break;
+      case 'n':
+       xbsd_new_part ();
+       break;
+      case 'p':
+       xbsd_print_disklabel (0);
+       break;
+      case 'q':
+       close (fd);
+       exit ( EXIT_SUCCESS );
+      case 'r':
+       return;
+      case 's':
+       xbsd_print_disklabel (1);
+       break;
+      case 't':
+       xbsd_change_fstype ();
+       break;
+      case 'u':
+       change_units();
+       break;
+      case 'w':
+       xbsd_write_disklabel ();
+       break;
+#if !defined (__alpha__)
+      case 'x':
+       xbsd_link_part ();
+       break;
+#endif
+      default:
+       bmenu ();
+       break;
+    }
+  }
+}
+
+static void
+xbsd_delete_part (void)
+{
+  int i;
+
+  i = xbsd_get_part_index (xbsd_dlabel.d_npartitions);
+  xbsd_dlabel.d_partitions[i].p_size   = 0;
+  xbsd_dlabel.d_partitions[i].p_offset = 0;
+  xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+  if (xbsd_dlabel.d_npartitions == i + 1)
+    while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0)
+      xbsd_dlabel.d_npartitions--;
+}
+
+static void
+xbsd_new_part (void)
+{
+  off_t begin, end;
+  char mesg[256];
+  int i;
+
+  if (!xbsd_check_new_partition (&i))
+    return;
+
+#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
+  begin = get_start_sect(xbsd_part);
+  end = begin + get_nr_sects(xbsd_part) - 1;
+#else
+  begin = 0;
+  end = xbsd_dlabel.d_secperunit - 1;
+#endif
+
+  snprintf (mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+  begin = read_int (bsd_cround (begin), bsd_cround (begin), bsd_cround (end),
+                   0, mesg);
+
+  if (display_in_cyl_units)
+    begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
+
+  snprintf (mesg, sizeof(mesg), _("Last %s or +size or +sizeM or +sizeK"),
+          str_units(SINGULAR));
+  end = read_int (bsd_cround (begin), bsd_cround (end), bsd_cround (end),
+                 bsd_cround (begin), mesg);
+
+  if (display_in_cyl_units)
+    end = end * xbsd_dlabel.d_secpercyl - 1;
+
+  xbsd_dlabel.d_partitions[i].p_size   = end - begin + 1;
+  xbsd_dlabel.d_partitions[i].p_offset = begin;
+  xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+}
+
+static void
+xbsd_print_disklabel (int show_all) {
+  struct xbsd_disklabel *lp = &xbsd_dlabel;
+  struct xbsd_partition *pp;
+  int i, j;
+
+  if (show_all) {
+#if defined (__alpha__)
+    printf("# %s:\n", disk_device);
+#else
+    printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0));
+#endif
+    if ((unsigned) lp->d_type < BSD_DKMAXTYPES)
+      printf(_("type: %s\n"), xbsd_dktypenames[lp->d_type]);
+    else
+      printf(_("type: %d\n"), lp->d_type);
+    printf(_("disk: %.*s\n"), (int) sizeof(lp->d_typename), lp->d_typename);
+    printf(_("label: %.*s\n"), (int) sizeof(lp->d_packname), lp->d_packname);
+    printf(_("flags:"));
+    if (lp->d_flags & BSD_D_REMOVABLE)
+      printf(_(" removable"));
+    if (lp->d_flags & BSD_D_ECC)
+      printf(_(" ecc"));
+    if (lp->d_flags & BSD_D_BADSECT)
+      printf(_(" badsect"));
+    printf("\n");
+    /* On various machines the fields of *lp are short/int/long */
+    /* In order to avoid problems, we cast them all to long. */
+    printf(_("bytes/sector: %ld\n"), (long) lp->d_secsize);
+    printf(_("sectors/track: %ld\n"), (long) lp->d_nsectors);
+    printf(_("tracks/cylinder: %ld\n"), (long) lp->d_ntracks);
+    printf(_("sectors/cylinder: %ld\n"), (long) lp->d_secpercyl);
+    printf(_("cylinders: %ld\n"), (long) lp->d_ncylinders);
+    printf(_("rpm: %d\n"), lp->d_rpm);
+    printf(_("interleave: %d\n"), lp->d_interleave);
+    printf(_("trackskew: %d\n"), lp->d_trackskew);
+    printf(_("cylinderskew: %d\n"), lp->d_cylskew);
+    printf(_("headswitch: %ld\t\t# milliseconds\n"),
+           (long) lp->d_headswitch);
+    printf(_("track-to-track seek: %ld\t# milliseconds\n"),
+           (long) lp->d_trkseek);
+    printf(_("drivedata: "));
+    for (i = NDDATA - 1; i >= 0; i--)
+      if (lp->d_drivedata[i])
+       break;
+    if (i < 0)
+      i = 0;
+    for (j = 0; j <= i; j++)
+      printf("%ld ", (long) lp->d_drivedata[j]);
+  }
+  printf(_("\n%d partitions:\n"), lp->d_npartitions);
+  printf(_("#       start       end      size     fstype   [fsize bsize   cpg]\n"));
+  pp = lp->d_partitions;
+  for (i = 0; i < lp->d_npartitions; i++, pp++) {
+    if (pp->p_size) {
+      if (display_in_cyl_units && lp->d_secpercyl) {
+       printf("  %c: %8ld%c %8ld%c %8ld%c  ",
+               'a' + i,
+               (long) pp->p_offset / lp->d_secpercyl + 1,
+               (pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
+               (long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1)
+                       / lp->d_secpercyl,
+               ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
+               (long) pp->p_size / lp->d_secpercyl,
+               (pp->p_size % lp->d_secpercyl) ? '*' : ' ');
+      } else {
+       printf("  %c: %8ld  %8ld  %8ld   ",
+               'a' + i,
+               (long) pp->p_offset,
+               (long) pp->p_offset + pp->p_size - 1,
+               (long) pp->p_size);
+      }
+      if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES)
+       printf("%8.8s", xbsd_fstypes[pp->p_fstype].name);
+      else
+       printf("%8x", pp->p_fstype);
+      switch (pp->p_fstype) {
+       case BSD_FS_UNUSED:
+         printf("    %5ld %5ld %5.5s ",
+                 (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
+         break;
+
+       case BSD_FS_BSDFFS:
+         printf("    %5ld %5ld %5d ",
+                 (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag,
+                 pp->p_cpg);
+         break;
+
+       default:
+         printf("%22.22s", "");
+         break;
+      }
+      printf("\n");
+    }
+  }
+}
+
+static void
+xbsd_write_disklabel (void) {
+#if defined (__alpha__)
+       printf (_("Writing disklabel to %s.\n"), disk_device);
+       xbsd_writelabel (NULL, &xbsd_dlabel);
+#else
+       printf (_("Writing disklabel to %s.\n"),
+               partname(disk_device, xbsd_part_index+1, 0));
+       xbsd_writelabel (xbsd_part, &xbsd_dlabel);
+#endif
+       reread_partition_table(0);      /* no exit yet */
+}
+
+static int
+xbsd_create_disklabel (void) {
+       char c;
+
+#if defined (__alpha__)
+       fprintf (stderr, _("%s contains no disklabel.\n"), disk_device);
+#else
+       fprintf (stderr, _("%s contains no disklabel.\n"),
+                partname(disk_device, xbsd_part_index+1, 0));
+#endif
+
+       while (1) {
+               c = read_char (_("Do you want to create a disklabel? (y/n) "));
+               if (c == 'y' || c == 'Y') {
+                       if (xbsd_initlabel (
+#if defined (__alpha__) || defined (__powerpc__) || defined (__hppa__) || \
+    defined (__s390__) || defined (__s390x__)
+                               NULL, &xbsd_dlabel, 0
+#else
+                               xbsd_part, &xbsd_dlabel, xbsd_part_index
+#endif
+                               ) == 1) {
+                               xbsd_print_disklabel (1);
+                               return 1;
+                       } else
+                               return 0;
+               } else if (c == 'n')
+                       return 0;
+       }
+}
+
+static int
+edit_int (int def, char *mesg)
+{
+  do {
+    fputs (mesg, stdout);
+    printf (" (%d): ", def);
+    if (!read_line ())
+      return def;
+  }
+  while (!isdigit (*line_ptr));
+  return atoi (line_ptr);
+}
+
+static void
+xbsd_edit_disklabel (void)
+{
+  struct xbsd_disklabel *d;
+
+  d = &xbsd_dlabel;
+
+#if defined (__alpha__) || defined (__ia64__)
+  d -> d_secsize    = (u_long) edit_int ((u_long) d -> d_secsize     ,_("bytes/sector"));
+  d -> d_nsectors   = (u_long) edit_int ((u_long) d -> d_nsectors    ,_("sectors/track"));
+  d -> d_ntracks    = (u_long) edit_int ((u_long) d -> d_ntracks     ,_("tracks/cylinder"));
+  d -> d_ncylinders = (u_long) edit_int ((u_long) d -> d_ncylinders  ,_("cylinders"));
+#endif
+
+  /* d -> d_secpercyl can be != d -> d_nsectors * d -> d_ntracks */
+  while (1)
+  {
+    d -> d_secpercyl = (u_long) edit_int ((u_long) d -> d_nsectors * d -> d_ntracks,
+                                         _("sectors/cylinder"));
+    if (d -> d_secpercyl <= d -> d_nsectors * d -> d_ntracks)
+      break;
+
+    printf (_("Must be <= sectors/track * tracks/cylinder (default).\n"));
+  }
+  d -> d_rpm        = (u_short) edit_int ((u_short) d -> d_rpm       ,_("rpm"));
+  d -> d_interleave = (u_short) edit_int ((u_short) d -> d_interleave,_("interleave"));
+  d -> d_trackskew  = (u_short) edit_int ((u_short) d -> d_trackskew ,_("trackskew"));
+  d -> d_cylskew    = (u_short) edit_int ((u_short) d -> d_cylskew   ,_("cylinderskew"));
+  d -> d_headswitch = (u_long) edit_int ((u_long) d -> d_headswitch  ,_("headswitch"));
+  d -> d_trkseek    = (u_long) edit_int ((u_long) d -> d_trkseek     ,_("track-to-track seek"));
+
+  d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+}
+
+static int
+xbsd_get_bootstrap (char *path, void *ptr, int size)
+{
+  int fdb;
+
+  if ((fdb = open (path, O_RDONLY)) < 0)
+  {
+    perror (path);
+    return 0;
+  }
+  if (read (fdb, ptr, size) < 0)
+  {
+    perror (path);
+    close (fdb);
+    return 0;
+  }
+  printf (" ... %s\n", path);
+  close (fdb);
+  return 1;
+}
+
+static void
+sync_disks (void)
+{
+  printf (_("\nSyncing disks.\n"));
+  sync ();
+  sleep (4);
+}
+
+static void
+xbsd_write_bootstrap (void)
+{
+  char *bootdir = BSD_LINUX_BOOTDIR;
+  char path[MAXPATHLEN];
+  char *dkbasename;
+  struct xbsd_disklabel dl;
+  char *d, *p, *e;
+  int sector;
+
+  if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
+    dkbasename = "sd";
+  else
+    dkbasename = "wd";
+
+  printf (_("Bootstrap: %sboot -> boot%s (%s): "),
+         dkbasename, dkbasename, dkbasename);
+  if (read_line ()) {
+    line_ptr[strlen (line_ptr)-1] = '\0';
+    dkbasename = line_ptr;
+  }
+  snprintf (path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
+  if (!xbsd_get_bootstrap (path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
+    return;
+
+  /* We need a backup of the disklabel (xbsd_dlabel might have changed). */
+  d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
+  bcopy (d, &dl, sizeof (struct xbsd_disklabel));
+
+  /* The disklabel will be overwritten by 0's from bootxx anyway */
+  bzero (d, sizeof (struct xbsd_disklabel));
+
+  snprintf (path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
+  if (!xbsd_get_bootstrap (path, &disklabelbuffer[xbsd_dlabel.d_secsize],
+                         (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
+    return;
+
+  e = d + sizeof (struct xbsd_disklabel);
+  for (p=d; p < e; p++)
+    if (*p) {
+      fprintf (stderr, _("Bootstrap overlaps with disk label!\n"));
+      exit ( EXIT_FAILURE );
+    }
+
+  bcopy (&dl, d, sizeof (struct xbsd_disklabel));
+
+#if defined (__powerpc__) || defined (__hppa__)
+  sector = 0;
+#elif defined (__alpha__)
+  sector = 0;
+  alpha_bootblock_checksum (disklabelbuffer);
+#else
+  sector = get_start_sect(xbsd_part);
+#endif
+
+  if (lseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1)
+    fdisk_fatal (unable_to_seek);
+  if (BSD_BBSIZE != write (fd, disklabelbuffer, BSD_BBSIZE))
+    fdisk_fatal (unable_to_write);
+
+#if defined (__alpha__)
+  printf (_("Bootstrap installed on %s.\n"), disk_device);
+#else
+  printf (_("Bootstrap installed on %s.\n"),
+    partname (disk_device, xbsd_part_index+1, 0));
+#endif
+
+  sync_disks ();
+}
+
+static void
+xbsd_change_fstype (void)
+{
+  int i;
+
+  i = xbsd_get_part_index (xbsd_dlabel.d_npartitions);
+  xbsd_dlabel.d_partitions[i].p_fstype = read_hex (xbsd_fstypes);
+}
+
+static int
+xbsd_get_part_index (int max)
+{
+  char prompt[256];
+  char l;
+
+  snprintf (prompt, sizeof(prompt), _("Partition (a-%c): "), 'a' + max - 1);
+  do
+     l = tolower (read_char (prompt));
+  while (l < 'a' || l > 'a' + max - 1);
+  return l - 'a';
+}
+
+static int
+xbsd_check_new_partition (int *i) {
+
+       /* room for more? various BSD flavours have different maxima */
+       if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
+               int t;
+
+               for (t = 0; t < BSD_MAXPARTITIONS; t++)
+                       if (xbsd_dlabel.d_partitions[t].p_size == 0)
+                               break;
+
+               if (t == BSD_MAXPARTITIONS) {
+                       fprintf (stderr, _("The maximum number of partitions "
+                                          "has been created\n"));
+                       return 0;
+               }
+       }
+
+       *i = xbsd_get_part_index (BSD_MAXPARTITIONS);
+
+       if (*i >= xbsd_dlabel.d_npartitions)
+               xbsd_dlabel.d_npartitions = (*i) + 1;
+
+       if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
+               fprintf (stderr, _("This partition already exists.\n"));
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+xbsd_list_types (void) {
+       list_types (xbsd_fstypes);
+}
+
+static u_short
+xbsd_dkcksum (struct xbsd_disklabel *lp) {
+       u_short *start, *end;
+       u_short sum = 0;
+
+       start = (u_short *) lp;
+       end = (u_short *) &lp->d_partitions[lp->d_npartitions];
+       while (start < end)
+               sum ^= *start++;
+       return sum;
+}
+
+static int
+xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d, int pindex) {
+       struct xbsd_partition *pp;
+
+       get_geometry ();
+       bzero (d, sizeof (struct xbsd_disklabel));
+
+       d -> d_magic = BSD_DISKMAGIC;
+
+       if (strncmp (disk_device, "/dev/sd", 7) == 0)
+               d -> d_type = BSD_DTYPE_SCSI;
+       else
+               d -> d_type = BSD_DTYPE_ST506;
+
+#if 0 /* not used (at least not written to disk) by NetBSD/i386 1.0 */
+       d -> d_subtype = BSD_DSTYPE_INDOSPART & pindex;
+#endif
+
+#if !defined (__alpha__)
+       d -> d_flags = BSD_D_DOSPART;
+#else
+       d -> d_flags = 0;
+#endif
+       d -> d_secsize = SECTOR_SIZE;           /* bytes/sector  */
+       d -> d_nsectors = sectors;            /* sectors/track */
+       d -> d_ntracks = heads;               /* tracks/cylinder (heads) */
+       d -> d_ncylinders = cylinders;
+       d -> d_secpercyl  = sectors * heads;/* sectors/cylinder */
+       if (d -> d_secpercyl == 0)
+               d -> d_secpercyl = 1;           /* avoid segfaults */
+       d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+
+       d -> d_rpm = 3600;
+       d -> d_interleave = 1;
+       d -> d_trackskew = 0;
+       d -> d_cylskew = 0;
+       d -> d_headswitch = 0;
+       d -> d_trkseek = 0;
+
+       d -> d_magic2 = BSD_DISKMAGIC;
+       d -> d_bbsize = BSD_BBSIZE;
+       d -> d_sbsize = BSD_SBSIZE;
+
+#if !defined (__alpha__)
+       d -> d_npartitions = 4;
+       pp = &d -> d_partitions[2];             /* Partition C should be
+                                                  the NetBSD partition */
+       pp -> p_offset = get_start_sect(p);
+       pp -> p_size   = get_nr_sects(p);
+       pp -> p_fstype = BSD_FS_UNUSED;
+       pp = &d -> d_partitions[3];             /* Partition D should be
+                                                  the whole disk */
+       pp -> p_offset = 0;
+       pp -> p_size   = d -> d_secperunit;
+       pp -> p_fstype = BSD_FS_UNUSED;
+#elif defined (__alpha__)
+       d -> d_npartitions = 3;
+       pp = &d -> d_partitions[2];             /* Partition C should be
+                                                  the whole disk */
+       pp -> p_offset = 0;
+       pp -> p_size   = d -> d_secperunit;
+       pp -> p_fstype = BSD_FS_UNUSED;
+#endif
+
+       return 1;
+}
+
+/*
+ * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 1.
+ */
+static int
+xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d)
+{
+       int t, sector;
+
+       /* p is used only to get the starting sector */
+#if !defined (__alpha__)
+       sector = (p ? get_start_sect(p) : 0);
+#elif defined (__alpha__)
+       sector = 0;
+#endif
+
+       if (lseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1)
+               fdisk_fatal (unable_to_seek);
+       if (BSD_BBSIZE != read (fd, disklabelbuffer, BSD_BBSIZE))
+               fdisk_fatal (unable_to_read);
+
+       bcopy (&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+              d, sizeof (struct xbsd_disklabel));
+
+       if (d -> d_magic != BSD_DISKMAGIC || d -> d_magic2 != BSD_DISKMAGIC)
+               return 0;
+
+       for (t = d -> d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+               d -> d_partitions[t].p_size   = 0;
+               d -> d_partitions[t].p_offset = 0;
+               d -> d_partitions[t].p_fstype = BSD_FS_UNUSED;
+       }
+
+       if (d -> d_npartitions > BSD_MAXPARTITIONS)
+               fprintf (stderr, _("Warning: too many partitions "
+                                  "(%d, maximum is %d).\n"),
+                        d -> d_npartitions, BSD_MAXPARTITIONS);
+       return 1;
+}
+
+static int
+xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d)
+{
+  unsigned int sector;
+
+#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
+  sector = get_start_sect(p) + BSD_LABELSECTOR;
+#else
+  sector = BSD_LABELSECTOR;
+#endif
+
+  d -> d_checksum = 0;
+  d -> d_checksum = xbsd_dkcksum (d);
+
+  /* This is necessary if we want to write the bootstrap later,
+     otherwise we'd write the old disklabel with the bootstrap.
+  */
+  bcopy (d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+        sizeof (struct xbsd_disklabel));
+
+#if defined (__alpha__) && BSD_LABELSECTOR == 0
+  alpha_bootblock_checksum (disklabelbuffer);
+  if (lseek (fd, 0, SEEK_SET) == -1)
+    fdisk_fatal (unable_to_seek);
+  if (BSD_BBSIZE != write (fd, disklabelbuffer, BSD_BBSIZE))
+    fdisk_fatal (unable_to_write);
+#else
+  if (lseek (fd, sector * SECTOR_SIZE + BSD_LABELOFFSET,
+                  SEEK_SET) == -1)
+    fdisk_fatal (unable_to_seek);
+  if (sizeof (struct xbsd_disklabel) != write (fd, d, sizeof (struct xbsd_disklabel)))
+    fdisk_fatal (unable_to_write);
+#endif
+
+  sync_disks ();
+
+  return 1;
+}
+
+
+#if !defined (__alpha__)
+static int
+xbsd_translate_fstype (int linux_type)
+{
+  switch (linux_type)
+  {
+    case 0x01: /* DOS 12-bit FAT   */
+    case 0x04: /* DOS 16-bit <32M  */
+    case 0x06: /* DOS 16-bit >=32M */
+    case 0xe1: /* DOS access       */
+    case 0xe3: /* DOS R/O          */
+    case 0xf2: /* DOS secondary    */
+      return BSD_FS_MSDOS;
+    case 0x07: /* OS/2 HPFS        */
+      return BSD_FS_HPFS;
+    default:
+      return BSD_FS_OTHER;
+  }
+}
+
+static void
+xbsd_link_part (void)
+{
+  int k, i;
+  struct partition *p;
+
+  k = get_partition (1, partitions);
+
+  if (!xbsd_check_new_partition (&i))
+    return;
+
+  p = get_part_table(k);
+
+  xbsd_dlabel.d_partitions[i].p_size   = get_nr_sects(p);
+  xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
+  xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
+}
+#endif
+
+#if defined (__alpha__)
+
+#if !defined(__GLIBC__)
+typedef unsigned long long uint64_t;
+#endif
+
+static void
+alpha_bootblock_checksum (char *boot)
+{
+  uint64_t *dp, sum;
+  int i;
+
+  dp = (uint64_t *)boot;
+  sum = 0;
+  for (i = 0; i < 63; i++)
+    sum += dp[i];
+  dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+#endif /* OSF_LABEL */
+
+#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL)
+static inline unsigned short
+__swap16(unsigned short x) {
+       return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8);
+}
+
+static inline uint32_t
+__swap32(uint32_t x) {
+        return (((x & 0xFF) << 24) |
+               ((x & 0xFF00) << 8) |
+               ((x & 0xFF0000) >> 8) |
+               ((x & 0xFF000000) >> 24));
+}
+#endif
+
+#ifdef CONFIG_FEATURE_SGI_LABEL
+/*
+ *
+ * fdisksgilabel.c
+ *
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *      This file may be modified and redistributed under
+ *      the terms of the GNU Public License.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *      Internationalization
+ */
+
+
+static  int     sgi_other_endian;
+static  int     debug;
+static  short   sgi_volumes=1;
+
+/*
+ * only dealing with free blocks here
+ */
+
+typedef struct { unsigned int first; unsigned int last; } freeblocks;
+static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */
+
+static void
+setfreelist(int i, unsigned int f, unsigned int l) {
+       freelist[i].first = f;
+       freelist[i].last = l;
+}
+
+static void
+add2freelist(unsigned int f, unsigned int l) {
+       int i = 0;
+       for ( ; i < 17 ; i++)
+               if (freelist[i].last == 0)
+                       break;
+       setfreelist(i, f, l);
+}
+
+static void
+clearfreelist(void) {
+       int i;
+
+       for (i = 0; i < 17 ; i++)
+               setfreelist(i, 0, 0);
+}
+
+static unsigned int
+isinfreelist(unsigned int b) {
+       int i;
+
+       for (i = 0; i < 17 ; i++)
+               if (freelist[i].first <= b && freelist[i].last >= b)
+                       return freelist[i].last;
+       return 0;
+}
+       /* return last vacant block of this stride (never 0). */
+       /* the '>=' is not quite correct, but simplifies the code */
+/*
+ * end of free blocks section
+ */
+
+static const struct systypes sgi_sys_types[] = {
+/* SGI_VOLHDR   */  {"\x00" "SGI volhdr"   },
+/* 0x01         */  {"\x01" "SGI trkrepl"  },
+/* 0x02         */  {"\x02" "SGI secrepl"  },
+/* SGI_SWAP     */  {"\x03" "SGI raw"      },
+/* 0x04         */  {"\x04" "SGI bsd"      },
+/* 0x05         */  {"\x05" "SGI sysv"     },
+/* ENTIRE_DISK  */  {"\x06" "SGI volume"   },
+/* SGI_EFS      */  {"\x07" "SGI efs"      },
+/* 0x08         */  {"\x08" "SGI lvol"     },
+/* 0x09         */  {"\x09" "SGI rlvol"    },
+/* SGI_XFS      */  {"\x0a" "SGI xfs"      },
+/* SGI_XFSLOG   */  {"\x0b" "SGI xfslog"   },
+/* SGI_XLV      */  {"\x0c" "SGI xlv"      },
+/* SGI_XVM      */  {"\x0d" "SGI xvm"      },
+/* LINUX_SWAP   */  {"\x82" "Linux swap"   },
+/* LINUX_NATIVE */  {"\x83" "Linux native" },
+/* LINUX_LVM    */  {"\x8d" "Linux LVM"    },
+/* LINUX_RAID   */  {"\xfd" "Linux RAID"   },
+                   { NULL             }
+};
+
+
+static int
+sgi_get_nsect(void) {
+    return SGI_SSWAP16(sgilabel->devparam.nsect);
+}
+
+static int
+sgi_get_ntrks(void) {
+    return SGI_SSWAP16(sgilabel->devparam.ntrks);
+}
+
+static void
+sgi_nolabel(void) {
+    sgilabel->magic = 0;
+    sgi_label = 0;
+    partitions = 4;
+}
+
+static unsigned int
+two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */) {
+    int i=0;
+    unsigned int sum=0;
+
+    size /= sizeof(unsigned int);
+    for (i = 0; i < size; i++)
+           sum -= SGI_SSWAP32(base[i]);
+    return sum;
+}
+
+static int
+check_sgi_label(void) {
+    if (sizeof(sgilabel) > 512) {
+           fprintf(stderr,
+                   _("According to MIPS Computer Systems, Inc the "
+                   "Label must not contain more than 512 bytes\n"));
+           exit(1);
+    }
+
+    if (sgilabel->magic != SGI_LABEL_MAGIC &&
+       sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED) {
+       sgi_label = 0;
+       sgi_other_endian = 0;
+       return 0;
+    }
+
+    sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED);
+    /*
+     * test for correct checksum
+     */
+    if (two_s_complement_32bit_sum((unsigned int*)sgilabel,
+                                      sizeof(*sgilabel))) {
+               fprintf(stderr,
+                       _("Detected sgi disklabel with wrong checksum.\n"));
+    }
+    update_units();
+    sgi_label = 1;
+    partitions= 16;
+    sgi_volumes = 15;
+    return 1;
+}
+
+static unsigned int
+sgi_get_start_sector(int i) {
+    return SGI_SSWAP32(sgilabel->partitions[i].start_sector);
+}
+
+static unsigned int
+sgi_get_num_sectors(int i) {
+    return SGI_SSWAP32(sgilabel->partitions[i].num_sectors);
+}
+
+static int
+sgi_get_sysid(int i)
+{
+    return SGI_SSWAP32(sgilabel->partitions[i].id);
+}
+
+static int
+sgi_get_bootpartition(void)
+{
+    return SGI_SSWAP16(sgilabel->boot_part);
+}
+
+static int
+sgi_get_swappartition(void)
+{
+    return SGI_SSWAP16(sgilabel->swap_part);
+}
+
+static void
+sgi_list_table(int xtra) {
+    int i, w, wd;
+    int kpi = 0;                /* kernel partition ID */
+
+    if(xtra) {
+       printf(_("\nDisk %s (SGI disk label): %d heads, %d sectors\n"
+              "%d cylinders, %d physical cylinders\n"
+              "%d extra sects/cyl, interleave %d:1\n"
+              "%s\n"
+              "Units = %s of %d * 512 bytes\n\n"),
+              disk_device, heads, sectors, cylinders,
+              SGI_SSWAP16(sgiparam.pcylcount),
+              SGI_SSWAP16(sgiparam.sparecyl),
+              SGI_SSWAP16(sgiparam.ilfact),
+              (char *)sgilabel,
+              str_units(PLURAL), units_per_sector);
+    } else {
+       printf( _("\nDisk %s (SGI disk label): "
+               "%d heads, %d sectors, %d cylinders\n"
+               "Units = %s of %d * 512 bytes\n\n"),
+               disk_device, heads, sectors, cylinders,
+               str_units(PLURAL), units_per_sector );
+    }
+
+    w = strlen(disk_device);
+    wd = strlen(_("Device"));
+    if (w < wd)
+       w = wd;
+
+    printf(_("----- partitions -----\n"
+          "Pt# %*s  Info     Start       End   Sectors  Id  System\n"),
+           w + 2, _("Device"));
+    for (i = 0 ; i < partitions; i++) {
+           if( sgi_get_num_sectors(i) || debug ) {
+           uint32_t start = sgi_get_start_sector(i);
+           uint32_t len = sgi_get_num_sectors(i);
+           kpi++;              /* only count nonempty partitions */
+           printf(
+               "%2d: %s %4s %9ld %9ld %9ld  %2x  %s\n",
+/* fdisk part number */   i+1,
+/* device */              partname(disk_device, kpi, w+3),
+/* flags */               (sgi_get_swappartition() == i) ? "swap" :
+/* flags */               (sgi_get_bootpartition() == i) ? "boot" : "    ",
+/* start */               (long) scround(start),
+/* end */                 (long) scround(start+len)-1,
+/* no odd flag on end */  (long) len,
+/* type id */             sgi_get_sysid(i),
+/* type name */           partition_type(sgi_get_sysid(i)));
+       }
+    }
+    printf(_("----- Bootinfo -----\nBootfile: %s\n"
+            "----- Directory Entries -----\n"),
+              sgilabel->boot_file);
+       for (i = 0 ; i < sgi_volumes; i++) {
+           if (sgilabel->directory[i].vol_file_size) {
+           uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start);
+           uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size);
+           char*name = sgilabel->directory[i].vol_file_name;
+
+           printf(_("%2d: %-10s sector%5u size%8u\n"),
+                   i, name, (unsigned int) start, (unsigned int) len);
+       }
+    }
+}
+
+static void
+sgi_set_bootpartition( int i )
+{
+    sgilabel->boot_part = SGI_SSWAP16(((short)i));
+}
+
+static unsigned int
+sgi_get_lastblock(void) {
+    return heads * sectors * cylinders;
+}
+
+static void
+sgi_set_swappartition( int i ) {
+    sgilabel->swap_part = SGI_SSWAP16(((short)i));
+}
+
+static int
+sgi_check_bootfile(const char* aFile) {
+
+       if (strlen(aFile) < 3) /* "/a\n" is minimum */ {
+               printf(_("\nInvalid Bootfile!\n"
+               "\tThe bootfile must be an absolute non-zero pathname,\n"
+                        "\te.g. \"/unix\" or \"/unix.save\".\n"));
+       return 0;
+       } else {
+               if (strlen(aFile) > 16) {
+                       printf(_("\n\tName of Bootfile too long:  "
+                                "16 bytes maximum.\n"));
+       return 0;
+               } else {
+                       if (aFile[0] != '/') {
+                               printf(_("\n\tBootfile must have a "
+                                        "fully qualified pathname.\n"));
+       return 0;
+    }
+               }
+       }
+       if (strncmp(aFile, sgilabel->boot_file, 16)) {
+               printf(_("\n\tBe aware, that the bootfile is not checked for existence.\n\t"
+                        "SGI's default is \"/unix\" and for backup \"/unix.save\".\n"));
+       /* filename is correct and did change */
+       return 1;
+    }
+    return 0;   /* filename did not change */
+}
+
+static const char *
+sgi_get_bootfile(void) {
+       return sgilabel->boot_file;
+}
+
+static void
+sgi_set_bootfile(const char* aFile) {
+    int i = 0;
+
+    if (sgi_check_bootfile(aFile)) {
+       while (i < 16) {
+           if ((aFile[i] != '\n')  /* in principle caught again by next line */
+                           &&  (strlen(aFile) > i))
+               sgilabel->boot_file[i] = aFile[i];
+           else
+               sgilabel->boot_file[i] = 0;
+           i++;
+       }
+       printf(_("\n\tBootfile is changed to \"%s\".\n"), sgilabel->boot_file);
+    }
+}
+
+static void
+create_sgiinfo(void)
+{
+    /* I keep SGI's habit to write the sgilabel to the second block */
+    sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2);
+    sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo));
+    strncpy(sgilabel->directory[0].vol_file_name, "sgilabel", 8);
+}
+
+static sgiinfo *fill_sgiinfo(void);
+
+static void
+sgi_write_table(void) {
+    sgilabel->csum = 0;
+     sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum(
+                                (unsigned int*)sgilabel,
+                                       sizeof(*sgilabel)));
+     assert(two_s_complement_32bit_sum(
+               (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
+     if (lseek(fd, 0, SEEK_SET) < 0)
+       fdisk_fatal(unable_to_seek);
+     if (write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE)
+       fdisk_fatal(unable_to_write);
+     if (! strncmp(sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
+       /*
+        * keep this habit of first writing the "sgilabel".
+        * I never tested whether it works without (AN 981002).
+        */
+        sgiinfo *info = fill_sgiinfo();
+        int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start);
+        if (lseek(fd, infostartblock*SECTOR_SIZE, SEEK_SET) < 0)
+           fdisk_fatal(unable_to_seek);
+        if (write(fd, info, SECTOR_SIZE) != SECTOR_SIZE)
+           fdisk_fatal(unable_to_write);
+        free(info);
+    }
+}
+
+static int
+compare_start(int *x, int *y) {
+    /*
+     * sort according to start sectors
+     * and prefers largest partition:
+     * entry zero is entire disk entry
+     */
+    unsigned int i = *x;
+    unsigned int j = *y;
+    unsigned int a = sgi_get_start_sector(i);
+    unsigned int b = sgi_get_start_sector(j);
+    unsigned int c = sgi_get_num_sectors(i);
+    unsigned int d = sgi_get_num_sectors(j);
+
+    if (a == b)
+       return (d > c) ? 1 : (d == c) ? 0 : -1;
+    return (a > b) ? 1 : -1;
+}
+
+
+static int
+verify_sgi(int verbose)
+{
+    int Index[16];      /* list of valid partitions */
+    int sortcount = 0;  /* number of used partitions, i.e. non-zero lengths */
+    int entire = 0, i = 0;
+    unsigned int start = 0;
+    long long gap = 0;      /* count unused blocks */
+    unsigned int lastblock = sgi_get_lastblock();
+
+    clearfreelist();
+    for (i=0; i<16; i++) {
+       if (sgi_get_num_sectors(i) != 0) {
+           Index[sortcount++]=i;
+           if (sgi_get_sysid(i) == ENTIRE_DISK) {
+               if (entire++ == 1) {
+                   if (verbose)
+                       printf(_("More than one entire disk entry present.\n"));
+               }
+           }
+       }
+    }
+    if (sortcount == 0) {
+       if (verbose)
+           printf(_("No partitions defined\n"));
+              return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+    }
+    qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start);
+    if (sgi_get_sysid(Index[0]) == ENTIRE_DISK) {
+       if ((Index[0] != 10) && verbose)
+           printf(_("IRIX likes when Partition 11 covers the entire disk.\n"));
+           if ((sgi_get_start_sector(Index[0]) != 0) && verbose)
+               printf(_("The entire disk partition should start "
+                               "at block 0,\n"
+                               "not at diskblock %d.\n"),
+                             sgi_get_start_sector(Index[0]));
+               if (debug)      /* I do not understand how some disks fulfil it */
+                      if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose)
+                              printf(_("The entire disk partition is only %d diskblock large,\n"
+                   "but the disk is %d diskblocks long.\n"),
+                                     sgi_get_num_sectors(Index[0]), lastblock);
+               lastblock = sgi_get_num_sectors(Index[0]);
+    } else {
+           if (verbose)
+               printf(_("One Partition (#11) should cover the entire disk.\n"));
+           if (debug>2)
+               printf("sysid=%d\tpartition=%d\n",
+                             sgi_get_sysid(Index[0]), Index[0]+1);
+    }
+    for (i=1, start=0; i<sortcount; i++) {
+       int cylsize = sgi_get_nsect() * sgi_get_ntrks();
+
+           if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) {
+               if (debug)      /* I do not understand how some disks fulfil it */
+                   if (verbose)
+                       printf(_("Partition %d does not start on cylinder boundary.\n"),
+                                             Index[i]+1);
+           }
+           if (sgi_get_num_sectors(Index[i]) % cylsize != 0) {
+               if (debug)      /* I do not understand how some disks fulfil it */
+                   if (verbose)
+                       printf(_("Partition %d does not end on cylinder boundary.\n"),
+                                             Index[i]+1);
+       }
+       /* We cannot handle several "entire disk" entries. */
+           if (sgi_get_sysid(Index[i]) == ENTIRE_DISK) continue;
+           if (start > sgi_get_start_sector(Index[i])) {
+               if (verbose)
+                   printf(_("The Partition %d and %d overlap by %d sectors.\n"),
+                       Index[i-1]+1, Index[i]+1,
+                               start - sgi_get_start_sector(Index[i]));
+               if (gap >  0) gap = -gap;
+               if (gap == 0) gap = -1;
+           }
+           if (start < sgi_get_start_sector(Index[i])) {
+               if (verbose)
+                   printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"),
+                               sgi_get_start_sector(Index[i]) - start,
+                               start, sgi_get_start_sector(Index[i])-1);
+               gap += sgi_get_start_sector(Index[i]) - start;
+               add2freelist(start, sgi_get_start_sector(Index[i]));
+           }
+           start = sgi_get_start_sector(Index[i])
+                      + sgi_get_num_sectors(Index[i]);
+           if (debug > 1) {
+               if (verbose)
+                   printf("%2d:%12d\t%12d\t%12d\n", Index[i],
+                       sgi_get_start_sector(Index[i]),
+                       sgi_get_num_sectors(Index[i]),
+                               sgi_get_sysid(Index[i]));
+       }
+    }
+    if (start < lastblock) {
+       if (verbose)
+               printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"),
+                           lastblock - start, start, lastblock-1);
+       gap += lastblock - start;
+       add2freelist(start, lastblock);
+    }
+    /*
+     * Done with arithmetics
+     * Go for details now
+     */
+    if (verbose) {
+       if (!sgi_get_num_sectors(sgi_get_bootpartition())) {
+           printf(_("\nThe boot partition does not exist.\n"));
+       }
+       if (!sgi_get_num_sectors(sgi_get_swappartition())) {
+           printf(_("\nThe swap partition does not exist.\n"));
+       } else {
+           if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP)
+                       &&  (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP))
+               printf(_("\nThe swap partition has no swap type.\n"));
+       }
+       if (sgi_check_bootfile("/unix"))
+           printf(_("\tYou have chosen an unusual boot file name.\n"));
+    }
+    return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int
+sgi_gaps(void) {
+    /*
+     * returned value is:
+     *  = 0 : disk is properly filled to the rim
+     *  < 0 : there is an overlap
+     *  > 0 : there is still some vacant space
+     */
+    return verify_sgi(0);
+}
+
+static void
+sgi_change_sysid( int i, int sys )
+{
+    if( sgi_get_num_sectors(i) == 0 ) /* caught already before, ... */
+    {
+       printf(_("Sorry You may change the Tag of non-empty partitions.\n"));
+       return;
+    }
+    if( ((sys != ENTIRE_DISK ) && (sys != SGI_VOLHDR))
+     && (sgi_get_start_sector(i)<1) )
+    {
+       read_chars(
+       _("It is highly recommended that the partition at offset 0\n"
+       "is of type \"SGI volhdr\", the IRIX system will rely on it to\n"
+       "retrieve from its directory standalone tools like sash and fx.\n"
+       "Only the \"SGI volume\" entire disk section may violate this.\n"
+       "Type YES if you are sure about tagging this partition differently.\n"));
+       if (strcmp (line_ptr, _("YES\n")))
+                   return;
+    }
+    sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+}
+
+/* returns partition index of first entry marked as entire disk */
+static int
+sgi_entire(void) {
+    int i;
+
+    for(i=0; i<16; i++)
+       if(sgi_get_sysid(i) == SGI_VOLUME)
+           return i;
+    return -1;
+}
+
+static void
+sgi_set_partition(int i, unsigned int start, unsigned int length, int sys) {
+
+    sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+    sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length);
+    sgilabel->partitions[i].start_sector = SGI_SSWAP32(start);
+    set_changed(i);
+    if (sgi_gaps() < 0)     /* rebuild freelist */
+       printf(_("Do You know, You got a partition overlap on the disk?\n"));
+}
+
+static void
+sgi_set_entire(void) {
+    int n;
+
+    for(n=10; n < partitions; n++) {
+       if(!sgi_get_num_sectors(n) ) {
+           sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME);
+           break;
+       }
+    }
+}
+
+static void
+sgi_set_volhdr(void)
+{
+    int n;
+    for( n=8; n<partitions; n++ )
+    {
+       if(!sgi_get_num_sectors( n ) )
+       {
+           /*
+            * 5 cylinders is an arbitrary value I like
+            * IRIX 5.3 stored files in the volume header
+            * (like sash, symmon, fx, ide) with ca. 3200
+            * sectors.
+            */
+           if( heads * sectors * 5 < sgi_get_lastblock() )
+               sgi_set_partition( n, 0, heads * sectors * 5, SGI_VOLHDR );
+           break;
+       }
+    }
+}
+
+static void
+sgi_delete_partition( int i )
+{
+    sgi_set_partition( i, 0, 0, 0 );
+}
+
+static void
+sgi_add_partition( int n, int sys )
+{
+    char mesg[256];
+    unsigned int first=0, last=0;
+
+    if( n == 10 ) {
+       sys = SGI_VOLUME;
+    } else if ( n == 8 ) {
+       sys = 0;
+    }
+    if(sgi_get_num_sectors(n)) {
+       printf(_("Partition %d is already defined.  Delete "
+               "it before re-adding it.\n"), n + 1);
+       return;
+    }
+    if( (sgi_entire() == -1) &&  (sys != SGI_VOLUME) ) {
+       printf(_("Attempting to generate entire disk entry automatically.\n"));
+       sgi_set_entire();
+       sgi_set_volhdr();
+    }
+    if( (sgi_gaps() == 0) &&  (sys != SGI_VOLUME) ) {
+       printf(_("The entire disk is already covered with partitions.\n"));
+       return;
+    }
+    if(sgi_gaps() < 0) {
+       printf(_("You got a partition overlap on the disk. Fix it first!\n"));
+       return;
+    }
+    snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+    for(;;) {
+       if(sys == SGI_VOLUME) {
+           last = sgi_get_lastblock();
+           first = read_int(0, 0, last-1, 0, mesg);
+           if( first != 0 ) {
+               printf(_("It is highly recommended that eleventh partition\n"
+                      "covers the entire disk and is of type `SGI volume'\n"));
+           }
+       } else {
+           first = freelist[0].first;
+           last  = freelist[0].last;
+           first = read_int(scround(first), scround(first), scround(last)-1,
+                            0, mesg);
+       }
+       if (display_in_cyl_units)
+           first *= units_per_sector;
+       else
+           first = first; /* align to cylinder if you know how ... */
+       if( !last )
+           last = isinfreelist(first);
+       if( last == 0 ) {
+           printf(_("You will get a partition overlap on the disk. "
+                   "Fix it first!\n"));
+       } else
+           break;
+    }
+    snprintf(mesg, sizeof(mesg), _(" Last %s"), str_units(SINGULAR));
+    last = read_int(scround(first), scround(last)-1, scround(last)-1,
+                   scround(first), mesg)+1;
+    if (display_in_cyl_units)
+       last *= units_per_sector;
+    else
+       last = last; /* align to cylinder if You know how ... */
+    if( (sys == SGI_VOLUME) && ( first != 0 || last != sgi_get_lastblock() ) )
+       printf(_("It is highly recommended that eleventh partition\n"
+               "covers the entire disk and is of type `SGI volume'\n"));
+    sgi_set_partition( n, first, last-first, sys );
+}
+
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+static void
+create_sgilabel(void)
+{
+    struct hd_geometry geometry;
+    struct {
+       unsigned int start;
+       unsigned int nsect;
+       int sysid;
+    } old[4];
+    int i=0;
+    long longsectors;               /* the number of sectors on the device */
+    int res;                        /* the result from the ioctl */
+    int sec_fac;                    /* the sector factor */
+
+    sec_fac = sector_size / 512;    /* determine the sector factor */
+
+    fprintf( stderr,
+       _("Building a new SGI disklabel. Changes will remain in memory only,\n"
+       "until you decide to write them. After that, of course, the previous\n"
+       "content will be unrecoverably lost.\n\n"));
+
+    sgi_other_endian = (BYTE_ORDER == LITTLE_ENDIAN);
+    res = ioctl(fd, BLKGETSIZE, &longsectors);
+    if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+       heads = geometry.heads;
+       sectors = geometry.sectors;
+       if (res == 0) {
+           /* the get device size ioctl was successful */
+           cylinders = longsectors / (heads * sectors);
+           cylinders /= sec_fac;
+       } else {
+           /* otherwise print error and use truncated version */
+       cylinders = geometry.cylinders;
+           fprintf(stderr,
+                  _("Warning:  BLKGETSIZE ioctl failed on %s.  "
+                    "Using geometry cylinder value of %d.\n"
+                    "This value may be truncated for devices"
+                    " > 33.8 GB.\n"), disk_device, cylinders);
+    }
+    }
+    for (i = 0; i < 4; i++) {
+       old[i].sysid = 0;
+       if(valid_part_table_flag(MBRbuffer)) {
+           if(get_part_table(i)->sys_ind) {
+               old[i].sysid = get_part_table(i)->sys_ind;
+               old[i].start = get_start_sect(get_part_table(i));
+               old[i].nsect = get_nr_sects(get_part_table(i));
+               printf(_("Trying to keep parameters of partition %d.\n"), i);
+               if (debug)
+                   printf(_("ID=%02x\tSTART=%d\tLENGTH=%d\n"),
+                       old[i].sysid, old[i].start, old[i].nsect);
+           }
+       }
+    }
+
+    memset(MBRbuffer, 0, sizeof(MBRbuffer));
+    sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC);
+    sgilabel->boot_part = SGI_SSWAP16(0);
+    sgilabel->swap_part = SGI_SSWAP16(1);
+
+    /* sizeof(sgilabel->boot_file) = 16 > 6 */
+    memset(sgilabel->boot_file, 0, 16);
+    strcpy(sgilabel->boot_file, "/unix");
+
+    sgilabel->devparam.skew                     = (0);
+    sgilabel->devparam.gap1                     = (0);
+    sgilabel->devparam.gap2                     = (0);
+    sgilabel->devparam.sparecyl                 = (0);
+    sgilabel->devparam.pcylcount                = SGI_SSWAP16(geometry.cylinders);
+    sgilabel->devparam.head_vol0                = SGI_SSWAP16(0);
+    sgilabel->devparam.ntrks                    = SGI_SSWAP16(geometry.heads);
+                                               /* tracks/cylinder (heads) */
+    sgilabel->devparam.cmd_tag_queue_depth      = (0);
+    sgilabel->devparam.unused0                  = (0);
+    sgilabel->devparam.unused1                  = SGI_SSWAP16(0);
+    sgilabel->devparam.nsect                    = SGI_SSWAP16(geometry.sectors);
+                                               /* sectors/track */
+    sgilabel->devparam.bytes                    = SGI_SSWAP16(512);
+    sgilabel->devparam.ilfact                   = SGI_SSWAP16(1);
+    sgilabel->devparam.flags                    = SGI_SSWAP32(TRACK_FWD|
+                                                       IGNORE_ERRORS|RESEEK);
+    sgilabel->devparam.datarate                 = SGI_SSWAP32(0);
+    sgilabel->devparam.retries_on_error         = SGI_SSWAP32(1);
+    sgilabel->devparam.ms_per_word              = SGI_SSWAP32(0);
+    sgilabel->devparam.xylogics_gap1            = SGI_SSWAP16(0);
+    sgilabel->devparam.xylogics_syncdelay       = SGI_SSWAP16(0);
+    sgilabel->devparam.xylogics_readdelay       = SGI_SSWAP16(0);
+    sgilabel->devparam.xylogics_gap2            = SGI_SSWAP16(0);
+    sgilabel->devparam.xylogics_readgate        = SGI_SSWAP16(0);
+    sgilabel->devparam.xylogics_writecont       = SGI_SSWAP16(0);
+    memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 );
+    memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partition)*16 );
+    sgi_label  =  1;
+    partitions = 16;
+    sgi_volumes    = 15;
+    sgi_set_entire();
+    sgi_set_volhdr();
+    for (i = 0; i < 4; i++) {
+       if(old[i].sysid) {
+           sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid);
+       }
+    }
+}
+
+static void
+sgi_set_xcyl(void)
+{
+    /* do nothing in the beginning */
+}
+#endif /* CONFIG_FEATURE_FDISK_ADVANCED */
+
+/* _____________________________________________________________
+ */
+
+static sgiinfo *
+fill_sgiinfo(void)
+{
+    sgiinfo *info = calloc(1, sizeof(sgiinfo));
+
+    info->magic=SGI_SSWAP32(SGI_INFO_MAGIC);
+    info->b1=SGI_SSWAP32(-1);
+    info->b2=SGI_SSWAP16(-1);
+    info->b3=SGI_SSWAP16(1);
+    /* You may want to replace this string !!!!!!! */
+    strcpy( info->scsi_string, "IBM OEM 0662S12         3 30" );
+    strcpy( info->serial, "0000" );
+    info->check1816 = SGI_SSWAP16(18*256 +16 );
+    strcpy( info->installer, "Sfx version 5.3, Oct 18, 1994" );
+    return info;
+}
+#endif /* SGI_LABEL */
+
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+/*
+ * fdisksunlabel.c
+ *
+ * I think this is mostly, or entirely, due to
+ *      Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *
+ * Merged with fdisk for other architectures, aeb, June 1998.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *      Internationalization
+ */
+
+
+static int     sun_other_endian;
+static int     scsi_disk;
+static int     floppy;
+
+#ifndef IDE0_MAJOR
+#define IDE0_MAJOR 3
+#endif
+#ifndef IDE1_MAJOR
+#define IDE1_MAJOR 22
+#endif
+
+static void guess_device_type(void) {
+       struct stat bootstat;
+
+       if (fstat (fd, &bootstat) < 0) {
+               scsi_disk = 0;
+               floppy = 0;
+       } else if (S_ISBLK(bootstat.st_mode)
+                  && (major(bootstat.st_rdev) == IDE0_MAJOR ||
+                      major(bootstat.st_rdev) == IDE1_MAJOR)) {
+               scsi_disk = 0;
+               floppy = 0;
+       } else if (S_ISBLK(bootstat.st_mode)
+                  && major(bootstat.st_rdev) == FLOPPY_MAJOR) {
+               scsi_disk = 0;
+               floppy = 1;
+       } else {
+               scsi_disk = 1;
+               floppy = 0;
+       }
+}
+
+static const struct systypes sun_sys_types[] = {
+/* 0            */  {"\x00" "Empty"        },
+/* 1            */  {"\x01" "Boot"         },
+/* 2            */  {"\x02" "SunOS root"   },
+/* SUNOS_SWAP   */  {"\x03" "SunOS swap"   },
+/* 4            */  {"\x04" "SunOS usr"    },
+/* WHOLE_DISK   */  {"\x05" "Whole disk"   },
+/* 6            */  {"\x06" "SunOS stand"  },
+/* 7            */  {"\x07" "SunOS var"    },
+/* 8            */  {"\x08" "SunOS home"   },
+/* LINUX_SWAP   */  {"\x82" "Linux swap"   },
+/* LINUX_NATIVE */  {"\x83" "Linux native" },
+/* 0x8e         */  {"\x8e" "Linux LVM"    },
+/* New (2.2.x) raid partition with autodetect using persistent superblock */
+/* 0xfd         */  {"\xfd" "Linux raid autodetect" },
+                   { NULL }
+};
+
+
+static void
+set_sun_partition(int i, uint start, uint stop, int sysid) {
+       sunlabel->infos[i].id = sysid;
+       sunlabel->partitions[i].start_cylinder =
+               SUN_SSWAP32(start / (heads * sectors));
+       sunlabel->partitions[i].num_sectors =
+               SUN_SSWAP32(stop - start);
+       set_changed(i);
+}
+
+static void
+sun_nolabel(void) {
+       sun_label = 0;
+       sunlabel->magic = 0;
+       partitions = 4;
+}
+
+static int
+check_sun_label(void) {
+       unsigned short *ush;
+       int csum;
+
+       if (sunlabel->magic != SUN_LABEL_MAGIC &&
+           sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
+               sun_label = 0;
+               sun_other_endian = 0;
+               return 0;
+       }
+       sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
+       ush = ((unsigned short *) (sunlabel + 1)) - 1;
+       for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--;
+       if (csum) {
+               fprintf(stderr,_("Detected sun disklabel with wrong checksum.\n"
+                               "Probably you'll have to set all the values,\n"
+                               "e.g. heads, sectors, cylinders and partitions\n"
+                               "or force a fresh label (s command in main menu)\n"));
+       } else {
+               heads = SUN_SSWAP16(sunlabel->ntrks);
+               cylinders = SUN_SSWAP16(sunlabel->ncyl);
+               sectors = SUN_SSWAP16(sunlabel->nsect);
+       }
+       update_units();
+       sun_label = 1;
+       partitions = 8;
+       return 1;
+}
+
+static const struct sun_predefined_drives {
+       const char *vendor;
+       const char *model;
+       unsigned short sparecyl;
+       unsigned short ncyl;
+       unsigned short nacyl;
+       unsigned short pcylcount;
+       unsigned short ntrks;
+       unsigned short nsect;
+       unsigned short rspeed;
+} sun_drives[] = {
+{"Quantum","ProDrive 80S",1,832,2,834,6,34,3662},
+{"Quantum","ProDrive 105S",1,974,2,1019,6,35,3662},
+{"CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600},
+{"IBM","DPES-31080",0,4901,2,4903,4,108,5400},
+{"IBM","DORS-32160",0,1015,2,1017,67,62,5400},
+{"IBM","DNES-318350",0,11199,2,11474,10,320,7200},
+{"SEAGATE","ST34371",0,3880,2,3882,16,135,7228},
+{"","SUN0104",1,974,2,1019,6,35,3662},
+{"","SUN0207",4,1254,2,1272,9,36,3600},
+{"","SUN0327",3,1545,2,1549,9,46,3600},
+{"","SUN0340",0,1538,2,1544,6,72,4200},
+{"","SUN0424",2,1151,2,2500,9,80,4400},
+{"","SUN0535",0,1866,2,2500,7,80,5400},
+{"","SUN0669",5,1614,2,1632,15,54,3600},
+{"","SUN1.0G",5,1703,2,1931,15,80,3597},
+{"","SUN1.05",0,2036,2,2038,14,72,5400},
+{"","SUN1.3G",6,1965,2,3500,17,80,5400},
+{"","SUN2.1G",0,2733,2,3500,19,80,5400},
+{"IOMEGA","Jaz",0,1019,2,1021,64,32,5394},
+};
+
+static const struct sun_predefined_drives *
+sun_autoconfigure_scsi(void) {
+    const struct sun_predefined_drives *p = NULL;
+
+#ifdef SCSI_IOCTL_GET_IDLUN
+    unsigned int id[2];
+    char buffer[2048];
+    char buffer2[2048];
+    FILE *pfd;
+    char *vendor;
+    char *model;
+    char *q;
+    int i;
+
+    if (!ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id)) {
+       sprintf(buffer,
+           "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
+#if 0
+           ((id[0]>>24)&0xff)-/*PROC_SCSI_SCSI+PROC_SCSI_FILE*/33,
+#else
+           /* This is very wrong (works only if you have one HBA),
+              but I haven't found a way how to get hostno
+              from the current kernel */
+           0,
+#endif
+           (id[0]>>16)&0xff,
+           id[0]&0xff,
+           (id[0]>>8)&0xff);
+       pfd = fopen("/proc/scsi/scsi","r");
+       if (pfd) {
+           while (fgets(buffer2,2048,pfd)) {
+               if (!strcmp(buffer, buffer2)) {
+                   if (fgets(buffer2,2048,pfd)) {
+                       q = strstr(buffer2,"Vendor: ");
+                       if (q) {
+                           q += 8;
+                           vendor = q;
+                           q = strstr(q," ");
+                           *q++ = 0;   /* truncate vendor name */
+                           q = strstr(q,"Model: ");
+                           if (q) {
+                               *q = 0;
+                               q += 7;
+                               model = q;
+                               q = strstr(q," Rev: ");
+                               if (q) {
+                                   *q = 0;
+                                   for (i = 0; i < SIZE(sun_drives); i++) {
+                                       if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor))
+                                           continue;
+                                       if (!strstr(model, sun_drives[i].model))
+                                           continue;
+                                       printf(_("Autoconfigure found a %s%s%s\n"),sun_drives[i].vendor,(*sun_drives[i].vendor) ? " " : "",sun_drives[i].model);
+                                       p = sun_drives + i;
+                                       break;
+                                   }
+                               }
+                           }
+                       }
+                   }
+                   break;
+               }
+           }
+           fclose(pfd);
+       }
+    }
+#endif
+    return p;
+}
+
+static void create_sunlabel(void)
+{
+       struct hd_geometry geometry;
+       unsigned int ndiv;
+       int i;
+       unsigned char c;
+       const struct sun_predefined_drives *p = NULL;
+
+       fprintf(stderr,
+       _("Building a new sun disklabel. Changes will remain in memory only,\n"
+       "until you decide to write them. After that, of course, the previous\n"
+       "content won't be recoverable.\n\n"));
+#if BYTE_ORDER == LITTLE_ENDIAN
+       sun_other_endian = 1;
+#else
+       sun_other_endian = 0;
+#endif
+       memset(MBRbuffer, 0, sizeof(MBRbuffer));
+       sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC);
+       if (!floppy) {
+           puts(_("Drive type\n"
+                "   ?   auto configure\n"
+                "   0   custom (with hardware detected defaults)"));
+           for (i = 0; i < SIZE(sun_drives); i++) {
+               printf("   %c   %s%s%s\n",
+                      i + 'a', sun_drives[i].vendor,
+                      (*sun_drives[i].vendor) ? " " : "",
+                      sun_drives[i].model);
+           }
+           for (;;) {
+               c = read_char(_("Select type (? for auto, 0 for custom): "));
+               if (c >= 'a' && c < 'a' + SIZE(sun_drives)) {
+                   p = sun_drives + c - 'a';
+                   break;
+               } else if (c >= 'A' && c < 'A' + SIZE(sun_drives)) {
+                   p = sun_drives + c - 'A';
+                   break;
+               } else if (c == '0') {
+                   break;
+               } else if (c == '?' && scsi_disk) {
+                   p = sun_autoconfigure_scsi();
+                   if (!p)
+                       printf(_("Autoconfigure failed.\n"));
+                   else
+                       break;
+               }
+           }
+       }
+       if (!p || floppy) {
+           if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+               heads = geometry.heads;
+               sectors = geometry.sectors;
+               cylinders = geometry.cylinders;
+           } else {
+               heads = 0;
+               sectors = 0;
+               cylinders = 0;
+           }
+           if (floppy) {
+               sunlabel->nacyl = 0;
+               sunlabel->pcylcount = SUN_SSWAP16(cylinders);
+               sunlabel->rspeed = SUN_SSWAP16(300);
+               sunlabel->ilfact = SUN_SSWAP16(1);
+               sunlabel->sparecyl = 0;
+           } else {
+               heads = read_int(1,heads,1024,0,_("Heads"));
+               sectors = read_int(1,sectors,1024,0,_("Sectors/track"));
+               if (cylinders)
+                   cylinders = read_int(1,cylinders-2,65535,0,_("Cylinders"));
+               else
+                   cylinders = read_int(1,0,65535,0,_("Cylinders"));
+               sunlabel->nacyl =
+                       SUN_SSWAP16(read_int(0,2,65535,0,
+                                        _("Alternate cylinders")));
+               sunlabel->pcylcount =
+                       SUN_SSWAP16(read_int(0,cylinders+SUN_SSWAP16(sunlabel->nacyl),
+                                        65535,0,_("Physical cylinders")));
+               sunlabel->rspeed =
+                       SUN_SSWAP16(read_int(1,5400,100000,0,
+                                        _("Rotation speed (rpm)")));
+               sunlabel->ilfact =
+                       SUN_SSWAP16(read_int(1,1,32,0,_("Interleave factor")));
+               sunlabel->sparecyl =
+                       SUN_SSWAP16(read_int(0,0,sectors,0,
+                                        _("Extra sectors per cylinder")));
+           }
+       } else {
+           sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl);
+           sunlabel->ncyl = SUN_SSWAP16(p->ncyl);
+           sunlabel->nacyl = SUN_SSWAP16(p->nacyl);
+           sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount);
+           sunlabel->ntrks = SUN_SSWAP16(p->ntrks);
+           sunlabel->nsect = SUN_SSWAP16(p->nsect);
+           sunlabel->rspeed = SUN_SSWAP16(p->rspeed);
+           sunlabel->ilfact = SUN_SSWAP16(1);
+           cylinders = p->ncyl;
+           heads = p->ntrks;
+           sectors = p->nsect;
+           puts(_("You may change all the disk params from the x menu"));
+       }
+
+       snprintf(sunlabel->info, sizeof(sunlabel->info),
+                "%s%s%s cyl %d alt %d hd %d sec %d",
+                p ? p->vendor : "", (p && *p->vendor) ? " " : "",
+                p ? p->model
+                  : (floppy ? _("3,5\" floppy") : _("Linux custom")),
+               cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors);
+
+       sunlabel->ntrks = SUN_SSWAP16(heads);
+       sunlabel->nsect = SUN_SSWAP16(sectors);
+       sunlabel->ncyl = SUN_SSWAP16(cylinders);
+       if (floppy)
+           set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE);
+       else {
+           if (cylinders * heads * sectors >= 150 * 2048) {
+               ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */
+           } else
+               ndiv = cylinders * 2 / 3;
+           set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE);
+           set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP);
+           sunlabel->infos[1].flags |= 0x01; /* Not mountable */
+       }
+       set_sun_partition(2, 0, cylinders * heads * sectors, WHOLE_DISK);
+       {
+               unsigned short *ush = (unsigned short *)sunlabel;
+               unsigned short csum = 0;
+               while(ush < (unsigned short *)(&sunlabel->csum))
+                       csum ^= *ush++;
+               sunlabel->csum = csum;
+       }
+
+       set_all_unchanged();
+       set_changed(0);
+       get_boot(create_empty_sun);
+}
+
+static void
+toggle_sunflags(int i, unsigned char mask) {
+       if (sunlabel->infos[i].flags & mask)
+               sunlabel->infos[i].flags &= ~mask;
+       else sunlabel->infos[i].flags |= mask;
+       set_changed(i);
+}
+
+static void
+fetch_sun(uint *starts, uint *lens, uint *start, uint *stop) {
+       int i, continuous = 1;
+       *start = 0; *stop = cylinders * heads * sectors;
+       for (i = 0; i < partitions; i++) {
+               if (sunlabel->partitions[i].num_sectors
+                   && sunlabel->infos[i].id
+                   && sunlabel->infos[i].id != WHOLE_DISK) {
+                       starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
+                       lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
+                       if (continuous) {
+                               if (starts[i] == *start)
+                                       *start += lens[i];
+                               else if (starts[i] + lens[i] >= *stop)
+                                       *stop = starts[i];
+                               else
+                                       continuous = 0;
+                                       /* There will be probably more gaps
+                                         than one, so lets check afterwards */
+                       }
+               } else {
+                       starts[i] = 0;
+                       lens[i] = 0;
+               }
+       }
+}
+
+static uint *verify_sun_starts;
+
+static int
+verify_sun_cmp(int *a, int *b) {
+    if (*a == -1) return 1;
+    if (*b == -1) return -1;
+    if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1;
+    return -1;
+}
+
+static void
+verify_sun(void) {
+    uint starts[8], lens[8], start, stop;
+    int i,j,k,starto,endo;
+    int array[8];
+
+    verify_sun_starts = starts;
+    fetch_sun(starts,lens,&start,&stop);
+    for (k = 0; k < 7; k++) {
+       for (i = 0; i < 8; i++) {
+           if (k && (lens[i] % (heads * sectors))) {
+               printf(_("Partition %d doesn't end on cylinder boundary\n"), i+1);
+           }
+           if (lens[i]) {
+               for (j = 0; j < i; j++)
+                   if (lens[j]) {
+                       if (starts[j] == starts[i]+lens[i]) {
+                           starts[j] = starts[i]; lens[j] += lens[i];
+                           lens[i] = 0;
+                       } else if (starts[i] == starts[j]+lens[j]){
+                           lens[j] += lens[i];
+                           lens[i] = 0;
+                       } else if (!k) {
+                           if (starts[i] < starts[j]+lens[j] &&
+                               starts[j] < starts[i]+lens[i]) {
+                               starto = starts[i];
+                               if (starts[j] > starto)
+                                       starto = starts[j];
+                               endo = starts[i]+lens[i];
+                               if (starts[j]+lens[j] < endo)
+                                       endo = starts[j]+lens[j];
+                               printf(_("Partition %d overlaps with others in "
+                                      "sectors %d-%d\n"), i+1, starto, endo);
+                           }
+                       }
+                   }
+           }
+       }
+    }
+    for (i = 0; i < 8; i++) {
+       if (lens[i])
+           array[i] = i;
+       else
+           array[i] = -1;
+    }
+    qsort(array,SIZE(array),sizeof(array[0]),
+         (int (*)(const void *,const void *)) verify_sun_cmp);
+    if (array[0] == -1) {
+       printf(_("No partitions defined\n"));
+       return;
+    }
+    stop = cylinders * heads * sectors;
+    if (starts[array[0]])
+       printf(_("Unused gap - sectors 0-%d\n"),starts[array[0]]);
+    for (i = 0; i < 7 && array[i+1] != -1; i++) {
+       printf(_("Unused gap - sectors %d-%d\n"),starts[array[i]]+lens[array[i]],starts[array[i+1]]);
+    }
+    start = starts[array[i]]+lens[array[i]];
+    if (start < stop)
+       printf(_("Unused gap - sectors %d-%d\n"),start,stop);
+}
+
+static void
+add_sun_partition(int n, int sys) {
+       uint start, stop, stop2;
+       uint starts[8], lens[8];
+       int whole_disk = 0;
+
+       char mesg[256];
+       int i, first, last;
+
+       if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) {
+               printf(_("Partition %d is already defined.  Delete "
+                       "it before re-adding it.\n"), n + 1);
+               return;
+       }
+
+       fetch_sun(starts,lens,&start,&stop);
+       if (stop <= start) {
+               if (n == 2)
+                       whole_disk = 1;
+               else {
+                       printf(_("Other partitions already cover the whole disk.\nDelete "
+                              "some/shrink them before retry.\n"));
+                       return;
+               }
+       }
+       snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+       for (;;) {
+               if (whole_disk)
+                       first = read_int(0, 0, 0, 0, mesg);
+               else
+                       first = read_int(scround(start), scround(stop)+1,
+                                        scround(stop), 0, mesg);
+               if (display_in_cyl_units)
+                       first *= units_per_sector;
+               else
+                       /* Starting sector has to be properly aligned */
+                       first = (first + heads * sectors - 1) / (heads * sectors);
+               if (n == 2 && first != 0)
+                       printf ("\
+It is highly recommended that the third partition covers the whole disk\n\
+and is of type `Whole disk'\n");
+               /* ewt asks to add: "don't start a partition at cyl 0"
+                  However, edmundo@rano.demon.co.uk writes:
+                  "In addition to having a Sun partition table, to be able to
+                  boot from the disc, the first partition, /dev/sdX1, must
+                  start at cylinder 0. This means that /dev/sdX1 contains
+                  the partition table and the boot block, as these are the
+                  first two sectors of the disc. Therefore you must be
+                  careful what you use /dev/sdX1 for. In particular, you must
+                  not use a partition starting at cylinder 0 for Linux swap,
+                  as that would overwrite the partition table and the boot
+                  block. You may, however, use such a partition for a UFS
+                  or EXT2 file system, as these file systems leave the first
+                  1024 bytes undisturbed. */
+               /* On the other hand, one should not use partitions
+                  starting at block 0 in an md, or the label will
+                  be trashed. */
+               for (i = 0; i < partitions; i++)
+                       if (lens[i] && starts[i] <= first
+                                   && starts[i] + lens[i] > first)
+                               break;
+               if (i < partitions && !whole_disk) {
+                       if (n == 2 && !first) {
+                           whole_disk = 1;
+                           break;
+                       }
+                       printf(_("Sector %d is already allocated\n"), first);
+               } else
+                       break;
+       }
+       stop = cylinders * heads * sectors;
+       stop2 = stop;
+       for (i = 0; i < partitions; i++) {
+               if (starts[i] > first && starts[i] < stop)
+                       stop = starts[i];
+       }
+       snprintf(mesg, sizeof(mesg),
+                _("Last %s or +size or +sizeM or +sizeK"),
+                str_units(SINGULAR));
+       if (whole_disk)
+               last = read_int(scround(stop2), scround(stop2), scround(stop2),
+                               0, mesg);
+       else if (n == 2 && !first)
+               last = read_int(scround(first), scround(stop2), scround(stop2),
+                               scround(first), mesg);
+       else
+               last = read_int(scround(first), scround(stop), scround(stop),
+                               scround(first), mesg);
+       if (display_in_cyl_units)
+               last *= units_per_sector;
+       if (n == 2 && !first) {
+               if (last >= stop2) {
+                   whole_disk = 1;
+                   last = stop2;
+               } else if (last > stop) {
+                   printf (
+   _("You haven't covered the whole disk with the 3rd partition, but your value\n"
+     "%d %s covers some other partition. Your entry has been changed\n"
+     "to %d %s\n"),
+                       scround(last), str_units(SINGULAR),
+                       scround(stop), str_units(SINGULAR));
+                   last = stop;
+               }
+       } else if (!whole_disk && last > stop)
+               last = stop;
+
+       if (whole_disk) sys = WHOLE_DISK;
+       set_sun_partition(n, first, last, sys);
+}
+
+static void
+sun_delete_partition(int i) {
+       unsigned int nsec;
+
+       if (i == 2 && sunlabel->infos[i].id == WHOLE_DISK &&
+           !sunlabel->partitions[i].start_cylinder &&
+           (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors))
+             == heads * sectors * cylinders)
+               printf(_("If you want to maintain SunOS/Solaris compatibility, "
+                      "consider leaving this\n"
+                      "partition as Whole disk (5), starting at 0, with %u "
+                      "sectors\n"), nsec);
+       sunlabel->infos[i].id = 0;
+       sunlabel->partitions[i].num_sectors = 0;
+}
+
+static void
+sun_change_sysid(int i, int sys) {
+       if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) {
+           read_chars(
+             _("It is highly recommended that the partition at offset 0\n"
+             "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+             "there may destroy your partition table and bootblock.\n"
+             "Type YES if you're very sure you would like that partition\n"
+             "tagged with 82 (Linux swap): "));
+           if (strcmp (line_ptr, _("YES\n")))
+                   return;
+       }
+       switch (sys) {
+       case SUNOS_SWAP:
+       case LINUX_SWAP:
+               /* swaps are not mountable by default */
+               sunlabel->infos[i].flags |= 0x01;
+               break;
+       default:
+               /* assume other types are mountable;
+                  user can change it anyway */
+               sunlabel->infos[i].flags &= ~0x01;
+               break;
+       }
+       sunlabel->infos[i].id = sys;
+}
+
+static void
+sun_list_table(int xtra) {
+       int i, w;
+
+       w = strlen(disk_device);
+       if (xtra)
+               printf(
+               _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n"
+               "%d cylinders, %d alternate cylinders, %d physical cylinders\n"
+               "%d extra sects/cyl, interleave %d:1\n"
+               "%s\n"
+               "Units = %s of %d * 512 bytes\n\n"),
+                      disk_device, heads, sectors, SUN_SSWAP16(sunlabel->rspeed),
+                      cylinders, SUN_SSWAP16(sunlabel->nacyl),
+                      SUN_SSWAP16(sunlabel->pcylcount),
+                      SUN_SSWAP16(sunlabel->sparecyl),
+                      SUN_SSWAP16(sunlabel->ilfact),
+                      (char *)sunlabel,
+                      str_units(PLURAL), units_per_sector);
+       else
+               printf(
+       _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n"
+       "Units = %s of %d * 512 bytes\n\n"),
+                      disk_device, heads, sectors, cylinders,
+                      str_units(PLURAL), units_per_sector);
+
+       printf(_("%*s Flag    Start       End    Blocks   Id  System\n"),
+              w + 1, _("Device"));
+       for (i = 0 ; i < partitions; i++) {
+               if (sunlabel->partitions[i].num_sectors) {
+                       uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
+                       uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
+                       printf(
+                           "%s %c%c %9ld %9ld %9ld%c  %2x  %s\n",
+/* device */              partname(disk_device, i+1, w),
+/* flags */               (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ',
+                         (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
+/* start */               (long) scround(start),
+/* end */                 (long) scround(start+len),
+/* odd flag on end */     (long) len / 2, len & 1 ? '+' : ' ',
+/* type id */             sunlabel->infos[i].id,
+/* type name */           partition_type(sunlabel->infos[i].id));
+               }
+       }
+}
+
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+
+static void
+sun_set_alt_cyl(void) {
+       sunlabel->nacyl =
+               SUN_SSWAP16(read_int(0,SUN_SSWAP16(sunlabel->nacyl), 65535, 0,
+                                _("Number of alternate cylinders")));
+}
+
+static void
+sun_set_ncyl(int cyl) {
+       sunlabel->ncyl = SUN_SSWAP16(cyl);
+}
+
+static void
+sun_set_xcyl(void) {
+       sunlabel->sparecyl =
+               SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), sectors, 0,
+                                _("Extra sectors per cylinder")));
+}
+
+static void
+sun_set_ilfact(void) {
+       sunlabel->ilfact =
+               SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0,
+                                _("Interleave factor")));
+}
+
+static void
+sun_set_rspeed(void) {
+       sunlabel->rspeed =
+               SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0,
+                                _("Rotation speed (rpm)")));
+}
+
+static void
+sun_set_pcylcount(void) {
+       sunlabel->pcylcount =
+               SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0,
+                                _("Number of physical cylinders")));
+}
+#endif /* CONFIG_FEATURE_FDISK_ADVANCED */
+
+static void
+sun_write_table(void) {
+       unsigned short *ush = (unsigned short *)sunlabel;
+       unsigned short csum = 0;
+
+       while(ush < (unsigned short *)(&sunlabel->csum))
+               csum ^= *ush++;
+       sunlabel->csum = csum;
+       if (lseek(fd, 0, SEEK_SET) < 0)
+               fdisk_fatal(unable_to_seek);
+       if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE)
+               fdisk_fatal(unable_to_write);
+}
+#endif /* SUN_LABEL */
+
+/* DOS partition types */
+
+static const struct systypes i386_sys_types[] = {
+       {"\x00" "Empty"},
+       {"\x01" "FAT12"},
+       {"\x04" "FAT16 <32M"},
+       {"\x05" "Extended"},         /* DOS 3.3+ extended partition */
+       {"\x06" "FAT16"},            /* DOS 16-bit >=32M */
+       {"\x07" "HPFS/NTFS"},        /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+       {"\x0a" "OS/2 Boot Manager"},/* OS/2 Boot Manager */
+       {"\x0b" "Win95 FAT32"},
+       {"\x0c" "Win95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */
+       {"\x0e" "Win95 FAT16 (LBA)"},
+       {"\x0f" "Win95 Ext'd (LBA)"},
+       {"\x11" "Hidden FAT12"},
+       {"\x12" "Compaq diagnostics"},
+       {"\x14" "Hidden FAT16 <32M"},
+       {"\x16" "Hidden FAT16"},
+       {"\x17" "Hidden HPFS/NTFS"},
+       {"\x1b" "Hidden Win95 FAT32"},
+       {"\x1c" "Hidden Win95 FAT32 (LBA)"},
+       {"\x1e" "Hidden Win95 FAT16 (LBA)"},
+       {"\x3c" "PartitionMagic recovery"},
+       {"\x41" "PPC PReP Boot"},
+       {"\x42" "SFS"},
+       {"\x63" "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+       {"\x80" "Old Minix"},        /* Minix 1.4a and earlier */
+       {"\x81" "Minix / old Linux"},/* Minix 1.4b and later */
+       {"\x82" "Linux swap"},       /* also Solaris */
+       {"\x83" "Linux"},
+       {"\x84" "OS/2 hidden C: drive"},
+       {"\x85" "Linux extended"},
+       {"\x86" "NTFS volume set"},
+       {"\x87" "NTFS volume set"},
+       {"\x8e" "Linux LVM"},
+       {"\x9f" "BSD/OS"},           /* BSDI */
+       {"\xa0" "IBM Thinkpad hibernation"},
+       {"\xa5" "FreeBSD"},          /* various BSD flavours */
+       {"\xa6" "OpenBSD"},
+       {"\xa8" "Darwin UFS"},
+       {"\xa9" "NetBSD"},
+       {"\xab" "Darwin boot"},
+       {"\xb7" "BSDI fs"},
+       {"\xb8" "BSDI swap"},
+       {"\xbe" "Solaris boot"},
+       {"\xeb" "BeOS fs"},
+       {"\xee" "EFI GPT"},          /* Intel EFI GUID Partition Table */
+       {"\xef" "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */
+       {"\xf0" "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */
+       {"\xf2" "DOS secondary"},    /* DOS 3.3+ secondary */
+       {"\xfd" "Linux raid autodetect"},/* New (2.2.x) raid partition with
+                                           autodetect using persistent
+                                           superblock */
+#ifdef CONFIG_WEIRD_PARTITION_TYPES
+       {"\x02" "XENIX root"},
+       {"\x03" "XENIX usr"},
+       {"\x08" "AIX"},              /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+       {"\x09" "AIX bootable"},     /* AIX data or Coherent */
+       {"\x10" "OPUS"},
+       {"\x18" "AST SmartSleep"},
+       {"\x24" "NEC DOS"},
+       {"\x39" "Plan 9"},
+       {"\x40" "Venix 80286"},
+       {"\x4d" "QNX4.x"},
+       {"\x4e" "QNX4.x 2nd part"},
+       {"\x4f" "QNX4.x 3rd part"},
+       {"\x50" "OnTrack DM"},
+       {"\x51" "OnTrack DM6 Aux1"}, /* (or Novell) */
+       {"\x52" "CP/M"},             /* CP/M or Microport SysV/AT */
+       {"\x53" "OnTrack DM6 Aux3"},
+       {"\x54" "OnTrackDM6"},
+       {"\x55" "EZ-Drive"},
+       {"\x56" "Golden Bow"},
+       {"\x5c" "Priam Edisk"},
+       {"\x61" "SpeedStor"},
+       {"\x64" "Novell Netware 286"},
+       {"\x65" "Novell Netware 386"},
+       {"\x70" "DiskSecure Multi-Boot"},
+       {"\x75" "PC/IX"},
+       {"\x93" "Amoeba"},
+       {"\x94" "Amoeba BBT"},       /* (bad block table) */
+       {"\xa7" "NeXTSTEP"},
+       {"\xbb" "Boot Wizard hidden"},
+       {"\xc1" "DRDOS/sec (FAT-12)"},
+       {"\xc4" "DRDOS/sec (FAT-16 < 32M)"},
+       {"\xc6" "DRDOS/sec (FAT-16)"},
+       {"\xc7" "Syrinx"},
+       {"\xda" "Non-FS data"},
+       {"\xdb" "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or
+                                       Concurrent DOS or CTOS */
+       {"\xde" "Dell Utility"},     /* Dell PowerEdge Server utilities */
+       {"\xdf" "BootIt"},           /* BootIt EMBRM */
+       {"\xe1" "DOS access"},       /* DOS access or SpeedStor 12-bit FAT
+                                       extended partition */
+       {"\xe3" "DOS R/O"},          /* DOS R/O or SpeedStor */
+       {"\xe4" "SpeedStor"},        /* SpeedStor 16-bit FAT extended
+                                       partition < 1024 cyl. */
+       {"\xf1" "SpeedStor"},
+       {"\xf4" "SpeedStor"},        /* SpeedStor large partition */
+       {"\xfe" "LANstep"},          /* SpeedStor >1024 cyl. or LANstep */
+       {"\xff" "BBT"},              /* Xenix Bad Block Table */
+#endif
+       { 0 }
+};
+
+
+
+/* A valid partition table sector ends in 0x55 0xaa */
+static unsigned int
+part_table_flag(const char *b) {
+       return ((uint) b[510]) + (((uint) b[511]) << 8);
+}
+
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+write_part_table_flag(char *b) {
+       b[510] = 0x55;
+       b[511] = 0xaa;
+}
+
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static void
+store4_little_endian(unsigned char *cp, unsigned int val) {
+       cp[0] = (val & 0xff);
+       cp[1] = ((val >> 8) & 0xff);
+       cp[2] = ((val >> 16) & 0xff);
+       cp[3] = ((val >> 24) & 0xff);
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+static unsigned int
+read4_little_endian(const unsigned char *cp) {
+       return (uint)(cp[0]) + ((uint)(cp[1]) << 8)
+               + ((uint)(cp[2]) << 16) + ((uint)(cp[3]) << 24);
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+set_start_sect(struct partition *p, unsigned int start_sect) {
+       store4_little_endian(p->start4, start_sect);
+}
+#endif
+
+static int32_t
+get_start_sect(const struct partition *p) {
+       return read4_little_endian(p->start4);
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+set_nr_sects(struct partition *p, int32_t nr_sects) {
+       store4_little_endian(p->size4, nr_sects);
+}
+#endif
+
+static int32_t
+get_nr_sects(const struct partition *p) {
+       return read4_little_endian(p->size4);
+}
+
+/* normally O_RDWR, -l option gives O_RDONLY */
+static int type_open = O_RDWR;
+
+
+static int ext_index,               /* the prime extended partition */
+       listing,                    /* no aborts for fdisk -l */
+       dos_compatible_flag = ~0;
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static int dos_changed;
+static int nowarn;            /* no warnings for fdisk -l/-s */
+#endif
+
+
+
+static uint    user_cylinders, user_heads, user_sectors;
+static uint    pt_heads, pt_sectors;
+static uint    kern_heads, kern_sectors;
+
+static off_t extended_offset;            /* offset of link pointers */
+
+static unsigned long long total_number_of_sectors;
+
+
+static jmp_buf listingbuf;
+
+static void fdisk_fatal(enum failure why) {
+       const char *message;
+
+       if (listing) {
+               close(fd);
+               longjmp(listingbuf, 1);
+       }
+
+       switch (why) {
+               case unable_to_open:
+                       message = "Unable to open %s\n";
+                       break;
+               case unable_to_read:
+                       message = "Unable to read %s\n";
+                       break;
+               case unable_to_seek:
+                       message = "Unable to seek on %s\n";
+                       break;
+               case unable_to_write:
+                       message = "Unable to write %s\n";
+                       break;
+               case ioctl_error:
+                       message = "BLKGETSIZE ioctl failed on %s\n";
+                       break;
+               default:
+                       message = "Fatal error\n";
+       }
+
+       fputc('\n', stderr);
+       fprintf(stderr, message, disk_device);
+       exit(1);
+}
+
+static void
+seek_sector(off_t secno) {
+       off_t offset = secno * sector_size;
+       if (lseek(fd, offset, SEEK_SET) == (off_t) -1)
+               fdisk_fatal(unable_to_seek);
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+write_sector(off_t secno, char *buf) {
+       seek_sector(secno);
+       if (write(fd, buf, sector_size) != sector_size)
+               fdisk_fatal(unable_to_write);
+}
+#endif
+
+/* Allocate a buffer and read a partition table sector */
+static void
+read_pte(struct pte *pe, off_t offset) {
+
+       pe->offset = offset;
+       pe->sectorbuffer = (char *) xmalloc(sector_size);
+       seek_sector(offset);
+       if (read(fd, pe->sectorbuffer, sector_size) != sector_size)
+               fdisk_fatal(unable_to_read);
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       pe->changed = 0;
+#endif
+       pe->part_table = pe->ext_pointer = NULL;
+}
+
+static unsigned int
+get_partition_start(const struct pte *pe) {
+       return pe->offset + get_start_sect(pe->part_table);
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+/*
+ * Avoid warning about DOS partitions when no DOS partition was changed.
+ * Here a heuristic "is probably dos partition".
+ * We might also do the opposite and warn in all cases except
+ * for "is probably nondos partition".
+ */
+static int
+is_dos_partition(int t) {
+       return (t == 1 || t == 4 || t == 6 ||
+               t == 0x0b || t == 0x0c || t == 0x0e ||
+               t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
+               t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
+               t == 0xc1 || t == 0xc4 || t == 0xc6);
+}
+
+static void
+menu(void) {
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (sun_label) {
+          puts(_("Command action"));
+          puts(_("\ta\ttoggle a read only flag"));           /* sun */
+          puts(_("\tb\tedit bsd disklabel"));
+          puts(_("\tc\ttoggle the mountable flag"));         /* sun */
+          puts(_("\td\tdelete a partition"));
+          puts(_("\tl\tlist known partition types"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tn\tadd a new partition"));
+          puts(_("\to\tcreate a new empty DOS partition table"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\ts\tcreate a new empty Sun disklabel"));  /* sun */
+          puts(_("\tt\tchange a partition's system id"));
+          puts(_("\tu\tchange display/entry units"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+          puts(_("\tx\textra functionality (experts only)"));
+#endif
+       } else
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (sgi_label) {
+          puts(_("Command action"));
+          puts(_("\ta\tselect bootable partition"));    /* sgi flavour */
+          puts(_("\tb\tedit bootfile entry"));          /* sgi */
+          puts(_("\tc\tselect sgi swap partition"));    /* sgi flavour */
+          puts(_("\td\tdelete a partition"));
+          puts(_("\tl\tlist known partition types"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tn\tadd a new partition"));
+          puts(_("\to\tcreate a new empty DOS partition table"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\ts\tcreate a new empty Sun disklabel"));  /* sun */
+          puts(_("\tt\tchange a partition's system id"));
+          puts(_("\tu\tchange display/entry units"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+       } else
+#endif
+#ifdef CONFIG_FEATURE_AIX_LABEL
+       if (aix_label) {
+          puts(_("Command action"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\to\tcreate a new empty DOS partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\ts\tcreate a new empty Sun disklabel"));  /* sun */
+       } else
+#endif
+       {
+          puts(_("Command action"));
+          puts(_("\ta\ttoggle a bootable flag"));
+          puts(_("\tb\tedit bsd disklabel"));
+          puts(_("\tc\ttoggle the dos compatibility flag"));
+          puts(_("\td\tdelete a partition"));
+          puts(_("\tl\tlist known partition types"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tn\tadd a new partition"));
+          puts(_("\to\tcreate a new empty DOS partition table"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\ts\tcreate a new empty Sun disklabel"));  /* sun */
+          puts(_("\tt\tchange a partition's system id"));
+          puts(_("\tu\tchange display/entry units"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+          puts(_("\tx\textra functionality (experts only)"));
+#endif
+       }
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+static void
+xmenu(void) {
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (sun_label) {
+          puts(_("Command action"));
+          puts(_("\ta\tchange number of alternate cylinders"));      /*sun*/
+          puts(_("\tc\tchange number of cylinders"));
+          puts(_("\td\tprint the raw data in the partition table"));
+          puts(_("\te\tchange number of extra sectors per cylinder"));/*sun*/
+          puts(_("\th\tchange number of heads"));
+          puts(_("\ti\tchange interleave factor"));                  /*sun*/
+          puts(_("\to\tchange rotation speed (rpm)"));               /*sun*/
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\tr\treturn to main menu"));
+          puts(_("\ts\tchange number of sectors/track"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+          puts(_("\ty\tchange number of physical cylinders"));       /*sun*/
+       }  else
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (sgi_label) {
+          puts(_("Command action"));
+          puts(_("\tb\tmove beginning of data in a partition")); /* !sun */
+          puts(_("\tc\tchange number of cylinders"));
+          puts(_("\td\tprint the raw data in the partition table"));
+          puts(_("\te\tlist extended partitions"));          /* !sun */
+          puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */
+          puts(_("\th\tchange number of heads"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\tr\treturn to main menu"));
+          puts(_("\ts\tchange number of sectors/track"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+       } else
+#endif
+#ifdef CONFIG_FEATURE_AIX_LABEL
+       if (aix_label) {
+          puts(_("Command action"));
+          puts(_("\tb\tmove beginning of data in a partition")); /* !sun */
+          puts(_("\tc\tchange number of cylinders"));
+          puts(_("\td\tprint the raw data in the partition table"));
+          puts(_("\te\tlist extended partitions"));          /* !sun */
+          puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */
+          puts(_("\th\tchange number of heads"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\tr\treturn to main menu"));
+          puts(_("\ts\tchange number of sectors/track"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+       }  else
+#endif
+       {
+          puts(_("Command action"));
+          puts(_("\tb\tmove beginning of data in a partition")); /* !sun */
+          puts(_("\tc\tchange number of cylinders"));
+          puts(_("\td\tprint the raw data in the partition table"));
+          puts(_("\te\tlist extended partitions"));          /* !sun */
+          puts(_("\tf\tfix partition order"));               /* !sun, !aix, !sgi */
+#ifdef CONFIG_FEATURE_SGI_LABEL
+          puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */
+#endif
+          puts(_("\th\tchange number of heads"));
+          puts(_("\tm\tprint this menu"));
+          puts(_("\tp\tprint the partition table"));
+          puts(_("\tq\tquit without saving changes"));
+          puts(_("\tr\treturn to main menu"));
+          puts(_("\ts\tchange number of sectors/track"));
+          puts(_("\tv\tverify the partition table"));
+          puts(_("\tw\twrite table to disk and exit"));
+       }
+}
+#endif /* ADVANCED mode */
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static const struct systypes *
+get_sys_types(void) {
+       return (
+#ifdef CONFIG_FEATURE_SUN_LABEL
+               sun_label ? sun_sys_types :
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+               sgi_label ? sgi_sys_types :
+#endif
+               i386_sys_types);
+}
+#else
+#define get_sys_types() i386_sys_types
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+static const char *partition_type(unsigned char type)
+{
+       int i;
+       const struct systypes *types = get_sys_types();
+
+       for (i=0; types[i].name; i++)
+               if (types[i].name[0] == type)
+                       return types[i].name + 1;
+
+       return _("Unknown");
+}
+
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static int
+get_sysid(int i) {
+       return (
+#ifdef CONFIG_FEATURE_SUN_LABEL
+               sun_label ? sunlabel->infos[i].id :
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+               sgi_label ? sgi_get_sysid(i) :
+#endif
+               ptes[i].part_table->sys_ind);
+}
+
+void list_types(const struct systypes *sys)
+{
+       uint last[4], done = 0, next = 0, size;
+       int i;
+
+       for (i = 0; sys[i].name; i++);
+       size = i;
+
+       for (i = 3; i >= 0; i--)
+               last[3 - i] = done += (size + i - done) / (i + 1);
+       i = done = 0;
+
+       do {
+               printf("%c%2x  %-15.15s", i ? ' ' : '\n',
+                       sys[next].name[0], partition_type(sys[next].name[0]));
+               next = last[i++] + done;
+               if (i > 3 || next >= last[i]) {
+                       i = 0;
+                       next = ++done;
+               }
+       } while (done < last[0]);
+       putchar('\n');
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+static int
+is_cleared_partition(const struct partition *p) {
+       return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
+                p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
+                get_start_sect(p) || get_nr_sects(p));
+}
+
+static void
+clear_partition(struct partition *p) {
+       if (!p)
+               return;
+       memset(p, 0, sizeof(struct partition));
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+set_partition(int i, int doext, off_t start, off_t stop, int sysid) {
+       struct partition *p;
+       off_t offset;
+
+       if (doext) {
+               p = ptes[i].ext_pointer;
+               offset = extended_offset;
+       } else {
+               p = ptes[i].part_table;
+               offset = ptes[i].offset;
+       }
+       p->boot_ind = 0;
+       p->sys_ind = sysid;
+       set_start_sect(p, start - offset);
+       set_nr_sects(p, stop - start + 1);
+       if (dos_compatible_flag && (start/(sectors*heads) > 1023))
+               start = heads*sectors*1024 - 1;
+       set_hsc(p->head, p->sector, p->cyl, start);
+       if (dos_compatible_flag && (stop/(sectors*heads) > 1023))
+               stop = heads*sectors*1024 - 1;
+       set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
+       ptes[i].changed = 1;
+}
+#endif
+
+static int
+test_c(const char **m, const char *mesg) {
+       int val = 0;
+       if (!*m)
+               fprintf(stderr, _("You must set"));
+       else {
+               fprintf(stderr, " %s", *m);
+               val = 1;
+       }
+       *m = mesg;
+       return val;
+}
+
+static int
+warn_geometry(void) {
+       const char *m = NULL;
+       int prev = 0;
+
+       if (!heads)
+               prev = test_c(&m, _("heads"));
+       if (!sectors)
+               prev = test_c(&m, _("sectors"));
+       if (!cylinders)
+               prev = test_c(&m, _("cylinders"));
+       if (!m)
+               return 0;
+
+       fprintf(stderr, "%s%s.\n"
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+                       "You can do this from the extra functions menu.\n"
+#endif
+               , prev ? _(" and ") : " ", m);
+
+       return 1;
+}
+
+static void update_units(void)
+{
+       int cyl_units = heads * sectors;
+
+       if (display_in_cyl_units && cyl_units)
+               units_per_sector = cyl_units;
+       else
+               units_per_sector = 1;   /* in sectors */
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+warn_cylinders(void) {
+       if (dos_label && cylinders > 1024 && !nowarn)
+               fprintf(stderr, _("\n"
+"The number of cylinders for this disk is set to %d.\n"
+"There is nothing wrong with that, but this is larger than 1024,\n"
+"and could in certain setups cause problems with:\n"
+"1) software that runs at boot time (e.g., old versions of LILO)\n"
+"2) booting and partitioning software from other OSs\n"
+"   (e.g., DOS FDISK, OS/2 FDISK)\n"),
+                       cylinders);
+}
+#endif
+
+static void
+read_extended(int ext) {
+       int i;
+       struct pte *pex;
+       struct partition *p, *q;
+
+       ext_index = ext;
+       pex = &ptes[ext];
+       pex->ext_pointer = pex->part_table;
+
+       p = pex->part_table;
+       if (!get_start_sect(p)) {
+               fprintf(stderr,
+                       _("Bad offset in primary extended partition\n"));
+               return;
+       }
+
+       while (IS_EXTENDED (p->sys_ind)) {
+               struct pte *pe = &ptes[partitions];
+
+               if (partitions >= MAXIMUM_PARTS) {
+                       /* This is not a Linux restriction, but
+                          this program uses arrays of size MAXIMUM_PARTS.
+                          Do not try to `improve' this test. */
+                       struct pte *pre = &ptes[partitions-1];
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+                       fprintf(stderr,
+                               _("Warning: deleting partitions after %d\n"),
+                               partitions);
+                       pre->changed = 1;
+#endif
+                       clear_partition(pre->ext_pointer);
+                       return;
+               }
+
+               read_pte(pe, extended_offset + get_start_sect(p));
+
+               if (!extended_offset)
+                       extended_offset = get_start_sect(p);
+
+               q = p = pt_offset(pe->sectorbuffer, 0);
+               for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
+                       if (IS_EXTENDED (p->sys_ind)) {
+                               if (pe->ext_pointer)
+                                       fprintf(stderr,
+                                               _("Warning: extra link "
+                                                 "pointer in partition table"
+                                                 " %d\n"), partitions + 1);
+                               else
+                                       pe->ext_pointer = p;
+                       } else if (p->sys_ind) {
+                               if (pe->part_table)
+                                       fprintf(stderr,
+                                               _("Warning: ignoring extra "
+                                                 "data in partition table"
+                                                 " %d\n"), partitions + 1);
+                               else
+                                       pe->part_table = p;
+                       }
+               }
+
+               /* very strange code here... */
+               if (!pe->part_table) {
+                       if (q != pe->ext_pointer)
+                               pe->part_table = q;
+                       else
+                               pe->part_table = q + 1;
+               }
+               if (!pe->ext_pointer) {
+                       if (q != pe->part_table)
+                               pe->ext_pointer = q;
+                       else
+                               pe->ext_pointer = q + 1;
+               }
+
+               p = pe->ext_pointer;
+               partitions++;
+       }
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       /* remove empty links */
+ remove:
+       for (i = 4; i < partitions; i++) {
+               struct pte *pe = &ptes[i];
+
+               if (!get_nr_sects(pe->part_table) &&
+                   (partitions > 5 || ptes[4].part_table->sys_ind)) {
+                       printf("omitting empty partition (%d)\n", i+1);
+                       delete_partition(i);
+                       goto remove;    /* numbering changed */
+               }
+       }
+#endif
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+create_doslabel(void) {
+       int i;
+
+       fprintf(stderr,
+       _("Building a new DOS disklabel. Changes will remain in memory only,\n"
+         "until you decide to write them. After that, of course, the previous\n"
+         "content won't be recoverable.\n\n"));
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       sun_nolabel();  /* otherwise always recognised as sun */
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       sgi_nolabel();  /* otherwise always recognised as sgi */
+#endif
+#ifdef CONFIG_FEATURE_AIX_LABEL
+       aix_label = 0;
+#endif
+#ifdef CONFIG_FEATURE_OSF_LABEL
+       osf_label = 0;
+       possibly_osf_label = 0;
+#endif
+       partitions = 4;
+
+       for (i = 510-64; i < 510; i++)
+               MBRbuffer[i] = 0;
+       write_part_table_flag(MBRbuffer);
+       extended_offset = 0;
+       set_all_unchanged();
+       set_changed(0);
+       get_boot(create_empty_dos);
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+static void
+get_sectorsize(void) {
+       if (!user_set_sector_size &&
+           get_kernel_revision() >= MAKE_VERSION(2,3,3)) {
+               int arg;
+               if (ioctl(fd, BLKSSZGET, &arg) == 0)
+                       sector_size = arg;
+               if (sector_size != DEFAULT_SECTOR_SIZE)
+                       printf(_("Note: sector size is %d (not %d)\n"),
+                              sector_size, DEFAULT_SECTOR_SIZE);
+       }
+}
+
+static inline void
+get_kernel_geometry(void) {
+       struct hd_geometry geometry;
+
+       if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+               kern_heads = geometry.heads;
+               kern_sectors = geometry.sectors;
+               /* never use geometry.cylinders - it is truncated */
+       }
+}
+
+static void
+get_partition_table_geometry(void) {
+       const unsigned char *bufp = MBRbuffer;
+       struct partition *p;
+       int i, h, s, hh, ss;
+       int first = 1;
+       int bad = 0;
+
+       if (!(valid_part_table_flag(bufp)))
+               return;
+
+       hh = ss = 0;
+       for (i=0; i<4; i++) {
+               p = pt_offset(bufp, i);
+               if (p->sys_ind != 0) {
+                       h = p->end_head + 1;
+                       s = (p->end_sector & 077);
+                       if (first) {
+                               hh = h;
+                               ss = s;
+                               first = 0;
+                       } else if (hh != h || ss != s)
+                               bad = 1;
+               }
+       }
+
+       if (!first && !bad) {
+               pt_heads = hh;
+               pt_sectors = ss;
+       }
+}
+
+void
+get_geometry(void) {
+       int sec_fac;
+       unsigned long long bytes;       /* really u64 */
+
+       get_sectorsize();
+       sec_fac = sector_size / 512;
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       guess_device_type();
+#endif
+       heads = cylinders = sectors = 0;
+       kern_heads = kern_sectors = 0;
+       pt_heads = pt_sectors = 0;
+
+       get_kernel_geometry();
+       get_partition_table_geometry();
+
+       heads = user_heads ? user_heads :
+               pt_heads ? pt_heads :
+               kern_heads ? kern_heads : 255;
+       sectors = user_sectors ? user_sectors :
+               pt_sectors ? pt_sectors :
+               kern_sectors ? kern_sectors : 63;
+       if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) {
+               /* got bytes */
+       } else {
+               unsigned long longsectors;
+
+       if (ioctl(fd, BLKGETSIZE, &longsectors))
+               longsectors = 0;
+                       bytes = ((unsigned long long) longsectors) << 9;
+       }
+
+       total_number_of_sectors = (bytes >> 9);
+
+       sector_offset = 1;
+       if (dos_compatible_flag)
+               sector_offset = sectors;
+
+       cylinders = total_number_of_sectors / (heads * sectors * sec_fac);
+       if (!cylinders)
+               cylinders = user_cylinders;
+}
+
+/*
+ * Read MBR.  Returns:
+ *   -1: no 0xaa55 flag present (possibly entire disk BSD)
+ *    0: found or created label
+ *    1: I/O error
+ */
+int
+get_boot(enum action what) {
+       int i;
+
+       partitions = 4;
+
+       for (i = 0; i < 4; i++) {
+               struct pte *pe = &ptes[i];
+
+               pe->part_table = pt_offset(MBRbuffer, i);
+               pe->ext_pointer = NULL;
+               pe->offset = 0;
+               pe->sectorbuffer = MBRbuffer;
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+               pe->changed = (what == create_empty_dos);
+#endif
+       }
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (what == create_empty_sun && check_sun_label())
+               return 0;
+#endif
+
+       memset(MBRbuffer, 0, 512);
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       if (what == create_empty_dos)
+               goto got_dos_table;             /* skip reading disk */
+
+       if ((fd = open(disk_device, type_open)) < 0) {
+           if ((fd = open(disk_device, O_RDONLY)) < 0) {
+               if (what == try_only)
+                   return 1;
+               fdisk_fatal(unable_to_open);
+           } else
+               printf(_("You will not be able to write "
+                        "the partition table.\n"));
+       }
+
+       if (512 != read(fd, MBRbuffer, 512)) {
+               if (what == try_only)
+                       return 1;
+               fdisk_fatal(unable_to_read);
+       }
+#else
+       if ((fd = open(disk_device, O_RDONLY)) < 0)
+               return 1;
+       if (512 != read(fd, MBRbuffer, 512))
+               return 1;
+#endif
+
+       get_geometry();
+
+       update_units();
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (check_sun_label())
+               return 0;
+#endif
+
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (check_sgi_label())
+               return 0;
+#endif
+
+#ifdef CONFIG_FEATURE_AIX_LABEL
+       if (check_aix_label())
+               return 0;
+#endif
+
+#ifdef CONFIG_FEATURE_OSF_LABEL
+       if (check_osf_label()) {
+               possibly_osf_label = 1;
+               if (!valid_part_table_flag(MBRbuffer)) {
+                       osf_label = 1;
+                       return 0;
+               }
+               printf(_("This disk has both DOS and BSD magic.\n"
+                        "Give the 'b' command to go to BSD mode.\n"));
+       }
+#endif
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+got_dos_table:
+#endif
+
+       if (!valid_part_table_flag(MBRbuffer)) {
+#ifndef CONFIG_FEATURE_FDISK_WRITABLE
+               return -1;
+#else
+               switch(what) {
+               case fdisk:
+                       fprintf(stderr,
+                               _("Device contains neither a valid DOS "
+                                 "partition table, nor Sun, SGI or OSF "
+                                 "disklabel\n"));
+#ifdef __sparc__
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       create_sunlabel();
+#endif
+#else
+                       create_doslabel();
+#endif
+                       return 0;
+               case try_only:
+                       return -1;
+               case create_empty_dos:
+#ifdef CONFIG_FEATURE_SUN_LABEL
+               case create_empty_sun:
+#endif
+                       break;
+               default:
+                       fprintf(stderr, _("Internal error\n"));
+                       exit(1);
+               }
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+       }
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       warn_cylinders();
+#endif
+       warn_geometry();
+
+       for (i = 0; i < 4; i++) {
+               struct pte *pe = &ptes[i];
+
+               if (IS_EXTENDED (pe->part_table->sys_ind)) {
+                       if (partitions != 4)
+                               fprintf(stderr, _("Ignoring extra extended "
+                                       "partition %d\n"), i + 1);
+                       else
+                               read_extended(i);
+               }
+       }
+
+       for (i = 3; i < partitions; i++) {
+               struct pte *pe = &ptes[i];
+
+               if (!valid_part_table_flag(pe->sectorbuffer)) {
+                       fprintf(stderr,
+                               _("Warning: invalid flag 0x%04x of partition "
+                               "table %d will be corrected by w(rite)\n"),
+                               part_table_flag(pe->sectorbuffer), i + 1);
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+                       pe->changed = 1;
+#endif
+               }
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+/*
+ * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
+ * If the user hits Enter, DFLT is returned.
+ * Answers like +10 are interpreted as offsets from BASE.
+ *
+ * There is no default if DFLT is not between LOW and HIGH.
+ */
+static uint
+read_int(uint low, uint dflt, uint high, uint base, char *mesg)
+{
+       uint i;
+       int default_ok = 1;
+       static char *ms = NULL;
+       static int mslen = 0;
+
+       if (!ms || strlen(mesg)+100 > mslen) {
+               mslen = strlen(mesg)+200;
+               ms = xrealloc(ms,mslen);
+       }
+
+       if (dflt < low || dflt > high)
+               default_ok = 0;
+
+       if (default_ok)
+               snprintf(ms, mslen, _("%s (%u-%u, default %u): "),
+                        mesg, low, high, dflt);
+       else
+               snprintf(ms, mslen, "%s (%u-%u): ",
+                        mesg, low, high);
+
+       while (1) {
+               int use_default = default_ok;
+
+               /* ask question and read answer */
+               while (read_chars(ms) != '\n' && !isdigit(*line_ptr)
+                      && *line_ptr != '-' && *line_ptr != '+')
+                       continue;
+
+               if (*line_ptr == '+' || *line_ptr == '-') {
+                      int minus = (*line_ptr == '-');
+                      int absolute = 0;
+
+                      i = atoi(line_ptr+1);
+
+                      while (isdigit(*++line_ptr))
+                              use_default = 0;
+
+                      switch (*line_ptr) {
+                              case 'c':
+                              case 'C':
+                                       if (!display_in_cyl_units)
+                                               i *= heads * 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) i * absolute;
+                              unit = sector_size * units_per_sector;
+                              bytes += unit/2; /* round */
+                              bytes /= unit;
+                              i = bytes;
+                       }
+                      if (minus)
+                              i = -i;
+                      i += base;
+               } else {
+                       i = atoi(line_ptr);
+                       while (isdigit(*line_ptr)) {
+                               line_ptr++;
+                               use_default = 0;
+                       }
+               }
+               if (use_default)
+                       printf(_("Using default value %u\n"), i = dflt);
+               if (i >= low && i <= high)
+                       break;
+               else
+                       printf(_("Value out of range.\n"));
+       }
+       return i;
+}
+
+int
+get_partition(int warn, int max) {
+       struct pte *pe;
+       int i;
+
+       i = read_int(1, 0, max, 0, _("Partition number")) - 1;
+       pe = &ptes[i];
+
+       if (warn) {
+               if ((!sun_label && !sgi_label && !pe->part_table->sys_ind)
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                   || (sun_label &&
+                       (!sunlabel->partitions[i].num_sectors ||
+                        !sunlabel->infos[i].id))
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                   || (sgi_label && (!sgi_get_num_sectors(i)))
+#endif
+                  )
+                       fprintf(stderr,
+                               _("Warning: partition %d has empty type\n"),
+                               i+1);
+       }
+       return i;
+}
+
+static int
+get_existing_partition(int warn, int max) {
+       int pno = -1;
+       int i;
+
+       for (i = 0; i < max; i++) {
+               struct pte *pe = &ptes[i];
+               struct partition *p = pe->part_table;
+
+               if (p && !is_cleared_partition(p)) {
+                       if (pno >= 0)
+                               goto not_unique;
+                       pno = i;
+               }
+       }
+       if (pno >= 0) {
+               printf(_("Selected partition %d\n"), pno+1);
+               return pno;
+       }
+       printf(_("No partition is defined yet!\n"));
+       return -1;
+
+ not_unique:
+       return get_partition(warn, max);
+}
+
+static int
+get_nonexisting_partition(int warn, int max) {
+       int pno = -1;
+       int i;
+
+       for (i = 0; i < max; i++) {
+               struct pte *pe = &ptes[i];
+               struct partition *p = pe->part_table;
+
+               if (p && is_cleared_partition(p)) {
+                       if (pno >= 0)
+                               goto not_unique;
+                       pno = i;
+               }
+       }
+       if (pno >= 0) {
+               printf(_("Selected partition %d\n"), pno+1);
+               return pno;
+       }
+       printf(_("All primary partitions have been defined already!\n"));
+       return -1;
+
+ not_unique:
+       return get_partition(warn, max);
+}
+
+
+void change_units(void)
+{
+       display_in_cyl_units = !display_in_cyl_units;
+       update_units();
+       printf(_("Changing display/entry units to %s\n"),
+               str_units(PLURAL));
+}
+
+static void
+toggle_active(int i) {
+       struct pte *pe = &ptes[i];
+       struct partition *p = pe->part_table;
+
+       if (IS_EXTENDED (p->sys_ind) && !p->boot_ind)
+               fprintf(stderr,
+                       _("WARNING: Partition %d is an extended partition\n"),
+                       i + 1);
+       p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+       pe->changed = 1;
+}
+
+static void
+toggle_dos_compatibility_flag(void) {
+       dos_compatible_flag = ~dos_compatible_flag;
+       if (dos_compatible_flag) {
+               sector_offset = sectors;
+               printf(_("DOS Compatibility flag is set\n"));
+       }
+       else {
+               sector_offset = 1;
+               printf(_("DOS Compatibility flag is not set\n"));
+       }
+}
+
+static void
+delete_partition(int i) {
+       struct pte *pe = &ptes[i];
+       struct partition *p = pe->part_table;
+       struct partition *q = pe->ext_pointer;
+
+/* Note that for the fifth partition (i == 4) we don't actually
+ * decrement partitions.
+ */
+
+       if (warn_geometry())
+               return;         /* C/H/S not set */
+       pe->changed = 1;
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (sun_label) {
+               sun_delete_partition(i);
+               return;
+       }
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (sgi_label) {
+               sgi_delete_partition(i);
+               return;
+       }
+#endif
+
+       if (i < 4) {
+               if (IS_EXTENDED (p->sys_ind) && i == ext_index) {
+                       partitions = 4;
+                       ptes[ext_index].ext_pointer = NULL;
+                       extended_offset = 0;
+               }
+               clear_partition(p);
+               return;
+       }
+
+       if (!q->sys_ind && i > 4) {
+               /* the last one in the chain - just delete */
+               --partitions;
+               --i;
+               clear_partition(ptes[i].ext_pointer);
+               ptes[i].changed = 1;
+       } else {
+               /* not the last one - further ones will be moved down */
+               if (i > 4) {
+                       /* delete this link in the chain */
+                       p = ptes[i-1].ext_pointer;
+                       *p = *q;
+                       set_start_sect(p, get_start_sect(q));
+                       set_nr_sects(p, get_nr_sects(q));
+                       ptes[i-1].changed = 1;
+               } else if (partitions > 5) {    /* 5 will be moved to 4 */
+                       /* the first logical in a longer chain */
+                       pe = &ptes[5];
+
+                       if (pe->part_table) /* prevent SEGFAULT */
+                               set_start_sect(pe->part_table,
+                                              get_partition_start(pe) -
+                                              extended_offset);
+                       pe->offset = extended_offset;
+                       pe->changed = 1;
+               }
+
+               if (partitions > 5) {
+                       partitions--;
+                       while (i < partitions) {
+                               ptes[i] = ptes[i+1];
+                               i++;
+                       }
+               } else
+                       /* the only logical: clear only */
+                       clear_partition(ptes[i].part_table);
+       }
+}
+
+static void
+change_sysid(void) {
+       int i, sys, origsys;
+       struct partition *p;
+
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       /* If sgi_label then don't use get_existing_partition,
+          let the user select a partition, since get_existing_partition()
+          only works for Linux like partition tables. */
+       if (!sgi_label) {
+       i = get_existing_partition(0, partitions);
+       } else {
+               i = get_partition(0, partitions);
+       }
+#else
+       i = get_existing_partition(0, partitions);
+#endif
+       if (i == -1)
+               return;
+       p = ptes[i].part_table;
+       origsys = sys = get_sysid(i);
+
+       /* if changing types T to 0 is allowed, then
+          the reverse change must be allowed, too */
+       if (!sys && !sgi_label && !sun_label && !get_nr_sects(p))
+               printf(_("Partition %d does not exist yet!\n"), i + 1);
+       else while (1) {
+               sys = read_hex (get_sys_types());
+
+               if (!sys && !sgi_label && !sun_label) {
+                       printf(_("Type 0 means free space to many systems\n"
+                              "(but not to Linux). Having partitions of\n"
+                              "type 0 is probably unwise. You can delete\n"
+                              "a partition using the `d' command.\n"));
+                       /* break; */
+               }
+
+               if (!sun_label && !sgi_label) {
+                       if (IS_EXTENDED (sys) != IS_EXTENDED (p->sys_ind)) {
+                               printf(_("You cannot change a partition into"
+                                      " an extended one or vice versa\n"
+                                      "Delete it first.\n"));
+                               break;
+                       }
+               }
+
+               if (sys < 256) {
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label && i == 2 && sys != WHOLE_DISK)
+                               printf(_("Consider leaving partition 3 "
+                                      "as Whole disk (5),\n"
+                                      "as SunOS/Solaris expects it and "
+                                      "even Linux likes it.\n\n"));
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       if (sgi_label && ((i == 10 && sys != ENTIRE_DISK)
+                                         || (i == 8 && sys != 0)))
+                               printf(_("Consider leaving partition 9 "
+                                      "as volume header (0),\nand "
+                                      "partition 11 as entire volume (6)"
+                                      "as IRIX expects it.\n\n"));
+#endif
+                       if (sys == origsys)
+                               break;
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label) {
+                               sun_change_sysid(i, sys);
+                       } else
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       if (sgi_label) {
+                               sgi_change_sysid(i, sys);
+                       } else
+#endif
+                               p->sys_ind = sys;
+                       printf (_("Changed system type of partition %d "
+                               "to %x (%s)\n"), i + 1, sys,
+                               partition_type(sys));
+                       ptes[i].changed = 1;
+                       if (is_dos_partition(origsys) ||
+                           is_dos_partition(sys))
+                               dos_changed = 1;
+                       break;
+               }
+       }
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+
+/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct.  1991). */
+
+static void long2chs(ulong ls, uint *c, uint *h, uint *s) {
+       int     spc = heads * sectors;
+
+       *c = ls / spc;
+       ls = ls % spc;
+       *h = ls / sectors;
+       *s = ls % sectors + 1;  /* sectors count from 1 */
+}
+
+static void check_consistency(const struct partition *p, int partition) {
+       uint    pbc, pbh, pbs;          /* physical beginning c, h, s */
+       uint    pec, peh, pes;          /* physical ending c, h, s */
+       uint    lbc, lbh, lbs;          /* logical beginning c, h, s */
+       uint    lec, leh, les;          /* logical ending c, h, s */
+
+       if (!heads || !sectors || (partition >= 4))
+               return;         /* do not check extended partitions */
+
+/* physical beginning c, h, s */
+       pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
+       pbh = p->head;
+       pbs = p->sector & 0x3f;
+
+/* physical ending c, h, s */
+       pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
+       peh = p->end_head;
+       pes = p->end_sector & 0x3f;
+
+/* compute logical beginning (c, h, s) */
+       long2chs(get_start_sect(p), &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+       long2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
+
+/* Same physical / logical beginning? */
+       if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+               printf(_("Partition %d has different physical/logical "
+                       "beginnings (non-Linux?):\n"), partition + 1);
+               printf(_("     phys=(%d, %d, %d) "), pbc, pbh, pbs);
+               printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs);
+       }
+
+/* Same physical / logical ending? */
+       if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+               printf(_("Partition %d has different physical/logical "
+                       "endings:\n"), partition + 1);
+               printf(_("     phys=(%d, %d, %d) "), pec, peh, pes);
+               printf(_("logical=(%d, %d, %d)\n"),lec, leh, les);
+       }
+
+#if 0
+/* Beginning on cylinder boundary? */
+       if (pbh != !pbc || pbs != 1) {
+               printf(_("Partition %i does not start on cylinder "
+                       "boundary:\n"), partition + 1);
+               printf(_("     phys=(%d, %d, %d) "), pbc, pbh, pbs);
+               printf(_("should be (%d, %d, 1)\n"), pbc, !pbc);
+       }
+#endif
+
+/* Ending on cylinder boundary? */
+       if (peh != (heads - 1) || pes != sectors) {
+               printf(_("Partition %i does not end on cylinder boundary.\n"),
+                       partition + 1);
+#if 0
+               printf(_("     phys=(%d, %d, %d) "), pec, peh, pes);
+               printf(_("should be (%d, %d, %d)\n"),
+               pec, heads - 1, sectors);
+#endif
+       }
+}
+
+static void
+list_disk_geometry(void) {
+       long long bytes = (total_number_of_sectors << 9);
+       long megabytes = bytes/1000000;
+
+       if (megabytes < 10000)
+               printf(_("\nDisk %s: %ld MB, %lld bytes\n"),
+                      disk_device, megabytes, bytes);
+       else
+               printf(_("\nDisk %s: %ld.%ld GB, %lld bytes\n"),
+                      disk_device, megabytes/1000, (megabytes/100)%10, bytes);
+       printf(_("%d heads, %d sectors/track, %d cylinders"),
+              heads, sectors, cylinders);
+       if (units_per_sector == 1)
+               printf(_(", total %llu sectors"),
+                      total_number_of_sectors / (sector_size/512));
+       printf(_("\nUnits = %s of %d * %d = %d bytes\n\n"),
+              str_units(PLURAL),
+              units_per_sector, sector_size, units_per_sector * sector_size);
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int
+wrong_p_order(int *prev) {
+       const struct pte *pe;
+       const struct partition *p;
+       off_t last_p_start_pos = 0, p_start_pos;
+       int i, last_i = 0;
+
+       for (i = 0 ; i < partitions; i++) {
+               if (i == 4) {
+                       last_i = 4;
+                       last_p_start_pos = 0;
+               }
+               pe = &ptes[i];
+               if ((p = pe->part_table)->sys_ind) {
+                       p_start_pos = get_partition_start(pe);
+
+                       if (last_p_start_pos > p_start_pos) {
+                               if (prev)
+                                       *prev = last_i;
+                               return i;
+                       }
+
+                       last_p_start_pos = p_start_pos;
+                       last_i = i;
+               }
+       }
+       return 0;
+}
+
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+/*
+ * Fix the chain of logicals.
+ * extended_offset is unchanged, the set of sectors used is unchanged
+ * The chain is sorted so that sectors increase, and so that
+ * starting sectors increase.
+ *
+ * After this it may still be that cfdisk doesnt like the table.
+ * (This is because cfdisk considers expanded parts, from link to
+ * end of partition, and these may still overlap.)
+ * Now
+ *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
+ * may help.
+ */
+static void
+fix_chain_of_logicals(void) {
+       int j, oj, ojj, sj, sjj;
+       struct partition *pj,*pjj,tmp;
+
+       /* Stage 1: sort sectors but leave sector of part 4 */
+       /* (Its sector is the global extended_offset.) */
+ stage1:
+       for (j = 5; j < partitions-1; j++) {
+               oj = ptes[j].offset;
+               ojj = ptes[j+1].offset;
+               if (oj > ojj) {
+                       ptes[j].offset = ojj;
+                       ptes[j+1].offset = oj;
+                       pj = ptes[j].part_table;
+                       set_start_sect(pj, get_start_sect(pj)+oj-ojj);
+                       pjj = ptes[j+1].part_table;
+                       set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
+                       set_start_sect(ptes[j-1].ext_pointer,
+                                      ojj-extended_offset);
+                       set_start_sect(ptes[j].ext_pointer,
+                                      oj-extended_offset);
+                       goto stage1;
+               }
+       }
+
+       /* Stage 2: sort starting sectors */
+ stage2:
+       for (j = 4; j < partitions-1; j++) {
+               pj = ptes[j].part_table;
+               pjj = ptes[j+1].part_table;
+               sj = get_start_sect(pj);
+               sjj = get_start_sect(pjj);
+               oj = ptes[j].offset;
+               ojj = ptes[j+1].offset;
+               if (oj+sj > ojj+sjj) {
+                       tmp = *pj;
+                       *pj = *pjj;
+                       *pjj = tmp;
+                       set_start_sect(pj, ojj+sjj-oj);
+                       set_start_sect(pjj, oj+sj-ojj);
+                       goto stage2;
+               }
+       }
+
+       /* Probably something was changed */
+       for (j = 4; j < partitions; j++)
+               ptes[j].changed = 1;
+}
+
+
+static void
+fix_partition_table_order(void) {
+       struct pte *pei, *pek;
+       int i,k;
+
+       if (!wrong_p_order(NULL)) {
+               printf(_("Nothing to do. Ordering is correct already.\n\n"));
+               return;
+       }
+
+       while ((i = wrong_p_order(&k)) != 0 && i < 4) {
+               /* partition i should have come earlier, move it */
+               /* We have to move data in the MBR */
+               struct partition *pi, *pk, *pe, pbuf;
+               pei = &ptes[i];
+               pek = &ptes[k];
+
+               pe = pei->ext_pointer;
+               pei->ext_pointer = pek->ext_pointer;
+               pek->ext_pointer = pe;
+
+               pi = pei->part_table;
+               pk = pek->part_table;
+
+               memmove(&pbuf, pi, sizeof(struct partition));
+               memmove(pi, pk, sizeof(struct partition));
+               memmove(pk, &pbuf, sizeof(struct partition));
+
+               pei->changed = pek->changed = 1;
+       }
+
+       if (i)
+               fix_chain_of_logicals();
+
+       printf("Done.\n");
+
+}
+#endif
+
+static void
+list_table(int xtra) {
+       const struct partition *p;
+       int i, w;
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (sun_label) {
+               sun_list_table(xtra);
+               return;
+       }
+#endif
+
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (sgi_label) {
+               sgi_list_table(xtra);
+               return;
+       }
+#endif
+
+       list_disk_geometry();
+
+#ifdef CONFIG_FEATURE_OSF_LABEL
+       if (osf_label) {
+               xbsd_print_disklabel(xtra);
+               return;
+       }
+#endif
+
+       /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
+          but if the device name ends in a digit, say /dev/foo1,
+          then the partition is called /dev/foo1p3. */
+       w = strlen(disk_device);
+       if (w && isdigit(disk_device[w-1]))
+               w++;
+       if (w < 5)
+               w = 5;
+
+       printf(_("%*s Boot    Start       End    Blocks   Id  System\n"),
+              w+1, _("Device"));
+
+       for (i = 0; i < partitions; i++) {
+               const struct pte *pe = &ptes[i];
+
+               p = pe->part_table;
+               if (p && !is_cleared_partition(p)) {
+                       off_t psects = get_nr_sects(p);
+                       off_t pblocks = psects;
+                       unsigned int podd = 0;
+
+                       if (sector_size < 1024) {
+                               pblocks /= (1024 / sector_size);
+                               podd = psects % (1024 / sector_size);
+                       }
+                       if (sector_size > 1024)
+                               pblocks *= (sector_size / 1024);
+                       printf(
+                           "%s  %c %11llu %11llu %11llu%c  %2x  %s\n",
+                       partname(disk_device, i+1, w+2),
+/* boot flag */         !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
+                       ? '*' : '?',
+/* start */             (unsigned long long) cround(get_partition_start(pe)),
+/* end */               (unsigned long long) cround(get_partition_start(pe) + psects
+                               - (psects ? 1 : 0)),
+/* odd flag on end */   (unsigned long long) pblocks, podd ? '+' : ' ',
+/* type id */           p->sys_ind,
+/* type name */         partition_type(p->sys_ind));
+                       check_consistency(p, i);
+               }
+       }
+
+       /* Is partition table in disk order? It need not be, but... */
+       /* partition table entries are not checked for correct order if this
+          is a sgi, sun or aix labeled disk... */
+       if (dos_label && wrong_p_order(NULL)) {
+               printf(_("\nPartition table entries are not in disk order\n"));
+       }
+}
+
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+static void
+x_list_table(int extend) {
+       const struct pte *pe;
+       const struct partition *p;
+       int i;
+
+       printf(_("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n"),
+               disk_device, heads, sectors, cylinders);
+       printf(_("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl    Start     Size ID\n"));
+       for (i = 0 ; i < partitions; i++) {
+               pe = &ptes[i];
+               p = (extend ? pe->ext_pointer : pe->part_table);
+               if (p != NULL) {
+                       printf("%2d %02x%4d%4d%5d%4d%4d%5d%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),
+                               get_start_sect(p), get_nr_sects(p), p->sys_ind);
+                       if (p->sys_ind)
+                               check_consistency(p, i);
+               }
+       }
+}
+#endif
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+fill_bounds(off_t *first, off_t *last) {
+       int i;
+       const struct pte *pe = &ptes[0];
+       const struct partition *p;
+
+       for (i = 0; i < partitions; pe++,i++) {
+               p = pe->part_table;
+               if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) {
+                       first[i] = 0xffffffff;
+                       last[i] = 0;
+               } else {
+                       first[i] = get_partition_start(pe);
+                       last[i] = first[i] + get_nr_sects(p) - 1;
+               }
+       }
+}
+
+static void
+check(int n, uint h, uint s, uint c, off_t start) {
+       off_t total, real_s, real_c;
+
+       real_s = sector(s) - 1;
+       real_c = cylinder(s, c);
+       total = (real_c * sectors + real_s) * heads + h;
+       if (!total)
+               fprintf(stderr, _("Warning: partition %d contains sector 0\n"), n);
+       if (h >= heads)
+               fprintf(stderr,
+                       _("Partition %d: head %d greater than maximum %d\n"),
+                       n, h + 1, heads);
+       if (real_s >= sectors)
+               fprintf(stderr, _("Partition %d: sector %d greater than "
+                       "maximum %d\n"), n, s, sectors);
+       if (real_c >= cylinders)
+               fprintf(stderr, _("Partitions %d: cylinder %llu greater than "
+                       "maximum %d\n"), n, (unsigned long long)real_c + 1, cylinders);
+       if (cylinders <= 1024 && start != total)
+               fprintf(stderr,
+                       _("Partition %d: previous sectors %llu disagrees with "
+                       "total %llu\n"), n, (unsigned long long)start, (unsigned long long)total);
+}
+
+static void
+verify(void) {
+       int i, j;
+       uint total = 1;
+       off_t first[partitions], last[partitions];
+       struct partition *p;
+
+       if (warn_geometry())
+               return;
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (sun_label) {
+               verify_sun();
+               return;
+       }
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (sgi_label) {
+               verify_sgi(1);
+               return;
+       }
+#endif
+
+       fill_bounds(first, last);
+       for (i = 0; i < partitions; i++) {
+               struct pte *pe = &ptes[i];
+
+               p = pe->part_table;
+               if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) {
+                       check_consistency(p, i);
+                       if (get_partition_start(pe) < first[i])
+                               printf(_("Warning: bad start-of-data in "
+                                       "partition %d\n"), i + 1);
+                       check(i + 1, p->end_head, p->end_sector, p->end_cyl,
+                               last[i]);
+                       total += last[i] + 1 - first[i];
+                       for (j = 0; j < i; j++)
+                       if ((first[i] >= first[j] && first[i] <= last[j])
+                        || ((last[i] <= last[j] && last[i] >= first[j]))) {
+                               printf(_("Warning: partition %d overlaps "
+                                       "partition %d.\n"), j + 1, i + 1);
+                               total += first[i] >= first[j] ?
+                                       first[i] : first[j];
+                               total -= last[i] <= last[j] ?
+                                       last[i] : last[j];
+                       }
+               }
+       }
+
+       if (extended_offset) {
+               struct pte *pex = &ptes[ext_index];
+               off_t e_last = get_start_sect(pex->part_table) +
+                       get_nr_sects(pex->part_table) - 1;
+
+               for (i = 4; i < partitions; i++) {
+                       total++;
+                       p = ptes[i].part_table;
+                       if (!p->sys_ind) {
+                               if (i != 4 || i + 1 < partitions)
+                                       printf(_("Warning: partition %d "
+                                               "is empty\n"), i + 1);
+                       }
+                       else if (first[i] < extended_offset ||
+                                       last[i] > e_last)
+                               printf(_("Logical partition %d not entirely in "
+                                       "partition %d\n"), i + 1, ext_index + 1);
+               }
+       }
+
+       if (total > heads * sectors * cylinders)
+               printf(_("Total allocated sectors %d greater than the maximum "
+                       "%d\n"), total, heads * sectors * cylinders);
+       else if ((total = heads * sectors * cylinders - total) != 0)
+               printf(_("%d unallocated sectors\n"), total);
+}
+
+static void
+add_partition(int n, int sys) {
+       char mesg[256];         /* 48 does not suffice in Japanese */
+       int i, readed = 0;
+       struct partition *p = ptes[n].part_table;
+       struct partition *q = ptes[ext_index].part_table;
+       long long llimit;
+       off_t start, stop = 0, limit, temp,
+               first[partitions], last[partitions];
+
+       if (p && p->sys_ind) {
+               printf(_("Partition %d is already defined.  Delete "
+                        "it before re-adding it.\n"), n + 1);
+               return;
+       }
+       fill_bounds(first, last);
+       if (n < 4) {
+               start = sector_offset;
+               if (display_in_cyl_units || !total_number_of_sectors)
+                       llimit = heads * sectors * cylinders - 1;
+               else
+                       llimit = total_number_of_sectors - 1;
+               limit = llimit;
+               if (limit != llimit)
+                       limit = 0x7fffffff;
+               if (extended_offset) {
+                       first[ext_index] = extended_offset;
+                       last[ext_index] = get_start_sect(q) +
+                               get_nr_sects(q) - 1;
+               }
+       } else {
+               start = extended_offset + sector_offset;
+               limit = get_start_sect(q) + get_nr_sects(q) - 1;
+       }
+       if (display_in_cyl_units)
+               for (i = 0; i < partitions; i++)
+                       first[i] = (cround(first[i]) - 1) * units_per_sector;
+
+       snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+       do {
+               temp = start;
+               for (i = 0; i < partitions; i++) {
+                       int lastplusoff;
+
+                       if (start == ptes[i].offset)
+                               start += sector_offset;
+                       lastplusoff = last[i] + ((n<4) ? 0 : sector_offset);
+                       if (start >= first[i] && start <= lastplusoff)
+                               start = lastplusoff + 1;
+               }
+               if (start > limit)
+                       break;
+               if (start >= temp+units_per_sector && readed) {
+                       printf(_("Sector %llu is already allocated\n"), (unsigned long long)temp);
+                       temp = start;
+                       readed = 0;
+               }
+               if (!readed && start == temp) {
+                       off_t saved_start;
+
+                       saved_start = start;
+                       start = read_int(cround(saved_start), cround(saved_start), cround(limit),
+                                        0, mesg);
+                       if (display_in_cyl_units) {
+                               start = (start - 1) * units_per_sector;
+                               if (start < saved_start) start = saved_start;
+                       }
+                       readed = 1;
+               }
+       } while (start != temp || !readed);
+       if (n > 4) {                    /* NOT for fifth partition */
+               struct pte *pe = &ptes[n];
+
+               pe->offset = start - sector_offset;
+               if (pe->offset == extended_offset) { /* must be corrected */
+                       pe->offset++;
+                       if (sector_offset == 1)
+                               start++;
+               }
+       }
+
+       for (i = 0; i < partitions; i++) {
+               struct pte *pe = &ptes[i];
+
+               if (start < pe->offset && limit >= pe->offset)
+                       limit = pe->offset - 1;
+               if (start < first[i] && limit >= first[i])
+                       limit = first[i] - 1;
+       }
+       if (start > limit) {
+               printf(_("No free sectors available\n"));
+               if (n > 4)
+                       partitions--;
+               return;
+       }
+       if (cround(start) == cround(limit)) {
+               stop = limit;
+       } else {
+               snprintf(mesg, sizeof(mesg),
+                        _("Last %s or +size or +sizeM or +sizeK"),
+                        str_units(SINGULAR));
+               stop = read_int(cround(start), cround(limit), cround(limit),
+                               cround(start), mesg);
+               if (display_in_cyl_units) {
+                       stop = stop * units_per_sector - 1;
+                       if (stop >limit)
+                               stop = limit;
+               }
+       }
+
+       set_partition(n, 0, start, stop, sys);
+       if (n > 4)
+               set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED);
+
+       if (IS_EXTENDED (sys)) {
+               struct pte *pe4 = &ptes[4];
+               struct pte *pen = &ptes[n];
+
+               ext_index = n;
+               pen->ext_pointer = p;
+               pe4->offset = extended_offset = start;
+               pe4->sectorbuffer = xcalloc(1, sector_size);
+               pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
+               pe4->ext_pointer = pe4->part_table + 1;
+               pe4->changed = 1;
+               partitions = 5;
+       }
+}
+
+static void
+add_logical(void) {
+       if (partitions > 5 || ptes[4].part_table->sys_ind) {
+               struct pte *pe = &ptes[partitions];
+
+               pe->sectorbuffer = xcalloc(1, sector_size);
+               pe->part_table = pt_offset(pe->sectorbuffer, 0);
+               pe->ext_pointer = pe->part_table + 1;
+               pe->offset = 0;
+               pe->changed = 1;
+               partitions++;
+       }
+       add_partition(partitions - 1, LINUX_NATIVE);
+}
+
+static void
+new_partition(void) {
+       int i, free_primary = 0;
+
+       if (warn_geometry())
+               return;
+
+#ifdef CONFIG_FEATURE_SUN_LABEL
+       if (sun_label) {
+               add_sun_partition(get_partition(0, partitions), LINUX_NATIVE);
+               return;
+       }
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       if (sgi_label) {
+               sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE);
+               return;
+       }
+#endif
+#ifdef CONFIG_FEATURE_AIX_LABEL
+       if (aix_label) {
+               printf(_("\tSorry - this fdisk cannot handle AIX disk labels."
+                        "\n\tIf you want to add DOS-type partitions, create"
+                        "\n\ta new empty DOS partition table first. (Use o.)"
+                        "\n\tWARNING: "
+                        "This will destroy the present disk contents.\n"));
+               return;
+       }
+#endif
+
+       for (i = 0; i < 4; i++)
+               free_primary += !ptes[i].part_table->sys_ind;
+
+       if (!free_primary && partitions >= MAXIMUM_PARTS) {
+               printf(_("The maximum number of partitions has been created\n"));
+               return;
+       }
+
+       if (!free_primary) {
+               if (extended_offset)
+                       add_logical();
+               else
+                       printf(_("You must delete some partition and add "
+                                "an extended partition first\n"));
+       } else {
+               char c, line[LINE_LENGTH];
+               snprintf(line, sizeof(line), "%s\n   %s\n   p   primary "
+                                               "partition (1-4)\n",
+                        "Command action", (extended_offset ?
+                        "l   logical (5 or over)" : "e   extended"));
+               while (1) {
+                       if ((c = read_char(line)) == 'p' || c == 'P') {
+                               i = get_nonexisting_partition(0, 4);
+                               if (i >= 0)
+                                       add_partition(i, LINUX_NATIVE);
+                               return;
+                       }
+                       else if (c == 'l' && extended_offset) {
+                               add_logical();
+                               return;
+                       }
+                       else if (c == 'e' && !extended_offset) {
+                               i = get_nonexisting_partition(0, 4);
+                               if (i >= 0)
+                                       add_partition(i, EXTENDED);
+                               return;
+                       }
+                       else
+                               printf(_("Invalid partition number "
+                                        "for type `%c'\n"), c);
+               }
+       }
+}
+
+static void
+write_table(void) {
+       int i;
+
+       if (dos_label) {
+               for (i=0; i<3; i++)
+                       if (ptes[i].changed)
+                               ptes[3].changed = 1;
+               for (i = 3; i < partitions; i++) {
+                       struct pte *pe = &ptes[i];
+
+                       if (pe->changed) {
+                               write_part_table_flag(pe->sectorbuffer);
+                               write_sector(pe->offset, pe->sectorbuffer);
+                       }
+               }
+       }
+#ifdef CONFIG_FEATURE_SGI_LABEL
+       else if (sgi_label) {
+               /* no test on change? the printf below might be mistaken */
+               sgi_write_table();
+       }
+#endif
+#ifdef CONFIG_FEATURE_SUN_LABEL
+         else if (sun_label) {
+               int needw = 0;
+
+               for (i=0; i<8; i++)
+                       if (ptes[i].changed)
+                               needw = 1;
+               if (needw)
+                       sun_write_table();
+       }
+#endif
+
+       printf(_("The partition table has been altered!\n\n"));
+       reread_partition_table(1);
+}
+
+void
+reread_partition_table(int leave) {
+       int error = 0;
+       int i;
+
+       printf(_("Calling ioctl() to re-read partition table.\n"));
+       sync();
+       sleep(2);
+       if ((i = ioctl(fd, BLKRRPART)) != 0) {
+               error = errno;
+       } else {
+               /* some kernel versions (1.2.x) seem to have trouble
+                  rereading the partition table, but if asked to do it
+                  twice, the second time works. - biro@yggdrasil.com */
+               sync();
+               sleep(2);
+               if ((i = ioctl(fd, BLKRRPART)) != 0)
+                       error = errno;
+       }
+
+       if (i) {
+               printf(_("\nWARNING: Re-reading the partition table "
+                        "failed with error %d: %s.\n"
+                        "The kernel still uses the old table.\n"
+                        "The new table will be used "
+                        "at the next reboot.\n"),
+                       error, strerror(error));
+       }
+
+       if (dos_changed)
+           printf(
+               _("\nWARNING: If you have created or modified any DOS 6.x\n"
+               "partitions, please see the fdisk manual page for additional\n"
+               "information.\n"));
+
+       if (leave) {
+               close(fd);
+
+               printf(_("Syncing disks.\n"));
+               sync();
+               sleep(4);               /* for sync() */
+               exit(!!i);
+       }
+}
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+#define MAX_PER_LINE    16
+static void
+print_buffer(char pbuffer[]) {
+       int     i,
+               l;
+
+       for (i = 0, l = 0; i < sector_size; i++, l++) {
+               if (l == 0)
+                       printf("0x%03X:", i);
+               printf(" %02X", (unsigned char) pbuffer[i]);
+               if (l == MAX_PER_LINE - 1) {
+                       printf("\n");
+                       l = -1;
+               }
+       }
+       if (l > 0)
+               printf("\n");
+       printf("\n");
+}
+
+
+static void
+print_raw(void) {
+       int i;
+
+       printf(_("Device: %s\n"), disk_device);
+#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL)
+       if (sun_label || sgi_label)
+               print_buffer(MBRbuffer);
+       else
+#endif
+               for (i = 3; i < partitions; i++)
+                       print_buffer(ptes[i].sectorbuffer);
+}
+
+static void
+move_begin(int i) {
+       struct pte *pe = &ptes[i];
+       struct partition *p = pe->part_table;
+       off_t new, first;
+
+       if (warn_geometry())
+               return;
+       if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED (p->sys_ind)) {
+               printf(_("Partition %d has no data area\n"), i + 1);
+               return;
+       }
+       first = get_partition_start(pe);
+       new = read_int(first, first, first + get_nr_sects(p) - 1, first,
+                      _("New beginning of data")) - pe->offset;
+
+       if (new != get_nr_sects(p)) {
+               first = get_nr_sects(p) + get_start_sect(p) - new;
+               set_nr_sects(p, first);
+               set_start_sect(p, new);
+               pe->changed = 1;
+       }
+}
+
+static void
+xselect(void) {
+       char c;
+
+       while(1) {
+               putchar('\n');
+               c = tolower(read_char(_("Expert command (m for help): ")));
+               switch (c) {
+               case 'a':
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label)
+                               sun_set_alt_cyl();
+#endif
+                       break;
+               case 'b':
+                       if (dos_label)
+                               move_begin(get_partition(0, partitions));
+                       break;
+               case 'c':
+                       user_cylinders = cylinders =
+                               read_int(1, cylinders, 1048576, 0,
+                                        _("Number of cylinders"));
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label)
+                               sun_set_ncyl(cylinders);
+#endif
+                       if (dos_label)
+                               warn_cylinders();
+                       break;
+               case 'd':
+                       print_raw();
+                       break;
+               case 'e':
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       if (sgi_label)
+                               sgi_set_xcyl();
+                        else
+#endif
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                        if (sun_label)
+                               sun_set_xcyl();
+                        else
+#endif
+                       if (dos_label)
+                               x_list_table(1);
+                       break;
+               case 'f':
+                       if (dos_label)
+                               fix_partition_table_order();
+                       break;
+               case 'g':
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       create_sgilabel();
+#endif
+                       break;
+               case 'h':
+                       user_heads = heads = read_int(1, heads, 256, 0,
+                                        _("Number of heads"));
+                       update_units();
+                       break;
+               case 'i':
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label)
+                               sun_set_ilfact();
+#endif
+                       break;
+               case 'o':
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label)
+                               sun_set_rspeed();
+#endif
+                       break;
+               case 'p':
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label)
+                               list_table(1);
+                       else
+#endif
+                               x_list_table(0);
+                       break;
+               case 'q':
+                       close(fd);
+                       printf("\n");
+                       exit(0);
+               case 'r':
+                       return;
+               case 's':
+                       user_sectors = sectors = read_int(1, sectors, 63, 0,
+                                          _("Number of sectors"));
+                       if (dos_compatible_flag) {
+                               sector_offset = sectors;
+                               fprintf(stderr, _("Warning: setting "
+                                       "sector offset for DOS "
+                                       "compatiblity\n"));
+                       }
+                       update_units();
+                       break;
+               case 'v':
+                       verify();
+                       break;
+               case 'w':
+                       write_table();  /* does not return */
+                       break;
+               case 'y':
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       if (sun_label)
+                               sun_set_pcylcount();
+#endif
+                       break;
+               default:
+                       xmenu();
+               }
+       }
+}
+#endif /* ADVANCED mode */
+
+static int
+is_ide_cdrom_or_tape(const char *device) {
+       FILE *procf;
+       char buf[100];
+       struct stat statbuf;
+       int is_ide = 0;
+
+       /* No device was given explicitly, and we are trying some
+          likely things.  But opening /dev/hdc may produce errors like
+          "hdc: tray open or drive not ready"
+          if it happens to be a CD-ROM drive. It even happens that
+          the process hangs on the attempt to read a music CD.
+          So try to be careful. This only works since 2.1.73. */
+
+       if (strncmp("/dev/hd", device, 7))
+               return 0;
+
+       snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
+       procf = fopen(buf, "r");
+       if (procf != NULL && fgets(buf, sizeof(buf), procf))
+               is_ide = (!strncmp(buf, "cdrom", 5) ||
+                         !strncmp(buf, "tape", 4));
+       else
+               /* Now when this proc file does not exist, skip the
+                  device when it is read-only. */
+               if (stat(device, &statbuf) == 0)
+                       is_ide = ((statbuf.st_mode & 0222) == 0);
+
+       if (procf)
+               fclose(procf);
+       return is_ide;
+}
+
+static void
+try(const char *device, int user_specified) {
+       int gb;
+
+       disk_device = device;
+       if (setjmp(listingbuf))
+               return;
+       if (!user_specified)
+               if (is_ide_cdrom_or_tape(device))
+                       return;
+       if ((fd = open(disk_device, type_open)) >= 0) {
+               gb = get_boot(try_only);
+               if (gb > 0) {   /* I/O error */
+                       close(fd);
+               } else if (gb < 0) { /* no DOS signature */
+                       list_disk_geometry();
+                       if (aix_label)
+                               return;
+#ifdef CONFIG_FEATURE_OSF_LABEL
+                       if (btrydev(device) < 0)
+#endif
+                               fprintf(stderr,
+                                       _("Disk %s doesn't contain a valid "
+                                         "partition table\n"), device);
+                       close(fd);
+               } else {
+                       close(fd);
+                       list_table(0);
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+                       if (!sun_label && partitions > 4)
+                               delete_partition(ext_index);
+#endif
+               }
+       } else {
+               /* Ignore other errors, since we try IDE
+                  and SCSI hard disks which may not be
+                  installed on the system. */
+               if (errno == EACCES) {
+                       fprintf(stderr, _("Cannot open %s\n"), device);
+                       return;
+               }
+       }
+}
+
+/* for fdisk -l: try all things in /proc/partitions
+   that look like a partition name (do not end in a digit) */
+static void
+tryprocpt(void) {
+       FILE *procpt;
+       char line[100], ptname[100], devname[120], *s;
+       int ma, mi, sz;
+
+       procpt = bb_wfopen(PROC_PARTITIONS, "r");
+
+       while (fgets(line, sizeof(line), procpt)) {
+               if (sscanf (line, " %d %d %d %[^\n ]",
+                           &ma, &mi, &sz, ptname) != 4)
+                       continue;
+               for (s = ptname; *s; s++);
+               if (isdigit(s[-1]))
+                       continue;
+               sprintf(devname, "/dev/%s", ptname);
+               try(devname, 0);
+       }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       fclose(procpt);
+#endif
+}
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+static void
+unknown_command(int c) {
+       printf(_("%c: unknown command\n"), c);
+}
+#endif
+
+int fdisk_main(int argc, char **argv) {
+       int c;
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       int optl = 0;
+#endif
+#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
+       int opts = 0;
+#endif
+       /*
+        * Calls:
+        *  fdisk -v
+        *  fdisk -l [-b sectorsize] [-u] device ...
+        *  fdisk -s [partition] ...
+        *  fdisk [-b sectorsize] [-u] device
+        *
+        * Options -C, -H, -S set the geometry.
+        *
+        */
+       while ((c = getopt(argc, argv, "b:C:H:lS:uvV"
+#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
+                                       "s"
+#endif
+                                               )) != -1) {
+               switch (c) {
+               case 'b':
+                       /* Ugly: this sector size is really per device,
+                          so cannot be combined with multiple disks,
+                          and te same goes for the C/H/S options.
+                       */
+                       sector_size = atoi(optarg);
+                       if (sector_size != 512 && sector_size != 1024 &&
+                           sector_size != 2048)
+                               bb_show_usage();
+                       sector_offset = 2;
+                       user_set_sector_size = 1;
+                       break;
+               case 'C':
+                       user_cylinders = atoi(optarg);
+                       break;
+               case 'H':
+                       user_heads = atoi(optarg);
+                       if (user_heads <= 0 || user_heads >= 256)
+                               user_heads = 0;
+                       break;
+               case 'S':
+                       user_sectors = atoi(optarg);
+                       if (user_sectors <= 0 || user_sectors >= 64)
+                               user_sectors = 0;
+                       break;
+               case 'l':
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+                       optl = 1;
+#endif
+                       break;
+#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
+               case 's':
+                       opts = 1;
+                       break;
+#endif
+               case 'u':
+                       display_in_cyl_units = 0;
+                       break;
+               case 'V':
+               case 'v':
+                       printf("fdisk v" UTIL_LINUX_VERSION "\n");
+                       return 0;
+               default:
+                       bb_show_usage();
+               }
+       }
+
+#if 0
+       printf(_("This kernel finds the sector size itself - "
+                "-b option ignored\n"));
+#else
+       if (user_set_sector_size && argc-optind != 1)
+               printf(_("Warning: the -b (set sector size) option should"
+                        " be used with one specified device\n"));
+#endif
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       if (optl) {
+               nowarn = 1;
+#endif
+               type_open = O_RDONLY;
+               if (argc > optind) {
+                       int k;
+#if __GNUC__
+                       /* avoid gcc warning:
+                          variable `k' might be clobbered by `longjmp' */
+                       (void)&k;
+#endif
+                       listing = 1;
+                       for (k=optind; k<argc; k++)
+                               try(argv[k], 1);
+               } else {
+                       /* we no longer have default device names */
+                       /* but, we can use /proc/partitions instead */
+                       tryprocpt();
+               }
+               return 0;
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       }
+#endif
+
+#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
+       if (opts) {
+               long size;
+               int j;
+
+               nowarn = 1;
+               type_open = O_RDONLY;
+
+               opts = argc - optind;
+               if (opts <= 0)
+                       bb_show_usage();
+
+               for (j = optind; j < argc; j++) {
+                       disk_device = argv[j];
+                       if ((fd = open(disk_device, type_open)) < 0)
+                               fdisk_fatal(unable_to_open);
+                       if (ioctl(fd, BLKGETSIZE, &size))
+                               fdisk_fatal(ioctl_error);
+                       close(fd);
+                       if (opts == 1)
+                               printf("%ld\n", size/2);
+                       else
+                               printf("%s: %ld\n", argv[j], size/2);
+               }
+               return 0;
+       }
+#endif
+
+#ifdef CONFIG_FEATURE_FDISK_WRITABLE
+       if (argc-optind == 1)
+               disk_device = argv[optind];
+       else
+               bb_show_usage();
+
+       get_boot(fdisk);
+
+#ifdef CONFIG_FEATURE_OSF_LABEL
+       if (osf_label) {
+               /* OSF label, and no DOS label */
+               printf(_("Detected an OSF/1 disklabel on %s, entering "
+                        "disklabel mode.\n"),
+                      disk_device);
+               bselect();
+               osf_label = 0;
+               /* If we return we may want to make an empty DOS label? */
+       }
+#endif
+
+       while (1) {
+               putchar('\n');
+               c = tolower(read_char(_("Command (m for help): ")));
+               switch (c) {
+               case 'a':
+                       if (dos_label)
+                               toggle_active(get_partition(1, partitions));
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       else if (sun_label)
+                               toggle_sunflags(get_partition(1, partitions),
+                                               0x01);
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       else if (sgi_label)
+                               sgi_set_bootpartition(
+                                       get_partition(1, partitions));
+#endif
+                       else
+                               unknown_command(c);
+                       break;
+               case 'b':
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       if (sgi_label) {
+                               printf(_("\nThe current boot file is: %s\n"),
+                                      sgi_get_bootfile());
+                               if (read_chars(_("Please enter the name of the "
+                                              "new boot file: ")) == '\n')
+                                       printf(_("Boot file unchanged\n"));
+                               else
+                                       sgi_set_bootfile(line_ptr);
+                       } else
+#endif
+#ifdef CONFIG_FEATURE_OSF_LABEL
+                               bselect();
+#endif
+                       break;
+               case 'c':
+                       if (dos_label)
+                               toggle_dos_compatibility_flag();
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       else if (sun_label)
+                               toggle_sunflags(get_partition(1, partitions),
+                                               0x10);
+#endif
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       else if (sgi_label)
+                               sgi_set_swappartition(
+                                               get_partition(1, partitions));
+#endif
+                       else
+                               unknown_command(c);
+                       break;
+               case 'd':
+                       {
+                               int j;
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       /* If sgi_label then don't use get_existing_partition,
+                          let the user select a partition, since
+                          get_existing_partition() only works for Linux-like
+                          partition tables */
+                               if (!sgi_label) {
+                                       j = get_existing_partition(1, partitions);
+                               } else {
+                                       j = get_partition(1, partitions);
+                               }
+#else
+                               j = get_existing_partition(1, partitions);
+#endif
+                               if (j >= 0)
+                                       delete_partition(j);
+                       }
+                       break;
+               case 'i':
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       if (sgi_label)
+                               create_sgiinfo();
+                       else
+#endif
+                               unknown_command(c);
+               case 'l':
+                       list_types(get_sys_types());
+                       break;
+               case 'm':
+                       menu();
+                       break;
+               case 'n':
+                       new_partition();
+                       break;
+               case 'o':
+                       create_doslabel();
+                       break;
+               case 'p':
+                       list_table(0);
+                       break;
+               case 'q':
+                       close(fd);
+                       printf("\n");
+                       return 0;
+               case 's':
+#ifdef CONFIG_FEATURE_SUN_LABEL
+                       create_sunlabel();
+#endif
+                       break;
+               case 't':
+                       change_sysid();
+                       break;
+               case 'u':
+                       change_units();
+                       break;
+               case 'v':
+                       verify();
+                       break;
+               case 'w':
+                       write_table();          /* does not return */
+                       break;
+#ifdef CONFIG_FEATURE_FDISK_ADVANCED
+               case 'x':
+#ifdef CONFIG_FEATURE_SGI_LABEL
+                       if (sgi_label) {
+                               fprintf(stderr,
+                                       _("\n\tSorry, no experts menu for SGI "
+                                       "partition tables available.\n\n"));
+                       } else
+#endif
+
+                               xselect();
+                       break;
+#endif
+               default:
+                       unknown_command(c);
+                       menu();
+               }
+       }
+       return 0;
+#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
+}
diff --git a/busybox/util-linux/freeramdisk.c b/busybox/util-linux/freeramdisk.c
new file mode 100644 (file)
index 0000000..e5061dc
--- /dev/null
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * freeramdisk implementation for busybox
+ *
+ * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it>
+ * Adjusted a bit by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+
+/* From linux/fs.h */
+#define BLKFLSBUF  _IO(0x12,97)        /* flush buffer cache */
+
+extern int
+freeramdisk_main(int argc, char **argv)
+{
+       int result;
+       int fd;
+
+       if (argc != 2) {
+               bb_show_usage();
+       }
+
+       fd = bb_xopen(argv[1], O_RDWR);
+
+       result = ioctl(fd, BLKFLSBUF);
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       close(fd);
+#endif
+       if (result < 0) {
+               bb_perror_msg_and_die("failed ioctl on %s", argv[1]);
+       }
+
+       /* Don't bother closing.  Exit does
+        * that, so we can save a few bytes */
+       return EXIT_SUCCESS;
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
+
diff --git a/busybox/util-linux/fsck_minix.c b/busybox/util-linux/fsck_minix.c
new file mode 100644 (file)
index 0000000..f6d7dea
--- /dev/null
@@ -0,0 +1,1476 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
+ * as per the GNU copyleft.
+ */
+
+/*
+ * 09.11.91  -  made the first rudimentary functions
+ *
+ * 10.11.91  -  updated, does checking, no repairs yet.
+ *             Sent out to the mailing-list for testing.
+ *
+ * 14.11.91  - Testing seems to have gone well. Added some
+ *             correction-code, and changed some functions.
+ *
+ * 15.11.91  -  More correction code. Hopefully it notices most
+ *             cases now, and tries to do something about them.
+ *
+ * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
+ *             things seem to work now. Yeah, sure.
+ *
+ *
+ * 19.04.92  - Had to start over again from this old version, as a
+ *             kernel bug ate my enhanced fsck in february.
+ *
+ * 28.02.93  - added support for different directory entry sizes..
+ *
+ * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
+ *                           super-block information
+ *
+ * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
+ *                           to that required by fsutil
+ *
+ * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
+ *                           Added support for file system valid flag.  Also
+ *                           added program_version variable and output of
+ *                           program name and version number when program
+ *                           is executed.
+ *
+ * 30.10.94 - added support for v2 filesystem
+ *            (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 10.12.94  -  added test to prevent checking of mounted fs adapted
+ *              from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
+ *              program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such
+ *            for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
+ *
+ * 02.07.96  - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
+ *             (Russell King).  He made them for ARM.  It would seem
+ *            that the ARM is powerful enough to do this in C whereas
+ *             i386 and m64k must use assembly to get it fast >:-)
+ *            This should make minix fsck system-independent.
+ *            (janl@math.uio.no, Nicolai Langfeldt)
+ *
+ * 04.11.96  - Added minor fixes from Andreas Schwab to avoid compiler
+ *             warnings.  Added mc68k bitops from
+ *            Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
+ *
+ * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by
+ *             Andreas Schwab.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ *
+ * I've had no time to add comments - hopefully the function names
+ * are comments enough. As with all file system checkers, this assumes
+ * the file system is quiescent - don't use it on a mounted device
+ * unless you can be sure nobody is writing to it (and remember that the
+ * kernel can write to it when it searches for files).
+ *
+ * Usage: fsck [-larvsm] device
+ *     -l for a listing of all the filenames
+ *     -a for automatic repairs (not implemented)
+ *     -r for repairs (interactive) (not implemented)
+ *     -v for verbose (tells how many files)
+ *     -s for super-block info
+ *     -m for minix-like "mode not cleared" warnings
+ *     -f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <mntent.h>
+#include <sys/param.h>
+#include "busybox.h"
+
+static const int MINIX_ROOT_INO = 1;
+static const int MINIX_LINK_MAX = 250;
+static const int MINIX2_LINK_MAX = 65530;
+
+static const int MINIX_I_MAP_SLOTS = 8;
+static const int MINIX_Z_MAP_SLOTS = 64;
+static const int MINIX_SUPER_MAGIC = 0x137F;           /* original minix fs */
+static const int MINIX_SUPER_MAGIC2 = 0x138F;          /* minix fs, 30 char names */
+static const int MINIX2_SUPER_MAGIC = 0x2468;          /* minix V2 fs */
+static const int MINIX2_SUPER_MAGIC2 = 0x2478;         /* minix V2 fs, 30 char names */
+static const int MINIX_VALID_FS = 0x0001;              /* Clean fs. */
+static const int MINIX_ERROR_FS = 0x0002;              /* fs has errors. */
+
+#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+static const int MINIX_V1 = 0x0001;            /* original minix fs */
+static const int MINIX_V2 = 0x0002;            /* minix V2 fs */
+
+#define INODE_VERSION(inode)   inode->i_sb->u.minix_sb.s_version
+
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix_inode {
+       uint16_t i_mode;
+       uint16_t i_uid;
+       uint32_t i_size;
+       uint32_t i_time;
+       uint8_t  i_gid;
+       uint8_t  i_nlinks;
+       uint16_t i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct minix2_inode {
+       uint16_t i_mode;
+       uint16_t i_nlinks;
+       uint16_t i_uid;
+       uint16_t i_gid;
+       uint32_t i_size;
+       uint32_t i_atime;
+       uint32_t i_mtime;
+       uint32_t i_ctime;
+       uint32_t i_zone[10];
+};
+
+/*
+ * minix super-block data on disk
+ */
+struct minix_super_block {
+       uint16_t s_ninodes;
+       uint16_t s_nzones;
+       uint16_t s_imap_blocks;
+       uint16_t s_zmap_blocks;
+       uint16_t s_firstdatazone;
+       uint16_t s_log_zone_size;
+       uint32_t s_max_size;
+       uint16_t s_magic;
+       uint16_t s_state;
+       uint32_t s_zones;
+};
+
+struct minix_dir_entry {
+       uint16_t inode;
+       char name[0];
+};
+
+#define BLOCK_SIZE_BITS 10
+#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
+
+#define NAME_MAX         255   /* # chars in a file name */
+
+#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+
+#ifndef BLKGETSIZE
+#define BLKGETSIZE _IO(0x12,96)    /* return device size */
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+static const int ROOT_INO = 1;
+
+#define UPPER(size,n) ((size+((n)-1))/(n))
+#define INODE_SIZE (sizeof(struct minix_inode))
+#ifdef CONFIG_FEATURE_MINIX2
+#define INODE_SIZE2 (sizeof(struct minix2_inode))
+#define INODE_BLOCKS UPPER(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \
+                                   : MINIX_INODES_PER_BLOCK))
+#else
+#define INODE_BLOCKS UPPER(INODES, (MINIX_INODES_PER_BLOCK))
+#endif
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+
+#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
+
+static char *program_version = "1.2 - 11/11/96";
+static char *device_name = NULL;
+static int IN;
+static int repair = 0, automatic = 0, verbose = 0, list = 0, show =
+       0, warn_mode = 0, force = 0;
+static int directory = 0, regular = 0, blockdev = 0, chardev = 0, links =
+       0, symlinks = 0, total = 0;
+
+static int changed = 0;                        /* flags if the filesystem has been changed */
+static int errors_uncorrected = 0;     /* flag if some error was not corrected */
+static int dirsize = 16;
+static int namelen = 14;
+static int version2 = 0;
+static struct termios termios;
+static int termios_set = 0;
+
+/* File-name data */
+static const int MAX_DEPTH = 32;
+static int name_depth = 0;
+// static char name_list[MAX_DEPTH][BUFSIZ + 1];
+static char **name_list = NULL;
+
+static char *inode_buffer = NULL;
+
+#define Inode (((struct minix_inode *) inode_buffer)-1)
+#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
+static char super_block_buffer[BLOCK_SIZE];
+
+#define Super (*(struct minix_super_block *)super_block_buffer)
+#define INODES ((unsigned long)Super.s_ninodes)
+#ifdef CONFIG_FEATURE_MINIX2
+#define ZONES ((unsigned long)(version2 ? Super.s_zones : Super.s_nzones))
+#else
+#define ZONES ((unsigned long)(Super.s_nzones))
+#endif
+#define IMAPS ((unsigned long)Super.s_imap_blocks)
+#define ZMAPS ((unsigned long)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
+#define ZONESIZE ((unsigned long)Super.s_log_zone_size)
+#define MAXSIZE ((unsigned long)Super.s_max_size)
+#define MAGIC (Super.s_magic)
+#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
+
+static char *inode_map;
+static char *zone_map;
+
+static unsigned char *inode_count = NULL;
+static unsigned char *zone_count = NULL;
+
+static void recursive_check(unsigned int ino);
+#ifdef CONFIG_FEATURE_MINIX2
+static void recursive_check2(unsigned int ino);
+#endif
+
+static inline int bit(char * a,unsigned int i)
+{
+         return (a[i >> 3] & (1<<(i & 7))) != 0;
+}
+#define inode_in_use(x) (bit(inode_map,(x)))
+#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
+
+#define mark_inode(x) (setbit(inode_map,(x)),changed=1)
+#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
+
+#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1)
+#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1)
+
+static void leave(int) __attribute__ ((noreturn));
+static void leave(int status)
+{
+       if (termios_set)
+               tcsetattr(0, TCSANOW, &termios);
+       exit(status);
+}
+
+static void die(const char *str)
+{
+       bb_error_msg("%s", str);
+       leave(8);
+}
+
+/*
+ * This simply goes through the file-name data and prints out the
+ * current file.
+ */
+static void print_current_name(void)
+{
+       int i = 0;
+
+       while (i < name_depth)
+               printf("/%.*s", namelen, name_list[i++]);
+       if (i == 0)
+               printf("/");
+}
+
+static int ask(const char *string, int def)
+{
+       int c;
+
+       if (!repair) {
+               printf("\n");
+               errors_uncorrected = 1;
+               return 0;
+       }
+       if (automatic) {
+               printf("\n");
+               if (!def)
+                       errors_uncorrected = 1;
+               return def;
+       }
+       printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
+       for (;;) {
+               fflush(stdout);
+               if ((c = getchar()) == EOF) {
+                       if (!def)
+                               errors_uncorrected = 1;
+                       return def;
+               }
+               c = toupper(c);
+               if (c == 'Y') {
+                       def = 1;
+                       break;
+               } else if (c == 'N') {
+                       def = 0;
+                       break;
+               } else if (c == ' ' || c == '\n')
+                       break;
+       }
+       if (def)
+               printf("y\n");
+       else {
+               printf("n\n");
+               errors_uncorrected = 1;
+       }
+       return def;
+}
+
+/*
+ * Make certain that we aren't checking a filesystem that is on a
+ * mounted partition.  Code adapted from e2fsck, Copyright (C) 1993,
+ * 1994 Theodore Ts'o.  Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+       FILE *f;
+       struct mntent *mnt;
+       int cont;
+       int fd;
+
+       if ((f = setmntent(MOUNTED, "r")) == NULL)
+               return;
+       while ((mnt = getmntent(f)) != NULL)
+               if (strcmp(device_name, mnt->mnt_fsname) == 0)
+                       break;
+       endmntent(f);
+       if (!mnt)
+               return;
+
+       /*
+        * If the root is mounted read-only, then /etc/mtab is
+        * probably not correct; so we won't issue a warning based on
+        * it.
+        */
+       fd = open(MOUNTED, O_RDWR);
+       if (fd < 0 && errno == EROFS)
+               return;
+       else
+               close(fd);
+
+       printf("%s is mounted.   ", device_name);
+       if (isatty(0) && isatty(1))
+               cont = ask("Do you really want to continue", 0);
+       else
+               cont = 0;
+       if (!cont) {
+               printf("check aborted.\n");
+               exit(0);
+       }
+       return;
+}
+
+/*
+ * check_zone_nr checks to see that *nr is a valid zone nr. If it
+ * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
+ * if an error was corrected, and returns the zone (0 for no zone
+ * or a bad zone-number).
+ */
+static int check_zone_nr(unsigned short *nr, int *corrected)
+{
+       if (!*nr)
+               return 0;
+       if (*nr < FIRSTZONE)
+               printf("Zone nr < FIRSTZONE in file `");
+       else if (*nr >= ZONES)
+               printf("Zone nr >= ZONES in file `");
+       else
+               return *nr;
+       print_current_name();
+       printf("'.");
+       if (ask("Remove block", 1)) {
+               *nr = 0;
+               *corrected = 1;
+       }
+       return 0;
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static int check_zone_nr2(unsigned int *nr, int *corrected)
+{
+       if (!*nr)
+               return 0;
+       if (*nr < FIRSTZONE)
+               printf("Zone nr < FIRSTZONE in file `");
+       else if (*nr >= ZONES)
+               printf("Zone nr >= ZONES in file `");
+       else
+               return *nr;
+       print_current_name();
+       printf("'.");
+       if (ask("Remove block", 1)) {
+               *nr = 0;
+               *corrected = 1;
+       }
+       return 0;
+}
+#endif
+
+/*
+ * read-block reads block nr into the buffer at addr.
+ */
+static void read_block(unsigned int nr, char *addr)
+{
+       if (!nr) {
+               memset(addr, 0, BLOCK_SIZE);
+               return;
+       }
+       if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) {
+               printf("Read error: unable to seek to block in file '");
+               print_current_name();
+               printf("'\n");
+               memset(addr, 0, BLOCK_SIZE);
+               errors_uncorrected = 1;
+       } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
+               printf("Read error: bad block in file '");
+               print_current_name();
+               printf("'\n");
+               memset(addr, 0, BLOCK_SIZE);
+               errors_uncorrected = 1;
+       }
+}
+
+/*
+ * write_block writes block nr to disk.
+ */
+static void write_block(unsigned int nr, char *addr)
+{
+       if (!nr)
+               return;
+       if (nr < FIRSTZONE || nr >= ZONES) {
+               printf("Internal error: trying to write bad block\n"
+                          "Write request ignored\n");
+               errors_uncorrected = 1;
+               return;
+       }
+       if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET))
+               die("seek failed in write_block");
+       if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
+               printf("Write error: bad block in file '");
+               print_current_name();
+               printf("'\n");
+               errors_uncorrected = 1;
+       }
+}
+
+/*
+ * map-block calculates the absolute block nr of a block in a file.
+ * It sets 'changed' if the inode has needed changing, and re-writes
+ * any indirect blocks with errors.
+ */
+static int map_block(struct minix_inode *inode, unsigned int blknr)
+{
+       unsigned short ind[BLOCK_SIZE >> 1];
+       unsigned short dind[BLOCK_SIZE >> 1];
+       int blk_chg, block, result;
+
+       if (blknr < 7)
+               return check_zone_nr(inode->i_zone + blknr, &changed);
+       blknr -= 7;
+       if (blknr < 512) {
+               block = check_zone_nr(inode->i_zone + 7, &changed);
+               read_block(block, (char *) ind);
+               blk_chg = 0;
+               result = check_zone_nr(blknr + ind, &blk_chg);
+               if (blk_chg)
+                       write_block(block, (char *) ind);
+               return result;
+       }
+       blknr -= 512;
+       block = check_zone_nr(inode->i_zone + 8, &changed);
+       read_block(block, (char *) dind);
+       blk_chg = 0;
+       result = check_zone_nr(dind + (blknr / 512), &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) dind);
+       block = result;
+       read_block(block, (char *) ind);
+       blk_chg = 0;
+       result = check_zone_nr(ind + (blknr % 512), &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) ind);
+       return result;
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static int map_block2(struct minix2_inode *inode, unsigned int blknr)
+{
+       unsigned int ind[BLOCK_SIZE >> 2];
+       unsigned int dind[BLOCK_SIZE >> 2];
+       unsigned int tind[BLOCK_SIZE >> 2];
+       int blk_chg, block, result;
+
+       if (blknr < 7)
+               return check_zone_nr2(inode->i_zone + blknr, &changed);
+       blknr -= 7;
+       if (blknr < 256) {
+               block = check_zone_nr2(inode->i_zone + 7, &changed);
+               read_block(block, (char *) ind);
+               blk_chg = 0;
+               result = check_zone_nr2(blknr + ind, &blk_chg);
+               if (blk_chg)
+                       write_block(block, (char *) ind);
+               return result;
+       }
+       blknr -= 256;
+       if (blknr >= 256 * 256) {
+               block = check_zone_nr2(inode->i_zone + 8, &changed);
+               read_block(block, (char *) dind);
+               blk_chg = 0;
+               result = check_zone_nr2(dind + blknr / 256, &blk_chg);
+               if (blk_chg)
+                       write_block(block, (char *) dind);
+               block = result;
+               read_block(block, (char *) ind);
+               blk_chg = 0;
+               result = check_zone_nr2(ind + blknr % 256, &blk_chg);
+               if (blk_chg)
+                       write_block(block, (char *) ind);
+               return result;
+       }
+       blknr -= 256 * 256;
+       block = check_zone_nr2(inode->i_zone + 9, &changed);
+       read_block(block, (char *) tind);
+       blk_chg = 0;
+       result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) tind);
+       block = result;
+       read_block(block, (char *) dind);
+       blk_chg = 0;
+       result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) dind);
+       block = result;
+       read_block(block, (char *) ind);
+       blk_chg = 0;
+       result = check_zone_nr2(ind + blknr % 256, &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) ind);
+       return result;
+}
+#endif
+
+static void write_super_block(void)
+{
+       /*
+        * Set the state of the filesystem based on whether or not there
+        * are uncorrected errors.  The filesystem valid flag is
+        * unconditionally set if we get this far.
+        */
+       Super.s_state |= MINIX_VALID_FS;
+       if (errors_uncorrected)
+               Super.s_state |= MINIX_ERROR_FS;
+       else
+               Super.s_state &= ~MINIX_ERROR_FS;
+
+       if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+               die("seek failed in write_super_block");
+       if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE))
+               die("unable to write super-block");
+
+       return;
+}
+
+static void write_tables(void)
+{
+       write_super_block();
+
+       if (IMAPS * BLOCK_SIZE != write(IN, inode_map, IMAPS * BLOCK_SIZE))
+               die("Unable to write inode map");
+       if (ZMAPS * BLOCK_SIZE != write(IN, zone_map, ZMAPS * BLOCK_SIZE))
+               die("Unable to write zone map");
+       if (INODE_BUFFER_SIZE != write(IN, inode_buffer, INODE_BUFFER_SIZE))
+               die("Unable to write inodes");
+}
+
+static void get_dirsize(void)
+{
+       int block;
+       char blk[BLOCK_SIZE];
+       int size;
+
+#ifdef CONFIG_FEATURE_MINIX2
+       if (version2)
+               block = Inode2[ROOT_INO].i_zone[0];
+       else
+#endif
+               block = Inode[ROOT_INO].i_zone[0];
+       read_block(block, blk);
+       for (size = 16; size < BLOCK_SIZE; size <<= 1) {
+               if (strcmp(blk + size + 2, "..") == 0) {
+                       dirsize = size;
+                       namelen = size - 2;
+                       return;
+               }
+       }
+       /* use defaults */
+}
+
+static void read_superblock(void)
+{
+       if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+               die("seek failed");
+       if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE))
+               die("unable to read super block");
+       if (MAGIC == MINIX_SUPER_MAGIC) {
+               namelen = 14;
+               dirsize = 16;
+               version2 = 0;
+       } else if (MAGIC == MINIX_SUPER_MAGIC2) {
+               namelen = 30;
+               dirsize = 32;
+               version2 = 0;
+#ifdef CONFIG_FEATURE_MINIX2
+       } else if (MAGIC == MINIX2_SUPER_MAGIC) {
+               namelen = 14;
+               dirsize = 16;
+               version2 = 1;
+       } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
+               namelen = 30;
+               dirsize = 32;
+               version2 = 1;
+#endif
+       } else
+               die("bad magic number in super-block");
+       if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
+               die("Only 1k blocks/zones supported");
+       if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
+               die("bad s_imap_blocks field in super-block");
+       if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
+               die("bad s_zmap_blocks field in super-block");
+}
+
+static void read_tables(void)
+{
+       inode_map = xmalloc(IMAPS * BLOCK_SIZE);
+       zone_map = xmalloc(ZMAPS * BLOCK_SIZE);
+       memset(inode_map, 0, sizeof(inode_map));
+       memset(zone_map, 0, sizeof(zone_map));
+       inode_buffer = xmalloc(INODE_BUFFER_SIZE);
+       inode_count = xmalloc(INODES + 1);
+       zone_count = xmalloc(ZONES);
+       if (IMAPS * BLOCK_SIZE != read(IN, inode_map, IMAPS * BLOCK_SIZE))
+               die("Unable to read inode map");
+       if (ZMAPS * BLOCK_SIZE != read(IN, zone_map, ZMAPS * BLOCK_SIZE))
+               die("Unable to read zone map");
+       if (INODE_BUFFER_SIZE != read(IN, inode_buffer, INODE_BUFFER_SIZE))
+               die("Unable to read inodes");
+       if (NORM_FIRSTZONE != FIRSTZONE) {
+               printf("Warning: Firstzone != Norm_firstzone\n");
+               errors_uncorrected = 1;
+       }
+       get_dirsize();
+       if (show) {
+               printf("%ld inodes\n", INODES);
+               printf("%ld blocks\n", ZONES);
+               printf("Firstdatazone=%ld (%ld)\n", FIRSTZONE, NORM_FIRSTZONE);
+               printf("Zonesize=%d\n", BLOCK_SIZE << ZONESIZE);
+               printf("Maxsize=%ld\n", MAXSIZE);
+               printf("Filesystem state=%d\n", Super.s_state);
+               printf("namelen=%d\n\n", namelen);
+       }
+}
+
+static struct minix_inode *get_inode(unsigned int nr)
+{
+       struct minix_inode *inode;
+
+       if (!nr || nr > INODES)
+               return NULL;
+       total++;
+       inode = Inode + nr;
+       if (!inode_count[nr]) {
+               if (!inode_in_use(nr)) {
+                       printf("Inode %d marked not used, but used for file '", nr);
+                       print_current_name();
+                       printf("'\n");
+                       if (repair) {
+                               if (ask("Mark in use", 1))
+                                       mark_inode(nr);
+                       } else {
+                               errors_uncorrected = 1;
+                       }
+               }
+               if (S_ISDIR(inode->i_mode))
+                       directory++;
+               else if (S_ISREG(inode->i_mode))
+                       regular++;
+               else if (S_ISCHR(inode->i_mode))
+                       chardev++;
+               else if (S_ISBLK(inode->i_mode))
+                       blockdev++;
+               else if (S_ISLNK(inode->i_mode))
+                       symlinks++;
+               else if (S_ISSOCK(inode->i_mode));
+               else if (S_ISFIFO(inode->i_mode));
+               else {
+                       print_current_name();
+                       printf(" has mode %05o\n", inode->i_mode);
+               }
+
+       } else
+               links++;
+       if (!++inode_count[nr]) {
+               printf("Warning: inode count too big.\n");
+               inode_count[nr]--;
+               errors_uncorrected = 1;
+       }
+       return inode;
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static struct minix2_inode *get_inode2(unsigned int nr)
+{
+       struct minix2_inode *inode;
+
+       if (!nr || nr > INODES)
+               return NULL;
+       total++;
+       inode = Inode2 + nr;
+       if (!inode_count[nr]) {
+               if (!inode_in_use(nr)) {
+                       printf("Inode %d marked not used, but used for file '", nr);
+                       print_current_name();
+                       printf("'\n");
+                       if (repair) {
+                               if (ask("Mark in use", 1))
+                                       mark_inode(nr);
+                               else
+                                       errors_uncorrected = 1;
+                       }
+               }
+               if (S_ISDIR(inode->i_mode))
+                       directory++;
+               else if (S_ISREG(inode->i_mode))
+                       regular++;
+               else if (S_ISCHR(inode->i_mode))
+                       chardev++;
+               else if (S_ISBLK(inode->i_mode))
+                       blockdev++;
+               else if (S_ISLNK(inode->i_mode))
+                       symlinks++;
+               else if (S_ISSOCK(inode->i_mode));
+               else if (S_ISFIFO(inode->i_mode));
+               else {
+                       print_current_name();
+                       printf(" has mode %05o\n", inode->i_mode);
+               }
+       } else
+               links++;
+       if (!++inode_count[nr]) {
+               printf("Warning: inode count too big.\n");
+               inode_count[nr]--;
+               errors_uncorrected = 1;
+       }
+       return inode;
+}
+#endif
+
+static void check_root(void)
+{
+       struct minix_inode *inode = Inode + ROOT_INO;
+
+       if (!inode || !S_ISDIR(inode->i_mode))
+               die("root inode isn't a directory");
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void check_root2(void)
+{
+       struct minix2_inode *inode = Inode2 + ROOT_INO;
+
+       if (!inode || !S_ISDIR(inode->i_mode))
+               die("root inode isn't a directory");
+}
+#endif
+
+static int add_zone(unsigned short *znr, int *corrected)
+{
+       int result;
+       int block;
+
+       result = 0;
+       block = check_zone_nr(znr, corrected);
+       if (!block)
+               return 0;
+       if (zone_count[block]) {
+               printf("Block has been used before. Now in file `");
+               print_current_name();
+               printf("'.");
+               if (ask("Clear", 1)) {
+                       *znr = 0;
+                       block = 0;
+                       *corrected = 1;
+               }
+       }
+       if (!block)
+               return 0;
+       if (!zone_in_use(block)) {
+               printf("Block %d in file `", block);
+               print_current_name();
+               printf("' is marked not in use.");
+               if (ask("Correct", 1))
+                       mark_zone(block);
+       }
+       if (!++zone_count[block])
+               zone_count[block]--;
+       return block;
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static int add_zone2(unsigned int *znr, int *corrected)
+{
+       int result;
+       int block;
+
+       result = 0;
+       block = check_zone_nr2(znr, corrected);
+       if (!block)
+               return 0;
+       if (zone_count[block]) {
+               printf("Block has been used before. Now in file `");
+               print_current_name();
+               printf("'.");
+               if (ask("Clear", 1)) {
+                       *znr = 0;
+                       block = 0;
+                       *corrected = 1;
+               }
+       }
+       if (!block)
+               return 0;
+       if (!zone_in_use(block)) {
+               printf("Block %d in file `", block);
+               print_current_name();
+               printf("' is marked not in use.");
+               if (ask("Correct", 1))
+                       mark_zone(block);
+       }
+       if (!++zone_count[block])
+               zone_count[block]--;
+       return block;
+}
+#endif
+
+static void add_zone_ind(unsigned short *znr, int *corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, chg_blk = 0;
+       int block;
+
+       block = add_zone(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+               add_zone(i + (unsigned short *) blk, &chg_blk);
+       if (chg_blk)
+               write_block(block, blk);
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void add_zone_ind2(unsigned int *znr, int *corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, chg_blk = 0;
+       int block;
+
+       block = add_zone2(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i = 0; i < BLOCK_SIZE >> 2; i++)
+               add_zone2(i + (unsigned int *) blk, &chg_blk);
+       if (chg_blk)
+               write_block(block, blk);
+}
+#endif
+
+static void add_zone_dind(unsigned short *znr, int *corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, blk_chg = 0;
+       int block;
+
+       block = add_zone(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+               add_zone_ind(i + (unsigned short *) blk, &blk_chg);
+       if (blk_chg)
+               write_block(block, blk);
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void add_zone_dind2(unsigned int *znr, int *corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, blk_chg = 0;
+       int block;
+
+       block = add_zone2(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i = 0; i < BLOCK_SIZE >> 2; i++)
+               add_zone_ind2(i + (unsigned int *) blk, &blk_chg);
+       if (blk_chg)
+               write_block(block, blk);
+}
+
+static void add_zone_tind2(unsigned int *znr, int *corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, blk_chg = 0;
+       int block;
+
+       block = add_zone2(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i = 0; i < BLOCK_SIZE >> 2; i++)
+               add_zone_dind2(i + (unsigned int *) blk, &blk_chg);
+       if (blk_chg)
+               write_block(block, blk);
+}
+#endif
+
+static void check_zones(unsigned int i)
+{
+       struct minix_inode *inode;
+
+       if (!i || i > INODES)
+               return;
+       if (inode_count[i] > 1)         /* have we counted this file already? */
+               return;
+       inode = Inode + i;
+       if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
+               !S_ISLNK(inode->i_mode)) return;
+       for (i = 0; i < 7; i++)
+               add_zone(i + inode->i_zone, &changed);
+       add_zone_ind(7 + inode->i_zone, &changed);
+       add_zone_dind(8 + inode->i_zone, &changed);
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void check_zones2(unsigned int i)
+{
+       struct minix2_inode *inode;
+
+       if (!i || i > INODES)
+               return;
+       if (inode_count[i] > 1)         /* have we counted this file already? */
+               return;
+       inode = Inode2 + i;
+       if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
+               && !S_ISLNK(inode->i_mode))
+               return;
+       for (i = 0; i < 7; i++)
+               add_zone2(i + inode->i_zone, &changed);
+       add_zone_ind2(7 + inode->i_zone, &changed);
+       add_zone_dind2(8 + inode->i_zone, &changed);
+       add_zone_tind2(9 + inode->i_zone, &changed);
+}
+#endif
+
+static void check_file(struct minix_inode *dir, unsigned int offset)
+{
+       static char blk[BLOCK_SIZE];
+       struct minix_inode *inode;
+       int ino;
+       char *name;
+       int block;
+
+       block = map_block(dir, offset / BLOCK_SIZE);
+       read_block(block, blk);
+       name = blk + (offset % BLOCK_SIZE) + 2;
+       ino = *(unsigned short *) (name - 2);
+       if (ino > INODES) {
+               print_current_name();
+               printf(" contains a bad inode number for file '");
+               printf("%.*s'.", namelen, name);
+               if (ask(" Remove", 1)) {
+                       *(unsigned short *) (name - 2) = 0;
+                       write_block(block, blk);
+               }
+               ino = 0;
+       }
+       if (name_depth < MAX_DEPTH)
+               strncpy(name_list[name_depth], name, namelen);
+       name_depth++;
+       inode = get_inode(ino);
+       name_depth--;
+       if (!offset) {
+               if (!inode || strcmp(".", name)) {
+                       print_current_name();
+                       printf(": bad directory: '.' isn't first\n");
+                       errors_uncorrected = 1;
+               } else
+                       return;
+       }
+       if (offset == dirsize) {
+               if (!inode || strcmp("..", name)) {
+                       print_current_name();
+                       printf(": bad directory: '..' isn't second\n");
+                       errors_uncorrected = 1;
+               } else
+                       return;
+       }
+       if (!inode)
+               return;
+       if (name_depth < MAX_DEPTH)
+               strncpy(name_list[name_depth], name, namelen);
+       name_depth++;
+       if (list) {
+               if (verbose)
+                       printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+               print_current_name();
+               if (S_ISDIR(inode->i_mode))
+                       printf(":\n");
+               else
+                       printf("\n");
+       }
+       check_zones(ino);
+       if (inode && S_ISDIR(inode->i_mode))
+               recursive_check(ino);
+       name_depth--;
+       return;
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void check_file2(struct minix2_inode *dir, unsigned int offset)
+{
+       static char blk[BLOCK_SIZE];
+       struct minix2_inode *inode;
+       int ino;
+       char *name;
+       int block;
+
+       block = map_block2(dir, offset / BLOCK_SIZE);
+       read_block(block, blk);
+       name = blk + (offset % BLOCK_SIZE) + 2;
+       ino = *(unsigned short *) (name - 2);
+       if (ino > INODES) {
+               print_current_name();
+               printf(" contains a bad inode number for file '");
+               printf("%.*s'.", namelen, name);
+               if (ask(" Remove", 1)) {
+                       *(unsigned short *) (name - 2) = 0;
+                       write_block(block, blk);
+               }
+               ino = 0;
+       }
+       if (name_depth < MAX_DEPTH)
+               strncpy(name_list[name_depth], name, namelen);
+       name_depth++;
+       inode = get_inode2(ino);
+       name_depth--;
+       if (!offset) {
+               if (!inode || strcmp(".", name)) {
+                       print_current_name();
+                       printf(": bad directory: '.' isn't first\n");
+                       errors_uncorrected = 1;
+               } else
+                       return;
+       }
+       if (offset == dirsize) {
+               if (!inode || strcmp("..", name)) {
+                       print_current_name();
+                       printf(": bad directory: '..' isn't second\n");
+                       errors_uncorrected = 1;
+               } else
+                       return;
+       }
+       if (!inode)
+               return;
+       name_depth++;
+       if (list) {
+               if (verbose)
+                       printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+               print_current_name();
+               if (S_ISDIR(inode->i_mode))
+                       printf(":\n");
+               else
+                       printf("\n");
+       }
+       check_zones2(ino);
+       if (inode && S_ISDIR(inode->i_mode))
+               recursive_check2(ino);
+       name_depth--;
+       return;
+}
+#endif
+
+static void recursive_check(unsigned int ino)
+{
+       struct minix_inode *dir;
+       unsigned int offset;
+
+       dir = Inode + ino;
+       if (!S_ISDIR(dir->i_mode))
+               die("internal error");
+       if (dir->i_size < 2 * dirsize) {
+               print_current_name();
+               printf(": bad directory: size<32");
+               errors_uncorrected = 1;
+       }
+       for (offset = 0; offset < dir->i_size; offset += dirsize)
+               check_file(dir, offset);
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void recursive_check2(unsigned int ino)
+{
+       struct minix2_inode *dir;
+       unsigned int offset;
+
+       dir = Inode2 + ino;
+       if (!S_ISDIR(dir->i_mode))
+               die("internal error");
+       if (dir->i_size < 2 * dirsize) {
+               print_current_name();
+               printf(": bad directory: size < 32");
+               errors_uncorrected = 1;
+       }
+       for (offset = 0; offset < dir->i_size; offset += dirsize)
+               check_file2(dir, offset);
+}
+#endif
+
+static int bad_zone(int i)
+{
+       char buffer[1024];
+
+       if (BLOCK_SIZE * i != lseek(IN, BLOCK_SIZE * i, SEEK_SET))
+               die("seek failed in bad_zone");
+       return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
+}
+
+static void check_counts(void)
+{
+       int i;
+
+       for (i = 1; i <= INODES; i++) {
+               if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
+                       printf("Inode %d mode not cleared.", i);
+                       if (ask("Clear", 1)) {
+                               Inode[i].i_mode = 0;
+                               changed = 1;
+                       }
+               }
+               if (!inode_count[i]) {
+                       if (!inode_in_use(i))
+                               continue;
+                       printf("Inode %d not used, marked used in the bitmap.", i);
+                       if (ask("Clear", 1))
+                               unmark_inode(i);
+                       continue;
+               }
+               if (!inode_in_use(i)) {
+                       printf("Inode %d used, marked unused in the bitmap.", i);
+                       if (ask("Set", 1))
+                               mark_inode(i);
+               }
+               if (Inode[i].i_nlinks != inode_count[i]) {
+                       printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
+                                  i, Inode[i].i_mode, Inode[i].i_nlinks, inode_count[i]);
+                       if (ask("Set i_nlinks to count", 1)) {
+                               Inode[i].i_nlinks = inode_count[i];
+                               changed = 1;
+                       }
+               }
+       }
+       for (i = FIRSTZONE; i < ZONES; i++) {
+               if (zone_in_use(i) == zone_count[i])
+                       continue;
+               if (!zone_count[i]) {
+                       if (bad_zone(i))
+                               continue;
+                       printf("Zone %d: marked in use, no file uses it.", i);
+                       if (ask("Unmark", 1))
+                               unmark_zone(i);
+                       continue;
+               }
+               printf("Zone %d: %sin use, counted=%d\n",
+                          i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+       }
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void check_counts2(void)
+{
+       int i;
+
+       for (i = 1; i <= INODES; i++) {
+               if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) {
+                       printf("Inode %d mode not cleared.", i);
+                       if (ask("Clear", 1)) {
+                               Inode2[i].i_mode = 0;
+                               changed = 1;
+                       }
+               }
+               if (!inode_count[i]) {
+                       if (!inode_in_use(i))
+                               continue;
+                       printf("Inode %d not used, marked used in the bitmap.", i);
+                       if (ask("Clear", 1))
+                               unmark_inode(i);
+                       continue;
+               }
+               if (!inode_in_use(i)) {
+                       printf("Inode %d used, marked unused in the bitmap.", i);
+                       if (ask("Set", 1))
+                               mark_inode(i);
+               }
+               if (Inode2[i].i_nlinks != inode_count[i]) {
+                       printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
+                                  i, Inode2[i].i_mode, Inode2[i].i_nlinks,
+                                  inode_count[i]);
+                       if (ask("Set i_nlinks to count", 1)) {
+                               Inode2[i].i_nlinks = inode_count[i];
+                               changed = 1;
+                       }
+               }
+       }
+       for (i = FIRSTZONE; i < ZONES; i++) {
+               if (zone_in_use(i) == zone_count[i])
+                       continue;
+               if (!zone_count[i]) {
+                       if (bad_zone(i))
+                               continue;
+                       printf("Zone %d: marked in use, no file uses it.", i);
+                       if (ask("Unmark", 1))
+                               unmark_zone(i);
+                       continue;
+               }
+               printf("Zone %d: %sin use, counted=%d\n",
+                          i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+       }
+}
+#endif
+
+static void check(void)
+{
+       memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+       memset(zone_count, 0, ZONES * sizeof(*zone_count));
+       check_zones(ROOT_INO);
+       recursive_check(ROOT_INO);
+       check_counts();
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static void check2(void)
+{
+       memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+       memset(zone_count, 0, ZONES * sizeof(*zone_count));
+       check_zones2(ROOT_INO);
+       recursive_check2(ROOT_INO);
+       check_counts2();
+}
+#endif
+
+/* Wed Feb  9 15:17:06 MST 2000 */
+/* dynamically allocate name_list (instead of making it static) */
+static void alloc_name_list(void)
+{
+       int i;
+
+       name_list = xmalloc(sizeof(char *) * MAX_DEPTH);
+       for (i = 0; i < MAX_DEPTH; i++)
+               name_list[i] = xmalloc(sizeof(char) * BUFSIZ + 1);
+}
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+/* execute this atexit() to deallocate name_list[] */
+/* piptigger was here */
+static void free_name_list(void)
+{
+       int i;
+
+       if (name_list) {
+               for (i = 0; i < MAX_DEPTH; i++) {
+                       free(name_list[i]);
+               }
+               free(name_list);
+       }
+}
+#endif
+
+extern int fsck_minix_main(int argc, char **argv)
+{
+       struct termios tmp;
+       int count;
+       int retcode = 0;
+
+       alloc_name_list();
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       /* Don't bother to free memory.  Exit does
+        * that automagically, so we can save a few bytes */
+       atexit(free_name_list);
+#endif
+
+       if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
+               die("bad inode size");
+#ifdef CONFIG_FEATURE_MINIX2
+       if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
+               die("bad v2 inode size");
+#endif
+       while (argc-- > 1) {
+               argv++;
+               if (argv[0][0] != '-') {
+                       if (device_name)
+                               bb_show_usage();
+                       else
+                               device_name = argv[0];
+               } else
+                       while (*++argv[0])
+                               switch (argv[0][0]) {
+                               case 'l':
+                                       list = 1;
+                                       break;
+                               case 'a':
+                                       automatic = 1;
+                                       repair = 1;
+                                       break;
+                               case 'r':
+                                       automatic = 0;
+                                       repair = 1;
+                                       break;
+                               case 'v':
+                                       verbose = 1;
+                                       break;
+                               case 's':
+                                       show = 1;
+                                       break;
+                               case 'm':
+                                       warn_mode = 1;
+                                       break;
+                               case 'f':
+                                       force = 1;
+                                       break;
+                               default:
+                                       bb_show_usage();
+                               }
+       }
+       if (!device_name)
+               bb_show_usage();
+       check_mount();                          /* trying to check a mounted filesystem? */
+       if (repair && !automatic) {
+               if (!isatty(0) || !isatty(1))
+                       die("need terminal for interactive repairs");
+       }
+       IN = open(device_name, repair ? O_RDWR : O_RDONLY);
+       if (IN < 0){
+               fprintf(stderr,"unable to open device '%s'.\n",device_name);
+               leave(8);
+       }
+       for (count = 0; count < 3; count++)
+               sync();
+       read_superblock();
+
+       /*
+        * Determine whether or not we should continue with the checking.
+        * This is based on the status of the filesystem valid and error
+        * flags and whether or not the -f switch was specified on the
+        * command line.
+        */
+       printf("%s, %s\n", bb_applet_name, program_version);
+       if (!(Super.s_state & MINIX_ERROR_FS) &&
+               (Super.s_state & MINIX_VALID_FS) && !force) {
+               if (repair)
+                       printf("%s is clean, no check.\n", device_name);
+               return retcode;
+       } else if (force)
+               printf("Forcing filesystem check on %s.\n", device_name);
+       else if (repair)
+               printf("Filesystem on %s is dirty, needs checking.\n",
+                          device_name);
+
+       read_tables();
+
+       if (repair && !automatic) {
+               tcgetattr(0, &termios);
+               tmp = termios;
+               tmp.c_lflag &= ~(ICANON | ECHO);
+               tcsetattr(0, TCSANOW, &tmp);
+               termios_set = 1;
+       }
+#ifdef CONFIG_FEATURE_MINIX2
+       if (version2) {
+               check_root2();
+               check2();
+       } else
+#endif
+       {
+               check_root();
+               check();
+       }
+       if (verbose) {
+               int i, free_cnt;
+
+               for (i = 1, free_cnt = 0; i <= INODES; i++)
+                       if (!inode_in_use(i))
+                               free_cnt++;
+               printf("\n%6ld inodes used (%ld%%)\n", (INODES - free_cnt),
+                          100 * (INODES - free_cnt) / INODES);
+               for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
+                       if (!zone_in_use(i))
+                               free_cnt++;
+               printf("%6ld zones used (%ld%%)\n", (ZONES - free_cnt),
+                          100 * (ZONES - free_cnt) / ZONES);
+               printf("\n%6d regular files\n"
+                          "%6d directories\n"
+                          "%6d character device files\n"
+                          "%6d block device files\n"
+                          "%6d links\n"
+                          "%6d symbolic links\n"
+                          "------\n"
+                          "%6d files\n",
+                          regular, directory, chardev, blockdev,
+                          links - 2 * directory + 1, symlinks,
+                          total - 2 * directory + 1);
+       }
+       if (changed) {
+               write_tables();
+               printf("----------------------------\n"
+                          "FILE SYSTEM HAS BEEN CHANGED\n"
+                          "----------------------------\n");
+               for (count = 0; count < 3; count++)
+                       sync();
+       } else if (repair)
+               write_super_block();
+
+       if (repair && !automatic)
+               tcsetattr(0, TCSANOW, &termios);
+
+       if (changed)
+               retcode += 3;
+       if (errors_uncorrected)
+               retcode += 4;
+       return retcode;
+}
diff --git a/busybox/util-linux/getopt.c b/busybox/util-linux/getopt.c
new file mode 100644 (file)
index 0000000..032d0dc
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * getopt.c - Enhanced implementation of BSD getopt(1)
+ *   Copyright (c) 1997, 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 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.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ *   Bumped up the version number to 1.0
+ *   Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ *   Fixed sizeof instead of strlen bug
+ *   Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ *   Fixed gcc-2.8.1 warnings
+ *   Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ *   Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ *   No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ *   Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
+ *     <misiek@misiek.eu.org>)
+ * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
+ *  Removed --version/-V and --help/-h in
+ *  Removed parse_error(), using bb_error_msg() from Busybox instead
+ *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#include "busybox.h"
+
+/* NON_OPT is the code that is returned when a non-option is found in '+'
+   mode */
+static const int NON_OPT = 1;
+/* LONG_OPT is the code that is returned when a long option is found. */
+static const int LONG_OPT = 2;
+
+/* The shells recognized. */
+typedef enum {BASH,TCSH} shell_t;
+
+
+/* Some global variables that tells us how to parse. */
+static shell_t shell=BASH; /* The shell we generate output for. */
+static int quiet_errors=0; /* 0 is not quiet. */
+static int quiet_output=0; /* 0 is not quiet. */
+static int quote=1; /* 1 is do quote. */
+static int alternative=0; /* 0 is getopt_long, 1 is getopt_long_only */
+
+/* Function prototypes */
+static const char *normalize(const char *arg);
+static int generate_output(char * argv[],int argc,const char *optstr,
+                    const struct option *longopts);
+static void add_long_options(char *options);
+static void add_longopt(const char *name,int has_arg);
+static void set_shell(const char *new_shell);
+
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes around
+ * it and escapes other special characters. If quote is false, it just
+ * returns its argument.
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace.
+ * This function returns a pointer to a buffer that is overwritten by
+ * each call.
+ */
+const char *normalize(const char *arg)
+{
+        static char *BUFFER=NULL;
+        const char *argptr=arg;
+        char *bufptr;
+
+        free(BUFFER);
+
+        if (!quote) { /* Just copy arg */
+               BUFFER=bb_xstrdup(arg);
+                return BUFFER;
+        }
+
+        /* Each character in arg may take up to four characters in the result:
+           For a quote we need a closing quote, a backslash, a quote and an
+           opening quote! We need also the global opening and closing quote,
+           and one extra character for '\0'. */
+        BUFFER=xmalloc(strlen(arg)*4+3);
+
+        bufptr=BUFFER;
+        *bufptr++='\'';
+
+        while (*argptr) {
+                if (*argptr == '\'') {
+                        /* Quote: replace it with: '\'' */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++='\'';
+                        *bufptr++='\'';
+                } else if (shell==TCSH && *argptr=='!') {
+                        /* Exclamation mark: replace it with: \! */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++='!';
+                        *bufptr++='\'';
+                } else if (shell==TCSH && *argptr=='\n') {
+                        /* Newline: replace it with: \n */
+                        *bufptr++='\\';
+                        *bufptr++='n';
+                } else if (shell==TCSH && isspace(*argptr)) {
+                        /* Non-newline whitespace: replace it with \<ws> */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++=*argptr;
+                        *bufptr++='\'';
+                } else
+                        /* Just copy */
+                        *bufptr++=*argptr;
+                argptr++;
+        }
+        *bufptr++='\'';
+        *bufptr++='\0';
+        return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+int generate_output(char * argv[],int argc,const char *optstr,
+                    const struct option *longopts)
+{
+        int exit_code = 0; /* We assume everything will be OK */
+        int opt;
+        int longindex;
+        const char *charptr;
+
+        if (quiet_errors) /* No error reporting from getopt(3) */
+                opterr=0;
+        optind=0; /* Reset getopt(3) */
+
+        while ((opt = (alternative?
+                       getopt_long_only(argc,argv,optstr,longopts,&longindex):
+                       getopt_long(argc,argv,optstr,longopts,&longindex)))
+               != EOF)
+                if (opt == '?' || opt == ':' )
+                        exit_code = 1;
+                else if (!quiet_output) {
+                        if (opt == LONG_OPT) {
+                                printf(" --%s",longopts[longindex].name);
+                                if (longopts[longindex].has_arg)
+                                        printf(" %s",
+                                               normalize(optarg?optarg:""));
+                        } else if (opt == NON_OPT)
+                                printf(" %s",normalize(optarg));
+                        else {
+                                printf(" -%c",opt);
+                                charptr = strchr(optstr,opt);
+                                if (charptr != NULL && *++charptr == ':')
+                                        printf(" %s",
+                                               normalize(optarg?optarg:""));
+                        }
+                }
+
+        if (! quiet_output) {
+                printf(" --");
+                while (optind < argc)
+                        printf(" %s",normalize(argv[optind++]));
+                printf("\n");
+        }
+        return exit_code;
+}
+
+static struct option *long_options=NULL;
+static int long_options_length=0; /* Length of array */
+static int long_options_nr=0; /* Nr of used elements in array */
+static const int LONG_OPTIONS_INCR = 10;
+#define init_longopt() add_longopt(NULL,0)
+
+/* Register a long option. The contents of name is copied. */
+void add_longopt(const char *name,int has_arg)
+{
+        if (!name) { /* init */
+                free(long_options);
+                long_options=NULL;
+                long_options_length=0;
+                long_options_nr=0;
+        }
+
+        if (long_options_nr == long_options_length) {
+                long_options_length += LONG_OPTIONS_INCR;
+                long_options=xrealloc(long_options,
+                                         sizeof(struct option) *
+                                         long_options_length);
+        }
+
+        long_options[long_options_nr].name=NULL;
+        long_options[long_options_nr].has_arg=0;
+        long_options[long_options_nr].flag=NULL;
+        long_options[long_options_nr].val=0;
+
+        if (long_options_nr) { /* Not for init! */
+                long_options[long_options_nr-1].has_arg=has_arg;
+                long_options[long_options_nr-1].flag=NULL;
+                long_options[long_options_nr-1].val=LONG_OPT;
+               long_options[long_options_nr-1].name=bb_xstrdup(name);
+        }
+        long_options_nr++;
+}
+
+
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace.
+ * This nukes options!
+ */
+void add_long_options(char *options)
+{
+        int arg_opt, tlen;
+        char *tokptr=strtok(options,", \t\n");
+        while (tokptr) {
+                arg_opt=no_argument;
+               tlen=strlen(tokptr);
+                if (tlen > 0) {
+                        if (tokptr[tlen-1] == ':') {
+                                if (tlen > 1 && tokptr[tlen-2] == ':') {
+                                        tokptr[tlen-2]='\0';
+                                       tlen -= 2;
+                                        arg_opt=optional_argument;
+                                } else {
+                                        tokptr[tlen-1]='\0';
+                                       tlen -= 1;
+                                        arg_opt=required_argument;
+                                }
+                                if (tlen == 0)
+                                        bb_error_msg("empty long option after -l or --long argument");
+                        }
+                        add_longopt(tokptr,arg_opt);
+                }
+                tokptr=strtok(NULL,", \t\n");
+        }
+}
+
+void set_shell(const char *new_shell)
+{
+        if (!strcmp(new_shell,"bash"))
+                shell=BASH;
+        else if (!strcmp(new_shell,"tcsh"))
+                shell=TCSH;
+        else if (!strcmp(new_shell,"sh"))
+                shell=BASH;
+        else if (!strcmp(new_shell,"csh"))
+                shell=TCSH;
+        else
+                bb_error_msg("unknown shell after -s or --shell argument");
+}
+
+
+/* Exit codes:
+ *   0) No errors, successful operation.
+ *   1) getopt(3) returned an error.
+ *   2) A problem with parameter parsing for getopt(1).
+ *   3) Internal error, out of memory
+ *   4) Returned for -T
+ */
+
+static struct option longopts[]=
+{
+        {"options",required_argument,NULL,'o'},
+        {"longoptions",required_argument,NULL,'l'},
+        {"quiet",no_argument,NULL,'q'},
+        {"quiet-output",no_argument,NULL,'Q'},
+        {"shell",required_argument,NULL,'s'},
+        {"test",no_argument,NULL,'T'},
+        {"unquoted",no_argument,NULL,'u'},
+        {"alternative",no_argument,NULL,'a'},
+        {"name",required_argument,NULL,'n'},
+        {NULL,0,NULL,0}
+};
+
+/* Stop scanning as soon as a non-option argument is found! */
+static const char *shortopts="+ao:l:n:qQs:Tu";
+
+
+int getopt_main(int argc, char *argv[])
+{
+        const char *optstr = NULL;
+        const char *name = NULL;
+        int opt;
+        int compatible=0;
+
+        init_longopt();
+
+        if (getenv("GETOPT_COMPATIBLE"))
+                compatible=1;
+
+        if (argc == 1) {
+                if (compatible) {
+                        /* For some reason, the original getopt gave no error
+                           when there were no arguments. */
+                        printf(" --\n");
+                       return 0;
+                } else
+                        bb_error_msg_and_die("missing optstring argument");
+        }
+
+        if (argv[1][0] != '-' || compatible) {
+                quote=0;
+                optstr=xmalloc(strlen(argv[1])+1);
+                strcpy(optstr,argv[1]+strspn(argv[1],"-+"));
+                argv[1]=argv[0];
+               return (generate_output(argv+1,argc-1,optstr,long_options));
+        }
+
+        while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
+                switch (opt) {
+                case 'a':
+                        alternative=1;
+                        break;
+                case 'o':
+                       free(optstr);
+                       optstr = optarg;
+                        break;
+                case 'l':
+                        add_long_options(optarg);
+                        break;
+                case 'n':
+                       free(name);
+                       name = optarg;
+                        break;
+                case 'q':
+                        quiet_errors=1;
+                        break;
+                case 'Q':
+                        quiet_output=1;
+                        break;
+                case 's':
+                        set_shell(optarg);
+                        break;
+                case 'T':
+                       return 4;
+                case 'u':
+                        quote=0;
+                        break;
+                default:
+                        bb_show_usage();
+                }
+
+        if (!optstr) {
+                if (optind >= argc)
+                        bb_error_msg_and_die("missing optstring argument");
+                else {
+                       optstr=bb_xstrdup(argv[optind]);
+                        optind++;
+                }
+        }
+        if (name)
+                argv[optind-1]=name;
+        else
+                argv[optind-1]=argv[0];
+       return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
+}
+
+/*
+  Local Variables:
+  c-file-style: "linux"
+  c-basic-offset: 4
+  tab-width: 4
+  End:
+*/
diff --git a/busybox/util-linux/hexdump.c b/busybox/util-linux/hexdump.c
new file mode 100644 (file)
index 0000000..1858b08
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * hexdump implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include "busybox.h"
+#include "dump.h"
+
+static void bb_dump_addfile(char *name)
+{
+       register char *p;
+       FILE *fp;
+       char *buf;
+
+       fp = bb_xfopen(name, "r");
+
+       while ((buf = bb_get_chomped_line_from_file(fp)) != NULL) {
+               p = (char *) bb_skip_whitespace(buf);
+
+               if (*p && (*p != '#')) {
+                       bb_dump_add(p);
+               }
+               free(buf);
+       }
+       fclose(fp);
+}
+
+static const char * const add_strings[] = {
+                       "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"",         /* b */
+                       "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"",         /* c */
+                       "\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\"",        /* d */
+                       "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"",         /* o */
+                       "\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\"",       /* x */
+};
+
+static const char add_first[] = "\"%07.7_Ax\n\"";
+
+static const char hexdump_opts[] = "bcdoxe:f:n:s:v";
+
+static const struct suffix_mult suffixes[] = {
+       {"b",  512 },
+       {"k",  1024 },
+       {"m",  1024*1024 },
+       {NULL, 0 }
+};
+
+int hexdump_main(int argc, char **argv)
+{
+//     register FS *tfs;
+       const char *p;
+       int ch;
+
+       bb_dump_vflag = FIRST;
+       bb_dump_length = -1;
+
+       while ((ch = getopt(argc, argv, hexdump_opts)) > 0) {
+               if ((p = strchr(hexdump_opts, ch)) != NULL) {
+                       if ((p - hexdump_opts) < 5) {
+                               bb_dump_add(add_first);
+                               bb_dump_add(add_strings[(int)(p - hexdump_opts)]);
+                       } else {
+                               /* Sae a little bit of space below by omitting the 'else's. */
+                               if (ch == 'e') {
+                                       bb_dump_add(optarg);
+                               } /* else */
+                               if (ch == 'f') {
+                                       bb_dump_addfile(optarg);
+                               } /* else */
+                               if (ch == 'n') {
+                                       bb_dump_length = bb_xgetularg10_bnd(optarg, 0, INT_MAX);
+                               } /* else */
+                               if (ch == 's') {
+                                       bb_dump_skip = bb_xgetularg_bnd_sfx(optarg, 10, 0, LONG_MAX, suffixes);
+                               } /* else */
+                               if (ch == 'v') {
+                                       bb_dump_vflag = ALL;
+                               }
+                       }
+               } else {
+                       bb_show_usage();
+               }
+       }
+
+       if (!bb_dump_fshead) {
+               bb_dump_add(add_first);
+               bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+       }
+
+       argv += optind;
+
+       return(bb_dump_dump(argv));
+}
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/busybox/util-linux/hwclock.c b/busybox/util-linux/hwclock.c
new file mode 100644 (file)
index 0000000..a260d74
--- /dev/null
@@ -0,0 +1,230 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hwclock implementation for busybox
+ *
+ * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include "busybox.h"
+
+/* Copied from linux/rtc.h to eliminate the kernel dependency */
+struct linux_rtc_time {
+       int tm_sec;
+       int tm_min;
+       int tm_hour;
+       int tm_mday;
+       int tm_mon;
+       int tm_year;
+       int tm_wday;
+       int tm_yday;
+       int tm_isdst;
+};
+                
+#define RTC_SET_TIME   _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
+#define RTC_RD_TIME    _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */
+
+#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE
+# endif
+#endif
+
+static time_t read_rtc(int utc)
+{
+       int rtc;
+       struct tm tm;
+       char *oldtz = 0;
+       time_t t = 0;
+
+       if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
+               if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 )
+                       bb_perror_msg_and_die ( "Could not access RTC" );
+       }
+       memset ( &tm, 0, sizeof( struct tm ));
+       if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
+               bb_perror_msg_and_die ( "Could not read time from RTC" );
+       tm. tm_isdst = -1; // not known
+
+       close ( rtc );
+
+       if ( utc ) {
+               oldtz = getenv ( "TZ" );
+               setenv ( "TZ", "UTC 0", 1 );
+               tzset ( );
+       }
+
+       t = mktime ( &tm );
+
+       if ( utc ) {
+               if ( oldtz )
+                       setenv ( "TZ", oldtz, 1 );
+               else
+                       unsetenv ( "TZ" );
+               tzset ( );
+       }
+       return t;
+}
+
+static void write_rtc(time_t t, int utc)
+{
+       int rtc;
+       struct tm tm;
+
+       if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
+               if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 )
+                       bb_perror_msg_and_die ( "Could not access RTC" );
+       }
+
+       tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
+       tm. tm_isdst = 0;
+
+       if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
+               bb_perror_msg_and_die ( "Could not set the RTC time" );
+
+       close ( rtc );
+}
+
+static int show_clock(int utc)
+{
+       struct tm *ptm;
+       time_t t;
+       char buffer [64];
+
+       t = read_rtc ( utc );
+       ptm = localtime ( &t );  /* Sets 'tzname[]' */
+
+       safe_strncpy ( buffer, ctime ( &t ), sizeof( buffer ));
+       if ( buffer [0] )
+               buffer [bb_strlen ( buffer ) - 1] = 0;
+
+       //printf ( "%s  %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] ));
+       printf ( "%s  %.6f seconds\n", buffer, 0.0 );
+
+       return 0;
+}
+
+static int to_sys_clock(int utc)
+{
+       struct timeval tv = { 0, 0 };
+       const struct timezone tz = { timezone/60 - 60*daylight, 0 };
+
+       tv. tv_sec = read_rtc ( utc );
+
+       if ( settimeofday ( &tv, &tz ))
+               bb_perror_msg_and_die ( "settimeofday() failed" );
+
+       return 0;
+}
+
+static int from_sys_clock(int utc)
+{
+       struct timeval tv = { 0, 0 };
+       struct timezone tz = { 0, 0 };
+
+       if ( gettimeofday ( &tv, &tz ))
+               bb_perror_msg_and_die ( "gettimeofday() failed" );
+
+       write_rtc ( tv. tv_sec, utc );
+       return 0;
+}
+
+
+static int check_utc(void)
+{
+       int utc = 0;
+       FILE *f = fopen ( "/var/lib/hwclock/adjtime", "r" );
+
+       if ( f ) {
+               char buffer [128];
+
+               while ( fgets ( buffer, sizeof( buffer ), f )) {
+                       int len = bb_strlen ( buffer );
+
+                       while ( len && isspace ( buffer [len - 1] ))
+                               len--;
+
+                       buffer [len] = 0;
+
+                       if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
+                               utc = 1;
+                               break;
+                       }
+               }
+               fclose ( f );
+       }
+       return utc;
+}
+
+#define HWCLOCK_OPT_LOCALTIME  1
+#define HWCLOCK_OPT_UTC        2
+#define HWCLOCK_OPT_SHOW       4
+#define HWCLOCK_OPT_HCTOSYS    8
+#define HWCLOCK_OPT_SYSTOHC    16
+
+extern int hwclock_main ( int argc, char **argv )
+{
+       unsigned long opt;
+       int utc;
+
+#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
+static const struct option hwclock_long_options[] = {
+               { "localtime", 0, 0, 'l' },
+               { "utc",       0, 0, 'u' },
+               { "show",      0, 0, 'r' },
+               { "hctosys",   0, 0, 's' },
+               { "systohc",   0, 0, 'w' },
+               { 0,           0, 0, 0 }
+       };
+       bb_applet_long_options = hwclock_long_options;
+#endif
+
+       bb_opt_complementaly = "r~ws:w~rs:s~wr:l~u:u~l";
+       opt = bb_getopt_ulflags(argc, argv, "lursw");
+       /* Check only one mode was given */
+       if(opt & 0x80000000UL) {
+               bb_show_usage();
+       }
+
+       /* If -u or -l wasn't given check if we are using utc */
+       if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) 
+               utc = opt & HWCLOCK_OPT_UTC;
+       else
+               utc = check_utc();
+       
+       if (opt & HWCLOCK_OPT_HCTOSYS) {
+               return to_sys_clock ( utc );
+       }
+       else if (opt & HWCLOCK_OPT_SYSTOHC) {
+               return from_sys_clock ( utc );
+       } else {
+               /* default HWCLOCK_OPT_SHOW */
+               return show_clock ( utc );
+       }
+}
diff --git a/busybox/util-linux/losetup.c b/busybox/util-linux/losetup.c
new file mode 100644 (file)
index 0000000..c944565
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Mini losetup implementation for busybox
+ *
+ * Copyright (C) 2002  Matt Kraai.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+
+#include "busybox.h"
+
+int
+losetup_main (int argc, char **argv)
+{
+  int delete = 0;
+  int offset = 0;
+  int opt;
+
+  while ((opt = getopt (argc, argv, "do:")) != -1)
+    switch (opt)
+      {
+      case 'd':
+       delete = 1;
+       break;
+
+      case 'o':
+       offset = bb_xparse_number (optarg, NULL);
+       break;
+
+      default:
+       bb_show_usage();
+      }
+
+  if ((delete && (offset || optind + 1 != argc))
+      || (!delete && optind + 2 != argc))
+    bb_show_usage();
+
+  opt = 0;
+  if (delete)
+    return del_loop (argv[optind]) ? EXIT_SUCCESS : EXIT_FAILURE;
+  else
+    return set_loop (argv[optind], argv[optind + 1], offset, &opt)
+      ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/busybox/util-linux/mkfs_minix.c b/busybox/util-linux/mkfs_minix.c
new file mode 100644 (file)
index 0000000..264569a
--- /dev/null
@@ -0,0 +1,852 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * DD.MM.YY
+ *
+ * 24.11.91  - Time began. Used the fsck sources to get started.
+ *
+ * 25.11.91  - Corrected some bugs. Added support for ".badblocks"
+ *             The algorithm for ".badblocks" is a bit weird, but
+ *             it should work. Oh, well.
+ *
+ * 25.01.92  - Added the -l option for getting the list of bad blocks
+ *             out of a named file. (Dave Rivers, rivers@ponds.uucp)
+ *
+ * 28.02.92  - Added %-information when using -c.
+ *
+ * 28.02.93  - Added support for other namelengths than the original
+ *             14 characters so that I can test the new kernel routines..
+ *
+ * 09.10.93  - Make exit status conform to that required by fsutil
+ *             (Rik Faith, faith@cs.unc.edu)
+ *
+ * 31.10.93  - Added inode request feature, for backup floppies: use
+ *             32 inodes, for a news partition use more.
+ *             (Scott Heavner, sdh@po.cwru.edu)
+ *
+ * 03.01.94  - Added support for file system valid flag.
+ *             (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu)
+ *
+ * 30.10.94 - added support for v2 filesystem
+ *           (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 09.11.94  - Added test to prevent overwrite of mounted fs adapted
+ *             from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
+ *             program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 03.20.95  - Clear first 512 bytes of filesystem to make certain that
+ *             the filesystem is not misidentified as a MS-DOS FAT filesystem.
+ *             (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 02.07.96  -  Added small patch from Russell King to make the program a
+ *             good deal more portable (janl@math.uio.no)
+ *
+ * Usage:  mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks]
+ *
+ *     -c for readability checking (SLOW!)
+ *      -l for getting a list of bad blocks from a file.
+ *     -n for namelength (currently the kernel only uses 14 or 30)
+ *     -i for number of inodes
+ *     -v for v2 filesystem
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@debian.org> --
+ *     removed getopt based parser and added a hand rolled one.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <mntent.h>
+#include "busybox.h"
+
+#define MINIX_ROOT_INO 1
+#define MINIX_LINK_MAX 250
+#define MINIX2_LINK_MAX        65530
+
+#define MINIX_I_MAP_SLOTS      8
+#define MINIX_Z_MAP_SLOTS      64
+#define MINIX_SUPER_MAGIC      0x137F          /* original minix fs */
+#define MINIX_SUPER_MAGIC2     0x138F          /* minix fs, 30 char names */
+#define MINIX2_SUPER_MAGIC     0x2468          /* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2    0x2478          /* minix V2 fs, 30 char names */
+#define MINIX_VALID_FS         0x0001          /* Clean fs. */
+#define MINIX_ERROR_FS         0x0002          /* fs has errors. */
+
+#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+#define MINIX_V1               0x0001          /* original minix fs */
+#define MINIX_V2               0x0002          /* minix V2 fs */
+
+#define INODE_VERSION(inode)   inode->i_sb->u.minix_sb.s_version
+
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix_inode {
+       uint16_t i_mode;
+       uint16_t i_uid;
+       uint32_t i_size;
+       uint32_t i_time;
+       uint8_t  i_gid;
+       uint8_t  i_nlinks;
+       uint16_t i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct minix2_inode {
+       uint16_t i_mode;
+       uint16_t i_nlinks;
+       uint16_t i_uid;
+       uint16_t i_gid;
+       uint32_t i_size;
+       uint32_t i_atime;
+       uint32_t i_mtime;
+       uint32_t i_ctime;
+       uint32_t i_zone[10];
+};
+
+/*
+ * minix super-block data on disk
+ */
+struct minix_super_block {
+       uint16_t s_ninodes;
+       uint16_t s_nzones;
+       uint16_t s_imap_blocks;
+       uint16_t s_zmap_blocks;
+       uint16_t s_firstdatazone;
+       uint16_t s_log_zone_size;
+       uint32_t s_max_size;
+       uint16_t s_magic;
+       uint16_t s_state;
+       uint32_t s_zones;
+};
+
+struct minix_dir_entry {
+       uint16_t inode;
+       char name[0];
+};
+
+#define BLOCK_SIZE_BITS 10
+#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
+
+#define NAME_MAX         255   /* # chars in a file name */
+
+#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+
+#define MINIX_VALID_FS               0x0001          /* Clean fs. */
+#define MINIX_ERROR_FS               0x0002          /* fs has errors. */
+
+#define MINIX_SUPER_MAGIC    0x137F          /* original minix fs */
+#define MINIX_SUPER_MAGIC2   0x138F          /* minix fs, 30 char names */
+
+#ifndef BLKGETSIZE
+#define BLKGETSIZE _IO(0x12,96)    /* return device size */
+#endif
+
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define MINIX_ROOT_INO 1
+#define MINIX_BAD_INO 2
+
+#define TEST_BUFFER_BLOCKS 16
+#define MAX_GOOD_BLOCKS 512
+
+#define UPPER(size,n) (((size)+((n)-1))/(n))
+#define INODE_SIZE (sizeof(struct minix_inode))
+#ifdef CONFIG_FEATURE_MINIX2
+#define INODE_SIZE2 (sizeof(struct minix2_inode))
+#define INODE_BLOCKS UPPER(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \
+                                   : MINIX_INODES_PER_BLOCK))
+#else
+#define INODE_BLOCKS UPPER(INODES, (MINIX_INODES_PER_BLOCK))
+#endif
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+
+#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
+
+static char *device_name = NULL;
+static int DEV = -1;
+static uint32_t BLOCKS = 0;
+static int check = 0;
+static int badblocks = 0;
+static int namelen = 30;               /* default (changed to 30, per Linus's
+
+                                                                  suggestion, Sun Nov 21 08:05:07 1993) */
+static int dirsize = 32;
+static int magic = MINIX_SUPER_MAGIC2;
+static int version2 = 0;
+
+static char root_block[BLOCK_SIZE] = "\0";
+
+static char *inode_buffer = NULL;
+
+#define Inode (((struct minix_inode *) inode_buffer)-1)
+#ifdef CONFIG_FEATURE_MINIX2
+#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
+#endif
+static char super_block_buffer[BLOCK_SIZE];
+static char boot_block_buffer[512];
+
+#define Super (*(struct minix_super_block *)super_block_buffer)
+#define INODES (Super.s_ninodes)
+#ifdef CONFIG_FEATURE_MINIX2
+#define ZONES (version2 ? Super.s_zones : Super.s_nzones)
+#else
+#define ZONES (Super.s_nzones)
+#endif
+#define IMAPS (Super.s_imap_blocks)
+#define ZMAPS (Super.s_zmap_blocks)
+#define FIRSTZONE (Super.s_firstdatazone)
+#define ZONESIZE (Super.s_log_zone_size)
+#define MAXSIZE (Super.s_max_size)
+#define MAGIC (Super.s_magic)
+#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
+
+static char *inode_map;
+static char *zone_map;
+
+static unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+static int used_good_blocks = 0;
+static unsigned long req_nr_inodes = 0;
+
+static inline int bit(char * a,unsigned int i)
+{
+         return (a[i >> 3] & (1<<(i & 7))) != 0;
+}
+#define inode_in_use(x) (bit(inode_map,(x)))
+#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
+
+#define mark_inode(x) (setbit(inode_map,(x)))
+#define unmark_inode(x) (clrbit(inode_map,(x)))
+
+#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1))
+#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1))
+
+/*
+ * Check to make certain that our new filesystem won't be created on
+ * an already mounted partition.  Code adapted from mke2fs, Copyright
+ * (C) 1994 Theodore Ts'o.  Also licensed under GPL.
+ */
+static inline void check_mount(void)
+{
+       FILE *f;
+       struct mntent *mnt;
+
+       if ((f = setmntent(MOUNTED, "r")) == NULL)
+               return;
+       while ((mnt = getmntent(f)) != NULL)
+               if (strcmp(device_name, mnt->mnt_fsname) == 0)
+                       break;
+       endmntent(f);
+       if (!mnt)
+               return;
+
+       bb_error_msg_and_die("%s is mounted; will not make a filesystem here!", device_name);
+}
+
+static long valid_offset(int fd, int offset)
+{
+       char ch;
+
+       if (lseek(fd, offset, 0) < 0)
+               return 0;
+       if (read(fd, &ch, 1) < 1)
+               return 0;
+       return 1;
+}
+
+static inline int count_blocks(int fd)
+{
+       int high, low;
+
+       low = 0;
+       for (high = 1; valid_offset(fd, high); high *= 2)
+               low = high;
+       while (low < high - 1) {
+               const int mid = (low + high) / 2;
+
+               if (valid_offset(fd, mid))
+                       low = mid;
+               else
+                       high = mid;
+       }
+       valid_offset(fd, 0);
+       return (low + 1);
+}
+
+static inline int get_size(const char *file)
+{
+       int fd;
+       long size;
+
+       if ((fd = open(file, O_RDWR)) < 0)
+               bb_perror_msg_and_die("%s", file);
+       if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+               close(fd);
+               return (size * 512);
+       }
+
+       size = count_blocks(fd);
+       close(fd);
+       return size;
+}
+
+static inline void write_tables(void)
+{
+       /* Mark the super block valid. */
+       Super.s_state |= MINIX_VALID_FS;
+       Super.s_state &= ~MINIX_ERROR_FS;
+
+       if (lseek(DEV, 0, SEEK_SET))
+               bb_error_msg_and_die("seek to boot block failed in write_tables");
+       if (512 != write(DEV, boot_block_buffer, 512))
+               bb_error_msg_and_die("unable to clear boot sector");
+       if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET))
+               bb_error_msg_and_die("seek failed in write_tables");
+       if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE))
+               bb_error_msg_and_die("unable to write super-block");
+       if (IMAPS * BLOCK_SIZE != write(DEV, inode_map, IMAPS * BLOCK_SIZE))
+               bb_error_msg_and_die("unable to write inode map");
+       if (ZMAPS * BLOCK_SIZE != write(DEV, zone_map, ZMAPS * BLOCK_SIZE))
+               bb_error_msg_and_die("unable to write zone map");
+       if (INODE_BUFFER_SIZE != write(DEV, inode_buffer, INODE_BUFFER_SIZE))
+               bb_error_msg_and_die("unable to write inodes");
+
+}
+
+static void write_block(int blk, char *buffer)
+{
+       if (blk * BLOCK_SIZE != lseek(DEV, blk * BLOCK_SIZE, SEEK_SET))
+               bb_error_msg_and_die("seek failed in write_block");
+       if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE))
+               bb_error_msg_and_die("write failed in write_block");
+}
+
+static int get_free_block(void)
+{
+       int blk;
+
+       if (used_good_blocks + 1 >= MAX_GOOD_BLOCKS)
+               bb_error_msg_and_die("too many bad blocks");
+       if (used_good_blocks)
+               blk = good_blocks_table[used_good_blocks - 1] + 1;
+       else
+               blk = FIRSTZONE;
+       while (blk < ZONES && zone_in_use(blk))
+               blk++;
+       if (blk >= ZONES)
+               bb_error_msg_and_die("not enough good blocks");
+       good_blocks_table[used_good_blocks] = blk;
+       used_good_blocks++;
+       return blk;
+}
+
+static inline void mark_good_blocks(void)
+{
+       int blk;
+
+       for (blk = 0; blk < used_good_blocks; blk++)
+               mark_zone(good_blocks_table[blk]);
+}
+
+static int next(int zone)
+{
+       if (!zone)
+               zone = FIRSTZONE - 1;
+       while (++zone < ZONES)
+               if (zone_in_use(zone))
+                       return zone;
+       return 0;
+}
+
+static inline void make_bad_inode(void)
+{
+       struct minix_inode *inode = &Inode[MINIX_BAD_INO];
+       int i, j, zone;
+       int ind = 0, dind = 0;
+       unsigned short ind_block[BLOCK_SIZE >> 1];
+       unsigned short dind_block[BLOCK_SIZE >> 1];
+
+#define NEXT_BAD (zone = next(zone))
+
+       if (!badblocks)
+               return;
+       mark_inode(MINIX_BAD_INO);
+       inode->i_nlinks = 1;
+       inode->i_time = time(NULL);
+       inode->i_mode = S_IFREG + 0000;
+       inode->i_size = badblocks * BLOCK_SIZE;
+       zone = next(0);
+       for (i = 0; i < 7; i++) {
+               inode->i_zone[i] = zone;
+               if (!NEXT_BAD)
+                       goto end_bad;
+       }
+       inode->i_zone[7] = ind = get_free_block();
+       memset(ind_block, 0, BLOCK_SIZE);
+       for (i = 0; i < 512; i++) {
+               ind_block[i] = zone;
+               if (!NEXT_BAD)
+                       goto end_bad;
+       }
+       inode->i_zone[8] = dind = get_free_block();
+       memset(dind_block, 0, BLOCK_SIZE);
+       for (i = 0; i < 512; i++) {
+               write_block(ind, (char *) ind_block);
+               dind_block[i] = ind = get_free_block();
+               memset(ind_block, 0, BLOCK_SIZE);
+               for (j = 0; j < 512; j++) {
+                       ind_block[j] = zone;
+                       if (!NEXT_BAD)
+                               goto end_bad;
+               }
+       }
+       bb_error_msg_and_die("too many bad blocks");
+  end_bad:
+       if (ind)
+               write_block(ind, (char *) ind_block);
+       if (dind)
+               write_block(dind, (char *) dind_block);
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static inline void make_bad_inode2(void)
+{
+       struct minix2_inode *inode = &Inode2[MINIX_BAD_INO];
+       int i, j, zone;
+       int ind = 0, dind = 0;
+       unsigned long ind_block[BLOCK_SIZE >> 2];
+       unsigned long dind_block[BLOCK_SIZE >> 2];
+
+       if (!badblocks)
+               return;
+       mark_inode(MINIX_BAD_INO);
+       inode->i_nlinks = 1;
+       inode->i_atime = inode->i_mtime = inode->i_ctime = time(NULL);
+       inode->i_mode = S_IFREG + 0000;
+       inode->i_size = badblocks * BLOCK_SIZE;
+       zone = next(0);
+       for (i = 0; i < 7; i++) {
+               inode->i_zone[i] = zone;
+               if (!NEXT_BAD)
+                       goto end_bad;
+       }
+       inode->i_zone[7] = ind = get_free_block();
+       memset(ind_block, 0, BLOCK_SIZE);
+       for (i = 0; i < 256; i++) {
+               ind_block[i] = zone;
+               if (!NEXT_BAD)
+                       goto end_bad;
+       }
+       inode->i_zone[8] = dind = get_free_block();
+       memset(dind_block, 0, BLOCK_SIZE);
+       for (i = 0; i < 256; i++) {
+               write_block(ind, (char *) ind_block);
+               dind_block[i] = ind = get_free_block();
+               memset(ind_block, 0, BLOCK_SIZE);
+               for (j = 0; j < 256; j++) {
+                       ind_block[j] = zone;
+                       if (!NEXT_BAD)
+                               goto end_bad;
+               }
+       }
+       /* Could make triple indirect block here */
+       bb_error_msg_and_die("too many bad blocks");
+  end_bad:
+       if (ind)
+               write_block(ind, (char *) ind_block);
+       if (dind)
+               write_block(dind, (char *) dind_block);
+}
+#endif
+
+static inline void make_root_inode(void)
+{
+       struct minix_inode *inode = &Inode[MINIX_ROOT_INO];
+
+       mark_inode(MINIX_ROOT_INO);
+       inode->i_zone[0] = get_free_block();
+       inode->i_nlinks = 2;
+       inode->i_time = time(NULL);
+       if (badblocks)
+               inode->i_size = 3 * dirsize;
+       else {
+               root_block[2 * dirsize] = '\0';
+               root_block[2 * dirsize + 1] = '\0';
+               inode->i_size = 2 * dirsize;
+       }
+       inode->i_mode = S_IFDIR + 0755;
+       inode->i_uid = getuid();
+       if (inode->i_uid)
+               inode->i_gid = getgid();
+       write_block(inode->i_zone[0], root_block);
+}
+
+#ifdef CONFIG_FEATURE_MINIX2
+static inline void make_root_inode2(void)
+{
+       struct minix2_inode *inode = &Inode2[MINIX_ROOT_INO];
+
+       mark_inode(MINIX_ROOT_INO);
+       inode->i_zone[0] = get_free_block();
+       inode->i_nlinks = 2;
+       inode->i_atime = inode->i_mtime = inode->i_ctime = time(NULL);
+       if (badblocks)
+               inode->i_size = 3 * dirsize;
+       else {
+               root_block[2 * dirsize] = '\0';
+               root_block[2 * dirsize + 1] = '\0';
+               inode->i_size = 2 * dirsize;
+       }
+       inode->i_mode = S_IFDIR + 0755;
+       inode->i_uid = getuid();
+       if (inode->i_uid)
+               inode->i_gid = getgid();
+       write_block(inode->i_zone[0], root_block);
+}
+#endif
+
+static inline void setup_tables(void)
+{
+       int i;
+       unsigned long inodes;
+
+       memset(super_block_buffer, 0, BLOCK_SIZE);
+       memset(boot_block_buffer, 0, 512);
+       MAGIC = magic;
+       ZONESIZE = 0;
+       MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024;
+#ifdef CONFIG_FEATURE_MINIX2
+       if (version2) {
+               Super.s_zones =  BLOCKS;
+       } else
+#endif
+               Super.s_nzones = BLOCKS;
+
+/* some magic nrs: 1 inode / 3 blocks */
+       if (req_nr_inodes == 0)
+               inodes = BLOCKS / 3;
+       else
+               inodes = req_nr_inodes;
+       /* Round up inode count to fill block size */
+#ifdef CONFIG_FEATURE_MINIX2
+       if (version2)
+               inodes = ((inodes + MINIX2_INODES_PER_BLOCK - 1) &
+                                 ~(MINIX2_INODES_PER_BLOCK - 1));
+       else
+#endif
+               inodes = ((inodes + MINIX_INODES_PER_BLOCK - 1) &
+                                 ~(MINIX_INODES_PER_BLOCK - 1));
+       if (inodes > 65535)
+               inodes = 65535;
+       INODES = inodes;
+       IMAPS = UPPER(INODES + 1, BITS_PER_BLOCK);
+       ZMAPS = 0;
+       i = 0;
+       while (ZMAPS !=
+                  UPPER(BLOCKS - (2 + IMAPS + ZMAPS + INODE_BLOCKS) + 1,
+                                BITS_PER_BLOCK) && i < 1000) {
+               ZMAPS =
+                       UPPER(BLOCKS - (2 + IMAPS + ZMAPS + INODE_BLOCKS) + 1,
+                                 BITS_PER_BLOCK);
+               i++;
+       }
+       /* Real bad hack but overwise mkfs.minix can be thrown
+        * in infinite loop...
+        * try:
+        * dd if=/dev/zero of=test.fs count=10 bs=1024
+        * /sbin/mkfs.minix -i 200 test.fs
+        * */
+       if (i >= 999) {
+               bb_error_msg_and_die("unable to allocate buffers for maps");
+       }
+       FIRSTZONE = NORM_FIRSTZONE;
+       inode_map = xmalloc(IMAPS * BLOCK_SIZE);
+       zone_map = xmalloc(ZMAPS * BLOCK_SIZE);
+       memset(inode_map, 0xff, IMAPS * BLOCK_SIZE);
+       memset(zone_map, 0xff, ZMAPS * BLOCK_SIZE);
+       for (i = FIRSTZONE; i < ZONES; i++)
+               unmark_zone(i);
+       for (i = MINIX_ROOT_INO; i <= INODES; i++)
+               unmark_inode(i);
+       inode_buffer = xmalloc(INODE_BUFFER_SIZE);
+       memset(inode_buffer, 0, INODE_BUFFER_SIZE);
+       printf("%ld inodes\n", (long)INODES);
+       printf("%ld blocks\n", (long)ZONES);
+       printf("Firstdatazone=%ld (%ld)\n", (long)FIRSTZONE, (long)NORM_FIRSTZONE);
+       printf("Zonesize=%d\n", BLOCK_SIZE << ZONESIZE);
+       printf("Maxsize=%ld\n\n", (long)MAXSIZE);
+}
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable/writable.
+ */
+static inline long do_check(char *buffer, int try, unsigned int current_block)
+{
+       long got;
+
+       /* Seek to the correct loc. */
+       if (lseek(DEV, current_block * BLOCK_SIZE, SEEK_SET) !=
+               current_block * BLOCK_SIZE) {
+               bb_error_msg_and_die("seek failed during testing of blocks");
+       }
+
+
+       /* Try the read */
+       got = read(DEV, buffer, try * BLOCK_SIZE);
+       if (got < 0)
+               got = 0;
+       if (got & (BLOCK_SIZE - 1)) {
+               printf("Weird values in do_check: probably bugs\n");
+       }
+       got /= BLOCK_SIZE;
+       return got;
+}
+
+static unsigned int currently_testing = 0;
+
+static void alarm_intr(int alnum)
+{
+       if (currently_testing >= ZONES)
+               return;
+       signal(SIGALRM, alarm_intr);
+       alarm(5);
+       if (!currently_testing)
+               return;
+       printf("%d ...", currently_testing);
+       fflush(stdout);
+}
+
+static void check_blocks(void)
+{
+       int try, got;
+       static char buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+       currently_testing = 0;
+       signal(SIGALRM, alarm_intr);
+       alarm(5);
+       while (currently_testing < ZONES) {
+               if (lseek(DEV, currently_testing * BLOCK_SIZE, SEEK_SET) !=
+                       currently_testing * BLOCK_SIZE)
+                       bb_error_msg_and_die("seek failed in check_blocks");
+               try = TEST_BUFFER_BLOCKS;
+               if (currently_testing + try > ZONES)
+                       try = ZONES - currently_testing;
+               got = do_check(buffer, try, currently_testing);
+               currently_testing += got;
+               if (got == try)
+                       continue;
+               if (currently_testing < FIRSTZONE)
+                       bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
+               mark_zone(currently_testing);
+               badblocks++;
+               currently_testing++;
+       }
+       if (badblocks > 1)
+               printf("%d bad blocks\n", badblocks);
+       else if (badblocks == 1)
+               printf("one bad block\n");
+}
+
+static void get_list_blocks(char *filename)
+{
+       FILE *listfile;
+       unsigned long blockno;
+
+       listfile = bb_xfopen(filename, "r");
+       while (!feof(listfile)) {
+               fscanf(listfile, "%ld\n", &blockno);
+               mark_zone(blockno);
+               badblocks++;
+       }
+       if (badblocks > 1)
+               printf("%d bad blocks\n", badblocks);
+       else if (badblocks == 1)
+               printf("one bad block\n");
+}
+
+extern int mkfs_minix_main(int argc, char **argv)
+{
+       int i=1;
+       char *tmp;
+       struct stat statbuf;
+       char *listfile = NULL;
+       int stopIt=FALSE;
+
+       if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
+               bb_error_msg_and_die("bad inode size");
+#ifdef CONFIG_FEATURE_MINIX2
+       if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
+               bb_error_msg_and_die("bad inode size");
+#endif
+
+       /* Parse options */
+       argv++;
+       while (--argc >= 0 && *argv && **argv) {
+               if (**argv == '-') {
+                       stopIt=FALSE;
+                       while (i > 0 && *++(*argv) && stopIt==FALSE) {
+                               switch (**argv) {
+                                       case 'c':
+                                               check = 1;
+                                               break;
+                                       case 'i':
+                                               {
+                                                       char *cp=NULL;
+                                                       if (*(*argv+1) != 0) {
+                                                               cp = ++(*argv);
+                                                       } else {
+                                                               if (--argc == 0) {
+                                                                       goto goodbye;
+                                                               }
+                                                               cp = *(++argv);
+                                                       }
+                                                       req_nr_inodes = strtoul(cp, &tmp, 0);
+                                                       if (*tmp)
+                                                               bb_show_usage();
+                                                       stopIt=TRUE;
+                                                       break;
+                                               }
+                                       case 'l':
+                                               if (--argc == 0) {
+                                                       goto goodbye;
+                                               }
+                                               listfile = *(++argv);
+                                               break;
+                                       case 'n':
+                                               {
+                                                       char *cp=NULL;
+
+                                                       if (*(*argv+1) != 0) {
+                                                               cp = ++(*argv);
+                                                       } else {
+                                                               if (--argc == 0) {
+                                                                       goto goodbye;
+                                                               }
+                                                               cp = *(++argv);
+                                                       }
+                                                       i = strtoul(cp, &tmp, 0);
+                                                       if (*tmp)
+                                                               bb_show_usage();
+                                                       if (i == 14)
+                                                               magic = MINIX_SUPER_MAGIC;
+                                                       else if (i == 30)
+                                                               magic = MINIX_SUPER_MAGIC2;
+                                                       else
+                                                               bb_show_usage();
+                                                       namelen = i;
+                                                       dirsize = i + 2;
+                                                       stopIt=TRUE;
+                                                       break;
+                                               }
+                                       case 'v':
+#ifdef CONFIG_FEATURE_MINIX2
+                                               version2 = 1;
+#else
+                                               bb_error_msg("%s: not compiled with minix v2 support",
+                                                               device_name);
+                                               exit(-1);
+#endif
+                                               break;
+                                       case '-':
+                                       case 'h':
+                                       default:
+goodbye:
+                                               bb_show_usage();
+                               }
+                       }
+               } else {
+                       if (device_name == NULL)
+                               device_name = *argv;
+                       else if (BLOCKS == 0)
+                               BLOCKS = strtol(*argv, &tmp, 0);
+                       else {
+                               goto goodbye;
+                       }
+               }
+               argv++;
+       }
+
+       if (device_name && !BLOCKS)
+               BLOCKS = get_size(device_name) / 1024;
+       if (!device_name || BLOCKS < 10) {
+               bb_show_usage();
+       }
+#ifdef CONFIG_FEATURE_MINIX2
+       if (version2) {
+               if (namelen == 14)
+                       magic = MINIX2_SUPER_MAGIC;
+               else
+                       magic = MINIX2_SUPER_MAGIC2;
+       } else
+#endif
+       if (BLOCKS > 65535)
+               BLOCKS = 65535;
+       check_mount();                          /* is it already mounted? */
+       tmp = root_block;
+       *(short *) tmp = 1;
+       strcpy(tmp + 2, ".");
+       tmp += dirsize;
+       *(short *) tmp = 1;
+       strcpy(tmp + 2, "..");
+       tmp += dirsize;
+       *(short *) tmp = 2;
+       strcpy(tmp + 2, ".badblocks");
+       DEV = open(device_name, O_RDWR);
+       if (DEV < 0)
+               bb_error_msg_and_die("unable to open %s", device_name);
+       if (fstat(DEV, &statbuf) < 0)
+               bb_error_msg_and_die("unable to stat %s", device_name);
+       if (!S_ISBLK(statbuf.st_mode))
+               check = 0;
+       else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+               bb_error_msg_and_die("will not try to make filesystem on '%s'", device_name);
+       setup_tables();
+       if (check)
+               check_blocks();
+       else if (listfile)
+               get_list_blocks(listfile);
+#ifdef CONFIG_FEATURE_MINIX2
+       if (version2) {
+               make_root_inode2();
+               make_bad_inode2();
+       } else
+#endif
+       {
+               make_root_inode();
+               make_bad_inode();
+       }
+       mark_good_blocks();
+       write_tables();
+       return( 0);
+
+}
diff --git a/busybox/util-linux/mkswap.c b/busybox/util-linux/mkswap.c
new file mode 100644 (file)
index 0000000..1fc648f
--- /dev/null
@@ -0,0 +1,418 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkswap.c - set up a linux swap device
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 20.12.91  - time began. Got VM working yesterday by doing this by hand.
+ *
+ * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
+ *
+ *     -c   for readability checking. (Use it unless you are SURE!)
+ *     -vN  for swap areas version N. (Only N=0,1 known today.)
+ *      -f   for forcing swap creation even if it would smash partition table.
+ *
+ * The device may be a block device or an image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
+ * size-in-blocks parameter optional added Wed Feb  8 10:33:43 1995.
+ *
+ * Version 1 swap area code (for kernel 2.1.117), aeb, 981010.
+ *
+ * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb.
+ * V1_MAX_PAGES fixes, jj, 990325.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ *  from util-linux -- adapted for busybox by
+ *  Erik Andersen <andersen@codepoet.org>. I ripped out Native Language
+ *  Support, made some stuff smaller, and fitted for life in busybox.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>                 /* for _IO */
+#include <sys/utsname.h>
+#include <asm/page.h>                  /* for PAGE_SIZE and PAGE_SHIFT */
+                               /* we also get PAGE_SIZE via getpagesize() */
+#include "busybox.h"
+
+#ifndef _IO
+/* pre-1.3.45 */
+static const int BLKGETSIZE = 0x1260;
+#else
+/* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
+#define BLKGETSIZE _IO(0x12,96)
+#endif
+
+static char *device_name = NULL;
+static int DEV = -1;
+static long PAGES = 0;
+static int check = 0;
+static int badpages = 0;
+static int version = -1;
+
+#define MAKE_VERSION(p,q,r)    (65536*(p) + 256*(q) + (r))
+
+/*
+ * The definition of the union swap_header uses the constant PAGE_SIZE.
+ * Unfortunately, on some architectures this depends on the hardware model,
+ * and can only be found at run time -- we use getpagesize().
+ */
+
+static int pagesize;
+static int *signature_page;
+
+static struct swap_header_v1 {
+       char bootbits[1024];            /* Space for disklabel etc. */
+       unsigned int version;
+       unsigned int last_page;
+       unsigned int nr_badpages;
+       unsigned int padding[125];
+       unsigned int badpages[1];
+} *p;
+
+static inline void init_signature_page(void)
+{
+       pagesize = getpagesize();
+
+#ifdef PAGE_SIZE
+       if (pagesize != PAGE_SIZE)
+               bb_error_msg("Assuming pages of size %d", pagesize);
+#endif
+       signature_page = (int *) xmalloc(pagesize);
+       memset(signature_page, 0, pagesize);
+       p = (struct swap_header_v1 *) signature_page;
+}
+
+static inline void write_signature(char *sig)
+{
+       char *sp = (char *) signature_page;
+
+       strncpy(sp + pagesize - 10, sig, 10);
+}
+
+#define V0_MAX_PAGES   (8 * (pagesize - 10))
+/* Before 2.2.0pre9 */
+#define V1_OLD_MAX_PAGES       ((0x7fffffff / pagesize) - 1)
+/* Since 2.2.0pre9:
+   error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
+   with variations on
+       #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+       #define SWP_OFFSET(entry) ((entry) >> 8)
+   on the various architectures. Below the result - yuk.
+
+   Machine     pagesize        SWP_ENTRY       SWP_OFFSET      bound+1 oldbound+2
+   i386                2^12            o<<8            e>>8            1<<24   1<<19
+   mips                2^12            o<<15           e>>15           1<<17   1<<19
+   alpha       2^13            o<<40           e>>40           1<<24   1<<18
+   m68k                2^12            o<<12           e>>12           1<<20   1<<19
+   sparc       2^{12,13}       (o&0x3ffff)<<9  (e>>9)&0x3ffff  1<<18   1<<{19,18}
+   sparc64     2^13            o<<13           e>>13           1<<51   1<<18
+   ppc         2^12            o<<8            e>>8            1<<24   1<<19
+   armo                2^{13,14,15}    o<<8            e>>8            1<<24   1<<{18,17,16}
+   armv                2^12            o<<9            e>>9            1<<23   1<<19
+
+   assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
+
+   The bad part is that we need to know this since the kernel will
+   refuse a swap space if it is too large.
+*/
+/* patch from jj - why does this differ from the above? */
+#if defined(__alpha__)
+#define V1_MAX_PAGES           ((1 << 24) - 1)
+#elif defined(__mips__)
+#define V1_MAX_PAGES           ((1 << 17) - 1)
+#elif defined(__sparc_v9__)
+#define V1_MAX_PAGES           ((3 << 29) - 1)
+#elif defined(__sparc__)
+#define V1_MAX_PAGES           (pagesize == 8192 ? ((3 << 29) - 1) : ((1 << 18) - 1))
+#else
+#define V1_MAX_PAGES           V1_OLD_MAX_PAGES
+#endif
+/* man page now says:
+The maximum useful size of a swap area now depends on the architecture.
+It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
+128GB on alpha and 3TB on sparc64.
+*/
+
+#define MAX_BADPAGES   ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
+
+static inline void bit_set(unsigned int *addr, unsigned int nr)
+{
+       unsigned int r, m;
+
+       addr += nr / (8 * sizeof(int));
+
+       r = *addr;
+       m = 1 << (nr & (8 * sizeof(int) - 1));
+
+       *addr = r | m;
+}
+
+static int bit_test_and_clear(unsigned int *addr, unsigned int nr)
+{
+       unsigned int r, m;
+
+       addr += nr / (8 * sizeof(int));
+
+       r = *addr;
+       m = 1 << (nr & (8 * sizeof(int) - 1));
+
+       *addr = r & ~m;
+       return (r & m) != 0;
+}
+
+
+static void page_ok(int page)
+{
+       if (version == 0)
+               bit_set(signature_page, page);
+}
+
+static inline void page_bad(int page)
+{
+       if (version == 0)
+               bit_test_and_clear(signature_page, page);
+       else {
+               if (badpages == MAX_BADPAGES)
+                       bb_error_msg_and_die("too many bad pages");
+               p->badpages[badpages] = page;
+       }
+       badpages++;
+}
+
+static void check_blocks(void)
+{
+       unsigned int current_page;
+       int do_seek = 1;
+       char *buffer;
+
+       buffer = xmalloc(pagesize);
+       current_page = 0;
+       while (current_page < PAGES) {
+               if (!check) {
+                       page_ok(current_page++);
+                       continue;
+               }
+               if (do_seek && lseek(DEV, current_page * pagesize, SEEK_SET) !=
+                       current_page * pagesize)
+                       bb_error_msg_and_die("seek failed in check_blocks");
+               if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
+                       page_bad(current_page++);
+                       continue;
+               }
+               page_ok(current_page++);
+       }
+       if (badpages == 1)
+               printf("one bad page\n");
+       else if (badpages > 1)
+               printf("%d bad pages\n", badpages);
+}
+
+static long valid_offset(int fd, int offset)
+{
+       char ch;
+
+       if (lseek(fd, offset, 0) < 0)
+               return 0;
+       if (read(fd, &ch, 1) < 1)
+               return 0;
+       return 1;
+}
+
+static int find_size(int fd)
+{
+       unsigned int high, low;
+
+       low = 0;
+       for (high = 1; high > 0 && valid_offset(fd, high); high *= 2)
+               low = high;
+       while (low < high - 1) {
+               const int mid = (low + high) / 2;
+
+               if (valid_offset(fd, mid))
+                       low = mid;
+               else
+                       high = mid;
+       }
+       return (low + 1);
+}
+
+/* return size in pages, to avoid integer overflow */
+static long get_size(const char *file)
+{
+       int fd;
+       long size;
+
+       if ((fd = open(file, O_RDONLY)) < 0)
+               bb_perror_msg_and_die("%s", file);
+       if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+               int sectors_per_page = pagesize / 512;
+
+               size /= sectors_per_page;
+       } else {
+               size = find_size(fd) / pagesize;
+       }
+       close(fd);
+       return size;
+}
+
+int mkswap_main(int argc, char **argv)
+{
+       char *tmp;
+       struct stat statbuf;
+       int sz;
+       int maxpages;
+       int goodpages;
+       int offset;
+       int force = 0;
+
+       init_signature_page();          /* get pagesize */
+
+       while (argc-- > 1) {
+               argv++;
+               if (argv[0][0] != '-') {
+                       if (device_name) {
+                               int blocks_per_page = pagesize / 1024;
+
+                               PAGES = strtol(argv[0], &tmp, 0) / blocks_per_page;
+                               if (*tmp)
+                                       bb_show_usage();
+                       } else
+                               device_name = argv[0];
+               } else {
+                       switch (argv[0][1]) {
+                       case 'c':
+                               check = 1;
+                               break;
+                       case 'f':
+                               force = 1;
+                               break;
+                       case 'v':
+                               version = atoi(argv[0] + 2);
+                               break;
+                       default:
+                               bb_show_usage();
+                       }
+               }
+       }
+       if (!device_name) {
+               bb_error_msg("error: Nowhere to set up swap on?");
+               bb_show_usage();
+       }
+       sz = get_size(device_name);
+       if (!PAGES) {
+               PAGES = sz;
+       } else if (PAGES > sz && !force) {
+               bb_error_msg("error: size %ld is larger than device size %d",
+                               PAGES * (pagesize / 1024), sz * (pagesize / 1024));
+               return EXIT_FAILURE;
+       }
+
+       if (version == -1) {
+               if (get_kernel_revision() < MAKE_VERSION(2, 1, 117))
+                       version = 0;
+               else
+                       version = 1;
+       }
+       if (version != 0 && version != 1) {
+               bb_error_msg("error: unknown version %d", version);
+               bb_show_usage();
+       }
+       if (PAGES < 10) {
+               bb_error_msg("error: swap area needs to be at least %ldkB",
+                               (long) (10 * pagesize / 1024));
+               bb_show_usage();
+       }
+#if 0
+       maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
+#else
+       if (!version)
+               maxpages = V0_MAX_PAGES;
+       else if (get_kernel_revision() >= MAKE_VERSION(2, 2, 1))
+               maxpages = V1_MAX_PAGES;
+       else {
+               maxpages = V1_OLD_MAX_PAGES;
+               if (maxpages > V1_MAX_PAGES)
+                       maxpages = V1_MAX_PAGES;
+       }
+#endif
+       if (PAGES > maxpages) {
+               PAGES = maxpages;
+               bb_error_msg("warning: truncating swap area to %ldkB",
+                               PAGES * pagesize / 1024);
+       }
+
+       DEV = open(device_name, O_RDWR);
+       if (DEV < 0 || fstat(DEV, &statbuf) < 0)
+               bb_perror_msg_and_die("%s", device_name);
+       if (!S_ISBLK(statbuf.st_mode))
+               check = 0;
+       else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+               bb_error_msg_and_die("Will not try to make swapdevice on '%s'", device_name);
+
+#ifdef __sparc__
+       if (!force && version == 0) {
+               /* Don't overwrite partition table unless forced */
+               unsigned char *buffer = (unsigned char *) signature_page;
+               unsigned short *q, sum;
+
+               if (read(DEV, buffer, 512) != 512)
+                       bb_error_msg_and_die("fatal: first page unreadable");
+               if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
+                       q = (unsigned short *) (buffer + 510);
+                       for (sum = 0; q >= (unsigned short *) buffer;)
+                               sum ^= *q--;
+                       if (!sum) {
+                               bb_error_msg("Device '%s' contains a valid Sun disklabel.\n"
+"This probably means creating v0 swap would destroy your partition table\n"
+"No swap created. If you really want to create swap v0 on that device, use\n"
+"the -f option to force it.", device_name);
+                               return EXIT_FAILURE;
+                       }
+               }
+       }
+#endif
+
+       if (version == 0 || check)
+               check_blocks();
+       if (version == 0 && !bit_test_and_clear(signature_page, 0))
+               bb_error_msg_and_die("fatal: first page unreadable");
+       if (version == 1) {
+               p->version = version;
+               p->last_page = PAGES - 1;
+               p->nr_badpages = badpages;
+       }
+
+       goodpages = PAGES - badpages - 1;
+       if (goodpages <= 0)
+               bb_error_msg_and_die("Unable to set up swap-space: unreadable");
+       printf("Setting up swapspace version %d, size = %ld bytes\n",
+                  version, (long) (goodpages * pagesize));
+       write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
+
+       offset = ((version == 0) ? 0 : 1024);
+       if (lseek(DEV, offset, SEEK_SET) != offset)
+               bb_error_msg_and_die("unable to rewind swap-device");
+       if (write(DEV, (char *) signature_page + offset, pagesize - offset)
+               != pagesize - offset)
+               bb_error_msg_and_die("unable to write signature page");
+
+       /*
+        * A subsequent swapon() will fail if the signature
+        * is not actually on disk. (This is a kernel bug.)
+        */
+       if (fsync(DEV))
+               bb_error_msg_and_die("fsync failed");
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/util-linux/more.c b/busybox/util-linux/more.c
new file mode 100644 (file)
index 0000000..e910388
--- /dev/null
@@ -0,0 +1,211 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini more implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
+ * based on the original more implementation by Bruce, and code from the
+ * Debian boot-floppies team.
+ *
+ * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+
+#ifdef CONFIG_FEATURE_USE_TERMIOS
+static int cin_fileno;
+#include <termios.h>
+#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
+#define getTermSettings(fd,argp) tcgetattr(fd, argp);
+
+static struct termios initial_settings, new_settings;
+
+static void set_tty_to_initial_mode(void)
+{
+       setTermSettings(cin_fileno, &initial_settings);
+}
+
+static void gotsig(int sig)
+{
+       putchar('\n');
+       exit(EXIT_FAILURE);
+}
+#endif /* CONFIG_FEATURE_USE_TERMIOS */
+
+
+extern int more_main(int argc, char **argv)
+{
+       int c, lines, input = 0;
+       int please_display_more_prompt = 0;
+       struct stat st;
+       FILE *file;
+       FILE *cin;
+       int len, page_height;
+       int terminal_width;
+       int terminal_height;
+#ifndef CONFIG_FEATURE_USE_TERMIOS
+       int cin_fileno;
+#endif
+
+       argc--;
+       argv++;
+
+
+       /* not use inputing from terminal if usage: more > outfile */
+       if(isatty(STDOUT_FILENO)) {
+               cin = fopen(CURRENT_TTY, "r");
+               if (!cin)
+                       cin = bb_xfopen(CONSOLE_DEV, "r");
+               cin_fileno = fileno(cin);
+               please_display_more_prompt = 2;
+#ifdef CONFIG_FEATURE_USE_TERMIOS
+               getTermSettings(cin_fileno, &initial_settings);
+               new_settings = initial_settings;
+               new_settings.c_lflag &= ~ICANON;
+               new_settings.c_lflag &= ~ECHO;
+               new_settings.c_cc[VMIN] = 1;
+               new_settings.c_cc[VTIME] = 0;
+               setTermSettings(cin_fileno, &new_settings);
+               atexit(set_tty_to_initial_mode);
+               (void) signal(SIGINT, gotsig);
+               (void) signal(SIGQUIT, gotsig);
+               (void) signal(SIGTERM, gotsig);
+#endif
+       } else {
+               cin = stdin;
+       }
+
+       do {
+               if (argc == 0) {
+                       file = stdin;
+               } else
+                       file = bb_wfopen(*argv, "r");
+               if(file==0)
+                       goto loop;
+
+               st.st_size = 0;
+               fstat(fileno(file), &st);
+
+               please_display_more_prompt &= ~1;
+
+               get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
+               if (terminal_height > 4)
+                       terminal_height -= 2;
+               if (terminal_width > 0)
+                       terminal_width -= 1;
+
+               len=0;
+               lines = 0;
+               page_height = terminal_height;
+               while ((c = getc(file)) != EOF) {
+
+                       if ((please_display_more_prompt & 3) == 3) {
+                               len = printf("--More-- ");
+                               if (file != stdin && st.st_size > 0) {
+#if _FILE_OFFSET_BITS == 64
+                                       len += printf("(%d%% of %lld bytes)",
+                                                  (int) (100 * ((double) ftell(file) /
+                                                  (double) st.st_size)), (long long)st.st_size);
+#else
+                                       len += printf("(%d%% of %ld bytes)",
+                                                  (int) (100 * ((double) ftell(file) /
+                                                  (double) st.st_size)), (long)st.st_size);
+#endif
+                               }
+
+                               fflush(stdout);
+
+                               /*
+                                * We've just displayed the "--More--" prompt, so now we need
+                                * to get input from the user.
+                                */
+                               input = getc(cin);
+#ifndef CONFIG_FEATURE_USE_TERMIOS
+                               printf("\033[A"); /* up cursor */
+#endif
+                               /* Erase the "More" message */
+                               putc('\r', stdout);
+                               while (--len >= 0)
+                                       putc(' ', stdout);
+                               putc('\r', stdout);
+                               len=0;
+                               lines = 0;
+                               page_height = terminal_height;
+                               please_display_more_prompt &= ~1;
+
+                               if (input == 'q')
+                                       goto end;
+                       }
+
+                       /*
+                        * There are two input streams to worry about here:
+                        *
+                        * c     : the character we are reading from the file being "mored"
+                        * input : a character received from the keyboard
+                        *
+                        * If we hit a newline in the _file_ stream, we want to test and
+                        * see if any characters have been hit in the _input_ stream. This
+                        * allows the user to quit while in the middle of a file.
+                        */
+                       if (c == '\n') {
+                               /* increment by just one line if we are at
+                                * the end of this line */
+                               if (input == '\n')
+                                       please_display_more_prompt |= 1;
+                               /* Adjust the terminal height for any overlap, so that
+                                * no lines get lost off the top. */
+                               if (len >= terminal_width) {
+                                       int quot, rem;
+                                       quot = len / terminal_width;
+                                       rem  = len - (quot * terminal_width);
+                                       if (quot) {
+                                               if (rem)
+                                                       page_height-=quot;
+                                               else
+                                                       page_height-=(quot-1);
+                                       }
+                               }
+                               if (++lines >= page_height) {
+                                       please_display_more_prompt |= 1;
+                               }
+                               len=0;
+                       }
+                       /*
+                        * If we just read a newline from the file being 'mored' and any
+                        * key other than a return is hit, scroll by one page
+                        */
+                       putc(c, stdout);
+                       len++;
+               }
+               fclose(file);
+               fflush(stdout);
+loop:
+               argv++;
+       } while (--argc > 0);
+  end:
+       return 0;
+}
diff --git a/busybox/util-linux/mount.c b/busybox/util-linux/mount.c
new file mode 100644 (file)
index 0000000..b059d70
--- /dev/null
@@ -0,0 +1,497 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mount implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 3/21/1999   Charles P. Wright <cpwright@cpwright.com>
+ *             searches through fstab when -a is passed
+ *             will try mounting stuff with all fses when passed -t auto
+ *
+ * 1999-04-17  Dave Cinege...Rewrote -t auto. Fixed ro mtab.
+ *
+ * 1999-10-07  Erik Andersen <andersen@codepoet.org>.
+ *              Rewrite of a lot of code. Removed mtab usage (I plan on
+ *              putting it back as a compile-time option some time),
+ *              major adjustments to option parsing, and some serious
+ *              dieting all around.
+ *
+ * 1999-11-06  mtab support is back - andersee
+ *
+ * 2000-01-12   Ben Collins <bcollins@debian.org>, Borrowed utils-linux's
+ *              mount to add loop support.
+ *
+ * 2000-04-30  Dave Cinege <dcinege@psychosis.com>
+ *             Rewrote fstab while loop and lower mount section. Can now do
+ *             single mounts from fstab. Can override fstab options for single
+ *             mount. Common mount_one call for single mounts and 'all'. Fixed
+ *             mtab updating and stale entries. Removed 'remount' default.
+ *
+ */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <mntent.h>
+#include <ctype.h>
+#include "busybox.h"
+
+#ifdef CONFIG_NFSMOUNT
+#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
+#error "You need to build uClibc with UCLIBC_HAS_RPC for busybox mount with NFS support to compile."
+#endif
+#endif
+
+enum {
+       MS_MGC_VAL = 0xc0ed0000,        /* Magic number indicatng "new" flags */
+       MS_RDONLY = 1,          /* Mount read-only */
+       MS_NOSUID = 2,          /* Ignore suid and sgid bits */
+       MS_NODEV = 4,           /* Disallow access to device special files */
+       MS_NOEXEC = 8,          /* Disallow program execution */
+       MS_SYNCHRONOUS = 16,    /* Writes are synced at once */
+       MS_REMOUNT = 32,        /* Alter flags of a mounted FS */
+       MS_MANDLOCK = 64,       /* Allow mandatory locks on an FS */
+       S_QUOTA = 128,          /* Quota initialized for file/directory/symlink */
+       S_APPEND = 256,         /* Append-only file */
+       S_IMMUTABLE = 512,      /* Immutable file */
+       MS_NOATIME = 1024,      /* Do not update access times. */
+       MS_NODIRATIME = 2048,   /* Do not update directory access times */
+       MS_BIND = 4096,         /* Use the new linux 2.4.x "mount --bind" feature */
+       MS_MOVE = 8192,         /* Use the new linux 2.4.x "mount --move" feature */
+};
+
+
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+#include <fcntl.h>
+#include <sys/ioctl.h>
+static int use_loop = FALSE;
+#endif
+
+extern int mount(__const char *__special_file, __const char *__dir,
+                                __const char *__fstype, unsigned long int __rwflag,
+                                __const void *__data);
+extern int umount(__const char *__special_file);
+extern int umount2(__const char *__special_file, int __flags);
+
+extern int sysfs(int option, unsigned int fs_index, char *buf);
+
+struct mount_options {
+       const char *name;
+       unsigned long and;
+       unsigned long or;
+};
+
+static const struct mount_options mount_options[] = {
+       {"async", ~MS_SYNCHRONOUS, 0},
+       {"atime", ~0, ~MS_NOATIME},
+       {"defaults", ~0, 0},
+       {"noauto", ~0, 0},
+       {"dev", ~MS_NODEV, 0},
+       {"diratime", ~0, ~MS_NODIRATIME},
+       {"exec", ~MS_NOEXEC, 0},
+       {"noatime", ~0, MS_NOATIME},
+       {"nodev", ~0, MS_NODEV},
+       {"nodiratime", ~0, MS_NODIRATIME},
+       {"noexec", ~0, MS_NOEXEC},
+       {"nosuid", ~0, MS_NOSUID},
+       {"remount", ~0, MS_REMOUNT},
+       {"ro", ~0, MS_RDONLY},
+       {"rw", ~MS_RDONLY, 0},
+       {"suid", ~MS_NOSUID, 0},
+       {"sync", ~0, MS_SYNCHRONOUS},
+       {"bind", ~0, MS_BIND},
+       {"move", ~0, MS_MOVE},
+       {0, 0, 0}
+};
+
+static int
+do_mount(char *specialfile, char *dir, char *filesystemtype, long flags,
+                void *string_flags, int useMtab, int fakeIt, char *mtab_opts,
+                int mount_all)
+{
+       int status = 0;
+
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+       char *lofile = NULL;
+#endif
+
+       if (!fakeIt) {
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+               if (use_loop == TRUE) {
+                       int loro = flags & MS_RDONLY;
+
+                       lofile = specialfile;
+
+                       specialfile = find_unused_loop_device();
+                       if (specialfile == NULL) {
+                               bb_error_msg_and_die("Could not find a spare loop device");
+                       }
+                       if (set_loop(specialfile, lofile, 0, &loro)) {
+                               bb_error_msg_and_die("Could not setup loop device");
+                       }
+                       if (!(flags & MS_RDONLY) && loro) {     /* loop is ro, but wanted rw */
+                               bb_error_msg("WARNING: loop device is read-only");
+                               flags |= MS_RDONLY;
+                       }
+               }
+#endif
+               status = mount(specialfile, dir, filesystemtype, flags, string_flags);
+               if (status < 0 && errno == EROFS) {
+                       bb_error_msg("%s is write-protected, mounting read-only",
+                                         specialfile);
+                       status = mount(specialfile, dir, filesystemtype, flags |=
+                                                  MS_RDONLY, string_flags);
+               }
+               /* Don't whine about already mounted filesystems when mounting all. */
+               if (status < 0 && errno == EBUSY && mount_all) {
+                       return TRUE;
+               }
+       }
+
+
+       /* If the mount was sucessful, do anything needed, then return TRUE */
+       if (status == 0 || fakeIt == TRUE) {
+
+#if defined CONFIG_FEATURE_MTAB_SUPPORT
+               if (useMtab) {
+                       erase_mtab(specialfile);        /* Clean any stale entries */
+                       write_mtab(specialfile, dir, filesystemtype, flags, mtab_opts);
+               }
+#endif
+               return (TRUE);
+       }
+
+       /* Bummer.  mount failed.  Clean up */
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+       if (lofile != NULL) {
+               del_loop(specialfile);
+       }
+#endif
+
+       if (errno == EPERM) {
+               bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+       }
+
+       return (FALSE);
+}
+
+
+static void paste_str(char **s1, const char *s2)
+{
+       *s1 = xrealloc(*s1, strlen(*s1) + strlen(s2) + 1);
+       strcat(*s1, s2);
+}
+
+/* Seperate standard mount options from the nonstandard string options */
+static void parse_mount_options(char *options, int *flags, char **strflags)
+{
+       while (options) {
+               int gotone = FALSE;
+               char *comma = strchr(options, ',');
+               const struct mount_options *f = mount_options;
+
+               if (comma) {
+                       *comma = '\0';
+               }
+
+               while (f->name != 0) {
+                       if (strcasecmp(f->name, options) == 0) {
+
+                               *flags &= f->and;
+                               *flags |= f->or;
+                               gotone = TRUE;
+                               break;
+                       }
+                       f++;
+               }
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+               if (!strcasecmp("loop", options)) {     /* loop device support */
+                       use_loop = TRUE;
+                       gotone = TRUE;
+               }
+#endif
+               if (!gotone) {
+                       if (**strflags) {
+                               /* have previous parsed options */
+                               paste_str(strflags, ",");
+                       }
+                       paste_str(strflags, options);
+               }
+               if (comma) {
+                       *comma = ',';
+                       options = ++comma;
+               } else {
+                       break;
+               }
+       }
+}
+
+static int mount_one(char *blockDevice, char *directory, char *filesystemType,
+                                        unsigned long flags, char *string_flags, int useMtab,
+                                        int fakeIt, char *mtab_opts, int whineOnErrors,
+                                        int mount_all)
+{
+       int status = 0;
+       if (strcmp(filesystemType, "auto") == 0) {
+               char buf[255];
+               FILE *f;
+               int read_proc = 0;
+
+               f = fopen("/etc/filesystems", "r");
+
+               if (f) {
+                       while (fgets(buf, sizeof(buf), f)) {
+                               if (*buf == '*') {
+                                       read_proc = 1;
+                               } else if (*buf == '#') {
+                                       continue;
+                               } else {
+                                       filesystemType = buf;
+
+                                       /* Add NULL termination to each line */
+                                       while (*filesystemType && !isspace(*filesystemType)) {
+                                               filesystemType++;
+                                       }
+                                       *filesystemType = '\0';
+
+                                       filesystemType = buf;
+
+                                       if (bb_strlen(filesystemType)) {
+                                               status = do_mount(blockDevice, directory, filesystemType,
+                                                                        flags | MS_MGC_VAL, string_flags,
+                                                                        useMtab, fakeIt, mtab_opts, mount_all);
+                                               if (status) {
+                                                       break;
+                                               }
+                                       }
+
+                               }
+                       }
+                       fclose(f);
+               } else {
+                       read_proc = 1;
+               }
+
+               if (read_proc && !status) {
+
+                       f = bb_xfopen("/proc/filesystems", "r");
+
+                       while (fgets(buf, sizeof(buf), f) != NULL) {
+                               filesystemType = buf;
+                               if (*filesystemType == '\t') {  /* Not a nodev filesystem */
+
+                                       /* Add NULL termination to each line */
+                                       while (*filesystemType && *filesystemType != '\n') {
+                                               filesystemType++;
+                                       }
+                                       *filesystemType = '\0';
+
+                                       filesystemType = buf;
+                                       filesystemType++;       /* hop past tab */
+
+                                       status = do_mount(blockDevice, directory, filesystemType,
+                                                                flags | MS_MGC_VAL, string_flags, useMtab,
+                                                                fakeIt, mtab_opts, mount_all);
+                                       if (status) {
+                                               break;
+                                       }
+                               }
+                       }
+                       fclose(f);
+               }
+       } else {
+               status = do_mount(blockDevice, directory, filesystemType,
+                                        flags | MS_MGC_VAL, string_flags, useMtab, fakeIt,
+                                        mtab_opts, mount_all);
+       }
+
+       if (!status) {
+               if (whineOnErrors) {
+                       bb_perror_msg("Mounting %s on %s failed", blockDevice, directory);
+               }
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+static void show_mounts(char *onlytype)
+{
+       FILE *mountTable = setmntent(bb_path_mtab_file, "r");
+
+       if (mountTable) {
+               struct mntent *m;
+
+               while ((m = getmntent(mountTable)) != 0) {
+                       char *blockDevice = m->mnt_fsname;
+
+                       if (strcmp(blockDevice, "rootfs") == 0) {
+                               continue;
+                       } else if (strcmp(blockDevice, "/dev/root") == 0) {
+                               blockDevice = find_real_root_device_name();
+                       }
+                       if (!onlytype || (strcmp(m->mnt_type, onlytype) == 0)) {
+                               printf("%s on %s type %s (%s)\n", blockDevice, m->mnt_dir,
+                                          m->mnt_type, m->mnt_opts);
+                       }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+                       if (blockDevice != m->mnt_fsname) {
+                               free(blockDevice);
+                       }
+#endif
+               }
+               endmntent(mountTable);
+       } else {
+               bb_perror_msg_and_die(bb_path_mtab_file);
+       }
+       exit(EXIT_SUCCESS);
+}
+
+extern int mount_main(int argc, char **argv)
+{
+       struct stat statbuf;
+       char *string_flags = bb_xstrdup("");
+       char *extra_opts;
+       int flags = 0;
+       char *filesystemType = "auto";
+       int got_filesystemType = 0;
+       char *device = xmalloc(PATH_MAX);
+       char *directory = xmalloc(PATH_MAX);
+       struct mntent *m = NULL;
+       int all = FALSE;
+       int fakeIt = FALSE;
+       int useMtab = TRUE;
+       int rc = EXIT_FAILURE;
+       FILE *f = 0;
+       int opt;
+
+       /* Parse options */
+       while ((opt = getopt(argc, argv, "o:rt:wafnv")) > 0) {
+               switch (opt) {
+               case 'o':
+                       parse_mount_options(optarg, &flags, &string_flags);
+                       break;
+               case 'r':
+                       flags |= MS_RDONLY;
+                       break;
+               case 't':
+                       filesystemType = optarg;
+                       got_filesystemType = 1;
+                       break;
+               case 'w':
+                       flags &= ~MS_RDONLY;
+                       break;
+               case 'a':
+                       all = TRUE;
+                       break;
+               case 'f':
+                       fakeIt = TRUE;
+                       break;
+               case 'n':
+#ifdef CONFIG_FEATURE_MTAB_SUPPORT
+                       useMtab = FALSE;
+#endif
+                       break;
+               case 'v':
+                       break;          /* ignore -v */
+               }
+       }
+
+       if (!all && (optind == argc)) {
+               show_mounts(got_filesystemType ? filesystemType : NULL);
+       }
+
+       if (optind < argc) {
+               /* if device is a filename get its real path */
+               if (stat(argv[optind], &statbuf) == 0) {
+                       char *tmp = bb_simplify_path(argv[optind]);
+
+                       safe_strncpy(device, tmp, PATH_MAX);
+               } else {
+                       safe_strncpy(device, argv[optind], PATH_MAX);
+               }
+       }
+
+       if (optind + 1 < argc)
+               directory = bb_simplify_path(argv[optind + 1]);
+
+       if (all || optind + 1 == argc) {
+               f = setmntent("/etc/fstab", "r");
+
+               if (f == NULL)
+                       bb_perror_msg_and_die("\nCannot read /etc/fstab");
+
+               while ((m = getmntent(f)) != NULL) {
+                       if (!all && (optind + 1 == argc)
+                               && ((strcmp(device, m->mnt_fsname) != 0)
+                                       && (strcmp(device, m->mnt_dir) != 0))) {
+                               continue;
+                       }
+
+                       if (all && (    /* If we're mounting 'all' */
+                                                  (strstr(m->mnt_opts, "noauto")) ||   /* and the file system isn't noauto, */
+                                                  (strstr(m->mnt_type, "swap")))) /* and isn't swap, then mount it */
+                       {
+                               continue;
+                       }
+
+                       if (all || flags == 0) {        /* Allow single mount to override fstab flags */
+                               flags = 0;
+                               string_flags[0] = 0;
+                               parse_mount_options(m->mnt_opts, &flags, &string_flags);
+                       }
+
+                       strcpy(device, m->mnt_fsname);
+                       strcpy(directory, m->mnt_dir);
+                       filesystemType = bb_xstrdup(m->mnt_type);
+                 singlemount:
+                       extra_opts = string_flags;
+                       rc = EXIT_SUCCESS;
+#ifdef CONFIG_NFSMOUNT
+                       if (strchr(device, ':') != NULL) {
+                               filesystemType = "nfs";
+                               if (nfsmount
+                                       (device, directory, &flags, &extra_opts, &string_flags,
+                                        1)) {
+                                       bb_perror_msg("nfsmount failed");
+                                       rc = EXIT_FAILURE;
+                               }
+                       }
+#endif
+                       if (!mount_one
+                               (device, directory, filesystemType, flags, string_flags,
+                                useMtab, fakeIt, extra_opts, TRUE, all)) {
+                               rc = EXIT_FAILURE;
+                       }
+                       if (!all) {
+                               break;
+                       }
+               }
+               if (f) {
+                       endmntent(f);
+               }
+               if (!all && f && m == NULL) {
+                       fprintf(stderr, "Can't find %s in /etc/fstab\n", device);
+               }
+               return rc;
+       }
+
+       goto singlemount;
+}
diff --git a/busybox/util-linux/nfsmount.c b/busybox/util-linux/nfsmount.c
new file mode 100644 (file)
index 0000000..0ebab80
--- /dev/null
@@ -0,0 +1,1026 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * nfsmount.c -- Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.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, 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.
+ *
+ * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
+ * numbers to be specified on the command line.
+ *
+ * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ */
+
+/*
+ * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include "busybox.h"
+#undef TRUE
+#undef FALSE
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include "nfsmount.h"
+
+
+/*
+ * NFS stats. The good thing with these values is that NFSv3 errors are
+ * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
+ * no-one uses anyway), so we can happily mix code as long as we make sure
+ * no NFSv3 errors are returned to NFSv2 clients.
+ * Error codes that have a `--' in the v2 column are not part of the
+ * standard, but seem to be widely used nevertheless.
+ */
+enum nfs_stat {
+       NFS_OK = 0,                     /* v2 v3 */
+       NFSERR_PERM = 1,                /* v2 v3 */
+       NFSERR_NOENT = 2,               /* v2 v3 */
+       NFSERR_IO = 5,                  /* v2 v3 */
+       NFSERR_NXIO = 6,                /* v2 v3 */
+       NFSERR_EAGAIN = 11,             /* v2 v3 */
+       NFSERR_ACCES = 13,              /* v2 v3 */
+       NFSERR_EXIST = 17,              /* v2 v3 */
+       NFSERR_XDEV = 18,               /*    v3 */
+       NFSERR_NODEV = 19,              /* v2 v3 */
+       NFSERR_NOTDIR = 20,             /* v2 v3 */
+       NFSERR_ISDIR = 21,              /* v2 v3 */
+       NFSERR_INVAL = 22,              /* v2 v3 that Sun forgot */
+       NFSERR_FBIG = 27,               /* v2 v3 */
+       NFSERR_NOSPC = 28,              /* v2 v3 */
+       NFSERR_ROFS = 30,               /* v2 v3 */
+       NFSERR_MLINK = 31,              /*    v3 */
+       NFSERR_OPNOTSUPP = 45,          /* v2 v3 */
+       NFSERR_NAMETOOLONG = 63,        /* v2 v3 */
+       NFSERR_NOTEMPTY = 66,           /* v2 v3 */
+       NFSERR_DQUOT = 69,              /* v2 v3 */
+       NFSERR_STALE = 70,              /* v2 v3 */
+       NFSERR_REMOTE = 71,             /* v2 v3 */
+       NFSERR_WFLUSH = 99,             /* v2    */
+       NFSERR_BADHANDLE = 10001,       /*    v3 */
+       NFSERR_NOT_SYNC = 10002,        /*    v3 */
+       NFSERR_BAD_COOKIE = 10003,      /*    v3 */
+       NFSERR_NOTSUPP = 10004,         /*    v3 */
+       NFSERR_TOOSMALL = 10005,        /*    v3 */
+       NFSERR_SERVERFAULT = 10006,     /*    v3 */
+       NFSERR_BADTYPE = 10007,         /*    v3 */
+       NFSERR_JUKEBOX = 10008          /*    v3 */
+};
+
+#define NFS_PROGRAM    100003
+
+
+
+#ifndef NFS_FHSIZE
+static const int NFS_FHSIZE = 32;
+#endif
+#ifndef NFS_PORT
+static const int NFS_PORT = 2049;
+#endif
+
+/* Disable the nls stuff */
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) /* empty */
+# undef textdomain
+# define textdomain(Domain) /* empty */
+# define _(Text) (Text)
+# define N_(Text) (Text)
+
+static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
+static const int MS_RDONLY = 1;      /* Mount read-only */
+static const int MS_NOSUID = 2;      /* Ignore suid and sgid bits */
+static const int MS_NODEV = 4;      /* Disallow access to device special files */
+static const int MS_NOEXEC = 8;      /* Disallow program execution */
+static const int MS_SYNCHRONOUS = 16;      /* Writes are synced at once */
+static const int MS_REMOUNT = 32;      /* Alter flags of a mounted FS */
+static const int MS_MANDLOCK = 64;      /* Allow mandatory locks on an FS */
+static const int S_QUOTA = 128;     /* Quota initialized for file/directory/symlink */
+static const int S_APPEND = 256;     /* Append-only file */
+static const int S_IMMUTABLE = 512;     /* Immutable file */
+static const int MS_NOATIME = 1024;    /* Do not update access times. */
+static const int MS_NODIRATIME = 2048;    /* Do not update directory access times */
+
+
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+/* NOTE: Do not make this into a 'static const int' because the pre-processor
+ * needs to test this value in some #if statements. */
+#define NFS_MOUNT_VERSION 4
+
+struct nfs2_fh {
+        char                    data[32];
+};
+struct nfs3_fh {
+        unsigned short          size;
+        unsigned char           data[64];
+};
+
+struct nfs_mount_data {
+       int             version;                /* 1 */
+       int             fd;                     /* 1 */
+       struct nfs2_fh  old_root;               /* 1 */
+       int             flags;                  /* 1 */
+       int             rsize;                  /* 1 */
+       int             wsize;                  /* 1 */
+       int             timeo;                  /* 1 */
+       int             retrans;                /* 1 */
+       int             acregmin;               /* 1 */
+       int             acregmax;               /* 1 */
+       int             acdirmin;               /* 1 */
+       int             acdirmax;               /* 1 */
+       struct sockaddr_in addr;                /* 1 */
+       char            hostname[256];          /* 1 */
+       int             namlen;                 /* 2 */
+       unsigned int    bsize;                  /* 3 */
+       struct nfs3_fh  root;                   /* 4 */
+};
+
+/* bits in the flags field */
+
+static const int NFS_MOUNT_SOFT = 0x0001;      /* 1 */
+static const int NFS_MOUNT_INTR = 0x0002;      /* 1 */
+static const int NFS_MOUNT_SECURE = 0x0004;    /* 1 */
+static const int NFS_MOUNT_POSIX = 0x0008;     /* 1 */
+static const int NFS_MOUNT_NOCTO = 0x0010;     /* 1 */
+static const int NFS_MOUNT_NOAC = 0x0020;      /* 1 */
+static const int NFS_MOUNT_TCP = 0x0040;       /* 2 */
+static const int NFS_MOUNT_VER3 = 0x0080;      /* 3 */
+static const int NFS_MOUNT_KERBEROS = 0x0100;  /* 3 */
+static const int NFS_MOUNT_NONLM = 0x0200;     /* 3 */
+
+
+#define UTIL_LINUX_VERSION "2.10m"
+#define util_linux_version "util-linux-2.10m"
+
+#define HAVE_inet_aton
+#define HAVE_scsi_h
+#define HAVE_blkpg_h
+#define HAVE_kd_h
+#define HAVE_termcap
+#define HAVE_locale_h
+#define HAVE_libintl_h
+#define ENABLE_NLS
+#define HAVE_langinfo_h
+#define HAVE_progname
+#define HAVE_openpty
+#define HAVE_nanosleep
+#define HAVE_personality
+#define HAVE_tm_gmtoff
+
+static char *nfs_strerror(int status);
+
+#define MAKE_VERSION(p,q,r)    (65536*(p) + 256*(q) + (r))
+#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
+
+static const int EX_FAIL = 32;       /* mount failure */
+static const int EX_BG = 256;       /* retry in background (internal only) */
+
+
+/*
+ * nfs_mount_version according to the sources seen at compile time.
+ */
+static int nfs_mount_version;
+
+/*
+ * Unfortunately, the kernel prints annoying console messages
+ * in case of an unexpected nfs mount version (instead of
+ * just returning some error).  Therefore we'll have to try
+ * and figure out what version the kernel expects.
+ *
+ * Variables:
+ *     KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
+ *     NFS_MOUNT_VERSION: these nfsmount sources at compile time
+ *     nfs_mount_version: version this source and running kernel can handle
+ */
+static void
+find_kernel_nfs_mount_version(void)
+{
+       static int kernel_version = 0;
+
+       if (kernel_version)
+               return;
+
+       nfs_mount_version = NFS_MOUNT_VERSION; /* default */
+
+       kernel_version = get_kernel_revision();
+       if (kernel_version) {
+               if (kernel_version < MAKE_VERSION(2,1,32))
+                       nfs_mount_version = 1;
+               else if (kernel_version < MAKE_VERSION(2,2,18) ||
+                               (kernel_version >=   MAKE_VERSION(2,3,0) &&
+                                kernel_version < MAKE_VERSION(2,3,99)))
+                       nfs_mount_version = 3;
+               else
+                       nfs_mount_version = 4; /* since 2.3.99pre4 */
+       }
+       if (nfs_mount_version > NFS_MOUNT_VERSION)
+               nfs_mount_version = NFS_MOUNT_VERSION;
+}
+
+static struct pmap *
+get_mountport(struct sockaddr_in *server_addr,
+      long unsigned prog,
+      long unsigned version,
+      long unsigned proto,
+      long unsigned port)
+{
+struct pmaplist *pmap;
+static struct pmap p = {0, 0, 0, 0};
+
+server_addr->sin_port = PMAPPORT;
+pmap = pmap_getmaps(server_addr);
+
+if (version > MAX_NFSPROT)
+       version = MAX_NFSPROT;
+if (!prog)
+       prog = MOUNTPROG;
+p.pm_prog = prog;
+p.pm_vers = version;
+p.pm_prot = proto;
+p.pm_port = port;
+
+while (pmap) {
+       if (pmap->pml_map.pm_prog != prog)
+               goto next;
+       if (!version && p.pm_vers > pmap->pml_map.pm_vers)
+               goto next;
+       if (version > 2 && pmap->pml_map.pm_vers != version)
+               goto next;
+       if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
+               goto next;
+       if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
+           (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
+           (port && pmap->pml_map.pm_port != port))
+               goto next;
+       memcpy(&p, &pmap->pml_map, sizeof(p));
+next:
+       pmap = pmap->pml_next;
+}
+if (!p.pm_vers)
+       p.pm_vers = MOUNTVERS;
+if (!p.pm_port)
+       p.pm_port = MOUNTPORT;
+if (!p.pm_prot)
+       p.pm_prot = IPPROTO_TCP;
+return &p;
+}
+
+int nfsmount(const char *spec, const char *node, int *flags,
+            char **extra_opts, char **mount_opts, int running_bg)
+{
+       static char *prev_bg_host;
+       char hostdir[1024];
+       CLIENT *mclient;
+       char *hostname;
+       char *pathname;
+       char *old_opts;
+       char *mounthost=NULL;
+       char new_opts[1024];
+       struct timeval total_timeout;
+       enum clnt_stat clnt_stat;
+       struct nfs_mount_data data;
+       char *opt, *opteq;
+       int val;
+       struct hostent *hp;
+       struct sockaddr_in server_addr;
+       struct sockaddr_in mount_server_addr;
+       struct pmap* pm_mnt;
+       int msock, fsock;
+       struct timeval retry_timeout;
+       union {
+               struct fhstatus nfsv2;
+               struct mountres3 nfsv3;
+       } status;
+       struct stat statbuf;
+       char *s;
+       int port;
+       int mountport;
+       int proto;
+       int bg;
+       int soft;
+       int intr;
+       int posix;
+       int nocto;
+       int noac;
+       int nolock;
+       int retry;
+       int tcp;
+       int mountprog;
+       int mountvers;
+       int nfsprog;
+       int nfsvers;
+       int retval;
+       time_t t;
+       time_t prevt;
+       time_t timeout;
+
+       find_kernel_nfs_mount_version();
+
+       retval = EX_FAIL;
+       msock = fsock = -1;
+       mclient = NULL;
+       if (strlen(spec) >= sizeof(hostdir)) {
+               bb_error_msg("excessively long host:dir argument");
+               goto fail;
+       }
+       strcpy(hostdir, spec);
+       if ((s = strchr(hostdir, ':'))) {
+               hostname = hostdir;
+               pathname = s + 1;
+               *s = '\0';
+               /* Ignore all but first hostname in replicated mounts
+                  until they can be fully supported. (mack@sgi.com) */
+               if ((s = strchr(hostdir, ','))) {
+                       *s = '\0';
+                       bb_error_msg("warning: multiple hostnames not supported");
+               }
+       } else {
+               bb_error_msg("directory to mount not in host:dir format");
+               goto fail;
+       }
+
+       server_addr.sin_family = AF_INET;
+#ifdef HAVE_inet_aton
+       if (!inet_aton(hostname, &server_addr.sin_addr))
+#endif
+       {
+               if ((hp = gethostbyname(hostname)) == NULL) {
+                       bb_herror_msg("%s", hostname);
+                       goto fail;
+               } else {
+                       if (hp->h_length > sizeof(struct in_addr)) {
+                               bb_error_msg("got bad hp->h_length");
+                               hp->h_length = sizeof(struct in_addr);
+                       }
+                       memcpy(&server_addr.sin_addr,
+                              hp->h_addr, hp->h_length);
+               }
+       }
+
+       memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
+
+       /* add IP address to mtab options for use when unmounting */
+
+       s = inet_ntoa(server_addr.sin_addr);
+       old_opts = *extra_opts;
+       if (!old_opts)
+               old_opts = "";
+       if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
+               bb_error_msg("excessively long option argument");
+               goto fail;
+       }
+       sprintf(new_opts, "%s%saddr=%s",
+               old_opts, *old_opts ? "," : "", s);
+       *extra_opts = bb_xstrdup(new_opts);
+
+       /* Set default options.
+        * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
+        * let the kernel decide.
+        * timeo is filled in after we know whether it'll be TCP or UDP. */
+       memset(&data, 0, sizeof(data));
+       data.retrans    = 3;
+       data.acregmin   = 3;
+       data.acregmax   = 60;
+       data.acdirmin   = 30;
+       data.acdirmax   = 60;
+#if NFS_MOUNT_VERSION >= 2
+       data.namlen     = NAME_MAX;
+#endif
+
+       bg = 0;
+       soft = 0;
+       intr = 0;
+       posix = 0;
+       nocto = 0;
+       nolock = 0;
+       noac = 0;
+       retry = 10000;          /* 10000 minutes ~ 1 week */
+       tcp = 0;
+
+       mountprog = MOUNTPROG;
+       mountvers = 0;
+       port = 0;
+       mountport = 0;
+       nfsprog = NFS_PROGRAM;
+       nfsvers = 0;
+
+       /* parse options */
+
+       for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
+               if ((opteq = strchr(opt, '='))) {
+                       val = atoi(opteq + 1);
+                       *opteq = '\0';
+                       if (!strcmp(opt, "rsize"))
+                               data.rsize = val;
+                       else if (!strcmp(opt, "wsize"))
+                               data.wsize = val;
+                       else if (!strcmp(opt, "timeo"))
+                               data.timeo = val;
+                       else if (!strcmp(opt, "retrans"))
+                               data.retrans = val;
+                       else if (!strcmp(opt, "acregmin"))
+                               data.acregmin = val;
+                       else if (!strcmp(opt, "acregmax"))
+                               data.acregmax = val;
+                       else if (!strcmp(opt, "acdirmin"))
+                               data.acdirmin = val;
+                       else if (!strcmp(opt, "acdirmax"))
+                               data.acdirmax = val;
+                       else if (!strcmp(opt, "actimeo")) {
+                               data.acregmin = val;
+                               data.acregmax = val;
+                               data.acdirmin = val;
+                               data.acdirmax = val;
+                       }
+                       else if (!strcmp(opt, "retry"))
+                               retry = val;
+                       else if (!strcmp(opt, "port"))
+                               port = val;
+                       else if (!strcmp(opt, "mountport"))
+                               mountport = val;
+                       else if (!strcmp(opt, "mounthost"))
+                               mounthost=bb_xstrndup(opteq+1,
+                                                 strcspn(opteq+1," \t\n\r,"));
+                       else if (!strcmp(opt, "mountprog"))
+                               mountprog = val;
+                       else if (!strcmp(opt, "mountvers"))
+                               mountvers = val;
+                       else if (!strcmp(opt, "nfsprog"))
+                               nfsprog = val;
+                       else if (!strcmp(opt, "nfsvers") ||
+                                !strcmp(opt, "vers"))
+                               nfsvers = val;
+                       else if (!strcmp(opt, "proto")) {
+                               if (!strncmp(opteq+1, "tcp", 3))
+                                       tcp = 1;
+                               else if (!strncmp(opteq+1, "udp", 3))
+                                       tcp = 0;
+                               else
+                                       printf(_("Warning: Unrecognized proto= option.\n"));
+                       } else if (!strcmp(opt, "namlen")) {
+#if NFS_MOUNT_VERSION >= 2
+                               if (nfs_mount_version >= 2)
+                                       data.namlen = val;
+                               else
+#endif
+                               printf(_("Warning: Option namlen is not supported.\n"));
+                       } else if (!strcmp(opt, "addr"))
+                               /* ignore */;
+                       else {
+                               printf(_("unknown nfs mount parameter: "
+                                      "%s=%d\n"), opt, val);
+                               goto fail;
+                       }
+               }
+               else {
+                       val = 1;
+                       if (!strncmp(opt, "no", 2)) {
+                               val = 0;
+                               opt += 2;
+                       }
+                       if (!strcmp(opt, "bg"))
+                               bg = val;
+                       else if (!strcmp(opt, "fg"))
+                               bg = !val;
+                       else if (!strcmp(opt, "soft"))
+                               soft = val;
+                       else if (!strcmp(opt, "hard"))
+                               soft = !val;
+                       else if (!strcmp(opt, "intr"))
+                               intr = val;
+                       else if (!strcmp(opt, "posix"))
+                               posix = val;
+                       else if (!strcmp(opt, "cto"))
+                               nocto = !val;
+                       else if (!strcmp(opt, "ac"))
+                               noac = !val;
+                       else if (!strcmp(opt, "tcp"))
+                               tcp = val;
+                       else if (!strcmp(opt, "udp"))
+                               tcp = !val;
+                       else if (!strcmp(opt, "lock")) {
+                               if (nfs_mount_version >= 3)
+                                       nolock = !val;
+                               else
+                                       printf(_("Warning: option nolock is not supported.\n"));
+                       } else {
+                               printf(_("unknown nfs mount option: "
+                                          "%s%s\n"), val ? "" : "no", opt);
+                               goto fail;
+                       }
+               }
+       }
+       proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+
+       data.flags = (soft ? NFS_MOUNT_SOFT : 0)
+               | (intr ? NFS_MOUNT_INTR : 0)
+               | (posix ? NFS_MOUNT_POSIX : 0)
+               | (nocto ? NFS_MOUNT_NOCTO : 0)
+               | (noac ? NFS_MOUNT_NOAC : 0);
+#if NFS_MOUNT_VERSION >= 2
+       if (nfs_mount_version >= 2)
+               data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
+#endif
+#if NFS_MOUNT_VERSION >= 3
+       if (nfs_mount_version >= 3)
+               data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
+#endif
+       if (nfsvers > MAX_NFSPROT) {
+               bb_error_msg("NFSv%d not supported!", nfsvers);
+               return 0;
+       }
+       if (mountvers > MAX_NFSPROT) {
+               bb_error_msg("NFSv%d not supported!", nfsvers);
+               return 0;
+       }
+       if (nfsvers && !mountvers)
+               mountvers = (nfsvers < 3) ? 1 : nfsvers;
+       if (nfsvers && nfsvers < mountvers) {
+               mountvers = nfsvers;
+       }
+
+       /* Adjust options if none specified */
+       if (!data.timeo)
+               data.timeo = tcp ? 70 : 7;
+
+#ifdef NFS_MOUNT_DEBUG
+       printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+               data.rsize, data.wsize, data.timeo, data.retrans);
+       printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
+               data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+       printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
+               port, bg, retry, data.flags);
+       printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
+               mountprog, mountvers, nfsprog, nfsvers);
+       printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
+               (data.flags & NFS_MOUNT_SOFT) != 0,
+               (data.flags & NFS_MOUNT_INTR) != 0,
+               (data.flags & NFS_MOUNT_POSIX) != 0,
+               (data.flags & NFS_MOUNT_NOCTO) != 0,
+               (data.flags & NFS_MOUNT_NOAC) != 0);
+#if NFS_MOUNT_VERSION >= 2
+       printf("tcp = %d\n",
+               (data.flags & NFS_MOUNT_TCP) != 0);
+#endif
+#endif
+
+       data.version = nfs_mount_version;
+
+       if (*flags & MS_REMOUNT)
+               goto copy_data_and_return;
+
+       /*
+        * If the previous mount operation on the same host was
+        * backgrounded, and the "bg" for this mount is also set,
+        * give up immediately, to avoid the initial timeout.
+        */
+       if (bg && !running_bg &&
+           prev_bg_host && strcmp(hostname, prev_bg_host) == 0) {
+               if (retry > 0)
+                       retval = EX_BG;
+               return retval;
+       }
+
+       /* create mount deamon client */
+       /* See if the nfs host = mount host. */
+       if (mounthost) {
+         if (mounthost[0] >= '0' && mounthost[0] <= '9') {
+           mount_server_addr.sin_family = AF_INET;
+           mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
+         } else {
+                 if ((hp = gethostbyname(mounthost)) == NULL) {
+                         bb_herror_msg("%s", mounthost);
+                         goto fail;
+                 } else {
+                         if (hp->h_length > sizeof(struct in_addr)) {
+                                 bb_error_msg("got bad hp->h_length?");
+                                 hp->h_length = sizeof(struct in_addr);
+                         }
+                         mount_server_addr.sin_family = AF_INET;
+                         memcpy(&mount_server_addr.sin_addr,
+                                hp->h_addr, hp->h_length);
+                 }
+         }
+       }
+
+       /*
+        * The following loop implements the mount retries. On the first
+        * call, "running_bg" is 0. When the mount times out, and the
+        * "bg" option is set, the exit status EX_BG will be returned.
+        * For a backgrounded mount, there will be a second call by the
+        * child process with "running_bg" set to 1.
+        *
+        * The case where the mount point is not present and the "bg"
+        * option is set, is treated as a timeout. This is done to
+        * support nested mounts.
+        *
+        * The "retry" count specified by the user is the number of
+        * minutes to retry before giving up.
+        *
+        * Only the first error message will be displayed.
+        */
+       retry_timeout.tv_sec = 3;
+       retry_timeout.tv_usec = 0;
+       total_timeout.tv_sec = 20;
+       total_timeout.tv_usec = 0;
+       timeout = time(NULL) + 60 * retry;
+       prevt = 0;
+       t = 30;
+       val = 1;
+       for (;;) {
+               if (bg && stat(node, &statbuf) == -1) {
+                       if (running_bg) {
+                               sleep(val);     /* 1, 2, 4, 8, 16, 30, ... */
+                               val *= 2;
+                               if (val > 30)
+                                       val = 30;
+                       }
+               } else {
+                       /* be careful not to use too many CPU cycles */
+                       if (t - prevt < 30)
+                               sleep(30);
+
+                       pm_mnt = get_mountport(&mount_server_addr,
+                                      mountprog,
+                                      mountvers,
+                                      proto,
+                                      mountport);
+
+                       /* contact the mount daemon via TCP */
+                       mount_server_addr.sin_port = htons(pm_mnt->pm_port);
+                       msock = RPC_ANYSOCK;
+
+                       switch (pm_mnt->pm_prot) {
+                       case IPPROTO_UDP:
+                               mclient = clntudp_create(&mount_server_addr,
+                                                pm_mnt->pm_prog,
+                                                pm_mnt->pm_vers,
+                                                retry_timeout,
+                                                &msock);
+                 if (mclient)
+                         break;
+                 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
+                 msock = RPC_ANYSOCK;
+               case IPPROTO_TCP:
+                       mclient = clnttcp_create(&mount_server_addr,
+                                                pm_mnt->pm_prog,
+                                                pm_mnt->pm_vers,
+                                                &msock, 0, 0);
+                       break;
+               default:
+                       mclient = 0;
+                       }
+                       if (mclient) {
+                               /* try to mount hostname:pathname */
+                               mclient->cl_auth = authunix_create_default();
+
+                       /* make pointers in xdr_mountres3 NULL so
+                        * that xdr_array allocates memory for us
+                        */
+                       memset(&status, 0, sizeof(status));
+
+                       if (pm_mnt->pm_vers == 3)
+                               clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
+                                                     (xdrproc_t) xdr_dirpath,
+                                                     (caddr_t) &pathname,
+                                                     (xdrproc_t) xdr_mountres3,
+                                                     (caddr_t) &status,
+                                       total_timeout);
+                       else
+                               clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
+                                                     (xdrproc_t) xdr_dirpath,
+                                                     (caddr_t) &pathname,
+                                                     (xdrproc_t) xdr_fhstatus,
+                                                     (caddr_t) &status,
+                                                     total_timeout);
+
+                               if (clnt_stat == RPC_SUCCESS)
+                                       break;          /* we're done */
+                               if (errno != ECONNREFUSED) {
+                                       clnt_perror(mclient, "mount");
+                                       goto fail;      /* don't retry */
+                               }
+                               if (!running_bg && prevt == 0)
+                                       clnt_perror(mclient, "mount");
+                               auth_destroy(mclient->cl_auth);
+                               clnt_destroy(mclient);
+                               mclient = 0;
+                               close(msock);
+                       } else {
+                               if (!running_bg && prevt == 0)
+                                       clnt_pcreateerror("mount");
+                       }
+                       prevt = t;
+               }
+               if (!bg)
+                       goto fail;
+               if (!running_bg) {
+                       prev_bg_host = bb_xstrdup(hostname);
+                       if (retry > 0)
+                               retval = EX_BG;
+                       goto fail;
+               }
+               t = time(NULL);
+               if (t >= timeout)
+                       goto fail;
+       }
+       nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
+
+       if (nfsvers == 2) {
+               if (status.nfsv2.fhs_status != 0) {
+                       bb_error_msg("%s:%s failed, reason given by server: %s",
+                               hostname, pathname,
+                               nfs_strerror(status.nfsv2.fhs_status));
+                       goto fail;
+               }
+               memcpy(data.root.data,
+                      (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+                      NFS_FHSIZE);
+#if NFS_MOUNT_VERSION >= 4
+               data.root.size = NFS_FHSIZE;
+               memcpy(data.old_root.data,
+                      (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+                      NFS_FHSIZE);
+#endif
+       } else {
+#if NFS_MOUNT_VERSION >= 4
+               fhandle3 *my_fhandle;
+               if (status.nfsv3.fhs_status != 0) {
+                       bb_error_msg("%s:%s failed, reason given by server: %s",
+                               hostname, pathname,
+                               nfs_strerror(status.nfsv3.fhs_status));
+                       goto fail;
+               }
+               my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
+               memset(data.old_root.data, 0, NFS_FHSIZE);
+               memset(&data.root, 0, sizeof(data.root));
+               data.root.size = my_fhandle->fhandle3_len;
+               memcpy(data.root.data,
+                      (char *) my_fhandle->fhandle3_val,
+                      my_fhandle->fhandle3_len);
+
+               data.flags |= NFS_MOUNT_VER3;
+#endif
+       }
+
+       /* create nfs socket for kernel */
+
+       if (tcp) {
+               if (nfs_mount_version < 3) {
+                       printf(_("NFS over TCP is not supported.\n"));
+                       goto fail;
+               }
+               fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       } else
+               fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (fsock < 0) {
+               perror(_("nfs socket"));
+               goto fail;
+       }
+       if (bindresvport(fsock, 0) < 0) {
+               perror(_("nfs bindresvport"));
+               goto fail;
+       }
+       if (port == 0) {
+               server_addr.sin_port = PMAPPORT;
+               port = pmap_getport(&server_addr, nfsprog, nfsvers,
+                       tcp ? IPPROTO_TCP : IPPROTO_UDP);
+               if (port == 0)
+                       port = NFS_PORT;
+#ifdef NFS_MOUNT_DEBUG
+               else
+                       printf(_("used portmapper to find NFS port\n"));
+#endif
+       }
+#ifdef NFS_MOUNT_DEBUG
+       printf(_("using port %d for nfs deamon\n"), port);
+#endif
+       server_addr.sin_port = htons(port);
+        /*
+         * connect() the socket for kernels 1.3.10 and below only,
+         * to avoid problems with multihomed hosts.
+         * --Swen
+         */
+       if (get_kernel_revision() <= 66314
+           && connect(fsock, (struct sockaddr *) &server_addr,
+                      sizeof (server_addr)) < 0) {
+               perror(_("nfs connect"));
+               goto fail;
+       }
+
+       /* prepare data structure for kernel */
+
+       data.fd = fsock;
+       memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
+       strncpy(data.hostname, hostname, sizeof(data.hostname));
+
+       /* clean up */
+
+       auth_destroy(mclient->cl_auth);
+       clnt_destroy(mclient);
+       close(msock);
+copy_data_and_return:
+       *mount_opts = xrealloc(*mount_opts, sizeof(data));
+       memcpy(*mount_opts, &data, sizeof(data));
+       return 0;
+
+       /* abort */
+
+fail:
+       if (msock != -1) {
+               if (mclient) {
+                       auth_destroy(mclient->cl_auth);
+                       clnt_destroy(mclient);
+               }
+               close(msock);
+       }
+       if (fsock != -1)
+               close(fsock);
+       return retval;
+}
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ *
+ * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> the symbol errno is reserved for any use,
+ *  it cannot even be used as a struct tag or field name".
+ */
+
+#ifndef EDQUOT
+#define EDQUOT ENOSPC
+#endif
+
+static struct {
+       enum nfs_stat stat;
+       int errnum;
+} nfs_errtbl[] = {
+       { NFS_OK,               0               },
+       { NFSERR_PERM,          EPERM           },
+       { NFSERR_NOENT,         ENOENT          },
+       { NFSERR_IO,            EIO             },
+       { NFSERR_NXIO,          ENXIO           },
+       { NFSERR_ACCES,         EACCES          },
+       { NFSERR_EXIST,         EEXIST          },
+       { NFSERR_NODEV,         ENODEV          },
+       { NFSERR_NOTDIR,        ENOTDIR         },
+       { NFSERR_ISDIR,         EISDIR          },
+#ifdef NFSERR_INVAL
+       { NFSERR_INVAL,         EINVAL          },      /* that Sun forgot */
+#endif
+       { NFSERR_FBIG,          EFBIG           },
+       { NFSERR_NOSPC,         ENOSPC          },
+       { NFSERR_ROFS,          EROFS           },
+       { NFSERR_NAMETOOLONG,   ENAMETOOLONG    },
+       { NFSERR_NOTEMPTY,      ENOTEMPTY       },
+       { NFSERR_DQUOT,         EDQUOT          },
+       { NFSERR_STALE,         ESTALE          },
+#ifdef EWFLUSH
+       { NFSERR_WFLUSH,        EWFLUSH         },
+#endif
+       /* Throw in some NFSv3 values for even more fun (HP returns these) */
+       { 71,                   EREMOTE         },
+
+       { -1,                   EIO             }
+};
+
+static char *nfs_strerror(int status)
+{
+       int i;
+       static char buf[256];
+
+       for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+               if (nfs_errtbl[i].stat == status)
+                       return strerror(nfs_errtbl[i].errnum);
+       }
+       sprintf(buf, _("unknown nfs status return value: %d"), status);
+       return buf;
+}
+
+static bool_t
+xdr_fhandle (XDR *xdrs, fhandle objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_opaque (xdrs, objp, FHSIZE))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_fhstatus (XDR *xdrs, fhstatus *objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_u_int (xdrs, &objp->fhs_status))
+                return FALSE;
+       switch (objp->fhs_status) {
+       case 0:
+                if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_dirpath (XDR *xdrs, dirpath *objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_string (xdrs, objp, MNTPATHLEN))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_fhandle3 (xdrs, &objp->fhandle))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->auth_flavours.auth_flavours_val, (unsigned int *) &objp->auth_flavours.auth_flavours_len, ~0,
+               sizeof (int), (xdrproc_t) xdr_int))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_mountres3 (XDR *xdrs, mountres3 *objp)
+{
+       //register int32_t *buf;
+
+        if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
+                return FALSE;
+       switch (objp->fhs_status) {
+       case MNT_OK:
+                if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))
+                        return FALSE;
+               break;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
diff --git a/busybox/util-linux/nfsmount.h b/busybox/util-linux/nfsmount.h
new file mode 100644 (file)
index 0000000..78a1bdf
--- /dev/null
@@ -0,0 +1,242 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * This file was originally generated using rpcgen.
+ * But now we edit it by hand as needed to make it
+ * shut up...
+ */
+
+#ifndef _NFSMOUNT_H_RPCGEN
+#define _NFSMOUNT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+/*
+ * Copyright (c) 1985, 1990 by Sun Microsystems, Inc.
+ */
+
+/* from @(#)mount.x    1.3 91/03/11 TIRPC 1.0 */
+#ifndef _rpcsvc_mount_h
+#define _rpcsvc_mount_h
+#include <asm/types.h>
+#define MOUNTPORT 635
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+#define FHSIZE3 64
+
+typedef char fhandle[FHSIZE];
+
+typedef struct {
+       unsigned int fhandle3_len;
+       char *fhandle3_val;
+} fhandle3;
+
+enum mountstat3 {
+       MNT_OK = 0,
+       MNT3ERR_PERM = 1,
+       MNT3ERR_NOENT = 2,
+       MNT3ERR_IO = 5,
+       MNT3ERR_ACCES = 13,
+       MNT3ERR_NOTDIR = 20,
+       MNT3ERR_INVAL = 22,
+       MNT3ERR_NAMETOOLONG = 63,
+       MNT3ERR_NOTSUPP = 10004,
+       MNT3ERR_SERVERFAULT = 10006,
+};
+typedef enum mountstat3 mountstat3;
+
+struct fhstatus {
+       unsigned int fhs_status;
+       union {
+               fhandle fhs_fhandle;
+       } fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+
+struct mountres3_ok {
+       fhandle3 fhandle;
+       struct {
+               unsigned int auth_flavours_len;
+               int *auth_flavours_val;
+       } auth_flavours;
+};
+typedef struct mountres3_ok mountres3_ok;
+
+struct mountres3 {
+       mountstat3 fhs_status;
+       union {
+               mountres3_ok mountinfo;
+       } mountres3_u;
+};
+typedef struct mountres3 mountres3;
+
+typedef char *dirpath;
+
+typedef char *name;
+
+typedef struct mountbody *mountlist;
+
+struct mountbody {
+       name ml_hostname;
+       dirpath ml_directory;
+       mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+
+typedef struct groupnode *groups;
+
+struct groupnode {
+       name gr_name;
+       groups gr_next;
+};
+typedef struct groupnode groupnode;
+
+typedef struct exportnode *exports;
+
+struct exportnode {
+       dirpath ex_dir;
+       groups ex_groups;
+       exports ex_next;
+};
+typedef struct exportnode exportnode;
+
+struct ppathcnf {
+       int pc_link_max;
+       short pc_max_canon;
+       short pc_max_input;
+       short pc_name_max;
+       short pc_path_max;
+       short pc_pipe_buf;
+       u_char pc_vdisable;
+       char pc_xxx;
+       short pc_mask[2];
+};
+typedef struct ppathcnf ppathcnf;
+#endif /*!_rpcsvc_mount_h*/
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+#define MOUNTPROC_NULL 0
+extern  void * mountproc_null_1(void *, CLIENT *);
+extern  void * mountproc_null_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT 1
+extern  fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
+extern  fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP 2
+extern  mountlist * mountproc_dump_1(void *, CLIENT *);
+extern  mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT 3
+extern  void * mountproc_umnt_1(dirpath *, CLIENT *);
+extern  void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL 4
+extern  void * mountproc_umntall_1(void *, CLIENT *);
+extern  void * mountproc_umntall_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT 5
+extern  exports * mountproc_export_1(void *, CLIENT *);
+extern  exports * mountproc_export_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORTALL 6
+extern  exports * mountproc_exportall_1(void *, CLIENT *);
+extern  exports * mountproc_exportall_1_svc(void *, struct svc_req *);
+extern int mountprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#define MOUNTVERS_POSIX 2
+
+extern  void * mountproc_null_2(void *, CLIENT *);
+extern  void * mountproc_null_2_svc(void *, struct svc_req *);
+extern  fhstatus * mountproc_mnt_2(dirpath *, CLIENT *);
+extern  fhstatus * mountproc_mnt_2_svc(dirpath *, struct svc_req *);
+extern  mountlist * mountproc_dump_2(void *, CLIENT *);
+extern  mountlist * mountproc_dump_2_svc(void *, struct svc_req *);
+extern  void * mountproc_umnt_2(dirpath *, CLIENT *);
+extern  void * mountproc_umnt_2_svc(dirpath *, struct svc_req *);
+extern  void * mountproc_umntall_2(void *, CLIENT *);
+extern  void * mountproc_umntall_2_svc(void *, struct svc_req *);
+extern  exports * mountproc_export_2(void *, CLIENT *);
+extern  exports * mountproc_export_2_svc(void *, struct svc_req *);
+extern  exports * mountproc_exportall_2(void *, CLIENT *);
+extern  exports * mountproc_exportall_2_svc(void *, struct svc_req *);
+#define MOUNTPROC_PATHCONF 7
+extern  ppathcnf * mountproc_pathconf_2(dirpath *, CLIENT *);
+extern  ppathcnf * mountproc_pathconf_2_svc(dirpath *, struct svc_req *);
+extern int mountprog_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#define MOUNT_V3 3
+
+#define MOUNTPROC3_NULL 0
+extern  void * mountproc3_null_3(void *, CLIENT *);
+extern  void * mountproc3_null_3_svc(void *, struct svc_req *);
+#define MOUNTPROC3_MNT 1
+extern  mountres3 * mountproc3_mnt_3(dirpath *, CLIENT *);
+extern  mountres3 * mountproc3_mnt_3_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC3_DUMP 2
+extern  mountlist * mountproc3_dump_3(void *, CLIENT *);
+extern  mountlist * mountproc3_dump_3_svc(void *, struct svc_req *);
+#define MOUNTPROC3_UMNT 3
+extern  void * mountproc3_umnt_3(dirpath *, CLIENT *);
+extern  void * mountproc3_umnt_3_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC3_UMNTALL 4
+extern  void * mountproc3_umntall_3(void *, CLIENT *);
+extern  void * mountproc3_umntall_3_svc(void *, struct svc_req *);
+#define MOUNTPROC3_EXPORT 5
+extern  exports * mountproc3_export_3(void *, CLIENT *);
+extern  exports * mountproc3_export_3_svc(void *, struct svc_req *);
+extern int mountprog_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+/* the xdr functions */
+
+static  bool_t xdr_fhandle (XDR *, fhandle);
+extern  bool_t xdr_fhandle3 (XDR *, fhandle3*);
+extern  bool_t xdr_mountstat3 (XDR *, mountstat3*);
+extern  bool_t xdr_fhstatus (XDR *, fhstatus*);
+extern  bool_t xdr_mountres3_ok (XDR *, mountres3_ok*);
+extern  bool_t xdr_mountres3 (XDR *, mountres3*);
+extern  bool_t xdr_dirpath (XDR *, dirpath*);
+extern  bool_t xdr_name (XDR *, name*);
+extern  bool_t xdr_mountlist (XDR *, mountlist*);
+extern  bool_t xdr_mountbody (XDR *, mountbody*);
+extern  bool_t xdr_groups (XDR *, groups*);
+extern  bool_t xdr_groupnode (XDR *, groupnode*);
+extern  bool_t xdr_exports (XDR *, exports*);
+extern  bool_t xdr_exportnode (XDR *, exportnode*);
+extern  bool_t xdr_ppathcnf (XDR *, ppathcnf*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_NFSMOUNT_H_RPCGEN */
diff --git a/busybox/util-linux/pivot_root.c b/busybox/util-linux/pivot_root.c
new file mode 100644 (file)
index 0000000..85e180c
--- /dev/null
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pivot_root.c - Change root file system.  Based on util-linux 2.10s
+ *
+ * busyboxed by Evin Robertson
+ * pivot_root syscall stubbed by Erik Andersen, so it will compile
+ *     regardless of the kernel being used.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "busybox.h"
+
+extern int pivot_root(const char * new_root,const char * put_old);
+
+int pivot_root_main(int argc, char **argv)
+{
+    if (argc != 3)
+        bb_show_usage();
+
+       if (pivot_root(argv[1],argv[2]) < 0)
+               bb_perror_msg_and_die("pivot_root");
+
+    return EXIT_SUCCESS;
+
+}
+
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/busybox/util-linux/rdate.c b/busybox/util-linux/rdate.c
new file mode 100644 (file)
index 0000000..a73e8ee
--- /dev/null
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * The Rdate command will ask a time server for the RFC 868 time
+ *  and optionally set the system time.
+ *
+ * by Sterling Huxley <sterling@europa.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "busybox.h"
+
+
+static const int RFC_868_BIAS = 2208988800UL;
+
+static void socket_timeout(int sig)
+{
+       bb_error_msg_and_die("timeout connecting to time server");
+}
+
+static time_t askremotedate(const char *host)
+{
+       unsigned long int nett, localt;
+       struct sockaddr_in s_in;
+       int fd;
+
+       bb_lookup_host(&s_in, host);
+       s_in.sin_port = bb_lookup_port("time", "tcp", 37);
+
+       /* Add a timeout for dead or non accessable servers */
+       alarm(10);
+       signal(SIGALRM, socket_timeout);
+
+       fd = xconnect(&s_in);
+
+       if (safe_read(fd, (void *)&nett, 4) != 4)    /* read time from server */
+               bb_error_msg_and_die("%s did not send the complete time", host);
+
+       close(fd);
+
+       /* convert from network byte order to local byte order.
+        * RFC 868 time is the number of seconds
+        *  since 00:00 (midnight) 1 January 1900 GMT
+        *  the RFC 868 time 2,208,988,800 corresponds to 00:00  1 Jan 1970 GMT
+        * Subtract the RFC 868 time  to get Linux epoch
+        */
+       localt= ntohl(nett) - RFC_868_BIAS;
+
+       return(localt);
+}
+
+int rdate_main(int argc, char **argv)
+{
+       time_t remote_time;
+       int opt;
+       int setdate = 1;
+       int printdate = 1;
+
+       /* Interpret command line args */
+       while ((opt = getopt(argc, argv, "sp")) > 0) {
+               switch (opt) {
+                       case 's':
+                               printdate = 0;
+                               setdate = 1;
+                               break;
+                       case 'p':
+                               printdate = 1;
+                               setdate = 0;
+                               break;
+                       default:
+                               bb_show_usage();
+               }
+       }
+
+       if (optind == argc)
+               bb_show_usage();
+
+       remote_time = askremotedate(argv[optind]);
+
+       if (setdate) {
+               time_t current_time;
+
+               time(&current_time);
+               if (current_time == remote_time)
+                       bb_error_msg("Current time matches remote time.");
+               else
+                       if (stime(&remote_time) < 0)
+                               bb_perror_msg_and_die("Could not set time of day");
+       }
+
+       if (printdate)
+               printf("%s", ctime(&remote_time));
+
+       return EXIT_SUCCESS;
+}
diff --git a/busybox/util-linux/swaponoff.c b/busybox/util-linux/swaponoff.c
new file mode 100644 (file)
index 0000000..7c7031b
--- /dev/null
@@ -0,0 +1,120 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini swapon/swapoff implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <mntent.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/swap.h>
+
+#include "busybox.h"
+
+static int whichApp;    /* default SWAPON_APP */
+
+static const int SWAPON_APP = 0;
+static const int SWAPOFF_APP = 1;
+
+
+static int swap_enable_disable(const char *device)
+{
+       int status;
+       struct stat st;
+
+       if (stat(device, &st) < 0) {
+               bb_perror_msg_and_die("cannot stat %s", device);
+       }
+
+       /* test for holes */
+       if (S_ISREG(st.st_mode)) {
+               if (st.st_blocks * 512 < st.st_size) {
+                       bb_error_msg_and_die("swap file has holes");
+               }
+       }
+
+       if (whichApp == SWAPON_APP)
+               status = swapon(device, 0);
+       else
+               status = swapoff(device);
+
+       if (status != 0) {
+               bb_perror_msg("%s", device);
+               return EXIT_FAILURE;
+       }
+       return EXIT_SUCCESS;
+}
+
+static int do_em_all(void)
+{
+       struct mntent *m;
+       FILE *f = setmntent("/etc/fstab", "r");
+       int err = 0;
+
+       if (f == NULL)
+               bb_perror_msg_and_die("/etc/fstab");
+       while ((m = getmntent(f)) != NULL) {
+               if (strcmp(m->mnt_type, MNTTYPE_SWAP)==0) {
+                       if(swap_enable_disable(m->mnt_fsname) == EXIT_FAILURE)
+                               err++;
+               }
+       }
+       endmntent(f);
+       return err;
+}
+
+
+extern int swap_on_off_main(int argc, char **argv)
+{
+       if (bb_applet_name[5] == 'f') { /* "swapoff" */
+               whichApp = SWAPOFF_APP;
+       }
+
+       if (argc != 2) {
+               goto usage_and_exit;
+       }
+       argc--;
+       argv++;
+
+       /* Parse any options */
+       while (**argv == '-') {
+               while (*++(*argv))
+                       switch (**argv) {
+                       case 'a':
+                               {
+                                       struct stat statBuf;
+
+                                       if (stat("/etc/fstab", &statBuf) < 0)
+                                               bb_error_msg_and_die("/etc/fstab file missing");
+                               }
+                               return do_em_all();
+                               break;
+                       default:
+                               goto usage_and_exit;
+                       }
+       }
+       return swap_enable_disable(*argv);
+
+  usage_and_exit:
+       bb_show_usage();
+}
diff --git a/busybox/util-linux/umount.c b/busybox/util-linux/umount.c
new file mode 100644 (file)
index 0000000..21c2e6e
--- /dev/null
@@ -0,0 +1,298 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini umount implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <mntent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+/* Teach libc5 about realpath -- it includes it but the
+ * prototype is missing... */
+#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
+extern char *realpath(const char *path, char *resolved_path);
+#endif
+
+static const int MNT_FORCE = 1;
+static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
+static const int MS_REMOUNT = 32;      /* Alter flags of a mounted FS.  */
+static const int MS_RDONLY = 1;        /* Mount read-only.  */
+
+extern int mount (__const char *__special_file, __const char *__dir,
+                       __const char *__fstype, unsigned long int __rwflag,
+                       __const void *__data);
+extern int umount (__const char *__special_file);
+extern int umount2 (__const char *__special_file, int __flags);
+
+struct _mtab_entry_t {
+       char *device;
+       char *mountpt;
+       struct _mtab_entry_t *next;
+};
+
+static struct _mtab_entry_t *mtab_cache = NULL;
+
+
+
+#if defined CONFIG_FEATURE_MOUNT_FORCE
+static int doForce = FALSE;
+#endif
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+static int freeLoop = TRUE;
+#endif
+#if defined CONFIG_FEATURE_MTAB_SUPPORT
+static int useMtab = TRUE;
+#endif
+static int umountAll = FALSE;
+static int doRemount = FALSE;
+
+
+
+/* These functions are here because the getmntent functions do not appear
+ * to be re-entrant, which leads to all sorts of problems when we try to
+ * use them recursively - randolph
+ *
+ * TODO: Perhaps switch to using Glibc's getmntent_r
+ *        -Erik
+ */
+static void mtab_read(void)
+{
+       struct _mtab_entry_t *entry = NULL;
+       struct mntent *e;
+       FILE *fp;
+
+       if (mtab_cache != NULL)
+               return;
+
+       if ((fp = setmntent(bb_path_mtab_file, "r")) == NULL) {
+               bb_error_msg("Cannot open %s", bb_path_mtab_file);
+               return;
+       }
+       while ((e = getmntent(fp))) {
+               entry = xmalloc(sizeof(struct _mtab_entry_t));
+               entry->device = strdup(e->mnt_fsname);
+               entry->mountpt = strdup(e->mnt_dir);
+               entry->next = mtab_cache;
+               mtab_cache = entry;
+       }
+       endmntent(fp);
+}
+
+static char *mtab_getinfo(const char *match, const char which)
+{
+       struct _mtab_entry_t *cur = mtab_cache;
+
+       while (cur) {
+               if (strcmp(cur->mountpt, match) == 0 ||
+                       strcmp(cur->device, match) == 0) {
+                       if (which == MTAB_GETMOUNTPT) {
+                               return cur->mountpt;
+                       } else {
+#if !defined CONFIG_FEATURE_MTAB_SUPPORT
+                               if (strcmp(cur->device, "rootfs") == 0) {
+                                       continue;
+                               } else if (strcmp(cur->device, "/dev/root") == 0) {
+                                       /* Adjusts device to be the real root device,
+                                        * or leaves device alone if it can't find it */
+                                       cur->device = find_real_root_device_name();
+                               }
+#endif
+                               return cur->device;
+                       }
+               }
+               cur = cur->next;
+       }
+       return NULL;
+}
+
+static char *mtab_next(void **iter)
+{
+       char *mp;
+
+       if (iter == NULL || *iter == NULL)
+               return NULL;
+       mp = ((struct _mtab_entry_t *) (*iter))->mountpt;
+       *iter = (void *) ((struct _mtab_entry_t *) (*iter))->next;
+       return mp;
+}
+
+static char *mtab_first(void **iter)
+{
+       struct _mtab_entry_t *mtab_iter;
+
+       if (!iter)
+               return NULL;
+       mtab_iter = mtab_cache;
+       *iter = (void *) mtab_iter;
+       return mtab_next(iter);
+}
+
+/* Don't bother to clean up, since exit() does that
+ * automagically, so we can save a few bytes */
+#ifdef CONFIG_FEATURE_CLEAN_UP
+static void mtab_free(void)
+{
+       struct _mtab_entry_t *this, *next;
+
+       this = mtab_cache;
+       while (this) {
+               next = this->next;
+               free(this->device);
+               free(this->mountpt);
+               free(this);
+               this = next;
+       }
+}
+#endif
+
+static int do_umount(const char *name)
+{
+       int status;
+       char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE);
+
+       if (blockDevice && strcmp(blockDevice, name) == 0)
+               name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT);
+
+       status = umount(name);
+
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+       if (freeLoop && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9))
+               /* this was a loop device, delete it */
+               del_loop(blockDevice);
+#endif
+#if defined CONFIG_FEATURE_MOUNT_FORCE
+       if (status != 0 && doForce) {
+               status = umount2(blockDevice, MNT_FORCE);
+               if (status != 0) {
+                       bb_error_msg_and_die("forced umount of %s failed!", blockDevice);
+               }
+       }
+#endif
+       if (status != 0 && doRemount && errno == EBUSY) {
+               status = mount(blockDevice, name, NULL,
+                                          MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
+               if (status == 0) {
+                       bb_error_msg("%s busy - remounted read-only", blockDevice);
+               } else {
+                       bb_error_msg("Cannot remount %s read-only", blockDevice);
+               }
+       }
+       if (status == 0) {
+#if defined CONFIG_FEATURE_MTAB_SUPPORT
+               if (useMtab)
+                       erase_mtab(name);
+#endif
+               return (TRUE);
+       }
+       return (FALSE);
+}
+
+static int umount_all(void)
+{
+       int status = TRUE;
+       char *mountpt;
+       void *iter;
+
+       for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) {
+               /* Never umount /proc on a umount -a */
+               if (strstr(mountpt, "proc")!= NULL)
+                       continue;
+               if (!do_umount(mountpt)) {
+                       /* Don't bother retrying the umount on busy devices */
+                       if (errno == EBUSY) {
+                               bb_perror_msg("%s", mountpt);
+                               status = FALSE;
+                               continue;
+                       }
+                       if (!do_umount(mountpt)) {
+                               printf("Couldn't umount %s on %s: %s\n",
+                                          mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE),
+                                          strerror(errno));
+                               status = FALSE;
+                       }
+               }
+       }
+       return (status);
+}
+
+extern int umount_main(int argc, char **argv)
+{
+       char path[PATH_MAX], result = 0;
+
+       if (argc < 2) {
+               bb_show_usage();
+       }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+       atexit(mtab_free);
+#endif
+
+       /* Parse any options */
+       while (--argc > 0 && **(++argv) == '-') {
+               while (*++(*argv))
+                       switch (**argv) {
+                       case 'a':
+                               umountAll = TRUE;
+                               break;
+#if defined CONFIG_FEATURE_MOUNT_LOOP
+                       case 'l':
+                               freeLoop = FALSE;
+                               break;
+#endif
+#ifdef CONFIG_FEATURE_MTAB_SUPPORT
+                       case 'n':
+                               useMtab = FALSE;
+                               break;
+#endif
+#ifdef CONFIG_FEATURE_MOUNT_FORCE
+                       case 'f':
+                               doForce = TRUE;
+                               break;
+#endif
+                       case 'r':
+                               doRemount = TRUE;
+                               break;
+                       case 'v':
+                               break; /* ignore -v */
+                       default:
+                               bb_show_usage();
+                       }
+       }
+
+       mtab_read();
+       if (umountAll) {
+               if (umount_all())
+                       return EXIT_SUCCESS;
+               else
+                       return EXIT_FAILURE;
+       }
+
+       do {
+               if (realpath(*argv, path) != NULL)
+                       if (do_umount(path))
+                               continue;
+               bb_perror_msg("%s", path);
+               result++;
+       } while (--argc > 0 && ++argv);
+       return result;
+}